UNPKG

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