Skip to content

Commit 026fba8

Browse files
committed
add support for ts2154 in the compiler
1 parent 818c980 commit 026fba8

17 files changed

+418
-127
lines changed

src/compiler/binder.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,8 @@ import {
243243
ModifierFlags,
244244
ModuleBlock,
245245
ModuleDeclaration,
246+
moduleExportNameText,
247+
moduleExportNameTextEscaped,
246248
Mutable,
247249
NamespaceExportDeclaration,
248250
Node,
@@ -432,7 +434,7 @@ function getModuleInstanceStateForAliasTarget(specifier: ExportSpecifier, visite
432434
const statements = p.statements;
433435
let found: ModuleInstanceState | undefined;
434436
for (const statement of statements) {
435-
if (nodeHasName(statement, name)) {
437+
if (nodeHasName(statement, moduleExportNameText(name))) {
436438
if (!statement.parent) {
437439
setParent(statement, p);
438440
setParentRecursive(statement, /*incremental*/ false);
@@ -738,7 +740,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
738740
function declareSymbol(symbolTable: SymbolTable, parent: Symbol | undefined, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags, isReplaceableByMethod?: boolean, isComputedName?: boolean): Symbol {
739741
Debug.assert(isComputedName || !hasDynamicName(node));
740742

741-
const isDefaultExport = hasSyntacticModifier(node, ModifierFlags.Default) || isExportSpecifier(node) && node.name.escapedText === "default";
743+
const isDefaultExport = hasSyntacticModifier(node, ModifierFlags.Default) || isExportSpecifier(node) && moduleExportNameTextEscaped(node.name) === "default";
742744

743745
// The exported symbol for an export default function/class node is always named "default"
744746
const name = isComputedName ? InternalSymbolName.Computed

src/compiler/checker.ts

Lines changed: 101 additions & 47 deletions
Large diffs are not rendered by default.

src/compiler/diagnosticMessages.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,6 +1609,10 @@
16091609
"category": "Error",
16101610
"code": 1490
16111611
},
1612+
"String literal module export names are not allowed when the 'module' option is set to 'es2020' or lower.": {
1613+
"category": "Error",
1614+
"code": 1491
1615+
},
16121616

16131617
"The types of '{0}' are incompatible between these types.": {
16141618
"category": "Error",
@@ -1667,6 +1671,10 @@
16671671
"category": "Message",
16681672
"code": 2212
16691673
},
1674+
"String literal module export names must be followed by a 'from' clause.": {
1675+
"category": "Error",
1676+
"code": 2213
1677+
},
16701678

16711679
"Duplicate identifier '{0}'.": {
16721680
"category": "Error",

src/compiler/factory/nodeFactory.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ import {
174174
isHoistedFunction,
175175
isHoistedVariableStatement,
176176
isIdentifier,
177+
isIdentifierText,
177178
isImportDeclaration,
178179
isImportEqualsDeclaration,
179180
isImportKeyword,
@@ -312,6 +313,7 @@ import {
312313
ModuleBlock,
313314
ModuleBody,
314315
ModuleDeclaration,
316+
ModuleExportName,
315317
ModuleKind,
316318
ModuleName,
317319
ModuleReference,
@@ -982,6 +984,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
982984
get createLogicalNot() { return getPrefixUnaryCreateFunction(SyntaxKind.ExclamationToken); },
983985
get createPostfixIncrement() { return getPostfixUnaryCreateFunction(SyntaxKind.PlusPlusToken); },
984986
get createPostfixDecrement() { return getPostfixUnaryCreateFunction(SyntaxKind.MinusMinusToken); },
987+
createModuleExportName,
985988

986989
// Compound nodes
987990
createImmediatelyInvokedFunctionExpression,
@@ -4711,7 +4714,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
47114714
}
47124715

47134716
// @api
4714-
function createNamespaceExport(name: Identifier): NamespaceExport {
4717+
function createNamespaceExport(name: ModuleExportName): NamespaceExport {
47154718
const node = createBaseDeclaration<NamespaceExport>(SyntaxKind.NamespaceExport);
47164719
node.name = name;
47174720
node.transformFlags |=
@@ -4722,7 +4725,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
47224725
}
47234726

47244727
// @api
4725-
function updateNamespaceExport(node: NamespaceExport, name: Identifier) {
4728+
function updateNamespaceExport(node: NamespaceExport, name: ModuleExportName) {
47264729
return node.name !== name
47274730
? update(createNamespaceExport(name), node)
47284731
: node;
@@ -4745,7 +4748,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
47454748
}
47464749

47474750
// @api
4748-
function createImportSpecifier(isTypeOnly: boolean, propertyName: Identifier | undefined, name: Identifier) {
4751+
function createImportSpecifier(isTypeOnly: boolean, propertyName: ModuleExportName | undefined, name: Identifier) {
47494752
const node = createBaseDeclaration<ImportSpecifier>(SyntaxKind.ImportSpecifier);
47504753
node.isTypeOnly = isTypeOnly;
47514754
node.propertyName = propertyName;
@@ -4758,7 +4761,15 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
47584761
}
47594762

47604763
// @api
4761-
function updateImportSpecifier(node: ImportSpecifier, isTypeOnly: boolean, propertyName: Identifier | undefined, name: Identifier) {
4764+
function createModuleExportName(name: string, languageVersion: ScriptTarget): ModuleExportName;
4765+
function createModuleExportName(name: string | undefined, languageVersion: ScriptTarget): ModuleExportName | undefined;
4766+
function createModuleExportName(name: string | undefined, languageVersion: ScriptTarget): ModuleExportName | undefined {
4767+
if (name === undefined) return undefined;
4768+
return isIdentifierText(name, languageVersion) ? createIdentifier(name) : createStringLiteral(name);
4769+
}
4770+
4771+
// @api
4772+
function updateImportSpecifier(node: ImportSpecifier, isTypeOnly: boolean, propertyName: ModuleExportName | undefined, name: Identifier) {
47624773
return node.isTypeOnly !== isTypeOnly
47634774
|| node.propertyName !== propertyName
47644775
|| node.name !== name
@@ -4866,11 +4877,11 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
48664877
}
48674878

48684879
// @api
4869-
function createExportSpecifier(isTypeOnly: boolean, propertyName: string | Identifier | undefined, name: string | Identifier) {
4880+
function createExportSpecifier(isTypeOnly: boolean, propertyName: ModuleExportName | undefined, name: ModuleExportName) {
48704881
const node = createBaseNode<ExportSpecifier>(SyntaxKind.ExportSpecifier);
48714882
node.isTypeOnly = isTypeOnly;
4872-
node.propertyName = asName(propertyName);
4873-
node.name = asName(name);
4883+
node.propertyName = propertyName;
4884+
node.name = name;
48744885
node.transformFlags |=
48754886
propagateChildFlags(node.propertyName) |
48764887
propagateChildFlags(node.name);
@@ -4881,7 +4892,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
48814892
}
48824893

48834894
// @api
4884-
function updateExportSpecifier(node: ExportSpecifier, isTypeOnly: boolean, propertyName: Identifier | undefined, name: Identifier) {
4895+
function updateExportSpecifier(node: ExportSpecifier, isTypeOnly: boolean, propertyName: ModuleExportName | undefined, name: ModuleExportName) {
48854896
return node.isTypeOnly !== isTypeOnly
48864897
|| node.propertyName !== propertyName
48874898
|| node.name !== name

src/compiler/factory/nodeTests.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ import {
143143
MissingDeclaration,
144144
ModuleBlock,
145145
ModuleDeclaration,
146+
ModuleExportName,
146147
NamedExports,
147148
NamedImports,
148149
NamedTupleMember,
@@ -321,6 +322,10 @@ export function isPrivateIdentifier(node: Node): node is PrivateIdentifier {
321322
return node.kind === SyntaxKind.PrivateIdentifier;
322323
}
323324

325+
export function isModuleExportName(node: Node): node is ModuleExportName {
326+
return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.StringLiteral;
327+
}
328+
324329
// Reserved Words
325330

326331
/** @internal */

src/compiler/factory/utilities.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -792,15 +792,19 @@ export function getOrCreateExternalHelpersModuleNameIfNeeded(factory: NodeFactor
792792
}
793793

794794
/**
795-
* Get the name of that target module from an import or export declaration
795+
* Get the name of that target module from an import or export declaration.
796+
*
797+
* This is only used in AMD and SystemJS emit.
796798
*
797799
* @internal
798800
*/
799801
export function getLocalNameForExternalImport(factory: NodeFactory, node: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration, sourceFile: SourceFile): Identifier | undefined {
800802
const namespaceDeclaration = getNamespaceDeclarationNode(node);
801803
if (namespaceDeclaration && !isDefaultImport(node) && !isExportNamespaceAsDefaultDeclaration(node)) {
802804
const name = namespaceDeclaration.name;
803-
return isGeneratedIdentifier(name) ? name : factory.createIdentifier(getSourceTextOfNodeFromSourceFile(sourceFile, name) || idText(name));
805+
if (isGeneratedIdentifier(name)) return name;
806+
if (name.kind === SyntaxKind.StringLiteral) return factory.getGeneratedNameForNode(name);
807+
return factory.createIdentifier(getSourceTextOfNodeFromSourceFile(sourceFile, name) || idText(name));
804808
}
805809
if (node.kind === SyntaxKind.ImportDeclaration && node.importClause) {
806810
return factory.getGeneratedNameForNode(node);

src/compiler/parser.ts

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ import {
253253
modifiersToFlags,
254254
ModuleBlock,
255255
ModuleDeclaration,
256+
ModuleExportName,
256257
ModuleKind,
257258
Mutable,
258259
NamedExportBindings,
@@ -2867,7 +2868,7 @@ namespace Parser {
28672868
case ParsingContext.HeritageClauses:
28682869
return isHeritageClause();
28692870
case ParsingContext.ImportOrExportSpecifiers:
2870-
return tokenIsIdentifierOrKeyword(token());
2871+
return token() === SyntaxKind.StringLiteral || tokenIsIdentifierOrKeyword(token());
28712872
case ParsingContext.JsxAttributes:
28722873
return tokenIsIdentifierOrKeyword(token()) || token() === SyntaxKind.OpenBraceToken;
28732874
case ParsingContext.JsxChildren:
@@ -8331,27 +8332,32 @@ namespace Parser {
83318332

83328333
function parseImportOrExportSpecifier(kind: SyntaxKind): ImportOrExportSpecifier {
83338334
const pos = getNodePos();
8335+
// ModuleExportName:
8336+
// Identifier
8337+
// StringLiteral
83348338
// ImportSpecifier:
83358339
// BindingIdentifier
8336-
// IdentifierName as BindingIdentifier
8340+
// ModuleExportName as BindingIdentifier
83378341
// ExportSpecifier:
8338-
// IdentifierName
8339-
// IdentifierName as IdentifierName
8342+
// ModuleExportName
8343+
// ModuleExportName as ModuleExportName
83408344
let checkIdentifierIsKeyword = isKeyword(token()) && !isIdentifier();
83418345
let checkIdentifierStart = scanner.getTokenStart();
83428346
let checkIdentifierEnd = scanner.getTokenEnd();
83438347
let isTypeOnly = false;
8344-
let propertyName: Identifier | undefined;
8348+
let propertyName: ModuleExportName | undefined;
83458349
let canParseAsKeyword = true;
8346-
let name = parseIdentifierName();
8347-
if (name.escapedText === "type") {
8350+
let mustParseAsKeyword = false;
8351+
let name = parseModuleExportName(parseIdentifierName);
8352+
if (name.kind === SyntaxKind.Identifier && name.escapedText === "type") {
83488353
// If the first token of an import specifier is 'type', there are a lot of possibilities,
83498354
// especially if we see 'as' afterwards:
83508355
//
83518356
// import { type } from "mod"; - isTypeOnly: false, name: type
83528357
// import { type as } from "mod"; - isTypeOnly: true, name: as
83538358
// import { type as as } from "mod"; - isTypeOnly: false, name: as, propertyName: type
83548359
// import { type as as as } from "mod"; - isTypeOnly: true, name: as, propertyName: as
8360+
// export { type as as "s" } from "mod";- isTypeOnly: true, name: "s", propertyName: as
83558361
if (token() === SyntaxKind.AsKeyword) {
83568362
// { type as ...? }
83578363
const firstAs = parseIdentifierName();
@@ -8360,9 +8366,10 @@ namespace Parser {
83608366
const secondAs = parseIdentifierName();
83618367
if (tokenIsIdentifierOrKeyword(token())) {
83628368
// { type as as something }
8369+
// { type as as "something" } (only in exports)
83638370
isTypeOnly = true;
83648371
propertyName = firstAs;
8365-
name = parseNameWithKeywordCheck();
8372+
name = parseModuleExportNameOnlyForExports();
83668373
canParseAsKeyword = false;
83678374
}
83688375
else {
@@ -8376,31 +8383,43 @@ namespace Parser {
83768383
// { type as something }
83778384
propertyName = name;
83788385
canParseAsKeyword = false;
8379-
name = parseNameWithKeywordCheck();
8386+
name = parseModuleExportNameOnlyForExports();
83808387
}
83818388
else {
83828389
// { type as }
83838390
isTypeOnly = true;
83848391
name = firstAs;
83858392
}
83868393
}
8394+
// export { type "x" }
8395+
// import { type "x" as ... }
8396+
else if (token() === SyntaxKind.StringLiteral) {
8397+
isTypeOnly = true;
8398+
if (kind === SyntaxKind.ImportSpecifier) mustParseAsKeyword = true;
8399+
name = parseModuleExportName(parseNameWithKeywordCheck);
8400+
}
83878401
else if (tokenIsIdentifierOrKeyword(token())) {
83888402
// { type something ...? }
83898403
isTypeOnly = true;
83908404
name = parseNameWithKeywordCheck();
83918405
}
83928406
}
8407+
// import { "x" as ... }
8408+
else if (kind === SyntaxKind.ImportSpecifier && name.kind === SyntaxKind.StringLiteral) {
8409+
mustParseAsKeyword = true;
8410+
}
83938411

8394-
if (canParseAsKeyword && token() === SyntaxKind.AsKeyword) {
8412+
if (mustParseAsKeyword || (canParseAsKeyword && token() === SyntaxKind.AsKeyword)) {
83958413
propertyName = name;
83968414
parseExpected(SyntaxKind.AsKeyword);
8397-
name = parseNameWithKeywordCheck();
8415+
name = parseModuleExportNameOnlyForExports();
83988416
}
83998417
if (kind === SyntaxKind.ImportSpecifier && checkIdentifierIsKeyword) {
84008418
parseErrorAt(checkIdentifierStart, checkIdentifierEnd, Diagnostics.Identifier_expected);
84018419
}
8420+
if (kind === SyntaxKind.ImportSpecifier) Debug.assert(name.kind === SyntaxKind.Identifier);
84028421
const node = kind === SyntaxKind.ImportSpecifier
8403-
? factory.createImportSpecifier(isTypeOnly, propertyName, name)
8422+
? factory.createImportSpecifier(isTypeOnly, propertyName, name as Identifier)
84048423
: factory.createExportSpecifier(isTypeOnly, propertyName, name);
84058424
return finishNode(node, pos);
84068425

@@ -8410,10 +8429,22 @@ namespace Parser {
84108429
checkIdentifierEnd = scanner.getTokenEnd();
84118430
return parseIdentifierName();
84128431
}
8432+
function parseModuleExportNameOnlyForExports() {
8433+
if (kind === SyntaxKind.ImportSpecifier) return parseNameWithKeywordCheck();
8434+
return parseModuleExportName(parseNameWithKeywordCheck);
8435+
}
8436+
function parseModuleExportName(parser: () => Identifier): ModuleExportName {
8437+
if (token() === SyntaxKind.StringLiteral) return parseStringLiteral();
8438+
return parser();
8439+
}
8440+
function parseStringLiteral(): StringLiteral {
8441+
// TODO: the spec requires it pass IsStringWellFormedUnicode
8442+
return parseLiteralLikeNode(SyntaxKind.StringLiteral) as StringLiteral;
8443+
}
84138444
}
84148445

84158446
function parseNamespaceExport(pos: number): NamespaceExport {
8416-
return finishNode(factory.createNamespaceExport(parseIdentifierName()), pos);
8447+
return finishNode(factory.createNamespaceExport(token() === SyntaxKind.StringLiteral ? parseLiteralLikeNode(SyntaxKind.StringLiteral) as StringLiteral : parseIdentifierName()), pos);
84178448
}
84188449

84198450
function parseExportDeclaration(pos: number, hasJSDoc: boolean, modifiers: NodeArray<ModifierLike> | undefined): ExportDeclaration {

src/compiler/transformers/declarations.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import {
5959
getDirectoryPath,
6060
getEffectiveBaseTypeNode,
6161
getEffectiveModifierFlags,
62+
getEmitScriptTarget,
6263
getExternalModuleImportEqualsDeclarationExpression,
6364
getExternalModuleNameFromDeclaration,
6465
getFirstConstructorWithBody,
@@ -1520,7 +1521,7 @@ export function transformDeclarations(context: TransformationContext) {
15201521
/*modifiers*/ undefined,
15211522
/*isTypeOnly*/ false,
15221523
factory.createNamedExports(map(exportMappings, ([gen, exp]) => {
1523-
return factory.createExportSpecifier(/*isTypeOnly*/ false, gen, exp);
1524+
return factory.createExportSpecifier(/*isTypeOnly*/ false, gen, factory.createModuleExportName(exp, getEmitScriptTarget(context.getCompilerOptions())));
15241525
}))
15251526
));
15261527
}

0 commit comments

Comments
 (0)