Parse npm-static declaration and package files only when a rewrite can apply (#2146)

This commit is contained in:
Chris Tate
2026-10-24 17:22:04 -05:00
committed by GitHub
2 changed files with 28 additions and 28 deletions
+16 -27
View File
@@ -488,43 +488,33 @@ function visitNpmStaticDeclarationClosure(
visit(entryPath);
}
function npmStaticDeclarationOverloadsOf(
/** The overloads and properties of the classes a package's declaration
* closure declares, read from one parse of each declaration file. */
function npmStaticDeclarationMetadataOf(
services: FrontendServices,
entryPath: string,
): NpmStaticDeclarationOverloads {
const classes = new Map<string, Map<string, readonly NpmStaticOverloadSignature[]>>();
): { overloads: NpmStaticDeclarationOverloads; properties: NpmStaticDeclarationProperties } {
const overloads = new Map<string, Map<string, readonly NpmStaticOverloadSignature[]>>();
const properties = new Map<string, Map<string, string>>();
visitNpmStaticDeclarationClosure(services, entryPath, (path, source) => {
for (const [className, methods] of parseNpmStaticDeclarationOverloads(
services.parse(path, source, "ts"),
)) {
const sourceFile = services.parse(path, source, "ts");
for (const [className, methods] of parseNpmStaticDeclarationOverloads(sourceFile)) {
const target =
classes.get(className) ?? new Map<string, readonly NpmStaticOverloadSignature[]>();
classes.set(className, target);
overloads.get(className) ?? new Map<string, readonly NpmStaticOverloadSignature[]>();
overloads.set(className, target);
for (const [methodName, signatures] of methods) {
if (!target.has(methodName)) target.set(methodName, signatures);
}
}
});
return classes;
}
function npmStaticDeclarationPropertiesOf(
services: FrontendServices,
entryPath: string,
): NpmStaticDeclarationProperties {
const classes = new Map<string, Map<string, string>>();
visitNpmStaticDeclarationClosure(services, entryPath, (path, source) => {
for (const [className, properties] of parseNpmStaticDeclarationProperties(
services.parse(path, source, "ts"),
)) {
const target = classes.get(className) ?? new Map<string, string>();
classes.set(className, target);
for (const [propertyName, type] of properties) {
for (const [className, fields] of parseNpmStaticDeclarationProperties(sourceFile)) {
const target = properties.get(className) ?? new Map<string, string>();
properties.set(className, target);
for (const [propertyName, type] of fields) {
if (!target.has(propertyName)) target.set(propertyName, type);
}
}
});
return classes;
return { overloads, properties };
}
/** See LoadResult.startupCrash: Node's exact error message, the IR error
@@ -1096,8 +1086,7 @@ export function loadProgram(
) {
continue;
}
const overloads = npmStaticDeclarationOverloadsOf(services, resolved.typesFile);
const properties = npmStaticDeclarationPropertiesOf(services, resolved.typesFile);
const { overloads, properties } = npmStaticDeclarationMetadataOf(services, resolved.typesFile);
const classNames = new Set([...overloads.keys(), ...properties.keys()]);
if (classNames.size === 0) continue;
const runtime = resolveBareModule(entryPath, pkg, "js-only");
+12 -1
View File
@@ -20,6 +20,12 @@ import * as syntax from "./npm-static-declaration-syntax.js";
import { normalizeCjsExportsVar } from "./npm-static-cjs-exports-var.js";
import type { SourceFile } from "./ts7/ast-types.js";
/** Whether the source can contain `word` as a keyword or identifier: it
* holds the word itself or a Unicode escape that might spell it. */
function spells(source: string, word: string): boolean {
return source.includes(word) || source.includes("\\u");
}
/** Evaluate an isolated, self-contained TypeScript callback under a finite
* budget. Hosts supply execution; shared lowering owns capture checks and
* verifies the returned value before baking it into the program. */
@@ -150,13 +156,18 @@ export class FrontendServices {
return normalizeCjsExportsVar(this.parse(path, source, "js"));
}
nullableClassFields(path: string, source: string): syntax.NpmStaticOverloadRewrite | null {
// Rewrites `null`-initialized fields of class declarations.
if (!spells(source, "class") || !spells(source, "null")) return null;
return syntax.applyNpmStaticNullableClassFields(this.parse(path, source, "js"), source);
}
jsDocNamepaths(path: string, source: string): string | null {
if (!source.includes("~") && !source.includes("#")) return null;
// A namepath segment joined by `#` or `~` (applyNpmStaticJsDocNamepaths).
if (!/[$\w][#~][$A-Z_a-z]/.test(source)) return null;
return syntax.applyNpmStaticJsDocNamepaths(this.parse(path, source, "js"), source);
}
findReturnWidening(path: string, source: string): syntax.NpmStaticOverloadRewrite | null {
// Widens `find` calls returned by methods of class declarations.
if (!spells(source, "class") || !spells(source, "find")) return null;
return syntax.applyNpmStaticFindReturnWidening(this.parse(path, source, "js"), source);
}
declarationProperties(