isMap per profile: a map-less leaf no longer bakes, or preloads, a map engine it never uses
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions

generate-tree decided a single leaf's map-ness as "its own .vue
references an engine OR anything under the BUNDLE's src/ does", so a
map-less profile inherited it from a sibling. regions-dashboard/
regions-list shows a list and no map, but src/RegionCountryMap.vue made
it isMap=true. Its leaf loaded the Mapbox engine for nothing, and since
4a6886d2 its embed snippet also preloaded it.

A leaf's map-ness now follows the RELATIVE imports reachable from its
own .vue, bounded to its bundle, so a map several imports deep
(research-dashboard's DashboardMapPanel) still counts. An explicit
boolean isMap in <profile>.meta.json wins. Composites keep their
separate rule, since they do mount their siblings.

Measured: across all 34 profiles exactly one changed (regions-list,
true -> false). 18 map profiles keep isMap and their preload; 0 non-map
snippets carry one. On a served built tree regions-list makes 0 engine
requests and 0 api.mapbox.com requests and renders its list (673
elements, no errors), where before it downloaded the 1.75 MB engine.
regions-mfe still loads the engine and draws its map. The regenerated
files are only the regions-list leaf, the two manifest copies (still
byte-identical), the staging page and the gallery; check:sri passes.
This commit is contained in:
doxa-maps-88655c70 2026-09-25 10:17:33 +00:00
parent da8bc790b8
commit 413fa30d19
6 changed files with 56 additions and 18 deletions

View file

@ -271,6 +271,47 @@ function ownsAMapDeeply(profileDir) {
return false
}
/**
* Does THIS PROFILE reach a map through its own import graph?
*
* ★ PER PROFILE, NOT PER BUNDLE (BUG-FALSE-ISMAP-BAKES-403KB-MAPBOX-INTO-MAPLESS-LEAVES, fixed
* 2026-09-25). ownsAMapDeeply() answers "is there a map ANYWHERE under this bundle's src/", and the
* single-leaf branch used it for every profile — so a map-less sibling inherited map-ness from the
* bundle: regions-dashboard/regions-list shows a list and no map, but src/RegionCountryMap.vue made
* it isMap=true. Its leaf then baked the ~400 KB Mapbox engine it never uses, and since 4a6886d2 its
* embed snippet also preloaded it. This follows the RELATIVE imports reachable from the profile's
* own .vue (bounded to the bundle folder, .vue/.js/.mjs), so research-dashboard — whose map sits
* several imports deep in src/components/dashboards/DashboardMapPanel.vue — still resolves true.
* An explicit boolean `isMap` in <profile>.meta.json wins over the scan.
*/
const RELATIVE_IMPORT_RE = /(?:import[^'"`;]*?from\s*|import\s*\(\s*|import\s+)['"`](\.{1,2}\/[^'"`]+)['"`]/g
function profileReachesAMap(dir, file) {
const seen = new Set()
const stack = [join(dir, file)]
while (stack.length) {
const f = stack.pop()
if (seen.has(f)) continue
seen.add(f)
let src
try { src = readFileSync(f, 'utf8') } catch { continue }
if (MAP_ENGINE_RE.test(src)) return true
for (const m of src.matchAll(RELATIVE_IMPORT_RE)) {
const base = resolve(dirname(f), m[1])
if (!base.startsWith(dir)) continue // stay inside this bundle
for (const cand of [base, base + '.vue', base + '.js', base + '.mjs', join(base, 'index.js')]) {
if (NESTED_SCAN_EXT.test(cand) && existsSync(cand) && statSync(cand).isFile()) { stack.push(cand); break }
}
}
}
return false
}
function metaIsMap(dir, profileName) {
try {
const meta = JSON.parse(readFileSync(join(dir, `${profileName}.meta.json`), 'utf8'))
return typeof meta?.isMap === 'boolean' ? meta.isMap : null
} catch { return null }
}
/**
* Does a cap-4 composite (`nested-elements-mfe`) compose a child that paints a map?
*
@ -1206,7 +1247,7 @@ function scanBundles() {
? false
: profileType === 'nested-elements-mfe'
? composesAMapLeaf(dir, profileName, file)
: (isMapLeaf(join(dir, file)) || ownsAMapDeeply(dir))
: (metaIsMap(dir, profileName) ?? profileReachesAMap(dir, file))
return {
profile: profileName,

File diff suppressed because one or more lines are too long

View file

@ -837,7 +837,7 @@
"bundle": "pplr-data-maps",
"scriptUrl": "./pplr-data-maps/pplr-data-maps.js",
"output": "doxa-maps/pplr-data-maps/pplr-data-maps.js",
"integrity": "sha384-7Fp8ORq9SNmtZLwCG1euRM+dGy6sDgD/+Ts4s1B1WVk8XKuMJpYr3dCjoK80zvJp",
"integrity": "sha384-+EOcMT3P42D39HGIjZBbJ6waYI2yJDhNDwNf+8wFruaHqEhdYYa36M+iLIT5fRiH",
"elements": [
"pplr-data-maps"
],
@ -853,7 +853,7 @@
"scriptUrl": "./pplr-data-maps/pplr-data-maps.js",
"embed": {
"iframe": "<iframe src=\"./pplr-data-maps/pplr-data-maps/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./pplr-data-maps/pplr-data-maps.js\" integrity=\"sha384-7Fp8ORq9SNmtZLwCG1euRM+dGy6sDgD/+Ts4s1B1WVk8XKuMJpYr3dCjoK80zvJp\" crossorigin=\"anonymous\"></script>\n<pplr-data-maps profile-config='{\"profile\":\"pplr-data-maps\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></pplr-data-maps>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./pplr-data-maps/pplr-data-maps.js\" integrity=\"sha384-+EOcMT3P42D39HGIjZBbJ6waYI2yJDhNDwNf+8wFruaHqEhdYYa36M+iLIT5fRiH\" crossorigin=\"anonymous\"></script>\n<pplr-data-maps profile-config='{\"profile\":\"pplr-data-maps\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></pplr-data-maps>"
},
"bundle": "pplr-data-maps"
}
@ -917,12 +917,12 @@
"description": "A refactor of DX-life's /regions index page (app/pages/regions/index.vue) into",
"element": "regions-dashboard",
"type": "single",
"isMap": true,
"isMap": false,
"iframeUrl": "./regions-dashboard/regions-list/index.html",
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"embed": {
"iframe": "<iframe src=\"./regions-dashboard/regions-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-bt2/psRmAJTmO9mm3KULoLN7GSzcxwIeOTbVwIvSkMZOqyzaU5pOt7G0r2w8ir9r\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-bt2/psRmAJTmO9mm3KULoLN7GSzcxwIeOTbVwIvSkMZOqyzaU5pOt7G0r2w8ir9r\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
}
},
{

File diff suppressed because one or more lines are too long

View file

@ -917,12 +917,12 @@
"description": "A refactor of DX-life's /regions index page (app/pages/regions/index.vue) into",
"element": "regions-dashboard",
"type": "single",
"isMap": true,
"isMap": false,
"iframeUrl": "./regions-dashboard/regions-list/index.html",
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"embed": {
"iframe": "<iframe src=\"./regions-dashboard/regions-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-bt2/psRmAJTmO9mm3KULoLN7GSzcxwIeOTbVwIvSkMZOqyzaU5pOt7G0r2w8ir9r\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-bt2/psRmAJTmO9mm3KULoLN7GSzcxwIeOTbVwIvSkMZOqyzaU5pOt7G0r2w8ir9r\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
}
},
{

View file

@ -5,13 +5,10 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>regions-list · regions-dashboard</title>
<link rel="icon" href="data:,">
<!-- Mapbox peer loaded ONCE here; the map custom element mounts in this page and
uses window.mapboxgl directly (same realm → no cross-realm container errors). -->
<link rel="preconnect" href="https://api.mapbox.com" crossorigin />
<link href="https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css" rel="stylesheet" />
<link href="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.0/mapbox-gl-geocoder.css" rel="stylesheet" />
<script src="https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js"></script>
<script src="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.0/mapbox-gl-geocoder.min.js"></script>
<!-- NON-MAP leaf (app-shell / composite / grid / card / chart-table): NO map peer.
This element draws no map tiles, so it needs no Mapbox/MapLibre engine and MUST NOT
gate on a token. Any nested MAP (in a composite/shell) renders in its own iframe leaf,
which loads its own map peer + token. A peer here would be dead weight + imply a gate. -->
<style>
/* BARE full-page map — no chrome. The custom element IS the page, so the
browser URL shares the map itself. */
@ -25,7 +22,7 @@
</head>
<body>
<div id="slot"></div>
<div id="status">loading token…</div>
<div id="status">loading…</div>
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
@ -48,7 +45,7 @@
var BASE_CONFIG = {"pinHint":false,"profile":"regions-list","dataSource":"pray-tools","instanceId":"regions-dashboard-regions-list","assetsBase":"../../assets"};
// MapLibre map pages AND shell pages (app-shell / nested-mfe-spa) need NO token:
// the ladder below is skipped entirely and the element mounts immediately.
var TOKEN_FREE = false;
var TOKEN_FREE = true;
// A token-free COMPOSITE still nests plugins, and a nested Mapbox map reads window.MAP_TOKEN
// rather than this page's profile-config. With no baked token (the public copy), it must get
// one from ?tk= or a pasted token, or be asked for one — see the rungs below.