rewrite-live-references.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = rewriteLiveReferences;
  6. var _assert = require("assert");
  7. var t = require("@babel/types");
  8. var _template = require("@babel/template");
  9. var _helperSimpleAccess = require("@babel/helper-simple-access");
  10. function rewriteLiveReferences(programPath, metadata) {
  11. const imported = new Map();
  12. const exported = new Map();
  13. const requeueInParent = path => {
  14. programPath.requeue(path);
  15. };
  16. for (const [source, data] of metadata.source) {
  17. for (const [localName, importName] of data.imports) {
  18. imported.set(localName, [source, importName, null]);
  19. }
  20. for (const localName of data.importsNamespace) {
  21. imported.set(localName, [source, null, localName]);
  22. }
  23. }
  24. for (const [local, data] of metadata.local) {
  25. let exportMeta = exported.get(local);
  26. if (!exportMeta) {
  27. exportMeta = [];
  28. exported.set(local, exportMeta);
  29. }
  30. exportMeta.push(...data.names);
  31. }
  32. const rewriteBindingInitVisitorState = {
  33. metadata,
  34. requeueInParent,
  35. scope: programPath.scope,
  36. exported
  37. };
  38. programPath.traverse(rewriteBindingInitVisitor, rewriteBindingInitVisitorState);
  39. (0, _helperSimpleAccess.default)(programPath, new Set([...Array.from(imported.keys()), ...Array.from(exported.keys())]));
  40. const rewriteReferencesVisitorState = {
  41. seen: new WeakSet(),
  42. metadata,
  43. requeueInParent,
  44. scope: programPath.scope,
  45. imported,
  46. exported,
  47. buildImportReference: ([source, importName, localName], identNode) => {
  48. const meta = metadata.source.get(source);
  49. if (localName) {
  50. if (meta.lazy) identNode = t.callExpression(identNode, []);
  51. return identNode;
  52. }
  53. let namespace = t.identifier(meta.name);
  54. if (meta.lazy) namespace = t.callExpression(namespace, []);
  55. if (importName === "default" && meta.interop === "node-default") {
  56. return namespace;
  57. }
  58. const computed = metadata.stringSpecifiers.has(importName);
  59. return t.memberExpression(namespace, computed ? t.stringLiteral(importName) : t.identifier(importName), computed);
  60. }
  61. };
  62. programPath.traverse(rewriteReferencesVisitor, rewriteReferencesVisitorState);
  63. }
  64. const rewriteBindingInitVisitor = {
  65. Scope(path) {
  66. path.skip();
  67. },
  68. ClassDeclaration(path) {
  69. const {
  70. requeueInParent,
  71. exported,
  72. metadata
  73. } = this;
  74. const {
  75. id
  76. } = path.node;
  77. if (!id) throw new Error("Expected class to have a name");
  78. const localName = id.name;
  79. const exportNames = exported.get(localName) || [];
  80. if (exportNames.length > 0) {
  81. const statement = t.expressionStatement(buildBindingExportAssignmentExpression(metadata, exportNames, t.identifier(localName)));
  82. statement._blockHoist = path.node._blockHoist;
  83. requeueInParent(path.insertAfter(statement)[0]);
  84. }
  85. },
  86. VariableDeclaration(path) {
  87. const {
  88. requeueInParent,
  89. exported,
  90. metadata
  91. } = this;
  92. Object.keys(path.getOuterBindingIdentifiers()).forEach(localName => {
  93. const exportNames = exported.get(localName) || [];
  94. if (exportNames.length > 0) {
  95. const statement = t.expressionStatement(buildBindingExportAssignmentExpression(metadata, exportNames, t.identifier(localName)));
  96. statement._blockHoist = path.node._blockHoist;
  97. requeueInParent(path.insertAfter(statement)[0]);
  98. }
  99. });
  100. }
  101. };
  102. const buildBindingExportAssignmentExpression = (metadata, exportNames, localExpr) => {
  103. return (exportNames || []).reduce((expr, exportName) => {
  104. const {
  105. stringSpecifiers
  106. } = metadata;
  107. const computed = stringSpecifiers.has(exportName);
  108. return t.assignmentExpression("=", t.memberExpression(t.identifier(metadata.exportName), computed ? t.stringLiteral(exportName) : t.identifier(exportName), computed), expr);
  109. }, localExpr);
  110. };
  111. const buildImportThrow = localName => {
  112. return _template.default.expression.ast`
  113. (function() {
  114. throw new Error('"' + '${localName}' + '" is read-only.');
  115. })()
  116. `;
  117. };
  118. const rewriteReferencesVisitor = {
  119. ReferencedIdentifier(path) {
  120. const {
  121. seen,
  122. buildImportReference,
  123. scope,
  124. imported,
  125. requeueInParent
  126. } = this;
  127. if (seen.has(path.node)) return;
  128. seen.add(path.node);
  129. const localName = path.node.name;
  130. const importData = imported.get(localName);
  131. if (importData) {
  132. const localBinding = path.scope.getBinding(localName);
  133. const rootBinding = scope.getBinding(localName);
  134. if (rootBinding !== localBinding) return;
  135. const ref = buildImportReference(importData, path.node);
  136. ref.loc = path.node.loc;
  137. if ((path.parentPath.isCallExpression({
  138. callee: path.node
  139. }) || path.parentPath.isOptionalCallExpression({
  140. callee: path.node
  141. }) || path.parentPath.isTaggedTemplateExpression({
  142. tag: path.node
  143. })) && t.isMemberExpression(ref)) {
  144. path.replaceWith(t.sequenceExpression([t.numericLiteral(0), ref]));
  145. } else if (path.isJSXIdentifier() && t.isMemberExpression(ref)) {
  146. const {
  147. object,
  148. property
  149. } = ref;
  150. path.replaceWith(t.jsxMemberExpression(t.jsxIdentifier(object.name), t.jsxIdentifier(property.name)));
  151. } else {
  152. path.replaceWith(ref);
  153. }
  154. requeueInParent(path);
  155. path.skip();
  156. }
  157. },
  158. AssignmentExpression: {
  159. exit(path) {
  160. const {
  161. scope,
  162. seen,
  163. imported,
  164. exported,
  165. requeueInParent,
  166. buildImportReference
  167. } = this;
  168. if (seen.has(path.node)) return;
  169. seen.add(path.node);
  170. const left = path.get("left");
  171. if (left.isMemberExpression()) return;
  172. if (left.isIdentifier()) {
  173. const localName = left.node.name;
  174. if (scope.getBinding(localName) !== path.scope.getBinding(localName)) {
  175. return;
  176. }
  177. const exportedNames = exported.get(localName);
  178. const importData = imported.get(localName);
  179. if ((exportedNames == null ? void 0 : exportedNames.length) > 0 || importData) {
  180. _assert(path.node.operator === "=", "Path was not simplified");
  181. const assignment = path.node;
  182. if (importData) {
  183. assignment.left = buildImportReference(importData, assignment.left);
  184. assignment.right = t.sequenceExpression([assignment.right, buildImportThrow(localName)]);
  185. }
  186. path.replaceWith(buildBindingExportAssignmentExpression(this.metadata, exportedNames, assignment));
  187. requeueInParent(path);
  188. }
  189. } else {
  190. const ids = left.getOuterBindingIdentifiers();
  191. const programScopeIds = Object.keys(ids).filter(localName => scope.getBinding(localName) === path.scope.getBinding(localName));
  192. const id = programScopeIds.find(localName => imported.has(localName));
  193. if (id) {
  194. path.node.right = t.sequenceExpression([path.node.right, buildImportThrow(id)]);
  195. }
  196. const items = [];
  197. programScopeIds.forEach(localName => {
  198. const exportedNames = exported.get(localName) || [];
  199. if (exportedNames.length > 0) {
  200. items.push(buildBindingExportAssignmentExpression(this.metadata, exportedNames, t.identifier(localName)));
  201. }
  202. });
  203. if (items.length > 0) {
  204. let node = t.sequenceExpression(items);
  205. if (path.parentPath.isExpressionStatement()) {
  206. node = t.expressionStatement(node);
  207. node._blockHoist = path.parentPath.node._blockHoist;
  208. }
  209. const statement = path.insertAfter(node)[0];
  210. requeueInParent(statement);
  211. }
  212. }
  213. }
  214. },
  215. "ForOfStatement|ForInStatement"(path) {
  216. const {
  217. scope,
  218. node
  219. } = path;
  220. const {
  221. left
  222. } = node;
  223. const {
  224. exported,
  225. scope: programScope
  226. } = this;
  227. if (!t.isVariableDeclaration(left)) {
  228. let didTransform = false;
  229. const bodyPath = path.get("body");
  230. const loopBodyScope = bodyPath.scope;
  231. for (const name of Object.keys(t.getOuterBindingIdentifiers(left))) {
  232. if (exported.get(name) && programScope.getBinding(name) === scope.getBinding(name)) {
  233. didTransform = true;
  234. if (loopBodyScope.hasOwnBinding(name)) {
  235. loopBodyScope.rename(name);
  236. }
  237. }
  238. }
  239. if (!didTransform) {
  240. return;
  241. }
  242. const newLoopId = scope.generateUidIdentifierBasedOnNode(left);
  243. bodyPath.unshiftContainer("body", t.expressionStatement(t.assignmentExpression("=", left, newLoopId)));
  244. path.get("left").replaceWith(t.variableDeclaration("let", [t.variableDeclarator(t.cloneNode(newLoopId))]));
  245. scope.registerDeclaration(path.get("left"));
  246. }
  247. }
  248. };