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.
		
		
		
		
		
			
		
			
				
					366 lines
				
				12 KiB
			
		
		
			
		
	
	
					366 lines
				
				12 KiB
			| 
											3 years ago
										 | "use strict"; | ||
|  | 
 | ||
|  | Object.defineProperty(exports, "__esModule", { | ||
|  |   value: true | ||
|  | }); | ||
|  | exports.default = rewriteLiveReferences; | ||
|  | var _assert = require("assert"); | ||
|  | var _t = require("@babel/types"); | ||
|  | var _template = require("@babel/template"); | ||
|  | var _helperSimpleAccess = require("@babel/helper-simple-access"); | ||
|  | const { | ||
|  |   assignmentExpression, | ||
|  |   callExpression, | ||
|  |   cloneNode, | ||
|  |   expressionStatement, | ||
|  |   getOuterBindingIdentifiers, | ||
|  |   identifier, | ||
|  |   isMemberExpression, | ||
|  |   isVariableDeclaration, | ||
|  |   jsxIdentifier, | ||
|  |   jsxMemberExpression, | ||
|  |   memberExpression, | ||
|  |   numericLiteral, | ||
|  |   sequenceExpression, | ||
|  |   stringLiteral, | ||
|  |   variableDeclaration, | ||
|  |   variableDeclarator | ||
|  | } = _t; | ||
|  | function isInType(path) { | ||
|  |   do { | ||
|  |     switch (path.parent.type) { | ||
|  |       case "TSTypeAnnotation": | ||
|  |       case "TSTypeAliasDeclaration": | ||
|  |       case "TSTypeReference": | ||
|  |       case "TypeAnnotation": | ||
|  |       case "TypeAlias": | ||
|  |         return true; | ||
|  |       case "ExportSpecifier": | ||
|  |         return path.parentPath.parent.exportKind === "type"; | ||
|  |       default: | ||
|  |         if (path.parentPath.isStatement() || path.parentPath.isExpression()) { | ||
|  |           return false; | ||
|  |         } | ||
|  |     } | ||
|  |   } while (path = path.parentPath); | ||
|  | } | ||
|  | function rewriteLiveReferences(programPath, metadata) { | ||
|  |   const imported = new Map(); | ||
|  |   const exported = new Map(); | ||
|  |   const requeueInParent = path => { | ||
|  |     programPath.requeue(path); | ||
|  |   }; | ||
|  |   for (const [source, data] of metadata.source) { | ||
|  |     for (const [localName, importName] of data.imports) { | ||
|  |       imported.set(localName, [source, importName, null]); | ||
|  |     } | ||
|  |     for (const localName of data.importsNamespace) { | ||
|  |       imported.set(localName, [source, null, localName]); | ||
|  |     } | ||
|  |   } | ||
|  |   for (const [local, data] of metadata.local) { | ||
|  |     let exportMeta = exported.get(local); | ||
|  |     if (!exportMeta) { | ||
|  |       exportMeta = []; | ||
|  |       exported.set(local, exportMeta); | ||
|  |     } | ||
|  |     exportMeta.push(...data.names); | ||
|  |   } | ||
|  | 
 | ||
|  |   const rewriteBindingInitVisitorState = { | ||
|  |     metadata, | ||
|  |     requeueInParent, | ||
|  |     scope: programPath.scope, | ||
|  |     exported | ||
|  |   }; | ||
|  | 
 | ||
|  |   programPath.traverse( | ||
|  |   rewriteBindingInitVisitor, rewriteBindingInitVisitorState); | ||
|  |   (0, _helperSimpleAccess.default)(programPath, | ||
|  |   new Set([...Array.from(imported.keys()), ...Array.from(exported.keys())]), false); | ||
|  | 
 | ||
|  |   const rewriteReferencesVisitorState = { | ||
|  |     seen: new WeakSet(), | ||
|  |     metadata, | ||
|  |     requeueInParent, | ||
|  |     scope: programPath.scope, | ||
|  |     imported, | ||
|  |     exported, | ||
|  |     buildImportReference: ([source, importName, localName], identNode) => { | ||
|  |       const meta = metadata.source.get(source); | ||
|  |       if (localName) { | ||
|  |         if (meta.lazy) { | ||
|  |           identNode = callExpression( | ||
|  |           identNode, []); | ||
|  |         } | ||
|  |         return identNode; | ||
|  |       } | ||
|  |       let namespace = identifier(meta.name); | ||
|  |       if (meta.lazy) namespace = callExpression(namespace, []); | ||
|  |       if (importName === "default" && meta.interop === "node-default") { | ||
|  |         return namespace; | ||
|  |       } | ||
|  |       const computed = metadata.stringSpecifiers.has(importName); | ||
|  |       return memberExpression(namespace, computed ? stringLiteral(importName) : identifier(importName), computed); | ||
|  |     } | ||
|  |   }; | ||
|  |   programPath.traverse(rewriteReferencesVisitor, rewriteReferencesVisitorState); | ||
|  | } | ||
|  | 
 | ||
