mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-01 07:33:12 +08:00
Without @types/node, every program loaded scriptc-node-fallback.d.ts whole: the console and process globals and 47 Node modules, about 8,100 lines, parsed, bound and checked in every cold build whether or not the program imported them. - The fallback file keeps the globals and the modules they name (console, readline, readline/promises, perf_hooks, tty). Every other module, under both spellings, has its own file in ambient/node-fallback/, which references the files of the modules its declarations name. - A module's file joins the program when one of the program's files names the module in a string literal: imports, requires, import types and JSDoc types all name modules that way. The entry is read before the first program; the program-root discovery passes add modules that other files name. Files are read as tsgo reads them, and the TypeScript libraries and scriptc's own declarations are skipped. - The checks that recognized the fallback file by its path recognize every fallback file.
44 lines
2.1 KiB
TypeScript
44 lines
2.1 KiB
TypeScript
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
import { basename, dirname, join } from "node:path";
|
|
import { expect, test } from "vitest";
|
|
import {
|
|
fallbackDtsPath,
|
|
fallbackModulePath,
|
|
fallbackModulesNamedIn,
|
|
} from "../src/frontend/dts-paths.js";
|
|
|
|
const moduleDirectory = join(dirname(fallbackDtsPath()), "node-fallback");
|
|
const declaredModules = (text: string): string[] =>
|
|
[...text.matchAll(/^declare module "([^"]+)" \{/gm)].map((m) => m[1]!);
|
|
|
|
test("every fallback module file declares the specifiers that map to it, and only those", () => {
|
|
const files = readdirSync(moduleDirectory).filter((name) => name.endsWith(".d.ts"));
|
|
expect(files.length).toBeGreaterThan(0);
|
|
for (const file of files) {
|
|
const text = readFileSync(join(moduleDirectory, file), "utf8");
|
|
const declared = declaredModules(text);
|
|
expect(declared.length, file).toBeGreaterThan(0);
|
|
for (const specifier of declared) expect(basename(fallbackModulePath(specifier)!)).toBe(file);
|
|
// Every reference names another module file.
|
|
for (const [, reference] of text.matchAll(/^\/\/\/ <reference path="\.\/([^"]+)" \/>$/gm))
|
|
expect(existsSync(join(moduleDirectory, reference!)), `${file} -> ${reference}`).toBe(true);
|
|
}
|
|
});
|
|
|
|
test("the core fallback file declares no module that has a file of its own", () => {
|
|
for (const specifier of declaredModules(readFileSync(fallbackDtsPath(), "utf8")))
|
|
expect(fallbackModulePath(specifier), specifier).toBeNull();
|
|
});
|
|
|
|
test("a module named in a string literal selects its file, under either spelling", () => {
|
|
const files = fallbackModulesNamedIn(
|
|
`import { readFileSync } from "node:fs";\nconst os = require('os');\ntype T = import(\`node:stream/web\`);\nconst label = "not-a-module";`,
|
|
).map((file) => basename(file));
|
|
expect(files.sort()).toEqual(["fs.d.ts", "os.d.ts", "stream-web.d.ts"]);
|
|
expect(fallbackModulesNamedIn('console.log("hi")')).toEqual([]);
|
|
// Module names with digits.
|
|
expect(
|
|
fallbackModulesNamedIn(`require("http2"); import("node:v8");`).map((file) => basename(file)),
|
|
).toEqual(["http2.d.ts", "v8.d.ts"]);
|
|
});
|