fix(javascript): separate erased types from runtime semantics

Record erasure during definition indexing and reuse it across runtime references, captures, effects, module links, routes, Electron and Vite. Preserve source type links and emitted decorator and computed-key effects; keep actual runtime reads and escapes conservative.

Validation: 1,647 focused tests and 9 documentation tests passed, with build, static and documentation gates. The inherited reference-lifetimes stress file exhausts a 512 MiB worker on both unmodified 18451e479 and this change; that file remains unverified. TypeScript 7.0.2 emit was inspected for ES2022 and ES2018 without executing emitted code.
This commit is contained in:
morluto committed 2026-10-24 18:00:43 +08:00
1 parent cab78de506
commit 89006609f1
32 files changed
+756 -328

No files matched your search

@@ -1,4 +1,49 @@
import * as t from "@babel/types";
import {
isErasedJavaScriptDeclaration,
isTypeOnlyExportDeclaration,
isTypeOnlyExportSpecifier,
isTypeOnlyImportDeclaration,
isTypeOnlyImportSpecifier,
} from "./javascriptModuleDeclaration.js";
/** Whether this child and its descendants contribute no JavaScript value. */
export const isErasedJavaScriptValueChild = (
node: t.Node,
parent: t.Node | null,
): boolean => {
if (
isErasedJavaScriptDeclaration(node) ||
t.isTSType(node) ||
t.isTSTypeElement(node) ||
t.isTSDeclareMethod(node) ||
t.isTSTypeAnnotation(node) ||
t.isTSTypeParameterDeclaration(node) ||
t.isTSTypeParameterInstantiation(node) ||
t.isTSClassImplements(node) ||
t.isTSInterfaceHeritage(node) ||
t.isTSInterfaceBody(node) ||
(t.isImportDeclaration(node) && isTypeOnlyImportDeclaration(node)) ||
((t.isExportNamedDeclaration(node) || t.isExportAllDeclaration(node)) &&
isTypeOnlyExportDeclaration(node)) ||
(t.isImportSpecifier(node) && isTypeOnlyImportSpecifier(node)) ||
(t.isExportSpecifier(node) && isTypeOnlyExportSpecifier(node)) ||
(t.isTSImportEqualsDeclaration(node) && node.importKind === "type")
)
return true;
// Undecorated declared or abstract fields erase their keys and annotations.
// Decorators and a decorated field's computed key may execute at definition.
return (
t.isClassProperty(parent) &&
(parent.declare === true || parent.abstract === true) &&
!t.isDecorator(node) &&
!(
node === parent.key &&
parent.computed === true &&
(parent.decorators?.length ?? 0) > 0
)
);
};
/**
* Visit Babel AST nodes in deterministic source-tree order, excluding skipped subtrees.
@@ -15,6 +60,30 @@ export const traverseJavaScriptAst = (
visitor: JavaScriptAstVisitor,
): void => completeSemanticSteps(traverseJavaScriptAstSteps(root, visitor));
/** Visit only syntax retained as JavaScript values after TypeScript erasure. */
export const traverseJavaScriptValueAst = (
root: t.Node,
erasedSyntaxNodes: WeakSet<t.Node>,
visitor: JavaScriptAstVisitor,
): void =>
completeSemanticSteps(
traverseJavaScriptValueAstSteps(root, erasedSyntaxNodes, visitor),
);
/** Cooperative value-only view of the same deterministic AST traversal. */
export function* traverseJavaScriptValueAstSteps(
root: t.Node,
erasedSyntaxNodes: WeakSet<t.Node>,
visitor: JavaScriptAstVisitor,
): Generator<void, void> {
if (erasedSyntaxNodes.has(root)) return;
yield* traverseJavaScriptAstSteps(root, {
...visitor,
skip: (node) =>
erasedSyntaxNodes.has(node) || visitor.skip?.(node) === true,
});
}
/** Callbacks for one deterministic AST traversal. */
export interface JavaScriptAstVisitor {
/** Omit a child and its subtree without entering or exiting it. */