123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916 |
- import {IdentifierRole, isDeclaration, isObjectShorthandDeclaration} from "../parser/tokenizer";
- import {ContextualKeyword} from "../parser/tokenizer/keywords";
- import {TokenType as tt} from "../parser/tokenizer/types";
- import elideImportEquals from "../util/elideImportEquals";
- import getDeclarationInfo, {
- EMPTY_DECLARATION_INFO,
- } from "../util/getDeclarationInfo";
- import getImportExportSpecifierInfo from "../util/getImportExportSpecifierInfo";
- import isExportFrom from "../util/isExportFrom";
- import {removeMaybeImportAttributes} from "../util/removeMaybeImportAttributes";
- import shouldElideDefaultExport from "../util/shouldElideDefaultExport";
- import Transformer from "./Transformer";
- /**
- * Class for editing import statements when we are transforming to commonjs.
- */
- export default class CJSImportTransformer extends Transformer {
- __init() {this.hadExport = false}
- __init2() {this.hadNamedExport = false}
- __init3() {this.hadDefaultExport = false}
-
- constructor(
- rootTransformer,
- tokens,
- importProcessor,
- nameManager,
- helperManager,
- reactHotLoaderTransformer,
- enableLegacyBabel5ModuleInterop,
- enableLegacyTypeScriptModuleInterop,
- isTypeScriptTransformEnabled,
- isFlowTransformEnabled,
- preserveDynamicImport,
- keepUnusedImports,
- ) {
- 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);;
- this.declarationInfo = isTypeScriptTransformEnabled
- ? getDeclarationInfo(tokens)
- : EMPTY_DECLARATION_INFO;
- }
- getPrefixCode() {
- let prefix = "";
- if (this.hadExport) {
- prefix += 'Object.defineProperty(exports, "__esModule", {value: true});';
- }
- return prefix;
- }
- getSuffixCode() {
- if (this.enableLegacyBabel5ModuleInterop && this.hadDefaultExport && !this.hadNamedExport) {
- return "\nmodule.exports = exports.default;\n";
- }
- return "";
- }
- process() {
- // TypeScript `import foo = require('foo');` should always just be translated to plain require.
- if (this.tokens.matches3(tt._import, tt.name, tt.eq)) {
- return this.processImportEquals();
- }
- if (this.tokens.matches1(tt._import)) {
- this.processImport();
- return true;
- }
- if (this.tokens.matches2(tt._export, tt.eq)) {
- this.tokens.replaceToken("module.exports");
- return true;
- }
- if (this.tokens.matches1(tt._export) && !this.tokens.currentToken().isType) {
- this.hadExport = true;
- return this.processExport();
- }
- if (this.tokens.matches2(tt.name, tt.postIncDec)) {
- // Fall through to normal identifier matching if this doesn't apply.
- if (this.processPostIncDec()) {
- return true;
- }
- }
- if (this.tokens.matches1(tt.name) || this.tokens.matches1(tt.jsxName)) {
- return this.processIdentifier();
- }
- if (this.tokens.matches1(tt.eq)) {
- return this.processAssignment();
- }
- if (this.tokens.matches1(tt.assign)) {
- return this.processComplexAssignment();
- }
- if (this.tokens.matches1(tt.preIncDec)) {
- return this.processPreIncDec();
- }
- return false;
- }
- processImportEquals() {
- const importName = this.tokens.identifierNameAtIndex(this.tokens.currentIndex() + 1);
- if (this.importProcessor.shouldAutomaticallyElideImportedName(importName)) {
- // If this name is only used as a type, elide the whole import.
- elideImportEquals(this.tokens);
- } else {
- // Otherwise, switch `import` to `const`.
- this.tokens.replaceToken("const");
- }
- return true;
- }
- /**
- * Transform this:
- * import foo, {bar} from 'baz';
- * into
- * var _baz = require('baz'); var _baz2 = _interopRequireDefault(_baz);
- *
- * The import code was already generated in the import preprocessing step, so
- * we just need to look it up.
- */
- processImport() {
- if (this.tokens.matches2(tt._import, tt.parenL)) {
- if (this.preserveDynamicImport) {
- // Bail out, only making progress for this one token.
- this.tokens.copyToken();
- return;
- }
- const requireWrapper = this.enableLegacyTypeScriptModuleInterop
- ? ""
- : `${this.helperManager.getHelperName("interopRequireWildcard")}(`;
- this.tokens.replaceToken(`Promise.resolve().then(() => ${requireWrapper}require`);
- const contextId = this.tokens.currentToken().contextId;
- if (contextId == null) {
- throw new Error("Expected context ID on dynamic import invocation.");
- }
- this.tokens.copyToken();
- while (!this.tokens.matchesContextIdAndLabel(tt.parenR, contextId)) {
- this.rootTransformer.processToken();
- }
- this.tokens.replaceToken(requireWrapper ? ")))" : "))");
- return;
- }
- const shouldElideImport = this.removeImportAndDetectIfShouldElide();
- if (shouldElideImport) {
- this.tokens.removeToken();
- } else {
- const path = this.tokens.stringValue();
- this.tokens.replaceTokenTrimmingLeftWhitespace(this.importProcessor.claimImportCode(path));
- this.tokens.appendCode(this.importProcessor.claimImportCode(path));
- }
- removeMaybeImportAttributes(this.tokens);
- if (this.tokens.matches1(tt.semi)) {
- this.tokens.removeToken();
- }
- }
- /**
- * Erase this import (since any CJS output would be completely different), and
- * return true if this import is should be elided due to being a type-only
- * import. Such imports will not be emitted at all to avoid side effects.
- *
- * Import elision only happens with the TypeScript or Flow transforms enabled.
- *
- * TODO: This function has some awkward overlap with
- * CJSImportProcessor.pruneTypeOnlyImports , and the two should be unified.
- * That function handles TypeScript implicit import name elision, and removes
- * an import if all typical imported names (without `type`) are removed due
- * to being type-only imports. This function handles Flow import removal and
- * properly distinguishes `import 'foo'` from `import {} from 'foo'` for TS
- * purposes.
- *
- * The position should end at the import string.
- */
- removeImportAndDetectIfShouldElide() {
- this.tokens.removeInitialToken();
- if (
- this.tokens.matchesContextual(ContextualKeyword._type) &&
- !this.tokens.matches1AtIndex(this.tokens.currentIndex() + 1, tt.comma) &&
- !this.tokens.matchesContextualAtIndex(this.tokens.currentIndex() + 1, ContextualKeyword._from)
- ) {
- // This is an "import type" statement, so exit early.
- this.removeRemainingImport();
- return true;
- }
- if (this.tokens.matches1(tt.name) || this.tokens.matches1(tt.star)) {
- // We have a default import or namespace import, so there must be some
- // non-type import.
- this.removeRemainingImport();
- return false;
- }
- if (this.tokens.matches1(tt.string)) {
- // This is a bare import, so we should proceed with the import.
- return false;
- }
- let foundNonTypeImport = false;
- let foundAnyNamedImport = false;
- while (!this.tokens.matches1(tt.string)) {
- // Check if any named imports are of the form "foo" or "foo as bar", with
- // no leading "type".
- if (
- (!foundNonTypeImport && this.tokens.matches1(tt.braceL)) ||
- this.tokens.matches1(tt.comma)
- ) {
- this.tokens.removeToken();
- if (!this.tokens.matches1(tt.braceR)) {
- foundAnyNamedImport = true;
- }
- if (
- this.tokens.matches2(tt.name, tt.comma) ||
- this.tokens.matches2(tt.name, tt.braceR) ||
- this.tokens.matches4(tt.name, tt.name, tt.name, tt.comma) ||
- this.tokens.matches4(tt.name, tt.name, tt.name, tt.braceR)
- ) {
- foundNonTypeImport = true;
- }
- }
- this.tokens.removeToken();
- }
- if (this.keepUnusedImports) {
- return false;
- }
- if (this.isTypeScriptTransformEnabled) {
- return !foundNonTypeImport;
- } else if (this.isFlowTransformEnabled) {
- // In Flow, unlike TS, `import {} from 'foo';` preserves the import.
- return foundAnyNamedImport && !foundNonTypeImport;
- } else {
- return false;
- }
- }
- removeRemainingImport() {
- while (!this.tokens.matches1(tt.string)) {
- this.tokens.removeToken();
- }
- }
- processIdentifier() {
- const token = this.tokens.currentToken();
- if (token.shadowsGlobal) {
- return false;
- }
- if (token.identifierRole === IdentifierRole.ObjectShorthand) {
- return this.processObjectShorthand();
- }
- if (token.identifierRole !== IdentifierRole.Access) {
- return false;
- }
- const replacement = this.importProcessor.getIdentifierReplacement(
- this.tokens.identifierNameForToken(token),
- );
- if (!replacement) {
- return false;
- }
- // Tolerate any number of closing parens while looking for an opening paren
- // that indicates a function call.
- let possibleOpenParenIndex = this.tokens.currentIndex() + 1;
- while (
- possibleOpenParenIndex < this.tokens.tokens.length &&
- this.tokens.tokens[possibleOpenParenIndex].type === tt.parenR
- ) {
- possibleOpenParenIndex++;
- }
- // Avoid treating imported functions as methods of their `exports` object
- // by using `(0, f)` when the identifier is in a paren expression. Else
- // use `Function.prototype.call` when the identifier is a guaranteed
- // function call. When using `call`, pass undefined as the context.
- if (this.tokens.tokens[possibleOpenParenIndex].type === tt.parenL) {
- if (
- this.tokens.tokenAtRelativeIndex(1).type === tt.parenL &&
- this.tokens.tokenAtRelativeIndex(-1).type !== tt._new
- ) {
- this.tokens.replaceToken(`${replacement}.call(void 0, `);
- // Remove the old paren.
- this.tokens.removeToken();
- // Balance out the new paren.
- this.rootTransformer.processBalancedCode();
- this.tokens.copyExpectedToken(tt.parenR);
- } else {
- // See here: http://2ality.com/2015/12/references.html
- this.tokens.replaceToken(`(0, ${replacement})`);
- }
- } else {
- this.tokens.replaceToken(replacement);
- }
- return true;
- }
- processObjectShorthand() {
- const identifier = this.tokens.identifierName();
- const replacement = this.importProcessor.getIdentifierReplacement(identifier);
- if (!replacement) {
- return false;
- }
- this.tokens.replaceToken(`${identifier}: ${replacement}`);
- return true;
- }
- processExport() {
- if (
- this.tokens.matches2(tt._export, tt._enum) ||
- this.tokens.matches3(tt._export, tt._const, tt._enum)
- ) {
- this.hadNamedExport = true;
- // Let the TypeScript transform handle it.
- return false;
- }
- if (this.tokens.matches2(tt._export, tt._default)) {
- if (this.tokens.matches3(tt._export, tt._default, tt._enum)) {
- this.hadDefaultExport = true;
- // Flow export default enums need some special handling, so handle them
- // in that tranform rather than this one.
- return false;
- }
- this.processExportDefault();
- return true;
- } else if (this.tokens.matches2(tt._export, tt.braceL)) {
- this.processExportBindings();
- return true;
- } else if (
- this.tokens.matches2(tt._export, tt.name) &&
- this.tokens.matchesContextualAtIndex(this.tokens.currentIndex() + 1, ContextualKeyword._type)
- ) {
- // export type {a};
- // export type {a as b};
- // export type {a} from './b';
- // export type * from './b';
- // export type * as ns from './b';
- this.tokens.removeInitialToken();
- this.tokens.removeToken();
- if (this.tokens.matches1(tt.braceL)) {
- while (!this.tokens.matches1(tt.braceR)) {
- this.tokens.removeToken();
- }
- this.tokens.removeToken();
- } else {
- // *
- this.tokens.removeToken();
- if (this.tokens.matches1(tt._as)) {
- // as
- this.tokens.removeToken();
- // ns
- this.tokens.removeToken();
- }
- }
- // Remove type re-export `... } from './T'`
- if (
- this.tokens.matchesContextual(ContextualKeyword._from) &&
- this.tokens.matches1AtIndex(this.tokens.currentIndex() + 1, tt.string)
- ) {
- this.tokens.removeToken();
- this.tokens.removeToken();
- removeMaybeImportAttributes(this.tokens);
- }
- return true;
- }
- this.hadNamedExport = true;
- if (
- this.tokens.matches2(tt._export, tt._var) ||
- this.tokens.matches2(tt._export, tt._let) ||
- this.tokens.matches2(tt._export, tt._const)
- ) {
- this.processExportVar();
- return true;
- } else if (
- this.tokens.matches2(tt._export, tt._function) ||
- // export async function
- this.tokens.matches3(tt._export, tt.name, tt._function)
- ) {
- this.processExportFunction();
- return true;
- } else if (
- this.tokens.matches2(tt._export, tt._class) ||
- this.tokens.matches3(tt._export, tt._abstract, tt._class) ||
- this.tokens.matches2(tt._export, tt.at)
- ) {
- this.processExportClass();
- return true;
- } else if (this.tokens.matches2(tt._export, tt.star)) {
- this.processExportStar();
- return true;
- } else {
- throw new Error("Unrecognized export syntax.");
- }
- }
- processAssignment() {
- const index = this.tokens.currentIndex();
- const identifierToken = this.tokens.tokens[index - 1];
- // If the LHS is a type identifier, this must be a declaration like `let a: b = c;`,
- // with `b` as the identifier, so nothing needs to be done in that case.
- if (identifierToken.isType || identifierToken.type !== tt.name) {
- return false;
- }
- if (identifierToken.shadowsGlobal) {
- return false;
- }
- if (index >= 2 && this.tokens.matches1AtIndex(index - 2, tt.dot)) {
- return false;
- }
- if (index >= 2 && [tt._var, tt._let, tt._const].includes(this.tokens.tokens[index - 2].type)) {
- // Declarations don't need an extra assignment. This doesn't avoid the
- // assignment for comma-separated declarations, but it's still correct
- // since the assignment is just redundant.
- return false;
- }
- const assignmentSnippet = this.importProcessor.resolveExportBinding(
- this.tokens.identifierNameForToken(identifierToken),
- );
- if (!assignmentSnippet) {
- return false;
- }
- this.tokens.copyToken();
- this.tokens.appendCode(` ${assignmentSnippet} =`);
- return true;
- }
- /**
- * Process something like `a += 3`, where `a` might be an exported value.
- */
- processComplexAssignment() {
- const index = this.tokens.currentIndex();
- const identifierToken = this.tokens.tokens[index - 1];
- if (identifierToken.type !== tt.name) {
- return false;
- }
- if (identifierToken.shadowsGlobal) {
- return false;
- }
- if (index >= 2 && this.tokens.matches1AtIndex(index - 2, tt.dot)) {
- return false;
- }
- const assignmentSnippet = this.importProcessor.resolveExportBinding(
- this.tokens.identifierNameForToken(identifierToken),
- );
- if (!assignmentSnippet) {
- return false;
- }
- this.tokens.appendCode(` = ${assignmentSnippet}`);
- this.tokens.copyToken();
- return true;
- }
- /**
- * Process something like `++a`, where `a` might be an exported value.
- */
- processPreIncDec() {
- const index = this.tokens.currentIndex();
- const identifierToken = this.tokens.tokens[index + 1];
- if (identifierToken.type !== tt.name) {
- return false;
- }
- if (identifierToken.shadowsGlobal) {
- return false;
- }
- // Ignore things like ++a.b and ++a[b] and ++a().b.
- if (
- index + 2 < this.tokens.tokens.length &&
- (this.tokens.matches1AtIndex(index + 2, tt.dot) ||
- this.tokens.matches1AtIndex(index + 2, tt.bracketL) ||
- this.tokens.matches1AtIndex(index + 2, tt.parenL))
- ) {
- return false;
- }
- const identifierName = this.tokens.identifierNameForToken(identifierToken);
- const assignmentSnippet = this.importProcessor.resolveExportBinding(identifierName);
- if (!assignmentSnippet) {
- return false;
- }
- this.tokens.appendCode(`${assignmentSnippet} = `);
- this.tokens.copyToken();
- return true;
- }
- /**
- * Process something like `a++`, where `a` might be an exported value.
- * This starts at the `a`, not at the `++`.
- */
- processPostIncDec() {
- const index = this.tokens.currentIndex();
- const identifierToken = this.tokens.tokens[index];
- const operatorToken = this.tokens.tokens[index + 1];
- if (identifierToken.type !== tt.name) {
- return false;
- }
- if (identifierToken.shadowsGlobal) {
- return false;
- }
- if (index >= 1 && this.tokens.matches1AtIndex(index - 1, tt.dot)) {
- return false;
- }
- const identifierName = this.tokens.identifierNameForToken(identifierToken);
- const assignmentSnippet = this.importProcessor.resolveExportBinding(identifierName);
- if (!assignmentSnippet) {
- return false;
- }
- const operatorCode = this.tokens.rawCodeForToken(operatorToken);
- // We might also replace the identifier with something like exports.x, so
- // do that replacement here as well.
- const base = this.importProcessor.getIdentifierReplacement(identifierName) || identifierName;
- if (operatorCode === "++") {
- this.tokens.replaceToken(`(${base} = ${assignmentSnippet} = ${base} + 1, ${base} - 1)`);
- } else if (operatorCode === "--") {
- this.tokens.replaceToken(`(${base} = ${assignmentSnippet} = ${base} - 1, ${base} + 1)`);
- } else {
- throw new Error(`Unexpected operator: ${operatorCode}`);
- }
- this.tokens.removeToken();
- return true;
- }
- processExportDefault() {
- let exportedRuntimeValue = true;
- if (
- this.tokens.matches4(tt._export, tt._default, tt._function, tt.name) ||
- // export default async function
- (this.tokens.matches5(tt._export, tt._default, tt.name, tt._function, tt.name) &&
- this.tokens.matchesContextualAtIndex(
- this.tokens.currentIndex() + 2,
- ContextualKeyword._async,
- ))
- ) {
- this.tokens.removeInitialToken();
- this.tokens.removeToken();
- // Named function export case: change it to a top-level function
- // declaration followed by exports statement.
- const name = this.processNamedFunction();
- this.tokens.appendCode(` exports.default = ${name};`);
- } else if (
- this.tokens.matches4(tt._export, tt._default, tt._class, tt.name) ||
- this.tokens.matches5(tt._export, tt._default, tt._abstract, tt._class, tt.name) ||
- this.tokens.matches3(tt._export, tt._default, tt.at)
- ) {
- this.tokens.removeInitialToken();
- this.tokens.removeToken();
- this.copyDecorators();
- if (this.tokens.matches1(tt._abstract)) {
- this.tokens.removeToken();
- }
- const name = this.rootTransformer.processNamedClass();
- this.tokens.appendCode(` exports.default = ${name};`);
- // After this point, this is a plain "export default E" statement.
- } else if (
- shouldElideDefaultExport(
- this.isTypeScriptTransformEnabled,
- this.keepUnusedImports,
- this.tokens,
- this.declarationInfo,
- )
- ) {
- // If the exported value is just an identifier and should be elided by TypeScript
- // rules, then remove it entirely. It will always have the form `export default e`,
- // where `e` is an identifier.
- exportedRuntimeValue = false;
- this.tokens.removeInitialToken();
- this.tokens.removeToken();
- this.tokens.removeToken();
- } else if (this.reactHotLoaderTransformer) {
- // We need to assign E to a variable. Change "export default E" to
- // "let _default; exports.default = _default = E"
- const defaultVarName = this.nameManager.claimFreeName("_default");
- this.tokens.replaceToken(`let ${defaultVarName}; exports.`);
- this.tokens.copyToken();
- this.tokens.appendCode(` = ${defaultVarName} =`);
- this.reactHotLoaderTransformer.setExtractedDefaultExportName(defaultVarName);
- } else {
- // Change "export default E" to "exports.default = E"
- this.tokens.replaceToken("exports.");
- this.tokens.copyToken();
- this.tokens.appendCode(" =");
- }
- if (exportedRuntimeValue) {
- this.hadDefaultExport = true;
- }
- }
- copyDecorators() {
- while (this.tokens.matches1(tt.at)) {
- this.tokens.copyToken();
- if (this.tokens.matches1(tt.parenL)) {
- this.tokens.copyExpectedToken(tt.parenL);
- this.rootTransformer.processBalancedCode();
- this.tokens.copyExpectedToken(tt.parenR);
- } else {
- this.tokens.copyExpectedToken(tt.name);
- while (this.tokens.matches1(tt.dot)) {
- this.tokens.copyExpectedToken(tt.dot);
- this.tokens.copyExpectedToken(tt.name);
- }
- if (this.tokens.matches1(tt.parenL)) {
- this.tokens.copyExpectedToken(tt.parenL);
- this.rootTransformer.processBalancedCode();
- this.tokens.copyExpectedToken(tt.parenR);
- }
- }
- }
- }
- /**
- * Transform a declaration like `export var`, `export let`, or `export const`.
- */
- processExportVar() {
- if (this.isSimpleExportVar()) {
- this.processSimpleExportVar();
- } else {
- this.processComplexExportVar();
- }
- }
- /**
- * Determine if the export is of the form:
- * export var/let/const [varName] = [expr];
- * In other words, determine if function name inference might apply.
- */
- isSimpleExportVar() {
- let tokenIndex = this.tokens.currentIndex();
- // export
- tokenIndex++;
- // var/let/const
- tokenIndex++;
- if (!this.tokens.matches1AtIndex(tokenIndex, tt.name)) {
- return false;
- }
- tokenIndex++;
- while (tokenIndex < this.tokens.tokens.length && this.tokens.tokens[tokenIndex].isType) {
- tokenIndex++;
- }
- if (!this.tokens.matches1AtIndex(tokenIndex, tt.eq)) {
- return false;
- }
- return true;
- }
- /**
- * Transform an `export var` declaration initializing a single variable.
- *
- * For example, this:
- * export const f = () => {};
- * becomes this:
- * const f = () => {}; exports.f = f;
- *
- * The variable is unused (e.g. exports.f has the true value of the export).
- * We need to produce an assignment of this form so that the function will
- * have an inferred name of "f", which wouldn't happen in the more general
- * case below.
- */
- processSimpleExportVar() {
- // export
- this.tokens.removeInitialToken();
- // var/let/const
- this.tokens.copyToken();
- const varName = this.tokens.identifierName();
- // x: number -> x
- while (!this.tokens.matches1(tt.eq)) {
- this.rootTransformer.processToken();
- }
- const endIndex = this.tokens.currentToken().rhsEndIndex;
- if (endIndex == null) {
- throw new Error("Expected = token with an end index.");
- }
- while (this.tokens.currentIndex() < endIndex) {
- this.rootTransformer.processToken();
- }
- this.tokens.appendCode(`; exports.${varName} = ${varName}`);
- }
- /**
- * Transform normal declaration exports, including handling destructuring.
- * For example, this:
- * export const {x: [a = 2, b], c} = d;
- * becomes this:
- * ({x: [exports.a = 2, exports.b], c: exports.c} = d;)
- */
- processComplexExportVar() {
- this.tokens.removeInitialToken();
- this.tokens.removeToken();
- const needsParens = this.tokens.matches1(tt.braceL);
- if (needsParens) {
- this.tokens.appendCode("(");
- }
- let depth = 0;
- while (true) {
- if (
- this.tokens.matches1(tt.braceL) ||
- this.tokens.matches1(tt.dollarBraceL) ||
- this.tokens.matches1(tt.bracketL)
- ) {
- depth++;
- this.tokens.copyToken();
- } else if (this.tokens.matches1(tt.braceR) || this.tokens.matches1(tt.bracketR)) {
- depth--;
- this.tokens.copyToken();
- } else if (
- depth === 0 &&
- !this.tokens.matches1(tt.name) &&
- !this.tokens.currentToken().isType
- ) {
- break;
- } else if (this.tokens.matches1(tt.eq)) {
- // Default values might have assignments in the RHS that we want to ignore, so skip past
- // them.
- const endIndex = this.tokens.currentToken().rhsEndIndex;
- if (endIndex == null) {
- throw new Error("Expected = token with an end index.");
- }
- while (this.tokens.currentIndex() < endIndex) {
- this.rootTransformer.processToken();
- }
- } else {
- const token = this.tokens.currentToken();
- if (isDeclaration(token)) {
- const name = this.tokens.identifierName();
- let replacement = this.importProcessor.getIdentifierReplacement(name);
- if (replacement === null) {
- throw new Error(`Expected a replacement for ${name} in \`export var\` syntax.`);
- }
- if (isObjectShorthandDeclaration(token)) {
- replacement = `${name}: ${replacement}`;
- }
- this.tokens.replaceToken(replacement);
- } else {
- this.rootTransformer.processToken();
- }
- }
- }
- if (needsParens) {
- // Seek to the end of the RHS.
- const endIndex = this.tokens.currentToken().rhsEndIndex;
- if (endIndex == null) {
- throw new Error("Expected = token with an end index.");
- }
- while (this.tokens.currentIndex() < endIndex) {
- this.rootTransformer.processToken();
- }
- this.tokens.appendCode(")");
- }
- }
- /**
- * Transform this:
- * export function foo() {}
- * into this:
- * function foo() {} exports.foo = foo;
- */
- processExportFunction() {
- this.tokens.replaceToken("");
- const name = this.processNamedFunction();
- this.tokens.appendCode(` exports.${name} = ${name};`);
- }
- /**
- * Skip past a function with a name and return that name.
- */
- processNamedFunction() {
- if (this.tokens.matches1(tt._function)) {
- this.tokens.copyToken();
- } else if (this.tokens.matches2(tt.name, tt._function)) {
- if (!this.tokens.matchesContextual(ContextualKeyword._async)) {
- throw new Error("Expected async keyword in function export.");
- }
- this.tokens.copyToken();
- this.tokens.copyToken();
- }
- if (this.tokens.matches1(tt.star)) {
- this.tokens.copyToken();
- }
- if (!this.tokens.matches1(tt.name)) {
- throw new Error("Expected identifier for exported function name.");
- }
- const name = this.tokens.identifierName();
- this.tokens.copyToken();
- if (this.tokens.currentToken().isType) {
- this.tokens.removeInitialToken();
- while (this.tokens.currentToken().isType) {
- this.tokens.removeToken();
- }
- }
- this.tokens.copyExpectedToken(tt.parenL);
- this.rootTransformer.processBalancedCode();
- this.tokens.copyExpectedToken(tt.parenR);
- this.rootTransformer.processPossibleTypeRange();
- this.tokens.copyExpectedToken(tt.braceL);
- this.rootTransformer.processBalancedCode();
- this.tokens.copyExpectedToken(tt.braceR);
- return name;
- }
- /**
- * Transform this:
- * export class A {}
- * into this:
- * class A {} exports.A = A;
- */
- processExportClass() {
- this.tokens.removeInitialToken();
- this.copyDecorators();
- if (this.tokens.matches1(tt._abstract)) {
- this.tokens.removeToken();
- }
- const name = this.rootTransformer.processNamedClass();
- this.tokens.appendCode(` exports.${name} = ${name};`);
- }
- /**
- * Transform this:
- * export {a, b as c};
- * into this:
- * exports.a = a; exports.c = b;
- *
- * OR
- *
- * Transform this:
- * export {a, b as c} from './foo';
- * into the pre-generated Object.defineProperty code from the ImportProcessor.
- *
- * For the first case, if the TypeScript transform is enabled, we need to skip
- * exports that are only defined as types.
- */
- processExportBindings() {
- this.tokens.removeInitialToken();
- this.tokens.removeToken();
- const isReExport = isExportFrom(this.tokens);
- const exportStatements = [];
- while (true) {
- if (this.tokens.matches1(tt.braceR)) {
- this.tokens.removeToken();
- break;
- }
- const specifierInfo = getImportExportSpecifierInfo(this.tokens);
- while (this.tokens.currentIndex() < specifierInfo.endIndex) {
- this.tokens.removeToken();
- }
- const shouldRemoveExport =
- specifierInfo.isType ||
- (!isReExport && this.shouldElideExportedIdentifier(specifierInfo.leftName));
- if (!shouldRemoveExport) {
- const exportedName = specifierInfo.rightName;
- if (exportedName === "default") {
- this.hadDefaultExport = true;
- } else {
- this.hadNamedExport = true;
- }
- const localName = specifierInfo.leftName;
- const newLocalName = this.importProcessor.getIdentifierReplacement(localName);
- exportStatements.push(`exports.${exportedName} = ${newLocalName || localName};`);
- }
- if (this.tokens.matches1(tt.braceR)) {
- this.tokens.removeToken();
- break;
- }
- if (this.tokens.matches2(tt.comma, tt.braceR)) {
- this.tokens.removeToken();
- this.tokens.removeToken();
- break;
- } else if (this.tokens.matches1(tt.comma)) {
- this.tokens.removeToken();
- } else {
- throw new Error(`Unexpected token: ${JSON.stringify(this.tokens.currentToken())}`);
- }
- }
- if (this.tokens.matchesContextual(ContextualKeyword._from)) {
- // This is an export...from, so throw away the normal named export code
- // and use the Object.defineProperty code from ImportProcessor.
- this.tokens.removeToken();
- const path = this.tokens.stringValue();
- this.tokens.replaceTokenTrimmingLeftWhitespace(this.importProcessor.claimImportCode(path));
- removeMaybeImportAttributes(this.tokens);
- } else {
- // This is a normal named export, so use that.
- this.tokens.appendCode(exportStatements.join(" "));
- }
- if (this.tokens.matches1(tt.semi)) {
- this.tokens.removeToken();
- }
- }
- processExportStar() {
- this.tokens.removeInitialToken();
- while (!this.tokens.matches1(tt.string)) {
- this.tokens.removeToken();
- }
- const path = this.tokens.stringValue();
- this.tokens.replaceTokenTrimmingLeftWhitespace(this.importProcessor.claimImportCode(path));
- removeMaybeImportAttributes(this.tokens);
- if (this.tokens.matches1(tt.semi)) {
- this.tokens.removeToken();
- }
- }
- shouldElideExportedIdentifier(name) {
- return (
- this.isTypeScriptTransformEnabled &&
- !this.keepUnusedImports &&
- !this.declarationInfo.valueDeclarations.has(name)
- );
- }
- }
|