diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 10bef2c444f..e7ba874d3d2 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2725,6 +2725,10 @@ namespace ts { transformFlags |= TransformFlags.AssertES2015; } + if (expression.kind === SyntaxKind.ImportKeyword) { + transformFlags |= TransformFlags.ContainsDynamicImport; + } + node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; return transformFlags & ~TransformFlags.ArrayLiteralOrCallOrNewExcludes; } @@ -3495,9 +3499,6 @@ namespace ts { case SyntaxKind.BreakStatement: transformFlags |= TransformFlags.ContainsHoistedDeclarationOrCompletion; break; - - case SyntaxKind.ImportCallExpression: - transformFlags |= TransformFlags.ContainsDynamicImport; } node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d28d6bc48a1..6df2efeea83 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14830,6 +14830,10 @@ namespace ts { return voidType; } + if (node.expression.kind === SyntaxKind.ImportKeyword) { + return checkImportCallExpression(node); + } + if (node.kind === SyntaxKind.NewExpression) { const declaration = signature.declaration; @@ -14868,19 +14872,21 @@ namespace ts { return getReturnTypeOfSignature(signature); } - function checkImportCallExpression(node: ImportCallExpression): Type { + function checkImportCallExpression(node: CallExpression): Type { if (modulekind === ModuleKind.ES2015) { grammarErrorOnNode(node, Diagnostics.Dynamic_import_cannot_be_used_when_targeting_ECMAScript_2015_modules); } - const specifierType = checkExpression(node.specifier); + // We already error in parse if arguments list is not length of 1 + const specifier = node.arguments[0]; + const specifierType = checkExpression(specifier); if (!isTypeAssignableTo(specifierType, stringType)) { - error(node.specifier, Diagnostics.Dynamic_import_s_specifier_must_be_of_type_string_but_here_has_type_0, typeToString(specifierType)); + error(specifier, Diagnostics.Dynamic_import_s_specifier_must_be_of_type_string_but_here_has_type_0, typeToString(specifierType)); } // resolveExternalModuleName will return undefined if the moduleReferenceExpression is not a string literal - const moduleSymbol = resolveExternalModuleName(node, node.specifier); + const moduleSymbol = resolveExternalModuleName(node, specifier); if (moduleSymbol) { - const esModuleSymbol = resolveESModuleSymbol(moduleSymbol, node.specifier); + const esModuleSymbol = resolveESModuleSymbol(moduleSymbol, specifier); if (esModuleSymbol) { return createPromiseReturnType(node, getTypeOfSymbol(esModuleSymbol)); } @@ -15097,16 +15103,16 @@ namespace ts { return emptyObjectType; } - function createPromiseReturnType(func: FunctionLikeDeclaration | ImportCallExpression, promisedType: Type) { + function createPromiseReturnType(func: FunctionLikeDeclaration | CallExpression, promisedType: Type) { const promiseType = createPromiseType(promisedType); if (promiseType === emptyObjectType) { - error(func, isImportCallExpression(func) ? + error(func, isImportCall(func) ? Diagnostics.A_dynamic_import_call_must_return_a_Promise_Make_sure_you_have_a_declaration_for_Promise_or_include_ES2015_in_your_lib_option : 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); return unknownType; } else if (!getGlobalPromiseConstructorSymbol(/*reportErrors*/ true)) { - error(func, isImportCallExpression(func) ? + error(func, isImportCall(func) ? 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 : 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); } @@ -16431,8 +16437,6 @@ namespace ts { case SyntaxKind.CallExpression: case SyntaxKind.NewExpression: return checkCallExpression(node); - case SyntaxKind.ImportCallExpression: - return checkImportCallExpression(node); case SyntaxKind.TaggedTemplateExpression: return checkTaggedTemplateExpression(node); case SyntaxKind.ParenthesizedExpression: diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 5e200cf4a93..4618776af26 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3297,6 +3297,14 @@ "category": "Error", "code": 17013 }, + "Dynamic import can only have one specifier as an argument.": { + "category": "Error", + "code": 17014 + }, + "Specifier of dynamic import cannot be spread element.": { + "category": "Error", + "code": 17015 + }, "Circularity detected while resolving configuration: {0}": { "category": "Error", diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 5f0e1bd5e92..c9cd941c7fc 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -655,6 +655,7 @@ namespace ts { case SyntaxKind.SuperKeyword: case SyntaxKind.TrueKeyword: case SyntaxKind.ThisKeyword: + case SyntaxKind.ImportKeyword: writeTokenText(kind); return; @@ -671,8 +672,6 @@ namespace ts { return emitCallExpression(node); case SyntaxKind.NewExpression: return emitNewExpression(node); - case SyntaxKind.ImportCallExpression: - return emitImportCallExpression(node); case SyntaxKind.TaggedTemplateExpression: return emitTaggedTemplateExpression(node); case SyntaxKind.TypeAssertionExpression: @@ -1155,13 +1154,6 @@ namespace ts { emitExpressionList(node, node.arguments, ListFormat.NewExpressionArguments); } - function emitImportCallExpression(node: ImportCallExpression) { - write("import"); - write("("); - emitExpression(node.specifier); - write(")"); - } - function emitTaggedTemplateExpression(node: TaggedTemplateExpression) { emitExpression(node.tag); write(" "); diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 0bf545f4fe1..3ff3bc2782f 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -503,18 +503,6 @@ namespace ts { : node; } - export function createImportCall(specifier: Expression): ImportCallExpression { - const node = createSynthesizedNode(SyntaxKind.ImportCallExpression); - node.specifier = specifier; - return node; - } - - export function updateImportCall(node: ImportCallExpression, specifier: Expression): ImportCallExpression { - return node.specifier !== specifier - ? updateNode(createImportCall(specifier), node) - : node; - } - export function createTaggedTemplate(tag: Expression, template: TemplateLiteral) { const node = createSynthesizedNode(SyntaxKind.TaggedTemplateExpression); node.tag = parenthesizeForAccess(tag); diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 8d924ff154c..f9bb5afd945 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -167,8 +167,6 @@ namespace ts { return visitNode(cbNode, (node).expression) || visitNodes(cbNodes, (node).typeArguments) || visitNodes(cbNodes, (node).arguments); - case SyntaxKind.ImportCallExpression: - return visitNode(cbNode, (node).specifier); case SyntaxKind.TaggedTemplateExpression: return visitNode(cbNode, (node).tag) || visitNode(cbNode, (node).template); @@ -3654,6 +3652,20 @@ namespace ts { return finishNode(node); } + if (isImportCall(expression)) { + // Check that the argument array is strictly of length 1 and the argument is assignment-expression + const arguments = expression.arguments; + if (arguments.length !== 1) { + parseErrorAtPosition(arguments.pos, arguments.end - arguments.pos, Diagnostics.Dynamic_import_can_only_have_one_specifier_as_an_argument); + } + + // see: parseArgumentOrArrayLiteralElement...we use this function which parse arguments of callExpression to parse specifier for dynamic import. + // parseArgumentOrArrayLiteralElement allows spread element to be in an argument list which is not allowed in dynamic import. + if (expression.arguments.length >= 1 && isSpreadExpression(arguments[0])) { + parseErrorAtPosition(arguments.pos, arguments.end - arguments.pos, Diagnostics.Specifier_of_dynamic_import_cannot_be_spread_element); + } + } + return expression; } @@ -3689,16 +3701,17 @@ namespace ts { // the last two CallExpression productions. 2) We see 'import' which must start import call. // 3)we have a MemberExpression which either completes the LeftHandSideExpression, // or starts the beginning of the first four CallExpression productions. - + let expression: MemberExpression; if (token() === SyntaxKind.ImportKeyword && lookAhead(nextTokenIsOpenParen)) { // We don't want to eagerly consume all import keyword as import call expression so we look a head to find "(" // For example: // var foo3 = require("subfolder // import * as foo1 from "module-from-node -> we want this import to be a statement rather than import call expression - const importCall = parseImportCallExpression(); - return importCall; + expression = parseTokenNode(); + } + else { + expression = token() === SyntaxKind.SuperKeyword ? parseSuperExpression() : parseMemberExpressionOrHigher(); } - const expression = token() === SyntaxKind.SuperKeyword ? parseSuperExpression() : parseMemberExpressionOrHigher(); // Now, we *may* be complete. However, we might have consumed the start of a // CallExpression. As such, we need to consume the rest of it here to be complete. @@ -3772,15 +3785,6 @@ namespace ts { return finishNode(node); } - function parseImportCallExpression(): ImportCallExpression { - const importCallExpr = createNode(SyntaxKind.ImportCallExpression); - parseExpected(SyntaxKind.ImportKeyword); - parseExpected(SyntaxKind.OpenParenToken); - importCallExpr.specifier = parseAssignmentExpressionOrHigher(); - parseExpected(SyntaxKind.CloseParenToken); - return finishNode(importCallExpr); - } - function tagNamesAreEquivalent(lhs: JsxTagNameExpression, rhs: JsxTagNameExpression): boolean { if (lhs.kind !== rhs.kind) { return false; diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 39632061c7c..e6581c382ea 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1268,8 +1268,9 @@ namespace ts { if (isJavaScriptFile && isRequireCall(node, /*checkArgumentIsStringLiteral*/true)) { (imports || (imports = [])).push((node).arguments[0]); } - else if (isImportCallExpression(node) && node.specifier.kind === SyntaxKind.StringLiteral) { - (imports || (imports = [])).push((node).specifier); + // we can safely get the first argument in the list here because we already issue parsing error if the length is not 1 + else if (isImportCall(node) && node.arguments[0].kind === SyntaxKind.StringLiteral) { + (imports || (imports = [])).push((node).arguments[0]); } else { forEachChild(node, collectImportOrRequireCalls); diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 375c44a312d..4524f660ddf 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -493,15 +493,15 @@ namespace ts { return node; } - switch (node.kind) { - case SyntaxKind.ImportCallExpression: - return visitImportCallExpression(node); - default: - return visitEachChild(node, importCallExpressionVisitor, context); + if (isImportCall(node)) { + return visitImportCallExpression(node); + } + else { + return visitEachChild(node, importCallExpressionVisitor, context); } } - function visitImportCallExpression(node: ImportCallExpression): Expression { + function visitImportCallExpression(node: ImportCall): Expression { switch (compilerOptions.module) { case ModuleKind.CommonJS: return transformImportCallExpressionCommonJS(node); @@ -513,7 +513,7 @@ namespace ts { Debug.fail("All supported module kind in this transformation step should have been handled"); } - function transformImportCallExpressionUMD(node: ImportCallExpression): Expression { + function transformImportCallExpressionUMD(node: ImportCall): Expression { // (function (factory) { // ... (regular UMD) // } @@ -532,7 +532,7 @@ namespace ts { ); } - function transformImportCallExpressionAMD(node: ImportCallExpression): Expression { + function transformImportCallExpressionAMD(node: ImportCall): Expression { // improt("./blah") // emit as // define(["require", "exports", "blah"], function (require, exports) { @@ -550,13 +550,13 @@ namespace ts { [createParameter(/*decorator*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, /*name*/ resolve)], /*type*/ undefined, createToken(SyntaxKind.EqualsGreaterThanToken), - createCall(createIdentifier("require"), /*typeArguments*/ undefined, [createArrayLiteral([node.specifier]), resolve]) + createCall(createIdentifier("require"), /*typeArguments*/ undefined, [createArrayLiteral([node.arguments[0]]), resolve]) ) ] ); } - function transformImportCallExpressionCommonJS(node: ImportCallExpression): Expression { + function transformImportCallExpressionCommonJS(node: ImportCall): Expression { // import("./blah") // emit as // Promise.resolve().then(() => require("./blah")); @@ -567,7 +567,7 @@ namespace ts { createCall(/*expression*/ createPropertyAccess(createIdentifier("Promise"), "resolve"), /*typeArguments*/ undefined, /*argumentsArray*/[]), "then"), /*typeArguments*/ undefined, - [createArrowFunction(/*modifiers*/ undefined, /*typeParameters*/ undefined, /*parameters*/ undefined, /*type*/ undefined, createToken(SyntaxKind.EqualsGreaterThanToken), createCall(createIdentifier("require"), /*typeArguments*/ undefined, [node.specifier]))] + [createArrowFunction(/*modifiers*/ undefined, /*typeParameters*/ undefined, /*parameters*/ undefined, /*type*/ undefined, createToken(SyntaxKind.EqualsGreaterThanToken), createCall(createIdentifier("require"), /*typeArguments*/ undefined, [node.arguments[0]]))] ); } diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 2c2466f8c82..03a1c8cb00b 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -1459,7 +1459,7 @@ namespace ts { && node.kind === SyntaxKind.BinaryExpression) { return visitDestructuringAssignment(node); } - else if (isImportCallExpression(node)) { + else if (isImportCall(node)) { return visitImportCallExpression(node); } else if ((node.transformFlags & TransformFlags.ContainsDestructuringAssignment) || (node.transformFlags & TransformFlags.ContainsDynamicImport)) { @@ -1470,7 +1470,7 @@ namespace ts { } } - function visitImportCallExpression(node: ImportCallExpression): Expression { + function visitImportCallExpression(node: ImportCall): Expression { // import("./blah") // emit as // System.register([], function (_export, _context) { @@ -1487,7 +1487,7 @@ namespace ts { createIdentifier("import") ), /*typeArguments*/ undefined, - [node.specifier] + [node.arguments[0]] ); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 3a08bd45084..762bbef3f82 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -391,9 +391,6 @@ namespace ts { MergeDeclarationMarker, EndOfDeclarationMarker, - // Dynamic import - ImportCallExpression, - // Enum value count Count, @@ -1447,9 +1444,12 @@ namespace ts { expression: SuperExpression; } - export interface ImportCallExpression extends LeftHandSideExpression, Declaration { - kind: SyntaxKind.ImportCallExpression; - specifier: Expression; + export interface ImportExpression extends PrimaryExpression { + kind: SyntaxKind.ImportKeyword; + } + + export interface ImportCall extends CallExpression { + expression: ImportExpression; } export interface ExpressionWithTypeArguments extends TypeNode { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 47c26301d1b..4eb78736e16 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -662,8 +662,8 @@ namespace ts { return n.kind === SyntaxKind.CallExpression && (n).expression.kind === SyntaxKind.SuperKeyword; } - export function isImportCallExpression(n: Node): n is ImportCallExpression { - return n.kind === SyntaxKind.ImportCallExpression; + export function isImportCall(n: Node): n is ImportCall { + return n.kind === SyntaxKind.CallExpression && (n).expression.kind === SyntaxKind.ImportKeyword; } export function isPrologueDirective(node: Node): node is PrologueDirective { @@ -3891,7 +3891,6 @@ namespace ts { || kind === SyntaxKind.SuperKeyword || kind === SyntaxKind.NonNullExpression || kind === SyntaxKind.MetaProperty - || kind === SyntaxKind.ImportCallExpression; } export function isLeftHandSideExpression(node: Node): node is LeftHandSideExpression { diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 91ae6560c7d..0f4a4ed6561 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -339,10 +339,6 @@ namespace ts { visitNodes((node).typeArguments, visitor, isTypeNode), visitNodes((node).arguments, visitor, isExpression)); - case SyntaxKind.ImportCallExpression: - return updateImportCall(node, - visitNode((node).specifier, visitor, isExpression)); - case SyntaxKind.NewExpression: return updateNew(node, visitNode((node).expression, visitor, isExpression),