From d009ac94fb1d782aae96f61ed56af64f99254c42 Mon Sep 17 00:00:00 2001 From: cproudlock Date: Sun, 19 Jul 2026 12:10:40 -0400 Subject: [PATCH] Lean build: always ship core (manifest-less) frontends A frontend dir under plugins/ with no manifest.json is a CORE feature, not a per-site plugin - applications is one (backend is shopdb/core/api/applications.py, nav is advertised as core in dashboard.py). stage-frontend.mjs treated it like a plugin and dropped it under SITE_PLUGINS, so a lean site showed the core Applications nav item but had no route for it -> blank page. Now manifest-less frontends always stage regardless of SITE_PLUGINS; SITE_PLUGINS selection applies only to real plugins. CI lean-build job asserts ApplicationsList ships in a lean bundle. Found while testing a live machines+printers lean site. --- .github/workflows/ci.yml | 5 +++++ scripts/stage-frontend.mjs | 19 +++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 605cb36..d0d4c6a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,6 +82,11 @@ jobs: echo "FAIL: chosen-plugin code '$code' missing from the lean bundle" exit 1; } done + # Core frontends (no manifest, e.g. applications) must ship in EVERY + # build regardless of SITE_PLUGINS, or a lean site loses a core page. + grep -rqoh "ApplicationsList" "$assets"/*.js || { + echo "FAIL: core page 'ApplicationsList' missing from the lean bundle" + exit 1; } test -d /tmp/leansite/plugins/machines test ! -d /tmp/leansite/plugins/printedparts echo "lean build verified: only chosen plugins present" diff --git a/scripts/stage-frontend.mjs b/scripts/stage-frontend.mjs index a38bb6c..56fb386 100644 --- a/scripts/stage-frontend.mjs +++ b/scripts/stage-frontend.mjs @@ -5,7 +5,9 @@ // plugins' frontend dirs into frontend/src/.plugins-staged// and codegens // frontend/src/router/routes.gen.js, which the router imports. A per-site build // selects plugins via the SITE_PLUGINS env (comma-separated); with none set, -// every plugin that has a frontend/ is staged (the default full build). +// every plugin that has a frontend/ is staged (the default full build). A +// frontend dir with no manifest.json is a CORE feature and is ALWAYS staged +// regardless of SITE_PLUGINS. // // Both outputs are generated (gitignored). Run by npm predev/prebuild. @@ -26,9 +28,18 @@ const withFrontend = readdirSync(pluginsDir, { withFileTypes: true }) && existsSync(join(pluginsDir, entry.name, 'frontend', 'routes.js'))) .map(entry => entry.name) -const chosen = requested.length - ? withFrontend.filter(name => requested.includes(name)) - : withFrontend +// A frontend dir with no manifest.json is a CORE feature (e.g. applications), +// not a per-site plugin - it MUST always ship, or a lean build loses a core +// page (its nav is advertised by the core, so the route would 404 to a blank +// screen). SITE_PLUGINS selection applies only to real plugins (those with a +// manifest). +const isPlugin = name => existsSync(join(pluginsDir, name, 'manifest.json')) +const coreFrontend = withFrontend.filter(name => !isPlugin(name)) +const pluginFrontend = withFrontend.filter(isPlugin) +const chosenPlugins = requested.length + ? pluginFrontend.filter(name => requested.includes(name)) + : pluginFrontend +const chosen = [...coreFrontend, ...chosenPlugins] // Copy each chosen plugin's frontend dir into the staging area (clean first so // a removed plugin does not linger).