Skip to content

Commit 9b7921f

Browse files
committed
fix(scripts): close three coverage gaps in the specifier audit
Review round 1 on #6351. All three findings were real and all three let the exact regression this guard exists for slip through. - Reported line numbers were one early. `SPECIFIER_RE` opens with `(?:^|\n)`, so `m.index` is the newline ENDING the previous line, not the start of the statement. `./helpers.js` on line 13 was reported as line 12. Anchoring to the specifier's own offset is exact, and for a multi-line import it points at the `from '...'` line — where the reader needs to look anyway. - `require()` was not scanned. This repo uses lazy requires deliberately to break import cycles: `tools/params.ts` reaches `@/blocks` that way and `blocks/blocks/agent.ts` reaches `@/blocks/registry`, 22 first-party call sites in total. Those edges resolve exactly like static ones, so a bad specifier in one fails identically. Verified by pointing `tools/params.ts` at a non-existent module and watching the audit catch it. - `apps/docs` was not scanned, despite being a second Next.js app with its own `next.config.ts` — so it carries identical Turbopack exposure. Now covered, and clean. Side-effect imports and dynamic `import()` were called out in the same round but are already covered: the optional `from` group in `SPECIFIER_RE` matches bare `import '...'`, and `DYNAMIC_RE` handles `import('...')`. That review ran against 1c6073e, before the resolver rewrite. Coverage goes from 37,307 specifiers across 11,182 files to 37,438 across 11,243, still with zero violations.
1 parent 6112f9d commit 9b7921f

1 file changed

Lines changed: 18 additions & 4 deletions

File tree

scripts/check-import-specifiers.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ import { fileURLToPath } from 'node:url'
4343

4444
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url))
4545
const ROOT = resolve(SCRIPT_DIR, '..')
46-
const SCAN_DIRS = ['apps/sim', 'apps/realtime', 'packages']
46+
const SCAN_DIRS = ['apps/sim', 'apps/realtime', 'apps/docs', 'packages']
4747
const SKIP_DIRS = new Set(['node_modules', '.next', 'dist', 'build', '.turbo'])
4848

4949
/**
@@ -58,6 +58,13 @@ const SPECIFIER_RE =
5858
/(?:^|\n)\s*(?:import|export)\s+(?!type\s)(?:[\s\S]*?from\s*)?['"]([^'"]+)['"]/g
5959
/** `import(...)` — resolved at call time, but the path still has to exist. */
6060
const DYNAMIC_RE = /\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g
61+
/**
62+
* `require('@/...')` — this repo uses lazy requires deliberately to break import cycles
63+
* (`tools/params.ts` reaches `@/blocks` that way, `blocks/blocks/agent.ts` reaches
64+
* `@/blocks/registry`). Those edges resolve exactly like static ones, so a bad specifier
65+
* in one fails identically and must be checked.
66+
*/
67+
const REQUIRE_RE = /\brequire\s*\(\s*['"]([^'"]+)['"]\s*\)/g
6168

6269
/**
6370
* Packages that must be imported by subpath. Opt-in rather than opt-out: `@sim/emcn` and
@@ -312,18 +319,25 @@ for (const file of files) {
312319
return lo + 1
313320
}
314321

315-
for (const pattern of [SPECIFIER_RE, DYNAMIC_RE]) {
322+
for (const pattern of [SPECIFIER_RE, DYNAMIC_RE, REQUIRE_RE]) {
316323
pattern.lastIndex = 0
317324
let m = pattern.exec(src)
318325
while (m !== null) {
319326
const spec = m[1]
327+
/**
328+
* Anchor to the specifier, not to `m.index`. SPECIFIER_RE opens with `(?:^|\n)`, so
329+
* `m.index` is the newline ENDING the previous line — reporting it put every violation
330+
* one line early. The specifier's own offset is exact, and for a multi-line import it
331+
* points at the `from '...'` line, which is where the reader needs to look anyway.
332+
*/
333+
const at = m.index + m[0].lastIndexOf(spec)
320334
const outcome = resolveSpecifier(spec, file)
321335
if (outcome) {
322336
checked++
323337
if (!outcome.ok) {
324338
violations.push({
325339
file: relative(ROOT, file),
326-
line: lineAt(m.index),
340+
line: lineAt(at),
327341
specifier: spec,
328342
kind: 'unresolved',
329343
reason: outcome.reason,
@@ -335,7 +349,7 @@ for (const file of files) {
335349
const example = subs ? [...subs.keys()].find((k) => k !== '.') : undefined
336350
violations.push({
337351
file: relative(ROOT, file),
338-
line: lineAt(m.index),
352+
line: lineAt(at),
339353
specifier: spec,
340354
kind: 'bare-barrel',
341355
reason: example

0 commit comments

Comments
 (0)