rewrite-live-references.js 9.5 KB

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