diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index f54a3e0666f00..4eb2b2ad6e347 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -243,6 +243,8 @@ import { ModifierFlags, ModuleBlock, ModuleDeclaration, + moduleExportNameText, + moduleExportNameTextEscaped, Mutable, NamespaceExportDeclaration, Node, @@ -432,7 +434,7 @@ function getModuleInstanceStateForAliasTarget(specifier: ExportSpecifier, visite const statements = p.statements; let found: ModuleInstanceState | undefined; for (const statement of statements) { - if (nodeHasName(statement, name)) { + if (nodeHasName(statement, moduleExportNameText(name))) { if (!statement.parent) { setParent(statement, p); setParentRecursive(statement, /*incremental*/ false); @@ -738,7 +740,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { function declareSymbol(symbolTable: SymbolTable, parent: Symbol | undefined, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags, isReplaceableByMethod?: boolean, isComputedName?: boolean): Symbol { Debug.assert(isComputedName || !hasDynamicName(node)); - const isDefaultExport = hasSyntacticModifier(node, ModifierFlags.Default) || isExportSpecifier(node) && node.name.escapedText === "default"; + const isDefaultExport = hasSyntacticModifier(node, ModifierFlags.Default) || isExportSpecifier(node) && moduleExportNameTextEscaped(node.name) === "default"; // The exported symbol for an export default function/class node is always named "default" const name = isComputedName ? InternalSymbolName.Computed diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 967ad86941b40..f1b6b117dd859 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -623,6 +623,7 @@ import { isModifier, isModuleBlock, isModuleDeclaration, + isModuleExportName, isModuleExportsAccessExpression, isModuleIdentifier, isModuleOrEnumDeclaration, @@ -826,6 +827,9 @@ import { modifierToFlag, ModuleBlock, ModuleDeclaration, + ModuleExportName, + moduleExportNameText, + moduleExportNameTextEscaped, ModuleInstanceState, ModuleKind, ModuleResolutionKind, @@ -3329,7 +3333,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (lastLocation && lastLocation === (location as ExportSpecifier).propertyName && (location as ExportSpecifier).parent.parent.moduleSpecifier) { - location = location.parent.parent.parent; + // skip to to parent ModuleBlock or SourceFile. + location = (location as ExportSpecifier).parent.parent.parent; } break; } @@ -3916,7 +3921,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { : Diagnostics._0_was_imported_here; // TODO: how to get name for export *? - const name = typeOnlyDeclaration.kind === SyntaxKind.ExportDeclaration ? "*" : unescapeLeadingUnderscores(typeOnlyDeclaration.name.escapedText); + const name = typeOnlyDeclaration.kind === SyntaxKind.ExportDeclaration ? "*" : moduleExportNameText(typeOnlyDeclaration.name); addRelatedInfo(error(node.moduleReference, message), createDiagnosticForNode(typeOnlyDeclaration, relatedMessage, name)); } } @@ -4128,12 +4133,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return result; } - function getExportOfModule(symbol: Symbol, name: Identifier, specifier: Declaration, dontResolveAlias: boolean): Symbol | undefined { + function getExportOfModule(symbol: Symbol, name: ModuleExportName, specifier: Declaration, dontResolveAlias: boolean): Symbol | undefined { if (symbol.flags & SymbolFlags.Module) { - const exportSymbol = getExportsOfSymbol(symbol).get(name.escapedText); + const escapedName = moduleExportNameTextEscaped(name); + const exportSymbol = getExportsOfSymbol(symbol).get(escapedName); const resolved = resolveSymbol(exportSymbol, dontResolveAlias); - const exportStarDeclaration = getSymbolLinks(symbol).typeOnlyExportStarMap?.get(name.escapedText); - markSymbolOfAliasDeclarationIfTypeOnly(specifier, exportSymbol, resolved, /*overwriteEmpty*/ false, exportStarDeclaration, name.escapedText); + const exportStarDeclaration = getSymbolLinks(symbol).typeOnlyExportStarMap?.get(escapedName); + markSymbolOfAliasDeclarationIfTypeOnly(specifier, exportSymbol, resolved, /*overwriteEmpty*/ false, exportStarDeclaration, escapedName); return resolved; } } @@ -4151,13 +4157,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const moduleSpecifier = getExternalModuleRequireArgument(node) || (node as ImportDeclaration | ExportDeclaration).moduleSpecifier!; const moduleSymbol = resolveExternalModuleName(node, moduleSpecifier)!; // TODO: GH#18217 const name = !isPropertyAccessExpression(specifier) && specifier.propertyName || specifier.name; - if (!isIdentifier(name)) { + if (!isModuleExportName(name)) { return undefined; } - const suppressInteropError = name.escapedText === InternalSymbolName.Default && allowSyntheticDefaultImports; + const escapedName = moduleExportNameTextEscaped(name); + const suppressInteropError = escapedName === InternalSymbolName.Default && allowSyntheticDefaultImports; const targetSymbol = resolveESModuleSymbol(moduleSymbol, moduleSpecifier, /*dontResolveAlias*/ false, suppressInteropError); if (targetSymbol) { - if (name.escapedText) { + if (escapedName) { if (isShorthandAmbientModuleSymbol(moduleSymbol)) { return moduleSymbol; } @@ -4165,16 +4172,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { let symbolFromVariable: Symbol | undefined; // First check if module was specified with "export=". If so, get the member from the resolved type if (moduleSymbol && moduleSymbol.exports && moduleSymbol.exports.get(InternalSymbolName.ExportEquals)) { - symbolFromVariable = getPropertyOfType(getTypeOfSymbol(targetSymbol), name.escapedText, /*skipObjectFunctionPropertyAugment*/ true); + symbolFromVariable = getPropertyOfType(getTypeOfSymbol(targetSymbol), escapedName, /*skipObjectFunctionPropertyAugment*/ true); } else { - symbolFromVariable = getPropertyOfVariable(targetSymbol, name.escapedText); + symbolFromVariable = getPropertyOfVariable(targetSymbol, escapedName); } // if symbolFromVariable is export - get its final target symbolFromVariable = resolveSymbol(symbolFromVariable, dontResolveAlias); let symbolFromModule = getExportOfModule(targetSymbol, name, specifier, dontResolveAlias); - if (symbolFromModule === undefined && name.escapedText === InternalSymbolName.Default) { + if (symbolFromModule === undefined && escapedName === InternalSymbolName.Default) { const file = moduleSymbol.declarations?.find(isSourceFile); if (isOnlyImportedAsDefault(moduleSpecifier) || canHaveSyntheticDefault(file, moduleSymbol, dontResolveAlias, moduleSpecifier)) { symbolFromModule = resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) || resolveSymbol(moduleSymbol, dontResolveAlias); @@ -4192,7 +4199,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } - function errorNoModuleMemberSymbol(moduleSymbol: Symbol, targetSymbol: Symbol, node: Node, name: Identifier) { + function errorNoModuleMemberSymbol(moduleSymbol: Symbol, targetSymbol: Symbol, node: Node, name: ModuleExportName) { const moduleName = getFullyQualifiedName(moduleSymbol, node); const declarationName = declarationNameToString(name); const suggestion = getSuggestedSymbolForNonexistentModule(name, targetSymbol); @@ -4220,8 +4227,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } - function reportNonExportedMember(node: Node, name: Identifier, declarationName: string, moduleSymbol: Symbol, moduleName: string): void { - const localSymbol = tryCast(moduleSymbol.valueDeclaration, canHaveLocals)?.locals?.get(name.escapedText); + function reportNonExportedMember(node: Node, name: ModuleExportName, declarationName: string, moduleSymbol: Symbol, moduleName: string): void { + // for import { "ident" as T } we can provide meaningful error message. (ident is defined locally) + // for import { "not ident" as T } we will still look for the locals of the target module symbol, but + // since it's impossible to define a local ident with invalid name, we will use the normal message "Module_0_has_no_exported_member_1" + const localSymbol = tryCast(moduleSymbol.valueDeclaration, canHaveLocals)?.locals?.get(moduleExportNameTextEscaped(name)); const exports = moduleSymbol.exports; if (localSymbol) { const exportedEqualsSymbol = exports?.get(InternalSymbolName.ExportEquals); @@ -4245,7 +4255,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } - function reportInvalidImportEqualsExportMember(node: Node, name: Identifier, declarationName: string, moduleName: string) { + function reportInvalidImportEqualsExportMember(node: Node, name: ModuleExportName, declarationName: string, moduleName: string) { if (moduleKind >= ModuleKind.ES2015) { const message = getESModuleInterop(compilerOptions) ? Diagnostics._0_can_only_be_imported_by_using_a_default_import : Diagnostics._0_can_only_be_imported_by_turning_on_the_esModuleInterop_flag_and_using_a_default_import; @@ -4266,7 +4276,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function getTargetOfImportSpecifier(node: ImportSpecifier | BindingElement, dontResolveAlias: boolean): Symbol | undefined { - if (isImportSpecifier(node) && idText(node.propertyName || node.name) === InternalSymbolName.Default) { + if (isImportSpecifier(node) && moduleExportNameText(node.propertyName || node.name) === InternalSymbolName.Default) { const specifier = getModuleSpecifierForImportOrExport(node); const moduleSymbol = specifier && resolveExternalModuleName(node, specifier); if (moduleSymbol) { @@ -4299,16 +4309,22 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function getTargetOfExportSpecifier(node: ExportSpecifier, meaning: SymbolFlags, dontResolveAlias?: boolean) { - if (idText(node.propertyName || node.name) === InternalSymbolName.Default) { + if (moduleExportNameText(node.propertyName || node.name) === InternalSymbolName.Default) { const specifier = getModuleSpecifierForImportOrExport(node); const moduleSymbol = specifier && resolveExternalModuleName(node, specifier); if (moduleSymbol) { return getTargetofModuleDefault(moduleSymbol, node, !!dontResolveAlias); } } - const resolved = node.parent.parent.moduleSpecifier ? - getExternalModuleMember(node.parent.parent, node, dontResolveAlias) : - resolveEntityName(node.propertyName || node.name, meaning, /*ignoreErrors*/ false, dontResolveAlias); + const exported = node.propertyName || node.name; + let resolved: Symbol | undefined; + if (node.parent.parent.moduleSpecifier) { + resolved = getExternalModuleMember(node.parent.parent, node, dontResolveAlias); + } + // skip resolution for grammar error export { "x" } + else if (exported.kind !== SyntaxKind.StringLiteral) { + resolved = resolveEntityName(exported, meaning, /*ignoreErrors*/ false, dontResolveAlias); + } markSymbolOfAliasDeclarationIfTypeOnly(node, /*immediateTarget*/ undefined, resolved, /*overwriteEmpty*/ false); return resolved; } @@ -8508,7 +8524,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (!e.propertyName) { // export {name} - look thru `statements` for `name`, and if all results can take an `export` modifier, do so and filter it const indices = indicesOf(statements); - const associatedIndices = filter(indices, i => nodeHasName(statements[i], e.name)); + const associatedIndices = filter(indices, i => { + if (e.name.kind === SyntaxKind.StringLiteral) return false; + return nodeHasName(statements[i], e.name); + }); if (length(associatedIndices) && every(associatedIndices, i => canHaveExportModifier(statements[i]))) { for (const index of associatedIndices) { statements[index] = addExportModifier(statements[index] as Extract); @@ -8688,7 +8707,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { factory.createExportDeclaration( /*modifiers*/ undefined, /*isTypeOnly*/ false, - factory.createNamedExports([factory.createExportSpecifier(/*isTypeOnly*/ false, alias, localName)]) + factory.createNamedExports([factory.createExportSpecifier(/*isTypeOnly*/ false, alias, factory.createModuleExportName(localName, languageVersion))]) ), ModifierFlags.None ); @@ -8725,7 +8744,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { factory.createExportDeclaration( /*modifiers*/ undefined, /*isTypeOnly*/ false, - factory.createNamedExports([factory.createExportSpecifier(/*isTypeOnly*/ false, name, localName)]) + factory.createNamedExports([factory.createExportSpecifier(/*isTypeOnly*/ false, factory.createModuleExportName(name, languageVersion), factory.createModuleExportName(localName, languageVersion))]) ), ModifierFlags.None ); @@ -8784,7 +8803,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { addResult(factory.createExportDeclaration( /*modifiers*/ undefined, /*isTypeOnly*/ false, - factory.createNamedExports([factory.createExportSpecifier(/*isTypeOnly*/ false, getInternalSymbolName(symbol, symbolName), symbolName)]) + factory.createNamedExports([factory.createExportSpecifier(/*isTypeOnly*/ false, factory.createModuleExportName(getInternalSymbolName(symbol, symbolName), languageVersion), factory.createModuleExportName(symbolName, languageVersion))]) ), ModifierFlags.None); } } @@ -8922,7 +8941,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const target = aliasDecl && getTargetOfAliasDeclaration(aliasDecl, /*dontRecursivelyResolve*/ true); includePrivateSymbol(target || s); const targetName = target ? getInternalSymbolName(target, unescapeLeadingUnderscores(target.escapedName)) : localName; - return factory.createExportSpecifier(/*isTypeOnly*/ false, name === targetName ? undefined : targetName, name); + return factory.createExportSpecifier(/*isTypeOnly*/ false, factory.createModuleExportName(name === targetName ? undefined : targetName, languageVersion), factory.createModuleExportName(name, languageVersion)); })) )]); addResult(factory.createModuleDeclaration( @@ -9150,7 +9169,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getSomeTargetNameFromDeclarations(declarations: Declaration[] | undefined) { return firstDefined(declarations, d => { if (isImportSpecifier(d) || isExportSpecifier(d)) { - return idText(d.propertyName || d.name); + return moduleExportNameText(d.propertyName || d.name); } if (isBinaryExpression(d) || isExportAssignment(d)) { const expression = isExportAssignment(d) ? d.expression : d.right; @@ -9354,7 +9373,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { addResult(factory.createExportDeclaration( /*modifiers*/ undefined, /*isTypeOnly*/ false, - factory.createNamedExports([factory.createExportSpecifier(/*isTypeOnly*/ false, localName !== targetName ? targetName : undefined, localName)]), + factory.createNamedExports([factory.createExportSpecifier(/*isTypeOnly*/ false, factory.createModuleExportName(localName !== targetName ? targetName : undefined, languageVersion), factory.createModuleExportName(localName, languageVersion))]), specifier ), ModifierFlags.None); } @@ -10011,9 +10030,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } - function collectLinkedAliases(node: Identifier, setVisibility?: boolean): Node[] | undefined { + function collectLinkedAliases(node: ModuleExportName, setVisibility?: boolean): Node[] | undefined { let exportSymbol: Symbol | undefined; - if (node.parent && node.parent.kind === SyntaxKind.ExportAssignment) { + if (node.parent?.kind === SyntaxKind.ExportAssignment && node.kind !== SyntaxKind.StringLiteral) { exportSymbol = resolveName(node, node.escapedText, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias, /*nameNotFoundMessage*/ undefined, node, /*isUse*/ false); } else if (node.parent.kind === SyntaxKind.ExportSpecifier) { @@ -31919,8 +31938,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return symbolResult && symbolName(symbolResult); } - function getSuggestedSymbolForNonexistentModule(name: Identifier, targetModule: Symbol): Symbol | undefined { - return targetModule.exports && getSpellingSuggestionForName(idText(name), getExportsOfModuleAsArray(targetModule), SymbolFlags.ModuleMember); + function getSuggestedSymbolForNonexistentModule(name: ModuleExportName, targetModule: Symbol): Symbol | undefined { + return targetModule.exports && getSpellingSuggestionForName(moduleExportNameText(name), getExportsOfModuleAsArray(targetModule), SymbolFlags.ModuleMember); } function getSuggestionForNonexistentExport(name: Identifier, targetModule: Symbol): string | undefined { @@ -44143,7 +44162,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { Debug.assert(node.kind !== SyntaxKind.NamespaceExport); if (node.kind === SyntaxKind.ExportSpecifier) { const diag = error(errorNode, Diagnostics.Types_cannot_appear_in_export_declarations_in_JavaScript_files); - const alreadyExportedSymbol = getSourceFileOfNode(node).symbol?.exports?.get((node.propertyName || node.name).escapedText); + const exported = node.propertyName || node.name; + const alreadyExportedSymbol = getSourceFileOfNode(node).symbol?.exports?.get(moduleExportNameTextEscaped(exported)); if (alreadyExportedSymbol === target) { const exportingDeclaration = alreadyExportedSymbol.declarations?.find(isJSDocNode); if (exportingDeclaration) { @@ -44201,7 +44221,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { : compilerOptions.verbatimModuleSyntax ? Diagnostics._0_resolves_to_a_type_only_declaration_and_must_be_imported_using_a_type_only_import_when_verbatimModuleSyntax_is_enabled : Diagnostics._0_resolves_to_a_type_only_declaration_and_must_be_imported_using_a_type_only_import_when_preserveValueImports_and_isolatedModules_are_both_enabled; - const name = idText(node.kind === SyntaxKind.ImportSpecifier ? node.propertyName || node.name : node.name); + const name = moduleExportNameText(node.kind === SyntaxKind.ImportSpecifier ? node.propertyName || node.name : node.name); addTypeOnlyDeclarationRelatedInfo( error(node, message, name), isType ? undefined : typeOnlyAlias, @@ -44218,7 +44238,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // The exception is that `import type { A } from './a'; export { A }` is allowed // because single-file analysis can determine that the export should be dropped. if (compilerOptions.verbatimModuleSyntax || getSourceFileOfNode(typeOnlyAlias) !== getSourceFileOfNode(node)) { - const name = idText(node.propertyName || node.name); + + const exported = node.propertyName || node.name; + const name = moduleExportNameText(exported); const diagnostic = isType ? error(node, Diagnostics.Re_exporting_a_type_when_0_is_enabled_requires_using_export_type, isolatedModulesLikeFlagName) : error(node, Diagnostics._0_resolves_to_a_type_only_declaration_and_must_be_re_exported_using_a_type_only_re_export_when_1_is_enabled, name, isolatedModulesLikeFlagName); @@ -44281,7 +44303,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { checkCollisionsForDeclarationName(node, node.name); checkAliasSymbol(node); if (node.kind === SyntaxKind.ImportSpecifier && - idText(node.propertyName || node.name) === "default" && + moduleExportNameText(node.propertyName || node.name) === "default" && getESModuleInterop(compilerOptions) && moduleKind !== ModuleKind.System && (moduleKind < ModuleKind.ES2015 || getSourceFileOfNode(node).impliedNodeFormat === ModuleKind.CommonJS)) { checkExternalEmitHelpers(node, ExternalEmitHelpers.ImportDefault); @@ -44452,8 +44474,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function checkGrammarExportDeclaration(node: ExportDeclaration): boolean { - if (node.isTypeOnly && node.exportClause?.kind === SyntaxKind.NamedExports) { - return checkGrammarNamedImportsOrExports(node.exportClause); + if (node.exportClause?.kind === SyntaxKind.NamedExports) { + return checkGrammarNamedImportsOrExports(node.exportClause, node.isTypeOnly, !node.moduleSpecifier); + } + if ( + (moduleKind === ModuleKind.ES2015 || moduleKind === ModuleKind.ES2020) && + node.exportClause?.kind === SyntaxKind.NamespaceExport && + node.exportClause.name.kind === SyntaxKind.StringLiteral && + !isIdentifierText(node.exportClause.name.text, languageVersion) + ) { + grammarErrorOnNode(node.exportClause.name, Diagnostics.String_literal_module_export_names_are_not_allowed_when_the_module_option_is_set_to_es2020_or_lower); + return true; } return false; } @@ -44516,6 +44547,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } if (!node.parent.parent.moduleSpecifier) { const exportedName = node.propertyName || node.name; + if (exportedName.kind === SyntaxKind.StringLiteral) { + // export { "x" } or export { "x" as name } is a syntax error. reported in grammar check. + return; + } // find immediate value referenced by exported name (SymbolFlags.Alias is set so we don't chase down aliases) const symbol = resolveName(exportedName, exportedName.escapedText, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ true); @@ -44536,7 +44571,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (getESModuleInterop(compilerOptions) && moduleKind !== ModuleKind.System && (moduleKind < ModuleKind.ES2015 || getSourceFileOfNode(node).impliedNodeFormat === ModuleKind.CommonJS) && - idText(node.propertyName || node.name) === "default") { + moduleExportNameText(node.propertyName || node.name) === "default") { checkExternalEmitHelpers(node, ExternalEmitHelpers.ImportDefault); } } @@ -45835,9 +45870,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { /** Returns the target of an export specifier without following aliases */ function getExportSpecifierLocalTargetSymbol(node: ExportSpecifier | Identifier): Symbol | undefined { if (isExportSpecifier(node)) { - return node.parent.parent.moduleSpecifier ? - getExternalModuleMember(node.parent.parent, node) : - resolveEntityName(node.propertyName || node.name, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias); + if (node.parent.parent.moduleSpecifier) { + return getExternalModuleMember(node.parent.parent, node); + } + const exported = node.propertyName || node.name; + // skip resolution for grammar error export { "" as X }. + if (exported.kind === SyntaxKind.StringLiteral) return undefined; + return resolveEntityName(exported, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias); } else { return resolveEntityName(node, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias); @@ -48818,21 +48857,36 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (node.isTypeOnly && node.name && node.namedBindings) { return grammarErrorOnNode(node, Diagnostics.A_type_only_import_can_specify_a_default_import_or_named_bindings_but_not_both); } - if (node.isTypeOnly && node.namedBindings?.kind === SyntaxKind.NamedImports) { - return checkGrammarNamedImportsOrExports(node.namedBindings); + if (node.namedBindings?.kind === SyntaxKind.NamedImports) { + return checkGrammarNamedImportsOrExports(node.namedBindings, node.isTypeOnly, /*isExportLocal*/ false); } return false; } - function checkGrammarNamedImportsOrExports(namedBindings: NamedImportsOrExports): boolean { + function checkGrammarNamedImportsOrExports(namedBindings: NamedImportsOrExports, isTypeOnly: boolean, isExportLocal: boolean): boolean { + const checkString = moduleKind === ModuleKind.ES2015 || moduleKind === ModuleKind.ES2020; + if (!isTypeOnly && !isExportLocal && !checkString) return false; return !!forEach(namedBindings.elements, specifier => { - if (specifier.isTypeOnly) { + if (isTypeOnly && specifier.isTypeOnly) { return grammarErrorOnFirstToken( specifier, specifier.kind === SyntaxKind.ImportSpecifier ? Diagnostics.The_type_modifier_cannot_be_used_on_a_named_import_when_import_type_is_used_on_its_import_statement : Diagnostics.The_type_modifier_cannot_be_used_on_a_named_export_when_export_type_is_used_on_its_export_statement); } + const exported = specifier.propertyName || specifier.name; + if (isExportLocal && exported.kind === SyntaxKind.StringLiteral) { + return grammarErrorOnNode(exported, Diagnostics.String_literal_module_export_names_must_be_followed_by_a_from_clause); + } + + if (checkString) { + if (specifier.name.kind === SyntaxKind.StringLiteral && !isIdentifierText(specifier.name.text, languageVersion)) { + return grammarErrorOnNode(specifier.name, Diagnostics.String_literal_module_export_names_are_not_allowed_when_the_module_option_is_set_to_es2020_or_lower); + } + if (specifier.propertyName?.kind === SyntaxKind.StringLiteral && !isIdentifierText(specifier.propertyName.text, languageVersion)) { + return grammarErrorOnNode(specifier.propertyName, Diagnostics.String_literal_module_export_names_are_not_allowed_when_the_module_option_is_set_to_es2020_or_lower); + } + } }); } @@ -48986,7 +49040,7 @@ function isDeclarationNameOrImportPropertyName(name: Node): boolean { switch (name.parent.kind) { case SyntaxKind.ImportSpecifier: case SyntaxKind.ExportSpecifier: - return isIdentifier(name); + return isModuleExportName(name); default: return isDeclarationName(name); } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 65ccc4cb18fdc..be9b967e92810 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1601,6 +1601,10 @@ "category": "Error", "code": 1490 }, + "String literal module export names are not allowed when the 'module' option is set to 'es2020' or lower.": { + "category": "Error", + "code": 1491 + }, "The types of '{0}' are incompatible between these types.": { "category": "Error", @@ -1659,6 +1663,10 @@ "category": "Message", "code": 2212 }, + "String literal module export names must be followed by a 'from' clause.": { + "category": "Error", + "code": 2213 + }, "Duplicate identifier '{0}'.": { "category": "Error", diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 638ff154747fa..dfa1e16505bb1 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -175,6 +175,7 @@ import { isHoistedFunction, isHoistedVariableStatement, isIdentifier, + isIdentifierText, isImportDeclaration, isImportEqualsDeclaration, isImportKeyword, @@ -313,6 +314,7 @@ import { ModuleBlock, ModuleBody, ModuleDeclaration, + ModuleExportName, ModuleKind, ModuleName, ModuleReference, @@ -983,6 +985,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode get createLogicalNot() { return getPrefixUnaryCreateFunction(SyntaxKind.ExclamationToken); }, get createPostfixIncrement() { return getPostfixUnaryCreateFunction(SyntaxKind.PlusPlusToken); }, get createPostfixDecrement() { return getPostfixUnaryCreateFunction(SyntaxKind.MinusMinusToken); }, + createModuleExportName, // Compound nodes createImmediatelyInvokedFunctionExpression, @@ -4713,7 +4716,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode } // @api - function createNamespaceExport(name: Identifier): NamespaceExport { + function createNamespaceExport(name: ModuleExportName): NamespaceExport { const node = createBaseDeclaration(SyntaxKind.NamespaceExport); node.name = name; node.transformFlags |= @@ -4724,7 +4727,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode } // @api - function updateNamespaceExport(node: NamespaceExport, name: Identifier) { + function updateNamespaceExport(node: NamespaceExport, name: ModuleExportName) { return node.name !== name ? update(createNamespaceExport(name), node) : node; @@ -4747,7 +4750,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode } // @api - function createImportSpecifier(isTypeOnly: boolean, propertyName: Identifier | undefined, name: Identifier) { + function createImportSpecifier(isTypeOnly: boolean, propertyName: ModuleExportName | undefined, name: Identifier) { const node = createBaseDeclaration(SyntaxKind.ImportSpecifier); node.isTypeOnly = isTypeOnly; node.propertyName = propertyName; @@ -4760,7 +4763,15 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode } // @api - function updateImportSpecifier(node: ImportSpecifier, isTypeOnly: boolean, propertyName: Identifier | undefined, name: Identifier) { + function createModuleExportName(name: string, languageVersion: ScriptTarget): ModuleExportName; + function createModuleExportName(name: string | undefined, languageVersion: ScriptTarget): ModuleExportName | undefined; + function createModuleExportName(name: string | undefined, languageVersion: ScriptTarget): ModuleExportName | undefined { + if (name === undefined) return undefined; + return isIdentifierText(name, languageVersion) ? createIdentifier(name) : createStringLiteral(name); + } + + // @api + function updateImportSpecifier(node: ImportSpecifier, isTypeOnly: boolean, propertyName: ModuleExportName | undefined, name: Identifier) { return node.isTypeOnly !== isTypeOnly || node.propertyName !== propertyName || node.name !== name @@ -4868,11 +4879,11 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode } // @api - function createExportSpecifier(isTypeOnly: boolean, propertyName: string | Identifier | undefined, name: string | Identifier) { + function createExportSpecifier(isTypeOnly: boolean, propertyName: ModuleExportName | undefined, name: ModuleExportName) { const node = createBaseNode(SyntaxKind.ExportSpecifier); node.isTypeOnly = isTypeOnly; - node.propertyName = asName(propertyName); - node.name = asName(name); + node.propertyName = propertyName; + node.name = name; node.transformFlags |= propagateChildFlags(node.propertyName) | propagateChildFlags(node.name); @@ -4883,7 +4894,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode } // @api - function updateExportSpecifier(node: ExportSpecifier, isTypeOnly: boolean, propertyName: Identifier | undefined, name: Identifier) { + function updateExportSpecifier(node: ExportSpecifier, isTypeOnly: boolean, propertyName: ModuleExportName | undefined, name: ModuleExportName) { return node.isTypeOnly !== isTypeOnly || node.propertyName !== propertyName || node.name !== name diff --git a/src/compiler/factory/nodeTests.ts b/src/compiler/factory/nodeTests.ts index ffc8c4a44839d..f8d000f32a8e1 100644 --- a/src/compiler/factory/nodeTests.ts +++ b/src/compiler/factory/nodeTests.ts @@ -143,6 +143,7 @@ import { MissingDeclaration, ModuleBlock, ModuleDeclaration, + ModuleExportName, NamedExports, NamedImports, NamedTupleMember, @@ -321,6 +322,10 @@ export function isPrivateIdentifier(node: Node): node is PrivateIdentifier { return node.kind === SyntaxKind.PrivateIdentifier; } +export function isModuleExportName(node: Node): node is ModuleExportName { + return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.StringLiteral; +} + // Reserved Words /** @internal */ diff --git a/src/compiler/factory/utilities.ts b/src/compiler/factory/utilities.ts index 2e2721eadcaaf..7cc936efde63c 100644 --- a/src/compiler/factory/utilities.ts +++ b/src/compiler/factory/utilities.ts @@ -792,7 +792,9 @@ export function getOrCreateExternalHelpersModuleNameIfNeeded(factory: NodeFactor } /** - * Get the name of that target module from an import or export declaration + * Get the name of that target module from an import or export declaration. + * + * This is only used in AMD and SystemJS emit. * * @internal */ @@ -800,7 +802,9 @@ export function getLocalNameForExternalImport(factory: NodeFactory, node: Import const namespaceDeclaration = getNamespaceDeclarationNode(node); if (namespaceDeclaration && !isDefaultImport(node) && !isExportNamespaceAsDefaultDeclaration(node)) { const name = namespaceDeclaration.name; - return isGeneratedIdentifier(name) ? name : factory.createIdentifier(getSourceTextOfNodeFromSourceFile(sourceFile, name) || idText(name)); + if (isGeneratedIdentifier(name)) return name; + if (name.kind === SyntaxKind.StringLiteral) return factory.getGeneratedNameForNode(name); + return factory.createIdentifier(getSourceTextOfNodeFromSourceFile(sourceFile, name) || idText(name)); } if (node.kind === SyntaxKind.ImportDeclaration && node.importClause) { return factory.getGeneratedNameForNode(node); diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index f49b48bb44295..85421da60fe55 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -120,7 +120,6 @@ import { ImportClause, ImportDeclaration, ImportEqualsDeclaration, - ImportOrExportSpecifier, ImportSpecifier, ImportTypeAssertionContainer, ImportTypeNode, @@ -253,6 +252,7 @@ import { modifiersToFlags, ModuleBlock, ModuleDeclaration, + ModuleExportName, ModuleKind, Mutable, NamedExportBindings, @@ -2869,7 +2869,13 @@ namespace Parser { case ParsingContext.HeritageClauses: return isHeritageClause(); case ParsingContext.ImportOrExportSpecifiers: - return tokenIsIdentifierOrKeyword(token()); + // bail out if the next token is Identifier(from) StringLiteral. + // That means we're in something like `import { from "mod"` + // In this case we don't want parse it as import { from "mod", ... + if (token() === SyntaxKind.FromKeyword && lookAhead(nextTokenIsStringLiteral)) { + return false; + } + return token() === SyntaxKind.StringLiteral || tokenIsIdentifierOrKeyword(token()); case ParsingContext.JsxAttributes: return tokenIsIdentifierOrKeyword(token()) || token() === SyntaxKind.OpenBraceToken; case ParsingContext.JsxChildren: @@ -3390,7 +3396,11 @@ namespace Parser { case ParsingContext.TypeArguments: return parseErrorAtCurrentToken(Diagnostics.Type_argument_expected); case ParsingContext.TupleElementTypes: return parseErrorAtCurrentToken(Diagnostics.Type_expected); case ParsingContext.HeritageClauses: return parseErrorAtCurrentToken(Diagnostics.Unexpected_token_expected); - case ParsingContext.ImportOrExportSpecifiers: return parseErrorAtCurrentToken(Diagnostics.Identifier_expected); + case ParsingContext.ImportOrExportSpecifiers: + if (token() === SyntaxKind.FromKeyword) { + return parseErrorAtCurrentToken(Diagnostics._0_expected, "}"); + } + return parseErrorAtCurrentToken(Diagnostics.Identifier_expected); case ParsingContext.JsxAttributes: return parseErrorAtCurrentToken(Diagnostics.Identifier_expected); case ParsingContext.JsxChildren: return parseErrorAtCurrentToken(Diagnostics.Identifier_expected); case ParsingContext.AssertEntries: return parseErrorAtCurrentToken(Diagnostics.Identifier_or_string_literal_expected); // AssertionKey. @@ -7358,6 +7368,9 @@ namespace Parser { } } + function nextTokenIsStringLiteral() { + return nextToken() === SyntaxKind.StringLiteral; + } function nextTokenIsIdentifierOrStringLiteralOnSameLine() { nextToken(); return !scanner.hasPrecedingLineBreak() && (isIdentifier() || token() === SyntaxKind.StringLiteral); @@ -8345,22 +8358,26 @@ namespace Parser { return parseImportOrExportSpecifier(SyntaxKind.ImportSpecifier) as ImportSpecifier; } - function parseImportOrExportSpecifier(kind: SyntaxKind): ImportOrExportSpecifier { + function parseImportOrExportSpecifier(kind: SyntaxKind) { const pos = getNodePos(); + // ModuleExportName: + // Identifier + // StringLiteral // ImportSpecifier: // BindingIdentifier - // IdentifierName as BindingIdentifier + // ModuleExportName as BindingIdentifier // ExportSpecifier: - // IdentifierName - // IdentifierName as IdentifierName + // ModuleExportName + // ModuleExportName as ModuleExportName let checkIdentifierIsKeyword = isKeyword(token()) && !isIdentifier(); let checkIdentifierStart = scanner.getTokenStart(); let checkIdentifierEnd = scanner.getTokenEnd(); let isTypeOnly = false; - let propertyName: Identifier | undefined; + let propertyName: ModuleExportName | undefined; let canParseAsKeyword = true; - let name = parseIdentifierName(); - if (name.escapedText === "type") { + let mustParseAsKeyword = false; + let name = parseModuleExportName(parseIdentifierName); + if (name.kind === SyntaxKind.Identifier && name.escapedText === "type") { // If the first token of an import specifier is 'type', there are a lot of possibilities, // especially if we see 'as' afterwards: // @@ -8368,6 +8385,7 @@ namespace Parser { // import { type as } from "mod"; - isTypeOnly: true, name: as // import { type as as } from "mod"; - isTypeOnly: false, name: as, propertyName: type // import { type as as as } from "mod"; - isTypeOnly: true, name: as, propertyName: as + // export { type as as "s" } from "mod";- isTypeOnly: true, name: "s", propertyName: as if (token() === SyntaxKind.AsKeyword) { // { type as ...? } const firstAs = parseIdentifierName(); @@ -8376,9 +8394,10 @@ namespace Parser { const secondAs = parseIdentifierName(); if (tokenIsIdentifierOrKeyword(token())) { // { type as as something } + // { type as as "something" } (only in exports) isTypeOnly = true; propertyName = firstAs; - name = parseNameWithKeywordCheck(); + name = parseModuleExportNameOnlyForExports(); canParseAsKeyword = false; } else { @@ -8392,7 +8411,7 @@ namespace Parser { // { type as something } propertyName = name; canParseAsKeyword = false; - name = parseNameWithKeywordCheck(); + name = parseModuleExportNameOnlyForExports(); } else { // { type as } @@ -8400,23 +8419,35 @@ namespace Parser { name = firstAs; } } + // export { type "x" } + // import { type "x" as ... } + else if (token() === SyntaxKind.StringLiteral) { + isTypeOnly = true; + if (kind === SyntaxKind.ImportSpecifier) mustParseAsKeyword = true; + name = parseModuleExportName(parseNameWithKeywordCheck); + } else if (tokenIsIdentifierOrKeyword(token())) { // { type something ...? } isTypeOnly = true; name = parseNameWithKeywordCheck(); } } + // import { "x" as ... } + else if (kind === SyntaxKind.ImportSpecifier && name.kind === SyntaxKind.StringLiteral) { + mustParseAsKeyword = true; + } - if (canParseAsKeyword && token() === SyntaxKind.AsKeyword) { + if (mustParseAsKeyword || (canParseAsKeyword && token() === SyntaxKind.AsKeyword)) { propertyName = name; parseExpected(SyntaxKind.AsKeyword); - name = parseNameWithKeywordCheck(); + name = parseModuleExportNameOnlyForExports(); } if (kind === SyntaxKind.ImportSpecifier && checkIdentifierIsKeyword) { parseErrorAt(checkIdentifierStart, checkIdentifierEnd, Diagnostics.Identifier_expected); } + if (kind === SyntaxKind.ImportSpecifier) Debug.assert(name.kind === SyntaxKind.Identifier); const node = kind === SyntaxKind.ImportSpecifier - ? factory.createImportSpecifier(isTypeOnly, propertyName, name) + ? factory.createImportSpecifier(isTypeOnly, propertyName, name as Identifier) : factory.createExportSpecifier(isTypeOnly, propertyName, name); return finishNode(node, pos); @@ -8426,10 +8457,22 @@ namespace Parser { checkIdentifierEnd = scanner.getTokenEnd(); return parseIdentifierName(); } + function parseModuleExportNameOnlyForExports() { + if (kind === SyntaxKind.ImportSpecifier) return parseNameWithKeywordCheck(); + return parseModuleExportName(parseNameWithKeywordCheck); + } + function parseModuleExportName(parser: () => Identifier): ModuleExportName { + if (token() === SyntaxKind.StringLiteral) return parseStringLiteral(); + return parser(); + } + function parseStringLiteral(): StringLiteral { + // TODO: the spec requires it pass IsStringWellFormedUnicode + return parseLiteralLikeNode(SyntaxKind.StringLiteral) as StringLiteral; + } } function parseNamespaceExport(pos: number): NamespaceExport { - return finishNode(factory.createNamespaceExport(parseIdentifierName()), pos); + return finishNode(factory.createNamespaceExport(token() === SyntaxKind.StringLiteral ? parseLiteralLikeNode(SyntaxKind.StringLiteral) as StringLiteral : parseIdentifierName()), pos); } function parseExportDeclaration(pos: number, hasJSDoc: boolean, modifiers: NodeArray | undefined): ExportDeclaration { diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index 4be05fd79eb06..d0e9ef4c90b62 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -59,6 +59,7 @@ import { getDirectoryPath, getEffectiveBaseTypeNode, getEffectiveModifierFlags, + getEmitScriptTarget, getExternalModuleImportEqualsDeclarationExpression, getExternalModuleNameFromDeclaration, getFirstConstructorWithBody, @@ -1520,7 +1521,7 @@ export function transformDeclarations(context: TransformationContext) { /*modifiers*/ undefined, /*isTypeOnly*/ false, factory.createNamedExports(map(exportMappings, ([gen, exp]) => { - return factory.createExportSpecifier(/*isTypeOnly*/ false, gen, exp); + return factory.createExportSpecifier(/*isTypeOnly*/ false, gen, factory.createModuleExportName(exp, getEmitScriptTarget(context.getCompilerOptions()))); })) )); } diff --git a/src/compiler/transformers/module/esnextAnd2015.ts b/src/compiler/transformers/module/esnextAnd2015.ts index 879544063f9f0..01991d0f69aac 100644 --- a/src/compiler/transformers/module/esnextAnd2015.ts +++ b/src/compiler/transformers/module/esnextAnd2015.ts @@ -10,6 +10,7 @@ import { EmitHint, ExportAssignment, ExportDeclaration, + ExportSpecifier, Expression, GeneratedIdentifierFlags, getEmitFlags, @@ -22,17 +23,21 @@ import { idText, ImportDeclaration, ImportEqualsDeclaration, + ImportSpecifier, insertStatementsAfterCustomPrologue, isExportNamespaceAsDefaultDeclaration, isExternalModule, isExternalModuleImportEqualsDeclaration, isExternalModuleIndicator, isIdentifier, + isIdentifierText, isNamespaceExport, isSourceFile, isStatement, ModifierFlags, + ModuleExportName, ModuleKind, + NamedImports, Node, NodeFlags, ScriptTarget, @@ -119,6 +124,8 @@ export function transformECMAScriptModule(context: TransformationContext): (x: S function visitor(node: Node): VisitResult { switch (node.kind) { + case SyntaxKind.ImportDeclaration: + return visitImportDeclaration(node as ImportDeclaration); case SyntaxKind.ImportEqualsDeclaration: // Though an error in es2020 modules, in node-flavor es2020 modules, we can helpfully transform this to a synthetic `require` call // To give easy access to a synchronous `require` in node-flavor esm. We do the transform even in scenarios where we error, but `import.meta.url` @@ -185,6 +192,73 @@ export function transformECMAScriptModule(context: TransformationContext): (x: S return factory.createCallExpression(factory.cloneNode(name), /*typeArguments*/ undefined, args); } + /** + * Visits an ImportDeclaration node. + * + * @param node The node to visit. + */ + function visitImportDeclaration(node: ImportDeclaration): VisitResult { + if (compilerOptions.module !== ModuleKind.ES2015 && compilerOptions.module !== ModuleKind.ES2020) return node; + if (node.importClause?.namedBindings?.kind !== SyntaxKind.NamedImports) return node; + + const clause = node.importClause; + const substitutions: ImportSpecifier[] = []; + let needUpdate = false; + const bindings = clause.namedBindings as NamedImports; + const nextImports: ImportSpecifier[] = []; + for (const element of bindings.elements) { + if (element.propertyName?.kind === SyntaxKind.StringLiteral) { + const id = tryConvertModuleExportNameToIdentifier(element.propertyName); + if (id) { + needUpdate = true; + nextImports.push(factory.updateImportSpecifier(element, element.isTypeOnly, id, element.name)); + } + else { + substitutions.push(element); + } + } + else nextImports.push(element); + } + if (!needUpdate && !substitutions.length) return node; + node = factory.updateImportDeclaration( + node, + node.modifiers, + factory.updateImportClause(clause, clause.isTypeOnly, clause.name, factory.updateNamedImports(bindings, nextImports)), + node.moduleSpecifier, + node.assertClause + ); + if (!substitutions.length) return node; + const nsImportName = factory.getGeneratedNameForNode(node); + for (const element of substitutions) { + // TODO: this does not support ESM live binding. Not worth to introduce so much node substitute code for this? + context.addInitializationStatement( + factory.createVariableStatement( + /*modifiers*/ undefined, + factory.createVariableDeclarationList( + [factory.createVariableDeclaration( + element.name, + /*exclamationToken*/ undefined, + /*type*/ undefined, + factory.createElementAccessExpression( + nsImportName, + element.propertyName! + ) + )], + NodeFlags.Const + ) + ) + ); + } + const nsImport = factory.createImportDeclaration( + node.modifiers, + factory.createImportClause(/*isTypeOnly*/ false, /*name*/ undefined, factory.createNamespaceImport(nsImportName)), + node.moduleSpecifier, + node.assertClause + ); + if (!clause.name && nextImports.length === 0) return nsImport; + return [node, nsImport]; + } + /** * Visits an ImportEqualsDeclaration node. * @@ -226,7 +300,7 @@ export function transformECMAScriptModule(context: TransformationContext): (x: S statements = append(statements, factory.createExportDeclaration( /*modifiers*/ undefined, node.isTypeOnly, - factory.createNamedExports([factory.createExportSpecifier(/*isTypeOnly*/ false, /*propertyName*/ undefined, idText(node.name))]) + factory.createNamedExports([factory.createExportSpecifier(/*isTypeOnly*/ false, /*propertyName*/ undefined, node.name)]) )); } return statements; @@ -237,7 +311,61 @@ export function transformECMAScriptModule(context: TransformationContext): (x: S return node.isExportEquals ? undefined : node; } + function tryConvertModuleExportNameToIdentifier(node: ModuleExportName): Identifier | undefined { + if (node.kind === SyntaxKind.Identifier) return node; + if (!isIdentifierText(node.text, languageVersion)) return undefined; + const id = factory.createIdentifier(node.text); + setOriginalNode(id, node); + return id; + } + function visitExportDeclaration(node: ExportDeclaration) { + // transform StringLiteral to Identifier when it's possible. + if (compilerOptions.module === ModuleKind.ES2015 || compilerOptions.module === ModuleKind.ES2020) { + const clause = node.exportClause; + if (clause?.kind === SyntaxKind.NamespaceExport && clause.name.kind === SyntaxKind.StringLiteral) { + const id = tryConvertModuleExportNameToIdentifier(clause.name); + if (id) { + node = factory.updateExportDeclaration( + node, + node.modifiers, + node.isTypeOnly, + factory.updateNamespaceExport(clause, id), + node.moduleSpecifier, + node.assertClause + ); + } + else return undefined; + } + if (clause?.kind === SyntaxKind.NamedExports) { + const nextExports: ExportSpecifier[] = []; + let needUpdate = false; + for (const element of clause.elements) { + const exported = element.propertyName || element.name; + // ill-formed + if (exported.kind === SyntaxKind.StringLiteral && !node.moduleSpecifier) return node; + const prop = element.propertyName && tryConvertModuleExportNameToIdentifier(element.propertyName); + const name = tryConvertModuleExportNameToIdentifier(element.name); + if (element.name !== name || prop !== element.propertyName) { + needUpdate = true; + if ((element.propertyName && !prop) || !name) continue; + nextExports.push(factory.updateExportSpecifier(element, element.isTypeOnly, prop, name || element.name)); + } + else nextExports.push(element); + } + if (needUpdate) { + node = factory.updateExportDeclaration( + node, + node.modifiers, + node.isTypeOnly, + factory.updateNamedExports(clause, nextExports), + node.moduleSpecifier, + node.assertClause + ); + } + } + } + // `export * as ns` only needs to be transformed in ES2015 if (compilerOptions.module !== undefined && compilerOptions.module > ModuleKind.ES2015) { return node; diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index fc95446ce54c2..88523690a933f 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -17,6 +17,7 @@ import { chainBundle, ClassDeclaration, collectExternalModuleInfo, + createElementAccessOrPropertyAccessExpression, Debug, Declaration, DefaultClause, @@ -118,6 +119,8 @@ import { mapDefined, Modifier, ModifierFlags, + ModuleExportName, + moduleExportNameText, ModuleKind, Node, NodeArray, @@ -267,7 +270,7 @@ export function transformModule(context: TransformationContext): (x: SourceFile factory.createExpressionStatement( reduceLeft( currentModuleInfo.exportedNames!.slice(i, i + chunkSize), - (prev, nextId) => factory.createAssignment(factory.createPropertyAccessExpression(factory.createIdentifier("exports"), factory.createIdentifier(idText(nextId))), prev), + (prev, nextId) => factory.createAssignment(createElementAccessOrPropertyAccessExpression(factory.createIdentifier("exports"), factory.cloneNode(nextId)), prev), factory.createVoidZero() as Expression ) ) @@ -594,7 +597,7 @@ export function transformModule(context: TransformationContext): (x: SourceFile append(statements, createUnderscoreUnderscoreESModule()); } if (length(currentModuleInfo.exportedNames)) { - append(statements, factory.createExpressionStatement(reduceLeft(currentModuleInfo.exportedNames, (prev, nextId) => factory.createAssignment(factory.createPropertyAccessExpression(factory.createIdentifier("exports"), factory.createIdentifier(idText(nextId))), prev), factory.createVoidZero() as Expression))); + append(statements, factory.createExpressionStatement(reduceLeft(currentModuleInfo.exportedNames, (prev, nextId) => factory.createAssignment(createElementAccessOrPropertyAccessExpression(factory.createIdentifier("exports"), factory.cloneNode(nextId)), prev), factory.createVoidZero() as Expression))); } // Visit each statement of the module body. @@ -1587,8 +1590,8 @@ export function transformModule(context: TransformationContext): (x: SourceFile const exportNeedsImportDefault = !!getESModuleInterop(compilerOptions) && !(getInternalEmitFlags(node) & InternalEmitFlags.NeverApplyImportHelper) && - idText(specifier.propertyName || specifier.name) === "default"; - const exportedValue = factory.createPropertyAccessExpression( + moduleExportNameText(specifier.propertyName || specifier.name) === "default"; + const exportedValue = createElementAccessOrPropertyAccessExpression( exportNeedsImportDefault ? emitHelpers().createImportDefaultHelper(generatedName) : generatedName, specifier.propertyName || specifier.name); statements.push( @@ -1619,7 +1622,10 @@ export function transformModule(context: TransformationContext): (x: SourceFile getHelperExpressionForExport(node, moduleKind !== ModuleKind.AMD ? createRequireCall(node) : isExportNamespaceAsDefaultDeclaration(node) ? generatedName : - factory.createIdentifier(idText(node.exportClause.name))) + node.exportClause.name.kind === SyntaxKind.Identifier ? + factory.createIdentifier(idText(node.exportClause.name)) : + factory.getGeneratedNameForNode(node.exportClause.name) + ) ) ), node @@ -2037,7 +2043,7 @@ export function transformModule(context: TransformationContext): (x: SourceFile * @param location The location to use for source maps and comments for the export. * @param allowComments Whether to allow comments on the export. */ - function appendExportStatement(statements: Statement[] | undefined, exportName: Identifier, expression: Expression, location?: TextRange, allowComments?: boolean, liveBinding?: boolean): Statement[] | undefined { + function appendExportStatement(statements: Statement[] | undefined, exportName: ModuleExportName, expression: Expression, location?: TextRange, allowComments?: boolean, liveBinding?: boolean): Statement[] | undefined { statements = append(statements, createExportStatement(exportName, expression, location, allowComments, liveBinding)); return statements; } @@ -2079,7 +2085,7 @@ export function transformModule(context: TransformationContext): (x: SourceFile * @param location The location to use for source maps and comments for the export. * @param allowComments An optional value indicating whether to emit comments for the statement. */ - function createExportStatement(name: Identifier, value: Expression, location?: TextRange, allowComments?: boolean, liveBinding?: boolean) { + function createExportStatement(name: ModuleExportName, value: Expression, location?: TextRange, allowComments?: boolean, liveBinding?: boolean) { const statement = setTextRange(factory.createExpressionStatement(createExportExpression(name, value, /*location*/ undefined, liveBinding)), location); startOnNewLine(statement); if (!allowComments) { @@ -2096,7 +2102,7 @@ export function transformModule(context: TransformationContext): (x: SourceFile * @param value The exported value. * @param location The location to use for source maps and comments for the export. */ - function createExportExpression(name: Identifier, value: Expression, location?: TextRange, liveBinding?: boolean) { + function createExportExpression(name: ModuleExportName, value: Expression, location?: TextRange, liveBinding?: boolean) { return setTextRange( liveBinding && languageVersion !== ScriptTarget.ES3 ? factory.createCallExpression( factory.createPropertyAccessExpression( @@ -2121,7 +2127,7 @@ export function transformModule(context: TransformationContext): (x: SourceFile ]) ] ) : factory.createAssignment( - factory.createPropertyAccessExpression( + createElementAccessOrPropertyAccessExpression( factory.createIdentifier("exports"), factory.cloneNode(name) ), @@ -2320,7 +2326,7 @@ export function transformModule(context: TransformationContext): (x: SourceFile else if (isImportSpecifier(importDeclaration)) { const name = importDeclaration.propertyName || importDeclaration.name; return setTextRange( - factory.createPropertyAccessExpression( + createElementAccessOrPropertyAccessExpression( factory.getGeneratedNameForNode(importDeclaration.parent?.parent?.parent || importDeclaration), factory.cloneNode(name) ), diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 4e2d9b954e626..ffd0cc2cdd512 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -12,6 +12,7 @@ import { chainBundle, ClassDeclaration, collectExternalModuleInfo, + createElementAccessOrPropertyAccessExpression, Debug, Declaration, DefaultClause, @@ -89,6 +90,8 @@ import { map, MetaProperty, ModifierFlags, + moduleExportNameText, + moduleExportNameTextEscaped, moveEmitHelpers, Node, NodeFlags, @@ -452,7 +455,7 @@ export function transformSystemModule(context: TransformationContext): (x: Sourc const exportedNames: ObjectLiteralElementLike[] = []; if (moduleInfo.exportedNames) { for (const exportedLocalName of moduleInfo.exportedNames) { - if (exportedLocalName.escapedText === "default") { + if (moduleExportNameTextEscaped(exportedLocalName) === "default") { continue; } @@ -626,10 +629,10 @@ export function transformSystemModule(context: TransformationContext): (x: Sourc for (const e of entry.exportClause.elements) { properties.push( factory.createPropertyAssignment( - factory.createStringLiteral(idText(e.name)), + factory.createStringLiteral(moduleExportNameText(e.name)), factory.createElementAccessExpression( parameterName, - factory.createStringLiteral(idText(e.propertyName || e.name)) + factory.createStringLiteral(moduleExportNameText(e.propertyName || e.name)) ) ) ); @@ -652,7 +655,7 @@ export function transformSystemModule(context: TransformationContext): (x: Sourc exportFunction, /*typeArguments*/ undefined, [ - factory.createStringLiteral(idText(entry.exportClause.name)), + factory.createStringLiteral(moduleExportNameText(entry.exportClause.name)), parameterName ] ) @@ -1117,7 +1120,7 @@ export function transformSystemModule(context: TransformationContext): (x: Sourc const exportSpecifiers = moduleInfo.exportSpecifiers.get(idText(name)); if (exportSpecifiers) { for (const exportSpecifier of exportSpecifiers) { - if (exportSpecifier.name.escapedText !== excludeName) { + if (moduleExportNameTextEscaped(exportSpecifier.name) !== excludeName) { statements = appendExportStatement(statements, exportSpecifier.name, name); } } @@ -1806,7 +1809,7 @@ export function transformSystemModule(context: TransformationContext): (x: Sourc return setTextRange( factory.createPropertyAssignment( factory.cloneNode(name), - factory.createPropertyAccessExpression( + createElementAccessOrPropertyAccessExpression( factory.getGeneratedNameForNode(importDeclaration.parent?.parent?.parent || importDeclaration), factory.cloneNode(importDeclaration.propertyName || importDeclaration.name) ), @@ -1872,7 +1875,7 @@ export function transformSystemModule(context: TransformationContext): (x: Sourc } else if (isImportSpecifier(importDeclaration)) { return setTextRange( - factory.createPropertyAccessExpression( + createElementAccessOrPropertyAccessExpression( factory.getGeneratedNameForNode(importDeclaration.parent?.parent?.parent || importDeclaration), factory.cloneNode(importDeclaration.propertyName || importDeclaration.name) ), diff --git a/src/compiler/transformers/utilities.ts b/src/compiler/transformers/utilities.ts index c094a8dc0e76c..af2e1bf4bb1a9 100644 --- a/src/compiler/transformers/utilities.ts +++ b/src/compiler/transformers/utilities.ts @@ -66,6 +66,9 @@ import { map, MethodDeclaration, ModifierFlags, + ModuleExportName, + moduleExportNameText, + moduleExportNameTextEscaped, NamedImportBindings, NamespaceExport, Node, @@ -99,7 +102,7 @@ export interface ExternalModuleInfo { externalHelpersImportDeclaration: ImportDeclaration | undefined; // import of external helpers exportSpecifiers: Map; // file-local export specifiers by name (no reexports) exportedBindings: Identifier[][]; // exported names of local declarations - exportedNames: Identifier[] | undefined; // all exported names in the module, both local and reexported + exportedNames: ModuleExportName[] | undefined; // all exported names in the module, both local and reexported exportEquals: ExportAssignment | undefined; // an export= declaration if one was present hasExportStarsToExportValues: boolean; // whether this module contains export* } @@ -111,7 +114,7 @@ function containsDefaultReference(node: NamedImportBindings | undefined) { } function isNamedDefaultReference(e: ImportSpecifier): boolean { - return e.propertyName !== undefined && e.propertyName.escapedText === InternalSymbolName.Default; + return e.propertyName !== undefined && moduleExportNameTextEscaped(e.propertyName) === InternalSymbolName.Default; } /** @internal */ @@ -164,7 +167,7 @@ export function collectExternalModuleInfo(context: TransformationContext, source const exportSpecifiers = createMultiMap(); const exportedBindings: Identifier[][] = []; const uniqueExports = new Map(); - let exportedNames: Identifier[] | undefined; + let exportedNames: ModuleExportName[] | undefined; let hasExportDefault = false; let exportEquals: ExportAssignment | undefined; let hasExportStarsToExportValues = false; @@ -211,9 +214,10 @@ export function collectExternalModuleInfo(context: TransformationContext, source } else { const name = ((node as ExportDeclaration).exportClause as NamespaceExport).name; - if (!uniqueExports.get(idText(name))) { + const nameText = moduleExportNameText(name); + if (!uniqueExports.get(nameText)) { multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), name); - uniqueExports.set(idText(name), true); + uniqueExports.set(nameText, true); exportedNames = append(exportedNames, name); } // we use the same helpers for `export * as ns` as we do for `import * as ns` @@ -295,27 +299,30 @@ export function collectExternalModuleInfo(context: TransformationContext, source function addExportedNamesForExportDeclaration(node: ExportDeclaration) { for (const specifier of cast(node.exportClause, isNamedExports).elements) { - if (!uniqueExports.get(idText(specifier.name))) { + if (!uniqueExports.get(moduleExportNameText(specifier.name))) { const name = specifier.propertyName || specifier.name; - if (!node.moduleSpecifier) { + if (!node.moduleSpecifier && name.kind === SyntaxKind.Identifier) { exportSpecifiers.add(idText(name), specifier); } - const decl = resolver.getReferencedImportDeclaration(name) - || resolver.getReferencedValueDeclaration(name); + // export { "x" as ... } cannot be resolved locally. + if (name.kind === SyntaxKind.Identifier) { + const decl = resolver.getReferencedImportDeclaration(name) + || resolver.getReferencedValueDeclaration(name); - if (decl) { - multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(decl), specifier.name); + if (decl) { + multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(decl), specifier.name); + } } - uniqueExports.set(idText(specifier.name), true); + uniqueExports.set(moduleExportNameText(specifier.name), true); exportedNames = append(exportedNames, specifier.name); } } } } -function collectExportedVariableInfo(decl: VariableDeclaration | BindingElement, uniqueExports: Map, exportedNames: Identifier[] | undefined, exportedBindings: Identifier[][]) { +function collectExportedVariableInfo(decl: VariableDeclaration | BindingElement, uniqueExports: Map, exportedNames: ModuleExportName[] | undefined, exportedBindings: Identifier[][]) { if (isBindingPattern(decl.name)) { for (const element of decl.name.elements) { if (!isOmittedExpression(element)) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d5c402ea30784..7fa3678454acd 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3733,7 +3733,7 @@ export interface NamespaceImport extends NamedDeclaration { export interface NamespaceExport extends NamedDeclaration { readonly kind: SyntaxKind.NamespaceExport; readonly parent: ExportDeclaration; - readonly name: Identifier + readonly name: ModuleExportName; } export interface NamespaceExportDeclaration extends DeclarationStatement, JSDocContainer { @@ -3773,7 +3773,7 @@ export type NamedImportsOrExports = NamedImports | NamedExports; export interface ImportSpecifier extends NamedDeclaration { readonly kind: SyntaxKind.ImportSpecifier; readonly parent: NamedImports; - readonly propertyName?: Identifier; // Name preceding "as" keyword (or undefined when "as" is absent) + readonly propertyName?: ModuleExportName; // Name preceding "as" keyword (or undefined when "as" is absent) readonly name: Identifier; // Declared name readonly isTypeOnly: boolean; } @@ -3782,10 +3782,11 @@ export interface ExportSpecifier extends NamedDeclaration, JSDocContainer { readonly kind: SyntaxKind.ExportSpecifier; readonly parent: NamedExports; readonly isTypeOnly: boolean; - readonly propertyName?: Identifier; // Name preceding "as" keyword (or undefined when "as" is absent) - readonly name: Identifier; // Declared name + readonly propertyName?: ModuleExportName; // Name preceding "as" keyword (or undefined when "as" is absent) + readonly name: ModuleExportName; // Declared name } +export type ModuleExportName = Identifier | StringLiteral; export type ImportOrExportSpecifier = | ImportSpecifier | ExportSpecifier @@ -5681,7 +5682,7 @@ export interface EmitResolver { getNodeCheckFlags(node: Node): NodeCheckFlags; isDeclarationVisible(node: Declaration | AnyImportSyntax): boolean; isLateBound(node: Declaration): node is LateBoundDeclaration; - collectLinkedAliases(node: Identifier, setVisibility?: boolean): Node[] | undefined; + collectLinkedAliases(node: ModuleExportName, setVisibility?: boolean): Node[] | undefined; isImplementationOfOverload(node: SignatureDeclaration): boolean | undefined; isRequiredInitializedParameter(node: ParameterDeclaration): boolean; isOptionalUninitializedParameterProperty(node: ParameterDeclaration): boolean; @@ -5949,6 +5950,8 @@ export interface ReverseMappedSymbol extends TransientSymbol { links: ReverseMappedSymbolLinks; } +// All names should starts with __, since it's possible to declare an arbitrary symbol name with +// export { ident as "this" } export const enum InternalSymbolName { Call = "__call", // Call signatures Constructor = "__constructor", // Constructor implementations @@ -8625,20 +8628,20 @@ export interface NodeFactory { updateImportTypeAssertionContainer(node: ImportTypeAssertionContainer, clause: AssertClause, multiLine?: boolean): ImportTypeAssertionContainer; createNamespaceImport(name: Identifier): NamespaceImport; updateNamespaceImport(node: NamespaceImport, name: Identifier): NamespaceImport; - createNamespaceExport(name: Identifier): NamespaceExport; - updateNamespaceExport(node: NamespaceExport, name: Identifier): NamespaceExport; + createNamespaceExport(name: ModuleExportName): NamespaceExport; + updateNamespaceExport(node: NamespaceExport, name: ModuleExportName): NamespaceExport; createNamedImports(elements: readonly ImportSpecifier[]): NamedImports; updateNamedImports(node: NamedImports, elements: readonly ImportSpecifier[]): NamedImports; - createImportSpecifier(isTypeOnly: boolean, propertyName: Identifier | undefined, name: Identifier): ImportSpecifier; - updateImportSpecifier(node: ImportSpecifier, isTypeOnly: boolean, propertyName: Identifier | undefined, name: Identifier): ImportSpecifier; + createImportSpecifier(isTypeOnly: boolean, propertyName: ModuleExportName | undefined, name: Identifier): ImportSpecifier; + updateImportSpecifier(node: ImportSpecifier, isTypeOnly: boolean, propertyName: ModuleExportName | undefined, name: Identifier): ImportSpecifier; createExportAssignment(modifiers: readonly ModifierLike[] | undefined, isExportEquals: boolean | undefined, expression: Expression): ExportAssignment; updateExportAssignment(node: ExportAssignment, modifiers: readonly ModifierLike[] | undefined, expression: Expression): ExportAssignment; createExportDeclaration(modifiers: readonly ModifierLike[] | undefined, isTypeOnly: boolean, exportClause: NamedExportBindings | undefined, moduleSpecifier?: Expression, assertClause?: AssertClause): ExportDeclaration; updateExportDeclaration(node: ExportDeclaration, modifiers: readonly ModifierLike[] | undefined, isTypeOnly: boolean, exportClause: NamedExportBindings | undefined, moduleSpecifier: Expression | undefined, assertClause: AssertClause | undefined): ExportDeclaration; createNamedExports(elements: readonly ExportSpecifier[]): NamedExports; updateNamedExports(node: NamedExports, elements: readonly ExportSpecifier[]): NamedExports; - createExportSpecifier(isTypeOnly: boolean, propertyName: string | Identifier | undefined, name: string | Identifier): ExportSpecifier; - updateExportSpecifier(node: ExportSpecifier, isTypeOnly: boolean, propertyName: Identifier | undefined, name: Identifier): ExportSpecifier; + createExportSpecifier(isTypeOnly: boolean, propertyName: ModuleExportName | undefined, name: ModuleExportName): ExportSpecifier; + updateExportSpecifier(node: ExportSpecifier, isTypeOnly: boolean, propertyName: ModuleExportName | undefined, name: ModuleExportName): ExportSpecifier; /** @internal */ createMissingDeclaration(): MissingDeclaration; // @@ -8870,6 +8873,8 @@ export interface NodeFactory { createLogicalNot(operand: Expression): PrefixUnaryExpression; createPostfixIncrement(operand: Expression): PostfixUnaryExpression; createPostfixDecrement(operand: Expression): PostfixUnaryExpression; + createModuleExportName(name: string, languageVersion: ScriptTarget): ModuleExportName; + createModuleExportName(name: string | undefined, languageVersion: ScriptTarget): ModuleExportName | undefined; // // Compound Nodes diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 54b8f92786698..c462b2c4bd1ca 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -392,6 +392,7 @@ import { ModuleBlock, ModuleDeclaration, ModuleDetectionKind, + ModuleExportName, ModuleKind, ModuleResolutionKind, moduleResolutionOptionDeclarations, @@ -1206,7 +1207,7 @@ function isJSDocTypeExpressionOrChild(node: Node): boolean { /** @internal */ export function isExportNamespaceAsDefaultDeclaration(node: Node): boolean { - return !!(isExportDeclaration(node) && node.exportClause && isNamespaceExport(node.exportClause) && node.exportClause.name.escapedText === "default"); + return !!(isExportDeclaration(node) && node.exportClause && isNamespaceExport(node.exportClause) && moduleExportNameTextEscaped(node.exportClause.name) === "default"); } /** @internal */ @@ -2042,7 +2043,7 @@ export function forEachEnclosingBlockScopeContainer(node: Node, cb: (container: // Computed property names will just be emitted as "[]", where is the source // text of the expression in the computed property. /** @internal */ -export function declarationNameToString(name: DeclarationName | QualifiedName | undefined) { +export function declarationNameToString(name: DeclarationName | QualifiedName | ModuleExportName | undefined) { return !name || getFullWidth(name) === 0 ? "(Missing)" : getTextOfNode(name); } @@ -3872,6 +3873,12 @@ export function getElementOrPropertyAccessName(node: AccessExpression): __String return undefined; } +/** @internal */ +export function createElementAccessOrPropertyAccessExpression(lhs: Expression, rhs: Expression) { + if (isMemberName(rhs)) return factory.createPropertyAccessExpression(lhs, rhs); + return factory.createElementAccessExpression(lhs, rhs); +} + /** @internal */ export function getAssignmentDeclarationPropertyAccessKind(lhs: AccessExpression): AssignmentDeclarationKind { if (lhs.expression.kind === SyntaxKind.ThisKeyword) { @@ -4022,6 +4029,10 @@ export function getExternalModuleName(node: AnyImportOrReExport | ImportTypeNode } /** @internal */ +export function getNamespaceDeclarationNode(node: ImportDeclaration): NamespaceImport | undefined; +export function getNamespaceDeclarationNode(node: ImportEqualsDeclaration): ImportEqualsDeclaration; +export function getNamespaceDeclarationNode(node: ExportDeclaration): NamespaceExport | undefined; +export function getNamespaceDeclarationNode(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration): ImportEqualsDeclaration | NamespaceImport | NamespaceExport | undefined; export function getNamespaceDeclarationNode(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration): ImportEqualsDeclaration | NamespaceImport | NamespaceExport | undefined { switch (node.kind) { case SyntaxKind.ImportDeclaration: @@ -10270,3 +10281,18 @@ export function getTextOfJsxNamespacedName(node: JsxNamespacedName) { export function intrinsicTagNameToString(node: Identifier | JsxNamespacedName) { return isIdentifier(node) ? idText(node) : getTextOfJsxNamespacedName(node); } + +/** @internal */ +export function moduleExportNameText(node: ModuleExportName): string { + if (node.kind === SyntaxKind.StringLiteral) return node.text; + return idText(node); +} +/** @internal */ +export function moduleExportNameTextEscaped(node: ModuleExportName): __String { + if (node.kind === SyntaxKind.StringLiteral) return escapeLeadingUnderscores(node.text); + return node.escapedText; +} +export function isModuleExportNameStringLiteral(node: __String | ModuleExportName | undefined): node is StringLiteral { + if (!node) return false; + return typeof node !== "string" && node.kind === SyntaxKind.StringLiteral; +} diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index 4d0220bdf4308..2ef6d93ab5c4a 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -851,8 +851,8 @@ function getDeclarationIdentifier(node: Declaration | Expression): Identifier | } /** @internal */ -export function nodeHasName(statement: Node, name: Identifier) { - if (isNamedDeclaration(statement) && isIdentifier(statement.name) && idText(statement.name as Identifier) === idText(name)) { +export function nodeHasName(statement: Node, name: Identifier | string) { + if (isNamedDeclaration(statement) && isIdentifier(statement.name) && idText(statement.name) === (typeof name === "string" ? name : idText(name))) { return true; } if (isVariableStatement(statement) && some(statement.declarationList.declarations, d => nodeHasName(d, name))) { diff --git a/src/compiler/visitorPublic.ts b/src/compiler/visitorPublic.ts index 08475d5c0ae7a..c114b169cebb0 100644 --- a/src/compiler/visitorPublic.ts +++ b/src/compiler/visitorPublic.ts @@ -59,6 +59,7 @@ import { isModifier, isModifierLike, isModuleBody, + isModuleExportName, isModuleName, isModuleReference, isNamedExportBindings, @@ -1355,7 +1356,7 @@ const visitEachChildTable: VisitEachChildTable = { [SyntaxKind.NamespaceExport]: function visitEachChildOfNamespaceExport(node, visitor, context, _nodesVisitor, nodeVisitor, _tokenVisitor) { return context.factory.updateNamespaceExport(node, - Debug.checkDefined(nodeVisitor(node.name, visitor, isIdentifier))); + Debug.checkDefined(nodeVisitor(node.name, visitor, isModuleExportName))); }, [SyntaxKind.NamedImports]: function visitEachChildOfNamedImports(node, visitor, context, nodesVisitor, _nodeVisitor, _tokenVisitor) { @@ -1366,7 +1367,7 @@ const visitEachChildTable: VisitEachChildTable = { [SyntaxKind.ImportSpecifier]: function visitEachChildOfImportSpecifier(node, visitor, context, _nodesVisitor, nodeVisitor, _tokenVisitor) { return context.factory.updateImportSpecifier(node, node.isTypeOnly, - nodeVisitor(node.propertyName, visitor, isIdentifier), + nodeVisitor(node.propertyName, visitor, isModuleExportName), Debug.checkDefined(nodeVisitor(node.name, visitor, isIdentifier))); }, @@ -1393,8 +1394,8 @@ const visitEachChildTable: VisitEachChildTable = { [SyntaxKind.ExportSpecifier]: function visitEachChildOfExportSpecifier(node, visitor, context, _nodesVisitor, nodeVisitor, _tokenVisitor) { return context.factory.updateExportSpecifier(node, node.isTypeOnly, - nodeVisitor(node.propertyName, visitor, isIdentifier), - Debug.checkDefined(nodeVisitor(node.name, visitor, isIdentifier))); + nodeVisitor(node.propertyName, visitor, isModuleExportName), + Debug.checkDefined(nodeVisitor(node.name, visitor, isModuleExportName))); }, // Module references diff --git a/src/services/codefixes/convertToEsModule.ts b/src/services/codefixes/convertToEsModule.ts index b15a0521c1cb1..356876dbefb7b 100644 --- a/src/services/codefixes/convertToEsModule.ts +++ b/src/services/codefixes/convertToEsModule.ts @@ -376,7 +376,7 @@ function convertNamedExport( */ const newNodes = [ makeConst(/*modifiers*/ undefined, rename, assignment.right), - makeExportDeclaration([factory.createExportSpecifier(/*isTypeOnly*/ false, rename, text)]), + makeExportDeclaration([factory.createExportSpecifier(/*isTypeOnly*/ false, factory.createModuleExportName(rename, ScriptTarget.ESNext), factory.createModuleExportName(text, ScriptTarget.ESNext))]), ]; changes.replaceNodeWithNodes(sourceFile, assignment.parent, newNodes); } @@ -399,7 +399,7 @@ function reExportStar(moduleSpecifier: string): ExportDeclaration { return makeExportDeclaration(/*exportSpecifiers*/ undefined, moduleSpecifier); } function reExportDefault(moduleSpecifier: string): ExportDeclaration { - return makeExportDeclaration([factory.createExportSpecifier(/*isTypeOnly*/ false, /*propertyName*/ undefined, "default")], moduleSpecifier); + return makeExportDeclaration([factory.createExportSpecifier(/*isTypeOnly*/ false, /*propertyName*/ undefined, factory.createIdentifier("default"))], moduleSpecifier); } function convertExportsPropertyAssignment({ left, right, parent }: BinaryExpression & { left: PropertyAccessExpression }, sourceFile: SourceFile, changes: textChanges.ChangeTracker): void { diff --git a/src/services/codefixes/fixInvalidImportSyntax.ts b/src/services/codefixes/fixInvalidImportSyntax.ts index bae3f2d2c0f16..1124d10a305a0 100644 --- a/src/services/codefixes/fixInvalidImportSyntax.ts +++ b/src/services/codefixes/fixInvalidImportSyntax.ts @@ -18,7 +18,6 @@ import { isTransientSymbol, makeImport, ModuleKind, - NamespaceImport, NewExpression, Node, SourceFile, @@ -34,7 +33,7 @@ const fixName = "invalidImportSyntax"; function getCodeFixesForImportDeclaration(context: CodeFixContext, node: ImportDeclaration): CodeFixAction[] { const sourceFile = getSourceFileOfNode(node); - const namespace = getNamespaceDeclarationNode(node) as NamespaceImport; + const namespace = getNamespaceDeclarationNode(node)!; const opts = context.program.getCompilerOptions(); const variations: CodeFixAction[] = []; diff --git a/src/services/completions.ts b/src/services/completions.ts index 0becb60309ed2..525e89a8cd21a 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -285,6 +285,7 @@ import { ModifierSyntaxKind, modifierToFlag, ModuleDeclaration, + moduleExportNameTextEscaped, ModuleReference, moduleResolutionSupportsPackageJsonExportsAndImports, NamedImportBindings, @@ -1747,6 +1748,16 @@ function createCompletionEntry( data = originToCompletionEntryData(origin); hasAction = !importStatementCompletion; } + const parentNamedImportOrExport = findAncestor(location, isNamedImportsOrExports); + if (parentNamedImportOrExport && !isIdentifierText(name, ScriptTarget.Latest)) { + if (parentNamedImportOrExport.kind === SyntaxKind.NamedImports) { + insertText = `${quotePropertyName(sourceFile, preferences, name)} as $\{1:item}`; + isSnippet = true; + } + else { + insertText = quotePropertyName(sourceFile, preferences, name); + } + } // TODO(drosen): Right now we just permit *all* semantic meanings when calling // 'getSymbolKind' which is permissible given that it is backwards compatible; but @@ -2450,7 +2461,7 @@ export function getCompletionEntriesFromSymbols( for (let i = 0; i < symbols.length; i++) { const symbol = symbols[i]; const origin = symbolToOriginInfoMap?.[i]; - const info = getCompletionEntryDisplayNameForSymbol(symbol, target, origin, kind, !!jsxIdentifierExpected); + const info = getCompletionEntryDisplayNameForSymbol(symbol, target, origin, kind, !!jsxIdentifierExpected, !!findAncestor(location, isNamedImportsOrExports)); if (!info || (uniques.get(info.name) && (!origin || !originIsObjectLiteralMethod(origin))) || kind === CompletionKind.Global && symbolToSortTextMap && !shouldIncludeSymbol(symbol, symbolToSortTextMap)) { continue; } @@ -2660,7 +2671,7 @@ function getSymbolCompletionFromEntryId( // completion entry. return firstDefined(symbols, (symbol, index): SymbolCompletion | undefined => { const origin = symbolToOriginInfoMap[index]; - const info = getCompletionEntryDisplayNameForSymbol(symbol, getEmitScriptTarget(compilerOptions), origin, completionKind, completionData.isJsxIdentifierExpected); + const info = getCompletionEntryDisplayNameForSymbol(symbol, getEmitScriptTarget(compilerOptions), origin, completionKind, completionData.isJsxIdentifierExpected, !!completionData.importStatementCompletion); return info && info.name === entryId.name && ( entryId.source === CompletionSource.ClassMemberSnippet && symbol.flags & SymbolFlags.ClassMember || entryId.source === CompletionSource.ObjectLiteralMethodSnippet && symbol.flags & (SymbolFlags.Property | SymbolFlags.Method) @@ -3954,7 +3965,9 @@ function getCompletionData( getEmitScriptTarget(compilerOptions), /*origin*/ undefined, CompletionKind.ObjectPropertyDeclaration, - /*jsxIdentifierExpected*/ false); + /*jsxIdentifierExpected*/ false, + /*moduleExportNameExpected*/ false, + ); if (!displayName) { return; } @@ -4282,7 +4295,7 @@ function getCompletionData( completionKind = CompletionKind.MemberLike; isNewIdentifierLocation = false; const exports = typeChecker.getExportsAndPropertiesOfModule(moduleSpecifierSymbol); - const existing = new Set((namedImportsOrExports.elements as NodeArray).filter(n => !isCurrentlyEditingNode(n)).map(n => (n.propertyName || n.name).escapedText)); + const existing = new Set((namedImportsOrExports.elements as NodeArray).filter(n => !isCurrentlyEditingNode(n)).map(n => moduleExportNameTextEscaped(n.propertyName || n.name))); const uniques = exports.filter(e => e.escapedName !== InternalSymbolName.Default && !existing.has(e.escapedName)); symbols = concatenate(symbols, uniques); if (!uniques.length) { @@ -4793,7 +4806,9 @@ function getCompletionData( target, origin, CompletionKind.ObjectPropertyDeclaration, - /*jsxIdentifierExpected*/ false); + /*jsxIdentifierExpected*/ false, + /*moduleExportNameExpected*/ false + ); if (displayName) { const originalSortText = symbolToSortTextMap[symbolId] ?? SortText.LocationPriority; const { name } = displayName; @@ -4944,6 +4959,7 @@ function getCompletionEntryDisplayNameForSymbol( origin: SymbolOriginInfo | undefined, kind: CompletionKind, jsxIdentifierExpected: boolean, + moduleExportNameExpected: boolean ): CompletionEntryDisplayNameForSymbol | undefined { if (originIsIgnore(origin)) { return undefined; @@ -4959,6 +4975,9 @@ function getCompletionEntryDisplayNameForSymbol( } const validNameResult: CompletionEntryDisplayNameForSymbol = { name, needsConvertPropertyAccess: false }; + if (moduleExportNameExpected) { + return validNameResult; + } if (isIdentifierText(name, target, jsxIdentifierExpected ? LanguageVariant.JSX : LanguageVariant.Standard) || symbol.valueDeclaration && isPrivateIdentifierClassElementDeclaration(symbol.valueDeclaration)) { return validNameResult; } diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index 1aa55a6cbc2cf..268287766f7a2 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -191,6 +191,8 @@ import { MethodDeclaration, ModifierFlags, ModuleDeclaration, + ModuleExportName, + moduleExportNameTextEscaped, MultiMap, NamedDeclaration, Node, @@ -1519,7 +1521,7 @@ export namespace Core { exportingModuleSymbol: Symbol, exportName: string, isDefaultExport: boolean, - cb: (ref: Identifier) => void, + cb: (ref: ModuleExportName) => void, ): void { const importTracker = createImportTracker(sourceFiles, new Set(sourceFiles.map(f => f.fileName)), checker, cancellationToken); const { importSearches, indirectUsers, singleReferences } = importTracker(exportSymbol, { exportKind: isDefaultExport ? ExportKind.Default : ExportKind.Named, exportingModuleSymbol }, /*isForRename*/ false); @@ -1927,7 +1929,7 @@ export namespace Core { } function getReferencesAtExportSpecifier( - referenceLocation: Identifier, + referenceLocation: ModuleExportName, referenceSymbol: Symbol, exportSpecifier: ExportSpecifier, search: Search, @@ -1946,7 +1948,7 @@ export namespace Core { if (!propertyName) { // Don't rename at `export { default } from "m";`. (but do continue to search for imports of the re-export) - if (!(state.options.use === FindReferencesUse.Rename && (name.escapedText === InternalSymbolName.Default))) { + if (!(state.options.use === FindReferencesUse.Rename && (moduleExportNameTextEscaped(name) === InternalSymbolName.Default))) { addRef(); } } @@ -1969,8 +1971,8 @@ export namespace Core { // For `export { foo as bar }`, rename `foo`, but not `bar`. if (!isForRenameWithPrefixAndSuffixText(state.options) || alwaysGetReferences) { - const isDefaultExport = referenceLocation.escapedText === "default" - || exportSpecifier.name.escapedText === "default"; + const isDefaultExport = moduleExportNameTextEscaped(referenceLocation) === "default" + || moduleExportNameTextEscaped(exportSpecifier.name) === "default"; const exportKind = isDefaultExport ? ExportKind.Default : ExportKind.Named; const exportSymbol = Debug.checkDefined(exportSpecifier.symbol); const exportInfo = getExportInfo(exportSymbol, exportKind, state.checker); @@ -1990,11 +1992,11 @@ export namespace Core { } } - function getLocalSymbolForExportSpecifier(referenceLocation: Identifier, referenceSymbol: Symbol, exportSpecifier: ExportSpecifier, checker: TypeChecker): Symbol { + function getLocalSymbolForExportSpecifier(referenceLocation: ModuleExportName, referenceSymbol: Symbol, exportSpecifier: ExportSpecifier, checker: TypeChecker): Symbol { return isExportSpecifierAlias(referenceLocation, exportSpecifier) && checker.getExportSpecifierLocalTargetSymbol(exportSpecifier) || referenceSymbol; } - function isExportSpecifierAlias(referenceLocation: Identifier, exportSpecifier: ExportSpecifier): boolean { + function isExportSpecifierAlias(referenceLocation: ModuleExportName, exportSpecifier: ExportSpecifier): boolean { const { parent, propertyName, name } = exportSpecifier; Debug.assert(propertyName === referenceLocation || name === referenceLocation); if (propertyName) { diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts index 3688a688c53d8..86ae653132d60 100644 --- a/src/services/goToDefinition.ts +++ b/src/services/goToDefinition.ts @@ -58,9 +58,11 @@ import { isFunctionTypeNode, isIdentifier, isImportMeta, + isImportOrExportSpecifier, isJSDocOverrideTag, isJsxOpeningLikeElement, isJumpStatementTarget, + isModuleExportName, isModuleSpecifierLike, isNameOfFunctionDeclaration, isNewExpressionTarget, @@ -516,6 +518,9 @@ function getSymbol(node: Node, checker: TypeChecker, stopAtAlias: boolean | unde // (2) when the aliased symbol is originating from an import. // function shouldSkipAlias(node: Node, declaration: Node): boolean { + if (isModuleExportName(node) && isImportOrExportSpecifier(declaration)) { + return true; + } if (node.kind !== SyntaxKind.Identifier) { return false; } diff --git a/src/services/importTracker.ts b/src/services/importTracker.ts index f79eb63627f3c..78177d6a7fa0b 100644 --- a/src/services/importTracker.ts +++ b/src/services/importTracker.ts @@ -60,6 +60,8 @@ import { ModifierFlags, ModuleBlock, ModuleDeclaration, + ModuleExportName, + moduleExportNameTextEscaped, NamedImportsOrExports, NamespaceImport, Node, @@ -88,9 +90,9 @@ import { /** @internal */ export interface ImportsResult { /** For every import of the symbol, the location and local symbol for the import. */ - importSearches: readonly [Identifier, Symbol][]; + importSearches: readonly [ModuleExportName, Symbol][]; /** For rename imports/exports `{ foo as bar }`, `foo` is not a local, so it may be added as a reference immediately without further searching. */ - singleReferences: readonly (Identifier | StringLiteral)[]; + singleReferences: readonly ModuleExportName[]; /** List of source files that may (or may not) use the symbol via a namespace. (For UMD modules this is every file.) */ indirectUsers: readonly SourceFile[]; } @@ -307,9 +309,9 @@ function getImportersForExport( * But re-exports will be placed in 'singleReferences' since they cannot be locally referenced. */ function getSearchesFromDirectImports(directImports: Importer[], exportSymbol: Symbol, exportKind: ExportKind, checker: TypeChecker, isForRename: boolean): Pick { - const importSearches: [Identifier, Symbol][] = []; - const singleReferences: (Identifier | StringLiteral)[] = []; - function addSearch(location: Identifier, symbol: Symbol): void { + const importSearches: [ModuleExportName, Symbol][] = []; + const singleReferences: ModuleExportName[] = []; + function addSearch(location: ModuleExportName, symbol: Symbol): void { importSearches.push([location, symbol]); } @@ -405,7 +407,7 @@ function getSearchesFromDirectImports(directImports: Importer[], exportSymbol: S for (const element of namedBindings.elements) { const { name, propertyName } = element; - if (!isNameMatch((propertyName || name).escapedText)) { + if (!isNameMatch(moduleExportNameTextEscaped(propertyName || name))) { continue; } @@ -414,7 +416,7 @@ function getSearchesFromDirectImports(directImports: Importer[], exportSymbol: S singleReferences.push(propertyName); // If renaming `{ foo as bar }`, don't touch `bar`, just `foo`. // But do rename `foo` in ` { default as foo }` if that's the original export name. - if (!isForRename || name.escapedText === exportSymbol.escapedName) { + if (!isForRename || moduleExportNameTextEscaped(name) === exportSymbol.escapedName) { // Search locally for `bar`. addSearch(name, checker.getSymbolAtLocation(name)!); } diff --git a/src/services/organizeImports.ts b/src/services/organizeImports.ts index 573f908abf974..522cc946e6bbd 100644 --- a/src/services/organizeImports.ts +++ b/src/services/organizeImports.ts @@ -48,6 +48,7 @@ import { map, MemoizeCache, memoizeCached, + moduleExportNameTextEscaped, NamedImportBindings, NamedImports, NamespaceImport, @@ -763,7 +764,7 @@ function getImportKindOrder(s1: AnyImportOrRequireStatement) { function getNewImportSpecifiers(namedImports: ImportDeclaration[]) { return flatMap(namedImports, namedImport => map(tryGetNamedBindingElements(namedImport), importSpecifier => - importSpecifier.name && importSpecifier.propertyName && importSpecifier.name.escapedText === importSpecifier.propertyName.escapedText + importSpecifier.name && importSpecifier.propertyName && importSpecifier.name.escapedText === moduleExportNameTextEscaped(importSpecifier.propertyName) ? factory.updateImportSpecifier(importSpecifier, importSpecifier.isTypeOnly, /*propertyName*/ undefined, importSpecifier.name) : importSpecifier ) diff --git a/src/services/refactors/convertExport.ts b/src/services/refactors/convertExport.ts index 0509e1e0c512b..891c7621e014a 100644 --- a/src/services/refactors/convertExport.ts +++ b/src/services/refactors/convertExport.ts @@ -34,6 +34,7 @@ import { makeImport, ModifierFlags, ModuleBlock, + ModuleExportName, NamespaceDeclaration, Node, NodeFlags, @@ -232,7 +233,7 @@ function changeImports(program: Program, { wasDefault, exportName, exportingModu }); } -function changeDefaultToNamedImport(importingSourceFile: SourceFile, ref: Identifier, changes: textChanges.ChangeTracker, exportName: string): void { +function changeDefaultToNamedImport(importingSourceFile: SourceFile, ref: ModuleExportName, changes: textChanges.ChangeTracker, exportName: string): void { const { parent } = ref; switch (parent.kind) { case SyntaxKind.PropertyAccessExpression: @@ -278,7 +279,7 @@ function changeDefaultToNamedImport(importingSourceFile: SourceFile, ref: Identi } } -function changeNamedToDefaultImport(importingSourceFile: SourceFile, ref: Identifier, changes: textChanges.ChangeTracker): void { +function changeNamedToDefaultImport(importingSourceFile: SourceFile, ref: ModuleExportName, changes: textChanges.ChangeTracker): void { const parent = ref.parent as PropertyAccessExpression | ImportSpecifier | ExportSpecifier; switch (parent.kind) { case SyntaxKind.PropertyAccessExpression: diff --git a/src/testRunner/unittests/transform.ts b/src/testRunner/unittests/transform.ts index aa277bd21f536..9e9f001e3d0fd 100644 --- a/src/testRunner/unittests/transform.ts +++ b/src/testRunner/unittests/transform.ts @@ -282,7 +282,7 @@ describe("unittests:: TransformAPI", () => { if (node.kind === ts.SyntaxKind.ExportDeclaration) { const ed = node as ts.Node as ts.ExportDeclaration; const exports = [{ name: "x" }]; - const exportSpecifiers = exports.map(e => ts.factory.createExportSpecifier(/*isTypeOnly*/ false, e.name, e.name)); + const exportSpecifiers = exports.map(e => ts.factory.createExportSpecifier(/*isTypeOnly*/ false, ts.factory.createModuleExportName(e.name, ts.ScriptTarget.ESNext), ts.factory.createModuleExportName(e.name, ts.ScriptTarget.ESNext))); const exportClause = ts.factory.createNamedExports(exportSpecifiers); const newEd = ts.factory.updateExportDeclaration(ed, ed.modifiers, ed.isTypeOnly, exportClause, ed.moduleSpecifier, ed.assertClause); @@ -697,4 +697,3 @@ function test () { }).outputText; }); }); - diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 56c4b49ff6df2..dca8374261a2c 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -5752,7 +5752,7 @@ declare namespace ts { interface NamespaceExport extends NamedDeclaration { readonly kind: SyntaxKind.NamespaceExport; readonly parent: ExportDeclaration; - readonly name: Identifier; + readonly name: ModuleExportName; } interface NamespaceExportDeclaration extends DeclarationStatement, JSDocContainer { readonly kind: SyntaxKind.NamespaceExportDeclaration; @@ -5783,7 +5783,7 @@ declare namespace ts { interface ImportSpecifier extends NamedDeclaration { readonly kind: SyntaxKind.ImportSpecifier; readonly parent: NamedImports; - readonly propertyName?: Identifier; + readonly propertyName?: ModuleExportName; readonly name: Identifier; readonly isTypeOnly: boolean; } @@ -5791,9 +5791,10 @@ declare namespace ts { readonly kind: SyntaxKind.ExportSpecifier; readonly parent: NamedExports; readonly isTypeOnly: boolean; - readonly propertyName?: Identifier; - readonly name: Identifier; + readonly propertyName?: ModuleExportName; + readonly name: ModuleExportName; } + type ModuleExportName = Identifier | StringLiteral; type ImportOrExportSpecifier = ImportSpecifier | ExportSpecifier; type TypeOnlyCompatibleAliasDeclaration = ImportClause | ImportEqualsDeclaration | NamespaceImport | ImportOrExportSpecifier | ExportDeclaration | NamespaceExport; type TypeOnlyImportDeclaration = ImportClause & { @@ -7875,20 +7876,20 @@ declare namespace ts { updateImportTypeAssertionContainer(node: ImportTypeAssertionContainer, clause: AssertClause, multiLine?: boolean): ImportTypeAssertionContainer; createNamespaceImport(name: Identifier): NamespaceImport; updateNamespaceImport(node: NamespaceImport, name: Identifier): NamespaceImport; - createNamespaceExport(name: Identifier): NamespaceExport; - updateNamespaceExport(node: NamespaceExport, name: Identifier): NamespaceExport; + createNamespaceExport(name: ModuleExportName): NamespaceExport; + updateNamespaceExport(node: NamespaceExport, name: ModuleExportName): NamespaceExport; createNamedImports(elements: readonly ImportSpecifier[]): NamedImports; updateNamedImports(node: NamedImports, elements: readonly ImportSpecifier[]): NamedImports; - createImportSpecifier(isTypeOnly: boolean, propertyName: Identifier | undefined, name: Identifier): ImportSpecifier; - updateImportSpecifier(node: ImportSpecifier, isTypeOnly: boolean, propertyName: Identifier | undefined, name: Identifier): ImportSpecifier; + createImportSpecifier(isTypeOnly: boolean, propertyName: ModuleExportName | undefined, name: Identifier): ImportSpecifier; + updateImportSpecifier(node: ImportSpecifier, isTypeOnly: boolean, propertyName: ModuleExportName | undefined, name: Identifier): ImportSpecifier; createExportAssignment(modifiers: readonly ModifierLike[] | undefined, isExportEquals: boolean | undefined, expression: Expression): ExportAssignment; updateExportAssignment(node: ExportAssignment, modifiers: readonly ModifierLike[] | undefined, expression: Expression): ExportAssignment; createExportDeclaration(modifiers: readonly ModifierLike[] | undefined, isTypeOnly: boolean, exportClause: NamedExportBindings | undefined, moduleSpecifier?: Expression, assertClause?: AssertClause): ExportDeclaration; updateExportDeclaration(node: ExportDeclaration, modifiers: readonly ModifierLike[] | undefined, isTypeOnly: boolean, exportClause: NamedExportBindings | undefined, moduleSpecifier: Expression | undefined, assertClause: AssertClause | undefined): ExportDeclaration; createNamedExports(elements: readonly ExportSpecifier[]): NamedExports; updateNamedExports(node: NamedExports, elements: readonly ExportSpecifier[]): NamedExports; - createExportSpecifier(isTypeOnly: boolean, propertyName: string | Identifier | undefined, name: string | Identifier): ExportSpecifier; - updateExportSpecifier(node: ExportSpecifier, isTypeOnly: boolean, propertyName: Identifier | undefined, name: Identifier): ExportSpecifier; + createExportSpecifier(isTypeOnly: boolean, propertyName: ModuleExportName | undefined, name: ModuleExportName): ExportSpecifier; + updateExportSpecifier(node: ExportSpecifier, isTypeOnly: boolean, propertyName: ModuleExportName | undefined, name: ModuleExportName): ExportSpecifier; createExternalModuleReference(expression: Expression): ExternalModuleReference; updateExternalModuleReference(node: ExternalModuleReference, expression: Expression): ExternalModuleReference; createJSDocAllType(): JSDocAllType; @@ -8057,6 +8058,8 @@ declare namespace ts { createLogicalNot(operand: Expression): PrefixUnaryExpression; createPostfixIncrement(operand: Expression): PostfixUnaryExpression; createPostfixDecrement(operand: Expression): PostfixUnaryExpression; + createModuleExportName(name: string, languageVersion: ScriptTarget): ModuleExportName; + createModuleExportName(name: string | undefined, languageVersion: ScriptTarget): ModuleExportName | undefined; createImmediatelyInvokedFunctionExpression(statements: readonly Statement[]): CallExpression; createImmediatelyInvokedFunctionExpression(statements: readonly Statement[], param: ParameterDeclaration, paramValue: Expression): CallExpression; createImmediatelyInvokedArrowFunction(statements: readonly Statement[]): CallExpression; @@ -8824,6 +8827,9 @@ declare namespace ts { parent: ConstructorDeclaration; name: Identifier; }; + function getNamespaceDeclarationNode(node: ImportEqualsDeclaration): ImportEqualsDeclaration; + function getNamespaceDeclarationNode(node: ExportDeclaration): NamespaceExport | undefined; + function getNamespaceDeclarationNode(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration): ImportEqualsDeclaration | NamespaceImport | NamespaceExport | undefined; /** * This function checks multiple locations for JSDoc comments that apply to a host node. * At each location, the whole comment may apply to the node, or only a specific tag in @@ -8844,6 +8850,7 @@ declare namespace ts { * ``` */ function getJSDocCommentsAndTags(hostNode: Node): readonly (JSDoc | JSDocTag)[]; + function isModuleExportNameStringLiteral(node: __String | ModuleExportName | undefined): node is StringLiteral; /** @deprecated */ function createUnparsedSourceFile(text: string): UnparsedSource; /** @deprecated */ @@ -8950,6 +8957,7 @@ declare namespace ts { function isEqualsGreaterThanToken(node: Node): node is EqualsGreaterThanToken; function isIdentifier(node: Node): node is Identifier; function isPrivateIdentifier(node: Node): node is PrivateIdentifier; + function isModuleExportName(node: Node): node is ModuleExportName; function isAssertsKeyword(node: Node): node is AssertsKeyword; function isAwaitKeyword(node: Node): node is AwaitKeyword; function isQualifiedName(node: Node): node is QualifiedName; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 2b642a091ce2e..2df4a05d6a781 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -1699,7 +1699,7 @@ declare namespace ts { interface NamespaceExport extends NamedDeclaration { readonly kind: SyntaxKind.NamespaceExport; readonly parent: ExportDeclaration; - readonly name: Identifier; + readonly name: ModuleExportName; } interface NamespaceExportDeclaration extends DeclarationStatement, JSDocContainer { readonly kind: SyntaxKind.NamespaceExportDeclaration; @@ -1730,7 +1730,7 @@ declare namespace ts { interface ImportSpecifier extends NamedDeclaration { readonly kind: SyntaxKind.ImportSpecifier; readonly parent: NamedImports; - readonly propertyName?: Identifier; + readonly propertyName?: ModuleExportName; readonly name: Identifier; readonly isTypeOnly: boolean; } @@ -1738,9 +1738,10 @@ declare namespace ts { readonly kind: SyntaxKind.ExportSpecifier; readonly parent: NamedExports; readonly isTypeOnly: boolean; - readonly propertyName?: Identifier; - readonly name: Identifier; + readonly propertyName?: ModuleExportName; + readonly name: ModuleExportName; } + type ModuleExportName = Identifier | StringLiteral; type ImportOrExportSpecifier = ImportSpecifier | ExportSpecifier; type TypeOnlyCompatibleAliasDeclaration = ImportClause | ImportEqualsDeclaration | NamespaceImport | ImportOrExportSpecifier | ExportDeclaration | NamespaceExport; type TypeOnlyImportDeclaration = ImportClause & { @@ -3822,20 +3823,20 @@ declare namespace ts { updateImportTypeAssertionContainer(node: ImportTypeAssertionContainer, clause: AssertClause, multiLine?: boolean): ImportTypeAssertionContainer; createNamespaceImport(name: Identifier): NamespaceImport; updateNamespaceImport(node: NamespaceImport, name: Identifier): NamespaceImport; - createNamespaceExport(name: Identifier): NamespaceExport; - updateNamespaceExport(node: NamespaceExport, name: Identifier): NamespaceExport; + createNamespaceExport(name: ModuleExportName): NamespaceExport; + updateNamespaceExport(node: NamespaceExport, name: ModuleExportName): NamespaceExport; createNamedImports(elements: readonly ImportSpecifier[]): NamedImports; updateNamedImports(node: NamedImports, elements: readonly ImportSpecifier[]): NamedImports; - createImportSpecifier(isTypeOnly: boolean, propertyName: Identifier | undefined, name: Identifier): ImportSpecifier; - updateImportSpecifier(node: ImportSpecifier, isTypeOnly: boolean, propertyName: Identifier | undefined, name: Identifier): ImportSpecifier; + createImportSpecifier(isTypeOnly: boolean, propertyName: ModuleExportName | undefined, name: Identifier): ImportSpecifier; + updateImportSpecifier(node: ImportSpecifier, isTypeOnly: boolean, propertyName: ModuleExportName | undefined, name: Identifier): ImportSpecifier; createExportAssignment(modifiers: readonly ModifierLike[] | undefined, isExportEquals: boolean | undefined, expression: Expression): ExportAssignment; updateExportAssignment(node: ExportAssignment, modifiers: readonly ModifierLike[] | undefined, expression: Expression): ExportAssignment; createExportDeclaration(modifiers: readonly ModifierLike[] | undefined, isTypeOnly: boolean, exportClause: NamedExportBindings | undefined, moduleSpecifier?: Expression, assertClause?: AssertClause): ExportDeclaration; updateExportDeclaration(node: ExportDeclaration, modifiers: readonly ModifierLike[] | undefined, isTypeOnly: boolean, exportClause: NamedExportBindings | undefined, moduleSpecifier: Expression | undefined, assertClause: AssertClause | undefined): ExportDeclaration; createNamedExports(elements: readonly ExportSpecifier[]): NamedExports; updateNamedExports(node: NamedExports, elements: readonly ExportSpecifier[]): NamedExports; - createExportSpecifier(isTypeOnly: boolean, propertyName: string | Identifier | undefined, name: string | Identifier): ExportSpecifier; - updateExportSpecifier(node: ExportSpecifier, isTypeOnly: boolean, propertyName: Identifier | undefined, name: Identifier): ExportSpecifier; + createExportSpecifier(isTypeOnly: boolean, propertyName: ModuleExportName | undefined, name: ModuleExportName): ExportSpecifier; + updateExportSpecifier(node: ExportSpecifier, isTypeOnly: boolean, propertyName: ModuleExportName | undefined, name: ModuleExportName): ExportSpecifier; createExternalModuleReference(expression: Expression): ExternalModuleReference; updateExternalModuleReference(node: ExternalModuleReference, expression: Expression): ExternalModuleReference; createJSDocAllType(): JSDocAllType; @@ -4004,6 +4005,8 @@ declare namespace ts { createLogicalNot(operand: Expression): PrefixUnaryExpression; createPostfixIncrement(operand: Expression): PostfixUnaryExpression; createPostfixDecrement(operand: Expression): PostfixUnaryExpression; + createModuleExportName(name: string, languageVersion: ScriptTarget): ModuleExportName; + createModuleExportName(name: string | undefined, languageVersion: ScriptTarget): ModuleExportName | undefined; createImmediatelyInvokedFunctionExpression(statements: readonly Statement[]): CallExpression; createImmediatelyInvokedFunctionExpression(statements: readonly Statement[], param: ParameterDeclaration, paramValue: Expression): CallExpression; createImmediatelyInvokedArrowFunction(statements: readonly Statement[]): CallExpression; @@ -4771,6 +4774,9 @@ declare namespace ts { parent: ConstructorDeclaration; name: Identifier; }; + function getNamespaceDeclarationNode(node: ImportEqualsDeclaration): ImportEqualsDeclaration; + function getNamespaceDeclarationNode(node: ExportDeclaration): NamespaceExport | undefined; + function getNamespaceDeclarationNode(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration): ImportEqualsDeclaration | NamespaceImport | NamespaceExport | undefined; /** * This function checks multiple locations for JSDoc comments that apply to a host node. * At each location, the whole comment may apply to the node, or only a specific tag in @@ -4791,6 +4797,7 @@ declare namespace ts { * ``` */ function getJSDocCommentsAndTags(hostNode: Node): readonly (JSDoc | JSDocTag)[]; + function isModuleExportNameStringLiteral(node: __String | ModuleExportName | undefined): node is StringLiteral; /** @deprecated */ function createUnparsedSourceFile(text: string): UnparsedSource; /** @deprecated */ @@ -4897,6 +4904,7 @@ declare namespace ts { function isEqualsGreaterThanToken(node: Node): node is EqualsGreaterThanToken; function isIdentifier(node: Node): node is Identifier; function isPrivateIdentifier(node: Node): node is PrivateIdentifier; + function isModuleExportName(node: Node): node is ModuleExportName; function isAssertsKeyword(node: Node): node is AssertsKeyword; function isAwaitKeyword(node: Node): node is AwaitKeyword; function isQualifiedName(node: Node): node is QualifiedName; diff --git a/tests/baselines/reference/goToDefinitionImportedNames12.baseline.jsonc b/tests/baselines/reference/goToDefinitionImportedNames12.baseline.jsonc new file mode 100644 index 0000000000000..7553c9dd0acf9 --- /dev/null +++ b/tests/baselines/reference/goToDefinitionImportedNames12.baseline.jsonc @@ -0,0 +1,103 @@ +// === goToDefinition === +// === /tests/cases/fourslash/a.ts === +// <|const [|a|] = 1;|> +// export { a as " b " }; + +// === /tests/cases/fourslash/e.ts === +// import { c } from "./d"; +// /*GOTO DEF*/[|c|]; + + // === Details === + [ + { + "kind": "const", + "name": "a", + "containerName": "", + "isLocal": true, + "isAmbient": false, + "unverified": false + } + ] + + + +// === goToDefinition === +// === /tests/cases/fourslash/a.ts === +// <|const [|a|] = 1;|> +// export { a as " b " }; + +// === /tests/cases/fourslash/c.ts === +// export { /*GOTO DEF*/[|" b "|] as c } from "./b"; + + // === Details === + [ + { + "kind": "const", + "name": "a", + "containerName": "", + "isLocal": true, + "isAmbient": false, + "unverified": false + } + ] + + + +// === goToDefinition === +// === /tests/cases/fourslash/a.ts === +// <|const [|a|] = 1;|> +// export { a as " b " }; + +// === /tests/cases/fourslash/c.ts === +// export { " b " as /*GOTO DEF*/[|c|] } from "./b"; + + // === Details === + [ + { + "kind": "const", + "name": "a", + "containerName": "", + "isLocal": true, + "isAmbient": false, + "unverified": false + } + ] + + + +// === goToDefinition === +// === /tests/cases/fourslash/a.ts === +// <|const [|a|] = 1;|> +// export { /*GOTO DEF*/a as " b " }; + + // === Details === + [ + { + "kind": "const", + "name": "a", + "containerName": "", + "isLocal": true, + "isAmbient": false, + "unverified": false, + "failedAliasResolution": false + } + ] + + + +// === goToDefinition === +// === /tests/cases/fourslash/a.ts === +// <|const [|a|] = 1;|> +// export { a as /*GOTO DEF*/" b " }; + + // === Details === + [ + { + "kind": "const", + "name": "a", + "containerName": "", + "isLocal": true, + "isAmbient": false, + "unverified": false + } + ] \ No newline at end of file diff --git a/tests/baselines/reference/unclosedExportClause01.errors.txt b/tests/baselines/reference/unclosedExportClause01.errors.txt index ec3ef3cf322f0..825ad6a1b3c81 100644 --- a/tests/baselines/reference/unclosedExportClause01.errors.txt +++ b/tests/baselines/reference/unclosedExportClause01.errors.txt @@ -1,43 +1,28 @@ -t2.ts(1,13): error TS2305: Module '"./t1"' has no exported member 'from'. -t2.ts(1,18): error TS1005: ',' expected. -t3.ts(1,10): error TS2305: Module '"./t1"' has no exported member 'from'. -t3.ts(1,15): error TS1005: ',' expected. +t2.ts(1,13): error TS1005: '}' expected. +t3.ts(1,10): error TS1005: '}' expected. t4.ts(1,17): error TS1005: ',' expected. -t4.ts(1,17): error TS2305: Module '"./t1"' has no exported member 'from'. -t4.ts(1,22): error TS1005: ',' expected. -t5.ts(1,18): error TS2305: Module '"./t1"' has no exported member 'from'. -t5.ts(1,23): error TS1005: ',' expected. +t5.ts(1,18): error TS1005: '}' expected. ==== t1.ts (0 errors) ==== export var x = "x"; -==== t2.ts (2 errors) ==== +==== t2.ts (1 errors) ==== export { x, from "./t1" ~~~~ -!!! error TS2305: Module '"./t1"' has no exported member 'from'. - ~~~~~~ -!!! error TS1005: ',' expected. +!!! error TS1005: '}' expected. -==== t3.ts (2 errors) ==== +==== t3.ts (1 errors) ==== export { from "./t1" ~~~~ -!!! error TS2305: Module '"./t1"' has no exported member 'from'. - ~~~~~~ -!!! error TS1005: ',' expected. +!!! error TS1005: '}' expected. -==== t4.ts (3 errors) ==== +==== t4.ts (1 errors) ==== export { x as a from "./t1" ~~~~ !!! error TS1005: ',' expected. - ~~~~ -!!! error TS2305: Module '"./t1"' has no exported member 'from'. - ~~~~~~ -!!! error TS1005: ',' expected. -==== t5.ts (2 errors) ==== +==== t5.ts (1 errors) ==== export { x as a, from "./t1" ~~~~ -!!! error TS2305: Module '"./t1"' has no exported member 'from'. - ~~~~~~ -!!! error TS1005: ',' expected. \ No newline at end of file +!!! error TS1005: '}' expected. \ No newline at end of file diff --git a/tests/baselines/reference/unclosedExportClause01.js b/tests/baselines/reference/unclosedExportClause01.js index ef0d2a5be634c..a7b46515a687d 100644 --- a/tests/baselines/reference/unclosedExportClause01.js +++ b/tests/baselines/reference/unclosedExportClause01.js @@ -23,27 +23,21 @@ exports.x = "x"; //// [t2.js] "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.from = exports.x = void 0; +exports.x = void 0; var t1_1 = require("./t1"); Object.defineProperty(exports, "x", { enumerable: true, get: function () { return t1_1.x; } }); -Object.defineProperty(exports, "from", { enumerable: true, get: function () { return t1_1.from; } }); //// [t3.js] "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.from = void 0; -var t1_1 = require("./t1"); -Object.defineProperty(exports, "from", { enumerable: true, get: function () { return t1_1.from; } }); //// [t4.js] "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.from = exports.a = void 0; +exports.a = void 0; var t1_1 = require("./t1"); Object.defineProperty(exports, "a", { enumerable: true, get: function () { return t1_1.x; } }); -Object.defineProperty(exports, "from", { enumerable: true, get: function () { return t1_1.from; } }); //// [t5.js] "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.from = exports.a = void 0; +exports.a = void 0; var t1_1 = require("./t1"); Object.defineProperty(exports, "a", { enumerable: true, get: function () { return t1_1.x; } }); -Object.defineProperty(exports, "from", { enumerable: true, get: function () { return t1_1.from; } }); diff --git a/tests/baselines/reference/unclosedExportClause01.symbols b/tests/baselines/reference/unclosedExportClause01.symbols index 65cfcc297adc8..9b7f85697b97b 100644 --- a/tests/baselines/reference/unclosedExportClause01.symbols +++ b/tests/baselines/reference/unclosedExportClause01.symbols @@ -7,21 +7,18 @@ export var x = "x"; === t2.ts === export { x, from "./t1" >x : Symbol(x, Decl(t2.ts, 0, 8)) ->from : Symbol(from, Decl(t2.ts, 0, 11)) === t3.ts === + export { from "./t1" ->from : Symbol(from, Decl(t3.ts, 0, 8)) === t4.ts === export { x as a from "./t1" >x : Symbol(x, Decl(t1.ts, 0, 10)) >a : Symbol(a, Decl(t4.ts, 0, 8)) ->from : Symbol(from, Decl(t4.ts, 0, 15)) === t5.ts === export { x as a, from "./t1" >x : Symbol(x, Decl(t1.ts, 0, 10)) >a : Symbol(a, Decl(t5.ts, 0, 8)) ->from : Symbol(from, Decl(t5.ts, 0, 16)) diff --git a/tests/baselines/reference/unclosedExportClause01.types b/tests/baselines/reference/unclosedExportClause01.types index b9a286a5ab49f..a4b897f9091b0 100644 --- a/tests/baselines/reference/unclosedExportClause01.types +++ b/tests/baselines/reference/unclosedExportClause01.types @@ -8,21 +8,18 @@ export var x = "x"; === t2.ts === export { x, from "./t1" >x : string ->from : any === t3.ts === + export { from "./t1" ->from : any === t4.ts === export { x as a from "./t1" >x : string >a : string ->from : any === t5.ts === export { x as a, from "./t1" >x : string >a : string ->from : any diff --git a/tests/baselines/reference/unclosedExportClause02.errors.txt b/tests/baselines/reference/unclosedExportClause02.errors.txt index 930ebdc40a0e6..2f1978e46bc39 100644 --- a/tests/baselines/reference/unclosedExportClause02.errors.txt +++ b/tests/baselines/reference/unclosedExportClause02.errors.txt @@ -1,56 +1,32 @@ -t2.ts(1,10): error TS2304: Cannot find name 'x'. -t2.ts(1,13): error TS2304: Cannot find name 'from'. -t2.ts(2,5): error TS1005: ',' expected. -t3.ts(1,10): error TS2304: Cannot find name 'from'. -t3.ts(2,5): error TS1005: ',' expected. -t4.ts(1,10): error TS2304: Cannot find name 'x'. +t2.ts(1,13): error TS1005: '}' expected. +t3.ts(1,10): error TS1005: '}' expected. t4.ts(1,17): error TS1005: ',' expected. -t4.ts(1,17): error TS2304: Cannot find name 'from'. -t4.ts(2,5): error TS1005: ',' expected. -t5.ts(1,10): error TS2304: Cannot find name 'x'. -t5.ts(1,18): error TS2304: Cannot find name 'from'. -t5.ts(2,5): error TS1005: ',' expected. +t5.ts(1,18): error TS1005: '}' expected. ==== t1.ts (0 errors) ==== export var x = "x"; -==== t2.ts (3 errors) ==== +==== t2.ts (1 errors) ==== export { x, from - ~ -!!! error TS2304: Cannot find name 'x'. ~~~~ -!!! error TS2304: Cannot find name 'from'. +!!! error TS1005: '}' expected. "./t1"; - ~~~~~~ -!!! error TS1005: ',' expected. -==== t3.ts (2 errors) ==== +==== t3.ts (1 errors) ==== export { from ~~~~ -!!! error TS2304: Cannot find name 'from'. +!!! error TS1005: '}' expected. "./t1"; - ~~~~~~ -!!! error TS1005: ',' expected. -==== t4.ts (4 errors) ==== +==== t4.ts (1 errors) ==== export { x as a from - ~ -!!! error TS2304: Cannot find name 'x'. ~~~~ !!! error TS1005: ',' expected. - ~~~~ -!!! error TS2304: Cannot find name 'from'. "./t1"; - ~~~~~~ -!!! error TS1005: ',' expected. -==== t5.ts (3 errors) ==== +==== t5.ts (1 errors) ==== export { x as a, from - ~ -!!! error TS2304: Cannot find name 'x'. ~~~~ -!!! error TS2304: Cannot find name 'from'. - "./t1"; - ~~~~~~ -!!! error TS1005: ',' expected. \ No newline at end of file +!!! error TS1005: '}' expected. + "./t1"; \ No newline at end of file diff --git a/tests/baselines/reference/unclosedExportClause02.js b/tests/baselines/reference/unclosedExportClause02.js index d553c74911d5f..3403afdac989e 100644 --- a/tests/baselines/reference/unclosedExportClause02.js +++ b/tests/baselines/reference/unclosedExportClause02.js @@ -27,20 +27,21 @@ exports.x = "x"; //// [t2.js] "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.from = exports.x = void 0; -"./t1"; +exports.x = void 0; +var t1_1 = require("./t1"); +Object.defineProperty(exports, "x", { enumerable: true, get: function () { return t1_1.x; } }); //// [t3.js] "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.from = void 0; -"./t1"; //// [t4.js] "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.from = exports.a = void 0; -"./t1"; +exports.a = void 0; +var t1_1 = require("./t1"); +Object.defineProperty(exports, "a", { enumerable: true, get: function () { return t1_1.x; } }); //// [t5.js] "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.from = exports.a = void 0; -"./t1"; +exports.a = void 0; +var t1_1 = require("./t1"); +Object.defineProperty(exports, "a", { enumerable: true, get: function () { return t1_1.x; } }); diff --git a/tests/baselines/reference/unclosedExportClause02.symbols b/tests/baselines/reference/unclosedExportClause02.symbols index fa45dc4a5f2b8..a528768936790 100644 --- a/tests/baselines/reference/unclosedExportClause02.symbols +++ b/tests/baselines/reference/unclosedExportClause02.symbols @@ -7,26 +7,24 @@ export var x = "x"; === t2.ts === export { x, from >x : Symbol(x, Decl(t2.ts, 0, 8)) ->from : Symbol(from, Decl(t2.ts, 0, 11)) "./t1"; === t3.ts === -export { from ->from : Symbol(from, Decl(t3.ts, 0, 8)) +export { from "./t1"; === t4.ts === export { x as a from +>x : Symbol(x, Decl(t1.ts, 0, 10)) >a : Symbol(a, Decl(t4.ts, 0, 8)) ->from : Symbol(from, Decl(t4.ts, 0, 15)) "./t1"; === t5.ts === export { x as a, from +>x : Symbol(x, Decl(t1.ts, 0, 10)) >a : Symbol(a, Decl(t5.ts, 0, 8)) ->from : Symbol(from, Decl(t5.ts, 0, 16)) "./t1"; diff --git a/tests/baselines/reference/unclosedExportClause02.types b/tests/baselines/reference/unclosedExportClause02.types index 0ddbdbbc6db77..9588153e06c53 100644 --- a/tests/baselines/reference/unclosedExportClause02.types +++ b/tests/baselines/reference/unclosedExportClause02.types @@ -7,34 +7,25 @@ export var x = "x"; === t2.ts === export { x, from ->x : any ->from : any +>x : string "./t1"; ->"./t1" : "./t1" === t3.ts === -export { from ->from : any +export { from "./t1"; ->"./t1" : "./t1" === t4.ts === export { x as a from ->x : any ->a : any ->from : any +>x : string +>a : string "./t1"; ->"./t1" : "./t1" === t5.ts === export { x as a, from ->x : any ->a : any ->from : any +>x : string +>a : string "./t1"; ->"./t1" : "./t1" - diff --git a/tests/cases/fourslash/goToDefinitionImportedNames12.ts b/tests/cases/fourslash/goToDefinitionImportedNames12.ts new file mode 100644 index 0000000000000..06aad211cd22c --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionImportedNames12.ts @@ -0,0 +1,24 @@ +/// + +// @Filename: e.ts +//// import { c } from "./d"; +//// /*ref1*/c; + + +// @Filename: d.ts +////export * from "./c"; + + +// @Filename: c.ts +////export { /*ref2*/" b " as /*ref3*/c } from "./b"; + + +// @Filename: b.ts +////export * from "./a"; + + +// @Filename: a.ts +////const a = /*varDefinition*/1; +////export { /*ref4*/a as /*ref5*/" b " }; + +verify.baselineGoToDefinition('ref1', 'ref2', 'ref3', 'ref4', 'ref5')