check:shadow-css-parses reads the style id: it must be a plain identifier
gospel-plugin's trap (COMMON.md): a CSS rule moved past the first argument of useShadowStyles concatenates
onto the style id ('@media(...){...}doxa-gospel-map'). That is valid JS, so the build stays green and the rule
is silently lost; the brace-balance scan never sees it because the id is not CSS. The walker now folds the
second argument too, and a literal id must match /^[a-z0-9-]+$/ (a computed id is left alone). Measured on
the current tree first: 0 offenders, so this breaks no owner. Self-tests cover the trap and a plain id.
From dx-shepherd (END-DXS-CSSID).
This commit is contained in:
parent
12a647028e
commit
e9e1adc0d6
1 changed files with 21 additions and 1 deletions
|
|
@ -73,6 +73,12 @@ function shadowCssOf(script, filename) {
|
|||
if (node.type === 'CallExpression' && node.callee && node.callee.name === 'useShadowStyles') {
|
||||
const acc = { css: '', complete: true }
|
||||
fold(node.arguments[0], acc)
|
||||
// The style ID (second argument). A plain identifier is the only correct shape: a CSS rule moved
|
||||
// past the first argument concatenates onto it ('@media(...){...}doxa-gospel-map'), which is valid
|
||||
// JS, keeps the build green and silently loses the rule (gospel-plugin's trap, COMMON.md).
|
||||
const lab = { text: '', literal: true, present: !!node.arguments[1] }
|
||||
if (node.arguments[1]) { const la = { css: '', complete: true }; fold(node.arguments[1], la); lab.text = la.css; lab.literal = la.complete }
|
||||
acc.label = lab
|
||||
found.push(acc)
|
||||
}
|
||||
for (const k of Object.keys(node)) {
|
||||
|
|
@ -153,6 +159,14 @@ ok(r.open === 1 && !r.orphans.length, 'a data: URL with a ; inside url() does no
|
|||
ok(got.length === 1 && got[0].complete === false,
|
||||
'an argument built partly from an imported constant is reported PARTIAL, not scanned as a whole sheet')
|
||||
}
|
||||
{
|
||||
// gospel-plugin's trap: a rule moved past the CSS argument concatenates onto the style id
|
||||
const trap = "useShadowStyles('.a{ color:red; }', '@media (max-width:1px){ .b{ color:red; } }' + 'doxa-gospel-map')"
|
||||
const lab = shadowCssOf(trap, 'x.js')[0].label
|
||||
ok(lab.literal && !/^[a-z0-9-]+$/.test(lab.text), 'the style-id arm reads a rule concatenated onto the id as NOT a plain identifier')
|
||||
const good = shadowCssOf("useShadowStyles('.a{ color:red; }', 'doxa-gospel-map')", 'x.js')[0].label
|
||||
ok(good.literal && /^[a-z0-9-]+$/.test(good.text), 'NEG CONTROL: a plain id passes the style-id arm')
|
||||
}
|
||||
|
||||
// ── the tree ────────────────────────────────────────────────────────────────────────────────────
|
||||
const walk = (d) => readdirSync(d).flatMap((f) => {
|
||||
|
|
@ -172,7 +186,13 @@ for (const f of files) {
|
|||
let blocks
|
||||
try { blocks = scriptsOf(f, src).flatMap((s) => shadowCssOf(s, f)) }
|
||||
catch (e) { console.log(` FAIL - ${rel(f)}: could not be parsed (${e.message.slice(0, 70)})`); fails++; continue }
|
||||
for (const { css, complete } of blocks) {
|
||||
for (const { css, complete, label } of blocks) {
|
||||
// ID arm: a literal style ID must be a plain identifier. A computed one (a variable) is left alone.
|
||||
if (label && label.present && label.literal && !/^[a-z0-9-]+$/.test(label.text)) {
|
||||
console.log(` FAIL - ${rel(f)}: useShadowStyles id is not a plain identifier: ${JSON.stringify(label.text.slice(0, 80))}`)
|
||||
console.log(' a CSS rule has probably been concatenated onto the id; move it back into the first argument')
|
||||
fails++
|
||||
}
|
||||
if (!/[{}]/.test(css)) { partials.push(f); continue }
|
||||
// ★ A PARTIAL ARGUMENT IS STILL SCANNED, and skipping it was a false green waiting to happen.
|
||||
// region-dashboards.vue builds its sheet as DXLIFE_TOKENS + SHADOW_BOX_SIZING_RESET + literals,
|
||||
|
|
|
|||
Loading…
Reference in a new issue