Comparing version 3.32.0 to 3.33.0
@@ -46,4 +46,5 @@ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
isTypeScriptTransformEnabled, | ||
keepUnusedImports, | ||
helperManager, | ||
) {;this.nameManager = nameManager;this.tokens = tokens;this.enableLegacyTypeScriptModuleInterop = enableLegacyTypeScriptModuleInterop;this.options = options;this.isTypeScriptTransformEnabled = isTypeScriptTransformEnabled;this.helperManager = helperManager;CJSImportProcessor.prototype.__init.call(this);CJSImportProcessor.prototype.__init2.call(this);CJSImportProcessor.prototype.__init3.call(this);CJSImportProcessor.prototype.__init4.call(this);CJSImportProcessor.prototype.__init5.call(this);} | ||
) {;this.nameManager = nameManager;this.tokens = tokens;this.enableLegacyTypeScriptModuleInterop = enableLegacyTypeScriptModuleInterop;this.options = options;this.isTypeScriptTransformEnabled = isTypeScriptTransformEnabled;this.keepUnusedImports = keepUnusedImports;this.helperManager = helperManager;CJSImportProcessor.prototype.__init.call(this);CJSImportProcessor.prototype.__init2.call(this);CJSImportProcessor.prototype.__init3.call(this);CJSImportProcessor.prototype.__init4.call(this);CJSImportProcessor.prototype.__init5.call(this);} | ||
@@ -69,4 +70,4 @@ preprocessTokens() { | ||
/** | ||
* In TypeScript, import statements that only import types should be removed. This does not count | ||
* bare imports. | ||
* In TypeScript, import statements that only import types should be removed. | ||
* This includes `import {} from 'foo';`, but not `import 'foo';`. | ||
*/ | ||
@@ -89,3 +90,3 @@ pruneTypeOnlyImports() { | ||
]; | ||
if (names.every((name) => this.isTypeName(name))) { | ||
if (names.every((name) => this.shouldAutomaticallyElideImportedName(name))) { | ||
this.importsToReplace.set(path, ""); | ||
@@ -96,4 +97,8 @@ } | ||
isTypeName(name) { | ||
return this.isTypeScriptTransformEnabled && !this.nonTypeIdentifiers.has(name); | ||
shouldAutomaticallyElideImportedName(name) { | ||
return ( | ||
this.isTypeScriptTransformEnabled && | ||
!this.keepUnusedImports && | ||
!this.nonTypeIdentifiers.has(name) | ||
); | ||
} | ||
@@ -100,0 +105,0 @@ |
@@ -46,4 +46,5 @@ | ||
isTypeScriptTransformEnabled, | ||
keepUnusedImports, | ||
helperManager, | ||
) {;this.nameManager = nameManager;this.tokens = tokens;this.enableLegacyTypeScriptModuleInterop = enableLegacyTypeScriptModuleInterop;this.options = options;this.isTypeScriptTransformEnabled = isTypeScriptTransformEnabled;this.helperManager = helperManager;CJSImportProcessor.prototype.__init.call(this);CJSImportProcessor.prototype.__init2.call(this);CJSImportProcessor.prototype.__init3.call(this);CJSImportProcessor.prototype.__init4.call(this);CJSImportProcessor.prototype.__init5.call(this);} | ||
) {;this.nameManager = nameManager;this.tokens = tokens;this.enableLegacyTypeScriptModuleInterop = enableLegacyTypeScriptModuleInterop;this.options = options;this.isTypeScriptTransformEnabled = isTypeScriptTransformEnabled;this.keepUnusedImports = keepUnusedImports;this.helperManager = helperManager;CJSImportProcessor.prototype.__init.call(this);CJSImportProcessor.prototype.__init2.call(this);CJSImportProcessor.prototype.__init3.call(this);CJSImportProcessor.prototype.__init4.call(this);CJSImportProcessor.prototype.__init5.call(this);} | ||
@@ -69,4 +70,4 @@ preprocessTokens() { | ||
/** | ||
* In TypeScript, import statements that only import types should be removed. This does not count | ||
* bare imports. | ||
* In TypeScript, import statements that only import types should be removed. | ||
* This includes `import {} from 'foo';`, but not `import 'foo';`. | ||
*/ | ||
@@ -89,3 +90,3 @@ pruneTypeOnlyImports() { | ||
]; | ||
if (names.every((name) => this.isTypeName(name))) { | ||
if (names.every((name) => this.shouldAutomaticallyElideImportedName(name))) { | ||
this.importsToReplace.set(path, ""); | ||
@@ -96,4 +97,8 @@ } | ||
isTypeName(name) { | ||
return this.isTypeScriptTransformEnabled && !this.nonTypeIdentifiers.has(name); | ||
shouldAutomaticallyElideImportedName(name) { | ||
return ( | ||
this.isTypeScriptTransformEnabled && | ||
!this.keepUnusedImports && | ||
!this.nonTypeIdentifiers.has(name) | ||
); | ||
} | ||
@@ -100,0 +105,0 @@ |
@@ -34,2 +34,3 @@ import { | ||
token.type === tt.name && | ||
!token.isType && | ||
isNonTopLevelDeclaration(token) && | ||
@@ -68,3 +69,3 @@ globalNames.has(tokens.identifierNameForToken(token)) | ||
const name = tokens.identifierNameForToken(token); | ||
if (scopeStack.length > 1 && token.type === tt.name && globalNames.has(name)) { | ||
if (scopeStack.length > 1 && !token.isType && token.type === tt.name && globalNames.has(name)) { | ||
if (isBlockScopedDeclaration(token)) { | ||
@@ -71,0 +72,0 @@ markShadowedForScope(scopeStack[scopeStack.length - 1], tokens, name); |
@@ -7,2 +7,3 @@ import CJSImportProcessor from "./CJSImportProcessor"; | ||
import {validateOptions} from "./Options"; | ||
import {parse} from "./parser"; | ||
@@ -28,10 +29,7 @@ | ||
; | ||
export function getVersion() { | ||
/* istanbul ignore next */ | ||
return "3.32.0"; | ||
return "3.33.0"; | ||
} | ||
@@ -122,2 +120,3 @@ | ||
options.transforms.includes("typescript"), | ||
Boolean(options.keepUnusedImports), | ||
helperManager, | ||
@@ -129,6 +128,7 @@ ); | ||
identifyShadowedGlobals(tokenProcessor, scopes, importProcessor.getGlobalNames()); | ||
if (options.transforms.includes("typescript")) { | ||
if (options.transforms.includes("typescript") && !options.keepUnusedImports) { | ||
importProcessor.pruneTypeOnlyImports(); | ||
} | ||
} else if (options.transforms.includes("typescript")) { | ||
} else if (options.transforms.includes("typescript") && !options.keepUnusedImports) { | ||
// Shadowed global detection is needed for TS implicit elision of imported names. | ||
identifyShadowedGlobals(tokenProcessor, scopes, getTSImportedNames(tokenProcessor)); | ||
@@ -135,0 +135,0 @@ } |
@@ -28,2 +28,3 @@ /** | ||
jsxFragmentPragma: t.opt("string"), | ||
keepUnusedImports: t.opt("boolean"), | ||
preserveDynamicImport: t.opt("boolean"), | ||
@@ -30,0 +31,0 @@ injectCreateRequireForImportRequire: t.opt("boolean"), |
@@ -93,4 +93,10 @@ import {createCheckers} from "ts-interface-checker"; | ||
export function validateOptions(options) { | ||
OptionsChecker.strictCheck(options); | ||
} |
@@ -12,2 +12,3 @@ import { | ||
pushTypeContext, | ||
rescan_gt, | ||
} from "../tokenizer/index"; | ||
@@ -1157,7 +1158,22 @@ import {ContextualKeyword} from "../tokenizer/keywords"; | ||
expect(tt.lessThan); | ||
while (!eat(tt.greaterThan) && !state.error) { | ||
while (!match(tt.greaterThan) && !state.error) { | ||
tsParseType(); | ||
eat(tt.comma); | ||
} | ||
popTypeContext(oldIsType); | ||
if (!oldIsType) { | ||
// If the type arguments are present in an expression context, e.g. | ||
// f<number>(), then the > sign should be tokenized as a non-type token. | ||
// In particular, f(a < b, c >= d) should parse the >= as a single token, | ||
// resulting in a syntax error and fallback to the non-type-args | ||
// interpretation. In the success case, even though the > is tokenized as a | ||
// non-type token, it still must be marked as a type token so that it is | ||
// erased. | ||
popTypeContext(oldIsType); | ||
rescan_gt(); | ||
expect(tt.greaterThan); | ||
state.tokens[state.tokens.length - 1].isType = true; | ||
} else { | ||
expect(tt.greaterThan); | ||
popTypeContext(oldIsType); | ||
} | ||
} | ||
@@ -1164,0 +1180,0 @@ |
@@ -574,11 +574,14 @@ /* eslint max-len: 0 */ | ||
/** | ||
* Called after `as` expressions in TS; we're switching from a type to a | ||
* non-type context, so a > token may actually be >= . This is needed because >= | ||
* must be tokenized as a > in a type context because of code like | ||
* `const x: Array<T>=[];`, but `a as T >= 1` is a code example where it must be | ||
* treated as >=. | ||
* Reinterpret a possible > token when transitioning from a type to a non-type | ||
* context. | ||
* | ||
* Notably, this only applies to >, not <. In a code snippet like `a as T <= 1`, | ||
* we must NOT tokenize as <, or else the type parser will start parsing a type | ||
* argument and fail. | ||
* This comes up in two situations where >= needs to be treated as one token: | ||
* - After an `as` expression, like in the code `a as T >= 1`. | ||
* - In a type argument in an expression context, e.g. `f(a < b, c >= d)`, we | ||
* need to see the token as >= so that we get an error and backtrack to | ||
* normal expression parsing. | ||
* | ||
* Other situations require >= to be seen as two tokens, e.g. | ||
* `const x: Array<T>=[];`, so it's important to treat > as its own token in | ||
* typical type parsing situations. | ||
*/ | ||
@@ -585,0 +588,0 @@ export function rescan_gt() { |
@@ -226,2 +226,6 @@ /* eslint max-len: 0 */ | ||
return; | ||
} else if (startsAwaitUsing()) { | ||
expectContextual(ContextualKeyword._await); | ||
parseVarStatement(true); | ||
return; | ||
} | ||
@@ -259,2 +263,44 @@ default: | ||
/** | ||
* Determine if we're positioned at an `await using` declaration. | ||
* | ||
* Note that this can happen either in place of a regular variable declaration | ||
* or in a loop body, and in both places, there are similar-looking cases where | ||
* we need to return false. | ||
* | ||
* Examples returning true: | ||
* await using foo = bar(); | ||
* for (await using a of b) {} | ||
* | ||
* Examples returning false: | ||
* await using | ||
* await using + 1 | ||
* await using instanceof T | ||
* for (await using;;) {} | ||
* | ||
* For now, we early return if we don't see `await`, then do a simple | ||
* backtracking-based lookahead for the `using` and identifier tokens. In the | ||
* future, this could be optimized with a character-based approach. | ||
*/ | ||
function startsAwaitUsing() { | ||
if (!isContextual(ContextualKeyword._await)) { | ||
return false; | ||
} | ||
const snapshot = state.snapshot(); | ||
// await | ||
next(); | ||
if (!isContextual(ContextualKeyword._using) || hasPrecedingLineBreak()) { | ||
state.restoreFromSnapshot(snapshot); | ||
return false; | ||
} | ||
// using | ||
next(); | ||
if (!match(tt.name) || hasPrecedingLineBreak()) { | ||
state.restoreFromSnapshot(snapshot); | ||
return false; | ||
} | ||
state.restoreFromSnapshot(snapshot); | ||
return true; | ||
} | ||
export function parseDecorators() { | ||
@@ -366,3 +412,7 @@ while (match(tt.at)) { | ||
if (match(tt._var) || match(tt._let) || match(tt._const) || isUsingInLoop()) { | ||
const isAwaitUsing = startsAwaitUsing(); | ||
if (isAwaitUsing || match(tt._var) || match(tt._let) || match(tt._const) || isUsingInLoop()) { | ||
if (isAwaitUsing) { | ||
expectContextual(ContextualKeyword._await); | ||
} | ||
next(); | ||
@@ -1030,3 +1080,3 @@ parseVar(true, state.type !== tt._var); | ||
parseExprAtom(); | ||
maybeParseImportAssertions(); | ||
maybeParseImportAttributes(); | ||
} | ||
@@ -1199,3 +1249,3 @@ semicolon(); | ||
} | ||
maybeParseImportAssertions(); | ||
maybeParseImportAttributes(); | ||
semicolon(); | ||
@@ -1276,9 +1326,10 @@ } | ||
/** | ||
* Parse import assertions like `assert {type: "json"}`. | ||
* Parse import attributes like `with {type: "json"}`, or the legacy form | ||
* `assert {type: "json"}`. | ||
* | ||
* Import assertions technically have their own syntax, but are always parseable | ||
* Import attributes technically have their own syntax, but are always parseable | ||
* as a plain JS object, so just do that for simplicity. | ||
*/ | ||
function maybeParseImportAssertions() { | ||
if (isContextual(ContextualKeyword._assert) && !hasPrecedingLineBreak()) { | ||
function maybeParseImportAttributes() { | ||
if (match(tt._with) || (isContextual(ContextualKeyword._assert) && !hasPrecedingLineBreak())) { | ||
next(); | ||
@@ -1285,0 +1336,0 @@ parseObj(false, false); |
@@ -14,3 +14,4 @@ | ||
import getImportExportSpecifierInfo from "../util/getImportExportSpecifierInfo"; | ||
import {removeMaybeImportAssertion} from "../util/removeMaybeImportAssertion"; | ||
import isExportFrom from "../util/isExportFrom"; | ||
import {removeMaybeImportAttributes} from "../util/removeMaybeImportAttributes"; | ||
import shouldElideDefaultExport from "../util/shouldElideDefaultExport"; | ||
@@ -40,5 +41,7 @@ | ||
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.preserveDynamicImport = preserveDynamicImport;CJSImportTransformer.prototype.__init.call(this);CJSImportTransformer.prototype.__init2.call(this);CJSImportTransformer.prototype.__init3.call(this);; | ||
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 | ||
@@ -104,3 +107,3 @@ ? getDeclarationInfo(tokens) | ||
const importName = this.tokens.identifierNameAtIndex(this.tokens.currentIndex() + 1); | ||
if (this.importProcessor.isTypeName(importName)) { | ||
if (this.importProcessor.shouldAutomaticallyElideImportedName(importName)) { | ||
// If this name is only used as a type, elide the whole import. | ||
@@ -147,5 +150,4 @@ elideImportEquals(this.tokens); | ||
const wasOnlyTypes = this.removeImportAndDetectIfType(); | ||
if (wasOnlyTypes) { | ||
const shouldElideImport = this.removeImportAndDetectIfShouldElide(); | ||
if (shouldElideImport) { | ||
this.tokens.removeToken(); | ||
@@ -157,3 +159,3 @@ } else { | ||
} | ||
removeMaybeImportAssertion(this.tokens); | ||
removeMaybeImportAttributes(this.tokens); | ||
if (this.tokens.matches1(tt.semi)) { | ||
@@ -165,8 +167,19 @@ this.tokens.removeToken(); | ||
/** | ||
* Erase this import, and return true if it was either of the form "import type" or contained only | ||
* "type" named imports. Such imports should not even do a side-effect import. | ||
* 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. | ||
*/ | ||
removeImportAndDetectIfType() { | ||
removeImportAndDetectIfShouldElide() { | ||
this.tokens.removeInitialToken(); | ||
@@ -195,8 +208,15 @@ if ( | ||
let foundNonType = 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 ((!foundNonType && this.tokens.matches1(tt.braceL)) || this.tokens.matches1(tt.comma)) { | ||
if ( | ||
(!foundNonTypeImport && this.tokens.matches1(tt.braceL)) || | ||
this.tokens.matches1(tt.comma) | ||
) { | ||
this.tokens.removeToken(); | ||
if (!this.tokens.matches1(tt.braceR)) { | ||
foundAnyNamedImport = true; | ||
} | ||
if ( | ||
@@ -208,3 +228,3 @@ this.tokens.matches2(tt.name, tt.comma) || | ||
) { | ||
foundNonType = true; | ||
foundNonTypeImport = true; | ||
} | ||
@@ -214,3 +234,13 @@ } | ||
} | ||
return !foundNonType; | ||
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; | ||
} | ||
} | ||
@@ -292,2 +322,3 @@ | ||
) { | ||
this.hadNamedExport = true; | ||
// Let the TypeScript transform handle it. | ||
@@ -297,4 +328,4 @@ return false; | ||
if (this.tokens.matches2(tt._export, tt._default)) { | ||
this.hadDefaultExport = true; | ||
if (this.tokens.matches3(tt._export, tt._default, tt._enum)) { | ||
this.hadDefaultExport = true; | ||
// Flow export default enums need some special handling, so handle them | ||
@@ -306,31 +337,5 @@ // in that tranform rather than this one. | ||
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.braceL)) { | ||
this.processExportBindings(); | ||
return true; | ||
} else if (this.tokens.matches2(tt._export, tt.star)) { | ||
this.processExportStar(); | ||
return true; | ||
} else if ( | ||
@@ -369,5 +374,31 @@ this.tokens.matches2(tt._export, tt.name) && | ||
this.tokens.removeToken(); | ||
removeMaybeImportAssertion(this.tokens); | ||
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 { | ||
@@ -504,2 +535,3 @@ throw new Error("Unrecognized export syntax."); | ||
processExportDefault() { | ||
let exportedRuntimeValue = true; | ||
if ( | ||
@@ -535,3 +567,8 @@ this.tokens.matches4(tt._export, tt._default, tt._function, tt.name) || | ||
} else if ( | ||
shouldElideDefaultExport(this.isTypeScriptTransformEnabled, this.tokens, this.declarationInfo) | ||
shouldElideDefaultExport( | ||
this.isTypeScriptTransformEnabled, | ||
this.keepUnusedImports, | ||
this.tokens, | ||
this.declarationInfo, | ||
) | ||
) { | ||
@@ -541,2 +578,3 @@ // If the exported value is just an identifier and should be elided by TypeScript | ||
// where `e` is an identifier. | ||
exportedRuntimeValue = false; | ||
this.tokens.removeInitialToken(); | ||
@@ -559,2 +597,5 @@ this.tokens.removeToken(); | ||
} | ||
if (exportedRuntimeValue) { | ||
this.hadDefaultExport = true; | ||
} | ||
} | ||
@@ -810,2 +851,4 @@ | ||
const isReExport = isExportFrom(this.tokens); | ||
const exportStatements = []; | ||
@@ -819,8 +862,18 @@ while (true) { | ||
const specifierInfo = getImportExportSpecifierInfo(this.tokens); | ||
while (this.tokens.currentIndex() < specifierInfo.endIndex) { | ||
this.tokens.removeToken(); | ||
} | ||
if (!specifierInfo.isType && !this.shouldElideExportedIdentifier(specifierInfo.leftName)) { | ||
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 exportedName = specifierInfo.rightName; | ||
const newLocalName = this.importProcessor.getIdentifierReplacement(localName); | ||
@@ -851,3 +904,3 @@ exportStatements.push(`exports.${exportedName} = ${newLocalName || localName};`); | ||
this.tokens.replaceTokenTrimmingLeftWhitespace(this.importProcessor.claimImportCode(path)); | ||
removeMaybeImportAssertion(this.tokens); | ||
removeMaybeImportAttributes(this.tokens); | ||
} else { | ||
@@ -870,3 +923,3 @@ // This is a normal named export, so use that. | ||
this.tokens.replaceTokenTrimmingLeftWhitespace(this.importProcessor.claimImportCode(path)); | ||
removeMaybeImportAssertion(this.tokens); | ||
removeMaybeImportAttributes(this.tokens); | ||
if (this.tokens.matches1(tt.semi)) { | ||
@@ -878,4 +931,8 @@ this.tokens.removeToken(); | ||
shouldElideExportedIdentifier(name) { | ||
return this.isTypeScriptTransformEnabled && !this.declarationInfo.valueDeclarations.has(name); | ||
return ( | ||
this.isTypeScriptTransformEnabled && | ||
!this.keepUnusedImports && | ||
!this.declarationInfo.valueDeclarations.has(name) | ||
); | ||
} | ||
} |
@@ -14,3 +14,4 @@ | ||
import {getNonTypeIdentifiers} from "../util/getNonTypeIdentifiers"; | ||
import {removeMaybeImportAssertion} from "../util/removeMaybeImportAssertion"; | ||
import isExportFrom from "../util/isExportFrom"; | ||
import {removeMaybeImportAttributes} from "../util/removeMaybeImportAttributes"; | ||
import shouldElideDefaultExport from "../util/shouldElideDefaultExport"; | ||
@@ -35,11 +36,15 @@ | ||
isTypeScriptTransformEnabled, | ||
isFlowTransformEnabled, | ||
keepUnusedImports, | ||
options, | ||
) { | ||
super();this.tokens = tokens;this.nameManager = nameManager;this.helperManager = helperManager;this.reactHotLoaderTransformer = reactHotLoaderTransformer;this.isTypeScriptTransformEnabled = isTypeScriptTransformEnabled;; | ||
this.nonTypeIdentifiers = isTypeScriptTransformEnabled | ||
? getNonTypeIdentifiers(tokens, options) | ||
: new Set(); | ||
this.declarationInfo = isTypeScriptTransformEnabled | ||
? getDeclarationInfo(tokens) | ||
: EMPTY_DECLARATION_INFO; | ||
super();this.tokens = tokens;this.nameManager = nameManager;this.helperManager = helperManager;this.reactHotLoaderTransformer = reactHotLoaderTransformer;this.isTypeScriptTransformEnabled = isTypeScriptTransformEnabled;this.isFlowTransformEnabled = isFlowTransformEnabled;this.keepUnusedImports = keepUnusedImports;; | ||
this.nonTypeIdentifiers = | ||
isTypeScriptTransformEnabled && !keepUnusedImports | ||
? getNonTypeIdentifiers(tokens, options) | ||
: new Set(); | ||
this.declarationInfo = | ||
isTypeScriptTransformEnabled && !keepUnusedImports | ||
? getDeclarationInfo(tokens) | ||
: EMPTY_DECLARATION_INFO; | ||
this.injectCreateRequireForImportRequire = Boolean(options.injectCreateRequireForImportRequire); | ||
@@ -123,3 +128,3 @@ } | ||
this.tokens.removeToken(); | ||
removeMaybeImportAssertion(this.tokens); | ||
removeMaybeImportAttributes(this.tokens); | ||
} | ||
@@ -133,3 +138,3 @@ return true; | ||
const importName = this.tokens.identifierNameAtIndex(this.tokens.currentIndex() + 1); | ||
if (this.isTypeName(importName)) { | ||
if (this.shouldAutomaticallyElideImportedName(importName)) { | ||
// If this name is only used as a type, elide the whole import. | ||
@@ -169,3 +174,3 @@ elideImportEquals(this.tokens); | ||
this.tokens.removeToken(); | ||
removeMaybeImportAssertion(this.tokens); | ||
removeMaybeImportAttributes(this.tokens); | ||
if (this.tokens.matches1(tt.semi)) { | ||
@@ -210,6 +215,8 @@ this.tokens.removeToken(); | ||
let foundNonTypeImport = false; | ||
let foundAnyNamedImport = false; | ||
let needsComma = false; | ||
// Handle default import. | ||
if (this.tokens.matches1(tt.name)) { | ||
if (this.isTypeName(this.tokens.identifierName())) { | ||
if (this.shouldAutomaticallyElideImportedName(this.tokens.identifierName())) { | ||
this.tokens.removeToken(); | ||
@@ -238,3 +245,3 @@ if (this.tokens.matches1(tt.comma)) { | ||
if (this.tokens.matches1(tt.star)) { | ||
if (this.isTypeName(this.tokens.identifierNameAtRelativeIndex(2))) { | ||
if (this.shouldAutomaticallyElideImportedName(this.tokens.identifierNameAtRelativeIndex(2))) { | ||
this.tokens.removeToken(); | ||
@@ -258,4 +265,8 @@ this.tokens.removeToken(); | ||
while (!this.tokens.matches1(tt.braceR)) { | ||
foundAnyNamedImport = true; | ||
const specifierInfo = getImportExportSpecifierInfo(this.tokens); | ||
if (specifierInfo.isType || this.isTypeName(specifierInfo.rightName)) { | ||
if ( | ||
specifierInfo.isType || | ||
this.shouldAutomaticallyElideImportedName(specifierInfo.rightName) | ||
) { | ||
while (this.tokens.currentIndex() < specifierInfo.endIndex) { | ||
@@ -280,7 +291,21 @@ this.tokens.removeToken(); | ||
return !foundNonTypeImport; | ||
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; | ||
} | ||
} | ||
isTypeName(name) { | ||
return this.isTypeScriptTransformEnabled && !this.nonTypeIdentifiers.has(name); | ||
shouldAutomaticallyElideImportedName(name) { | ||
return ( | ||
this.isTypeScriptTransformEnabled && | ||
!this.keepUnusedImports && | ||
!this.nonTypeIdentifiers.has(name) | ||
); | ||
} | ||
@@ -290,3 +315,8 @@ | ||
if ( | ||
shouldElideDefaultExport(this.isTypeScriptTransformEnabled, this.tokens, this.declarationInfo) | ||
shouldElideDefaultExport( | ||
this.isTypeScriptTransformEnabled, | ||
this.keepUnusedImports, | ||
this.tokens, | ||
this.declarationInfo, | ||
) | ||
) { | ||
@@ -327,4 +357,10 @@ // If the exported value is just an identifier and should be elided by TypeScript | ||
/** | ||
* In TypeScript, we need to remove named exports that were never declared or only declared as a | ||
* type. | ||
* Handle a statement with one of these forms: | ||
* export {a, type b}; | ||
* export {c, type d} from 'foo'; | ||
* | ||
* In both cases, any explicit type exports should be removed. In the first | ||
* case, we also need to handle implicit export elision for names declared as | ||
* types. In the second case, we must NOT do implicit named export elision, | ||
* but we must remove the runtime import if all exports are type exports. | ||
*/ | ||
@@ -338,5 +374,10 @@ processNamedExports() { | ||
const isReExport = isExportFrom(this.tokens); | ||
let foundNonTypeExport = false; | ||
while (!this.tokens.matches1(tt.braceR)) { | ||
const specifierInfo = getImportExportSpecifierInfo(this.tokens); | ||
if (specifierInfo.isType || this.shouldElideExportedName(specifierInfo.leftName)) { | ||
if ( | ||
specifierInfo.isType || | ||
(!isReExport && this.shouldElideExportedName(specifierInfo.leftName)) | ||
) { | ||
// Type export, so remove all tokens, including any comma. | ||
@@ -351,2 +392,3 @@ while (this.tokens.currentIndex() < specifierInfo.endIndex) { | ||
// Non-type export, so copy all tokens, including any comma. | ||
foundNonTypeExport = true; | ||
while (this.tokens.currentIndex() < specifierInfo.endIndex) { | ||
@@ -361,2 +403,11 @@ this.tokens.copyToken(); | ||
this.tokens.copyExpectedToken(tt.braceR); | ||
if (!this.keepUnusedImports && isReExport && !foundNonTypeExport) { | ||
// This is a type-only re-export, so skip evaluating the other module. Technically this | ||
// leaves the statement as `export {}`, but that's ok since that's a no-op. | ||
this.tokens.removeToken(); | ||
this.tokens.removeToken(); | ||
removeMaybeImportAttributes(this.tokens); | ||
} | ||
return true; | ||
@@ -373,2 +424,3 @@ } | ||
this.isTypeScriptTransformEnabled && | ||
!this.keepUnusedImports && | ||
this.declarationInfo.typeDeclarations.has(name) && | ||
@@ -375,0 +427,0 @@ !this.declarationInfo.valueDeclarations.has(name) |
@@ -98,3 +98,5 @@ | ||
transforms.includes("typescript"), | ||
transforms.includes("flow"), | ||
Boolean(options.preserveDynamicImport), | ||
Boolean(options.keepUnusedImports), | ||
), | ||
@@ -110,2 +112,4 @@ ); | ||
transforms.includes("typescript"), | ||
transforms.includes("flow"), | ||
Boolean(options.keepUnusedImports), | ||
options, | ||
@@ -112,0 +116,0 @@ ), |
@@ -281,2 +281,7 @@ | ||
tokens.nextToken(); | ||
// Constructor type annotations are invalid, but skip them anyway since | ||
// they're easy to skip. | ||
while (tokens.currentToken().isType) { | ||
tokens.nextToken(); | ||
} | ||
let constructorInsertPos = tokens.currentIndex(); | ||
@@ -283,0 +288,0 @@ |
@@ -10,6 +10,7 @@ import {TokenType as tt} from "../parser/tokenizer/types"; | ||
isTypeScriptTransformEnabled, | ||
keepUnusedImports, | ||
tokens, | ||
declarationInfo, | ||
) { | ||
if (!isTypeScriptTransformEnabled) { | ||
if (!isTypeScriptTransformEnabled || keepUnusedImports) { | ||
return false; | ||
@@ -16,0 +17,0 @@ } |
@@ -34,2 +34,3 @@ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); | ||
token.type === _types.TokenType.name && | ||
!token.isType && | ||
_tokenizer.isNonTopLevelDeclaration.call(void 0, token) && | ||
@@ -68,3 +69,3 @@ globalNames.has(tokens.identifierNameForToken(token)) | ||
const name = tokens.identifierNameForToken(token); | ||
if (scopeStack.length > 1 && token.type === _types.TokenType.name && globalNames.has(name)) { | ||
if (scopeStack.length > 1 && !token.isType && token.type === _types.TokenType.name && globalNames.has(name)) { | ||
if (_tokenizer.isBlockScopedDeclaration.call(void 0, token)) { | ||
@@ -71,0 +72,0 @@ markShadowedForScope(scopeStack[scopeStack.length - 1], tokens, name); |
@@ -7,2 +7,3 @@ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }var _CJSImportProcessor = require('./CJSImportProcessor'); var _CJSImportProcessor2 = _interopRequireDefault(_CJSImportProcessor); | ||
var _Options = require('./Options'); | ||
var _parser = require('./parser'); | ||
@@ -28,10 +29,7 @@ | ||
; | ||
function getVersion() { | ||
/* istanbul ignore next */ | ||
return "3.32.0"; | ||
return "3.33.0"; | ||
} exports.getVersion = getVersion; | ||
@@ -122,2 +120,3 @@ | ||
options.transforms.includes("typescript"), | ||
Boolean(options.keepUnusedImports), | ||
helperManager, | ||
@@ -129,6 +128,7 @@ ); | ||
_identifyShadowedGlobals2.default.call(void 0, tokenProcessor, scopes, importProcessor.getGlobalNames()); | ||
if (options.transforms.includes("typescript")) { | ||
if (options.transforms.includes("typescript") && !options.keepUnusedImports) { | ||
importProcessor.pruneTypeOnlyImports(); | ||
} | ||
} else if (options.transforms.includes("typescript")) { | ||
} else if (options.transforms.includes("typescript") && !options.keepUnusedImports) { | ||
// Shadowed global detection is needed for TS implicit elision of imported names. | ||
_identifyShadowedGlobals2.default.call(void 0, tokenProcessor, scopes, _getTSImportedNames2.default.call(void 0, tokenProcessor)); | ||
@@ -135,0 +135,0 @@ } |
@@ -28,2 +28,3 @@ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } }/** | ||
jsxFragmentPragma: t.opt("string"), | ||
keepUnusedImports: t.opt("boolean"), | ||
preserveDynamicImport: t.opt("boolean"), | ||
@@ -30,0 +31,0 @@ injectCreateRequireForImportRequire: t.opt("boolean"), |
@@ -93,4 +93,10 @@ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }var _tsinterfacechecker = require('ts-interface-checker'); | ||
function validateOptions(options) { | ||
OptionsChecker.strictCheck(options); | ||
} exports.validateOptions = validateOptions; |
@@ -12,2 +12,3 @@ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); | ||
var _index = require('../tokenizer/index'); | ||
@@ -1157,7 +1158,22 @@ var _keywords = require('../tokenizer/keywords'); | ||
_util.expect.call(void 0, _types.TokenType.lessThan); | ||
while (!_index.eat.call(void 0, _types.TokenType.greaterThan) && !_base.state.error) { | ||
while (!_index.match.call(void 0, _types.TokenType.greaterThan) && !_base.state.error) { | ||
tsParseType(); | ||
_index.eat.call(void 0, _types.TokenType.comma); | ||
} | ||
_index.popTypeContext.call(void 0, oldIsType); | ||
if (!oldIsType) { | ||
// If the type arguments are present in an expression context, e.g. | ||
// f<number>(), then the > sign should be tokenized as a non-type token. | ||
// In particular, f(a < b, c >= d) should parse the >= as a single token, | ||
// resulting in a syntax error and fallback to the non-type-args | ||
// interpretation. In the success case, even though the > is tokenized as a | ||
// non-type token, it still must be marked as a type token so that it is | ||
// erased. | ||
_index.popTypeContext.call(void 0, oldIsType); | ||
_index.rescan_gt.call(void 0, ); | ||
_util.expect.call(void 0, _types.TokenType.greaterThan); | ||
_base.state.tokens[_base.state.tokens.length - 1].isType = true; | ||
} else { | ||
_util.expect.call(void 0, _types.TokenType.greaterThan); | ||
_index.popTypeContext.call(void 0, oldIsType); | ||
} | ||
} | ||
@@ -1164,0 +1180,0 @@ |
@@ -574,11 +574,14 @@ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }/* eslint max-len: 0 */ | ||
/** | ||
* Called after `as` expressions in TS; we're switching from a type to a | ||
* non-type context, so a > token may actually be >= . This is needed because >= | ||
* must be tokenized as a > in a type context because of code like | ||
* `const x: Array<T>=[];`, but `a as T >= 1` is a code example where it must be | ||
* treated as >=. | ||
* Reinterpret a possible > token when transitioning from a type to a non-type | ||
* context. | ||
* | ||
* Notably, this only applies to >, not <. In a code snippet like `a as T <= 1`, | ||
* we must NOT tokenize as <, or else the type parser will start parsing a type | ||
* argument and fail. | ||
* This comes up in two situations where >= needs to be treated as one token: | ||
* - After an `as` expression, like in the code `a as T >= 1`. | ||
* - In a type argument in an expression context, e.g. `f(a < b, c >= d)`, we | ||
* need to see the token as >= so that we get an error and backtrack to | ||
* normal expression parsing. | ||
* | ||
* Other situations require >= to be seen as two tokens, e.g. | ||
* `const x: Array<T>=[];`, so it's important to treat > as its own token in | ||
* typical type parsing situations. | ||
*/ | ||
@@ -585,0 +588,0 @@ function rescan_gt() { |
@@ -226,2 +226,6 @@ "use strict";Object.defineProperty(exports, "__esModule", {value: true});/* eslint max-len: 0 */ | ||
return; | ||
} else if (startsAwaitUsing()) { | ||
_util.expectContextual.call(void 0, _keywords.ContextualKeyword._await); | ||
parseVarStatement(true); | ||
return; | ||
} | ||
@@ -259,2 +263,44 @@ default: | ||
/** | ||
* Determine if we're positioned at an `await using` declaration. | ||
* | ||
* Note that this can happen either in place of a regular variable declaration | ||
* or in a loop body, and in both places, there are similar-looking cases where | ||
* we need to return false. | ||
* | ||
* Examples returning true: | ||
* await using foo = bar(); | ||
* for (await using a of b) {} | ||
* | ||
* Examples returning false: | ||
* await using | ||
* await using + 1 | ||
* await using instanceof T | ||
* for (await using;;) {} | ||
* | ||
* For now, we early return if we don't see `await`, then do a simple | ||
* backtracking-based lookahead for the `using` and identifier tokens. In the | ||
* future, this could be optimized with a character-based approach. | ||
*/ | ||
function startsAwaitUsing() { | ||
if (!_util.isContextual.call(void 0, _keywords.ContextualKeyword._await)) { | ||
return false; | ||
} | ||
const snapshot = _base.state.snapshot(); | ||
// await | ||
_tokenizer.next.call(void 0, ); | ||
if (!_util.isContextual.call(void 0, _keywords.ContextualKeyword._using) || _util.hasPrecedingLineBreak.call(void 0, )) { | ||
_base.state.restoreFromSnapshot(snapshot); | ||
return false; | ||
} | ||
// using | ||
_tokenizer.next.call(void 0, ); | ||
if (!_tokenizer.match.call(void 0, _types.TokenType.name) || _util.hasPrecedingLineBreak.call(void 0, )) { | ||
_base.state.restoreFromSnapshot(snapshot); | ||
return false; | ||
} | ||
_base.state.restoreFromSnapshot(snapshot); | ||
return true; | ||
} | ||
function parseDecorators() { | ||
@@ -366,3 +412,7 @@ while (_tokenizer.match.call(void 0, _types.TokenType.at)) { | ||
if (_tokenizer.match.call(void 0, _types.TokenType._var) || _tokenizer.match.call(void 0, _types.TokenType._let) || _tokenizer.match.call(void 0, _types.TokenType._const) || isUsingInLoop()) { | ||
const isAwaitUsing = startsAwaitUsing(); | ||
if (isAwaitUsing || _tokenizer.match.call(void 0, _types.TokenType._var) || _tokenizer.match.call(void 0, _types.TokenType._let) || _tokenizer.match.call(void 0, _types.TokenType._const) || isUsingInLoop()) { | ||
if (isAwaitUsing) { | ||
_util.expectContextual.call(void 0, _keywords.ContextualKeyword._await); | ||
} | ||
_tokenizer.next.call(void 0, ); | ||
@@ -1030,3 +1080,3 @@ parseVar(true, _base.state.type !== _types.TokenType._var); | ||
_expression.parseExprAtom.call(void 0, ); | ||
maybeParseImportAssertions(); | ||
maybeParseImportAttributes(); | ||
} | ||
@@ -1199,3 +1249,3 @@ _util.semicolon.call(void 0, ); | ||
} | ||
maybeParseImportAssertions(); | ||
maybeParseImportAttributes(); | ||
_util.semicolon.call(void 0, ); | ||
@@ -1276,9 +1326,10 @@ } exports.parseImport = parseImport; | ||
/** | ||
* Parse import assertions like `assert {type: "json"}`. | ||
* Parse import attributes like `with {type: "json"}`, or the legacy form | ||
* `assert {type: "json"}`. | ||
* | ||
* Import assertions technically have their own syntax, but are always parseable | ||
* Import attributes technically have their own syntax, but are always parseable | ||
* as a plain JS object, so just do that for simplicity. | ||
*/ | ||
function maybeParseImportAssertions() { | ||
if (_util.isContextual.call(void 0, _keywords.ContextualKeyword._assert) && !_util.hasPrecedingLineBreak.call(void 0, )) { | ||
function maybeParseImportAttributes() { | ||
if (_tokenizer.match.call(void 0, _types.TokenType._with) || (_util.isContextual.call(void 0, _keywords.ContextualKeyword._assert) && !_util.hasPrecedingLineBreak.call(void 0, ))) { | ||
_tokenizer.next.call(void 0, ); | ||
@@ -1285,0 +1336,0 @@ _expression.parseObj.call(void 0, false, false); |
@@ -14,3 +14,4 @@ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
var _getImportExportSpecifierInfo = require('../util/getImportExportSpecifierInfo'); var _getImportExportSpecifierInfo2 = _interopRequireDefault(_getImportExportSpecifierInfo); | ||
var _removeMaybeImportAssertion = require('../util/removeMaybeImportAssertion'); | ||
var _isExportFrom = require('../util/isExportFrom'); var _isExportFrom2 = _interopRequireDefault(_isExportFrom); | ||
var _removeMaybeImportAttributes = require('../util/removeMaybeImportAttributes'); | ||
var _shouldElideDefaultExport = require('../util/shouldElideDefaultExport'); var _shouldElideDefaultExport2 = _interopRequireDefault(_shouldElideDefaultExport); | ||
@@ -40,5 +41,7 @@ | ||
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.preserveDynamicImport = preserveDynamicImport;CJSImportTransformer.prototype.__init.call(this);CJSImportTransformer.prototype.__init2.call(this);CJSImportTransformer.prototype.__init3.call(this);; | ||
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 | ||
@@ -104,3 +107,3 @@ ? _getDeclarationInfo2.default.call(void 0, tokens) | ||
const importName = this.tokens.identifierNameAtIndex(this.tokens.currentIndex() + 1); | ||
if (this.importProcessor.isTypeName(importName)) { | ||
if (this.importProcessor.shouldAutomaticallyElideImportedName(importName)) { | ||
// If this name is only used as a type, elide the whole import. | ||
@@ -147,5 +150,4 @@ _elideImportEquals2.default.call(void 0, this.tokens); | ||
const wasOnlyTypes = this.removeImportAndDetectIfType(); | ||
if (wasOnlyTypes) { | ||
const shouldElideImport = this.removeImportAndDetectIfShouldElide(); | ||
if (shouldElideImport) { | ||
this.tokens.removeToken(); | ||
@@ -157,3 +159,3 @@ } else { | ||
} | ||
_removeMaybeImportAssertion.removeMaybeImportAssertion.call(void 0, this.tokens); | ||
_removeMaybeImportAttributes.removeMaybeImportAttributes.call(void 0, this.tokens); | ||
if (this.tokens.matches1(_types.TokenType.semi)) { | ||
@@ -165,8 +167,19 @@ this.tokens.removeToken(); | ||
/** | ||
* Erase this import, and return true if it was either of the form "import type" or contained only | ||
* "type" named imports. Such imports should not even do a side-effect import. | ||
* 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. | ||
*/ | ||
removeImportAndDetectIfType() { | ||
removeImportAndDetectIfShouldElide() { | ||
this.tokens.removeInitialToken(); | ||
@@ -195,8 +208,15 @@ if ( | ||
let foundNonType = false; | ||
let foundNonTypeImport = false; | ||
let foundAnyNamedImport = false; | ||
while (!this.tokens.matches1(_types.TokenType.string)) { | ||
// Check if any named imports are of the form "foo" or "foo as bar", with | ||
// no leading "type". | ||
if ((!foundNonType && this.tokens.matches1(_types.TokenType.braceL)) || this.tokens.matches1(_types.TokenType.comma)) { | ||
if ( | ||
(!foundNonTypeImport && this.tokens.matches1(_types.TokenType.braceL)) || | ||
this.tokens.matches1(_types.TokenType.comma) | ||
) { | ||
this.tokens.removeToken(); | ||
if (!this.tokens.matches1(_types.TokenType.braceR)) { | ||
foundAnyNamedImport = true; | ||
} | ||
if ( | ||
@@ -208,3 +228,3 @@ this.tokens.matches2(_types.TokenType.name, _types.TokenType.comma) || | ||
) { | ||
foundNonType = true; | ||
foundNonTypeImport = true; | ||
} | ||
@@ -214,3 +234,13 @@ } | ||
} | ||
return !foundNonType; | ||
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; | ||
} | ||
} | ||
@@ -292,2 +322,3 @@ | ||
) { | ||
this.hadNamedExport = true; | ||
// Let the TypeScript transform handle it. | ||
@@ -297,4 +328,4 @@ return false; | ||
if (this.tokens.matches2(_types.TokenType._export, _types.TokenType._default)) { | ||
this.hadDefaultExport = true; | ||
if (this.tokens.matches3(_types.TokenType._export, _types.TokenType._default, _types.TokenType._enum)) { | ||
this.hadDefaultExport = true; | ||
// Flow export default enums need some special handling, so handle them | ||
@@ -306,31 +337,5 @@ // in that tranform rather than this one. | ||
return true; | ||
} | ||
this.hadNamedExport = true; | ||
if ( | ||
this.tokens.matches2(_types.TokenType._export, _types.TokenType._var) || | ||
this.tokens.matches2(_types.TokenType._export, _types.TokenType._let) || | ||
this.tokens.matches2(_types.TokenType._export, _types.TokenType._const) | ||
) { | ||
this.processExportVar(); | ||
return true; | ||
} else if ( | ||
this.tokens.matches2(_types.TokenType._export, _types.TokenType._function) || | ||
// export async function | ||
this.tokens.matches3(_types.TokenType._export, _types.TokenType.name, _types.TokenType._function) | ||
) { | ||
this.processExportFunction(); | ||
return true; | ||
} else if ( | ||
this.tokens.matches2(_types.TokenType._export, _types.TokenType._class) || | ||
this.tokens.matches3(_types.TokenType._export, _types.TokenType._abstract, _types.TokenType._class) || | ||
this.tokens.matches2(_types.TokenType._export, _types.TokenType.at) | ||
) { | ||
this.processExportClass(); | ||
return true; | ||
} else if (this.tokens.matches2(_types.TokenType._export, _types.TokenType.braceL)) { | ||
this.processExportBindings(); | ||
return true; | ||
} else if (this.tokens.matches2(_types.TokenType._export, _types.TokenType.star)) { | ||
this.processExportStar(); | ||
return true; | ||
} else if ( | ||
@@ -369,5 +374,31 @@ this.tokens.matches2(_types.TokenType._export, _types.TokenType.name) && | ||
this.tokens.removeToken(); | ||
_removeMaybeImportAssertion.removeMaybeImportAssertion.call(void 0, this.tokens); | ||
_removeMaybeImportAttributes.removeMaybeImportAttributes.call(void 0, this.tokens); | ||
} | ||
return true; | ||
} | ||
this.hadNamedExport = true; | ||
if ( | ||
this.tokens.matches2(_types.TokenType._export, _types.TokenType._var) || | ||
this.tokens.matches2(_types.TokenType._export, _types.TokenType._let) || | ||
this.tokens.matches2(_types.TokenType._export, _types.TokenType._const) | ||
) { | ||
this.processExportVar(); | ||
return true; | ||
} else if ( | ||
this.tokens.matches2(_types.TokenType._export, _types.TokenType._function) || | ||
// export async function | ||
this.tokens.matches3(_types.TokenType._export, _types.TokenType.name, _types.TokenType._function) | ||
) { | ||
this.processExportFunction(); | ||
return true; | ||
} else if ( | ||
this.tokens.matches2(_types.TokenType._export, _types.TokenType._class) || | ||
this.tokens.matches3(_types.TokenType._export, _types.TokenType._abstract, _types.TokenType._class) || | ||
this.tokens.matches2(_types.TokenType._export, _types.TokenType.at) | ||
) { | ||
this.processExportClass(); | ||
return true; | ||
} else if (this.tokens.matches2(_types.TokenType._export, _types.TokenType.star)) { | ||
this.processExportStar(); | ||
return true; | ||
} else { | ||
@@ -504,2 +535,3 @@ throw new Error("Unrecognized export syntax."); | ||
processExportDefault() { | ||
let exportedRuntimeValue = true; | ||
if ( | ||
@@ -535,3 +567,8 @@ this.tokens.matches4(_types.TokenType._export, _types.TokenType._default, _types.TokenType._function, _types.TokenType.name) || | ||
} else if ( | ||
_shouldElideDefaultExport2.default.call(void 0, this.isTypeScriptTransformEnabled, this.tokens, this.declarationInfo) | ||
_shouldElideDefaultExport2.default.call(void 0, | ||
this.isTypeScriptTransformEnabled, | ||
this.keepUnusedImports, | ||
this.tokens, | ||
this.declarationInfo, | ||
) | ||
) { | ||
@@ -541,2 +578,3 @@ // If the exported value is just an identifier and should be elided by TypeScript | ||
// where `e` is an identifier. | ||
exportedRuntimeValue = false; | ||
this.tokens.removeInitialToken(); | ||
@@ -559,2 +597,5 @@ this.tokens.removeToken(); | ||
} | ||
if (exportedRuntimeValue) { | ||
this.hadDefaultExport = true; | ||
} | ||
} | ||
@@ -810,2 +851,4 @@ | ||
const isReExport = _isExportFrom2.default.call(void 0, this.tokens); | ||
const exportStatements = []; | ||
@@ -819,8 +862,18 @@ while (true) { | ||
const specifierInfo = _getImportExportSpecifierInfo2.default.call(void 0, this.tokens); | ||
while (this.tokens.currentIndex() < specifierInfo.endIndex) { | ||
this.tokens.removeToken(); | ||
} | ||
if (!specifierInfo.isType && !this.shouldElideExportedIdentifier(specifierInfo.leftName)) { | ||
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 exportedName = specifierInfo.rightName; | ||
const newLocalName = this.importProcessor.getIdentifierReplacement(localName); | ||
@@ -851,3 +904,3 @@ exportStatements.push(`exports.${exportedName} = ${newLocalName || localName};`); | ||
this.tokens.replaceTokenTrimmingLeftWhitespace(this.importProcessor.claimImportCode(path)); | ||
_removeMaybeImportAssertion.removeMaybeImportAssertion.call(void 0, this.tokens); | ||
_removeMaybeImportAttributes.removeMaybeImportAttributes.call(void 0, this.tokens); | ||
} else { | ||
@@ -870,3 +923,3 @@ // This is a normal named export, so use that. | ||
this.tokens.replaceTokenTrimmingLeftWhitespace(this.importProcessor.claimImportCode(path)); | ||
_removeMaybeImportAssertion.removeMaybeImportAssertion.call(void 0, this.tokens); | ||
_removeMaybeImportAttributes.removeMaybeImportAttributes.call(void 0, this.tokens); | ||
if (this.tokens.matches1(_types.TokenType.semi)) { | ||
@@ -878,4 +931,8 @@ this.tokens.removeToken(); | ||
shouldElideExportedIdentifier(name) { | ||
return this.isTypeScriptTransformEnabled && !this.declarationInfo.valueDeclarations.has(name); | ||
return ( | ||
this.isTypeScriptTransformEnabled && | ||
!this.keepUnusedImports && | ||
!this.declarationInfo.valueDeclarations.has(name) | ||
); | ||
} | ||
} exports.default = CJSImportTransformer; |
@@ -14,3 +14,4 @@ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
var _getNonTypeIdentifiers = require('../util/getNonTypeIdentifiers'); | ||
var _removeMaybeImportAssertion = require('../util/removeMaybeImportAssertion'); | ||
var _isExportFrom = require('../util/isExportFrom'); var _isExportFrom2 = _interopRequireDefault(_isExportFrom); | ||
var _removeMaybeImportAttributes = require('../util/removeMaybeImportAttributes'); | ||
var _shouldElideDefaultExport = require('../util/shouldElideDefaultExport'); var _shouldElideDefaultExport2 = _interopRequireDefault(_shouldElideDefaultExport); | ||
@@ -35,11 +36,15 @@ | ||
isTypeScriptTransformEnabled, | ||
isFlowTransformEnabled, | ||
keepUnusedImports, | ||
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; | ||
super();this.tokens = tokens;this.nameManager = nameManager;this.helperManager = helperManager;this.reactHotLoaderTransformer = reactHotLoaderTransformer;this.isTypeScriptTransformEnabled = isTypeScriptTransformEnabled;this.isFlowTransformEnabled = isFlowTransformEnabled;this.keepUnusedImports = keepUnusedImports;; | ||
this.nonTypeIdentifiers = | ||
isTypeScriptTransformEnabled && !keepUnusedImports | ||
? _getNonTypeIdentifiers.getNonTypeIdentifiers.call(void 0, tokens, options) | ||
: new Set(); | ||
this.declarationInfo = | ||
isTypeScriptTransformEnabled && !keepUnusedImports | ||
? _getDeclarationInfo2.default.call(void 0, tokens) | ||
: _getDeclarationInfo.EMPTY_DECLARATION_INFO; | ||
this.injectCreateRequireForImportRequire = Boolean(options.injectCreateRequireForImportRequire); | ||
@@ -123,3 +128,3 @@ } | ||
this.tokens.removeToken(); | ||
_removeMaybeImportAssertion.removeMaybeImportAssertion.call(void 0, this.tokens); | ||
_removeMaybeImportAttributes.removeMaybeImportAttributes.call(void 0, this.tokens); | ||
} | ||
@@ -133,3 +138,3 @@ return true; | ||
const importName = this.tokens.identifierNameAtIndex(this.tokens.currentIndex() + 1); | ||
if (this.isTypeName(importName)) { | ||
if (this.shouldAutomaticallyElideImportedName(importName)) { | ||
// If this name is only used as a type, elide the whole import. | ||
@@ -169,3 +174,3 @@ _elideImportEquals2.default.call(void 0, this.tokens); | ||
this.tokens.removeToken(); | ||
_removeMaybeImportAssertion.removeMaybeImportAssertion.call(void 0, this.tokens); | ||
_removeMaybeImportAttributes.removeMaybeImportAttributes.call(void 0, this.tokens); | ||
if (this.tokens.matches1(_types.TokenType.semi)) { | ||
@@ -210,6 +215,8 @@ this.tokens.removeToken(); | ||
let foundNonTypeImport = false; | ||
let foundAnyNamedImport = false; | ||
let needsComma = false; | ||
// Handle default import. | ||
if (this.tokens.matches1(_types.TokenType.name)) { | ||
if (this.isTypeName(this.tokens.identifierName())) { | ||
if (this.shouldAutomaticallyElideImportedName(this.tokens.identifierName())) { | ||
this.tokens.removeToken(); | ||
@@ -238,3 +245,3 @@ if (this.tokens.matches1(_types.TokenType.comma)) { | ||
if (this.tokens.matches1(_types.TokenType.star)) { | ||
if (this.isTypeName(this.tokens.identifierNameAtRelativeIndex(2))) { | ||
if (this.shouldAutomaticallyElideImportedName(this.tokens.identifierNameAtRelativeIndex(2))) { | ||
this.tokens.removeToken(); | ||
@@ -258,4 +265,8 @@ this.tokens.removeToken(); | ||
while (!this.tokens.matches1(_types.TokenType.braceR)) { | ||
foundAnyNamedImport = true; | ||
const specifierInfo = _getImportExportSpecifierInfo2.default.call(void 0, this.tokens); | ||
if (specifierInfo.isType || this.isTypeName(specifierInfo.rightName)) { | ||
if ( | ||
specifierInfo.isType || | ||
this.shouldAutomaticallyElideImportedName(specifierInfo.rightName) | ||
) { | ||
while (this.tokens.currentIndex() < specifierInfo.endIndex) { | ||
@@ -280,7 +291,21 @@ this.tokens.removeToken(); | ||
return !foundNonTypeImport; | ||
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; | ||
} | ||
} | ||
isTypeName(name) { | ||
return this.isTypeScriptTransformEnabled && !this.nonTypeIdentifiers.has(name); | ||
shouldAutomaticallyElideImportedName(name) { | ||
return ( | ||
this.isTypeScriptTransformEnabled && | ||
!this.keepUnusedImports && | ||
!this.nonTypeIdentifiers.has(name) | ||
); | ||
} | ||
@@ -290,3 +315,8 @@ | ||
if ( | ||
_shouldElideDefaultExport2.default.call(void 0, this.isTypeScriptTransformEnabled, this.tokens, this.declarationInfo) | ||
_shouldElideDefaultExport2.default.call(void 0, | ||
this.isTypeScriptTransformEnabled, | ||
this.keepUnusedImports, | ||
this.tokens, | ||
this.declarationInfo, | ||
) | ||
) { | ||
@@ -327,4 +357,10 @@ // If the exported value is just an identifier and should be elided by TypeScript | ||
/** | ||
* In TypeScript, we need to remove named exports that were never declared or only declared as a | ||
* type. | ||
* Handle a statement with one of these forms: | ||
* export {a, type b}; | ||
* export {c, type d} from 'foo'; | ||
* | ||
* In both cases, any explicit type exports should be removed. In the first | ||
* case, we also need to handle implicit export elision for names declared as | ||
* types. In the second case, we must NOT do implicit named export elision, | ||
* but we must remove the runtime import if all exports are type exports. | ||
*/ | ||
@@ -338,5 +374,10 @@ processNamedExports() { | ||
const isReExport = _isExportFrom2.default.call(void 0, this.tokens); | ||
let foundNonTypeExport = false; | ||
while (!this.tokens.matches1(_types.TokenType.braceR)) { | ||
const specifierInfo = _getImportExportSpecifierInfo2.default.call(void 0, this.tokens); | ||
if (specifierInfo.isType || this.shouldElideExportedName(specifierInfo.leftName)) { | ||
if ( | ||
specifierInfo.isType || | ||
(!isReExport && this.shouldElideExportedName(specifierInfo.leftName)) | ||
) { | ||
// Type export, so remove all tokens, including any comma. | ||
@@ -351,2 +392,3 @@ while (this.tokens.currentIndex() < specifierInfo.endIndex) { | ||
// Non-type export, so copy all tokens, including any comma. | ||
foundNonTypeExport = true; | ||
while (this.tokens.currentIndex() < specifierInfo.endIndex) { | ||
@@ -361,2 +403,11 @@ this.tokens.copyToken(); | ||
this.tokens.copyExpectedToken(_types.TokenType.braceR); | ||
if (!this.keepUnusedImports && isReExport && !foundNonTypeExport) { | ||
// This is a type-only re-export, so skip evaluating the other module. Technically this | ||
// leaves the statement as `export {}`, but that's ok since that's a no-op. | ||
this.tokens.removeToken(); | ||
this.tokens.removeToken(); | ||
_removeMaybeImportAttributes.removeMaybeImportAttributes.call(void 0, this.tokens); | ||
} | ||
return true; | ||
@@ -373,2 +424,3 @@ } | ||
this.isTypeScriptTransformEnabled && | ||
!this.keepUnusedImports && | ||
this.declarationInfo.typeDeclarations.has(name) && | ||
@@ -375,0 +427,0 @@ !this.declarationInfo.valueDeclarations.has(name) |
@@ -98,3 +98,5 @@ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
transforms.includes("typescript"), | ||
transforms.includes("flow"), | ||
Boolean(options.preserveDynamicImport), | ||
Boolean(options.keepUnusedImports), | ||
), | ||
@@ -110,2 +112,4 @@ ); | ||
transforms.includes("typescript"), | ||
transforms.includes("flow"), | ||
Boolean(options.keepUnusedImports), | ||
options, | ||
@@ -112,0 +116,0 @@ ), |
@@ -18,2 +18,3 @@ import type { HelperManager } from "./HelperManager"; | ||
readonly isTypeScriptTransformEnabled: boolean; | ||
readonly keepUnusedImports: boolean; | ||
readonly helperManager: HelperManager; | ||
@@ -25,10 +26,10 @@ private nonTypeIdentifiers; | ||
private exportBindingsByLocalName; | ||
constructor(nameManager: NameManager, tokens: TokenProcessor, enableLegacyTypeScriptModuleInterop: boolean, options: Options, isTypeScriptTransformEnabled: boolean, helperManager: HelperManager); | ||
constructor(nameManager: NameManager, tokens: TokenProcessor, enableLegacyTypeScriptModuleInterop: boolean, options: Options, isTypeScriptTransformEnabled: boolean, keepUnusedImports: boolean, helperManager: HelperManager); | ||
preprocessTokens(): void; | ||
/** | ||
* In TypeScript, import statements that only import types should be removed. This does not count | ||
* bare imports. | ||
* In TypeScript, import statements that only import types should be removed. | ||
* This includes `import {} from 'foo';`, but not `import 'foo';`. | ||
*/ | ||
pruneTypeOnlyImports(): void; | ||
isTypeName(name: string): boolean; | ||
shouldAutomaticallyElideImportedName(name: string): boolean; | ||
private generateImportReplacements; | ||
@@ -35,0 +36,0 @@ getFreeIdentifierForPath(path: string): string; |
import CJSImportProcessor from "./CJSImportProcessor"; | ||
import { RawSourceMap } from "./computeSourceMap"; | ||
import { type RawSourceMap } from "./computeSourceMap"; | ||
import { HelperManager } from "./HelperManager"; | ||
import NameManager from "./NameManager"; | ||
import type { Options, SourceMapOptions, Transform } from "./Options"; | ||
import type { Scope } from "./parser/tokenizer/state"; | ||
@@ -18,5 +19,3 @@ import TokenProcessor from "./TokenProcessor"; | ||
} | ||
export declare type Options = import("./Options").Options; | ||
export declare type SourceMapOptions = import("./Options").SourceMapOptions; | ||
export declare type Transform = import("./Options").Transform; | ||
export type { Options, SourceMapOptions, Transform }; | ||
export declare function getVersion(): string; | ||
@@ -23,0 +22,0 @@ export declare function transform(code: string, options: Options): TransformResult; |
@@ -1,2 +0,2 @@ | ||
export declare type Transform = "jsx" | "typescript" | "flow" | "imports" | "react-hot-loader" | "jest"; | ||
export type Transform = "jsx" | "typescript" | "flow" | "imports" | "react-hot-loader" | "jest"; | ||
export interface SourceMapOptions { | ||
@@ -51,2 +51,8 @@ /** | ||
/** | ||
* If specified, disable automatic removal of type-only import and export | ||
* statements and names. Only statements and names that explicitly use the | ||
* `type` keyword are removed. | ||
*/ | ||
keepUnusedImports?: boolean; | ||
/** | ||
* If specified, the imports transform does not attempt to change dynamic | ||
@@ -53,0 +59,0 @@ * import() expressions into require() calls. |
import { ContextualKeyword } from "../tokenizer/keywords"; | ||
import { StopState } from "../traverser/expression"; | ||
import { type StopState } from "../traverser/expression"; | ||
export declare function flowParseTypeParameterDeclaration(): void; | ||
@@ -4,0 +4,0 @@ export declare function flowParseTypeAnnotation(): void; |
import { ContextualKeyword } from "../tokenizer/keywords"; | ||
import { StopState } from "../traverser/expression"; | ||
import { type StopState } from "../traverser/expression"; | ||
export declare function tsParseModifiers(allowedModifiers: Array<ContextualKeyword>): void; | ||
@@ -4,0 +4,0 @@ /** Parses a modifier matching one the given modifier names. */ |
import { ContextualKeyword } from "./keywords"; | ||
import { TokenType } from "./types"; | ||
import { type TokenType } from "./types"; | ||
export declare enum IdentifierRole { | ||
@@ -78,11 +78,14 @@ Access = 0, | ||
/** | ||
* Called after `as` expressions in TS; we're switching from a type to a | ||
* non-type context, so a > token may actually be >= . This is needed because >= | ||
* must be tokenized as a > in a type context because of code like | ||
* `const x: Array<T>=[];`, but `a as T >= 1` is a code example where it must be | ||
* treated as >=. | ||
* Reinterpret a possible > token when transitioning from a type to a non-type | ||
* context. | ||
* | ||
* Notably, this only applies to >, not <. In a code snippet like `a as T <= 1`, | ||
* we must NOT tokenize as <, or else the type parser will start parsing a type | ||
* argument and fail. | ||
* This comes up in two situations where >= needs to be treated as one token: | ||
* - After an `as` expression, like in the code `a as T >= 1`. | ||
* - In a type argument in an expression context, e.g. `f(a < b, c >= d)`, we | ||
* need to see the token as >= so that we get an error and backtrack to | ||
* normal expression parsing. | ||
* | ||
* Other situations require >= to be seen as two tokens, e.g. | ||
* `const x: Array<T>=[];`, so it's important to treat > as its own token in | ||
* typical type parsing situations. | ||
*/ | ||
@@ -89,0 +92,0 @@ export declare function rescan_gt(): void; |
import type { Token } from "./index"; | ||
import { ContextualKeyword } from "./keywords"; | ||
import { TokenType } from "./types"; | ||
import { type TokenType } from "./types"; | ||
export declare class Scope { | ||
@@ -5,0 +5,0 @@ startTokenIndex: number; |
import { File } from "../index"; | ||
import { TokenType } from "../tokenizer/types"; | ||
import { type TokenType } from "../tokenizer/types"; | ||
export declare function parseTopLevel(): File; | ||
@@ -4,0 +4,0 @@ export declare function parseStatement(declaration: boolean): void; |
import type { ContextualKeyword } from "../tokenizer/keywords"; | ||
import { TokenType } from "../tokenizer/types"; | ||
import { type TokenType } from "../tokenizer/types"; | ||
export declare function isContextual(contextualKeyword: ContextualKeyword): boolean; | ||
@@ -4,0 +4,0 @@ export declare function isLookaheadContextual(contextualKeyword: ContextualKeyword): boolean; |
@@ -1,2 +0,2 @@ | ||
import { Options } from "./index"; | ||
import { type Options } from "./index"; | ||
export interface HookOptions { | ||
@@ -6,3 +6,3 @@ matcher?: (code: string) => boolean; | ||
} | ||
export declare type RevertFunction = () => void; | ||
export type RevertFunction = () => void; | ||
export declare function addHook(extension: string, options: Options, hookOptions?: HookOptions): RevertFunction; | ||
@@ -9,0 +9,0 @@ export declare function registerJS(hookOptions?: HookOptions): RevertFunction; |
import type { HelperManager } from "./HelperManager"; | ||
import type { Token } from "./parser/tokenizer"; | ||
import type { ContextualKeyword } from "./parser/tokenizer/keywords"; | ||
import { TokenType } from "./parser/tokenizer/types"; | ||
import { type TokenType } from "./parser/tokenizer/types"; | ||
export interface TokenProcessorSnapshot { | ||
@@ -6,0 +6,0 @@ resultCode: string; |
@@ -21,3 +21,5 @@ import type CJSImportProcessor from "../CJSImportProcessor"; | ||
readonly isTypeScriptTransformEnabled: boolean; | ||
readonly isFlowTransformEnabled: boolean; | ||
readonly preserveDynamicImport: boolean; | ||
readonly keepUnusedImports: boolean; | ||
private hadExport; | ||
@@ -27,3 +29,3 @@ private hadNamedExport; | ||
private declarationInfo; | ||
constructor(rootTransformer: RootTransformer, tokens: TokenProcessor, importProcessor: CJSImportProcessor, nameManager: NameManager, helperManager: HelperManager, reactHotLoaderTransformer: ReactHotLoaderTransformer | null, enableLegacyBabel5ModuleInterop: boolean, enableLegacyTypeScriptModuleInterop: boolean, isTypeScriptTransformEnabled: boolean, preserveDynamicImport: boolean); | ||
constructor(rootTransformer: RootTransformer, tokens: TokenProcessor, importProcessor: CJSImportProcessor, nameManager: NameManager, helperManager: HelperManager, reactHotLoaderTransformer: ReactHotLoaderTransformer | null, enableLegacyBabel5ModuleInterop: boolean, enableLegacyTypeScriptModuleInterop: boolean, isTypeScriptTransformEnabled: boolean, isFlowTransformEnabled: boolean, preserveDynamicImport: boolean, keepUnusedImports: boolean); | ||
getPrefixCode(): string; | ||
@@ -44,8 +46,19 @@ getSuffixCode(): string; | ||
/** | ||
* Erase this import, and return true if it was either of the form "import type" or contained only | ||
* "type" named imports. Such imports should not even do a side-effect import. | ||
* 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. | ||
*/ | ||
private removeImportAndDetectIfType; | ||
private removeImportAndDetectIfShouldElide; | ||
private removeRemainingImport; | ||
@@ -52,0 +65,0 @@ private processIdentifier; |
@@ -17,6 +17,8 @@ import type { HelperManager } from "../HelperManager"; | ||
readonly isTypeScriptTransformEnabled: boolean; | ||
readonly isFlowTransformEnabled: boolean; | ||
readonly keepUnusedImports: boolean; | ||
private nonTypeIdentifiers; | ||
private declarationInfo; | ||
private injectCreateRequireForImportRequire; | ||
constructor(tokens: TokenProcessor, nameManager: NameManager, helperManager: HelperManager, reactHotLoaderTransformer: ReactHotLoaderTransformer | null, isTypeScriptTransformEnabled: boolean, options: Options); | ||
constructor(tokens: TokenProcessor, nameManager: NameManager, helperManager: HelperManager, reactHotLoaderTransformer: ReactHotLoaderTransformer | null, isTypeScriptTransformEnabled: boolean, isFlowTransformEnabled: boolean, keepUnusedImports: boolean, options: Options); | ||
process(): boolean; | ||
@@ -32,7 +34,13 @@ private processImportEquals; | ||
private removeImportTypeBindings; | ||
private isTypeName; | ||
private shouldAutomaticallyElideImportedName; | ||
private processExportDefault; | ||
/** | ||
* In TypeScript, we need to remove named exports that were never declared or only declared as a | ||
* type. | ||
* Handle a statement with one of these forms: | ||
* export {a, type b}; | ||
* export {c, type d} from 'foo'; | ||
* | ||
* In both cases, any explicit type exports should be removed. In the first | ||
* case, we also need to handle implicit export elision for names declared as | ||
* types. In the second case, we must NOT do implicit named export elision, | ||
* but we must remove the runtime import if all exports are type exports. | ||
*/ | ||
@@ -39,0 +47,0 @@ private processNamedExports; |
@@ -6,3 +6,3 @@ import type CJSImportProcessor from "../CJSImportProcessor"; | ||
import type TokenProcessor from "../TokenProcessor"; | ||
import { JSXPragmaInfo } from "../util/getJSXPragmaInfo"; | ||
import { type JSXPragmaInfo } from "../util/getJSXPragmaInfo"; | ||
import type RootTransformer from "./RootTransformer"; | ||
@@ -9,0 +9,0 @@ import Transformer from "./Transformer"; |
import type { Options, SucraseContext, Transform } from "../index"; | ||
import { ClassInfo } from "../util/getClassInfo"; | ||
import { type ClassInfo } from "../util/getClassInfo"; | ||
export interface RootTransformerResult { | ||
@@ -4,0 +4,0 @@ code: string; |
import type TokenProcessor from "../TokenProcessor"; | ||
export declare type ImportExportSpecifierInfo = { | ||
export type ImportExportSpecifierInfo = { | ||
isType: false; | ||
@@ -4,0 +4,0 @@ leftName: string; |
@@ -6,2 +6,2 @@ import type TokenProcessor from "../TokenProcessor"; | ||
*/ | ||
export default function shouldElideDefaultExport(isTypeScriptTransformEnabled: boolean, tokens: TokenProcessor, declarationInfo: DeclarationInfo): boolean; | ||
export default function shouldElideDefaultExport(isTypeScriptTransformEnabled: boolean, keepUnusedImports: boolean, tokens: TokenProcessor, declarationInfo: DeclarationInfo): boolean; |
@@ -281,2 +281,7 @@ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); | ||
tokens.nextToken(); | ||
// Constructor type annotations are invalid, but skip them anyway since | ||
// they're easy to skip. | ||
while (tokens.currentToken().isType) { | ||
tokens.nextToken(); | ||
} | ||
let constructorInsertPos = tokens.currentIndex(); | ||
@@ -283,0 +288,0 @@ |
@@ -10,6 +10,7 @@ "use strict";Object.defineProperty(exports, "__esModule", {value: true});var _types = require('../parser/tokenizer/types'); | ||
isTypeScriptTransformEnabled, | ||
keepUnusedImports, | ||
tokens, | ||
declarationInfo, | ||
) { | ||
if (!isTypeScriptTransformEnabled) { | ||
if (!isTypeScriptTransformEnabled || keepUnusedImports) { | ||
return false; | ||
@@ -16,0 +17,0 @@ } |
{ | ||
"name": "sucrase", | ||
"version": "3.32.0", | ||
"version": "3.33.0", | ||
"description": "Super-fast alternative to Babel for when you can target modern JS runtimes", | ||
@@ -23,2 +23,3 @@ "author": "Alan Pierce <alangpierce@gmail.com>", | ||
"lint": "sucrase-node script/lint.ts", | ||
"lint-fix": "sucrase-node script/lint.ts --fix", | ||
"profile": "node --inspect-brk ./node_modules/.bin/sucrase-node ./benchmark/profile", | ||
@@ -51,3 +52,3 @@ "profile-project": "node --inspect-brk ./node_modules/.bin/sucrase-node ./benchmark/benchmark-project.ts --profile", | ||
"devDependencies": { | ||
"@babel/core": "^7.18.6", | ||
"@babel/core": "^7.22.5", | ||
"@jridgewell/trace-mapping": "^0.3.18", | ||
@@ -57,19 +58,19 @@ "@types/glob": "^7", | ||
"@types/mz": "^2.7.4", | ||
"@types/node": "^17.0.41", | ||
"@typescript-eslint/eslint-plugin": "^5.27.1", | ||
"@typescript-eslint/parser": "^5.27.1", | ||
"@types/node": "^20.3.2", | ||
"@typescript-eslint/eslint-plugin": "^5.60.1", | ||
"@typescript-eslint/parser": "^5.60.1", | ||
"chalk": "^4", | ||
"codecov": "^3.8.3", | ||
"eslint": "^8.17.0", | ||
"eslint": "^8.43.0", | ||
"eslint-config-airbnb-base": "^15.0.0", | ||
"eslint-config-prettier": "^8.5.0", | ||
"eslint-config-prettier": "^8.8.0", | ||
"eslint-plugin-import": "~2.26", | ||
"eslint-plugin-prettier": "^4.0.0", | ||
"mocha": "^10.0.0", | ||
"eslint-plugin-prettier": "^4.2.1", | ||
"mocha": "^10.2.0", | ||
"nyc": "^15.1.0", | ||
"prettier": "^2.6.2", | ||
"sucrase": "^3.31.0", | ||
"prettier": "^2.8.8", | ||
"sucrase": "^3.32.0", | ||
"test262-harness": "^10.0.0", | ||
"ts-interface-builder": "^0.3.3", | ||
"typescript": "~4.7" | ||
"typescript": "~5.0" | ||
}, | ||
@@ -89,4 +90,4 @@ "dependencies": { | ||
"resolutions": { | ||
"**/eshost/socket.io": "^4" | ||
"**/eshost/socket.io": "4.7.0" | ||
} | ||
} |
@@ -76,3 +76,5 @@ # Sucrase | ||
TypeScript flag so that the typechecker will disallow the few features like | ||
`const enum`s that need cross-file compilation. | ||
`const enum`s that need cross-file compilation. The Sucrase option `keepUnusedImports` | ||
can be used to disable all automatic removal of imports and exports, analogous to TS | ||
`verbatimModuleSyntax`. | ||
* **flow**: Removes Flow type annotations. Does not check types. | ||
@@ -79,0 +81,0 @@ * **imports**: Transforms ES Modules (`import`/`export`) to CommonJS |
@@ -24,10 +24,16 @@ const {transform} = require("../dist"); | ||
* One notable caveat is that importsNotUsedAsValues and preserveValueImports | ||
* are ignored right now, and Sucrase uses TypeScript's default behavior of | ||
* eliding imports only used as types. This usually makes no difference when | ||
* running the code, so for now we ignore these options without a warning. | ||
* are ignored right now, since they are deprecated and don't have exact Sucrase | ||
* equivalents. To preserve imports and exports, use verbatimModuleSyntax. | ||
*/ | ||
function create(createOptions) { | ||
const {nodeModuleEmitKind} = createOptions; | ||
const {module, jsx, jsxFactory, jsxFragmentFactory, jsxImportSource, esModuleInterop} = | ||
createOptions.service.config.options; | ||
const { | ||
module, | ||
jsx, | ||
jsxFactory, | ||
jsxFragmentFactory, | ||
jsxImportSource, | ||
esModuleInterop, | ||
verbatimModuleSyntax, | ||
} = createOptions.service.config.options; | ||
@@ -63,2 +69,3 @@ return { | ||
jsxFragmentPragma: jsxFragmentFactory, | ||
keepUnusedImports: verbatimModuleSyntax, | ||
preserveDynamicImport: nodeModuleEmitKind === "nodecjs", | ||
@@ -65,0 +72,0 @@ injectCreateRequireForImportRequire: nodeModuleEmitKind === "nodeesm", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1134333
190
28606
293