|  | const rewriteBindingInitVisitor = { | ||
|  |   Scope(path) { | ||
|  |     path.skip(); | ||
|  |   }, | ||
|  |   ClassDeclaration(path) { | ||
|  |     const { | ||
|  |       requeueInParent, | ||
|  |       exported, | ||
|  |       metadata | ||
|  |     } = this; | ||
|  |     const { | ||
|  |       id | ||
|  |     } = path.node; | ||
|  |     if (!id) throw new Error("Expected class to have a name"); | ||
|  |     const localName = id.name; | ||
|  |     const exportNames = exported.get(localName) || []; | ||
|  |     if (exportNames.length > 0) { | ||
|  |       const statement = expressionStatement( | ||
|  |       buildBindingExportAssignmentExpression(metadata, exportNames, identifier(localName), path.scope)); | ||
|  |       statement._blockHoist = path.node._blockHoist; | ||
|  |       requeueInParent(path.insertAfter(statement)[0]); | ||
|  |     } | ||
|  |   }, | ||
|  |   VariableDeclaration(path) { | ||
|  |     const { | ||
|  |       requeueInParent, | ||
|  |       exported, | ||
|  |       metadata | ||
|  |     } = this; | ||
|  |     Object.keys(path.getOuterBindingIdentifiers()).forEach(localName => { | ||
|  |       const exportNames = exported.get(localName) || []; | ||
|  |       if (exportNames.length > 0) { | ||
|  |         const statement = expressionStatement( | ||
|  |         buildBindingExportAssignmentExpression(metadata, exportNames, identifier(localName), path.scope)); | ||
|  |         statement._blockHoist = path.node._blockHoist; | ||
|  |         requeueInParent(path.insertAfter(statement)[0]); | ||
|  |       } | ||
|  |     }); | ||
|  |   } | ||
|  | }; | ||
|  | const buildBindingExportAssignmentExpression = (metadata, exportNames, localExpr, scope) => { | ||
|  |   const exportsObjectName = metadata.exportName; | ||
|  |   for (let currentScope = scope; currentScope != null; currentScope = currentScope.parent) { | ||
|  |     if (currentScope.hasOwnBinding(exportsObjectName)) { | ||
|  |       currentScope.rename(exportsObjectName); | ||
|  |     } | ||
|  |   } | ||
|  |   return (exportNames || []).reduce((expr, exportName) => { | ||
|  |     const { | ||
|  |       stringSpecifiers | ||
|  |     } = metadata; | ||
|  |     const computed = stringSpecifiers.has(exportName); | ||
|  |     return assignmentExpression("=", memberExpression(identifier(exportsObjectName), computed ? stringLiteral(exportName) : identifier(exportName), computed), expr); | ||
|  |   }, localExpr); | ||
|  | }; | ||
|  | const buildImportThrow = localName => { | ||
|  |   return _template.default.expression.ast`
 | ||
|  |     (function() { | ||
|  |       throw new Error('"' + '${localName}' + '" is read-only.'); | ||
|  |     })() | ||
|  |   `;
 | ||
|  | }; | ||
|  | const rewriteReferencesVisitor = { | ||
|  |   ReferencedIdentifier(path) { | ||
|  |     const { | ||
|  |       seen, | ||
|  |       buildImportReference, | ||
|  |       scope, | ||
|  |       imported, | ||
|  |       requeueInParent | ||
|  |     } = this; | ||
|  |     if (seen.has(path.node)) return; | ||
|  |     seen.add(path.node); | ||
|  |     const localName = path.node.name; | ||
|  |     const importData = imported.get(localName); | ||
|  |     if (importData) { | ||
|  |       if (isInType(path)) { | ||
|  |         throw path.buildCodeFrameError(`Cannot transform the imported binding "${localName}" since it's also used in a type annotation. ` + `Please strip type annotations using @babel/preset-typescript or @babel/preset-flow.`); | ||
|  |       } | ||
|  |       const localBinding = path.scope.getBinding(localName); | ||
|  |       const rootBinding = scope.getBinding(localName); | ||
|  | 
 | ||
|  |       if (rootBinding !== localBinding) return; | ||
|  |       const ref = buildImportReference(importData, path.node); | ||
|  | 
 | ||
|  |       ref.loc = path.node.loc; | ||
|  |       if ((path.parentPath.isCallExpression({ | ||
|  |         callee: path.node | ||
|  |       }) || path.parentPath.isOptionalCallExpression({ | ||
|  |         callee: path.node | ||
|  |       }) || path.parentPath.isTaggedTemplateExpression({ | ||
|  |         tag: path.node | ||
|  |       })) && isMemberExpression(ref)) { | ||
|  |         path.replaceWith(sequenceExpression([numericLiteral(0), ref])); | ||
|  |       } else if (path.isJSXIdentifier() && isMemberExpression(ref)) { | ||
|  |         const { | ||
|  |           object, | ||
|  |           property | ||
|  |         } = ref; | ||
|  |         path.replaceWith(jsxMemberExpression( | ||
|  |         jsxIdentifier(object.name), | ||
|  |         jsxIdentifier(property.name))); | ||
|  |       } else { | ||
|  |         path.replaceWith(ref); | ||
|  |       } | ||
|  |       requeueInParent(path); | ||
|  | 
 | ||
|  |       path.skip(); | ||
|  |     } | ||
|  |   }, | ||
|  |   UpdateExpression(path) { | ||
|  |     const { | ||
|  |       scope, | ||
|  |       seen, | ||
|  |       imported, | ||
|  |       exported, | ||
|  |       requeueInParent, | ||
|  |       buildImportReference | ||
|  |     } = this; | ||
|  |     if (seen.has(path.node)) return; | ||
|  |     seen.add(path.node); | ||
|  |     const arg = path.get("argument"); | ||
|  | 
 | ||
|  |     if (arg.isMemberExpression()) return; | ||
|  |     const update = path.node; | ||
|  |     if (arg.isIdentifier()) { | ||
|  |       const localName = arg.node.name; | ||
|  | 
 | ||
|  |       if (scope.getBinding(localName) !== path.scope.getBinding(localName)) { | ||
|  |         return; | ||
|  |       } | ||
|  |       const exportedNames = exported.get(localName); | ||
|  |       const importData = imported.get(localName); | ||
|  |       if ((exportedNames == null ? void 0 : exportedNames.length) > 0 || importData) { | ||
|  |         if (importData) { | ||
|  |           path.replaceWith(assignmentExpression(update.operator[0] + "=", buildImportReference(importData, arg.node), buildImportThrow(localName))); | ||
|  |         } else if (update.prefix) { | ||
|  |           path.replaceWith(buildBindingExportAssignmentExpression(this.metadata, exportedNames, cloneNode(update), path.scope)); | ||
|  |         } else { | ||
|  |           const ref = scope.generateDeclaredUidIdentifier(localName); | ||
|  |           path.replaceWith(sequenceExpression([assignmentExpression("=", cloneNode(ref), cloneNode(update)), buildBindingExportAssignmentExpression(this.metadata, exportedNames, identifier(localName), path.scope), cloneNode(ref)])); | ||
|  |         } | ||
|  |       } | ||
|  |     } | ||
|  |     requeueInParent(path); | ||
|  |     path.skip(); | ||
|  |   }, | ||
|  |   AssignmentExpression: { | ||
|  |     exit(path) { | ||
|  |       const { | ||
|  |         scope, | ||
|  |         seen, | ||
|  |         imported, | ||
|  |         exported, | ||
|  |         requeueInParent, | ||
|  |         buildImportReference | ||
|  |       } = this; | ||
|  |       if (seen.has(path.node)) return; | ||
|  |       seen.add(path.node); | ||
|  |       const left = path.get("left"); | ||
|  | 
 | ||
|  |       if (left.isMemberExpression()) return; | ||
|  |       if (left.isIdentifier()) { | ||
|  |         const localName = left.node.name; | ||
|  | 
 | ||
|  |         if (scope.getBinding(localName) !== path.scope.getBinding(localName)) { | ||
|  |           return; | ||
|  |         } | ||
|  |         const exportedNames = exported.get(localName); | ||
|  |         const importData = imported.get(localName); | ||
|  |         if ((exportedNames == null ? void 0 : exportedNames.length) > 0 || importData) { | ||
|  |           _assert(path.node.operator === "=", "Path was not simplified"); | ||
|  |           const assignment = path.node; | ||
|  |           if (importData) { | ||
|  |             assignment.left = buildImportReference(importData, left.node); | ||
|  |             assignment.right = sequenceExpression([assignment.right, buildImportThrow(localName)]); | ||
|  |           } | ||
|  |           path.replaceWith(buildBindingExportAssignmentExpression(this.metadata, exportedNames, assignment, path.scope)); | ||
|  |           requeueInParent(path); | ||
|  |         } | ||
|  |       } else { | ||
|  |         const ids = left.getOuterBindingIdentifiers(); | ||
|  |         const programScopeIds = Object.keys(ids).filter(localName => scope.getBinding(localName) === path.scope.getBinding(localName)); | ||
|  |         const id = programScopeIds.find(localName => imported.has(localName)); | ||
|  |         if (id) { | ||
|  |           path.node.right = sequenceExpression([path.node.right, buildImportThrow(id)]); | ||
|  |         } | ||
|  | 
 | ||
|  |         const items = []; | ||
|  |         programScopeIds.forEach(localName => { | ||
|  |           const exportedNames = exported.get(localName) || []; | ||
|  |           if (exportedNames.length > 0) { | ||
|  |             items.push(buildBindingExportAssignmentExpression(this.metadata, exportedNames, identifier(localName), path.scope)); | ||
|  |           } | ||
|  |         }); | ||
|  |         if (items.length > 0) { | ||
|  |           let node = sequenceExpression(items); | ||
|  |           if (path.parentPath.isExpressionStatement()) { | ||
|  |             node = expressionStatement(node); | ||
|  |             node._blockHoist = path.parentPath.node._blockHoist; | ||
|  |           } | ||
|  |           const statement = path.insertAfter(node)[0]; | ||
|  |           requeueInParent(statement); | ||
|  |         } | ||
|  |       } | ||
|  |     } | ||
|  |   }, | ||
|  |   "ForOfStatement|ForInStatement"(path) { | ||
|  |     const { | ||
|  |       scope, | ||
|  |       node | ||
|  |     } = path; | ||
|  |     const { | ||
|  |       left | ||
|  |     } = node; | ||
|  |     const { | ||
|  |       exported, | ||
|  |       imported, | ||
|  |       scope: programScope | ||
|  |     } = this; | ||
|  |     if (!isVariableDeclaration(left)) { | ||
|  |       let didTransformExport = false, | ||
|  |         importConstViolationName; | ||
|  |       const loopBodyScope = path.get("body").scope; | ||
|  |       for (const name of Object.keys(getOuterBindingIdentifiers(left))) { | ||
|  |         if (programScope.getBinding(name) === scope.getBinding(name)) { | ||
|  |           if (exported.has(name)) { | ||
|  |             didTransformExport = true; | ||
|  |             if (loopBodyScope.hasOwnBinding(name)) { | ||
|  |               loopBodyScope.rename(name); | ||
|  |             } | ||
|  |           } | ||
|  |           if (imported.has(name) && !importConstViolationName) { | ||
|  |             importConstViolationName = name; | ||
|  |           } | ||
|  |         } | ||
|  |       } | ||
|  |       if (!didTransformExport && !importConstViolationName) { | ||
|  |         return; | ||
|  |       } | ||
|  |       path.ensureBlock(); | ||
|  |       const bodyPath = path.get("body"); | ||
|  |       const newLoopId = scope.generateUidIdentifierBasedOnNode(left); | ||
|  |       path.get("left").replaceWith(variableDeclaration("let", [variableDeclarator(cloneNode(newLoopId))])); | ||
|  |       scope.registerDeclaration(path.get("left")); | ||
|  |       if (didTransformExport) { | ||
|  |         bodyPath.unshiftContainer("body", expressionStatement(assignmentExpression("=", left, newLoopId))); | ||
|  |       } | ||
|  |       if (importConstViolationName) { | ||
|  |         bodyPath.unshiftContainer("body", expressionStatement(buildImportThrow(importConstViolationName))); | ||
|  |       } | ||
|  |     } | ||
|  |   } | ||
|  | }; | ||
|  | 
 | ||
|  | //# sourceMappingURL=rewrite-live-references.js.map
 |