ProvenPin Install

esbuild build API bundling virtual files in memory (write:false): working versions (tested 2026-10-15)

Bundle TypeScript from an in-memory file map with an esbuild resolve/load plugin and write:false, use define, tree shaking, minify and external source maps, evaluate the bundle, and assert structured build errors.

passed5 passed · 0 failed2026-10-15 20:08 UTC
Status
passed
Last passed
2026-10-15 20:08 UTC
Tests
5 / 5
Language
TypeScript
Runtime
node v22.23.3
pinned versions3
esbuild@0.28.2@types/node@26.6.5tsx@4.23.15
run it
npm install --no-audit --no-fund --ignore-scripts=false
npm test
test environment
node v22.23.3
linux 6.17.0-1022-azure
x64
runner: github-actions 38799498972

The code

Exactly the files that passed. Your agent can pull them with get_recipe("esbuild-in-memory-bundle") (install), or see them on GitHub.

package.json
{
  "name": "esbuild-in-memory-bundle",
  "private": true,
  "type": "module",
  "scripts": {
    "test": "node --import tsx --test test/*.test.ts"
  },
  "dependencies": {
    "esbuild": "0.28.2"
  },
  "devDependencies": {
    "@types/node": "26.6.5",
    "tsx": "4.23.15"
  }
}
src/bundle.ts
import * as esbuild from "esbuild";

/** Virtual-file plugin: resolves bare imports from an in-memory map, so nothing touches disk. */
function virtualFs(files: Record<string, string>): esbuild.Plugin {
  return {
    name: "virtual-fs",
    setup(build) {
      build.onResolve({ filter: /.*/ }, (args) => {
        const key = args.path.replace(/^\.\//, "");
        if (key in files) return { path: key, namespace: "virtual" };
        if (args.path === "env:build") return { path: "env", namespace: "env" };
        return undefined;
      });
      build.onLoad({ filter: /.*/, namespace: "virtual" }, (args) => ({ contents: files[args.path], loader: args.path.endsWith(".ts") ? "ts" : "js" }));
      build.onLoad({ filter: /.*/, namespace: "env" }, () => ({ contents: JSON.stringify({ builtAt: "fixed" }), loader: "json" }));
    },
  };
}

export async function bundle(entry: string, files: Record<string, string>, opts: Partial<esbuild.BuildOptions> = {}) {
  const result = await esbuild.build({
    entryPoints: [entry],
    bundle: true,
    write: false,
    outfile: "out.js",
    format: "esm",
    platform: "neutral",
    metafile: true,
    logLevel: "silent",
    plugins: [virtualFs(files)],
    ...opts,
  });
  const out = result.outputFiles.find((f) => f.path.endsWith("out.js"))!;
  return { code: out.text, inputs: Object.keys(result.metafile!.inputs).sort(), warnings: result.warnings.length, files: result.outputFiles.map((f) => f.path.split("/").pop()) };
}

/** Run an ESM bundle string and return its exports. */
export async function evaluate(code: string) {
  return import("data:text/javascript;base64," + Buffer.from(code).toString("base64"));
}

export const transformTs = (src: string) => esbuild.transform(src, { loader: "ts", format: "esm", target: "es2020" });
test/bundle.test.ts
import { test } from "node:test";
import assert from "node:assert/strict";
import { bundle, evaluate, transformTs } from "../src/bundle.ts";

const files = {
  "main.ts": 'import { add } from "./math.ts";\nimport env from "env:build";\nimport { unused } from "./math.ts";\nexport const total: number = add(2, 3);\nexport const meta = env.builtAt;\nexport const DEBUG = process.env.NODE_ENV !== "production";\n',
  "math.ts": "export const add = (a: number, b: number): number => a + b;\nexport const unused = () => 'dead code';\n",
};

test("bundles virtual TS files in memory and runs them", async () => {
  const { code, inputs } = await bundle("main.ts", files, { define: { "process.env.NODE_ENV": '"production"' } });
  assert.deepEqual(inputs, ["env:env", "virtual:main.ts", "virtual:math.ts"]);
  const mod = await evaluate(code);
  assert.equal(mod.total, 5);
  assert.equal(mod.meta, "fixed");
  assert.equal(mod.DEBUG, false);
});

test("tree shaking drops unused exports", async () => {
  const { code } = await bundle("main.ts", files, { define: { "process.env.NODE_ENV": '"production"' } });
  assert.doesNotMatch(code, /dead code/);
});

test("minify plus external source map produces two outputs", async () => {
  const { code, files: outs } = await bundle("main.ts", files, { minify: true, sourcemap: "external", define: { "process.env.NODE_ENV": '"production"' } });
  assert.deepEqual(outs.sort(), ["out.js", "out.js.map"]);
  assert.ok(code.length < 200, `minified length ${code.length}`);
  assert.equal((await evaluate(code)).total, 5);
});

test("build errors are reported with location", async () => {
  await assert.rejects(bundle("main.ts", { "main.ts": "export const x = ;" }), (e: { errors: { text: string; location: { line: number } }[] }) => {
    assert.equal(e.errors[0].location.line, 1);
    assert.match(e.errors[0].text, /Unexpected/);
    return true;
  });
});

test("transform strips types only", async () => {
  const { code } = await transformTs("export const f = (x: number): string => `${x}` as string;\n");
  // esbuild hoists exports into a trailing export list
  assert.equal(code, "const f = (x) => `${x}`;\nexport {\n  f\n};\n");
});

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.