rewrite-live-references.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  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. const {
  11. assignmentExpression,
  12. callExpression,
  13. cloneNode,
  14. expressionStatement,
  15. getOuterBindingIdentifiers,
  16. identifier,
  17. isMemberExpression,
  18. isVariableDeclaration,
  19. jsxIdentifier,
  20. jsxMemberExpression,
  21. memberExpression,
  22. numericLiteral,
  23. sequenceExpression,
  24. stringLiteral,
  25. variableDeclaration,
  26. variableDeclarator
  27. } = _t;
  28. function isInType(path) {
  29. do {
  30. switch (path.parent.type) {
  31. case "TSTypeAnnotation":
  32. case "TSTypeAliasDeclaration":
  33. case "TSTypeReference":
  34. case "TypeAnnotation":
  35. case "TypeAlias":
  36. return true;
  37. case "ExportSpecifier":
  38. return path.parentPath.parent.exportKind === "type";
  39. default:
  40. if (path.parentPath.isStatement() || path.parentPath.isExpression()) {
  41. return false;
  42. }
  43. }
  44. } while (path = path.parentPath);
  45. }
  46. function rewriteLiveReferences(programPath, metadata) {
  47. const imported = new Map();
  48. const exported = new Map();
  49. const requeueInParent = path => {
  50. programPath.requeue(path);
  51. };
  52. for (const [source, data] of metadata.source) {
  53. for (const [localName, importName] of data.imports) {
  54. imported.set(localName, [source, importName, null]);
  55. }
  56. for (const localName of data.importsNamespace) {
  57. imported.set(localName, [source, null, localName]);
  58. }
  59. }
  60. for (const [local, data] of metadata.local) {
  61. let exportMeta = exported.get(local);
  62. if (!exportMeta) {
  63. exportMeta = [];
  64. exported.set(local, exportMeta);
  65. }
  66. exportMeta.push(...data.names);
  67. }
  68. const rewriteBindingInitVisitorState = {
  69. metadata,
  70. requeueInParent,
  71. scope: programPath.scope,
  72. exported
  73. };
  74. programPath.traverse(rewriteBindingInitVisitor, rewriteBindingInitVisitorState);
  75. (0, _helperSimpleAccess.default)(programPath, new Set([...Array.from(imported.keys()), ...Array.from(exported.keys())]), false);
  76. const rewriteReferencesVisitorState = {
  77. seen: new WeakSet(),
  78. metadata,
  79. requeueInParent,
  80. scope: programPath.scope,
  81. imported,
  82. exported,
  83. buildImportReference: ([source, importName, localName], identNode) => {
  84. const meta = metadata.source.get(source);
  85. if (localName) {
  86. if (meta.lazy) {
  87. identNode = callExpression(identNode, []);
  88. }
  89. return identNode;
  90. }
  91. let namespace = identifier(meta.name);
  92. if (meta.lazy) namespace = callExpression(namespace, []);
  93. if (importName === "default" && meta.interop === "node-default") {
  94. return namespace;
  95. }
  96. const computed = metadata.stringSpecifiers.has(importName);
  97. return memberExpression(namespace, computed ? stringLiteral(importName) : identifier(importName), computed);
  98. }
  99. };
  100. programPath.traverse(rewriteReferencesVisitor, rewriteReferencesVisitorState);
  101. }
  102. const rewriteBindingInitVisitor = {
  103. Scope(path) {
  104. path.skip();
  105. },
  106. ClassDeclaration(path) {
  107. const {
  108. requeueInParent,
  109. exported,
  110. metadata
  111. } = this;
  112. const {
  113. id
  114. } = path.node;
  115. if (!id) throw new Error("Expected class to have a name");
  116. const localName = id.name;
  117. const exportNames = exported.get(localName) || [];
  118. if (exportNames.length > 0) {
  119. const statement = expressionStatement(buildBindingExportAssignmentExpression(metadata, exportNames, identifier(localName), path.scope));
  120. statement._blockHoist = path.node._blockHoist;
  121. requeueInParent(path.insertAfter(statement)[0]);
  122. }
  123. },
  124. VariableDeclaration(path) {
  125. const {
  126. requeueInParent,
  127. exported,
  128. metadata
  129. } = this;
  130. Object.keys(path.getOuterBindingIdentifiers()).forEach(localName => {
  131. const exportNames = exported.get(localName) || [];
  132. if (exportNames.length > 0) {
  133. const statement = expressionStatement(buildBindingExportAssignmentExpression(metadata, exportNames, identifier(localName), path.scope));
  134. statement._blockHoist = path.node._blockHoist;
  135. requeueInParent(path.insertAfter(statement)[0]);
  136. }
  137. });
  138. }
  139. };
  140. const buildBindingExportAssignmentExpression = (metadata, exportNames, localExpr, scope) => {
  141. const exportsObjectName = metadata.exportName;
  142. for (let currentScope = scope; currentScope != null; currentScope = currentScope.parent) {
  143. if (currentScope.hasOwnBinding(exportsObjectName)) {
  144. currentScope.rename(exportsObjectName);
  145. }
  146. }
  147. return (exportNames || []).reduce((expr, exportName) => {
  148. const {
  149. stringSpecifiers
  150. } = metadata;
  151. const computed = stringSpecifiers.has(exportName);
  152. return assignmentExpression("=", memberExpression(identifier(exportsObjectName), computed ? stringLiteral(exportName) : identifier(exportName), computed), expr);
  153. }, localExpr);
  154. };
  155. const buildImportThrow = localName => {
  156. return _template.default.expression.ast`
  157. (function() {
  158. throw new Error('"' + '${localName}' + '" is read-only.');
  159. })()
  160. `;
  161. };
  162. const rewriteReferencesVisitor = {
  163. ReferencedIdentifier(path) {
  164. const {
  165. seen,
  166. buildImportReference,
  167. scope,
  168. imported,
  169. requeueInParent
  170. } = this;
  171. if (seen.has(path.node)) return;
  172. seen.add(path.node);
  173. const localName = path.node.name;
  174. const importData = imported.get(localName);
  175. if (importData) {
  176. if (isInType(path)) {
  177. 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.`);
  178. }
  179. const localBinding = path.scope.getBinding(localName);
  180. const rootBinding = scope.getBinding(localName);
  181. if (rootBinding !== localBinding) return;
  182. const ref = buildImportReference(importData, path.node);
  183. ref.loc = path.node.loc;
  184. if ((path.parentPath.isCallExpression({
  185. callee: path.node
  186. }) || path.parentPath.isOptionalCallExpression({
  187. callee: path.node
  188. }) || path.parentPath.isTaggedTemplateExpression({
  189. tag: path.node
  190. })) && isMemberExpression(ref)) {
  191. path.replaceWith(sequenceExpression([numericLiteral(0), ref]));
  192. } else if (path.isJSXIdentifier() && isMemberExpression(ref)) {
  193. const {
  194. object,
  195. property
  196. } = ref;
  197. path.replaceWith(jsxMemberExpression(jsxIdentifier(object.name), jsxIdentifier(property.name)));
  198. } else {
  199. path.replaceWith(ref);
  200. }
  201. requeueInParent(path);
  202. path.skip();
  203. }
  204. },
  205. UpdateExpression(path) {
  206. const {
  207. scope,
  208. seen,
  209. imported,
  210. exported,
  211. requeueInParent,
  212. buildImportReference
  213. } = this;
  214. if (seen.has(path.node)) return;
  215. seen.add(path.node);
  216. const arg = path.get("argument");
  217. if (arg.isMemberExpression()) return;
  218. const update = path.node;
  219. if (arg.isIdentifier()) {
  220. const localName = arg.node.name;
  221. if (scope.getBinding(localName) !== path.scope.getBinding(localName)) {
  222. return;
  223. }
  224. const exportedNames = exported.get(localName);
  225. const importData = imported.get(localName);
  226. if ((exportedNames == null ? void 0 : exportedNames.length) > 0 || importData) {
  227. if (importData) {
  228. path.replaceWith(assignmentExpression(update.operator[0] + "=", buildImportReference(importData, arg.node), buildImportThrow(localName)));
  229. } else if (update.prefix) {
  230. path.replaceWith(buildBindingExportAssignmentExpression(this.metadata, exportedNames, cloneNode(update), path.scope));
  231. } else {
  232. const ref = scope.generateDeclaredUidIdentifier(localName);
  233. path.replaceWith(sequenceExpression([assignmentExpression("=", cloneNode(ref), cloneNode(update)), buildBindingExportAssignmentExpression(this.metadata, exportedNames, identifier(localName), path.scope), cloneNode(ref)]));
  234. }
  235. }
  236. }
  237. requeueInParent(path);
  238. path.skip();
  239. },
  240. AssignmentExpression: {
  241. exit(path) {
  242. const {
  243. scope,
  244. seen,
  245. imported,
  246. exported,
  247. requeueInParent,
  248. buildImportReference
  249. } = this;
  250. if (seen.has(path.node)) return;
  251. seen.add(path.node);
  252. const left = path.get("left");
  253. if (left.isMemberExpression()) return;
  254. if (left.isIdentifier()) {
  255. const localName = left.node.name;
  256. if (scope.getBinding(localName) !== path.scope.getBinding(localName)) {
  257. return;
  258. }
  259. const exportedNames = exported.get(localName);
  260. const importData = imported.get(localName);
  261. if ((exportedNames == null ? void 0 : exportedNames.length) > 0 || importData) {
  262. _assert(path.node.operator === "=", "Path was not simplified");
  263. const assignment = path.node;
  264. if (importData) {
  265. assignment.left = buildImportReference(importData, left.node);
  266. assignment.right = sequenceExpression([assignment.right, buildImportThrow(localName)]);
  267. }
  268. path.replaceWith(buildBindingExportAssignmentExpression(this.metadata, exportedNames, assignment, path.scope));
  269. requeueInParent(path);
  270. }
  271. } else {
  272. const ids = left.getOuterBindingIdentifiers();
  273. const programScopeIds = Object.keys(ids).filter(localName => scope.getBinding(localName) === path.scope.getBinding(localName));
  274. const id = programScopeIds.find(localName => imported.has(localName));
  275. if (id) {
  276. path.node.right = sequenceExpression([path.node.right, buildImportThrow(id)]);
  277. }
  278. const items = [];
  279. programScopeIds.forEach(localName => {
  280. const exportedNames = exported.get(localName) || [];
  281. if (exportedNames.length > 0) {
  282. items.push(buildBindingExportAssignmentExpression(this.metadata, exportedNames, identifier(localName), path.scope));
  283. }
  284. });
  285. if (items.length > 0) {
  286. let node = sequenceExpression(items);
  287. if (path.parentPath.isExpressionStatement()) {
  288. node = expressionStatement(node);
  289. node._blockHoist = path.parentPath.node._blockHoist;
  290. }
  291. const statement = path.insertAfter(node)[0];
  292. requeueInParent(statement);
  293. }
  294. }
  295. }
  296. },
  297. "ForOfStatement|ForInStatement"(path) {
  298. const {
  299. scope,
  300. node
  301. } = path;
  302. const {
  303. left
  304. } = node;
  305. const {
  306. exported,
  307. imported,
  308. scope: programScope
  309. } = this;
  310. if (!isVariableDeclaration(left)) {
  311. let didTransformExport = false,
  312. importConstViolationName;
  313. const loopBodyScope = path.get("body").scope;
  314. for (const name of Object.keys(getOuterBindingIdentifiers(left))) {
  315. if (programScope.getBinding(name) === scope.getBinding(name)) {
  316. if (exported.has(name)) {
  317. didTransformExport = true;
  318. if (loopBodyScope.hasOwnBinding(name)) {
  319. loopBodyScope.rename(name);
  320. }
  321. }
  322. if (imported.has(name) && !importConstViolationName) {
  323. importConstViolationName = name;
  324. }
  325. }
  326. }
  327. if (!didTransformExport && !importConstViolationName) {
  328. return;
  329. }
  330. path.ensureBlock();
  331. const bodyPath = path.get("body");
  332. const newLoopId = scope.generateUidIdentifierBasedOnNode(left);
  333. path.get("left").replaceWith(variableDeclaration("let", [variableDeclarator(cloneNode(newLoopId))]));
  334. scope.registerDeclaration(path.get("left"));
  335. if (didTransformExport) {
  336. bodyPath.unshiftContainer("body", expressionStatement(assignmentExpression("=", left, newLoopId)));
  337. }
  338. if (importConstViolationName) {
  339. bodyPath.unshiftContainer("body", expressionStatement(buildImportThrow(importConstViolationName)));
  340. }
  341. }
  342. }
  343. };