285 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			285 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | "use strict"; | ||
|  | 
 | ||
|  | Object.defineProperty(exports, "__esModule", { | ||
|  |   value: true | ||
|  | }); | ||
|  | exports.get = get; | ||
|  | exports.minVersion = minVersion; | ||
|  | exports.getDependencies = getDependencies; | ||
|  | exports.default = exports.list = void 0; | ||
|  | 
 | ||
|  | function _traverse() { | ||
|  |   const data = _interopRequireDefault(require("@babel/traverse")); | ||
|  | 
 | ||
|  |   _traverse = function () { | ||
|  |     return data; | ||
|  |   }; | ||
|  | 
 | ||
|  |   return data; | ||
|  | } | ||
|  | 
 | ||
|  | function t() { | ||
|  |   const data = _interopRequireWildcard(require("@babel/types")); | ||
|  | 
 | ||
|  |   t = function () { | ||
|  |     return data; | ||
|  |   }; | ||
|  | 
 | ||
|  |   return data; | ||
|  | } | ||
|  | 
 | ||
|  | var _helpers = _interopRequireDefault(require("./helpers")); | ||
|  | 
 | ||
|  | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } | ||
|  | 
 | ||
|  | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
|  | 
 | ||
|  | 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("."); | ||
|  | } | ||
|  | 
 | ||
|  | 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 = []; | ||
|  |   (0, _traverse().default)(file, { | ||
|  |     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()) { | ||
|  |         if (!decl.node.id) { | ||
|  |           throw decl.buildCodeFrameError("Helpers should give names to their exported func declaration"); | ||
|  |         } | ||
|  | 
 | ||
|  |         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(); | ||
|  |     } | ||
|  | 
 | ||
|  |   }); | ||
|  |   (0, _traverse().default)(file, { | ||
|  |     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, true); | ||
|  | 
 | ||
|  |       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 && binding.scope.path.isProgram()) { | ||
|  |         exportBindingAssignments.push(makePath(child)); | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |   }); | ||
|  |   if (!exportPath) throw new Error("Helpers must default-export something."); | ||
|  |   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; | ||
|  |   } | ||
|  | 
 | ||
|  |   (0, _traverse().default)(file, { | ||
|  |     Program(path) { | ||
|  |       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") { | ||
|  |         if (decl.isFunctionDeclaration()) { | ||
|  |           exp.replaceWith(decl); | ||
|  |         } else { | ||
|  |           exp.replaceWith(t().variableDeclaration("var", [t().variableDeclarator(id, decl.node)])); | ||
|  |         } | ||
|  |       } else if (id.type === "MemberExpression") { | ||
|  |         if (decl.isFunctionDeclaration()) { | ||
|  |           exportBindingAssignments.forEach(assignPath => { | ||
|  |             const assign = path.get(assignPath); | ||
|  |             assign.replaceWith(t().assignmentExpression("=", id, assign.node)); | ||
|  |           }); | ||
|  |           exp.replaceWith(decl); | ||
|  |           path.pushContainer("body", t().expressionStatement(t().assignmentExpression("=", id, t().identifier(exportName)))); | ||
|  |         } else { | ||
|  |           exp.replaceWith(t().expressionStatement(t().assignmentExpression("=", id, decl.node))); | ||
|  |         } | ||
|  |       } 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 = t().cloneNode(dependenciesRefs[path.node.name]); | ||
|  |         path.replaceWith(node); | ||
|  |       } | ||
|  | 
 | ||
|  |       path.stop(); | ||
|  |     } | ||
|  | 
 | ||
|  |   }); | ||
|  | } | ||
|  | 
 | ||
|  | 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 = () => { | ||
|  |       return t().file(helper.ast()); | ||
|  |     }; | ||
|  | 
 | ||
|  |     const metadata = getHelperMetadata(fn()); | ||
|  |     helperData[name] = { | ||
|  |       build(getDependency, id, localBindings) { | ||
|  |         const file = fn(); | ||
|  |         permuteHelperAST(file, metadata, id, localBindings, getDependency); | ||
|  |         return { | ||
|  |           nodes: file.program.body, | ||
|  |           globals: metadata.globals | ||
|  |         }; | ||
|  |       }, | ||
|  | 
 | ||
|  |       minVersion() { | ||
|  |         return helper.minVersion; | ||
|  |       }, | ||
|  | 
 | ||
|  |       dependencies: metadata.dependencies | ||
|  |     }; | ||
|  |   } | ||
|  | 
 | ||
|  |   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 Array.from(loadHelper(name).dependencies.values()); | ||
|  | } | ||
|  | 
 | ||
|  | const list = Object.keys(_helpers.default).map(name => name.replace(/^_/, "")).filter(name => name !== "__esModule"); | ||
|  | exports.list = list; | ||
|  | var _default = get; | ||
|  | exports.default = _default; |