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.

251 lines
8.1 KiB

4 years ago
  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. return t.memberExpression(namespace, t.identifier(importName));
  58. }
  59. });
  60. }
  61. const rewriteBindingInitVisitor = {
  62. ClassProperty(path) {
  63. path.skip();
  64. },
  65. Function(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. return t.assignmentExpression("=", t.memberExpression(t.identifier(metadata.exportName), t.identifier(exportName)), expr);
  105. }, localExpr);
  106. };
  107. const buildImportThrow = localName => {
  108. return _template.default.expression.ast`
  109. (function() {
  110. throw new Error('"' + '${localName}' + '" is read-only.');
  111. })()
  112. `;
  113. };
  114. const rewriteReferencesVisitor = {
  115. ReferencedIdentifier(path) {
  116. const {
  117. seen,
  118. buildImportReference,
  119. scope,
  120. imported,
  121. requeueInParent
  122. } = this;
  123. if (seen.has(path.node)) return;
  124. seen.add(path.node);
  125. const localName = path.node.name;
  126. const localBinding = path.scope.getBinding(localName);
  127. const rootBinding = scope.getBinding(localName);
  128. if (rootBinding !== localBinding) return;
  129. const importData = imported.get(localName);
  130. if (importData) {
  131. const ref = buildImportReference(importData, path.node);
  132. ref.loc = path.node.loc;
  133. if (path.parentPath.isCallExpression({
  134. callee: path.node
  135. }) && t.isMemberExpression(ref)) {
  136. path.replaceWith(t.sequenceExpression([t.numericLiteral(0), ref]));
  137. } else if (path.isJSXIdentifier() && t.isMemberExpression(ref)) {
  138. const {
  139. object,
  140. property
  141. } = ref;
  142. path.replaceWith(t.JSXMemberExpression(t.JSXIdentifier(object.name), t.JSXIdentifier(property.name)));
  143. } else {
  144. path.replaceWith(ref);
  145. }
  146. requeueInParent(path);
  147. path.skip();
  148. }
  149. },
  150. AssignmentExpression: {
  151. exit(path) {
  152. const {
  153. scope,
  154. seen,
  155. imported,
  156. exported,
  157. requeueInParent,
  158. buildImportReference
  159. } = this;
  160. if (seen.has(path.node)) return;
  161. seen.add(path.node);
  162. const left = path.get("left");
  163. if (left.isMemberExpression()) return;
  164. if (left.isIdentifier()) {
  165. const localName = left.node.name;
  166. if (scope.getBinding(localName) !== path.scope.getBinding(localName)) {
  167. return;
  168. }
  169. const exportedNames = exported.get(localName);
  170. const importData = imported.get(localName);
  171. if ((exportedNames == null ? void 0 : exportedNames.length) > 0 || importData) {
  172. (0, _assert.default)(path.node.operator === "=", "Path was not simplified");
  173. const assignment = path.node;
  174. if (importData) {
  175. assignment.left = buildImportReference(importData, assignment.left);
  176. assignment.right = t.sequenceExpression([assignment.right, buildImportThrow(localName)]);
  177. }
  178. path.replaceWith(buildBindingExportAssignmentExpression(this.metadata, exportedNames, assignment));
  179. requeueInParent(path);
  180. }
  181. } else {
  182. const ids = left.getOuterBindingIdentifiers();
  183. const programScopeIds = Object.keys(ids).filter(localName => scope.getBinding(localName) === path.scope.getBinding(localName));
  184. const id = programScopeIds.find(localName => imported.has(localName));
  185. if (id) {
  186. path.node.right = t.sequenceExpression([path.node.right, buildImportThrow(id)]);
  187. }
  188. const items = [];
  189. programScopeIds.forEach(localName => {
  190. const exportedNames = exported.get(localName) || [];
  191. if (exportedNames.length > 0) {
  192. items.push(buildBindingExportAssignmentExpression(this.metadata, exportedNames, t.identifier(localName)));
  193. }
  194. });
  195. if (items.length > 0) {
  196. let node = t.sequenceExpression(items);
  197. if (path.parentPath.isExpressionStatement()) {
  198. node = t.expressionStatement(node);
  199. node._blockHoist = path.parentPath.node._blockHoist;
  200. }
  201. const statement = path.insertAfter(node)[0];
  202. requeueInParent(statement);
  203. }
  204. }
  205. }
  206. }
  207. };