FN-7936: alias @fusion/core to a runtime shim in bundled plugin outputs
Fix bundled example plugins (dependency-graph, grok-runtime, roadmap, acp-runtime, compound-engineering) crashing on enable with "Cannot find package '@fusion/core'" by aliasing the private import to a self-contained runtime shim during CLI bundling. - packages/cli/tsup.config.ts: drop @fusion/core from bundlePluginEntry's external list and alias it to the existing pluginSdkCoreRuntimeShim so bundled.js no longer references the private workspace package at runtime - packages/cli/src/__tests__/bundle-output.test.ts: add a regression test asserting every staged bundled plugin's bundled.js contains no bare @fusion/core import/reference - docs/PLUGIN_AUTHORING.md: document that bundled.js outputs must be self-contained and must not leak private @fusion/* workspace imports - .changeset/fn-7936-bundled-plugin-fusion-core-external.md: add a patch changeset for @runfusion/fusion describing the fix Files changed: .changeset/fn-7936-bundled-plugin-fusion-core-external.md | 7 +++++ docs/PLUGIN_AUTHORING.md | 3 +++ packages/cli/src/__tests__/bundle-output.test.ts | 30 ++++++++++++++++++++++ packages/cli/tsup.config.ts | 9 +++++-- 4 files changed, 47 insertions(+), 2 deletions(-) Fusion-Task-Id: FN-7936 Fusion-Task-Lineage: a8a391b2-9441-4a7c-92bc-f1675e1a8a0d Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
summary: Fix bundled example plugins failing to enable with a missing @fusion/core package error.
|
||||||
|
category: fix
|
||||||
|
dev: Aliases bundlePluginEntry @fusion/core imports to pluginSdkCoreRuntimeShim for self-contained bundled.js outputs.
|
||||||
@@ -767,6 +767,9 @@ Bundled workspace plugin pattern:
|
|||||||
- Register the lazy dashboard component in host code (currently `packages/dashboard/app/plugins/registerBundledPluginViews.ts`)
|
- Register the lazy dashboard component in host code (currently `packages/dashboard/app/plugins/registerBundledPluginViews.ts`)
|
||||||
- CLI bundling inlines backend plugin code from workspace packages; dashboard view modules are imported by the dashboard build via the host registry
|
- CLI bundling inlines backend plugin code from workspace packages; dashboard view modules are imported by the dashboard build via the host registry
|
||||||
|
|
||||||
|
<!-- FNXC:BundledPlugins 2026-07-13-00:00: FN-7936 requires `@runfusion/fusion` bundled plugin backend outputs to be install-self-contained. The CLI bundler resolves plugin-sdk runtime re-exports from `@fusion/core` through `plugin-sdk-core-runtime-shim.ts`, so `packages/cli/dist/plugins/<id>/bundled.js` must not ship private `@fusion/*` runtime specifiers that npm installs cannot resolve. -->
|
||||||
|
Bundled `bundled.js` outputs must be self-contained at runtime. Do not leave private workspace package imports such as `@fusion/core` in emitted bundled plugin code; `@fusion/plugin-sdk` core runtime re-exports are resolved through the CLI runtime shim during packaging.
|
||||||
|
|
||||||
### Bundled plugin build-freshness guard
|
### Bundled plugin build-freshness guard
|
||||||
|
|
||||||
<!-- FNXC:BundledPlugins 2026-06-17-22:31: Bundled plugins can load gitignored compiled artifacts before source during workspace/dev resolution, so plugin authors need a documented recovery path when the generic freshness guard detects stale dist output. -->
|
<!-- FNXC:BundledPlugins 2026-06-17-22:31: Bundled plugins can load gitignored compiled artifacts before source during workspace/dev resolution, so plugin authors need a documented recovery path when the generic freshness guard detects stale dist output. -->
|
||||||
|
|||||||
@@ -12,8 +12,16 @@ import {
|
|||||||
} from "./bundle-output-helpers";
|
} from "./bundle-output-helpers";
|
||||||
import { resolveClaudeCliExtensionFromModuleUrl } from "../commands/claude-cli-extension";
|
import { resolveClaudeCliExtensionFromModuleUrl } from "../commands/claude-cli-extension";
|
||||||
import { resolveDroidCliExtensionFromModuleUrl } from "../commands/droid-cli-extension";
|
import { resolveDroidCliExtensionFromModuleUrl } from "../commands/droid-cli-extension";
|
||||||
|
import { RUNTIME_PLUGIN_IDS } from "../plugins/staged-bundled-plugin-ids";
|
||||||
|
|
||||||
const tsupConfigPath = join(cliRoot, "tsup.config.ts");
|
const tsupConfigPath = join(cliRoot, "tsup.config.ts");
|
||||||
|
const bundlePluginEntryPluginIds = [
|
||||||
|
...RUNTIME_PLUGIN_IDS,
|
||||||
|
"fusion-plugin-dependency-graph",
|
||||||
|
"fusion-plugin-roadmap",
|
||||||
|
"fusion-plugin-compound-engineering",
|
||||||
|
"fusion-plugin-linear-import",
|
||||||
|
] as const;
|
||||||
|
|
||||||
describe("CLI bundle output", () => {
|
describe("CLI bundle output", () => {
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
@@ -222,6 +230,28 @@ describe("CLI bundle output", () => {
|
|||||||
expect(stagedPkg.dependencies?.["@fusion/core"]).toBeUndefined();
|
expect(stagedPkg.dependencies?.["@fusion/core"]).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("bundled plugin outputs do not import private @fusion/core at runtime", () => {
|
||||||
|
const inspectedPluginIds: string[] = [];
|
||||||
|
|
||||||
|
for (const pluginId of bundlePluginEntryPluginIds) {
|
||||||
|
const bundledPath = join(cliRoot, "dist", "plugins", pluginId, "bundled.js");
|
||||||
|
if (!existsSync(bundledPath)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
inspectedPluginIds.push(pluginId);
|
||||||
|
const bundled = readFileSync(bundledPath, "utf-8");
|
||||||
|
expect(bundled, `${pluginId} should not keep a bare @fusion/core import`).not.toMatch(
|
||||||
|
/from\s+["']@fusion\/core["']/,
|
||||||
|
);
|
||||||
|
expect(bundled, `${pluginId} should not mention the private @fusion/core package`).not.toContain(
|
||||||
|
'"@fusion/core"',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(inspectedPluginIds.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
it("dist/plugins/fusion-plugin-whatsapp-chat/ is staged with a valid manifest", () => {
|
it("dist/plugins/fusion-plugin-whatsapp-chat/ is staged with a valid manifest", () => {
|
||||||
const stagedRoot = join(cliRoot, "dist", "plugins", "fusion-plugin-whatsapp-chat");
|
const stagedRoot = join(cliRoot, "dist", "plugins", "fusion-plugin-whatsapp-chat");
|
||||||
const manifestPath = join(stagedRoot, "manifest.json");
|
const manifestPath = join(stagedRoot, "manifest.json");
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ const compoundEngineeringPluginSrc = join(__dirname, "..", "..", "plugins", "fus
|
|||||||
const compoundEngineeringPluginDest = join(__dirname, "dist", "plugins", "fusion-plugin-compound-engineering");
|
const compoundEngineeringPluginDest = join(__dirname, "dist", "plugins", "fusion-plugin-compound-engineering");
|
||||||
const linearImportPluginSrc = join(__dirname, "..", "..", "plugins", "fusion-plugin-linear-import");
|
const linearImportPluginSrc = join(__dirname, "..", "..", "plugins", "fusion-plugin-linear-import");
|
||||||
const linearImportPluginDest = join(__dirname, "dist", "plugins", "fusion-plugin-linear-import");
|
const linearImportPluginDest = join(__dirname, "dist", "plugins", "fusion-plugin-linear-import");
|
||||||
|
const pluginSdkCoreRuntimeShim = join(__dirname, "src", "plugin-sdk-core-runtime-shim.ts");
|
||||||
const dashboardClientStub = `<!doctype html>
|
const dashboardClientStub = `<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
@@ -174,9 +175,14 @@ async function bundlePluginEntry({ pluginId, srcDir, destDir, withMcpAsset = fal
|
|||||||
platform: "node",
|
platform: "node",
|
||||||
target: "node22",
|
target: "node22",
|
||||||
outfile: join(destDir, "bundled.js"),
|
outfile: join(destDir, "bundled.js"),
|
||||||
external: ["@fusion/core", "@fusion/engine"],
|
external: ["@fusion/engine"],
|
||||||
alias: {
|
alias: {
|
||||||
"@fusion/plugin-sdk": join(__dirname, "..", "plugin-sdk", "src", "index.ts"),
|
"@fusion/plugin-sdk": join(__dirname, "..", "plugin-sdk", "src", "index.ts"),
|
||||||
|
/*
|
||||||
|
* FNXC:BundledPlugins 2026-07-13-00:00:
|
||||||
|
* FN-7936 / issue #2059 requires every published bundled.js to be self-contained. The plugin-sdk source re-exports WORKFLOW_EXTENSION_SCHEMA_VERSION, workflowExtensionRegistryId, and createBoardActionServices from private @fusion/core; resolve those runtime values to the shared shim here so npm-installed bundled plugins never crash with Cannot find package '@fusion/core'.
|
||||||
|
*/
|
||||||
|
"@fusion/core": pluginSdkCoreRuntimeShim,
|
||||||
},
|
},
|
||||||
logLevel: "warning",
|
logLevel: "warning",
|
||||||
});
|
});
|
||||||
@@ -274,7 +280,6 @@ function assertAllStagedBundledPluginsLoadable() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pluginSdkEntry = join(__dirname, "..", "plugin-sdk", "src", "index.ts");
|
const pluginSdkEntry = join(__dirname, "..", "plugin-sdk", "src", "index.ts");
|
||||||
const pluginSdkCoreRuntimeShim = join(__dirname, "src", "plugin-sdk-core-runtime-shim.ts");
|
|
||||||
|
|
||||||
const cliBuildConfig = {
|
const cliBuildConfig = {
|
||||||
entry: ["src/bin.ts", "src/extension.ts"],
|
entry: ["src/bin.ts", "src/extension.ts"],
|
||||||
|
|||||||
Reference in New Issue
Block a user