Skip to content

fix(languages): add .mts/.cts extensions and *.spec.tsx test glob to the TypeScript config#455

Open
tirth8205 wants to merge 2 commits into
Egonex-AI:mainfrom
tirth8205:fix/typescript-config-extensions
Open

fix(languages): add .mts/.cts extensions and *.spec.tsx test glob to the TypeScript config#455
tirth8205 wants to merge 2 commits into
Egonex-AI:mainfrom
tirth8205:fix/typescript-config-extensions

Conversation

@tirth8205

Copy link
Copy Markdown
Contributor

Problem

  • The TypeScript LanguageConfig declares extensions: [".ts", ".tsx"] but omits .mts and .cts, the first-class TypeScript module file extensions (TS 4.7+, used in ESM/nodenext projects — which this repo itself targets, see PR Fix TypeScript runtime .js import resolution #405/fix(extract-import-map): apply NodeNext .js→.ts rewrite (#294) #359). The JavaScript config correctly includes the analogous .mjs and .cjs. Because LanguageRegistry derives its extension map directly from config.extensions, and TreeSitterPlugin (tree-sitter-plugin.ts lines 66-72) builds _extensionToLang the same way, a .mts or .cts file resolves to NULL: it is not detected as TypeScript by getByExtension/getForFile and is skipped entirely by the tree-sitter extraction pipeline. Verified at runtime: getByExtension('.mts') -> NULL, getByExtension('.cts') -> NULL, while getByExtension('.mjs') -> 'javascript' and getByExtension('.cjs') -> 'javascript'. The tree-sitter-typescript grammar parses…
  • The TypeScript config's test glob list is tests: ["*.test.ts", "*.spec.ts", "*.test.tsx"]. It covers .test.tsx (the React component test convention) but inconsistently omits *.spec.tsx, even though it covers both .test/.spec for plain .ts. The JavaScript config consistently lists both *.test.js and *.spec.js. The dashboard package is React + TypeScript and uses .tsx files, so Component.spec.tsx test files (a common naming style) are not recognized as tests by the file-pattern classifier, while the equivalent Component.test.tsx is. This causes spec-named TSX test files to be miscategorized as regular source.

Fix

  • Change line 6 to include the module extensions, mirroring the JavaScript config: extensions: [".ts", ".tsx", ".mts", ".cts"],
  • Add the missing spec-tsx glob for parity with the test-tsx and spec-ts entries: tests: [".test.ts", ".spec.ts", ".test.tsx", ".spec.tsx"],

Testing

Adds unit test(s) that fail before the change and pass after. The full core test suite, eslint, and tsc --noEmit all pass locally on this branch.

Found via a static correctness audit of the TypeScript language config.

🤖 Generated with Claude Code

…the TypeScript config

The TypeScript LanguageConfig omitted the first-class .mts/.cts module
extensions, so those files resolved to null and were skipped by the
tree-sitter pipeline. It also listed *.test.tsx but not *.spec.tsx,
miscategorizing spec-named TSX test files as regular source.

Add .mts/.cts to extensions and *.spec.tsx to the test glob, mirroring
the JavaScript config's .mjs/.cjs and test/spec parity.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@thejesh23 thejesh23 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few concerns before this lands.

1. .d.ts (and .d.mts / .d.cts) still skipped. Declaration files are the most common TS-extension outside .ts/.tsx, and getForFile("foo.d.ts") matches on .ts so they already resolve — but the extractor then sees a pure-types file (no runtime exports, no call edges). Worth either explicitly listing .d.ts / .d.mts / .d.cts and gating them out of call-graph extraction, or adding a short comment in the config explaining that .d.ts is intentionally not separated. Otherwise the next person doing this audit lands a third PR.

2. .mts / .cts now route to the plain TS grammar — confirm that's intended. tree-sitter-plugin.ts lines 111-118 hard-code ext === ".tsx" ? "tsx" and falls through to _extensionToLang.get(ext) for everything else. So .mts and .cts will use tree-sitter-typescript.wasm, which is correct (they don't carry JSX). Worth a one-line code comment on that special-case so a future reader doesn't add .mts to the TSX branch by analogy with the .mjs-treated-as-JS pattern.

3. Test glob shape only covers root-level patterns. *.test.tsx / *.spec.tsx are bare globs (no **/ prefix and no __tests__/*.tsx). Vitest's default include is **/*.{test,spec}.?(c|m)[jt]s?(x) and most repos colocate under src/**/__tests__/. Depending on how filePatterns.tests is consumed (couldn't find a hot caller in src/ besides the new test) this may already not match packages/foo/src/__tests__/bar.test.tsx. Worth confirming the matcher is suffix-based, or extending to **/__tests__/*.{ts,tsx} for parity with how the layer-detector already treats __tests__ as a test dir (analyzer/layer-detector.ts:58).

Nit: while you're here, JS config's tests is missing *.test.jsx / *.spec.jsx — same asymmetry you just fixed on the TS side, one config over.

…test patterns

- typescript config: comment that .d.ts/.d.mts/.d.cts intentionally fall
  through to .ts/.mts/.cts as types-only files (no call-graph gating)
- tree-sitter-plugin: comment that only .tsx needs the TSX grammar and
  .mts/.cts deliberately use the plain TS grammar
- javascript config: add *.test.jsx / *.spec.jsx for parity with the TS side

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@tirth8205

Copy link
Copy Markdown
Contributor Author

1. .d.ts / .d.mts / .d.cts — Added a comment in typescript.ts documenting that declaration files intentionally fall through to .ts / .mts / .cts and are parsed as ordinary types-only TypeScript (no separate call-graph gating). I did not add extractor gating: this behavior predates this PR (.d.ts already resolved via .ts before .mts/.cts were added), so special-casing the extractor is a behavior change outside the module-variant-extension scope here and risks regressing types-only parsing. Tracking the gating idea as a follow-up.

2. .mts / .cts route to plain TS grammar — Confirmed correct: languageKeyFromPath only special-cases ext === ".tsx" → synthetic tsx key; everything else (including .mts/.cts) falls through to _extensionToLang.get(ext)typescripttree-sitter-typescript.wasm. Added the requested one-line comment so a future reader doesn't add .mts to the .tsx branch by analogy with the .mjs-as-JS pattern.

3. Test glob shape — Not extending the globs. LanguageConfig.filePatterns.tests has no runtime matcher consumer anywhere in src/ (verified by content grep — only config definitions and the registry test read it; it's z.array(z.string()) declarative metadata). The filePatterns in layer-detector.ts/llm-analyzer.ts is an unrelated LLM-supplied layer-prefix field, and __tests__ layer detection is handled independently by the hardcoded LAYER_PATTERNS in layer-detector.ts. Adding **/__tests__/*.{ts,tsx} would introduce unused, potentially misleading config rather than change any matching behavior.

Nit (JS .jsx test patterns) — Fixed for parity: added *.test.jsx / *.spec.jsx to javascriptConfig.filePatterns.tests, with a test locking it. (No functional effect today given #3, but it removes the cross-config asymmetry.)

Full core suite green (694 passed); tsc --noEmit clean.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants