The dev server's manifest carries stage, heavy and widget, so Rudder's corner mounts under bun run dev
rega (7a7ca67b): on the dev server the corner widget never mounted, because /__plugin-gallery/manifest.json
built its profiles without the .meta.json fields only generate-tree read. Both now read them through one
module, internal/profile-meta.mjs (stage, heavy, widget per profile; bundle stage = production if any
profile says so), so the dev Studio and the built Studio cannot disagree. Verified: the dev model lists the
same 7 production bundles and rudder-voice:bottom-right; the built manifest is unchanged.
This commit is contained in:
parent
17763cdccc
commit
e53495dfcf
3 changed files with 41 additions and 25 deletions
|
|
@ -44,6 +44,7 @@ import { createHash } from 'node:crypto'
|
|||
// without a separate tierMap. Previously the manifest carried no `type`, so a manifest-fed
|
||||
// consumer (plugin-studio) mis-tiered every profile as cap-1 (single). Single source of truth.
|
||||
import { detectProfileType } from './plugin-gallery/discover.mjs'
|
||||
import { profileMetaFields, bundleStage } from './profile-meta.mjs'
|
||||
import { TREE_NAME as CONFIGURED_TREE_NAME } from './_tree-name.mjs'
|
||||
import { syncAppShells } from './sync-app-shells.mjs'
|
||||
|
||||
|
|
@ -301,25 +302,6 @@ function profileReachesAMap(dir, file) {
|
|||
}
|
||||
return false
|
||||
}
|
||||
/**
|
||||
* STAGE and HEAVY, read from <profile>.meta.json and carried into manifest.json (the Driver, 2026-09-27:
|
||||
* a public-but-unfinished plugin shows under EXPERIMENTAL in the Plugin Studio and the gallery until it works
|
||||
* as a production map). Both surfaces read the MANIFEST, so they cannot disagree, and neither keeps a list.
|
||||
* "stage": "production" | "experimental" default experimental; a bundle is production if ANY of its
|
||||
* profiles says so (a bundle is one folder in the studio)
|
||||
* "heavy": true the element holds live WebGL contexts, so a host must destroy
|
||||
* it when it leaves the screen (Plugin Studio's HEAVY rows)
|
||||
*/
|
||||
function metaStageHeavy(dir, profileName) {
|
||||
try {
|
||||
const meta = JSON.parse(readFileSync(join(dir, `${profileName}.meta.json`), 'utf8'))
|
||||
const corner = meta?.widget?.corner
|
||||
return { stage: meta?.stage === 'production' ? 'production' : 'experimental', heavy: meta?.heavy === true,
|
||||
// "widget": { "corner": "bottom-right" } — a corner widget the Plugin Studio mounts once, fixed, not in a pane
|
||||
widget: ['bottom-right', 'bottom-left', 'top-right', 'top-left'].includes(corner) ? { corner } : null }
|
||||
} catch { return { stage: 'experimental', heavy: false, widget: null } }
|
||||
}
|
||||
|
||||
function metaIsMap(dir, profileName) {
|
||||
try {
|
||||
const meta = JSON.parse(readFileSync(join(dir, `${profileName}.meta.json`), 'utf8'))
|
||||
|
|
@ -1305,12 +1287,9 @@ function scanBundles() {
|
|||
? composesAMapLeaf(dir, profileName, file)
|
||||
: (metaIsMap(dir, profileName) ?? profileReachesAMap(dir, file))
|
||||
|
||||
const { stage: profileStage, heavy: profileHeavy, widget: profileWidget } = metaStageHeavy(dir, profileName)
|
||||
return {
|
||||
profile: profileName,
|
||||
stage: profileStage,
|
||||
...(profileHeavy ? { heavy: true } : {}),
|
||||
...(profileWidget ? { widget: profileWidget } : {}),
|
||||
...profileMetaFields(dir, profileName), // stage / heavy / widget, shared with the dev manifest
|
||||
file,
|
||||
description: description || fileTag || '',
|
||||
element: tag,
|
||||
|
|
@ -1364,7 +1343,7 @@ function scanBundles() {
|
|||
integrity,
|
||||
elements: bundleElements,
|
||||
...(composesUnique.length ? { composes: composesUnique } : {}),
|
||||
stage: profiles.some((p) => p.stage === 'production') ? 'production' : 'experimental',
|
||||
stage: bundleStage(profiles),
|
||||
profiles,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import { readdirSync, statSync, existsSync, readFileSync } from 'node:fs'
|
|||
import { REPO_ROOT, discoverSpaRegistry } from './discover.mjs'
|
||||
import { readBuildConfig, renderGallery } from '../generate-gallery.mjs'
|
||||
import { TREE_NAME } from '../_tree-name.mjs'
|
||||
import { profileMetaFields, bundleStage } from '../profile-meta.mjs'
|
||||
|
||||
// ─── DEV plugin gallery ──────────────────────────────────────────────────────
|
||||
// `bun x vite` (dev) serves the plugin gallery at `/` — the same page `bun run build`
|
||||
|
|
@ -115,6 +116,7 @@ export function galleryModel(reg) {
|
|||
const p = {
|
||||
profile, file: `${profile}.vue`, description: '', element: b.tag,
|
||||
type: typeOf.get(profile) || 'single',
|
||||
...profileMetaFields(resolve(REPO_ROOT, 'plugin-designs', b.bundle), profile), // same as the built manifest
|
||||
iframeUrl: link(b.bundle, profile), scriptUrl,
|
||||
embed: {
|
||||
iframe: iframe(`./${b.bundle}/${profile}/index.html`),
|
||||
|
|
@ -135,7 +137,7 @@ export function galleryModel(reg) {
|
|||
// to drop them, so under the dev server — which is what the Driver's laptop runs — the studio's
|
||||
// Composed-of panel and composition trees were silently empty for every composite.
|
||||
const composes = [...new Set([...presets.keys()].flatMap((profile) => readComposes(b.bundle, profile)))]
|
||||
return { bundle: b.bundle, scriptUrl, integrity: '', elements: [b.tag], ...(composes.length ? { composes } : {}), profiles }
|
||||
return { bundle: b.bundle, scriptUrl, integrity: '', elements: [b.tag], stage: bundleStage(profiles), ...(composes.length ? { composes } : {}), profiles }
|
||||
})
|
||||
return {
|
||||
schema: 'doxa-maps-manifest@1', generatedFrom: 'plugin-designs/*/*.vue (dev discovery)', embedBase: '.',
|
||||
|
|
|
|||
35
internal/profile-meta.mjs
Normal file
35
internal/profile-meta.mjs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* profile-meta.mjs — the per-profile fields a host reads from <profile>.meta.json, in ONE place.
|
||||
*
|
||||
* generate-tree (the built manifest.json) and the dev server's /__plugin-gallery/manifest.json both call
|
||||
* this, so the dev Studio and the built Studio cannot disagree (rega: the corner widget never mounted in
|
||||
* dev because only the built manifest carried "widget").
|
||||
* stage "production" | "experimental" default experimental
|
||||
* heavy true when the element holds live WebGL contexts
|
||||
* widget { corner } for a corner widget the Plugin Studio mounts once, fixed
|
||||
*/
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
|
||||
const CORNERS = ['bottom-right', 'bottom-left', 'top-right', 'top-left']
|
||||
|
||||
export function readProfileMeta(dir, profileName) {
|
||||
try {
|
||||
const meta = JSON.parse(readFileSync(join(dir, `${profileName}.meta.json`), 'utf8'))
|
||||
const corner = meta?.widget?.corner
|
||||
return {
|
||||
stage: meta?.stage === 'production' ? 'production' : 'experimental',
|
||||
heavy: meta?.heavy === true,
|
||||
widget: CORNERS.includes(corner) ? { corner } : null,
|
||||
}
|
||||
} catch { return { stage: 'experimental', heavy: false, widget: null } }
|
||||
}
|
||||
|
||||
/** The optional manifest keys for one profile (stage always; heavy / widget only when set). */
|
||||
export function profileMetaFields(dir, profileName) {
|
||||
const m = readProfileMeta(dir, profileName)
|
||||
return { stage: m.stage, ...(m.heavy ? { heavy: true } : {}), ...(m.widget ? { widget: m.widget } : {}) }
|
||||
}
|
||||
|
||||
/** A bundle is production if ANY of its profiles says so. */
|
||||
export const bundleStage = (profiles) => (profiles.some((p) => p.stage === 'production') ? 'production' : 'experimental')
|
||||
Loading…
Reference in a new issue