From 9061065558871dce672250d810bb84491637af0a Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Tue, 29 Sep 2026 12:31:08 +0000 Subject: [PATCH] The two composites that draw a map get the map snippets: a MapLibre preload and a Mapbox variant research-map-plus-grid and regions-mfe draw a map through a child element, so their isMap is false, and their published snippets had no engine preload and no scriptMapbox. A pasted composite still draws on Mapbox given provider "mapbox" and a token (measured), so this is the snippet's convenience, not a broken map. generate-tree now decides after every profile's isMap is known: a composite (nested-elements-mfe) whose OWN composes reach a map profile gets the map form of the default snippet and the Mapbox variant. It reads the profile's own composes, never the bundle's hoisted list, which the bundle's plain leaves (charts, data-tables, the poster wall) share while drawing nothing. The gallery shell is left out until its passing of provider to its tabs is measured. Regenerated: exactly those two profiles' embed changes, in both manifests; nothing internal leaks into the output. check:snippet-engine asserts the same rule on the built manifest (21 map snippets, 19 maps + 2 composites). Its self-test adds a composite stripped of its variant, which fails. Against the manifest as it stood before this change, the widened gate fails on exactly these two composites; on the regenerated one it passes. --- internal/check-snippet-engine.mjs | 26 ++++++++++++++++++---- internal/generate-tree.mjs | 23 ++++++++++++++++++- public/dx-maps/doxa-maps-spa/manifest.json | 6 +++-- public/dx-maps/manifest.json | 6 +++-- 4 files changed, 52 insertions(+), 9 deletions(-) diff --git a/internal/check-snippet-engine.mjs b/internal/check-snippet-engine.mjs index 5556d0b68..d50b06310 100644 --- a/internal/check-snippet-engine.mjs +++ b/internal/check-snippet-engine.mjs @@ -9,10 +9,13 @@ * from a keyless page. Its only guard was a comment asking the partner to delete the line. * * Reads the BUILT manifest (public//manifest.json) and asserts, for every profile: - * a map profile embed.script names no api.mapbox.com URL and preloads MapLibre (its script pin); + * a profile that DRAWS A MAP (isMap, or a composite whose bundle composes a map profile: the map is + * its child, as in research-map-plus-grid and regions-mfe) + * embed.script names no api.mapbox.com URL and preloads MapLibre (its script pin); * embed.scriptMapbox preloads the Mapbox engine AND sets "provider":"mapbox" * any other profile embed.script names no api.mapbox.com URL, and there is no scriptMapbox - * `--self-test` plants the old snippet and a variant that forgot its provider; both must fail. + * `--self-test` plants the old snippet, a variant that forgot its provider, and a map-drawing composite + * stripped of its variant; each must fail. * Exit 0 pass · 1 fail · 2 no manifest. */ import { readFileSync, existsSync } from 'node:fs' @@ -25,15 +28,27 @@ const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..') const MANIFEST = join(ROOT, 'public', TREE_NAME, 'manifest.json') const MAPBOX_HOST = 'api.mapbox.com' +// A profile DRAWS A MAP if it is one, or if it is a composite (nested-elements-mfe) whose bundle composes a +// map profile: research-map-plus-grid and regions-mfe have isMap false because their map is a child, and +// they draw on Mapbox when pasted with provider "mapbox". A bundle's plain leaves (type single) share its +// composes list but draw nothing, so the type is part of the rule. Same rule as generate-tree's composite pass. +export function drawsMapOf(manifest) { + const isMap = new Map() + for (const b of manifest.bundles || []) for (const p of b.profiles || []) isMap.set(`${b.bundle}/${p.profile}`, !!p.isMap) + const reaches = (ref) => isMap.get(ref) || [...isMap].some(([k, v]) => v && k.startsWith(`${ref}/`)) + return (b, p) => !!p.isMap || (p.type === 'nested-elements-mfe' && (b.composes || []).some((r) => reaches(String(r)))) +} + export function checkSnippets(manifest) { const errs = [] let maps = 0 + const drawsMap = drawsMapOf(manifest) for (const b of manifest.bundles || []) { for (const p of b.profiles || []) { const id = `${b.bundle}/${p.profile}` const script = String(p.embed?.script || '') if (script.includes(MAPBOX_HOST)) errs.push(`${id}: the default snippet names ${MAPBOX_HOST} — an unedited paste would fetch an engine it never uses`) - if (p.isMap) { + if (drawsMap(b, p)) { maps++ if (!script.includes(CDN.maplibreGlJs.url)) errs.push(`${id}: the default snippet does not preload MapLibre, the engine it draws with`) const mb = String(p.embed?.scriptMapbox || '') @@ -56,7 +71,10 @@ if (process.argv.includes('--self-test')) { const firstMap = (m) => m.bundles.flatMap((b) => b.profiles).find((p) => p.isMap) const oldShape = clone(); { const p = firstMap(oldShape); p.embed.script = `\n` + p.embed.script.replace(/ b.profiles.map((p) => [b, p])).find(([b, p]) => !p.isMap && draws(b, p)); if (hit) delete hit[1].embed.scriptMapbox } + const cases = [['the built tree passes', manifest, 0], ['the old Mapbox-preloading default fails', oldShape, 1], ['a Mapbox variant without its provider fails', noProvider, 1], + ['a map-drawing composite without its Mapbox variant fails', composite, 1]] let ok = true for (const [name, m, want] of cases) { const n = checkSnippets(m).errs.length diff --git a/internal/generate-tree.mjs b/internal/generate-tree.mjs index b030f7064..41ec0dec8 100644 --- a/internal/generate-tree.mjs +++ b/internal/generate-tree.mjs @@ -1469,6 +1469,9 @@ function scanBundles() { // consumers but useless for deriving a tier — a bundle's composite and its plain leaves would // all inherit the same edges. The tier is a property of the profile that declares them. composesOwn: readComposes(dir, profileName), + // Generator-internal, stripped with the two above: what the snippets are built from, so the + // composite pass below can rebuild them once every profile's isMap is known. + _snippet: { cfg: embedCfg, build: scriptEmbedFor, mapNote: MAP_NOTE, mapboxNote: MAPBOX_NOTE }, ...rest, } }) @@ -1552,6 +1555,24 @@ function writeManifestAndHtml(root, bundles, tokenUrlExpr, bakedToken) { } } + // ★ A COMPOSITE THAT DRAWS ITS MAP THROUGH A CHILD GETS THE MAP SNIPPETS TOO. isMap is false on + // research-map-plus-grid and regions-mfe, because their map is a child element, so they published no + // engine preload and no Mapbox variant, though both draw a map and pass provider to it (pasted with + // provider "mapbox" and a token, both draw on Mapbox: measured). Decided here, once every profile's + // isMap exists, and from each profile's OWN composes, never the bundle's hoisted list: a bundle's + // plain leaves (charts, data-tables, a poster wall) share that list and must stay plain. The gallery + // shell (nested-mfe-spa) is left out until its passing of provider to its tabs is measured. + // check:snippet-engine asserts the same rule on the written manifest. + const isMapById = new Map() + for (const b of bundles) for (const p of b.profiles) isMapById.set(`${b.bundle}/${p.profile}`, !!p.isMap) + const reachesMap = (ref) => isMapById.get(ref) || [...isMapById].some(([k, v]) => v && k.startsWith(`${ref}/`)) + for (const b of bundles) for (const p of b.profiles) { + if (p.isMap || p.type !== 'nested-elements-mfe' || !(p.composesOwn || []).some((r) => reachesMap(String(r)))) continue + const s = p._snippet + p.embed.script = `\n${enginePreload('maplibre')}` + s.build(s.cfg, s.mapNote) + p.embed.scriptMapbox = `\n${enginePreload('mapbox')}` + s.build({ ...s.cfg, provider: 'mapbox' }, s.mapboxNote) + } + const manifest = { schema: 'doxa-maps-manifest@1', generatedFrom: 'plugin-designs/*/*.vue', @@ -1576,7 +1597,7 @@ function writeManifestAndHtml(root, bundles, tokenUrlExpr, bakedToken) { // Consumers read the manifest as a contract; a new key would be a silent schema change. bundles: bundles.map((b) => ({ ...b, - profiles: b.profiles.map(({ extraConfig, composesOwn, ...p }) => p), + profiles: b.profiles.map(({ extraConfig, composesOwn, _snippet, ...p }) => p), })), } mkdirSync(root, { recursive: true }) diff --git a/public/dx-maps/doxa-maps-spa/manifest.json b/public/dx-maps/doxa-maps-spa/manifest.json index 0d6569f07..c581ff6e5 100644 --- a/public/dx-maps/doxa-maps-spa/manifest.json +++ b/public/dx-maps/doxa-maps-spa/manifest.json @@ -1182,7 +1182,8 @@ "scriptUrl": "./regions-dashboard/regions-dashboard.js", "embed": { "iframe": "", - "script": "\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" } } ] @@ -1244,7 +1245,8 @@ "scriptUrl": "./research-map-plus-grid/research-map-plus-grid.js", "embed": { "iframe": "", - "script": "\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" }, "instances": [ { diff --git a/public/dx-maps/manifest.json b/public/dx-maps/manifest.json index 0d6569f07..c581ff6e5 100644 --- a/public/dx-maps/manifest.json +++ b/public/dx-maps/manifest.json @@ -1182,7 +1182,8 @@ "scriptUrl": "./regions-dashboard/regions-dashboard.js", "embed": { "iframe": "", - "script": "\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" } } ] @@ -1244,7 +1245,8 @@ "scriptUrl": "./research-map-plus-grid/research-map-plus-grid.js", "embed": { "iframe": "", - "script": "\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" }, "instances": [ {