From 47e82408afe04f9acba206e386b30eea0deaf2e9 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Tue, 9 Jun 2026 10:35:58 -0700 Subject: [PATCH] FN-6106: block plugin view re-exports from server entries Add an ESLint guard to keep plugin dashboard views out of server entrypoints. - add a custom fusion/no-plugin-view-reexport ESLint rule for plugin src/index.ts files - flag relative re-exports of *-view entrypoints so CSS-bearing dashboard modules stay out of Node-loaded plugin entries - document the server-entry export constraint in the plugin authoring guide Files changed: docs/PLUGIN_AUTHORING.md | 1 + eslint.config.mjs | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) Fusion-Task-Id: FN-6106 Fusion-Task-Lineage: 6bbfa073-e7fd-42a9-a332-c638b81cd55f --- docs/PLUGIN_AUTHORING.md | 1 + eslint.config.mjs | 47 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/docs/PLUGIN_AUTHORING.md b/docs/PLUGIN_AUTHORING.md index 15293151c3..8fac1aa646 100644 --- a/docs/PLUGIN_AUTHORING.md +++ b/docs/PLUGIN_AUTHORING.md @@ -763,6 +763,7 @@ The host then renders plugin views via `PluginDashboardViewHost` using the compo Bundled workspace plugin pattern: - Keep plugin package under `plugins/` (for example `plugins/fusion-plugin-roadmap`) - Export backend/plugin entry from `src/index.ts` and keep dashboard view exports in the plugin package (for example `./dashboard-view`) +- Do not re-export dashboard view entrypoints (including `dashboard-view`, `manage-view`, or other `-view` modules) from `src/index.ts`; the `fusion/no-plugin-view-reexport` ESLint guard catches violations because server-side plugin entries must not transitively load CSS - 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 diff --git a/eslint.config.mjs b/eslint.config.mjs index 2b706159d2..383f64b224 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -69,6 +69,36 @@ const detachedSpawnGuard = { }, }; +const noPluginViewReexport = { + meta: { + type: "problem", + docs: { + description: "ban dashboard view re-exports from plugin server entry points", + }, + schema: [], + }, + create(context) { + function isViewEntrypointReexportSource(value) { + return typeof value === "string" + && value.startsWith(".") + && /(?:^|\/)[^/]*-view(?:\.(?:js|tsx))?$/.test(value); + } + + return { + ExportNamedDeclaration(node) { + if (!isViewEntrypointReexportSource(node.source?.value)) { + return; + } + context.report({ + node: node.source, + message: + "Plugin server entry (src/index.ts) must not re-export dashboard view components. Use a dedicated subpath export (e.g. './dashboard-view') instead — see docs/PLUGIN_AUTHORING.md.", + }); + }, + }; + }, +}; + /** * ESLint Flat Config for Fusion Workspace * @@ -339,6 +369,23 @@ export default tseslint.config( }, }, + // ───────────────────────────────────────────────────────────── + // PLUGIN SERVER ENTRYPOINTS — keep dashboard views out of Node-loaded entries + // ───────────────────────────────────────────────────────────── + { + files: ["plugins/**/src/index.ts", "plugins/examples/**/src/index.ts"], + plugins: { + fusion: { + rules: { + "no-plugin-view-reexport": noPluginViewReexport, + }, + }, + }, + rules: { + "fusion/no-plugin-view-reexport": "error", + }, + }, + // ───────────────────────────────────────────────────────────── // AGENT SKILL TEMPLATES — template code with underscore prefix support // (agent prompt templates use _prefixed placeholders intentionally)