From d93fbda390c6b8b2741c552bd194b50c3e08e0c1 Mon Sep 17 00:00:00 2001 From: Ysopwnable <161784452+N0zoM1z0@users.noreply.github.com> Date: Mon, 12 Oct 2026 09:25:27 +0800 Subject: [PATCH] perf(cli): bundle runtime entries to reduce Windows startup I/O (#1996) Bundle ordinary CLI and output entries to reduce Windows module filesystem resolution while preserving source URLs, lazy loading and worker resources. Keep release identity in its shared owning module. Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com> --- package.json | 4 +- scripts/bundle-mcp-runtime.d.mts | 1 - scripts/bundle-mcp-runtime.mjs | 104 ------------- scripts/bundle-runtime.d.mts | 1 + scripts/bundle-runtime.mjs | 140 ++++++++++++++++++ scripts/prepack.mjs | 4 + scripts/rea.mjs | 17 ++- tests/boundary/cli/cliStartupImports.test.ts | 4 +- tests/boundary/cli/dispatcher.test.ts | 10 +- .../javascriptApplicationJsonOutput.test.ts | 20 ++- ...cpBundle.test.ts => runtimeBundle.test.ts} | 70 +++++++-- .../fixtures/browserScenarioCancellation.mjs | 5 +- turbo.json | 2 +- 13 files changed, 243 insertions(+), 139 deletions(-) delete mode 100644 scripts/bundle-mcp-runtime.d.mts delete mode 100644 scripts/bundle-mcp-runtime.mjs create mode 100644 scripts/bundle-runtime.d.mts create mode 100644 scripts/bundle-runtime.mjs rename tests/boundary/process/{mcpBundle.test.ts => runtimeBundle.test.ts} (57%) diff --git a/package.json b/package.json index 0108d6d05..4e4c6a7c4 100644 --- a/package.json +++ b/package.json @@ -256,8 +256,8 @@ "verify:evm:interface": "npm run build:cached && node scripts/verify-evm-interface.mjs", "verify:recorded:crash": "npm run build:cached && node scripts/verify-recorded-crash.mjs", "mcp-catalog:check:built": "node scripts/generate-mcp-tool-catalog.mjs --check", - "compile": "node scripts/clean-build-output.mjs && tsc -p tsconfig.build.json && node scripts/bundle-mcp-runtime.mjs", - "compile:termux": "node scripts/clean-build-output.mjs && node --max-old-space-size=3072 node_modules/typescript-termux/lib/tsc.js -p tsconfig.build.json && node scripts/bundle-mcp-runtime.mjs", + "compile": "node scripts/clean-build-output.mjs && tsc -p tsconfig.build.json && node scripts/bundle-runtime.mjs", + "compile:termux": "node scripts/clean-build-output.mjs && node --max-old-space-size=3072 node_modules/typescript-termux/lib/tsc.js -p tsconfig.build.json && node scripts/bundle-runtime.mjs", "artifacts:mcp-catalog": "node scripts/generate-mcp-tool-catalog.mjs", "artifacts:skills": "node scripts/generate-skill-metadata.mjs", "artifacts:product-catalog": "node scripts/generate-product-catalog.mjs", diff --git a/scripts/bundle-mcp-runtime.d.mts b/scripts/bundle-mcp-runtime.d.mts deleted file mode 100644 index 7fff6f125..000000000 --- a/scripts/bundle-mcp-runtime.d.mts +++ /dev/null @@ -1 +0,0 @@ -export const bundleMcpRuntime: (root: string) => Promise; diff --git a/scripts/bundle-mcp-runtime.mjs b/scripts/bundle-mcp-runtime.mjs deleted file mode 100644 index 2750883f5..000000000 --- a/scripts/bundle-mcp-runtime.mjs +++ /dev/null @@ -1,104 +0,0 @@ -import { parse } from "@babel/parser"; -import { readFile } from "node:fs/promises"; -import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path"; -import { fileURLToPath, pathToFileURL } from "node:url"; - -/** - * Reduce filesystem resolution during MCP startup. Keep dependencies external, - * dynamic imports lazy, and the original compiled files for CLI/worker entrypoints. - */ -export const bundleMcpRuntime = async (root) => { - const compiledRoot = join(root, "dist"); - const { rolldown, RUNTIME_MODULE_ID } = await import("rolldown"); - const { imports = {} } = JSON.parse( - await readFile(join(root, "package.json"), "utf8"), - ); - const bundle = await rolldown({ - input: { main: join(compiledRoot, "main.js") }, - platform: "node", - treeshake: false, - external: (source) => !source.startsWith(".") && !isAbsolute(source), - plugins: [ - { - name: "preserve-compiled-module-locations", - transform(code, id) { - if (id === RUNTIME_MODULE_ID) return null; - if (!code.includes("import.meta")) return null; - const sourcePath = relative(compiledRoot, id); - if ( - isAbsolute(sourcePath) || - sourcePath === ".." || - sourcePath.startsWith(`..${sep}`) - ) - throw new Error(`MCP bundle input is outside dist: ${id}`); - // All chunks are directly under dist/mcp. Preserve the original - // module URL, including URL escaping, for bridges and createRequire. - const moduleUrl = `new URL(${JSON.stringify(`../${sourcePath.split(sep).map(encodeURIComponent).join("/")}`)}, import.meta.url).href`; - const edits = []; - const visit = (node) => { - if (node === null || typeof node !== "object") return; - const isMeta = (value) => - value?.type === "MetaProperty" && - value.meta.name === "import" && - value.property.name === "meta"; - if ( - node.type === "CallExpression" && - node.callee.type === "MemberExpression" && - !node.callee.computed && - isMeta(node.callee.object) && - node.callee.property.name === "resolve" && - node.arguments.length === 1 && - node.arguments[0].type === "StringLiteral" && - Object.hasOwn(imports, node.arguments[0].value) - ) - // Package imports resolve in the same package scope after bundling. - return; - if ( - node.type === "MemberExpression" && - !node.computed && - isMeta(node.object) && - node.property.name === "url" - ) { - edits.push({ start: node.start, end: node.end }); - return; - } - if (isMeta(node)) - throw new Error( - `Unsupported import.meta use in MCP bundle: ${id}`, - ); - for (const value of Object.values(node)) { - if (Array.isArray(value)) value.forEach(visit); - else visit(value); - } - }; - visit(parse(code, { sourceType: "module" }).program); - for (const { start, end } of edits.sort((a, b) => b.start - a.start)) - code = code.slice(0, start) + moduleUrl + code.slice(end); - return { code, map: null }; - }, - }, - ], - }); - try { - await bundle.write({ - dir: join(compiledRoot, "mcp"), - format: "esm", - entryFileNames: "[name].js", - chunkFileNames: "[name]-[hash].js", - codeSplitting: true, - strictExecutionOrder: true, - minify: false, - sourcemap: false, - }); - } finally { - await bundle.close(); - } -}; - -if ( - process.argv[1] !== undefined && - pathToFileURL(resolve(process.argv[1])).href === import.meta.url -) - await bundleMcpRuntime( - resolve(dirname(fileURLToPath(import.meta.url)), ".."), - ); diff --git a/scripts/bundle-runtime.d.mts b/scripts/bundle-runtime.d.mts new file mode 100644 index 000000000..1f9637971 --- /dev/null +++ b/scripts/bundle-runtime.d.mts @@ -0,0 +1 @@ +export const bundleRuntime: (root: string) => Promise; diff --git a/scripts/bundle-runtime.mjs b/scripts/bundle-runtime.mjs new file mode 100644 index 000000000..bac18ed2c --- /dev/null +++ b/scripts/bundle-runtime.mjs @@ -0,0 +1,140 @@ +import { parse } from "@babel/parser"; +import { readFile } from "node:fs/promises"; +import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path"; +import { fileURLToPath, pathToFileURL } from "node:url"; + +/** + * Reduce filesystem resolution during CLI and MCP startup. Keep dependencies + * external, dynamic imports lazy, and original compiled worker entrypoints. + */ +export const bundleRuntime = async (root) => { + const compiledRoot = resolve(root, "dist"); + // CLI, MCP, workers and updater fixtures share one release-identity owner. + const metadataModule = join(compiledRoot, "generatedPackageMetadata.js"); + const isMetadataModule = (id) => + isAbsolute(id) && resolve(id) === metadataModule; + const { rolldown, RUNTIME_MODULE_ID } = await import("rolldown"); + const { imports = {} } = JSON.parse( + await readFile(join(root, "package.json"), "utf8"), + ); + for (const [directory, entries] of [ + ["mcp", { main: "main.js" }], + [ + "cli-runtime", + { + cli: "cli.js", + cliOutput: "cliOutput.js", + streamedJsonOutput: "cli/streamedJsonOutput.js", + mcpDoctor: "mcpDoctor.js", + }, + ], + ]) { + const bundle = await rolldown({ + input: Object.fromEntries( + Object.entries(entries).map(([name, path]) => [ + name, + join(compiledRoot, path), + ]), + ), + platform: "node", + treeshake: false, + external: (source) => + isMetadataModule(source) || + (!source.startsWith(".") && !isAbsolute(source)), + plugins: [ + { + name: "preserve-compiled-module-locations", + transform(code, id) { + if (id === RUNTIME_MODULE_ID) return null; + if (!code.includes("import.meta")) return null; + const sourcePath = relative(compiledRoot, id); + if ( + isAbsolute(sourcePath) || + sourcePath === ".." || + sourcePath.startsWith(`..${sep}`) + ) + throw new Error(`Runtime bundle input is outside dist: ${id}`); + // Each output directory is directly under dist. Preserve the original + // module URL, including URL escaping, for bridges and createRequire. + const moduleUrl = `new URL(${JSON.stringify(`../${sourcePath.split(sep).map(encodeURIComponent).join("/")}`)}, import.meta.url).href`; + const edits = []; + const visit = (node) => { + if (node === null || typeof node !== "object") return; + const isMeta = (value) => + value?.type === "MetaProperty" && + value.meta.name === "import" && + value.property.name === "meta"; + if ( + node.type === "CallExpression" && + node.callee.type === "MemberExpression" && + !node.callee.computed && + isMeta(node.callee.object) && + node.callee.property.name === "resolve" && + node.arguments.length === 1 && + node.arguments[0].type === "StringLiteral" && + Object.hasOwn(imports, node.arguments[0].value) + ) + // Package imports resolve in the same package scope after bundling. + return; + if ( + node.type === "MemberExpression" && + !node.computed && + isMeta(node.object) && + node.property.name === "url" + ) { + edits.push({ start: node.start, end: node.end }); + return; + } + if (isMeta(node)) + throw new Error( + `Unsupported import.meta use in runtime bundle: ${id}`, + ); + for (const value of Object.values(node)) { + if (Array.isArray(value)) value.forEach(visit); + else visit(value); + } + }; + visit(parse(code, { sourceType: "module" }).program); + for (const { start, end } of edits.sort( + (a, b) => b.start - a.start, + )) + code = code.slice(0, start) + moduleUrl + code.slice(end); + return { code, map: null }; + }, + }, + ], + }); + try { + await bundle.write({ + dir: join(compiledRoot, directory), + format: "esm", + entryFileNames: "[name].js", + chunkFileNames: + directory === "mcp" + ? "[name]-[hash].js" + : (chunk) => { + // Keep lazy facades distinguishable when source basenames repeat. + if (typeof chunk.facadeModuleId !== "string") + return "[name]-[hash].js"; + const path = relative(compiledRoot, chunk.facadeModuleId); + return `${path.replace(/\.js$/u, "").split(sep).join("__")}-[hash].js`; + }, + paths: (id) => + isMetadataModule(id) ? "../generatedPackageMetadata.js" : id, + minifyInternalExports: directory === "mcp", + codeSplitting: true, + strictExecutionOrder: true, + minify: false, + sourcemap: false, + }); + } finally { + await bundle.close(); + } + } +}; + +if ( + process.argv[1] !== undefined && + pathToFileURL(resolve(process.argv[1])).href === import.meta.url +) + await bundleRuntime(resolve(dirname(fileURLToPath(import.meta.url)), "..")); diff --git a/scripts/prepack.mjs b/scripts/prepack.mjs index 3b9f9adf1..9d0958d8e 100644 --- a/scripts/prepack.mjs +++ b/scripts/prepack.mjs @@ -47,6 +47,10 @@ if (await pathExists(join(root, "src"))) { for (const path of [ "dist/main.js", "dist/mcp/main.js", + "dist/cli-runtime/cli.js", + "dist/cli-runtime/cliOutput.js", + "dist/cli-runtime/streamedJsonOutput.js", + "dist/cli-runtime/mcpDoctor.js", "dist/mcpDoctor.js", "dist/cli.js", "dist/cliOutput.js", diff --git a/scripts/rea.mjs b/scripts/rea.mjs index 57272d987..3b1ef6c4a 100755 --- a/scripts/rea.mjs +++ b/scripts/rea.mjs @@ -33,8 +33,12 @@ if ( const runtimeFiles = isMcpMode ? ["dist/mcp/main.js"] : isMcpDoctorMode - ? ["dist/mcp/main.js", "dist/mcpDoctor.js"] - : ["dist/cli.js", "dist/cliOutput.js", "dist/cli/streamedJsonOutput.js"]; + ? ["dist/mcp/main.js", "dist/cli-runtime/mcpDoctor.js"] + : [ + "dist/cli-runtime/cli.js", + "dist/cli-runtime/cliOutput.js", + "dist/cli-runtime/streamedJsonOutput.js", + ]; if (!(await compiledRuntimeExists(runtimeFiles))) { process.stderr.write( @@ -45,7 +49,8 @@ if (!(await compiledRuntimeExists(runtimeFiles))) { const { runEntrypoint } = await import("../dist/mcp/main.js"); await runEntrypoint(); } else if (isMcpDoctorMode) { - const { runProductionMcpDoctorCli } = await import("../dist/mcpDoctor.js"); + const { runProductionMcpDoctorCli } = + await import("../dist/cli-runtime/mcpDoctor.js"); const result = await runProductionMcpDoctorCli(args.slice(2), { dispatcherPath: fileURLToPath(import.meta.url), packageRoot, @@ -53,14 +58,14 @@ if (!(await compiledRuntimeExists(runtimeFiles))) { process.stdout.write(result.output); process.exitCode = result.exitCode; } else { - const { createCli } = await import("../dist/cli.js"); + const { createCli } = await import("../dist/cli-runtime/cli.js"); const { createStreamedCliJsonOutput } = - await import("../dist/cli/streamedJsonOutput.js"); + await import("../dist/cli-runtime/streamedJsonOutput.js"); const { renderCliOutputArgumentError, renderEmptyFilteredCliOutput, validateCliOutputArguments, - } = await import("../dist/cliOutput.js"); + } = await import("../dist/cli-runtime/cliOutput.js"); const outputArguments = validateCliOutputArguments(args); if (!outputArguments.ok) { process.stdout.write(renderCliOutputArgumentError(outputArguments)); diff --git a/tests/boundary/cli/cliStartupImports.test.ts b/tests/boundary/cli/cliStartupImports.test.ts index a792c82b4..cc1232606 100644 --- a/tests/boundary/cli/cliStartupImports.test.ts +++ b/tests/boundary/cli/cliStartupImports.test.ts @@ -123,7 +123,9 @@ registerHooks({ resolve(specifier, context, next) { const resolved = next(specifier, context); const name = imports.find((item) => - resolved.url.includes(item.path), + resolved.url.includes(item.path) || + (item.path.startsWith("/dist/") && item.path.endsWith(".js") && + resolved.url.includes("/dist/cli-runtime/" + item.path.slice(6, -3).split("/").join("__") + "-")), ); if (name !== undefined) process.stderr.write(${JSON.stringify(MARKER)} + name.name + "\\n"); diff --git a/tests/boundary/cli/dispatcher.test.ts b/tests/boundary/cli/dispatcher.test.ts index f002fcd51..583bd601d 100644 --- a/tests/boundary/cli/dispatcher.test.ts +++ b/tests/boundary/cli/dispatcher.test.ts @@ -11,7 +11,7 @@ describe("executable dispatcher", () => { async ({ processes, workspace }) => { const scripts = await workspace.mkdir("scripts"); await workspace.mkdir("dist"); - await workspace.mkdir("dist/cli"); + await workspace.mkdir("dist/cli-runtime"); await workspace.mkdir("dist/mcp"); await Promise.all([ copyFile("scripts/rea.mjs", join(scripts, "rea.mjs")), @@ -24,19 +24,19 @@ describe("executable dispatcher", () => { 'export const runEntrypoint = () => process.stdout.write(JSON.stringify({ adapter: "mcp" }));\n', ), workspace.write( - "dist/cli.js", + "dist/cli-runtime/cli.js", 'export const createCli = () => ({ serve: (args) => process.stdout.write(JSON.stringify({ adapter: "cli", args })) });\n', ), workspace.write( - "dist/cliOutput.js", + "dist/cli-runtime/cliOutput.js", "export const validateCliOutputArguments = () => ({ ok: true }); export const renderCliOutputArgumentError = () => ''; export const renderEmptyFilteredCliOutput = () => undefined;\n", ), workspace.write( - "dist/cli/streamedJsonOutput.js", + "dist/cli-runtime/streamedJsonOutput.js", "export const createStreamedCliJsonOutput = () => undefined;\n", ), workspace.write( - "dist/mcpDoctor.js", + "dist/cli-runtime/mcpDoctor.js", 'export const runProductionMcpDoctorCli = (args) => Promise.resolve({ output: JSON.stringify({ adapter: "mcp-doctor", args }), exitCode: 0 });\n', ), ]); diff --git a/tests/boundary/cli/javascriptApplicationJsonOutput.test.ts b/tests/boundary/cli/javascriptApplicationJsonOutput.test.ts index af41425ed..0c694999e 100644 --- a/tests/boundary/cli/javascriptApplicationJsonOutput.test.ts +++ b/tests/boundary/cli/javascriptApplicationJsonOutput.test.ts @@ -1,15 +1,15 @@ import { writeFile } from "node:fs/promises"; import { join } from "node:path"; -import { Filter } from "incur"; import { expect } from "vitest"; import { z } from "zod"; +import { javascriptApplicationAnalysisResultSchema } from "../../../src/domain/javascript/javascriptApplicationAnalysis.js"; import { analyzeJavaScriptApplication } from "../../support/javascriptApplicationScope.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; import { cliTest } from "../../support/cli/cliFixture.js"; -const filter = "evidence_id,normalized_result.graph.nodes[0:1].node_id"; +const filter = "evidence_id,normalized_result.graph.nodes[0,1].node_id"; for (const scenario of [ { @@ -34,6 +34,9 @@ for (const scenario of [ await writeFile(join(root, "main.js"), "fetch('');\n"); const direct = await analyzeJavaScriptApplication({ input_path: root }); if (!direct.ok) throw direct.error; + const normalized = javascriptApplicationAnalysisResultSchema.parse( + direct.value.normalized_result, + ); const output = await cli.run({ arguments: [ scenario.command, @@ -61,9 +64,20 @@ for (const scenario of [ } expect(data).toEqual( scenario.filtered - ? Filter.apply(direct.value, Filter.parse(filter)) + ? { + evidence_id: direct.value.evidence_id, + normalized_result: { + graph: { + nodes: normalized.graph.nodes + .slice(0, 1) + .map(({ node_id }) => ({ node_id })), + }, + }, + } : direct.value, ); + if (scenario.filtered) + expect(normalized.graph.nodes.length).toBeGreaterThan(0); }, ); } diff --git a/tests/boundary/process/mcpBundle.test.ts b/tests/boundary/process/runtimeBundle.test.ts similarity index 57% rename from tests/boundary/process/mcpBundle.test.ts rename to tests/boundary/process/runtimeBundle.test.ts index 3895b2ce8..ab09521f3 100644 --- a/tests/boundary/process/mcpBundle.test.ts +++ b/tests/boundary/process/runtimeBundle.test.ts @@ -4,15 +4,16 @@ import { pathToFileURL } from "node:url"; import { describe, expect } from "vitest"; -import { bundleMcpRuntime } from "../../../scripts/bundle-mcp-runtime.mjs"; +import { bundleRuntime } from "../../../scripts/bundle-runtime.mjs"; import { workspaceCliTest } from "../../support/cli/workspaceCliFixture.js"; -describe("MCP runtime bundle", () => { +describe("CLI and MCP runtime bundles", () => { workspaceCliTest( "preserves module locations, lazy side effects, package resolution and worker entrypoints", async ({ processes, workspace }) => { await workspace.mkdir("dist/provider"); await workspace.mkdir("dist/composition"); + await workspace.mkdir("dist/cli"); await workspace.mkdir("bridge"); await workspace.mkdir("node_modules/external-probe"); const files = { @@ -45,8 +46,17 @@ globalThis.effects.push('lazy'); export const lazyUrl = import.meta.url;`, "dist/composition/javascriptAnalysisWorkerProcess.js": 'process.stdout.write("worker-entry");', + "dist/generatedPackageMetadata.js": + 'export const generatedPackageMetadata = { version: "1.0.0" };', + "dist/cli.js": 'export { facts, run } from "./main.js";', + "dist/cliOutput.js": 'export { facts } from "./provider/probe λ.js";', + "dist/cli/streamedJsonOutput.js": + 'export { facts } from "../provider/probe λ.js";', + "dist/mcpDoctor.js": 'export { run } from "./main.js";', "dist/main.js": ` import { facts } from './provider/probe λ.js'; +import { generatedPackageMetadata } from './generatedPackageMetadata.js'; +export { facts }; import { execFileSync } from 'node:child_process'; import { fileURLToPath } from 'node:url'; globalThis.effects.push('main'); @@ -54,7 +64,7 @@ export const run = async () => { const beforeLazy = [...globalThis.effects]; const { lazyUrl } = await import('./lazy.js'); return { - ...facts, beforeLazy, afterLazy: globalThis.effects, lazyUrl, + ...facts, version: generatedPackageMetadata.version, beforeLazy, afterLazy: globalThis.effects, lazyUrl, workerOutput: execFileSync(process.execPath, [fileURLToPath(facts.worker)], { encoding: 'utf8' }) }; };`, @@ -76,9 +86,23 @@ export const run = async () => { return JSON.parse(result.stdout) as unknown; }; const original = await invoke("dist/main.js"); - await bundleMcpRuntime(workspace.root); + await bundleRuntime(workspace.root); const bundled = await invoke("dist/mcp/main.js"); expect(bundled).toEqual(original); + expect(await invoke("dist/cli-runtime/cli.js")).toEqual( + await invoke("dist/cli.js"), + ); + const cliDirectory = join(workspace.root, "dist/cli-runtime"); + const identity = await processes.run( + process.execPath, + [ + "--input-type=module", + "--eval", + `const { facts } = await import(${JSON.stringify(pathToFileURL(join(cliDirectory, "cli.js")).href)}); const output = await import(${JSON.stringify(pathToFileURL(join(cliDirectory, "cliOutput.js")).href)}); const streamed = await import(${JSON.stringify(pathToFileURL(join(cliDirectory, "streamedJsonOutput.js")).href)}); process.stdout.write(JSON.stringify(facts === output.facts && facts === streamed.facts));`, + ], + { cwd: workspace.root }, + ); + expect(identity).toMatchObject({ exitCode: 0, stdout: "true" }); expect(bundled).toMatchObject({ resource: "bridge resource\n", external: "external", @@ -93,6 +117,14 @@ export const run = async () => { expect( (await readdir(join(workspace.root, "dist/mcp"))).length, ).toBeGreaterThan(1); + await workspace.write( + "dist/generatedPackageMetadata.js", + 'export const generatedPackageMetadata = { version: "0.0.0" };', + ); + const previousRelease = await invoke("dist/main.js"); + expect(previousRelease).toMatchObject({ version: "0.0.0" }); + expect(await invoke("dist/mcp/main.js")).toEqual(previousRelease); + expect(await invoke("dist/cli-runtime/cli.js")).toEqual(previousRelease); }, ); @@ -100,18 +132,26 @@ export const run = async () => { "rejects module-relative resolution and unknown import metadata during the build", async ({ workspace }) => { await workspace.mkdir("dist"); + await workspace.mkdir("dist/cli"); await workspace.write("package.json", '{"type":"module"}'); - for (const expression of [ - "import.meta.resolve('./worker.js')", - "import.meta.env", - ]) { - await workspace.write( - "dist/main.js", - `export const value = ${expression};`, - ); - await expect(bundleMcpRuntime(workspace.root)).rejects.toThrow( - "Unsupported import.meta use in MCP bundle", - ); + for (const path of [ + "dist/cliOutput.js", + "dist/cli/streamedJsonOutput.js", + "dist/mcpDoctor.js", + ]) + await workspace.write(path, "export {};\n"); + for (const entry of ["dist/main.js", "dist/cli.js"]) { + for (const expression of [ + "import.meta.resolve('./worker.js')", + "import.meta.env", + ]) { + await workspace.write("dist/main.js", "export {};\n"); + await workspace.write("dist/cli.js", "export {};\n"); + await workspace.write(entry, `export const value = ${expression};`); + await expect(bundleRuntime(workspace.root)).rejects.toThrow( + "Unsupported import.meta use in runtime bundle", + ); + } } }, ); diff --git a/tests/fixtures/browserScenarioCancellation.mjs b/tests/fixtures/browserScenarioCancellation.mjs index 2e0918f08..75c278f05 100644 --- a/tests/fixtures/browserScenarioCancellation.mjs +++ b/tests/fixtures/browserScenarioCancellation.mjs @@ -103,9 +103,12 @@ export const createBrowserScenarioProvider = () => const composition = pathToFileURL( root + "/dist/composition/browserScenario.js", ).href; +const bundledComposition = pathToFileURL( + root + "/dist/cli-runtime/composition__browserScenario-", +).href; registerHooks({ load(url, context, nextLoad) { - if (url === composition) + if (url === composition || url.startsWith(bundledComposition)) return { format: "module", shortCircuit: true, diff --git a/turbo.json b/turbo.json index 03ce42b73..a177552d3 100644 --- a/turbo.json +++ b/turbo.json @@ -10,7 +10,7 @@ "package-lock.json", "scripts/check-dependency-install.mjs", "scripts/clean-build-output.mjs", - "scripts/bundle-mcp-runtime.mjs", + "scripts/bundle-runtime.mjs", "scripts/generate-package-metadata.mjs", "src/**/*.ts", "!src/**/*.test.ts",