From 88991641cf378b32c06f319da5f1c5cf7e7c24a2 Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Tue, 29 Sep 2026 01:09:56 +0000 Subject: [PATCH] The offline packet generator runs again, and a gate now runs it e18c7f05a declared the map relabeller above LANG, which it reads: a temporal-dead-zone ReferenceError at startup, so every offline packet run stopped before building anything. node --check passed, because it checks syntax and not evaluation order, and no gate ran the generator. The block now sits after LANG and VARIANT, with a note on why. packet:ci already built the committed fixture packets through packet.mjs, with no network, in under five seconds - and it catches this exactly: exit 1 on the broken generator, exit 0 on the fix. It was in no gate list, so it never ran. It is now check:packet-offline in gates:pre. Its failure message printed the last line of stderr, which for a crash is Node's version banner ("Node.js v20.20.2"). It now names the error line: "ReferenceError: Cannot access 'LANG' before initialization". --- internal/build-packet-fixtures.mjs | 5 ++++- library/scripts/posters/packet.mjs | 21 ++++++++++++--------- package.json | 4 +++- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/internal/build-packet-fixtures.mjs b/internal/build-packet-fixtures.mjs index 2bdbc150d..dd15a2ead 100644 --- a/internal/build-packet-fixtures.mjs +++ b/internal/build-packet-fixtures.mjs @@ -63,7 +63,10 @@ for (const fx of fixtures) { }) const produced = join(OUT, `${ZONE}.html`) if (res.status !== 0 || !existsSync(produced)) { - failed.push(`${fx}: ${(res.stderr || res.stdout || '').trim().split('\n').slice(-1)[0] || 'no output'}`) + // The ERROR line, not the last line: Node ends a crash with its version banner, so the last line + // of a TDZ ReferenceError read "Node.js v20.20.2" and named nothing. + const lines = (res.stderr || res.stdout || '').trim().split('\n') + failed.push(`${fx}: ${lines.find((l) => /\b\w*Error\b/.test(l)) || lines.slice(-1)[0] || 'no output'}`) console.log(` ✗ ${fx}`) continue } diff --git a/library/scripts/posters/packet.mjs b/library/scripts/posters/packet.mjs index e1a75ed4e..fdb0c6381 100644 --- a/library/scripts/posters/packet.mjs +++ b/library/scripts/posters/packet.mjs @@ -66,15 +66,6 @@ const DATA = join(REPO, SERIES.dataDir); // which is the path zone-title.js was built for. A data set without zone-title.js keeps its labels. const ALPHA2 = JSON.parse(readFileSync(join(REPO, 'library/data/iso3-to-alpha2.json'), 'utf8')).map; const ZONE_TITLE = await import(join(DATA, 'zone-title.js')).then((m) => m.zoneTitle).catch(() => null); -// The map's country names in the packet's language (mapLabels.js), from the zone data's ISO3_NAME — the -// table generate.mjs labels the map from — with the same injected ISO-3 table for Intl. null in English. -const { makeCountryLabelFor } = await import('../../packet/mapLabels.js'); -const ISO3_NAME_TABLE = await import(join(DATA, 'territory-counts.js')).then((m) => m.ISO3_NAME).catch(() => null); -const LABEL_FOR = makeCountryLabelFor({ - iso3Name: ISO3_NAME_TABLE, - countryName: (iso3, lc) => { const two = ALPHA2[iso3]; try { return two ? new Intl.DisplayNames([lc], { type: 'region' }).of(two) : '' } catch { return '' } }, - lang: LANG, -}); const OUT = join(__dirname, 'out', 'packets'); mkdirSync(OUT, { recursive: true }); @@ -86,6 +77,18 @@ const LANG = process.env.PACKET_LANG || 'en'; // 'full', so the lightweight packet could only ever be printed from the browser. An unknown value falls // back to full, exactly as the renderer's own VARIANTS does — a typo must not print a shorter packet. const VARIANT = process.env.PACKET_VARIANT === 'lightweight' ? 'lightweight' : 'full'; +// ⚠ DECLARED AFTER LANG, NOT BESIDE THE OTHER DATA IMPORTS. It reads LANG, and the first placement +// sat above LANG's declaration: a temporal-dead-zone ReferenceError at startup that stopped every +// offline packet run, invisible to `node --check`, which checks syntax and not evaluation order. +// The map's country names in the packet's language (mapLabels.js), from the zone data's ISO3_NAME — the +// table generate.mjs labels the map from — with the same injected ISO-3 table for Intl. null in English. +const { makeCountryLabelFor } = await import('../../packet/mapLabels.js'); +const ISO3_NAME_TABLE = await import(join(DATA, 'territory-counts.js')).then((m) => m.ISO3_NAME).catch(() => null); +const LABEL_FOR = makeCountryLabelFor({ + iso3Name: ISO3_NAME_TABLE, + countryName: (iso3, lc) => { const two = ALPHA2[iso3]; try { return two ? new Intl.DisplayNames([lc], { type: 'region' }).of(two) : '' } catch { return '' } }, + lang: LANG, +}); const ONLY = process.env.ONLY || ''; const NOPHOTOS = process.env.NOPHOTOS === '1'; const API = process.env.VITE_API_BASE_URL || process.env.MAP_APP_API_URL; diff --git a/package.json b/package.json index 08937fcc0..525e94cd5 100644 --- a/package.json +++ b/package.json @@ -215,6 +215,7 @@ "check:zone-title-tier-a": "node internal/check-zone-title-tier-a.mjs", "check:evangelical-share": "node internal/check-evangelical-share.mjs", "check:map-labels": "node internal/check-map-labels.mjs", + "check:packet-offline": "node internal/build-packet-fixtures.mjs", "check:packets-all": "node internal/check-packets-all.mjs", "check:parse": "node internal/inline-parse-check.mjs", "check:pinned-umd": "node internal/check-pinned-umd.mjs", @@ -312,7 +313,7 @@ "fix:sri": "node internal/fix-sri.mjs", "gallery": "node internal/generate-gallery.mjs", "gates:post": "node internal/run-parallel.mjs --label=post -- check:capability-derived check:drop check:parse verify check:sri check:public-hygiene check:label-joins check:facet-cascade check:facet-one-field check:heartbeat check:kpi-unit check:composite-reg check:descriptions check:no-abs-paths check:no-codename check:dep-names check:composite-srcs check:embed-targets check:nested-token check:i18n-parity check:tabs-localized check:packets-all check:packet-strings-parity check:zone-census check:zoning-declared check:share-policy check:library-index check:csv-shape check:capability-citations check:catalog-identity check:no-orphan-gates check:quoted-scripts check:done-evidence check:script-flags check:backlog-status check:capability-exemplars check:packet-fixture check:packet-browser check:provider-switch check:locale-reachable check:url-resolver check:manifest-covers check:one-palette check:pin-dim-slider check:legend-source check:backlog-refs check:clustering-modes check:composite-embed-knobs check:composite-locale-leaks check:linked-vocabulary check:wp-embed check:dashboard-geocoder-provider check:leaf-engine-per-provider check:map-props-unit check:composites-mount check:composite-interop check:no-free-identifiers check:map-fly-reduced-motion check:mst-clustering check:list-prop-unit check:profiles-mount check:cdn-pins check:no-eval check:popup-escaping check:i18n-treeshake check:pinned-umd check:one-palette:self-test check:no-cross-plugin-imports check:no-cross-plugin-imports:self-test check:map-attribution check:studio-rail check:approx-pin-note check:embedder-keys check:provider-for check:legend-logical-insets check:legend-logical-insets:self-test check:optional-t-equivalence check:text-highlight check:religion-label-not-value check:i18n-stopped-scope check:no-silent-engine check:regions-uses-library check:shadow-css-parses check:square-map-controls check:preset-config-merge check:instance-config-keys check:frozen-labels check:plural-forms check:region-fill-repaint check:poster-print-fallback check:country-names check:reduced-motion check:no-dead-library", - "gates:pre": "node internal/run-parallel.mjs --label=pre -- check:template-literal check:mjs-parse check:sfc-parse check:tab-radius check:synced-css check:doc-scripts check:setup-order check:package-json-unique-keys check:no-repo-pb-hooks check:no-credentials-omit check:no-credentials-omit:self-test check:shadow-reduced-motion check:poster-action-print check:targets check:targets:self-test check:browser-gates-guarded check:pin-selection check:facet-shape check:hardcoded-english:self-test check:list-selection check:selector-fields check:upg-zone-joins check:gospel-set-charts check:gospel-set-fit check:describe-group check:one-scroll-container check:packet-charts check:composite-token-url check:language-tree-layout check:i18n-keys-exist check:packet-variants check:profile-table check:element-bootstrap check:embed-url-absolute check:frame-target check:language-tree-model check:map-menu-anchor check:selection-kinds check:tiered-points check:whiteboard-projection check:mercator-slice check:ring-vocabulary check:map-props-unit check:mobile-gating check:provide-shadowing check:legend-top-offset check:cdn-cache check:plural-forms check:select-zone-race check:script-registry check:map-control-overlap check:poster-attribution check:gospel-whiteboard check:packet-map-missing-loud check:poster-layout-optin check:ported-globals-identical check:ring-bands check:packet-a1-structure check:zone-title-tier-a check:evangelical-share check:map-labels", + "gates:pre": "node internal/run-parallel.mjs --label=pre -- check:template-literal check:mjs-parse check:sfc-parse check:tab-radius check:synced-css check:doc-scripts check:setup-order check:package-json-unique-keys check:no-repo-pb-hooks check:no-credentials-omit check:no-credentials-omit:self-test check:shadow-reduced-motion check:poster-action-print check:targets check:targets:self-test check:browser-gates-guarded check:pin-selection check:facet-shape check:hardcoded-english:self-test check:list-selection check:selector-fields check:upg-zone-joins check:gospel-set-charts check:gospel-set-fit check:describe-group check:one-scroll-container check:packet-charts check:composite-token-url check:language-tree-layout check:i18n-keys-exist check:packet-variants check:profile-table check:element-bootstrap check:embed-url-absolute check:frame-target check:language-tree-model check:map-menu-anchor check:selection-kinds check:tiered-points check:whiteboard-projection check:mercator-slice check:ring-vocabulary check:map-props-unit check:mobile-gating check:provide-shadowing check:legend-top-offset check:cdn-cache check:plural-forms check:select-zone-race check:script-registry check:map-control-overlap check:poster-attribution check:gospel-whiteboard check:packet-map-missing-loud check:poster-layout-optin check:ported-globals-identical check:ring-bands check:packet-a1-structure check:zone-title-tier-a check:evangelical-share check:map-labels check:packet-offline", "gen:iso3-to-alpha2": "node library/scripts/generate-iso3-to-alpha2.mjs", "internet:adm1": "node plugin-designs/cities-internet/scripts/build-adm1-internet.mjs", "library-index": "node internal/generate-library-index.mjs", @@ -489,6 +490,7 @@ "check:zone-title-tier-a": "generated zone titles ship for ONE-country zones only: the 3 such zones produce a localized, non-code title in en/es/ar that differs across languages, AND every other zone declines so its hand-written label survives unchanged (a multi-country zone has a name that is not a list of its members). Two-sided, and NOT MEASURED when the country-name supplier cannot resolve a known code.", "check:evangelical-share": "the packet's % evangelical page states THIS list's share: summed over every DOXA group the per-group table reproduces christian-share.json exactly; different lists give different figures; a list with no published levels prints a dash, never 0% (Number(null) is 0); the coverage clause appears only when partial.", "check:map-labels": "the packet's page-1 map names its countries in the packet's language, on a real tracked poster: English is byte-identical with and without the relabeller; in ar and zh no known English country name survives, halos match fills and the privacy line is localized; the width guard shrinks wider translations and grows nothing; Arabic drops letter tracking; an unknown label is left as drawn.", + "check:packet-offline": "the offline packet generator RUNS: it builds the committed fixture packets (no network) and fails naming the error if any build fails. node --check cannot see an evaluation-order fault - reading a const before its declaration stopped every offline run while the syntax check passed.", "check:packets-all": "runs check:packet-render over EVERY generated packet, not one hand-named file. The single-file gate SKIPs with no argument, so it was in package.json, passing in all 11 locales, and in neither gates:pre nor gates:post — it only ran when someone typed a filename. SKIPs when no packets are generated.", "check:pin-dim-slider": "the side menu's pin-dim slider and the legend's dimming must COMPOSE, not compete. Both want the pin layer's circle-opacity, so there is ONE writer and the value is the PRODUCT: slider x (1 matched, 0 dimmed). Asserts the LIVE paint property, not the store: flat 1 with clustering off, flat 0.5 with clustering on, ['case', expr, 0.5, 0] once something is dimmed, and ['case', expr, 1, 0] when the slider returns to 1. Dims by RELIGION deliberately - a region selection sets a layer FILTER rather than an opacity expression, so testing that path would report a failure about code that never writes opacity.", "check:pinned-umd": "library/utils/loadPinnedUmd.js (d3 for PPLR and the Wall) loads a pinned UMD with SRI and never touches the host page's global of the same name: a host's own d3 gains no keys, a failed load leaves it in place. Headless.",