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>
This commit is contained in:
parent
6611bcf6e8
commit
d93fbda390
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
export const bundleMcpRuntime: (root: string) => Promise<void>;
|
||||
|
|
@ -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)), ".."),
|
||||
);
|
||||
|
|
@ -0,0 +1 @@
|
|||
export const bundleRuntime: (root: string) => Promise<void>;
|
||||
|
|
@ -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)), ".."));
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
),
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in New Issue