summaryrefslogtreecommitdiff
path: root/node_modules/sucrase/dist/esm/transformers/CJSImportTransformer.js
diff options
context:
space:
mode:
authorPhilipp Tanlak <philipp.tanlak@gmail.com>2025-11-24 20:54:57 +0100
committerPhilipp Tanlak <philipp.tanlak@gmail.com>2025-11-24 20:57:48 +0100
commitb1e2c8fd5cb5dfa46bc440a12eafaf56cd844b1c (patch)
tree49d360fd6cbc6a2754efe93524ac47ff0fbe0f7d /node_modules/sucrase/dist/esm/transformers/CJSImportTransformer.js
Docs
Diffstat (limited to 'node_modules/sucrase/dist/esm/transformers/CJSImportTransformer.js')
-rw-r--r--node_modules/sucrase/dist/esm/transformers/CJSImportTransformer.js916
1 files changed, 916 insertions, 0 deletions
diff --git a/node_modules/sucrase/dist/esm/transformers/CJSImportTransformer.js b/node_modules/sucrase/dist/esm/transformers/CJSImportTransformer.js
new file mode 100644
index 0000000..78cf896
--- /dev/null
+++ b/node_modules/sucrase/dist/esm/transformers/CJSImportTransformer.js
@@ -0,0 +1,916 @@
+
+
+
+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)
+ );
+ }
+}