You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					248 lines
				
				7.5 KiB
			
		
		
			
		
	
	
					248 lines
				
				7.5 KiB
			| 
											3 years ago
										 | "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.isImportDeclaration() || child.isExportDeclaration()) 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; | ||
|  | 
 | ||
|  | //# sourceMappingURL=index.js.map
 |