Problem
The module-graph import scanner (packages/server/src/module-graph.js, parseFile) runs IMPORT_RE / EXPORT_FROM_RE directly on raw source with no string/template redaction. So an import '...' statement that appears as TEXT inside a template literal (e.g. example code shown in a <pre> inside an html\`` template) is mistaken for a real import, creating a phantom graph edge.
Observed live on docs.webjs.dev/docs/getting-started: the page shows import '../components/counter.ts'; as example code inside its html\`template. There is no realapp/docs/components/counter.tsfile, yet the SSR emits`, which 404s.
This hits any tutorial/docs/marketing app that displays import statements as content.
Design / approach
Reuse the existing position-preserving lexer redactStringsAndTemplates from js-scan.js as a MASK. It blanks string/template CONTENT to spaces while keeping code (including the import / export / from keywords) intact at the same offsets.
Cannot run IMPORT_RE on the redacted text directly, because the import SPECIFIER is itself a string and would be blanked (real relative imports would be missed). Instead:
- Compute
const masked = redactStringsAndTemplates(src) once.
- Run
IMPORT_RE / EXPORT_FROM_RE on the RAW src (so the specifier capture is intact).
- For each match, skip it unless
masked.slice(m.index, m.index + KEYWORD_LEN) still equals the keyword (import / export). A real top-level import keeps its keyword in masked; a keyword inside a template/string body is blanked, so the phantom is dropped.
This is verdict-safe for the gate and elision too: a path that only ever appears inside a string was never a real import, so dropping it is correct everywhere the graph is consumed.
Acceptance criteria
Found alongside #158 while auditing all four in-repo dogfood apps for broken modulepreload hints (blog hit #158; docs hit this; website + ui-website were clean).
Problem
The module-graph import scanner (
packages/server/src/module-graph.js,parseFile) runsIMPORT_RE/EXPORT_FROM_REdirectly on raw source with no string/template redaction. So animport '...'statement that appears as TEXT inside a template literal (e.g. example code shown in a<pre>inside anhtml\`` template) is mistaken for a real import, creating a phantom graph edge.Observed live on docs.webjs.dev/docs/getting-started: the page shows
import '../components/counter.ts';as example code inside itshtml\`template. There is no realapp/docs/components/counter.tsfile, yet the SSR emits`, which 404s.This hits any tutorial/docs/marketing app that displays import statements as content.
Design / approach
Reuse the existing position-preserving lexer
redactStringsAndTemplatesfromjs-scan.jsas a MASK. It blanks string/template CONTENT to spaces while keeping code (including theimport/export/fromkeywords) intact at the same offsets.Cannot run
IMPORT_REon the redacted text directly, because the import SPECIFIER is itself a string and would be blanked (real relative imports would be missed). Instead:const masked = redactStringsAndTemplates(src)once.IMPORT_RE/EXPORT_FROM_REon the RAWsrc(so the specifier capture is intact).masked.slice(m.index, m.index + KEYWORD_LEN)still equals the keyword (import/export). A real top-level import keeps its keyword inmasked; a keyword inside a template/string body is blanked, so the phantom is dropped.This is verdict-safe for the gate and elision too: a path that only ever appears inside a string was never a real import, so dropping it is correct everywhere the graph is consumed.
Acceptance criteria
import/export ... fromappearing inside a template literal or string is NOT added as a graph edgeexport ... fromre-exports, including multi-line) are still detected/app/docs/components/counter.tsphantom preload 404 is goneimport './x.ts'as template text; counterfactual fails without the maskFound alongside #158 while auditing all four in-repo dogfood apps for broken modulepreload hints (blog hit #158; docs hit this; website + ui-website were clean).