test(client): check what the chunk registers, and fix two defects in the checks

Three things, all about checks that had never met a real chunk.

`checkExternals.js` rejected slice 3's build outright, naming a fragment of
minified JSX as an imported specifier: a button reading "Approve and import"
puts the token immediately before a quote, and no regexp can tell that from a
statement. Same wall the server's `checkImports.js` hit, answered the same way —
a character walk. A mask rather than a rewrite, because a real import has its
keyword outside a string and its specifier inside one.

Writing the test for that false positive found the false NEGATIVE underneath
it: the pattern required whitespace after `import`, so it could not see
`import{useState}from"react"` — the one shape a minified build actually emits,
and the most likely way for a missed alias to reach production. It has never
been able to see it.

`registration.test.js` is new: stand up a fake `window.__rg` with a recording
registry and the real React, import the BUILT chunk, and read back what it
asked for. No DOM, because nothing renders. It holds the agreement that rots
quietly — every nav row points at a route this module actually registered —
rather than restating both lists.

CI now builds before it tests, because both of those read `dist/entry.js` and
skip without it. Run the other way round they are green and asking nothing.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-11 18:00:50 -05:00
parent 493cf296ab
commit e4af7dd9a8
7 changed files with 509 additions and 40 deletions

View File

@@ -20,6 +20,7 @@ import { fileURLToPath } from 'node:url'
const HERE = path.dirname(fileURLToPath(import.meta.url))
const CLIENT = path.resolve(HERE, '..')
const { bareImports, problemsWith } = await import('../scripts/checkExternals.js')
const configModule = await import('../vite.config.js')
const config = configModule.default
const { SHARED, SHARED_PACKAGES: guardedPackages } = configModule
@@ -92,15 +93,63 @@ test('modulePreload polyfilling stays off — an inline bootstrap is refused und
assert.strictEqual(config.build.modulePreload.polyfill, false)
})
test('every shim reads from window.__rg and imports nothing', () => {
test('exactly one file reads window.__rg, and every shim goes through it', () => {
// `shim/rg.js` is the single reader, and that is not tidiness: it is what
// makes the "core did not publish its dependencies" message reachable. The
// shims touch the global before anything else in the chunk does, so a check
// placed in the first-imported file is a guarantee that lasts until someone
// sorts the imports.
const dir = path.join(CLIENT, 'src', 'shim')
const shims = fs.readdirSync(dir)
assert.ok(shims.length >= 4)
assert.ok(shims.length >= 5)
for (const file of shims) {
const source = fs.readFileSync(path.join(dir, file), 'utf8')
assert.match(source, /window\.__rg/, `${file} does not read the global`)
// A shim that imported anything would be a shim with a dependency to
// resolve, which is the problem it exists to remove.
assert.doesNotMatch(source, /^\s*import\s/m, `${file} imports something`)
const code = source.replace(/^\s*\/\/.*$/gm, '') // the comments discuss the global
if (file === 'rg.js') {
assert.match(code, /window\.__rg/, 'rg.js must be the one that reads the global')
assert.doesNotMatch(code, /^\s*import\s/m, 'rg.js imports something')
continue
}
assert.doesNotMatch(code, /window\.__rg/, `${file} reads the global directly instead of via rg()`)
assert.match(code, /rg\(\)/, `${file} does not resolve through rg()`)
// A shim may import its sibling helper and nothing else — anything further
// would be a shim with a dependency to resolve, the problem it exists to remove.
for (const [, spec] of code.matchAll(/^\s*import\s[^'"]*['"]([^'"]+)['"]/gm)) {
assert.strictEqual(spec, './rg.js', `${file} imports ${spec}`)
}
}
})
test('the built chunk has no bare imports and bundles no shared dependency', () => {
// The artifact check itself, over the artifact that ships. Skipped rather than
// failed when there is no build: `npm test` must be runnable before `npm run
// build`, and CI runs them in order.
const chunk = path.join(CLIENT, 'dist', 'entry.js')
if (!fs.existsSync(chunk)) return
assert.deepStrictEqual(problemsWith(fs.readFileSync(chunk, 'utf8')), [])
})
test('an import inside a string is not an import — the check reads code, not text', () => {
// The regression that made this necessary: slice 3's chunk was the first with
// any content in it, and a button labelled "Approve and import" put the token
// immediately before a quote. The check rejected the whole build, naming a
// fragment of minified JSX as the offending specifier.
const uiCopy = 'const a=n("button",{children:"Approve and import"}),b=1;'
assert.deepStrictEqual(bareImports(uiCopy), [])
// Neither is one in a comment, or in a template literal.
assert.deepStrictEqual(bareImports('// import "react" would be wrong here\nconst a=1'), [])
assert.deepStrictEqual(bareImports('/* import "react" */ const a=1'), [])
assert.deepStrictEqual(bareImports('const s=`import "react"`'), [])
// And a real one still is, in each form the build could emit.
assert.deepStrictEqual(bareImports('import"react";'), ['react'])
assert.deepStrictEqual(bareImports('import{useState}from"react";'), ['react'])
assert.deepStrictEqual(bareImports('const m=await import("react-dom/client")'), ['react-dom/client'])
// A relative specifier is a split chunk, not a shared dependency: not our concern.
assert.deepStrictEqual(bareImports('import"./other.js";'), [])
// The case that proves the mask tracks escapes: a quote escaped INSIDE a
// string must not end it early and leave the tail looking like code.
assert.deepStrictEqual(bareImports('const s="he said \\"import\\" loudly";'), [])
})