"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; exports.ensure = ensure; exports.get = get; exports.getDependencies = getDependencies; exports.list = void 0; exports.minVersion = minVersion; var _traverse = require("@babel/traverse"); var _t = require("@babel/types"); var _helpers = require("./helpers"); const { assignmentExpression, cloneNode, expressionStatement, file, identifier } = _t; 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("."); } let FileClass = undefined; function getHelperMetadata(file) { const globals = new Set(); const localBindingNames = new Set(); const dependencies = new Map(); let exportName; let exportPath; const exportBindingAssignments = []; const importPaths = []; const importBindingsReferences = []; const dependencyVisitor = { ImportDeclaration(child) { const name = child.node.source.value; if (!_helpers.default[name]) { throw child.buildCodeFrameError(`Unknown helper ${name}`); } if (child.get("specifiers").length !== 1 || !child.get("specifiers.0").isImportDefaultSpecifier()) { throw child.buildCodeFrameError("Helpers can only import a default value"); } const bindingIdentifier = child.node.specifiers[0].local; dependencies.set(bindingIdentifier, name); importPaths.push(makePath(child)); }, ExportDefaultDeclaration(child) { const decl = child.get("declaration"); if (!decl.isFunctionDeclaration() || !decl.node.id) { throw decl.buildCodeFrameError("Helpers can only export named function declarations"); } exportName = decl.node.id.name; exportPath = makePath(child); }, ExportAllDeclaration(child) { throw child.buildCodeFrameError("Helpers can only export default"); }, ExportNamedDeclaration(child) { throw child.buildCodeFrameError("Helpers can only export default"); }, Statement(child) { if (child.isModuleDeclaration()) return; child.skip(); } }; const referenceVisitor = { Program(path) { const bindings = path.scope.getAllBindings(); Object.keys(bindings).forEach(name => { if (name === exportName) return; if (dependencies.has(bindings[name].identifier)) return; localBindingNames.add(name); }); }, ReferencedIdentifier(child) { const name = child.node.name; const binding = child.scope.getBinding(name); if (!binding) { globals.add(name); } else if (dependencies.has(binding.identifier)) { importBindingsReferences.push(makePath(child)); } }, AssignmentExpression(child) { const left = child.get("left"); if (!(exportName in left.getBindingIdentifiers())) return; if (!left.isIdentifier()) { throw left.buildCodeFrameError("Only simple assignments to exports are allowed in helpers"); } const binding = child.scope.getBinding(exportName); if (binding != null && binding.scope.path.isProgram()) { exportBindingAssignments.push(makePath(child)); } } }; (0, _traverse.default)(file.ast, dependencyVisitor, file.scope); (0, _traverse.default)(file.ast, referenceVisitor, file.scope); if (!exportPath) throw new Error("Helpers must have a default export."); exportBindingAssignments.reverse(); return { globals: Array.from(globals), localBindingNames: Array.from(localBindingNames), dependencies, exportBindingAssignments, exportPath, exportName, importBindingsReferences, importPaths }; } function permuteHelperAST(file, metadata, id, localBindings, getDependency) { if (localBindings && !id) { throw new Error("Unexpected local bindings for module-based helpers."); } if (!id) return; const { localBindingNames, dependencies, exportBindingAssignments, exportPath, exportName, importBindingsReferences, importPaths } = metadata; const dependenciesRefs = {}; dependencies.forEach((name, id) => { dependenciesRefs[id.name] = typeof getDependency === "function" && getDependency(name) || id; }); const toRename = {}; const bindings = new Set(localBindings || []); localBindingNames.forEach(name => { let newName = name; while (bindings.has(newName)) newName = "_" + newName; if (newName !== name) toRename[name] = newName; }); if (id.type === "Identifier" && exportName !== id.name) { toRename[exportName] = id.name; } const { path } = file; const exp = path.get(exportPath); const imps = importPaths.map(p => path.get(p)); const impsBindingRefs = importBindingsReferences.map(p => path.get(p)); const decl = exp.get("declaration"); if (id.type === "Identifier") { exp.replaceWith(decl); } else if (id.type === "MemberExpression") { exportBindingAssignments.forEach(assignPath => { const assign = path.get(assignPath); assign.replaceWith(assignmentExpression("=", id, assign.node)); }); exp.replaceWith(decl); path.pushContainer("body", expressionStatement(assignmentExpression("=", id, identifier(exportName)))); } else { throw new Error("Unexpected helper format."); } Object.keys(toRename).forEach(name => { path.scope.rename(name, toRename[name]); }); for (const path of imps) path.remove(); for (const path of impsBindingRefs) { const node = cloneNode(dependenciesRefs[path.node.name]); path.replaceWith(node); } } const helperData = Object.create(null); function loadHelper(name) { if (!helperData[name]) { const helper = _helpers.default[name]; if (!helper) { throw Object.assign(new ReferenceError(`Unknown helper ${name}`), { code: "BABEL_HELPER_UNKNOWN", helper: name }); } const fn = () => { { if (!FileClass) { const fakeFile = { ast: file(helper.ast()), path: null }; (0, _traverse.default)(fakeFile.ast, { Program: path => (fakeFile.path = path).stop() }); return fakeFile; } } return new FileClass({ filename: `babel-helper://${name}` }, { ast: file(helper.ast()), code: "[internal Babel helper code]", inputMap: null }); }; let metadata = null; helperData[name] = { minVersion: helper.minVersion, build(getDependency, id, localBindings) { const file = fn(); metadata || (metadata = getHelperMetadata(file)); permuteHelperAST(file, metadata, id, localBindings, getDependency); return { nodes: file.ast.program.body, globals: metadata.globals }; }, getDependencies() { metadata || (metadata = getHelperMetadata(fn())); return Array.from(metadata.dependencies.values()); } }; } return helperData[name]; } function get(name, getDependency, id, localBindings) { return loadHelper(name).build(getDependency, id, localBindings); } function minVersion(name) { return loadHelper(name).minVersion; } function getDependencies(name) { return loadHelper(name).getDependencies(); } function ensure(name, newFileClass) { FileClass || (FileClass = newFileClass); loadHelper(name); } const list = Object.keys(_helpers.default).map(name => name.replace(/^_/, "")); exports.list = list; var _default = get; exports.default = _default;