Files
Maxim 4b17ea7d35 fix(scripts): tokenize before hunting loader calls in the purity gate
The #4893 hard-fail gate's loader-call detector gave WRONG VERDICTS IN BOTH
DIRECTIONS. It layered two regexes — a comment/string/template alternation that
blanked only the comment branch, and `\b(?:import|require(?:\.resolve)?)\s*\(`
over the result — then classified an argument as static from the FIRST CHARACTER
after the paren. All nine shapes below were reproduced against the real gate
before the rewrite:

  false FAIL  throw new Error("use require(path) instead")
  false FAIL  `import(${x})` inside a template
  false FAIL  o.import(y) / mod.require(x)          (member calls, not loaders)
  false PASS  /https:\/\//; …import(n)              (the regex's `//` blanked the
                                                     rest of the line, hiding a
                                                     real dynamic call)
  false PASS  import(`stream${n}`)                  (merely STARTS with a quote)
  false PASS  import("zo" + n)                      (same)
  false PASS  import(`${base}/v2/index.mjs`)        (same — the fat entry)
  false PASS  __require(name)                       (no \b inside `__require`)

Replaced with `scanSource`, a single-pass tokenizer that classifies every
character as code / comment / string / template / regex and returns a
length-preserving masked view plus a literal-span list. The one surviving regex
now only ever sees code, so import-shaped TEXT cannot reach it at all; an
argument counts as static only when it is one COMPLETE literal with no
concatenation or interpolation; `__require` is matched; and a member call is
rejected both by lookbehind and by a whitespace-skipping back-scan (so
`m\n  .import(x)` is not a loader either).

Proven in both directions: nine innocent/violation pairs run through the real
`assertEntryPurity`, each innocent form CLEAN and each matching real violation
FAIL. Re-proved end-to-end by prepending `import "streamdown"` to the real
dist/v2/headless.mjs — exit 1 naming all five families — then restoring it
byte-identically. On the untouched dist the scan sees 66 loader calls in the
`.cjs` graph and classifies all 66 static, so it passes because it LOOKED.

Also adds the first `.cjs` fixtures: every existing fixture was `.mjs`, leaving
the script's `format: "cjs"` branch and the `require()` shape asserted by
nothing. Tests 24 → 47.

`stripComments` is renamed `maskNonCode`, since it now blanks literals and
regexes too; it had no caller outside this script and its test. The RN guard
keeps its own copy, untouched.

dev-docs/bundle-size.md: the four holes a sibling agent documented as known
limitations this round are closed and removed from that list; what genuinely
remains (regex-vs-division heuristic, no JSX/TS, indirect loaders) replaces them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 17:27:47 +02:00
..