mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-26 04:19:33 +08:00
fix(ts): a type-only re-export like export type * from './types' now parses (#2837)
TypeScript 5.0's type-only re-export of a whole module, `export type * from './x'` and `export type * as ns from './x'`, has no rule in the vendored tree-sitter-typescript grammar (v0.23.2, the revision the kernel's crate compiles) or in upstream master: `type` parses as an ERROR inside the export statement. The graph was already right, since the import resolver reads the statement from the file (PRs #1955 and #2526), but the error tree meant a barrel of re-exports got the "parse produced no symbols" warning and a parse_error row, `codegraph status` listed it among the files it could not parse, and the native kernel deferred every file with such a line to wasm. The TypeScript extractor's preParse (.ts and .tsx) now blanks that `type` to four spaces where a statement starts (a line start, or after `;`, `{`, `}` or a block comment) when `*` is followed by `from` or `as`, so the line reads as the `export *` it is to the graph. Offsets are kept. The kernel route applies the preParse before calling the kernel, so both engines parse the same bytes and no Rust change is needed. A grammar patch would mean vendoring and regenerating the grammar for both dialects, two wasm rebuilds and a kernel off the crates.io crate, for a statement `export *` already models. A literal pre-check skips other files: 16 ms over strapi's 4,659 TS files. The only node change is an import node's stored statement text, which now reads `export * from './x'`. Import nodes are filtered from search, explore and the context builder, and nothing in resolution reads it. status-index-health.test.ts used `export type * from` as its unparseable file (issue #2336); it now uses a file with merge-conflict markers. Validation: - New ts-export-type-star.test.ts, 15 tests: 13 fail with main's typescript.ts, 11 with the blank unwired, all pass with the fix (kernel and wasm, .ts and .tsx, LF and CRLF). - A/B, kernel engine, main vs fix, dumps by natural keys: masto.js, kysely, TanStack/query, graffle and strapi keep every edge and ref (162,421 and 192,248 on strapi). The only changes are 883 import signatures, one per `export type *` statement, and 76 cleared warnings (masto.js 11, kysely 1, query 4, graffle 28, strapi 32). The Go and Python controls gin and requests are byte-identical. - Kernel parity, typescript and tsx: 0 diffs on every sweep. Deferrals drop masto.js 11 to 0, graffle 203 to 13, strapi 105 to 69, query 32 to 27, kysely 14 to 12; the rest are other grammar gaps. - Index time, median of 3 interleaved: graffle 12.7 s vs 13.1 s, query 11.2 s vs 11.4 s, within noise. Fixes #2474 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
51c4124dad
commit
cf3e6bc525
@@ -69,6 +69,32 @@ export function blankFlowSyntax(source: string, filePath?: string): string {
|
||||
.replace(/\.\.\.(?=\s*[},])/g, ' ');
|
||||
}
|
||||
|
||||
/** Whether the file writes `export type *` anywhere: most files don't, and skip the rest. */
|
||||
const EXPORT_TYPE_STAR_HINT = /export\s+type\s*\*/;
|
||||
/**
|
||||
* `export type *` where a statement starts (a line's first word, or after `;`,
|
||||
* `{`, `}` or the end of a block comment), followed by `from` or `as`.
|
||||
*/
|
||||
const EXPORT_TYPE_STAR = /(^|[;{}]|\*\/)([ \t]*export\s+)type(?=\s*\*\s*(?:from|as)\b)/gm;
|
||||
|
||||
/**
|
||||
* TypeScript 5.0's type-only re-export of a whole module, `export type * from
|
||||
* './types'` and `export type * as ns from './types'`, with its `type` blanked
|
||||
* to spaces (offsets survive), so it reads as the `export *` it is to the
|
||||
* graph. The grammar has no rule for it (nor does upstream's): `type` parsed
|
||||
* as an error, so a barrel of re-exports was reported as producing no symbols
|
||||
* and counted by `codegraph status` as a file it could not parse, and the
|
||||
* kernel handed every such file to the wasm parser (#2474). Only a statement
|
||||
* start is blanked, so a mention in a comment or a doc keeps its text. The
|
||||
* import resolver reads the statement as written, `type` included, from the
|
||||
* file itself (`extractReExports`). Applied before both engines parse (the
|
||||
* kernel's preParse hoist), so they agree.
|
||||
*/
|
||||
export function blankExportTypeStar(source: string): string {
|
||||
if (!EXPORT_TYPE_STAR_HINT.test(source)) return source;
|
||||
return source.replace(EXPORT_TYPE_STAR, '$1$2 ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether `node` is declared in a `declare module 'x' { … }` or `declare
|
||||
* global { … }` body, which exports it without an `export` keyword.
|
||||
@@ -147,7 +173,7 @@ export function isAmbientFunctionSignature(node: SyntaxNode, filePath: string):
|
||||
}
|
||||
|
||||
export const typescriptExtractor: LanguageExtractor = {
|
||||
preParse: blankFlowSyntax,
|
||||
preParse: (source, filePath) => blankExportTypeStar(blankFlowSyntax(source, filePath)),
|
||||
functionTypes: ['function_declaration', 'generator_function_declaration', 'arrow_function', 'function_expression', 'generator_function'],
|
||||
isAmbientFunction: isAmbientFunctionSignature,
|
||||
// `class` is a class expression, `register(class Widget extends Base {…})`:
|
||||
|
||||
Reference in New Issue
Block a user