Ran /understand on a TanStack Start project and noticed the graph was missing a ton of import edges — turned out only relative imports (./foo) were showing up, and everything using our #/… path alias resolved to nothing. About half our internal imports just weren't in the graph, and a quarter of the nodes ended up orphaned.
Tracked it down to the comment stripping in parseTsConfigText() (skills/understand/extract-import-map.mjs). It strips JSONC comments with /\/\*[\s\S]*?\*\//g, which isn't string-aware — so the /* inside an include glob like "**/*.ts" gets read as the start of a comment, and it happily deletes everything up to the next */ (which is sitting in "**/*.tsx"). That mangles the JSON, JSON.parse throws, the raw fallback throws too (real /* */ comments this time), and the function just returns null — so every compilerOptions.paths alias gets dropped on the floor. You see it as a failed to parse — path aliases will not be applied warning.
The annoying part is **/*.ts includes are basically in every tsconfig, so I think this hits any project that uses path aliases + a glob include, not just ours.
Minimal repro — a tsconfig with:
→ aliases dropped, #/… imports resolve to [].
Fix is just making the strip string-aware (skip /* */ and // when you're inside a string), or swapping in a real JSONC parser like jsonc-parser. I tried a ~15-line string-aware version locally and the same project jumped from 105 → 222 internal import edges with nothing else changed. Patch:
@@ -148,11 +148,28 @@
*/
function parseTsConfigText(raw) {
// tsconfig.json often contains JSONC-style comments; strip line and block
- // comments before parsing. The strip is naive (it doesn't honor string
- // contents), so we fall back to the raw text on failure.
- const stripped = raw
- .replace(/\/\*[\s\S]*?\*\//g, '')
- .replace(/(^|[^:])\/\/.*$/gm, '$1');
+ // comments before parsing. String-aware strip (does NOT misfire on `/*` that
+ // appears inside string literals like the `**/*.ts` globs in `include`), then
+ // drop trailing commas.
+ let stripped = '';
+ {
+ let inStr = false, esc = false;
+ for (let i = 0; i < raw.length; i++) {
+ const c = raw[i], n = raw[i + 1];
+ if (inStr) {
+ stripped += c;
+ if (esc) esc = false;
+ else if (c === '\\') esc = true;
+ else if (c === '"') inStr = false;
+ continue;
+ }
+ if (c === '"') { inStr = true; stripped += c; continue; }
+ if (c === '/' && n === '*') { i += 2; while (i < raw.length && !(raw[i] === '*' && raw[i + 1] === '/')) i++; i++; continue; }
+ if (c === '/' && n === '/') { while (i < raw.length && raw[i] !== '\n') i++; stripped += '\n'; continue; }
+ stripped += c;
+ }
+ stripped = stripped.replace(/,(\s*[}\]])/g, '$1');
+ }
let parsed;
try {
parsed = JSON.parse(stripped);
Ran
/understandon a TanStack Start project and noticed the graph was missing a ton of import edges — turned out only relative imports (./foo) were showing up, and everything using our#/…path alias resolved to nothing. About half our internal imports just weren't in the graph, and a quarter of the nodes ended up orphaned.Tracked it down to the comment stripping in
parseTsConfigText()(skills/understand/extract-import-map.mjs). It strips JSONC comments with/\/\*[\s\S]*?\*\//g, which isn't string-aware — so the/*inside anincludeglob like"**/*.ts"gets read as the start of a comment, and it happily deletes everything up to the next*/(which is sitting in"**/*.tsx"). That mangles the JSON,JSON.parsethrows, the raw fallback throws too (real/* */comments this time), and the function just returnsnull— so everycompilerOptions.pathsalias gets dropped on the floor. You see it as afailed to parse — path aliases will not be appliedwarning.The annoying part is
**/*.tsincludes are basically in every tsconfig, so I think this hits any project that uses path aliases + a glob include, not just ours.Minimal repro — a tsconfig with:
{ "include": ["**/*.ts", "**/*.tsx"], "compilerOptions": { "paths": { "#/*": ["./src/*"] } /* any block comment */ } }→ aliases dropped,
#/…imports resolve to[].Fix is just making the strip string-aware (skip
/* */and//when you're inside a string), or swapping in a real JSONC parser likejsonc-parser. I tried a ~15-line string-aware version locally and the same project jumped from 105 → 222 internal import edges with nothing else changed. Patch: