diff options
| author | littleblack111 <littleblack11111@gmail.com> | 2024-08-30 17:21:18 +0800 |
|---|---|---|
| committer | littleblack111 <littleblack11111@gmail.com> | 2024-08-30 17:21:18 +0800 |
| commit | efbc2ffe6a1ee5ec38a4396305561d6515d0943e (patch) | |
| tree | 2b4c94f18f2c792dfda813050755d5c0273fae22 /node_modules/@babel/helpers/scripts | |
| parent | 43acceb75957c880a5ba5a31aae221394a36d5f6 (diff) | |
node fixes, updates
Diffstat (limited to 'node_modules/@babel/helpers/scripts')
4 files changed, 302 insertions, 18 deletions
diff --git a/node_modules/@babel/helpers/scripts/build-helper-metadata.js b/node_modules/@babel/helpers/scripts/build-helper-metadata.js new file mode 100644 index 00000000..6a54fa0e --- /dev/null +++ b/node_modules/@babel/helpers/scripts/build-helper-metadata.js @@ -0,0 +1,209 @@ +// NOTE: This file must be compatible with old Node.js versions, since it runs +// during testing. + +/** + * @typedef {Object} HelperMetadata + * @property {string[]} globals + * @property {{ [name: string]: string[] }} locals + * @property {{ [name: string]: string[] }} dependencies + * @property {string[]} exportBindingAssignments + * @property {string} exportName + */ + +/** + * Given a file AST for a given helper, get a bunch of metadata about it so that Babel can quickly render + * the helper is whatever context it is needed in. + * + * @param {typeof import("@babel/core")} babel + * + * @returns {HelperMetadata} + */ +export function getHelperMetadata(babel, code, helperName) { + const globals = new Set(); + // Maps imported identifier name -> helper name + const dependenciesBindings = new Map(); + + let exportName; + const exportBindingAssignments = []; + // helper name -> reference paths + const dependencies = new Map(); + // local variable name -> reference paths + const locals = new Map(); + + const spansToRemove = []; + + const validateDefaultExport = decl => { + if (exportName) { + throw new Error( + `Helpers can have only one default export (in ${helperName})` + ); + } + + if (!decl.isFunctionDeclaration() || !decl.node.id) { + throw new Error( + `Helpers can only export named function declarations (in ${helperName})` + ); + } + }; + + /** @type {import("@babel/traverse").Visitor} */ + const dependencyVisitor = { + Program(path) { + for (const child of path.get("body")) { + if (child.isImportDeclaration()) { + if ( + child.get("specifiers").length !== 1 || + !child.get("specifiers.0").isImportDefaultSpecifier() + ) { + throw new Error( + `Helpers can only import a default value (in ${helperName})` + ); + } + dependenciesBindings.set( + child.node.specifiers[0].local.name, + child.node.source.value + ); + dependencies.set(child.node.source.value, []); + spansToRemove.push([child.node.start, child.node.end]); + child.remove(); + } + } + for (const child of path.get("body")) { + if (child.isExportDefaultDeclaration()) { + const decl = child.get("declaration"); + validateDefaultExport(decl); + + exportName = decl.node.id.name; + spansToRemove.push([child.node.start, decl.node.start]); + child.replaceWith(decl.node); + } else if ( + child.isExportNamedDeclaration() && + child.node.specifiers.length === 1 && + child.get("specifiers.0.exported").isIdentifier({ name: "default" }) + ) { + const { name } = child.node.specifiers[0].local; + + validateDefaultExport(child.scope.getBinding(name).path); + + exportName = name; + spansToRemove.push([child.node.start, child.node.end]); + child.remove(); + } else if ( + process.env.IS_BABEL_OLD_E2E && + child.isExportNamedDeclaration() && + child.node.specifiers.length === 0 + ) { + spansToRemove.push([child.node.start, child.node.end]); + child.remove(); + } else if ( + child.isExportAllDeclaration() || + child.isExportNamedDeclaration() + ) { + throw new Error(`Helpers can only export default (in ${helperName})`); + } + } + + path.scope.crawl(); + + const bindings = path.scope.getAllBindings(); + Object.keys(bindings).forEach(name => { + if (dependencies.has(name)) return; + + const binding = bindings[name]; + + const references = [ + ...binding.path.getBindingIdentifierPaths(true)[name].map(makePath), + ...binding.referencePaths.map(makePath), + ]; + for (const violation of binding.constantViolations) { + violation.getBindingIdentifierPaths(true)[name].forEach(path => { + references.push(makePath(path)); + }); + } + + locals.set(name, references); + }); + }, + ReferencedIdentifier(child) { + const name = child.node.name; + const binding = child.scope.getBinding(name); + if (!binding) { + if (dependenciesBindings.has(name)) { + dependencies + .get(dependenciesBindings.get(name)) + .push(makePath(child)); + } else if (name !== "arguments" || child.scope.path.isProgram()) { + globals.add(name); + } + } + }, + AssignmentExpression(child) { + const left = child.get("left"); + + if (!(exportName in left.getBindingIdentifiers())) return; + + if (!left.isIdentifier()) { + throw new Error( + `Only simple assignments to exports are allowed in helpers (in ${helperName})` + ); + } + + const binding = child.scope.getBinding(exportName); + + if (binding && binding.scope.path.isProgram()) { + exportBindingAssignments.push(makePath(child)); + } + }, + }; + + babel.transformSync(code, { + configFile: false, + babelrc: false, + plugins: [() => ({ visitor: dependencyVisitor })], + }); + + if (!exportName) throw new Error("Helpers must have a named default export."); + + // Process these in reverse so that mutating the references does not invalidate any later paths in + // the list. + exportBindingAssignments.reverse(); + + spansToRemove.sort(([start1], [start2]) => start2 - start1); + for (const [start, end] of spansToRemove) { + code = code.slice(0, start) + code.slice(end); + } + + return [ + code, + { + globals: Array.from(globals), + locals: Object.fromEntries(locals), + dependencies: Object.fromEntries(dependencies), + exportBindingAssignments, + exportName, + }, + ]; +} + +function makePath(path) { + const parts = []; + + for (; path.parentPath; path = path.parentPath) { + parts.push(path.key); + if (path.inList) parts.push(path.listKey); + } + + return parts.reverse().join("."); +} + +export function stringifyMetadata(metadata) { + return `\ + { + globals: ${JSON.stringify(metadata.globals)}, + locals: ${JSON.stringify(metadata.locals)}, + exportBindingAssignments: ${JSON.stringify(metadata.exportBindingAssignments)}, + exportName: ${JSON.stringify(metadata.exportName)}, + dependencies: ${JSON.stringify(metadata.dependencies)}, + } + `; +} diff --git a/node_modules/@babel/helpers/scripts/generate-helpers.js b/node_modules/@babel/helpers/scripts/generate-helpers.js index 45182c95..9eeb95a2 100644 --- a/node_modules/@babel/helpers/scripts/generate-helpers.js +++ b/node_modules/@babel/helpers/scripts/generate-helpers.js @@ -3,13 +3,17 @@ import fs from "fs"; import { join } from "path"; import { URL, fileURLToPath } from "url"; import { minify } from "terser"; -import { transformSync } from "@babel/core"; -import presetTypescript from "@babel/preset-typescript"; -import { gzipSync } from "zlib"; +import { babel, presetTypescript } from "$repo-utils/babel-top-level"; import { IS_BABEL_8 } from "$repo-utils"; +import { gzipSync } from "zlib"; + +import { + getHelperMetadata, + stringifyMetadata, +} from "./build-helper-metadata.js"; const HELPERS_FOLDER = new URL("../src/helpers", import.meta.url); -const IGNORED_FILES = new Set(["package.json"]); +const IGNORED_FILES = new Set(["package.json", "tsconfig.json"]); export default async function generateHelpers() { let output = `/* @@ -18,17 +22,37 @@ export default async function generateHelpers() { */ import template from "@babel/template"; +import type * as t from "@babel/types"; + +interface Helper { + minVersion: string; + ast: () => t.Program; + metadata: HelperMetadata; +} + +export interface HelperMetadata { + globals: string[]; + locals: { [name: string]: string[] }; + dependencies: { [name: string]: string[] }; + exportBindingAssignments: string[]; + exportName: string; +} -function helper(minVersion: string, source: string) { +function helper(minVersion: string, source: string, metadata: HelperMetadata): Helper { return Object.freeze({ minVersion, ast: () => template.program.ast(source, { preserveComments: true }), + metadata, }) } -export default Object.freeze({ +export { helpers as default }; +const helpers: Record<string, Helper> = { + __proto__: null, `; + let babel7extraOutput = ""; + for (const file of (await fs.promises.readdir(HELPERS_FOLDER)).sort()) { if (IGNORED_FILES.has(file)) continue; if (file.startsWith(".")) continue; // ignore e.g. vim swap files @@ -52,14 +76,11 @@ export default Object.freeze({ } const { minVersion } = minVersionMatch.groups; - if (IS_BABEL_8() && code.includes("@onlyBabel7")) { - continue; - } - + const onlyBabel7 = code.includes("@onlyBabel7"); const mangleFns = code.includes("@mangleFns"); const noMangleFns = []; - code = transformSync(code, { + code = babel.transformSync(code, { configFile: false, babelrc: false, filename: filePath, @@ -76,7 +97,41 @@ export default Object.freeze({ /** * @type {import("@babel/core").PluginObj} */ - { + ({ types: t }) => ({ + // These pre/post hooks are needed because the TS transform is, + // when building in the old Babel e2e test, removing the + // `export { OverloadYield as default }` in the OverloadYield helper. + // TODO: Remove in Babel 8. + pre(file) { + if (!process.env.IS_BABEL_OLD_E2E) return; + file.metadata.exportName = null; + file.path.traverse({ + ExportSpecifier(path) { + if (path.node.exported.name === "default") { + file.metadata.exportName = path.node.local.name; + } + }, + }); + }, + post(file) { + if (!process.env.IS_BABEL_OLD_E2E) return; + if (!file.metadata.exportName) return; + file.path.traverse({ + ExportNamedDeclaration(path) { + if ( + !path.node.declaration && + path.node.specifiers.length === 0 + ) { + path.node.specifiers.push( + t.exportSpecifier( + t.identifier(file.metadata.exportName), + t.identifier("default") + ) + ); + } + }, + }); + }, visitor: { ImportDeclaration(path) { const source = path.node.source; @@ -96,7 +151,7 @@ export default Object.freeze({ } }, }, - }, + }), ], }).code; code = ( @@ -115,15 +170,38 @@ export default Object.freeze({ }) ).code; - output += `\ + let metadata; + // eslint-disable-next-line prefer-const + [code, metadata] = getHelperMetadata(babel, code, helperName); + + const helperStr = `\ // size: ${code.length}, gzip size: ${gzipSync(code).length} ${JSON.stringify(helperName)}: helper( ${JSON.stringify(minVersion)}, ${JSON.stringify(code)}, + ${stringifyMetadata(metadata)} ), `; + + if (onlyBabel7) { + if (!IS_BABEL_8()) babel7extraOutput += helperStr; + } else { + output += helperStr; + } + } + + output += "};"; + + if (babel7extraOutput) { + output += ` + +if (!process.env.BABEL_8_BREAKING) { + Object.assign(helpers, { + ${babel7extraOutput} + }); +} +`; } - output += "});"; return output; } diff --git a/node_modules/@babel/helpers/scripts/generate-regenerator-runtime.js b/node_modules/@babel/helpers/scripts/generate-regenerator-runtime.js index 34a46d63..73c301ce 100644 --- a/node_modules/@babel/helpers/scripts/generate-regenerator-runtime.js +++ b/node_modules/@babel/helpers/scripts/generate-regenerator-runtime.js @@ -1,5 +1,3 @@ -/* eslint-disable import/no-extraneous-dependencies */ - import fs from "fs"; import { createRequire } from "module"; diff --git a/node_modules/@babel/helpers/scripts/package.json b/node_modules/@babel/helpers/scripts/package.json deleted file mode 100644 index 5ffd9800..00000000 --- a/node_modules/@babel/helpers/scripts/package.json +++ /dev/null @@ -1 +0,0 @@ -{ "type": "module" } |
