diff --git a/docs/solutions/integration-issues/bundled-plugin-registration-drift.md b/docs/solutions/integration-issues/bundled-plugin-registration-drift.md index 7c8ba38f33..ba2dca0b3a 100644 --- a/docs/solutions/integration-issues/bundled-plugin-registration-drift.md +++ b/docs/solutions/integration-issues/bundled-plugin-registration-drift.md @@ -29,7 +29,7 @@ The four registration points: 3. **CLI startup** — `BUNDLED_PLUGIN_IDS` in `packages/cli/src/plugins/bundled-plugin-install.ts` (auto-install/upgrade of bundled plugins) 4. **Build staging** — `packages/cli/tsup.config.ts` (`bundlePluginEntry` or a copy block staging the plugin into `dist/plugins//` so packaged installs have a copy at all) -A plugin with a dashboard view additionally needs client-side view registration in `packages/dashboard/app/plugins/registerBundledPluginViews.ts`. +A plugin with a dashboard view additionally needs client-side view registration in `packages/dashboard/app/plugins/registerBundledPluginViews.ts`. Its dashboard view component must **not** be re-exported from the plugin's server-side `src/index.ts`; keep only the `dashboardViews` manifest metadata there so Node-side plugin loading does not chase React/CSS imports. ## Symptoms @@ -78,7 +78,7 @@ The Settings card sends a relative `./plugins/` path. The server resolves it ## Prevention -- **When adding a bundled plugin, grep for an existing one** (e.g. `rg -l "fusion-plugin-roadmap" packages/` ) and mirror every hit — that surfaces all four lists plus view registration. +- **When adding a bundled plugin, grep for an existing one** (e.g. `rg -l "fusion-plugin-roadmap" packages/` ) and mirror every hit — that surfaces all four lists plus view registration. If the plugin has a dashboard view, keep the view load path client-only and do not re-export the view component from the server entry. - Route tests must force the fallback: mock fs so the cwd-relative path **misses** and only `dist/plugins/` exists (see "installs bundled compound engineering plugin when relative path misses cwd" in `packages/dashboard/src/__tests__/plugin-routes.test.ts`). A mock that matches any path containing the plugin id tests nothing. - **Pin assertions to the exact contract, not substring containment.** `stringContaining(pluginId)` passed for both the correct entry-file path and the buggy directory path — when a mock or matcher can satisfy both the correct and the buggy value, the test proves nothing. Route tests now assert the registered path ends in an entry-file suffix, cover the `dist/index.js` and `src/index.ts` fallbacks, and the 400 no-entry branch. - When duplicating a helper is forced by test infrastructure (fs mocks vs externalized deps), add a real-fs drift-guard test that runs every copy against the same on-disk fixtures and asserts identical output. diff --git a/docs/solutions/integration-issues/bundled-plugin-vite-alias-missing.md b/docs/solutions/integration-issues/bundled-plugin-vite-alias-missing.md index c06f85c1b5..22b41a4ce6 100644 --- a/docs/solutions/integration-issues/bundled-plugin-vite-alias-missing.md +++ b/docs/solutions/integration-issues/bundled-plugin-vite-alias-missing.md @@ -76,12 +76,23 @@ const mod = await import(/* @vite-ignore */ moduleId); Vite's static analysis cannot trace these imports, so it relies on `resolve.alias` to map the module ID to a filesystem path. Without the alias, Vite falls through to default resolution, which fails because the plugin package is in a sibling `plugins/` directory outside the dashboard's root. The error surfaces through the CSS loader because Vite's fallback resolution path misattributes the failure. +## Related root cause: CSS import via index re-export + +The same `Unknown file extension ".css"` symptom can also happen when a plugin's **server-side entry** (`src/index.ts` / `dist/index.js`) re-exports its dashboard view component: + +```ts +export { SomeDashboardView } from "./dashboard-view.js"; +``` + +Server-side plugin loading uses Node.js `import()` against the plugin entry. If that entry re-exports a React dashboard view, Node follows the chain into the view and any imported `.css` files before Vite is involved, then crashes because the Node ESM loader does not handle CSS. The fix is to keep dashboard view components out of the server entry: preserve the `dashboardViews` manifest metadata (`componentPath: "./dashboard-view"`) and load the view client-side through `registerBundledPluginViews.ts` plus the Vite alias. + ## Prevention - **When adding a bundled plugin with a dashboard view, grep for an existing plugin alias** in `packages/dashboard/vite.config.ts` and mirror the pattern for the new plugin +- **Do not re-export dashboard view components from the plugin's server-side `index.ts`** — the server entry must stay free of React/CSS view imports; `dashboardViews` metadata is enough for registration - **Verify the alias in both dev and production builds** — the alias must resolve correctly for Vite's dev server and its production bundler - **Consider a consistency test** that asserts every plugin registered in `registerBundledPluginViews.ts` has a corresponding Vite alias (similar to the existing `lazy-loaded-views-docs.test.ts` that keeps the AGENTS.md view inventory in sync) -- **Watch for the misleading `.css` error** — when Vite reports an unknown file extension for a file that clearly exists, suspect module resolution failure before investigating loaders +- **Watch for the misleading `.css` error** — when Vite reports an unknown file extension for a file that clearly exists, suspect module resolution failure before investigating loaders; when Node reports it while enabling a plugin, suspect a server-entry re-export of the dashboard view ## Related Issues diff --git a/plugins/fusion-plugin-compound-engineering/src/index.ts b/plugins/fusion-plugin-compound-engineering/src/index.ts index 6be65a3c10..7aaf230632 100644 --- a/plugins/fusion-plugin-compound-engineering/src/index.ts +++ b/plugins/fusion-plugin-compound-engineering/src/index.ts @@ -9,7 +9,6 @@ import { reconcileCePipelines } from "./sync/reconciler.js"; import { settingsSchema } from "./settings.js"; import { getReconcileOnHooks } from "./settings.js"; -export { CompoundEngineeringDashboardView } from "./dashboard-view.js"; export { COMPOUND_ENGINEERING_SKILLS } from "./skills.js"; export { installBundledCeSkills,