Skip to content

Commit a6e1ced

Browse files
authored
Merge pull request #16281 from Microsoft/master-MergeDynamicImport
[Master] Remerge this back to master after fixing test failure
2 parents cbdf02f + fe4fec5 commit a6e1ced

File tree

169 files changed

+5274
-103
lines changed

Some content is hidden

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

169 files changed

+5274
-103
lines changed

src/compiler/binder.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2333,7 +2333,7 @@ namespace ts {
23332333
// A common practice in node modules is to set 'export = module.exports = {}', this ensures that 'exports'
23342334
// is still pointing to 'module.exports'.
23352335
// We do not want to consider this as 'export=' since a module can have only one of these.
2336-
// Similarlly we do not want to treat 'module.exports = exports' as an 'export='.
2336+
// Similarly we do not want to treat 'module.exports = exports' as an 'export='.
23372337
const assignedExpression = getRightMostAssignedExpression(node.right);
23382338
if (isEmptyObjectLiteral(assignedExpression) || isExportsOrModuleExportsOrAlias(assignedExpression)) {
23392339
// Mark it as a module in case there are no other exports in the file
@@ -2741,6 +2741,10 @@ namespace ts {
27412741
transformFlags |= TransformFlags.AssertES2015;
27422742
}
27432743

2744+
if (expression.kind === SyntaxKind.ImportKeyword) {
2745+
transformFlags |= TransformFlags.ContainsDynamicImport;
2746+
}
2747+
27442748
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
27452749
return transformFlags & ~TransformFlags.ArrayLiteralOrCallOrNewExcludes;
27462750
}

src/compiler/checker.ts

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8366,6 +8366,12 @@ namespace ts {
83668366
/**
83678367
* This is *not* a bi-directional relationship.
83688368
* If one needs to check both directions for comparability, use a second call to this function or 'checkTypeComparableTo'.
8369+
*
8370+
* A type S is comparable to a type T if some (but not necessarily all) of the possible values of S are also possible values of T.
8371+
* It is used to check following cases:
8372+
* - the types of the left and right sides of equality/inequality operators (`===`, `!==`, `==`, `!=`).
8373+
* - the types of `case` clause expressions and their respective `switch` expressions.
8374+
* - the type of an expression in a type assertion with the type being asserted.
83698375
*/
83708376
function isTypeComparableTo(source: Type, target: Type): boolean {
83718377
return isTypeRelatedTo(source, target, comparableRelation);
@@ -16173,6 +16179,35 @@ namespace ts {
1617316179
return getReturnTypeOfSignature(signature);
1617416180
}
1617516181

16182+
function checkImportCallExpression(node: ImportCall): Type {
16183+
// Check grammar of dynamic import
16184+
checkGrammarArguments(node, node.arguments) || checkGrammarImportCallExpression(node);
16185+
16186+
if (node.arguments.length === 0) {
16187+
return createPromiseReturnType(node, anyType);
16188+
}
16189+
const specifier = node.arguments[0];
16190+
const specifierType = checkExpressionCached(specifier);
16191+
// Even though multiple arugments is grammatically incorrect, type-check extra arguments for completion
16192+
for (let i = 1; i < node.arguments.length; ++i) {
16193+
checkExpressionCached(node.arguments[i]);
16194+
}
16195+
16196+
if (specifierType.flags & TypeFlags.Undefined || specifierType.flags & TypeFlags.Null || !isTypeAssignableTo(specifierType, stringType)) {
16197+
error(specifier, Diagnostics.Dynamic_import_s_specifier_must_be_of_type_string_but_here_has_type_0, typeToString(specifierType));
16198+
}
16199+
16200+
// resolveExternalModuleName will return undefined if the moduleReferenceExpression is not a string literal
16201+
const moduleSymbol = resolveExternalModuleName(node, specifier);
16202+
if (moduleSymbol) {
16203+
const esModuleSymbol = resolveESModuleSymbol(moduleSymbol, specifier, /*dontRecursivelyResolve*/ true);
16204+
if (esModuleSymbol) {
16205+
return createPromiseReturnType(node, getTypeOfSymbol(esModuleSymbol));
16206+
}
16207+
}
16208+
return createPromiseReturnType(node, anyType);
16209+
}
16210+
1617616211
function isCommonJsRequire(node: Node) {
1617716212
if (!isRequireCall(node, /*checkArgumentIsStringLiteral*/ true)) {
1617816213
return false;
@@ -16379,14 +16414,18 @@ namespace ts {
1637916414
return emptyObjectType;
1638016415
}
1638116416

16382-
function createPromiseReturnType(func: FunctionLikeDeclaration, promisedType: Type) {
16417+
function createPromiseReturnType(func: FunctionLikeDeclaration | ImportCall, promisedType: Type) {
1638316418
const promiseType = createPromiseType(promisedType);
1638416419
if (promiseType === emptyObjectType) {
16385-
error(func, Diagnostics.An_async_function_or_method_must_return_a_Promise_Make_sure_you_have_a_declaration_for_Promise_or_include_ES2015_in_your_lib_option);
16420+
error(func, isImportCall(func) ?
16421+
Diagnostics.A_dynamic_import_call_returns_a_Promise_Make_sure_you_have_a_declaration_for_Promise_or_include_ES2015_in_your_lib_option :
16422+
Diagnostics.An_async_function_or_method_must_return_a_Promise_Make_sure_you_have_a_declaration_for_Promise_or_include_ES2015_in_your_lib_option);
1638616423
return unknownType;
1638716424
}
1638816425
else if (!getGlobalPromiseConstructorSymbol(/*reportErrors*/ true)) {
16389-
error(func, Diagnostics.An_async_function_or_method_in_ES5_SlashES3_requires_the_Promise_constructor_Make_sure_you_have_a_declaration_for_the_Promise_constructor_or_include_ES2015_in_your_lib_option);
16426+
error(func, isImportCall(func) ?
16427+
Diagnostics.A_dynamic_import_call_in_ES5_SlashES3_requires_the_Promise_constructor_Make_sure_you_have_a_declaration_for_the_Promise_constructor_or_include_ES2015_in_your_lib_option :
16428+
Diagnostics.An_async_function_or_method_in_ES5_SlashES3_requires_the_Promise_constructor_Make_sure_you_have_a_declaration_for_the_Promise_constructor_or_include_ES2015_in_your_lib_option);
1639016429
}
1639116430

1639216431
return promiseType;
@@ -17745,6 +17784,10 @@ namespace ts {
1774517784
case SyntaxKind.ElementAccessExpression:
1774617785
return checkIndexedAccess(<ElementAccessExpression>node);
1774717786
case SyntaxKind.CallExpression:
17787+
if ((<CallExpression>node).expression.kind === SyntaxKind.ImportKeyword) {
17788+
return checkImportCallExpression(<ImportCall>node);
17789+
}
17790+
/* falls through */
1774817791
case SyntaxKind.NewExpression:
1774917792
return checkCallExpression(<CallExpression>node);
1775017793
case SyntaxKind.TaggedTemplateExpression:
@@ -24670,6 +24713,27 @@ namespace ts {
2467024713
});
2467124714
return result;
2467224715
}
24716+
24717+
function checkGrammarImportCallExpression(node: ImportCall): boolean {
24718+
if (modulekind === ModuleKind.ES2015) {
24719+
return grammarErrorOnNode(node, Diagnostics.Dynamic_import_cannot_be_used_when_targeting_ECMAScript_2015_modules);
24720+
}
24721+
24722+
if (node.typeArguments) {
24723+
return grammarErrorOnNode(node, Diagnostics.Dynamic_import_cannot_have_type_arguments);
24724+
}
24725+
24726+
const arguments = node.arguments;
24727+
if (arguments.length !== 1) {
24728+
return grammarErrorOnNode(node, Diagnostics.Dynamic_import_must_have_one_specifier_as_an_argument);
24729+
}
24730+
24731+
// see: parseArgumentOrArrayLiteralElement...we use this function which parse arguments of callExpression to parse specifier for dynamic import.
24732+
// parseArgumentOrArrayLiteralElement allows spread element to be in an argument list which is not allowed as specifier in dynamic import.
24733+
if (isSpreadElement(arguments[0])) {
24734+
return grammarErrorOnNode(arguments[0], Diagnostics.Specifier_of_dynamic_import_cannot_be_spread_element);
24735+
}
24736+
}
2467324737
}
2467424738

2467524739
/** Like 'isDeclarationName', but returns true for LHS of `import { x as y }` or `export { x as y }`. */

src/compiler/commandLineParser.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,12 @@ namespace ts {
100100
"umd": ModuleKind.UMD,
101101
"es6": ModuleKind.ES2015,
102102
"es2015": ModuleKind.ES2015,
103+
"esnext": ModuleKind.ESNext
103104
}),
104105
paramType: Diagnostics.KIND,
105106
showInSimplifiedHelpView: true,
106107
category: Diagnostics.Basic_Options,
107-
description: Diagnostics.Specify_module_code_generation_Colon_commonjs_amd_system_umd_or_es2015,
108+
description: Diagnostics.Specify_module_code_generation_Colon_commonjs_amd_system_umd_es2015_or_ESNext,
108109
},
109110
{
110111
name: "lib",

src/compiler/diagnosticMessages.json

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,23 @@
883883
"category": "Error",
884884
"code": 1322
885885
},
886+
"Dynamic import cannot be used when targeting ECMAScript 2015 modules.": {
887+
"category": "Error",
888+
"code": 1323
889+
},
890+
"Dynamic import must have one specifier as an argument.": {
891+
"category": "Error",
892+
"code": 1324
893+
},
894+
"Specifier of dynamic import cannot be spread element.": {
895+
"category": "Error",
896+
"code": 1325
897+
},
898+
"Dynamic import cannot have type arguments": {
899+
"category": "Error",
900+
"code": 1326
901+
},
902+
886903
"Duplicate identifier '{0}'.": {
887904
"category": "Error",
888905
"code": 2300
@@ -1927,10 +1944,6 @@
19271944
"category": "Error",
19281945
"code": 2649
19291946
},
1930-
"Cannot emit namespaced JSX elements in React.": {
1931-
"category": "Error",
1932-
"code": 2650
1933-
},
19341947
"A member initializer in a enum declaration cannot reference members declared after it, including members defined in other enums.": {
19351948
"category": "Error",
19361949
"code": 2651
@@ -2163,6 +2176,14 @@
21632176
"category": "Error",
21642177
"code": 2710
21652178
},
2179+
"A dynamic import call returns a 'Promise'. Make sure you have a declaration for 'Promise' or include 'ES2015' in your `--lib` option.": {
2180+
"category": "Error",
2181+
"code": 2711
2182+
},
2183+
"A dynamic import call in ES5/ES3 requires the 'Promise' constructor. Make sure you have a declaration for the 'Promise' constructor or include 'ES2015' in your `--lib` option.": {
2184+
"category": "Error",
2185+
"code": 2712
2186+
},
21662187

21672188
"Import declaration '{0}' is using private name '{1}'.": {
21682189
"category": "Error",
@@ -2629,7 +2650,7 @@
26292650
"category": "Message",
26302651
"code": 6015
26312652
},
2632-
"Specify module code generation: 'commonjs', 'amd', 'system', 'umd' or 'es2015'.": {
2653+
"Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'.": {
26332654
"category": "Message",
26342655
"code": 6016
26352656
},
@@ -3365,6 +3386,11 @@
33653386
"category": "Error",
33663387
"code": 7035
33673388
},
3389+
"Dynamic import's specifier must be of type 'string', but here has type '{0}'.": {
3390+
"category": "Error",
3391+
"code": 7036
3392+
},
3393+
33683394
"You cannot rename this element.": {
33693395
"category": "Error",
33703396
"code": 8000

src/compiler/emitter.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,7 @@ namespace ts {
676676
case SyntaxKind.SuperKeyword:
677677
case SyntaxKind.TrueKeyword:
678678
case SyntaxKind.ThisKeyword:
679+
case SyntaxKind.ImportKeyword:
679680
writeTokenNode(node);
680681
return;
681682

src/compiler/parser.ts

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,16 @@ namespace ts {
4646
}
4747
}
4848

49-
// Invokes a callback for each child of the given node. The 'cbNode' callback is invoked for all child nodes
50-
// stored in properties. If a 'cbNodes' callback is specified, it is invoked for embedded arrays; otherwise,
51-
// embedded arrays are flattened and the 'cbNode' callback is invoked for each element. If a callback returns
52-
// a truthy value, iteration stops and that value is returned. Otherwise, undefined is returned.
49+
/**
50+
* Invokes a callback for each child of the given node. The 'cbNode' callback is invoked for all child nodes
51+
* stored in properties. If a 'cbNodes' callback is specified, it is invoked for embedded arrays; otherwise,
52+
* embedded arrays are flattened and the 'cbNode' callback is invoked for each element. If a callback returns
53+
* a truthy value, iteration stops and that value is returned. Otherwise, undefined is returned.
54+
*
55+
* @param node a given node to visit its children
56+
* @param cbNode a callback to be invoked for all child nodes
57+
* @param cbNodeArray a callback to be invoked for embedded array
58+
*/
5359
export function forEachChild<T>(node: Node, cbNode: (node: Node) => T | undefined, cbNodeArray?: (nodes: NodeArray<Node>) => T | undefined): T | undefined {
5460
if (!node) {
5561
return;
@@ -2409,7 +2415,7 @@ namespace ts {
24092415
if (token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.LessThanToken) {
24102416
return parseSignatureMember(SyntaxKind.CallSignature);
24112417
}
2412-
if (token() === SyntaxKind.NewKeyword && lookAhead(isStartOfConstructSignature)) {
2418+
if (token() === SyntaxKind.NewKeyword && lookAhead(nextTokenIsOpenParenOrLessThan)) {
24132419
return parseSignatureMember(SyntaxKind.ConstructSignature);
24142420
}
24152421
const fullStart = getNodePos();
@@ -2420,7 +2426,7 @@ namespace ts {
24202426
return parsePropertyOrMethodSignature(fullStart, modifiers);
24212427
}
24222428

2423-
function isStartOfConstructSignature() {
2429+
function nextTokenIsOpenParenOrLessThan() {
24242430
nextToken();
24252431
return token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.LessThanToken;
24262432
}
@@ -2777,6 +2783,8 @@ namespace ts {
27772783
case SyntaxKind.SlashEqualsToken:
27782784
case SyntaxKind.Identifier:
27792785
return true;
2786+
case SyntaxKind.ImportKeyword:
2787+
return lookAhead(nextTokenIsOpenParenOrLessThan);
27802788
default:
27812789
return isIdentifier();
27822790
}
@@ -3509,10 +3517,10 @@ namespace ts {
35093517
* 5) --UnaryExpression[?Yield]
35103518
*/
35113519
if (isUpdateExpression()) {
3512-
const incrementExpression = parseIncrementExpression();
3520+
const updateExpression = parseUpdateExpression();
35133521
return token() === SyntaxKind.AsteriskAsteriskToken ?
3514-
<BinaryExpression>parseBinaryExpressionRest(getBinaryOperatorPrecedence(), incrementExpression) :
3515-
incrementExpression;
3522+
<BinaryExpression>parseBinaryExpressionRest(getBinaryOperatorPrecedence(), updateExpression) :
3523+
updateExpression;
35163524
}
35173525

35183526
/**
@@ -3578,7 +3586,7 @@ namespace ts {
35783586
}
35793587
// falls through
35803588
default:
3581-
return parseIncrementExpression();
3589+
return parseUpdateExpression();
35823590
}
35833591
}
35843592

@@ -3594,7 +3602,7 @@ namespace ts {
35943602
*/
35953603
function isUpdateExpression(): boolean {
35963604
// This function is called inside parseUnaryExpression to decide
3597-
// whether to call parseSimpleUnaryExpression or call parseIncrementExpression directly
3605+
// whether to call parseSimpleUnaryExpression or call parseUpdateExpression directly
35983606
switch (token()) {
35993607
case SyntaxKind.PlusToken:
36003608
case SyntaxKind.MinusToken:
@@ -3618,17 +3626,17 @@ namespace ts {
36183626
}
36193627

36203628
/**
3621-
* Parse ES7 IncrementExpression. IncrementExpression is used instead of ES6's PostFixExpression.
3629+
* Parse ES7 UpdateExpression. UpdateExpression is used instead of ES6's PostFixExpression.
36223630
*
3623-
* ES7 IncrementExpression[yield]:
3631+
* ES7 UpdateExpression[yield]:
36243632
* 1) LeftHandSideExpression[?yield]
36253633
* 2) LeftHandSideExpression[?yield] [[no LineTerminator here]]++
36263634
* 3) LeftHandSideExpression[?yield] [[no LineTerminator here]]--
36273635
* 4) ++LeftHandSideExpression[?yield]
36283636
* 5) --LeftHandSideExpression[?yield]
36293637
* In TypeScript (2), (3) are parsed as PostfixUnaryExpression. (4), (5) are parsed as PrefixUnaryExpression
36303638
*/
3631-
function parseIncrementExpression(): IncrementExpression {
3639+
function parseUpdateExpression(): UpdateExpression {
36323640
if (token() === SyntaxKind.PlusPlusToken || token() === SyntaxKind.MinusMinusToken) {
36333641
const node = <PrefixUnaryExpression>createNode(SyntaxKind.PrefixUnaryExpression);
36343642
node.operator = <PrefixUnaryOperator>token();
@@ -3678,25 +3686,35 @@ namespace ts {
36783686
// CallExpression Arguments
36793687
// CallExpression[Expression]
36803688
// CallExpression.IdentifierName
3681-
// super ( ArgumentListopt )
3689+
// import (AssignmentExpression)
3690+
// super Arguments
36823691
// super.IdentifierName
36833692
//
3684-
// Because of the recursion in these calls, we need to bottom out first. There are two
3685-
// bottom out states we can run into. Either we see 'super' which must start either of
3686-
// the last two CallExpression productions. Or we have a MemberExpression which either
3687-
// completes the LeftHandSideExpression, or starts the beginning of the first four
3688-
// CallExpression productions.
3689-
const expression = token() === SyntaxKind.SuperKeyword
3690-
? parseSuperExpression()
3691-
: parseMemberExpressionOrHigher();
3693+
// Because of the recursion in these calls, we need to bottom out first. There are three
3694+
// bottom out states we can run into: 1) We see 'super' which must start either of
3695+
// the last two CallExpression productions. 2) We see 'import' which must start import call.
3696+
// 3)we have a MemberExpression which either completes the LeftHandSideExpression,
3697+
// or starts the beginning of the first four CallExpression productions.
3698+
let expression: MemberExpression;
3699+
if (token() === SyntaxKind.ImportKeyword) {
3700+
// We don't want to eagerly consume all import keyword as import call expression so we look a head to find "("
3701+
// For example:
3702+
// var foo3 = require("subfolder
3703+
// import * as foo1 from "module-from-node -> we want this import to be a statement rather than import call expression
3704+
sourceFile.flags |= NodeFlags.PossiblyContainDynamicImport;
3705+
expression = parseTokenNode<PrimaryExpression>();
3706+
}
3707+
else {
3708+
expression = token() === SyntaxKind.SuperKeyword ? parseSuperExpression() : parseMemberExpressionOrHigher();
3709+
}
36923710

36933711
// Now, we *may* be complete. However, we might have consumed the start of a
36943712
// CallExpression. As such, we need to consume the rest of it here to be complete.
36953713
return parseCallExpressionRest(expression);
36963714
}
36973715

36983716
function parseMemberExpressionOrHigher(): MemberExpression {
3699-
// Note: to make our lives simpler, we decompose the the NewExpression productions and
3717+
// Note: to make our lives simpler, we decompose the NewExpression productions and
37003718
// place ObjectCreationExpression and FunctionExpression into PrimaryExpression.
37013719
// like so:
37023720
//
@@ -4792,9 +4810,11 @@ namespace ts {
47924810
case SyntaxKind.FinallyKeyword:
47934811
return true;
47944812

4813+
case SyntaxKind.ImportKeyword:
4814+
return isStartOfDeclaration() || lookAhead(nextTokenIsOpenParenOrLessThan);
4815+
47954816
case SyntaxKind.ConstKeyword:
47964817
case SyntaxKind.ExportKeyword:
4797-
case SyntaxKind.ImportKeyword:
47984818
return isStartOfDeclaration();
47994819

48004820
case SyntaxKind.AsyncKeyword:

0 commit comments

Comments
 (0)