import { readFileSync, readdirSync } from "node:fs"; import { dirname, resolve } from "node:path"; import { pathToFileURL } from "node:url"; import ts from "typescript"; // macOS 11 can update to Safari 16. Keep this floor in sync with vite.config.ts. // Syntax lowering does not replace newer runtime APIs in dependencies. const newerStatics = new Set([ "Promise.withResolvers", "Object.groupBy", "Map.groupBy", "Array.fromAsync", "Atomics.waitAsync", "CSS.registerProperty", "Uint8Array.fromBase64", "Uint8Array.fromHex", ]); const newerMethods = new Set(["isWellFormed", "toWellFormed"]); export function checkSource(source: string, startup: boolean): string[] { const tree = ts.createSourceFile("bundle.js", source, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS); const errors = new Set(); const checkRegex = (pattern: string, flags: string) => { if (flags.includes("v")) errors.add("RegExp v flag requires Safari 17"); if (/\(\?<([=!])/.test(pattern)) errors.add("RegExp lookbehind requires Safari 16.4"); }; const walk = (node: ts.Node) => { if (ts.isRegularExpressionLiteral(node)) { const end = node.text.lastIndexOf("/"); checkRegex(node.text.slice(1, end), node.text.slice(end + 1)); } if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) && node.attributes) { errors.add("Import attributes require newer WebKit"); } if (startup && ts.isPropertyAccessExpression(node)) { const name = node.getText(tree); if (newerStatics.has(name) || newerMethods.has(node.name.text)) errors.add(`${name} requires newer WebKit`); } if ( startup && (ts.isNewExpression(node) || ts.isCallExpression(node)) && node.expression.getText(tree) === "RegExp" && node.arguments?.[0] && ts.isStringLiteralLike(node.arguments[0]) ) { const flags = node.arguments[1]; checkRegex(node.arguments[0].text, flags && ts.isStringLiteralLike(flags) ? flags.text : ""); } ts.forEachChild(node, walk); }; walk(tree); return [...errors]; } export function checkBuild(dir: string): string[] { const entry = readFileSync(resolve(dir, "index.html"), "utf8").match(/