| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364 |
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
- var _keywords = require('../parser/tokenizer/keywords');
- var _types = require('../parser/tokenizer/types');
- var _elideImportEquals = require('../util/elideImportEquals'); var _elideImportEquals2 = _interopRequireDefault(_elideImportEquals);
- var _getDeclarationInfo = require('../util/getDeclarationInfo'); var _getDeclarationInfo2 = _interopRequireDefault(_getDeclarationInfo);
- var _getImportExportSpecifierInfo = require('../util/getImportExportSpecifierInfo'); var _getImportExportSpecifierInfo2 = _interopRequireDefault(_getImportExportSpecifierInfo);
- var _getNonTypeIdentifiers = require('../util/getNonTypeIdentifiers');
- var _removeMaybeImportAssertion = require('../util/removeMaybeImportAssertion');
- var _shouldElideDefaultExport = require('../util/shouldElideDefaultExport'); var _shouldElideDefaultExport2 = _interopRequireDefault(_shouldElideDefaultExport);
- var _Transformer = require('./Transformer'); var _Transformer2 = _interopRequireDefault(_Transformer);
- /**
- * Class for editing import statements when we are keeping the code as ESM. We still need to remove
- * type-only imports in TypeScript and Flow.
- */
- class ESMImportTransformer extends _Transformer2.default {
-
-
-
- constructor(
- tokens,
- nameManager,
- helperManager,
- reactHotLoaderTransformer,
- isTypeScriptTransformEnabled,
- options,
- ) {
- super();this.tokens = tokens;this.nameManager = nameManager;this.helperManager = helperManager;this.reactHotLoaderTransformer = reactHotLoaderTransformer;this.isTypeScriptTransformEnabled = isTypeScriptTransformEnabled;;
- this.nonTypeIdentifiers = isTypeScriptTransformEnabled
- ? _getNonTypeIdentifiers.getNonTypeIdentifiers.call(void 0, tokens, options)
- : new Set();
- this.declarationInfo = isTypeScriptTransformEnabled
- ? _getDeclarationInfo2.default.call(void 0, tokens)
- : _getDeclarationInfo.EMPTY_DECLARATION_INFO;
- this.injectCreateRequireForImportRequire = Boolean(options.injectCreateRequireForImportRequire);
- }
- process() {
- // TypeScript `import foo = require('foo');` should always just be translated to plain require.
- if (this.tokens.matches3(_types.TokenType._import, _types.TokenType.name, _types.TokenType.eq)) {
- return this.processImportEquals();
- }
- if (
- this.tokens.matches4(_types.TokenType._import, _types.TokenType.name, _types.TokenType.name, _types.TokenType.eq) &&
- this.tokens.matchesContextualAtIndex(this.tokens.currentIndex() + 1, _keywords.ContextualKeyword._type)
- ) {
- // import type T = require('T')
- this.tokens.removeInitialToken();
- // This construct is always exactly 8 tokens long, so remove the 7 remaining tokens.
- for (let i = 0; i < 7; i++) {
- this.tokens.removeToken();
- }
- return true;
- }
- if (this.tokens.matches2(_types.TokenType._export, _types.TokenType.eq)) {
- this.tokens.replaceToken("module.exports");
- return true;
- }
- if (
- this.tokens.matches5(_types.TokenType._export, _types.TokenType._import, _types.TokenType.name, _types.TokenType.name, _types.TokenType.eq) &&
- this.tokens.matchesContextualAtIndex(this.tokens.currentIndex() + 2, _keywords.ContextualKeyword._type)
- ) {
- // export import type T = require('T')
- this.tokens.removeInitialToken();
- // This construct is always exactly 9 tokens long, so remove the 8 remaining tokens.
- for (let i = 0; i < 8; i++) {
- this.tokens.removeToken();
- }
- return true;
- }
- if (this.tokens.matches1(_types.TokenType._import)) {
- return this.processImport();
- }
- if (this.tokens.matches2(_types.TokenType._export, _types.TokenType._default)) {
- return this.processExportDefault();
- }
- if (this.tokens.matches2(_types.TokenType._export, _types.TokenType.braceL)) {
- return this.processNamedExports();
- }
- if (
- this.tokens.matches2(_types.TokenType._export, _types.TokenType.name) &&
- this.tokens.matchesContextualAtIndex(this.tokens.currentIndex() + 1, _keywords.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(_types.TokenType.braceL)) {
- while (!this.tokens.matches1(_types.TokenType.braceR)) {
- this.tokens.removeToken();
- }
- this.tokens.removeToken();
- } else {
- // *
- this.tokens.removeToken();
- if (this.tokens.matches1(_types.TokenType._as)) {
- // as
- this.tokens.removeToken();
- // ns
- this.tokens.removeToken();
- }
- }
- // Remove type re-export `... } from './T'`
- if (
- this.tokens.matchesContextual(_keywords.ContextualKeyword._from) &&
- this.tokens.matches1AtIndex(this.tokens.currentIndex() + 1, _types.TokenType.string)
- ) {
- this.tokens.removeToken();
- this.tokens.removeToken();
- _removeMaybeImportAssertion.removeMaybeImportAssertion.call(void 0, this.tokens);
- }
- return true;
- }
- return false;
- }
- processImportEquals() {
- const importName = this.tokens.identifierNameAtIndex(this.tokens.currentIndex() + 1);
- if (this.isTypeName(importName)) {
- // If this name is only used as a type, elide the whole import.
- _elideImportEquals2.default.call(void 0, this.tokens);
- } else if (this.injectCreateRequireForImportRequire) {
- // We're using require in an environment (Node ESM) that doesn't provide
- // it as a global, so generate a helper to import it.
- // import -> const
- this.tokens.replaceToken("const");
- // Foo
- this.tokens.copyToken();
- // =
- this.tokens.copyToken();
- // require
- this.tokens.replaceToken(this.helperManager.getHelperName("require"));
- } else {
- // Otherwise, just switch `import` to `const`.
- this.tokens.replaceToken("const");
- }
- return true;
- }
- processImport() {
- if (this.tokens.matches2(_types.TokenType._import, _types.TokenType.parenL)) {
- // Dynamic imports don't need to be transformed.
- return false;
- }
- const snapshot = this.tokens.snapshot();
- const allImportsRemoved = this.removeImportTypeBindings();
- if (allImportsRemoved) {
- this.tokens.restoreToSnapshot(snapshot);
- while (!this.tokens.matches1(_types.TokenType.string)) {
- this.tokens.removeToken();
- }
- this.tokens.removeToken();
- _removeMaybeImportAssertion.removeMaybeImportAssertion.call(void 0, this.tokens);
- if (this.tokens.matches1(_types.TokenType.semi)) {
- this.tokens.removeToken();
- }
- }
- return true;
- }
- /**
- * Remove type bindings from this import, leaving the rest of the import intact.
- *
- * Return true if this import was ONLY types, and thus is eligible for removal. This will bail out
- * of the replacement operation, so we can return early here.
- */
- removeImportTypeBindings() {
- this.tokens.copyExpectedToken(_types.TokenType._import);
- if (
- this.tokens.matchesContextual(_keywords.ContextualKeyword._type) &&
- !this.tokens.matches1AtIndex(this.tokens.currentIndex() + 1, _types.TokenType.comma) &&
- !this.tokens.matchesContextualAtIndex(this.tokens.currentIndex() + 1, _keywords.ContextualKeyword._from)
- ) {
- // This is an "import type" statement, so exit early.
- return true;
- }
- if (this.tokens.matches1(_types.TokenType.string)) {
- // This is a bare import, so we should proceed with the import.
- this.tokens.copyToken();
- return false;
- }
- // Skip the "module" token in import reflection.
- if (
- this.tokens.matchesContextual(_keywords.ContextualKeyword._module) &&
- this.tokens.matchesContextualAtIndex(this.tokens.currentIndex() + 2, _keywords.ContextualKeyword._from)
- ) {
- this.tokens.copyToken();
- }
- let foundNonTypeImport = false;
- let needsComma = false;
- if (this.tokens.matches1(_types.TokenType.name)) {
- if (this.isTypeName(this.tokens.identifierName())) {
- this.tokens.removeToken();
- if (this.tokens.matches1(_types.TokenType.comma)) {
- this.tokens.removeToken();
- }
- } else {
- foundNonTypeImport = true;
- this.tokens.copyToken();
- if (this.tokens.matches1(_types.TokenType.comma)) {
- // We're in a statement like:
- // import A, * as B from './A';
- // or
- // import A, {foo} from './A';
- // where the `A` is being kept. The comma should be removed if an only
- // if the next part of the import statement is elided, but that's hard
- // to determine at this point in the code. Instead, always remove it
- // and set a flag to add it back if necessary.
- needsComma = true;
- this.tokens.removeToken();
- }
- }
- }
- if (this.tokens.matches1(_types.TokenType.star)) {
- if (this.isTypeName(this.tokens.identifierNameAtRelativeIndex(2))) {
- this.tokens.removeToken();
- this.tokens.removeToken();
- this.tokens.removeToken();
- } else {
- if (needsComma) {
- this.tokens.appendCode(",");
- }
- foundNonTypeImport = true;
- this.tokens.copyExpectedToken(_types.TokenType.star);
- this.tokens.copyExpectedToken(_types.TokenType.name);
- this.tokens.copyExpectedToken(_types.TokenType.name);
- }
- } else if (this.tokens.matches1(_types.TokenType.braceL)) {
- if (needsComma) {
- this.tokens.appendCode(",");
- }
- this.tokens.copyToken();
- while (!this.tokens.matches1(_types.TokenType.braceR)) {
- const specifierInfo = _getImportExportSpecifierInfo2.default.call(void 0, this.tokens);
- if (specifierInfo.isType || this.isTypeName(specifierInfo.rightName)) {
- while (this.tokens.currentIndex() < specifierInfo.endIndex) {
- this.tokens.removeToken();
- }
- if (this.tokens.matches1(_types.TokenType.comma)) {
- this.tokens.removeToken();
- }
- } else {
- foundNonTypeImport = true;
- while (this.tokens.currentIndex() < specifierInfo.endIndex) {
- this.tokens.copyToken();
- }
- if (this.tokens.matches1(_types.TokenType.comma)) {
- this.tokens.copyToken();
- }
- }
- }
- this.tokens.copyExpectedToken(_types.TokenType.braceR);
- }
- return !foundNonTypeImport;
- }
- isTypeName(name) {
- return this.isTypeScriptTransformEnabled && !this.nonTypeIdentifiers.has(name);
- }
- processExportDefault() {
- if (
- _shouldElideDefaultExport2.default.call(void 0, this.isTypeScriptTransformEnabled, 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.
- this.tokens.removeInitialToken();
- this.tokens.removeToken();
- this.tokens.removeToken();
- return true;
- }
- const alreadyHasName =
- this.tokens.matches4(_types.TokenType._export, _types.TokenType._default, _types.TokenType._function, _types.TokenType.name) ||
- // export default async function
- (this.tokens.matches5(_types.TokenType._export, _types.TokenType._default, _types.TokenType.name, _types.TokenType._function, _types.TokenType.name) &&
- this.tokens.matchesContextualAtIndex(
- this.tokens.currentIndex() + 2,
- _keywords.ContextualKeyword._async,
- )) ||
- this.tokens.matches4(_types.TokenType._export, _types.TokenType._default, _types.TokenType._class, _types.TokenType.name) ||
- this.tokens.matches5(_types.TokenType._export, _types.TokenType._default, _types.TokenType._abstract, _types.TokenType._class, _types.TokenType.name);
- if (!alreadyHasName && this.reactHotLoaderTransformer) {
- // This is a plain "export default E" statement and we need to assign E to a variable.
- // Change "export default E" to "let _default; export default _default = E"
- const defaultVarName = this.nameManager.claimFreeName("_default");
- this.tokens.replaceToken(`let ${defaultVarName}; export`);
- this.tokens.copyToken();
- this.tokens.appendCode(` ${defaultVarName} =`);
- this.reactHotLoaderTransformer.setExtractedDefaultExportName(defaultVarName);
- return true;
- }
- return false;
- }
- /**
- * In TypeScript, we need to remove named exports that were never declared or only declared as a
- * type.
- */
- processNamedExports() {
- if (!this.isTypeScriptTransformEnabled) {
- return false;
- }
- this.tokens.copyExpectedToken(_types.TokenType._export);
- this.tokens.copyExpectedToken(_types.TokenType.braceL);
- while (!this.tokens.matches1(_types.TokenType.braceR)) {
- const specifierInfo = _getImportExportSpecifierInfo2.default.call(void 0, this.tokens);
- if (specifierInfo.isType || this.shouldElideExportedName(specifierInfo.leftName)) {
- // Type export, so remove all tokens, including any comma.
- while (this.tokens.currentIndex() < specifierInfo.endIndex) {
- this.tokens.removeToken();
- }
- if (this.tokens.matches1(_types.TokenType.comma)) {
- this.tokens.removeToken();
- }
- } else {
- // Non-type export, so copy all tokens, including any comma.
- while (this.tokens.currentIndex() < specifierInfo.endIndex) {
- this.tokens.copyToken();
- }
- if (this.tokens.matches1(_types.TokenType.comma)) {
- this.tokens.copyToken();
- }
- }
- }
- this.tokens.copyExpectedToken(_types.TokenType.braceR);
- return true;
- }
- /**
- * ESM elides all imports with the rule that we only elide if we see that it's
- * a type and never see it as a value. This is in contrast to CJS, which
- * elides imports that are completely unknown.
- */
- shouldElideExportedName(name) {
- return (
- this.isTypeScriptTransformEnabled &&
- this.declarationInfo.typeDeclarations.has(name) &&
- !this.declarationInfo.valueDeclarations.has(name)
- );
- }
- } exports.default = ESMImportTransformer;
|