Skip to content

Commit 942385c

Browse files
ryzokukenNicolò Ribaudo
authored andcommitted
Add support for import defer proposal
Co-authored-by: Nicolò Ribaudo <[email protected]>
1 parent 4a18b5c commit 942385c

File tree

65 files changed

+1121
-246
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+1121
-246
lines changed

src/compiler/checker.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,7 @@ import {
432432
ImportDeclaration,
433433
ImportEqualsDeclaration,
434434
ImportOrExportSpecifier,
435+
ImportPhase,
435436
ImportSpecifier,
436437
ImportTypeNode,
437438
IndexedAccessType,
@@ -9858,6 +9859,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
98589859
propertyName && isIdentifier(propertyName) ? factory.createIdentifier(idText(propertyName)) : undefined,
98599860
factory.createIdentifier(localName),
98609861
)]),
9862+
ImportPhase.Evaluation,
98619863
),
98629864
factory.createStringLiteral(specifier),
98639865
/*attributes*/ undefined,
@@ -9944,7 +9946,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
99449946
addResult(
99459947
factory.createImportDeclaration(
99469948
/*modifiers*/ undefined,
9947-
factory.createImportClause(isTypeOnly, factory.createIdentifier(localName), /*namedBindings*/ undefined),
9949+
factory.createImportClause(isTypeOnly, factory.createIdentifier(localName), /*namedBindings*/ undefined, ImportPhase.Evaluation),
99489950
specifier,
99499951
attributes,
99509952
),
@@ -9959,7 +9961,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
99599961
addResult(
99609962
factory.createImportDeclaration(
99619963
/*modifiers*/ undefined,
9962-
factory.createImportClause(isTypeOnly, /*name*/ undefined, factory.createNamespaceImport(factory.createIdentifier(localName))),
9964+
factory.createImportClause(isTypeOnly, /*name*/ undefined, factory.createNamespaceImport(factory.createIdentifier(localName)), ImportPhase.Evaluation),
99639965
specifier,
99649966
(node as ImportClause).parent.attributes,
99659967
),
@@ -9995,6 +9997,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
99959997
factory.createIdentifier(localName),
99969998
),
99979999
]),
10000+
ImportPhase.Evaluation,
999810001
),
999910002
specifier,
1000010003
(node as ImportSpecifier).parent.parent.parent.attributes,
@@ -52860,6 +52863,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
5286052863
if (node.isTypeOnly && node.namedBindings?.kind === SyntaxKind.NamedImports) {
5286152864
return checkGrammarNamedImportsOrExports(node.namedBindings);
5286252865
}
52866+
if (node.phase !== ImportPhase.Evaluation && moduleKind !== ModuleKind.ESNext) {
52867+
return grammarErrorOnNode(node, Diagnostics.Deferred_imports_are_only_supported_when_the_module_flag_is_set_to_esnext);
52868+
}
5286352869
return false;
5286452870
}
5286552871

src/compiler/diagnosticMessages.json

+12
Original file line numberDiff line numberDiff line change
@@ -8433,5 +8433,17 @@
84338433
"String literal import and export names are not supported when the '--module' flag is set to 'es2015' or 'es2020'.": {
84348434
"category": "Error",
84358435
"code": 18057
8436+
},
8437+
"Default imports aren't allowed for deferred imports.": {
8438+
"category": "Error",
8439+
"code": 18058
8440+
},
8441+
"Named imports aren't allowed for deferred imports.": {
8442+
"category": "Error",
8443+
"code": 18059
8444+
},
8445+
"Deferred imports are only supported when the '--module' flag is set to 'esnext'.": {
8446+
"category": "Error",
8447+
"code": 18060
84368448
}
84378449
}

src/compiler/emitter.ts

