chore(vendor): import pi 1.3.0

Imported as a linear vendor commit so forge rebases do not replay the upstream root tree.

git-subtree-dir: third_party/pi

git-subtree-split: 71023aec1546d6d677a65669c5665c0927ce0dd0
This commit is contained in:
2026-10-15 17:58:48 -04:00
parent 719370c8f7
commit 6081e3e325
1743 changed files with 205460 additions and 131705 deletions

View File

@@ -3,9 +3,24 @@
import { existsSync, readFileSync } from "node:fs";
import { isBuiltin } from "node:module";
import { isAbsolute, join, relative, resolve } from "node:path";
import ts from "typescript";
import { SyntaxKind } from "typescript/unstable/ast";
import {
isCallExpression,
isExportDeclaration,
isIdentifier,
isImportDeclaration,
isNamedExports,
isNamedImports,
isNoSubstitutionTemplateLiteral,
isPropertyAccessExpression,
isStringLiteral,
} from "typescript/unstable/ast/is";
import { API } from "typescript/unstable/sync";
import { getPublicWorkspacePackages } from "./release-packages.mjs";
// Packages without tsconfig.build.json are checked against a synthetic config.
const fallbackConfigName = "tsconfig.runtime-deps-fallback.json";
const fallbackConfig = JSON.stringify({ include: ["src/**/*"] });
const failures = [];
function checkSource(source, manifest) {
@@ -18,9 +33,17 @@ function checkSource(source, manifest) {
]);
function checkSpecifier(node) {
if (!node || !ts.isStringLiteralLike(node)) return;
if (!node || !(isStringLiteral(node) || isNoSubstitutionTemplateLiteral(node))) return;
const specifier = node.text;
if (specifier.startsWith(".") || specifier.startsWith("/") || isBuiltin(specifier)) return;
// `cloudflare:` modules are built into the Workers runtime, like `node:` modules are into Node.
if (
specifier.startsWith(".") ||
specifier.startsWith("/") ||
specifier.startsWith("cloudflare:") ||
isBuiltin(specifier)
) {
return;
}
const name = specifier.split("/").slice(0, specifier.startsWith("@") ? 2 : 1).join("/");
if (declared.has(name)) return;
const { line } = source.getLineAndCharacterOfPosition(node.getStart(source));
@@ -28,61 +51,86 @@ function checkSource(source, manifest) {
}
function visit(node) {
if (ts.isImportDeclaration(node)) {
if (isImportDeclaration(node)) {
const clause = node.importClause;
const bindings = clause?.namedBindings;
if (
!clause ||
(!clause.isTypeOnly &&
(clause.name || !bindings || !ts.isNamedImports(bindings) ||
bindings.elements.length === 0 || bindings.elements.some((element) => !element.isTypeOnly)))
(clause.phaseModifier !== SyntaxKind.TypeKeyword &&
(clause.name ||
!bindings ||
!isNamedImports(bindings) ||
bindings.elements.length === 0 ||
bindings.elements.some((element) => !element.isTypeOnly)))
) {
checkSpecifier(node.moduleSpecifier);
}
} else if (ts.isExportDeclaration(node) && !node.isTypeOnly) {
} else if (isExportDeclaration(node) && !node.isTypeOnly) {
const clause = node.exportClause;
if (!clause || !ts.isNamedExports(clause) || clause.elements.length === 0 || clause.elements.some((element) => !element.isTypeOnly)) {
if (
!clause ||
!isNamedExports(clause) ||
clause.elements.length === 0 ||
clause.elements.some((element) => !element.isTypeOnly)
) {
checkSpecifier(node.moduleSpecifier);
}
} else if (
ts.isCallExpression(node) &&
(node.expression.kind === ts.SyntaxKind.ImportKeyword ||
(ts.isIdentifier(node.expression) && node.expression.text === "require") ||
(ts.isPropertyAccessExpression(node.expression) && node.expression.getText(source) === "require.resolve"))
isCallExpression(node) &&
(node.expression.kind === SyntaxKind.ImportKeyword ||
(isIdentifier(node.expression) && node.expression.text === "require") ||
(isPropertyAccessExpression(node.expression) && node.expression.getText(source) === "require.resolve"))
) {
checkSpecifier(node.arguments[0]);
}
ts.forEachChild(node, visit);
node.forEachChild(visit);
}
visit(source);
}
for (const { directory } of getPublicWorkspacePackages()) {
const sourceDirectory = resolve(directory, "src");
if (!existsSync(sourceDirectory)) continue;
const manifest = JSON.parse(readFileSync(join(directory, "package.json"), "utf8"));
const configPath = join(directory, "tsconfig.build.json");
const config = existsSync(configPath)
? ts.readConfigFile(configPath, ts.sys.readFile)
: { config: { include: ["src/**/*"] } };
if (config.error) throw new Error(ts.flattenDiagnosticMessageText(config.error.messageText, "\n"));
const parsed = ts.parseJsonConfigFileContent(config.config, ts.sys, resolve(directory));
if (parsed.errors.length > 0) {
throw new Error(parsed.errors.map((error) => ts.flattenDiagnosticMessageText(error.messageText, "\n")).join("\n"));
}
const roots = new Set(parsed.fileNames.map((file) => resolve(file)));
const program = ts.createProgram(parsed.fileNames, parsed.options);
for (const source of program.getSourceFiles()) {
if (source.isDeclarationFile || source.fileName.endsWith(".json")) continue;
const path = relative(sourceDirectory, resolve(source.fileName));
if (path.startsWith("..") || isAbsolute(path)) continue;
// TypeScript's exclude only filters roots: imports can pull excluded files
// back into the build. Reject that too, including type-only imports.
if (!roots.has(resolve(source.fileName))) {
failures.push(`${source.fileName} is excluded from ${manifest.name}'s build but imported by it`);
const packages = getPublicWorkspacePackages()
.map(({ directory }) => ({ directory, sourceDirectory: resolve(directory, "src") }))
.filter(({ sourceDirectory }) => existsSync(sourceDirectory))
.map(({ directory, sourceDirectory }) => {
const configPath = resolve(directory, "tsconfig.build.json");
return {
sourceDirectory,
manifest: JSON.parse(readFileSync(join(directory, "package.json"), "utf8")),
configPath: existsSync(configPath) ? configPath : resolve(directory, fallbackConfigName),
};
});
const fallbackConfigs = new Set(
packages.map(({ configPath }) => configPath).filter((path) => path.endsWith(fallbackConfigName)),
);
const api = new API({
cwd: process.cwd(),
fs: {
fileExists: (fileName) => (fallbackConfigs.has(resolve(fileName)) ? true : undefined),
readFile: (fileName) => (fallbackConfigs.has(resolve(fileName)) ? fallbackConfig : undefined),
},
});
try {
const snapshot = api.updateSnapshot({ openProjects: packages.map(({ configPath }) => configPath) });
for (const { sourceDirectory, manifest, configPath } of packages) {
const project = snapshot.getProject(configPath);
const diagnostics = project.program.getConfigFileParsingDiagnostics();
if (diagnostics.length > 0) throw new Error(diagnostics.map((diagnostic) => diagnostic.text).join("\n"));
const roots = new Set(project.rootFiles.map((file) => resolve(file)));
for (const fileName of project.program.getSourceFileNames()) {
if (fileName.endsWith(".d.ts") || fileName.endsWith(".json")) continue;
const path = relative(sourceDirectory, resolve(fileName));
if (path.startsWith("..") || isAbsolute(path)) continue;
// TypeScript's exclude only filters roots: imports can pull excluded files
// back into the build. Reject that too, including type-only imports.
if (!roots.has(resolve(fileName))) {
failures.push(`${fileName} is excluded from ${manifest.name}'s build but imported by it`);
}
checkSource(project.program.getSourceFile(fileName), manifest);
}
checkSource(source, manifest);
}
} finally {
api.close();
}
if (failures.length > 0) {