check:one-palette reads only the PALETTE export's values, not every hex to the end of the file
It claimed to read "only the PALETTE export" but matched every hex from `export const PALETTE` to the END OF THE FILE, comments included. A later note such as "REJECTED #AF7AC5, scored 2.95, under the 3.0 floor" (regions' affinity-block.js, being prepared) would have obliged every dashboard to ship a colour rejected on WCAG grounds. paletteHexes() now slices to the export's matching closing brace and reads hexes only inside string literals. --self-test (check:one-palette:self-test, gates:post) plants a hex in an inline comment, a block comment, a trailing comment and a later export, and requires only the two real values back. The verdict on main is unchanged (PASS, 6 palettes).
This commit is contained in:
parent
20c29442e8
commit
06ea24c98a
2 changed files with 52 additions and 7 deletions
|
|
@ -63,17 +63,61 @@ if (!existsSync(BUILT)) {
|
|||
process.exit(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* The hexes of the PALETTE export ONLY: from `export const PALETTE` to its matching closing brace, and
|
||||
* only inside string literals. It used to match every hex from the export to the END OF THE FILE,
|
||||
* comments included, so a later note such as "REJECTED #AF7AC5, scored 2.95, under the 3.0 floor"
|
||||
* (affinity-block.js) obliged every consumer to ship a colour rejected on WCAG grounds.
|
||||
* Returns null when the module has no PALETTE export.
|
||||
*/
|
||||
export function paletteHexes(src) {
|
||||
const at = src.indexOf('export const PALETTE')
|
||||
if (at < 0) return null
|
||||
const open = src.indexOf('{', at), openArr = src.indexOf('[', at)
|
||||
const start = [open, openArr].filter((i) => i >= 0).sort((a, b) => a - b)[0]
|
||||
if (start === undefined) return []
|
||||
const close = { '{': '}', '[': ']' }[src[start]]
|
||||
let depth = 0, i = start, quote = null
|
||||
const strings = []
|
||||
let cur = ''
|
||||
for (; i < src.length; i++) {
|
||||
const c = src[i], n = src[i + 1]
|
||||
if (quote) {
|
||||
if (c === '\\') { cur += c + n; i++; continue }
|
||||
if (c === quote) { strings.push(cur); cur = ''; quote = null; continue }
|
||||
cur += c; continue
|
||||
}
|
||||
if (c === '/' && n === '/') { i = src.indexOf('\n', i); if (i < 0) break; continue }
|
||||
if (c === '/' && n === '*') { i = src.indexOf('*/', i + 2) + 1; if (i <= 0) break; continue }
|
||||
if (c === "'" || c === '"' || c === '`') { quote = c; continue }
|
||||
if (c === src[start]) depth++
|
||||
else if (c === close && --depth === 0) break
|
||||
}
|
||||
return [...new Set(strings.flatMap((t) => [...t.matchAll(/#[0-9a-fA-F]{6}\b/g)].map((m) => m[0].toLowerCase())))]
|
||||
}
|
||||
|
||||
if (process.argv.includes('--self-test')) {
|
||||
const src = [
|
||||
"export const PALETTE = {",
|
||||
" a: '#112233', // not #445566 (a comment inside the export)",
|
||||
" /* nor #778899 */ b: \"#aabbcc\",",
|
||||
"}",
|
||||
"// REJECTED #af7ac5, scored 2.95, under the 3.0 floor",
|
||||
"export const OTHER = { c: '#ddeeff' }",
|
||||
].join('\n')
|
||||
const got = paletteHexes(src).join(',')
|
||||
if (got !== '#112233,#aabbcc') { console.error(`ONE-PALETTE: SELF-TEST FAIL — read ${got}, want #112233,#aabbcc`); process.exit(1) }
|
||||
console.log('ONE-PALETTE: SELF-TEST PASS — only the PALETTE export\'s string values are read; comments and later code are not.')
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
// ── the expected palettes, read from the strategy modules the MAP uses ────────────────────────
|
||||
const strategies = {}
|
||||
for (const f of readdirSync(COLORS)) {
|
||||
if (!f.endsWith('.js') || f.startsWith('_') || f.startsWith('.')) continue
|
||||
const src = readFileSync(join(COLORS, f), 'utf8')
|
||||
// Only the PALETTE export — a module may also contain incidental hexes (a default, a comment)
|
||||
// that no consumer is obliged to ship.
|
||||
const at = src.indexOf('export const PALETTE')
|
||||
if (at < 0) continue
|
||||
const hexes = [...new Set([...src.slice(at).matchAll(/#[0-9a-fA-F]{6}/g)].map((m) => m[0].toLowerCase()))]
|
||||
if (hexes.length) strategies[f.replace(/\.js$/, '')] = hexes
|
||||
const hexes = paletteHexes(src)
|
||||
if (hexes && hexes.length) strategies[f.replace(/\.js$/, '')] = hexes
|
||||
}
|
||||
|
||||
// ── the consumers that must agree ─────────────────────────────────────────────────────────────
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@
|
|||
"check:no-orphan-gates": "node internal/check-no-orphan-gates.mjs",
|
||||
"check:no-public-binds": "node internal/check-no-public-binds.mjs",
|
||||
"check:one-palette": "node internal/check-one-palette.mjs",
|
||||
"check:one-palette:self-test": "node internal/check-one-palette.mjs --self-test",
|
||||
"check:packet-browser": "node internal/check-packet-browser.mjs",
|
||||
"check:packet-fixture": "node internal/generate-packet-fixture.mjs --check",
|
||||
"check:packet-render": "node internal/check-packet-render.mjs",
|
||||
|
|
@ -199,7 +200,7 @@
|
|||
"dev": "bun x vite",
|
||||
"fix:sri": "node internal/fix-sri.mjs",
|
||||
"gallery": "node internal/generate-gallery.mjs",
|
||||
"gates:post": "node internal/run-parallel.mjs --label=post -- 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: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",
|
||||
"gates:post": "node internal/run-parallel.mjs --label=post -- 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: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",
|
||||
"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",
|
||||
"library-index": "node internal/generate-library-index.mjs",
|
||||
"lists": "node library/scripts/people-groups/build-lists.mjs",
|
||||
|
|
|
|||
Loading…
Reference in a new issue