+5
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ import {
182182
ImportDeclaration,
183183
ImportEqualsDeclaration,
184184
ImportOrExportSpecifier,
185+
ImportPhase,
185186
ImportSpecifier,
186187
ImportTypeNode,
187188
IndexedAccessTypeNode,
@@ -3689,6 +3690,10 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
36893690
emitTokenWithComment(SyntaxKind.TypeKeyword, node.pos, writeKeyword, node);
36903691
writeSpace();
36913692
}
3693+
else if (node.phase !== ImportPhase.Evaluation) {
3694+
emitTokenWithComment(SyntaxKind.DeferKeyword, node.pos, writeKeyword, node);
3695+
writeSpace();
3696+
}
36923697
emit(node.name);
36933698
if (node.name && node.namedBindings) {
36943699
emitTokenWithComment(SyntaxKind.CommaToken, node.name.end, writePunctuation, node);

src/compiler/factory/nodeFactory.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ import {
135135
ImportClause,
136136
ImportDeclaration,
137137
ImportEqualsDeclaration,
138+
ImportPhase,
138139
ImportSpecifier,
139140
ImportTypeAssertionContainer,
140141
ImportTypeNode,
@@ -4723,11 +4724,12 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
47234724
}
47244725

47254726
// @api
4726-
function createImportClause(isTypeOnly: boolean, name: Identifier | undefined, namedBindings: NamedImportBindings | undefined): ImportClause {
4727+
function createImportClause(isTypeOnly: boolean, name: Identifier | undefined, namedBindings: NamedImportBindings | undefined, phase: ImportPhase): ImportClause {
47274728
const node = createBaseDeclaration<ImportClause>(SyntaxKind.ImportClause);
47284729
node.isTypeOnly = isTypeOnly;
47294730
node.name = name;
47304731
node.namedBindings = namedBindings;
4732+
node.phase = phase;
47314733
node.transformFlags |= propagateChildFlags(node.name) |
47324734
propagateChildFlags(node.namedBindings);
47334735
if (isTypeOnly) {
@@ -4738,11 +4740,12 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
47384740
}
47394741

47404742
// @api
4741-
function updateImportClause(node: ImportClause, isTypeOnly: boolean, name: Identifier | undefined, namedBindings: NamedImportBindings | undefined) {
4743+
function updateImportClause(node: ImportClause, isTypeOnly: boolean, name: Identifier | undefined, namedBindings: NamedImportBindings | undefined, phase: ImportPhase) {
47424744
return node.isTypeOnly !== isTypeOnly
47434745
|| node.name !== name
47444746
|| node.namedBindings !== namedBindings
4745-
? update(createImportClause(isTypeOnly, name, namedBindings), node)
4747+
|| node.phase !== phase
4748+
? update(createImportClause(isTypeOnly, name, namedBindings, phase), node)
47464749
: node;
47474750
}
47484751

src/compiler/factory/utilities.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ import {
7575
ImportCall,
7676
ImportDeclaration,
7777
ImportEqualsDeclaration,
78+
ImportPhase,
7879
InternalEmitFlags,
7980
isAssignmentExpression,
8081
isAssignmentOperator,
@@ -730,7 +731,7 @@ export function createExternalHelpersImportDeclarationIfNeeded(nodeFactory: Node
730731

731732
const externalHelpersImportDeclaration = nodeFactory.createImportDeclaration(
732733
/*modifiers*/ undefined,
733-
nodeFactory.createImportClause(/*isTypeOnly*/ false, /*name*/ undefined, namedBindings),
734+
nodeFactory.createImportClause(/*isTypeOnly*/ false, /*name*/ undefined, namedBindings, ImportPhase.Evaluation),
734735
nodeFactory.createStringLiteral(externalHelpersModuleNameText),
735736
/*attributes*/ undefined,
736737
);

src/compiler/parser.ts

+29-9
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ import {
123123
ImportDeclaration,
124124
ImportEqualsDeclaration,
125125
ImportOrExportSpecifier,
126+
ImportPhase,
126127
ImportSpecifier,
127128
ImportTypeAssertionContainer,
128129
ImportTypeNode,
@@ -7190,6 +7191,7 @@ namespace Parser {
71907191
// could be legal, it would add complexity for very little gain.
71917192
case SyntaxKind.InterfaceKeyword:
71927193
case SyntaxKind.TypeKeyword:
7194+
case SyntaxKind.DeferKeyword:
71937195
return nextTokenIsIdentifierOnSameLine();
71947196
case SyntaxKind.ModuleKeyword:
71957197
case SyntaxKind.NamespaceKeyword:
@@ -7221,7 +7223,7 @@ namespace Parser {
72217223

72227224
case SyntaxKind.ImportKeyword:
72237225
nextToken();
7224-
return token() === SyntaxKind.StringLiteral || token() === SyntaxKind.AsteriskToken ||
7226+
return token() === SyntaxKind.DeferKeyword || token() === SyntaxKind.StringLiteral || token() === SyntaxKind.AsteriskToken ||
72257227
token() === SyntaxKind.OpenBraceToken || tokenIsIdentifierOrKeyword(token());
72267228
case SyntaxKind.ExportKeyword:
72277229
let currentToken = nextToken();
@@ -7295,6 +7297,7 @@ namespace Parser {
72957297
case SyntaxKind.NamespaceKeyword:
72967298
case SyntaxKind.TypeKeyword:
72977299
case SyntaxKind.GlobalKeyword:
7300+
case SyntaxKind.DeferKeyword:
72987301
// When these don't start a declaration, they're an identifier in an expression statement
72997302
return true;
73007303

@@ -8366,6 +8369,7 @@ namespace Parser {
83668369
}
83678370

83688371
let isTypeOnly = false;
8372+
let phase = ImportPhase.Evaluation;
83698373
if (
83708374
identifier?.escapedText === "type" &&
83718375
(token() !== SyntaxKind.FromKeyword || isIdentifier() && lookAhead(nextTokenIsFromKeywordOrEqualsToken)) &&
@@ -8374,12 +8378,20 @@ namespace Parser {
83748378
isTypeOnly = true;
83758379
identifier = isIdentifier() ? parseIdentifier() : undefined;
83768380
}
8381+
else if (identifier?.escapedText === "defer" && token() !== SyntaxKind.FromKeyword) {
8382+
phase = ImportPhase.Defer;
8383+
identifier = undefined;
8384+
if (isIdentifier()) {
8385+
parseErrorAtCurrentToken(Diagnostics.Default_imports_aren_t_allowed_for_deferred_imports);
8386+
identifier = parseIdentifier();
8387+
}
8388+
}
83778389

8378-
if (identifier && !tokenAfterImportedIdentifierDefinitelyProducesImportDeclaration()) {
8390+
if (identifier && !tokenAfterImportedIdentifierDefinitelyProducesImportDeclaration() && phase !== ImportPhase.Defer) {
83798391
return parseImportEqualsDeclaration(pos, hasJSDoc, modifiers, identifier, isTypeOnly);
83808392
}
83818393

8382-
const importClause = tryParseImportClause(identifier, afterImportPos, isTypeOnly);
8394+
const importClause = tryParseImportClause(identifier, afterImportPos, isTypeOnly, /*skipJsDocLeadingAsterisks*/ undefined, phase);
83838395
const moduleSpecifier = parseModuleSpecifier();
83848396
const attributes = tryParseImportAttributes();
83858397

@@ -8388,7 +8400,7 @@ namespace Parser {
83888400
return withJSDoc(finishNode(node, pos), hasJSDoc);
83898401
}
83908402

8391-
function tryParseImportClause(identifier: Identifier | undefined, pos: number, isTypeOnly: boolean, skipJsDocLeadingAsterisks = false) {
8403+
function tryParseImportClause(identifier: Identifier | undefined, pos: number, isTypeOnly: boolean, skipJsDocLeadingAsterisks = false, phase: ImportPhase) {
83928404
// ImportDeclaration:
83938405
// import ImportClause from ModuleSpecifier ;
83948406
// import ModuleSpecifier;
@@ -8398,7 +8410,7 @@ namespace Parser {
83988410
token() === SyntaxKind.AsteriskToken || // import *
83998411
token() === SyntaxKind.OpenBraceToken // import {
84008412
) {
8401-
importClause = parseImportClause(identifier, pos, isTypeOnly, skipJsDocLeadingAsterisks);
8413+
importClause = parseImportClause(identifier, pos, isTypeOnly, skipJsDocLeadingAsterisks, phase);
84028414
parseExpected(SyntaxKind.FromKeyword);
84038415
}
84048416
return importClause;
@@ -8464,7 +8476,7 @@ namespace Parser {
84648476
return finished;
84658477
}
84668478

8467-
function parseImportClause(identifier: Identifier | undefined, pos: number, isTypeOnly: boolean, skipJsDocLeadingAsterisks: boolean) {
8479+
function parseImportClause(identifier: Identifier | undefined, pos: number, isTypeOnly: boolean, skipJsDocLeadingAsterisks: boolean, phase: ImportPhase) {
84688480
// ImportClause:
84698481
// ImportedDefaultBinding
84708482
// NameSpaceImport
@@ -8480,11 +8492,19 @@ namespace Parser {
84808492
parseOptional(SyntaxKind.CommaToken)
84818493
) {
84828494
if (skipJsDocLeadingAsterisks) scanner.setSkipJsDocLeadingAsterisks(true);
8483-
namedBindings = token() === SyntaxKind.AsteriskToken ? parseNamespaceImport() : parseNamedImportsOrExports(SyntaxKind.NamedImports);
8495+
if (token() === SyntaxKind.AsteriskToken) {
8496+
namedBindings = parseNamespaceImport();
8497+
}
8498+
else {
8499+
if (phase === ImportPhase.Defer) {
8500+
parseErrorAtCurrentToken(Diagnostics.Named_imports_aren_t_allowed_for_deferred_imports);
8501+
}
8502+
namedBindings = parseNamedImportsOrExports(SyntaxKind.NamedImports);
8503+
}
84848504
if (skipJsDocLeadingAsterisks) scanner.setSkipJsDocLeadingAsterisks(false);
84858505
}
84868506

8487-
return finishNode(factory.createImportClause(isTypeOnly, identifier, namedBindings), pos);
8507+
return finishNode(factory.createImportClause(isTypeOnly, identifier, namedBindings, phase), pos);
84888508
}
84898509

84908510
function parseModuleReference() {
@@ -9518,7 +9538,7 @@ namespace Parser {
95189538
identifier = parseIdentifier();
95199539
}
95209540

9521-
const importClause = tryParseImportClause(identifier, afterImportTagPos, /*isTypeOnly*/ true, /*skipJsDocLeadingAsterisks*/ true);
9541+
const importClause = tryParseImportClause(identifier, afterImportTagPos, /*isTypeOnly*/ true, /*skipJsDocLeadingAsterisks*/ true, ImportPhase.Evaluation);
95229542
const moduleSpecifier = parseModuleSpecifier();
95239543
const attributes = tryParseImportAttributes();
95249544

src/compiler/scanner.ts

+1
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ export const textToKeywordObj: MapLike<KeywordSyntaxKind> = {
203203
true: SyntaxKind.TrueKeyword,
204204
try: SyntaxKind.TryKeyword,
205205
type: SyntaxKind.TypeKeyword,
206+
defer: SyntaxKind.DeferKeyword,
206207
typeof: SyntaxKind.TypeOfKeyword,
207208
undefined: SyntaxKind.UndefinedKeyword,
208209
unique: SyntaxKind.UniqueKeyword,

src/compiler/transformers/declarations.ts

+3
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,7 @@ export function transformDeclarations(context: TransformationContext): Transform
889889
decl.importClause.isTypeOnly,
890890
visibleDefaultBinding,
891891
/*namedBindings*/ undefined,
892+
decl.importClause.phase,
892893
),
893894
rewriteModuleSpecifier(decl, decl.moduleSpecifier),
894895
tryGetResolutionModeOverride(decl.attributes),
@@ -905,6 +906,7 @@ export function transformDeclarations(context: TransformationContext): Transform
905906
decl.importClause.isTypeOnly,
906907
visibleDefaultBinding,
907908
namedBindings,
909+
decl.importClause.phase,
908910
),
909911
rewriteModuleSpecifier(decl, decl.moduleSpecifier),
910912
tryGetResolutionModeOverride(decl.attributes),
@@ -921,6 +923,7 @@ export function transformDeclarations(context: TransformationContext): Transform
921923
decl.importClause.isTypeOnly,
922924
visibleDefaultBinding,
923925
bindingList && bindingList.length ? factory.updateNamedImports(decl.importClause.namedBindings, bindingList) : undefined,
926+
decl.importClause.phase,
924927
),
925928
rewriteModuleSpecifier(decl, decl.moduleSpecifier),
926929
tryGetResolutionModeOverride(decl.attributes),

src/compiler/transformers/jsx.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
getSemanticJsxChildren,
2323
Identifier,
2424
idText,
25+
ImportPhase,
2526
ImportSpecifier,
2627
insertStatementAfterCustomPrologue,
2728
isExpression,
@@ -172,7 +173,7 @@ export function transformJsx(context: TransformationContext): (x: SourceFile | B
172173
for (const [importSource, importSpecifiersMap] of arrayFrom(currentFileState.utilizedImplicitRuntimeImports.entries())) {
173174
if (isExternalModule(node)) {
174175
// Add `import` statement
175-
const importStatement = factory.createImportDeclaration(/*modifiers*/ undefined, factory.createImportClause(/*isTypeOnly*/ false, /*name*/ undefined, factory.createNamedImports(arrayFrom(importSpecifiersMap.values()))), factory.createStringLiteral(importSource), /*attributes*/ undefined);
176+
const importStatement = factory.createImportDeclaration(/*modifiers*/ undefined, factory.createImportClause(/*isTypeOnly*/ false, /*name*/ undefined, factory.createNamedImports(arrayFrom(importSpecifiersMap.values())), ImportPhase.Evaluation), factory.createStringLiteral(importSource), /*attributes*/ undefined);
176177
setParentRecursive(importStatement, /*incremental*/ false);
177178
statements = insertStatementAfterCustomPrologue(statements.slice(), importStatement);
178179
}

src/compiler/transformers/module/esnextAnd2015.ts

+3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
idText,
2929
ImportDeclaration,
3030
ImportEqualsDeclaration,
31+
ImportPhase,
3132
insertStatementsAfterCustomPrologue,
3233
isExportNamespaceAsDefaultDeclaration,
3334
isExternalModule,
@@ -224,6 +225,7 @@ export function transformECMAScriptModule(context: TransformationContext): (x: S
224225
factory.createNamedImports([
225226
factory.createImportSpecifier(/*isTypeOnly*/ false, factory.createIdentifier("createRequire"), createRequireName),
226227
]),
228+
ImportPhase.Evaluation,
227229
),
228230
factory.createStringLiteral("module"),
229231
/*attributes*/ undefined,
@@ -356,6 +358,7 @@ export function transformECMAScriptModule(context: TransformationContext): (x: S
356358
factory.createNamespaceImport(
357359
synthName,
358360
),
361+
ImportPhase.Evaluation,
359362
),
360363
updatedModuleSpecifier!,
361364
node.attributes,

src/compiler/transformers/ts.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2286,7 +2286,7 @@ export function transformTypeScript(context: TransformationContext): Transformer
22862286
// Elide the import clause if we elide both its name and its named bindings.
22872287
const name = shouldEmitAliasDeclaration(node) ? node.name : undefined;
22882288
const namedBindings = visitNode(node.namedBindings, visitNamedImportBindings, isNamedImportBindings);
2289-
return (name || namedBindings) ? factory.updateImportClause(node, /*isTypeOnly*/ false, name, namedBindings) : undefined;
2289+
return (name || namedBindings) ? factory.updateImportClause(node, /*isTypeOnly*/ false, name, namedBindings, node.phase) : undefined;
22902290
}
22912291

22922292
/**

0 commit comments

Comments
 (0)