From e18a74b936e43aeb7f74df433d88f294f9bcc877 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Wed, 26 Aug 2026 14:16:41 -0700 Subject: The app arrives in movements: real ESM chunks split the bundle (entry 333KB, shared chunks alongside, 61KB of gifts/NWC/hats deferred past first paint into slots that keep their hook seniority), the lazy scripts wear IIFE coats so their names can't collide with the page's — which is what silently broke the remote-signer button — nip98 moves out of the login module so minimal builds stop hauling backup relays, and the smoke test learns the front door's new steps while the regtest stack rises from its IBD nap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi --- tools/feature-registry-check.js | 51 ++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 31 deletions(-) (limited to 'tools/feature-registry-check.js') diff --git a/tools/feature-registry-check.js b/tools/feature-registry-check.js index 9b7ac5e..0931156 100644 --- a/tools/feature-registry-check.js +++ b/tools/feature-registry-check.js @@ -1,40 +1,29 @@ -// Guard: src/features/index.js and build.js's ALL_FEATURES must agree. +// Guard: src/features/index.js and build.js's featureIndexSource must agree. // They drifted once (nwc was added to index.js only, so its i18n strings // shipped but its code never did — the settings card silently never rendered, // because build.js SWAPS OUT features/index.js for a generated module). +// +// The strongest form of agreement is equality: the checked-in module must be +// code-identical (comments and whitespace aside) to what featureIndexSource +// generates for the full feature set — imports, construction order, deferred +// placeholder slots, and the deferred loader all included. // bun tools/feature-registry-check.js import { readFileSync } from 'node:fs'; +import { featureIndexSource, enabledFeatures } from '../build.js'; -const idx = readFileSync(new URL('../src/features/index.js', import.meta.url), 'utf8'); -const build = readFileSync(new URL('../build.js', import.meta.url), 'utf8'); +const strip = (s) => s + .replace(/\/\/[^\n]*/g, '') // line comments + .replace(/\s+/g, ' ') // whitespace runs + .replace(/, /g, ',').replace(/ ,/g, ',') + .trim(); -const inIndex = [...idx.matchAll(/import \{ (\w+Feature) \} from '\.\/([\w-]+)\.js'/g)] - .map((m) => ({ fn: m[1], name: m[2] })); -const registry = build.match(/const ALL_FEATURES = \{([^}]*)\}/s)?.[1] || ''; -const inBuild = [...registry.matchAll(/(\w+):\s*'(\w+Feature)'/g)].map((m) => ({ name: m[1], fn: m[2] })); +const idx = readFileSync(new URL('../src/features/index.js', import.meta.url), 'utf8'); +const gen = featureIndexSource(enabledFeatures(undefined)); -let ok = true; -for (const f of inIndex) { - if (!inBuild.find((b) => b.fn === f.fn)) { - console.log(`x ${f.fn} (${f.name}.js) is in features/index.js but NOT in build.js ALL_FEATURES — it will not ship`); - ok = false; - } -} -for (const b of inBuild) { - if (!inIndex.find((f) => f.fn === b.fn)) { - console.log(`x ${b.fn} is in build.js ALL_FEATURES but not imported in features/index.js`); - ok = false; - } -} -// Compare the ORDER FEATURES ARE CONSTRUCTED IN (hook precedence depends on -// it), not the order they happen to be imported in. -const built = [...(idx.match(/return \[([^\]]*)\]/s)?.[1] || '') - .matchAll(/(\w+Feature)\(ctx\)/g)].map((m) => m[1]); -const orderIdx = built.join(','); -const orderBuild = inBuild.map((b) => b.fn).join(','); -if (ok && orderIdx !== orderBuild) { - console.log(`x feature ORDER differs (hook precedence depends on it)\n index.js: ${orderIdx}\n build.js: ${orderBuild}`); - ok = false; +if (strip(idx) !== strip(gen)) { + console.log('x src/features/index.js does not match build.js featureIndexSource (full set)'); + console.log('\n--- generated (normalized) ---\n' + strip(gen)); + console.log('\n--- checked in (normalized) ---\n' + strip(idx)); + process.exit(1); } -if (ok) console.log(`ok ${inIndex.length} features registered consistently`); -process.exit(ok ? 0 : 1); +console.log(`ok features/index.js matches the generator (${enabledFeatures(undefined).length} features, deferred slots included)`); -- cgit v1.2.3