astro-ghostcms/.pnpm-store/v3/files/e0/c238f6b6cce68e2d6dfac1c666c...

917 lines
31 KiB
Plaintext

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)
);
}
}