rewrite-live-references.js 13 KB

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