fix(plugins): keep built plugin loading on one module graph (#48595)

This commit is contained in:
Harold Hunt
2026-03-16 20:58:58 -04:00
committed by GitHub
parent 4863b651c6
commit 94c27f34a1
8 changed files with 385 additions and 94 deletions

View File

@@ -1,13 +1,30 @@
import fs from "node:fs";
import path from "node:path";
import { defineConfig } from "tsdown";
import { defineConfig, type UserConfig } from "tsdown";
import { buildPluginSdkEntrySources } from "./scripts/lib/plugin-sdk-entries.mjs";
type InputOptionsFactory = Extract<NonNullable<UserConfig["inputOptions"]>, Function>;
type InputOptionsArg = InputOptionsFactory extends (
options: infer Options,
format: infer _Format,
context: infer _Context,
) => infer _Return
? Options
: never;
type InputOptionsReturn = InputOptionsFactory extends (
options: infer _Options,
format: infer _Format,
context: infer _Context,
) => infer Return
? Return
: never;
type OnLogFunction = InputOptionsArg extends { onLog?: infer OnLog } ? NonNullable<OnLog> : never;
const env = {
NODE_ENV: "production",
};
function buildInputOptions(options: { onLog?: unknown; [key: string]: unknown }) {
function buildInputOptions(options: InputOptionsArg): InputOptionsReturn {
if (process.env.OPENCLAW_BUILD_VERBOSE === "1") {
return undefined;
}
@@ -32,11 +49,8 @@ function buildInputOptions(options: { onLog?: unknown; [key: string]: unknown })
return {
...options,
onLog(
level: string,
log: { code?: string; message?: string; id?: string; importer?: string },
defaultHandler: (level: string, log: { code?: string }) => void,
) {
onLog(...args: Parameters<OnLogFunction>) {
const [level, log, defaultHandler] = args;
if (isSuppressedLog(log)) {
return;
}
@@ -49,7 +63,7 @@ function buildInputOptions(options: { onLog?: unknown; [key: string]: unknown })
};
}
function nodeBuildConfig(config: Record<string, unknown>) {
function nodeBuildConfig(config: UserConfig): UserConfig {
return {
...config,
env,
@@ -112,6 +126,33 @@ function listBundledPluginBuildEntries(): Record<string, string> {
const bundledPluginBuildEntries = listBundledPluginBuildEntries();
function buildBundledHookEntries(): Record<string, string> {
const hooksRoot = path.join(process.cwd(), "src", "hooks", "bundled");
const entries: Record<string, string> = {};
if (!fs.existsSync(hooksRoot)) {
return entries;
}
for (const dirent of fs.readdirSync(hooksRoot, { withFileTypes: true })) {
if (!dirent.isDirectory()) {
continue;
}
const hookName = dirent.name;
const handlerPath = path.join(hooksRoot, hookName, "handler.ts");
if (!fs.existsSync(handlerPath)) {
continue;
}
entries[`bundled/${hookName}/handler`] = handlerPath;
}
return entries;
}
const bundledHookEntries = buildBundledHookEntries();
function buildCoreDistEntries(): Record<string, string> {
return {
index: "src/index.ts",
@@ -130,33 +171,34 @@ function buildCoreDistEntries(): Record<string, string> {
"line/accounts": "src/line/accounts.ts",
"line/send": "src/line/send.ts",
"line/template-messages": "src/line/template-messages.ts",
"plugins/runtime/index": "src/plugins/runtime/index.ts",
"llm-slug-generator": "src/hooks/llm-slug-generator.ts",
};
}
const coreDistEntries = buildCoreDistEntries();
function buildUnifiedDistEntries(): Record<string, string> {
return {
...coreDistEntries,
...Object.fromEntries(
Object.entries(buildPluginSdkEntrySources()).map(([entry, source]) => [
`plugin-sdk/${entry}`,
source,
]),
),
...bundledPluginBuildEntries,
...bundledHookEntries,
};
}
export default defineConfig([
nodeBuildConfig({
// Build the root dist entrypoints together so they can share hashed chunks
// instead of emitting near-identical copies across separate builds.
entry: coreDistEntries,
}),
nodeBuildConfig({
// Bundle all plugin-sdk entries in a single build so the bundler can share
// common chunks instead of duplicating them per entry (~712MB heap saved).
entry: buildPluginSdkEntrySources(),
outDir: "dist/plugin-sdk",
}),
nodeBuildConfig({
// Bundle bundled plugin entrypoints so built gateway startup can load JS
// directly from dist/extensions instead of transpiling extensions/*.ts via Jiti.
entry: bundledPluginBuildEntries,
outDir: "dist",
// Build core entrypoints, plugin-sdk subpaths, bundled plugin entrypoints,
// and bundled hooks in one graph so runtime singletons are emitted once.
entry: buildUnifiedDistEntries(),
deps: {
neverBundle: ["@lancedb/lancedb"],
},
}),
nodeBuildConfig({
entry: ["src/hooks/bundled/*/handler.ts", "src/hooks/llm-slug-generator.ts"],
}),
]);