UNPKG

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