source: frontend/node_modules/sucrase/dist/transformers/CJSImportTransformer.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 33.9 KB
Line 
1"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
2
3
4var _tokenizer = require('../parser/tokenizer');
5var _keywords = require('../parser/tokenizer/keywords');
6var _types = require('../parser/tokenizer/types');
7
8var _elideImportEquals = require('../util/elideImportEquals'); var _elideImportEquals2 = _interopRequireDefault(_elideImportEquals);
9
10
11
12var _getDeclarationInfo = require('../util/getDeclarationInfo'); var _getDeclarationInfo2 = _interopRequireDefault(_getDeclarationInfo);
13var _getImportExportSpecifierInfo = require('../util/getImportExportSpecifierInfo'); var _getImportExportSpecifierInfo2 = _interopRequireDefault(_getImportExportSpecifierInfo);
14var _isExportFrom = require('../util/isExportFrom'); var _isExportFrom2 = _interopRequireDefault(_isExportFrom);
15var _removeMaybeImportAttributes = require('../util/removeMaybeImportAttributes');
16var _shouldElideDefaultExport = require('../util/shouldElideDefaultExport'); var _shouldElideDefaultExport2 = _interopRequireDefault(_shouldElideDefaultExport);
17
18
19var _Transformer = require('./Transformer'); var _Transformer2 = _interopRequireDefault(_Transformer);
20
21/**
22 * Class for editing import statements when we are transforming to commonjs.
23 */
24 class CJSImportTransformer extends _Transformer2.default {
25 __init() {this.hadExport = false}
26 __init2() {this.hadNamedExport = false}
27 __init3() {this.hadDefaultExport = false}
28
29
30 constructor(
31 rootTransformer,
32 tokens,
33 importProcessor,
34 nameManager,
35 helperManager,
36 reactHotLoaderTransformer,
37 enableLegacyBabel5ModuleInterop,
38 enableLegacyTypeScriptModuleInterop,
39 isTypeScriptTransformEnabled,
40 isFlowTransformEnabled,
41 preserveDynamicImport,
42 keepUnusedImports,
43 ) {
44 super();this.rootTransformer = rootTransformer;this.tokens = tokens;this.importProcessor = importProcessor;this.nameManager = nameManager;this.helperManager = helperManager;this.reactHotLoaderTransformer = reactHotLoaderTransformer;this.enableLegacyBabel5ModuleInterop = enableLegacyBabel5ModuleInterop;this.enableLegacyTypeScriptModuleInterop = enableLegacyTypeScriptModuleInterop;this.isTypeScriptTransformEnabled = isTypeScriptTransformEnabled;this.isFlowTransformEnabled = isFlowTransformEnabled;this.preserveDynamicImport = preserveDynamicImport;this.keepUnusedImports = keepUnusedImports;CJSImportTransformer.prototype.__init.call(this);CJSImportTransformer.prototype.__init2.call(this);CJSImportTransformer.prototype.__init3.call(this);;
45 this.declarationInfo = isTypeScriptTransformEnabled
46 ? _getDeclarationInfo2.default.call(void 0, tokens)
47 : _getDeclarationInfo.EMPTY_DECLARATION_INFO;
48 }
49
50 getPrefixCode() {
51 let prefix = "";
52 if (this.hadExport) {
53 prefix += 'Object.defineProperty(exports, "__esModule", {value: true});';
54 }
55 return prefix;
56 }
57
58 getSuffixCode() {
59 if (this.enableLegacyBabel5ModuleInterop && this.hadDefaultExport && !this.hadNamedExport) {
60 return "\nmodule.exports = exports.default;\n";
61 }
62 return "";
63 }
64
65 process() {
66 // TypeScript `import foo = require('foo');` should always just be translated to plain require.
67 if (this.tokens.matches3(_types.TokenType._import, _types.TokenType.name, _types.TokenType.eq)) {
68 return this.processImportEquals();
69 }
70 if (this.tokens.matches1(_types.TokenType._import)) {
71 this.processImport();
72 return true;
73 }
74 if (this.tokens.matches2(_types.TokenType._export, _types.TokenType.eq)) {
75 this.tokens.replaceToken("module.exports");
76 return true;
77 }
78 if (this.tokens.matches1(_types.TokenType._export) && !this.tokens.currentToken().isType) {
79 this.hadExport = true;
80 return this.processExport();
81 }
82 if (this.tokens.matches2(_types.TokenType.name, _types.TokenType.postIncDec)) {
83 // Fall through to normal identifier matching if this doesn't apply.
84 if (this.processPostIncDec()) {
85 return true;
86 }
87 }
88 if (this.tokens.matches1(_types.TokenType.name) || this.tokens.matches1(_types.TokenType.jsxName)) {
89 return this.processIdentifier();
90 }
91 if (this.tokens.matches1(_types.TokenType.eq)) {
92 return this.processAssignment();
93 }
94 if (this.tokens.matches1(_types.TokenType.assign)) {
95 return this.processComplexAssignment();
96 }
97 if (this.tokens.matches1(_types.TokenType.preIncDec)) {
98 return this.processPreIncDec();
99 }
100 return false;
101 }
102
103 processImportEquals() {
104 const importName = this.tokens.identifierNameAtIndex(this.tokens.currentIndex() + 1);
105 if (this.importProcessor.shouldAutomaticallyElideImportedName(importName)) {
106 // If this name is only used as a type, elide the whole import.
107 _elideImportEquals2.default.call(void 0, this.tokens);
108 } else {
109 // Otherwise, switch `import` to `const`.
110 this.tokens.replaceToken("const");
111 }
112 return true;
113 }
114
115 /**
116 * Transform this:
117 * import foo, {bar} from 'baz';
118 * into
119 * var _baz = require('baz'); var _baz2 = _interopRequireDefault(_baz);
120 *
121 * The import code was already generated in the import preprocessing step, so
122 * we just need to look it up.
123 */
124 processImport() {
125 if (this.tokens.matches2(_types.TokenType._import, _types.TokenType.parenL)) {
126 if (this.preserveDynamicImport) {
127 // Bail out, only making progress for this one token.
128 this.tokens.copyToken();
129 return;
130 }
131 const requireWrapper = this.enableLegacyTypeScriptModuleInterop
132 ? ""
133 : `${this.helperManager.getHelperName("interopRequireWildcard")}(`;
134 this.tokens.replaceToken(`Promise.resolve().then(() => ${requireWrapper}require`);
135 const contextId = this.tokens.currentToken().contextId;
136 if (contextId == null) {
137 throw new Error("Expected context ID on dynamic import invocation.");
138 }
139 this.tokens.copyToken();
140 while (!this.tokens.matchesContextIdAndLabel(_types.TokenType.parenR, contextId)) {
141 this.rootTransformer.processToken();
142 }
143 this.tokens.replaceToken(requireWrapper ? ")))" : "))");
144 return;
145 }
146
147 const shouldElideImport = this.removeImportAndDetectIfShouldElide();
148 if (shouldElideImport) {
149 this.tokens.removeToken();
150 } else {
151 const path = this.tokens.stringValue();
152 this.tokens.replaceTokenTrimmingLeftWhitespace(this.importProcessor.claimImportCode(path));
153 this.tokens.appendCode(this.importProcessor.claimImportCode(path));
154 }
155 _removeMaybeImportAttributes.removeMaybeImportAttributes.call(void 0, this.tokens);
156 if (this.tokens.matches1(_types.TokenType.semi)) {
157 this.tokens.removeToken();
158 }
159 }
160
161 /**
162 * Erase this import (since any CJS output would be completely different), and
163 * return true if this import is should be elided due to being a type-only
164 * import. Such imports will not be emitted at all to avoid side effects.
165 *
166 * Import elision only happens with the TypeScript or Flow transforms enabled.
167 *
168 * TODO: This function has some awkward overlap with
169 * CJSImportProcessor.pruneTypeOnlyImports , and the two should be unified.
170 * That function handles TypeScript implicit import name elision, and removes
171 * an import if all typical imported names (without `type`) are removed due
172 * to being type-only imports. This function handles Flow import removal and
173 * properly distinguishes `import 'foo'` from `import {} from 'foo'` for TS
174 * purposes.
175 *
176 * The position should end at the import string.
177 */
178 removeImportAndDetectIfShouldElide() {
179 this.tokens.removeInitialToken();
180 if (
181 this.tokens.matchesContextual(_keywords.ContextualKeyword._type) &&
182 !this.tokens.matches1AtIndex(this.tokens.currentIndex() + 1, _types.TokenType.comma) &&
183 !this.tokens.matchesContextualAtIndex(this.tokens.currentIndex() + 1, _keywords.ContextualKeyword._from)
184 ) {
185 // This is an "import type" statement, so exit early.
186 this.removeRemainingImport();
187 return true;
188 }
189
190 if (this.tokens.matches1(_types.TokenType.name) || this.tokens.matches1(_types.TokenType.star)) {
191 // We have a default import or namespace import, so there must be some
192 // non-type import.
193 this.removeRemainingImport();
194 return false;
195 }
196
197 if (this.tokens.matches1(_types.TokenType.string)) {
198 // This is a bare import, so we should proceed with the import.
199 return false;
200 }
201
202 let foundNonTypeImport = false;
203 let foundAnyNamedImport = false;
204 while (!this.tokens.matches1(_types.TokenType.string)) {
205 // Check if any named imports are of the form "foo" or "foo as bar", with
206 // no leading "type".
207 if (
208 (!foundNonTypeImport && this.tokens.matches1(_types.TokenType.braceL)) ||
209 this.tokens.matches1(_types.TokenType.comma)
210 ) {
211 this.tokens.removeToken();
212 if (!this.tokens.matches1(_types.TokenType.braceR)) {
213 foundAnyNamedImport = true;
214 }
215 if (
216 this.tokens.matches2(_types.TokenType.name, _types.TokenType.comma) ||
217 this.tokens.matches2(_types.TokenType.name, _types.TokenType.braceR) ||
218 this.tokens.matches4(_types.TokenType.name, _types.TokenType.name, _types.TokenType.name, _types.TokenType.comma) ||
219 this.tokens.matches4(_types.TokenType.name, _types.TokenType.name, _types.TokenType.name, _types.TokenType.braceR)
220 ) {
221 foundNonTypeImport = true;
222 }
223 }
224 this.tokens.removeToken();
225 }
226 if (this.keepUnusedImports) {
227 return false;
228 }
229 if (this.isTypeScriptTransformEnabled) {
230 return !foundNonTypeImport;
231 } else if (this.isFlowTransformEnabled) {
232 // In Flow, unlike TS, `import {} from 'foo';` preserves the import.
233 return foundAnyNamedImport && !foundNonTypeImport;
234 } else {
235 return false;
236 }
237 }
238
239 removeRemainingImport() {
240 while (!this.tokens.matches1(_types.TokenType.string)) {
241 this.tokens.removeToken();
242 }
243 }
244
245 processIdentifier() {
246 const token = this.tokens.currentToken();
247 if (token.shadowsGlobal) {
248 return false;
249 }
250
251 if (token.identifierRole === _tokenizer.IdentifierRole.ObjectShorthand) {
252 return this.processObjectShorthand();
253 }
254
255 if (token.identifierRole !== _tokenizer.IdentifierRole.Access) {
256 return false;
257 }
258 const replacement = this.importProcessor.getIdentifierReplacement(
259 this.tokens.identifierNameForToken(token),
260 );
261 if (!replacement) {
262 return false;
263 }
264 // Tolerate any number of closing parens while looking for an opening paren
265 // that indicates a function call.
266 let possibleOpenParenIndex = this.tokens.currentIndex() + 1;
267 while (
268 possibleOpenParenIndex < this.tokens.tokens.length &&
269 this.tokens.tokens[possibleOpenParenIndex].type === _types.TokenType.parenR
270 ) {
271 possibleOpenParenIndex++;
272 }
273 // Avoid treating imported functions as methods of their `exports` object
274 // by using `(0, f)` when the identifier is in a paren expression. Else
275 // use `Function.prototype.call` when the identifier is a guaranteed
276 // function call. When using `call`, pass undefined as the context.
277 if (this.tokens.tokens[possibleOpenParenIndex].type === _types.TokenType.parenL) {
278 if (
279 this.tokens.tokenAtRelativeIndex(1).type === _types.TokenType.parenL &&
280 this.tokens.tokenAtRelativeIndex(-1).type !== _types.TokenType._new
281 ) {
282 this.tokens.replaceToken(`${replacement}.call(void 0, `);
283 // Remove the old paren.
284 this.tokens.removeToken();
285 // Balance out the new paren.
286 this.rootTransformer.processBalancedCode();
287 this.tokens.copyExpectedToken(_types.TokenType.parenR);
288 } else {
289 // See here: http://2ality.com/2015/12/references.html
290 this.tokens.replaceToken(`(0, ${replacement})`);
291 }
292 } else {
293 this.tokens.replaceToken(replacement);
294 }
295 return true;
296 }
297
298 processObjectShorthand() {
299 const identifier = this.tokens.identifierName();
300 const replacement = this.importProcessor.getIdentifierReplacement(identifier);
301 if (!replacement) {
302 return false;
303 }
304 this.tokens.replaceToken(`${identifier}: ${replacement}`);
305 return true;
306 }
307
308 processExport() {
309 if (
310 this.tokens.matches2(_types.TokenType._export, _types.TokenType._enum) ||
311 this.tokens.matches3(_types.TokenType._export, _types.TokenType._const, _types.TokenType._enum)
312 ) {
313 this.hadNamedExport = true;
314 // Let the TypeScript transform handle it.
315 return false;
316 }
317 if (this.tokens.matches2(_types.TokenType._export, _types.TokenType._default)) {
318 if (this.tokens.matches3(_types.TokenType._export, _types.TokenType._default, _types.TokenType._enum)) {
319 this.hadDefaultExport = true;
320 // Flow export default enums need some special handling, so handle them
321 // in that tranform rather than this one.
322 return false;
323 }
324 this.processExportDefault();
325 return true;
326 } else if (this.tokens.matches2(_types.TokenType._export, _types.TokenType.braceL)) {
327 this.processExportBindings();
328 return true;
329 } else if (
330 this.tokens.matches2(_types.TokenType._export, _types.TokenType.name) &&
331 this.tokens.matchesContextualAtIndex(this.tokens.currentIndex() + 1, _keywords.ContextualKeyword._type)
332 ) {
333 // export type {a};
334 // export type {a as b};
335 // export type {a} from './b';
336 // export type * from './b';
337 // export type * as ns from './b';
338 this.tokens.removeInitialToken();
339 this.tokens.removeToken();
340 if (this.tokens.matches1(_types.TokenType.braceL)) {
341 while (!this.tokens.matches1(_types.TokenType.braceR)) {
342 this.tokens.removeToken();
343 }
344 this.tokens.removeToken();
345 } else {
346 // *
347 this.tokens.removeToken();
348 if (this.tokens.matches1(_types.TokenType._as)) {
349 // as
350 this.tokens.removeToken();
351 // ns
352 this.tokens.removeToken();
353 }
354 }
355 // Remove type re-export `... } from './T'`
356 if (
357 this.tokens.matchesContextual(_keywords.ContextualKeyword._from) &&
358 this.tokens.matches1AtIndex(this.tokens.currentIndex() + 1, _types.TokenType.string)
359 ) {
360 this.tokens.removeToken();
361 this.tokens.removeToken();
362 _removeMaybeImportAttributes.removeMaybeImportAttributes.call(void 0, this.tokens);
363 }
364 return true;
365 }
366 this.hadNamedExport = true;
367 if (
368 this.tokens.matches2(_types.TokenType._export, _types.TokenType._var) ||
369 this.tokens.matches2(_types.TokenType._export, _types.TokenType._let) ||
370 this.tokens.matches2(_types.TokenType._export, _types.TokenType._const)
371 ) {
372 this.processExportVar();
373 return true;
374 } else if (
375 this.tokens.matches2(_types.TokenType._export, _types.TokenType._function) ||
376 // export async function
377 this.tokens.matches3(_types.TokenType._export, _types.TokenType.name, _types.TokenType._function)
378 ) {
379 this.processExportFunction();
380 return true;
381 } else if (
382 this.tokens.matches2(_types.TokenType._export, _types.TokenType._class) ||
383 this.tokens.matches3(_types.TokenType._export, _types.TokenType._abstract, _types.TokenType._class) ||
384 this.tokens.matches2(_types.TokenType._export, _types.TokenType.at)
385 ) {
386 this.processExportClass();
387 return true;
388 } else if (this.tokens.matches2(_types.TokenType._export, _types.TokenType.star)) {
389 this.processExportStar();
390 return true;
391 } else {
392 throw new Error("Unrecognized export syntax.");
393 }
394 }
395
396 processAssignment() {
397 const index = this.tokens.currentIndex();
398 const identifierToken = this.tokens.tokens[index - 1];
399 // If the LHS is a type identifier, this must be a declaration like `let a: b = c;`,
400 // with `b` as the identifier, so nothing needs to be done in that case.
401 if (identifierToken.isType || identifierToken.type !== _types.TokenType.name) {
402 return false;
403 }
404 if (identifierToken.shadowsGlobal) {
405 return false;
406 }
407 if (index >= 2 && this.tokens.matches1AtIndex(index - 2, _types.TokenType.dot)) {
408 return false;
409 }
410 if (index >= 2 && [_types.TokenType._var, _types.TokenType._let, _types.TokenType._const].includes(this.tokens.tokens[index - 2].type)) {
411 // Declarations don't need an extra assignment. This doesn't avoid the
412 // assignment for comma-separated declarations, but it's still correct
413 // since the assignment is just redundant.
414 return false;
415 }
416 const assignmentSnippet = this.importProcessor.resolveExportBinding(
417 this.tokens.identifierNameForToken(identifierToken),
418 );
419 if (!assignmentSnippet) {
420 return false;
421 }
422 this.tokens.copyToken();
423 this.tokens.appendCode(` ${assignmentSnippet} =`);
424 return true;
425 }
426
427 /**
428 * Process something like `a += 3`, where `a` might be an exported value.
429 */
430 processComplexAssignment() {
431 const index = this.tokens.currentIndex();
432 const identifierToken = this.tokens.tokens[index - 1];
433 if (identifierToken.type !== _types.TokenType.name) {
434 return false;
435 }
436 if (identifierToken.shadowsGlobal) {
437 return false;
438 }
439 if (index >= 2 && this.tokens.matches1AtIndex(index - 2, _types.TokenType.dot)) {
440 return false;
441 }
442 const assignmentSnippet = this.importProcessor.resolveExportBinding(
443 this.tokens.identifierNameForToken(identifierToken),
444 );
445 if (!assignmentSnippet) {
446 return false;
447 }
448 this.tokens.appendCode(` = ${assignmentSnippet}`);
449 this.tokens.copyToken();
450 return true;
451 }
452
453 /**
454 * Process something like `++a`, where `a` might be an exported value.
455 */
456 processPreIncDec() {
457 const index = this.tokens.currentIndex();
458 const identifierToken = this.tokens.tokens[index + 1];
459 if (identifierToken.type !== _types.TokenType.name) {
460 return false;
461 }
462 if (identifierToken.shadowsGlobal) {
463 return false;
464 }
465 // Ignore things like ++a.b and ++a[b] and ++a().b.
466 if (
467 index + 2 < this.tokens.tokens.length &&
468 (this.tokens.matches1AtIndex(index + 2, _types.TokenType.dot) ||
469 this.tokens.matches1AtIndex(index + 2, _types.TokenType.bracketL) ||
470 this.tokens.matches1AtIndex(index + 2, _types.TokenType.parenL))
471 ) {
472 return false;
473 }
474 const identifierName = this.tokens.identifierNameForToken(identifierToken);
475 const assignmentSnippet = this.importProcessor.resolveExportBinding(identifierName);
476 if (!assignmentSnippet) {
477 return false;
478 }
479 this.tokens.appendCode(`${assignmentSnippet} = `);
480 this.tokens.copyToken();
481 return true;
482 }
483
484 /**
485 * Process something like `a++`, where `a` might be an exported value.
486 * This starts at the `a`, not at the `++`.
487 */
488 processPostIncDec() {
489 const index = this.tokens.currentIndex();
490 const identifierToken = this.tokens.tokens[index];
491 const operatorToken = this.tokens.tokens[index + 1];
492 if (identifierToken.type !== _types.TokenType.name) {
493 return false;
494 }
495 if (identifierToken.shadowsGlobal) {
496 return false;
497 }
498 if (index >= 1 && this.tokens.matches1AtIndex(index - 1, _types.TokenType.dot)) {
499 return false;
500 }
501 const identifierName = this.tokens.identifierNameForToken(identifierToken);
502 const assignmentSnippet = this.importProcessor.resolveExportBinding(identifierName);
503 if (!assignmentSnippet) {
504 return false;
505 }
506 const operatorCode = this.tokens.rawCodeForToken(operatorToken);
507 // We might also replace the identifier with something like exports.x, so
508 // do that replacement here as well.
509 const base = this.importProcessor.getIdentifierReplacement(identifierName) || identifierName;
510 if (operatorCode === "++") {
511 this.tokens.replaceToken(`(${base} = ${assignmentSnippet} = ${base} + 1, ${base} - 1)`);
512 } else if (operatorCode === "--") {
513 this.tokens.replaceToken(`(${base} = ${assignmentSnippet} = ${base} - 1, ${base} + 1)`);
514 } else {
515 throw new Error(`Unexpected operator: ${operatorCode}`);
516 }
517 this.tokens.removeToken();
518 return true;
519 }
520
521 processExportDefault() {
522 let exportedRuntimeValue = true;
523 if (
524 this.tokens.matches4(_types.TokenType._export, _types.TokenType._default, _types.TokenType._function, _types.TokenType.name) ||
525 // export default async function
526 (this.tokens.matches5(_types.TokenType._export, _types.TokenType._default, _types.TokenType.name, _types.TokenType._function, _types.TokenType.name) &&
527 this.tokens.matchesContextualAtIndex(
528 this.tokens.currentIndex() + 2,
529 _keywords.ContextualKeyword._async,
530 ))
531 ) {
532 this.tokens.removeInitialToken();
533 this.tokens.removeToken();
534 // Named function export case: change it to a top-level function
535 // declaration followed by exports statement.
536 const name = this.processNamedFunction();
537 this.tokens.appendCode(` exports.default = ${name};`);
538 } else if (
539 this.tokens.matches4(_types.TokenType._export, _types.TokenType._default, _types.TokenType._class, _types.TokenType.name) ||
540 this.tokens.matches5(_types.TokenType._export, _types.TokenType._default, _types.TokenType._abstract, _types.TokenType._class, _types.TokenType.name) ||
541 this.tokens.matches3(_types.TokenType._export, _types.TokenType._default, _types.TokenType.at)
542 ) {
543 this.tokens.removeInitialToken();
544 this.tokens.removeToken();
545 this.copyDecorators();
546 if (this.tokens.matches1(_types.TokenType._abstract)) {
547 this.tokens.removeToken();
548 }
549 const name = this.rootTransformer.processNamedClass();
550 this.tokens.appendCode(` exports.default = ${name};`);
551 // After this point, this is a plain "export default E" statement.
552 } else if (
553 _shouldElideDefaultExport2.default.call(void 0,
554 this.isTypeScriptTransformEnabled,
555 this.keepUnusedImports,
556 this.tokens,
557 this.declarationInfo,
558 )
559 ) {
560 // If the exported value is just an identifier and should be elided by TypeScript
561 // rules, then remove it entirely. It will always have the form `export default e`,
562 // where `e` is an identifier.
563 exportedRuntimeValue = false;
564 this.tokens.removeInitialToken();
565 this.tokens.removeToken();
566 this.tokens.removeToken();
567 } else if (this.reactHotLoaderTransformer) {
568 // We need to assign E to a variable. Change "export default E" to
569 // "let _default; exports.default = _default = E"
570 const defaultVarName = this.nameManager.claimFreeName("_default");
571 this.tokens.replaceToken(`let ${defaultVarName}; exports.`);
572 this.tokens.copyToken();
573 this.tokens.appendCode(` = ${defaultVarName} =`);
574 this.reactHotLoaderTransformer.setExtractedDefaultExportName(defaultVarName);
575 } else {
576 // Change "export default E" to "exports.default = E"
577 this.tokens.replaceToken("exports.");
578 this.tokens.copyToken();
579 this.tokens.appendCode(" =");
580 }
581 if (exportedRuntimeValue) {
582 this.hadDefaultExport = true;
583 }
584 }
585
586 copyDecorators() {
587 while (this.tokens.matches1(_types.TokenType.at)) {
588 this.tokens.copyToken();
589 if (this.tokens.matches1(_types.TokenType.parenL)) {
590 this.tokens.copyExpectedToken(_types.TokenType.parenL);
591 this.rootTransformer.processBalancedCode();
592 this.tokens.copyExpectedToken(_types.TokenType.parenR);
593 } else {
594 this.tokens.copyExpectedToken(_types.TokenType.name);
595 while (this.tokens.matches1(_types.TokenType.dot)) {
596 this.tokens.copyExpectedToken(_types.TokenType.dot);
597 this.tokens.copyExpectedToken(_types.TokenType.name);
598 }
599 if (this.tokens.matches1(_types.TokenType.parenL)) {
600 this.tokens.copyExpectedToken(_types.TokenType.parenL);
601 this.rootTransformer.processBalancedCode();
602 this.tokens.copyExpectedToken(_types.TokenType.parenR);
603 }
604 }
605 }
606 }
607
608 /**
609 * Transform a declaration like `export var`, `export let`, or `export const`.
610 */
611 processExportVar() {
612 if (this.isSimpleExportVar()) {
613 this.processSimpleExportVar();
614 } else {
615 this.processComplexExportVar();
616 }
617 }
618
619 /**
620 * Determine if the export is of the form:
621 * export var/let/const [varName] = [expr];
622 * In other words, determine if function name inference might apply.
623 */
624 isSimpleExportVar() {
625 let tokenIndex = this.tokens.currentIndex();
626 // export
627 tokenIndex++;
628 // var/let/const
629 tokenIndex++;
630 if (!this.tokens.matches1AtIndex(tokenIndex, _types.TokenType.name)) {
631 return false;
632 }
633 tokenIndex++;
634 while (tokenIndex < this.tokens.tokens.length && this.tokens.tokens[tokenIndex].isType) {
635 tokenIndex++;
636 }
637 if (!this.tokens.matches1AtIndex(tokenIndex, _types.TokenType.eq)) {
638 return false;
639 }
640 return true;
641 }
642
643 /**
644 * Transform an `export var` declaration initializing a single variable.
645 *
646 * For example, this:
647 * export const f = () => {};
648 * becomes this:
649 * const f = () => {}; exports.f = f;
650 *
651 * The variable is unused (e.g. exports.f has the true value of the export).
652 * We need to produce an assignment of this form so that the function will
653 * have an inferred name of "f", which wouldn't happen in the more general
654 * case below.
655 */
656 processSimpleExportVar() {
657 // export
658 this.tokens.removeInitialToken();
659 // var/let/const
660 this.tokens.copyToken();
661 const varName = this.tokens.identifierName();
662 // x: number -> x
663 while (!this.tokens.matches1(_types.TokenType.eq)) {
664 this.rootTransformer.processToken();
665 }
666 const endIndex = this.tokens.currentToken().rhsEndIndex;
667 if (endIndex == null) {
668 throw new Error("Expected = token with an end index.");
669 }
670 while (this.tokens.currentIndex() < endIndex) {
671 this.rootTransformer.processToken();
672 }
673 this.tokens.appendCode(`; exports.${varName} = ${varName}`);
674 }
675
676 /**
677 * Transform normal declaration exports, including handling destructuring.
678 * For example, this:
679 * export const {x: [a = 2, b], c} = d;
680 * becomes this:
681 * ({x: [exports.a = 2, exports.b], c: exports.c} = d;)
682 */
683 processComplexExportVar() {
684 this.tokens.removeInitialToken();
685 this.tokens.removeToken();
686 const needsParens = this.tokens.matches1(_types.TokenType.braceL);
687 if (needsParens) {
688 this.tokens.appendCode("(");
689 }
690
691 let depth = 0;
692 while (true) {
693 if (
694 this.tokens.matches1(_types.TokenType.braceL) ||
695 this.tokens.matches1(_types.TokenType.dollarBraceL) ||
696 this.tokens.matches1(_types.TokenType.bracketL)
697 ) {
698 depth++;
699 this.tokens.copyToken();
700 } else if (this.tokens.matches1(_types.TokenType.braceR) || this.tokens.matches1(_types.TokenType.bracketR)) {
701 depth--;
702 this.tokens.copyToken();
703 } else if (
704 depth === 0 &&
705 !this.tokens.matches1(_types.TokenType.name) &&
706 !this.tokens.currentToken().isType
707 ) {
708 break;
709 } else if (this.tokens.matches1(_types.TokenType.eq)) {
710 // Default values might have assignments in the RHS that we want to ignore, so skip past
711 // them.
712 const endIndex = this.tokens.currentToken().rhsEndIndex;
713 if (endIndex == null) {
714 throw new Error("Expected = token with an end index.");
715 }
716 while (this.tokens.currentIndex() < endIndex) {
717 this.rootTransformer.processToken();
718 }
719 } else {
720 const token = this.tokens.currentToken();
721 if (_tokenizer.isDeclaration.call(void 0, token)) {
722 const name = this.tokens.identifierName();
723 let replacement = this.importProcessor.getIdentifierReplacement(name);
724 if (replacement === null) {
725 throw new Error(`Expected a replacement for ${name} in \`export var\` syntax.`);
726 }
727 if (_tokenizer.isObjectShorthandDeclaration.call(void 0, token)) {
728 replacement = `${name}: ${replacement}`;
729 }
730 this.tokens.replaceToken(replacement);
731 } else {
732 this.rootTransformer.processToken();
733 }
734 }
735 }
736
737 if (needsParens) {
738 // Seek to the end of the RHS.
739 const endIndex = this.tokens.currentToken().rhsEndIndex;
740 if (endIndex == null) {
741 throw new Error("Expected = token with an end index.");
742 }
743 while (this.tokens.currentIndex() < endIndex) {
744 this.rootTransformer.processToken();
745 }
746 this.tokens.appendCode(")");
747 }
748 }
749
750 /**
751 * Transform this:
752 * export function foo() {}
753 * into this:
754 * function foo() {} exports.foo = foo;
755 */
756 processExportFunction() {
757 this.tokens.replaceToken("");
758 const name = this.processNamedFunction();
759 this.tokens.appendCode(` exports.${name} = ${name};`);
760 }
761
762 /**
763 * Skip past a function with a name and return that name.
764 */
765 processNamedFunction() {
766 if (this.tokens.matches1(_types.TokenType._function)) {
767 this.tokens.copyToken();
768 } else if (this.tokens.matches2(_types.TokenType.name, _types.TokenType._function)) {
769 if (!this.tokens.matchesContextual(_keywords.ContextualKeyword._async)) {
770 throw new Error("Expected async keyword in function export.");
771 }
772 this.tokens.copyToken();
773 this.tokens.copyToken();
774 }
775 if (this.tokens.matches1(_types.TokenType.star)) {
776 this.tokens.copyToken();
777 }
778 if (!this.tokens.matches1(_types.TokenType.name)) {
779 throw new Error("Expected identifier for exported function name.");
780 }
781 const name = this.tokens.identifierName();
782 this.tokens.copyToken();
783 if (this.tokens.currentToken().isType) {
784 this.tokens.removeInitialToken();
785 while (this.tokens.currentToken().isType) {
786 this.tokens.removeToken();
787 }
788 }
789 this.tokens.copyExpectedToken(_types.TokenType.parenL);
790 this.rootTransformer.processBalancedCode();
791 this.tokens.copyExpectedToken(_types.TokenType.parenR);
792 this.rootTransformer.processPossibleTypeRange();
793 this.tokens.copyExpectedToken(_types.TokenType.braceL);
794 this.rootTransformer.processBalancedCode();
795 this.tokens.copyExpectedToken(_types.TokenType.braceR);
796 return name;
797 }
798
799 /**
800 * Transform this:
801 * export class A {}
802 * into this:
803 * class A {} exports.A = A;
804 */
805 processExportClass() {
806 this.tokens.removeInitialToken();
807 this.copyDecorators();
808 if (this.tokens.matches1(_types.TokenType._abstract)) {
809 this.tokens.removeToken();
810 }
811 const name = this.rootTransformer.processNamedClass();
812 this.tokens.appendCode(` exports.${name} = ${name};`);
813 }
814
815 /**
816 * Transform this:
817 * export {a, b as c};
818 * into this:
819 * exports.a = a; exports.c = b;
820 *
821 * OR
822 *
823 * Transform this:
824 * export {a, b as c} from './foo';
825 * into the pre-generated Object.defineProperty code from the ImportProcessor.
826 *
827 * For the first case, if the TypeScript transform is enabled, we need to skip
828 * exports that are only defined as types.
829 */
830 processExportBindings() {
831 this.tokens.removeInitialToken();
832 this.tokens.removeToken();
833
834 const isReExport = _isExportFrom2.default.call(void 0, this.tokens);
835
836 const exportStatements = [];
837 while (true) {
838 if (this.tokens.matches1(_types.TokenType.braceR)) {
839 this.tokens.removeToken();
840 break;
841 }
842
843 const specifierInfo = _getImportExportSpecifierInfo2.default.call(void 0, this.tokens);
844
845 while (this.tokens.currentIndex() < specifierInfo.endIndex) {
846 this.tokens.removeToken();
847 }
848
849 const shouldRemoveExport =
850 specifierInfo.isType ||
851 (!isReExport && this.shouldElideExportedIdentifier(specifierInfo.leftName));
852 if (!shouldRemoveExport) {
853 const exportedName = specifierInfo.rightName;
854 if (exportedName === "default") {
855 this.hadDefaultExport = true;
856 } else {
857 this.hadNamedExport = true;
858 }
859 const localName = specifierInfo.leftName;
860 const newLocalName = this.importProcessor.getIdentifierReplacement(localName);
861 exportStatements.push(`exports.${exportedName} = ${newLocalName || localName};`);
862 }
863
864 if (this.tokens.matches1(_types.TokenType.braceR)) {
865 this.tokens.removeToken();
866 break;
867 }
868 if (this.tokens.matches2(_types.TokenType.comma, _types.TokenType.braceR)) {
869 this.tokens.removeToken();
870 this.tokens.removeToken();
871 break;
872 } else if (this.tokens.matches1(_types.TokenType.comma)) {
873 this.tokens.removeToken();
874 } else {
875 throw new Error(`Unexpected token: ${JSON.stringify(this.tokens.currentToken())}`);
876 }
877 }
878
879 if (this.tokens.matchesContextual(_keywords.ContextualKeyword._from)) {
880 // This is an export...from, so throw away the normal named export code
881 // and use the Object.defineProperty code from ImportProcessor.
882 this.tokens.removeToken();
883 const path = this.tokens.stringValue();
884 this.tokens.replaceTokenTrimmingLeftWhitespace(this.importProcessor.claimImportCode(path));
885 _removeMaybeImportAttributes.removeMaybeImportAttributes.call(void 0, this.tokens);
886 } else {
887 // This is a normal named export, so use that.
888 this.tokens.appendCode(exportStatements.join(" "));
889 }
890
891 if (this.tokens.matches1(_types.TokenType.semi)) {
892 this.tokens.removeToken();
893 }
894 }
895
896 processExportStar() {
897 this.tokens.removeInitialToken();
898 while (!this.tokens.matches1(_types.TokenType.string)) {
899 this.tokens.removeToken();
900 }
901 const path = this.tokens.stringValue();
902 this.tokens.replaceTokenTrimmingLeftWhitespace(this.importProcessor.claimImportCode(path));
903 _removeMaybeImportAttributes.removeMaybeImportAttributes.call(void 0, this.tokens);
904 if (this.tokens.matches1(_types.TokenType.semi)) {
905 this.tokens.removeToken();
906 }
907 }
908
909 shouldElideExportedIdentifier(name) {
910 return (
911 this.isTypeScriptTransformEnabled &&
912 !this.keepUnusedImports &&
913 !this.declarationInfo.valueDeclarations.has(name)
914 );
915 }
916} exports.default = CJSImportTransformer;
Note: See TracBrowser for help on using the repository browser.