check:frozen-labels holds a composed bundle to the stage of the page that shows it
Written by the UPG-100 plugin owner. It now takes the stricter of a file's own profile stage and its bundle's effective stage (internal/lib/effective-stage.mjs), so an experimental bundle that a production page composes is checked as production, and the output says why.
This commit is contained in:
parent
76fe9ddf28
commit
f45624915f
1 changed files with 27 additions and 6 deletions
|
|
@ -38,6 +38,10 @@ import { readFileSync, readdirSync, statSync, existsSync } from 'node:fs'
|
|||
import { join, relative, resolve, dirname } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { TREE_NAME } from './_tree-name.mjs'
|
||||
// ★ SHARED WITH check:hardcoded-english (regions-plugin). Two gates must agree about WHICH files are
|
||||
// in scope even when they disagree about what they find in them; a second copy would drift on exactly
|
||||
// the pair-form / transitive / cycle details below, and the drift would be silent.
|
||||
import { effectiveStages, stageReason } from './lib/effective-stage.mjs'
|
||||
import { parse as parseSfc } from '@vue/compiler-sfc'
|
||||
import * as babel from '@babel/parser'
|
||||
|
||||
|
|
@ -185,7 +189,9 @@ function stageIndex() {
|
|||
for (const b of m.bundles || []) for (const p of b.profiles || []) {
|
||||
byFile.set(join('plugin-designs', b.bundle, p.profile + '.vue'), p.stage || b.stage || 'experimental')
|
||||
}
|
||||
return byFile
|
||||
// The manifest itself and the bundle-level effective stage travel with it: the file-level answer
|
||||
// needs both, and re-reading the manifest twice would let the two drift apart.
|
||||
return { byFile, manifest: m, effStages: effectiveStages(m) }
|
||||
}
|
||||
|
||||
const walk = (dir, acc = []) => {
|
||||
|
|
@ -199,12 +205,13 @@ const walk = (dir, acc = []) => {
|
|||
return acc
|
||||
}
|
||||
|
||||
const stages = stageIndex()
|
||||
if (!stages) {
|
||||
const idx = stageIndex()
|
||||
if (!idx) {
|
||||
console.log(`FROZEN-LABELS: SKIP — no built tree at public/${TREE_NAME}. Run a build first.`)
|
||||
console.log(' Reported as a SKIP, not a pass: a gate that reports success when it never ran is worse than no gate.')
|
||||
process.exit(0)
|
||||
}
|
||||
const { byFile: stages, manifest, effStages } = idx
|
||||
|
||||
const files = ['library', 'plugin-designs'].flatMap((r) => walk(join(ROOT, r)))
|
||||
const failures = [], reported = []
|
||||
|
|
@ -215,11 +222,25 @@ for (const f of files) {
|
|||
catch (e) { console.error(`FROZEN-LABELS: parse error in ${relative(ROOT, f)} — ${e.message}`); process.exit(2) }
|
||||
if (!hits.length) continue
|
||||
const rel = relative(ROOT, f)
|
||||
// library/ is held to the production bar; a plugin file takes its own PROFILE stage.
|
||||
const stage = rel.startsWith('library/') ? 'production' : (stages.get(rel) || 'experimental')
|
||||
// ★ TWO QUESTIONS, BOTH ASKED. A .vue file IS a profile, so its own profile.stage is the precise
|
||||
// answer for the FILE — bundle.stage reads 'production' if ANY profile is, and four bundles are
|
||||
// mixed (cities-internet, gospel-resources, my-upg-100-list, regions-dashboard), so scoping by
|
||||
// bundle alone would FAIL on experimental files inside a production bundle. But profile stage alone
|
||||
// UNDER-reports: three experimental bundles are COMPOSED BY production ones (research-dashboard by
|
||||
// dashboard-plus-grid, doxa-research-map by research-map-plus-grid, people-groups-grid by both), so
|
||||
// a reader sees them on a production page while the file says experimental. Take the stricter of the
|
||||
// two: FAIL if the file's own profile is production OR its bundle is production by effective stage.
|
||||
const bundleOf = rel.startsWith('plugin-designs/') ? rel.split('/')[1] : null
|
||||
const byProfile = rel.startsWith('library/') ? 'production' : (stages.get(rel) || 'experimental')
|
||||
const byBundle = bundleOf ? (effStages.get(bundleOf) || 'experimental') : 'production'
|
||||
const stage = (byProfile === 'production' || byBundle === 'production') ? 'production' : 'experimental'
|
||||
// A widened scope must be able to EXPLAIN itself, or it reads as a bug to whoever it newly fails.
|
||||
const why = (stage === 'production' && byProfile !== 'production' && bundleOf)
|
||||
? ' [in scope: ' + stageReason(manifest, bundleOf) + ']'
|
||||
: ''
|
||||
for (const h of hits) {
|
||||
const refs = h.binding === '(expression)' ? -1 : (src.match(new RegExp(`\\b${h.binding}\\b`, 'g')) || []).length - 1
|
||||
const row = `${rel}:${h.line} ${h.binding} (${h.call}) refs=${refs < 0 ? '?' : refs}`
|
||||
const row = `${rel}:${h.line} ${h.binding} (${h.call}) refs=${refs < 0 ? '?' : refs}${why}`
|
||||
;(stage === 'production' ? failures : reported).push(row)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue