mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 08:00:42 +00:00
build: fix plugin shared chunk routing to preserve node_modules resolution
This commit is contained in:
committed by
George Zhang
parent
1e8564cb13
commit
d5a06defda
@@ -9,6 +9,9 @@ type TsdownConfigEntry = {
|
||||
entry?: Record<string, string> | string[];
|
||||
inputOptions?: TsdownInputOptions;
|
||||
outDir?: string;
|
||||
outputOptions?: (options: unknown) => {
|
||||
chunkFileNames?: (chunkInfo: { name: string; moduleIds: string[] }) => string;
|
||||
};
|
||||
};
|
||||
|
||||
type TsdownLog = {
|
||||
@@ -145,4 +148,117 @@ describe("tsdown config", () => {
|
||||
|
||||
expect(handled).toEqual([log]);
|
||||
});
|
||||
|
||||
it("routes bundled plugin shared chunks to their own directory", () => {
|
||||
const configs = asConfigArray(tsdownConfig);
|
||||
const unifiedGraph = configs.find((config) => entryKeys(config).includes("index"));
|
||||
expect(unifiedGraph).toBeDefined();
|
||||
|
||||
// Extract the chunkFileNames function from outputOptions
|
||||
const outputOptionsFn = unifiedGraph!.outputOptions;
|
||||
expect(typeof outputOptionsFn).toBe("function");
|
||||
|
||||
const outputOptions = outputOptionsFn!({});
|
||||
const chunkFileNames = outputOptions.chunkFileNames!;
|
||||
expect(typeof chunkFileNames).toBe("function");
|
||||
|
||||
// Scenario 1: A chunk containing only slack files
|
||||
expect(
|
||||
chunkFileNames({
|
||||
name: "shared-slack-api",
|
||||
moduleIds: [
|
||||
"extensions/slack/src/api.ts",
|
||||
"extensions/slack/src/token.ts",
|
||||
],
|
||||
}),
|
||||
).toBe("extensions/slack/[name]-[hash].js");
|
||||
|
||||
// Scenario 2: A chunk containing only telegram files
|
||||
expect(
|
||||
chunkFileNames({
|
||||
name: "shared-telegram-api",
|
||||
moduleIds: [
|
||||
"extensions/telegram/src/api.ts",
|
||||
"extensions/telegram/src/config.ts",
|
||||
],
|
||||
}),
|
||||
).toBe("extensions/telegram/[name]-[hash].js");
|
||||
|
||||
// Scenario 3: A chunk containing mixed files (architectural violation)
|
||||
expect(
|
||||
chunkFileNames({
|
||||
name: "shared-mixed",
|
||||
moduleIds: [
|
||||
"extensions/slack/src/api.ts",
|
||||
"extensions/telegram/src/api.ts",
|
||||
],
|
||||
}),
|
||||
).toBe("[name]-[hash].js");
|
||||
|
||||
// Scenario 4: A chunk containing only core files
|
||||
expect(
|
||||
chunkFileNames({
|
||||
name: "shared-core",
|
||||
moduleIds: [
|
||||
"src/gateway/server-http.ts",
|
||||
"src/gateway/client.ts",
|
||||
],
|
||||
}),
|
||||
).toBe("[name]-[hash].js");
|
||||
|
||||
// Scenario 5: A chunk containing plugin and core files
|
||||
expect(
|
||||
chunkFileNames({
|
||||
name: "shared-plugin-and-core",
|
||||
moduleIds: [
|
||||
"extensions/slack/src/api.ts",
|
||||
"src/gateway/server-http.ts",
|
||||
],
|
||||
}),
|
||||
).toBe("[name]-[hash].js");
|
||||
|
||||
// Scenario 5b: A chunk containing plugin files and virtual modules
|
||||
expect(
|
||||
chunkFileNames({
|
||||
name: "shared-plugin-with-virtual",
|
||||
moduleIds: [
|
||||
"extensions/slack/src/api.ts",
|
||||
"\0commonjsHelpers.js",
|
||||
],
|
||||
}),
|
||||
).toBe("extensions/slack/[name]-[hash].js");
|
||||
|
||||
// Scenario 5c: A chunk containing plugin files and node_modules dependencies
|
||||
expect(
|
||||
chunkFileNames({
|
||||
name: "shared-plugin-with-deps",
|
||||
moduleIds: [
|
||||
"extensions/slack/src/api.ts",
|
||||
"node_modules/@slack/web-api/index.js",
|
||||
],
|
||||
}),
|
||||
).toBe("extensions/slack/[name]-[hash].js");
|
||||
|
||||
// Scenario 6: Fallback to previous function
|
||||
const outputOptionsWithFn = outputOptionsFn!({
|
||||
chunkFileNames: () => "custom-fn-[hash].js",
|
||||
});
|
||||
expect(
|
||||
outputOptionsWithFn.chunkFileNames!({
|
||||
name: "shared-core",
|
||||
moduleIds: ["src/gateway/server-http.ts"],
|
||||
}),
|
||||
).toBe("custom-fn-[hash].js");
|
||||
|
||||
// Scenario 7: Fallback to previous string
|
||||
const outputOptionsWithStr = outputOptionsFn!({
|
||||
chunkFileNames: "custom-str-[hash].js",
|
||||
});
|
||||
expect(
|
||||
outputOptionsWithStr.chunkFileNames!({
|
||||
name: "shared-core",
|
||||
moduleIds: ["src/gateway/server-http.ts"],
|
||||
}),
|
||||
).toBe("custom-str-[hash].js");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -88,6 +88,44 @@ function nodeBuildConfig(config: UserConfig): UserConfig {
|
||||
fixedExtension: false,
|
||||
platform: "node",
|
||||
inputOptions: buildInputOptions,
|
||||
outputOptions(options) {
|
||||
const previousChunkFileNames = options.chunkFileNames;
|
||||
return {
|
||||
...options,
|
||||
chunkFileNames(chunkInfo) {
|
||||
const moduleIds = chunkInfo.moduleIds || [];
|
||||
const extensionIds = new Set<string>();
|
||||
let hasNonPluginModules = false;
|
||||
for (const id of moduleIds) {
|
||||
if (id.startsWith("\0")) {
|
||||
continue;
|
||||
}
|
||||
const absoluteId = path.resolve(process.cwd(), id);
|
||||
const relativeToRoot = path.relative(process.cwd(), absoluteId);
|
||||
const parts = relativeToRoot.split(path.sep);
|
||||
|
||||
if (parts[0] === "extensions" && parts.length > 2) {
|
||||
extensionIds.add(parts[1]);
|
||||
} else if (parts.includes("node_modules")) {
|
||||
continue;
|
||||
} else {
|
||||
hasNonPluginModules = true;
|
||||
}
|
||||
}
|
||||
if (extensionIds.size === 1 && !hasNonPluginModules) {
|
||||
const extId = Array.from(extensionIds)[0];
|
||||
return `extensions/${extId}/[name]-[hash].js`;
|
||||
}
|
||||
if (typeof previousChunkFileNames === "function") {
|
||||
return previousChunkFileNames(chunkInfo);
|
||||
}
|
||||
if (typeof previousChunkFileNames === "string") {
|
||||
return previousChunkFileNames;
|
||||
}
|
||||
return `[name]-[hash].js`;
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user