Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Master] wip-dynamic import #14774

Merged
merged 76 commits into from
Jun 5, 2017
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
787ed37
Use term UpdateExpression instead of IncrementExpression to keep it c…
Mar 8, 2017
ca08167
Wip-parsing import call expression
Mar 8, 2017
330f7bf
Correctly parse import()
Mar 9, 2017
e7b3a15
Merge branch 'master' into master-dynamicImport
Mar 10, 2017
827abb3
Parse import call and use collect its specifier
Mar 10, 2017
6933b58
Wip-type check dynamic import
Mar 11, 2017
126fb64
Wip-type check dynamic import
Mar 11, 2017
bd50ccf
Merge branch 'master-dynamicImport' of https://github.com/Microsoft/T…
Mar 11, 2017
7340d84
Rename importcall to importCallExpression
Mar 13, 2017
18c826b
Add new value "es2018" for module kind
Mar 13, 2017
ddf5ed9
Emit dynamic import when module kind is es2018
Mar 13, 2017
26f9a52
Add initial tests
Mar 14, 2017
20293b0
Fix up error when we can't find promise type in import call expression
Mar 14, 2017
369af08
Change filename to indicate it uses module kind of es2018
Mar 14, 2017
ffbb445
wip-Emit import call expression for commonjs
Mar 14, 2017
be375cb
Fix missing import call expression in function and class declaration
Mar 14, 2017
a84645c
Add more tests for emitting of es2018 module-kind
Mar 14, 2017
9864b4e
Emit import callExpression for UMD and AMD
Mar 14, 2017
86c7129
wip-system emit
Mar 15, 2017
6db02e7
Fix up incorrect system emit
Mar 16, 2017
a77f0d2
Do not collect imports in parser as it makes it very hard for increme…
Mar 17, 2017
dbac66c
Update baselines from adding es2018 value into moduleKind
Mar 17, 2017
804ab2c
Remove duplicat es2018 tests
Mar 17, 2017
1af1005
Correctly bind import call expression
Mar 17, 2017
e019216
Use emitFlags to indicate that the subtree contains dynamic import
Mar 20, 2017
015f71b
Issue error when use with ES2015
Mar 20, 2017
8fd660b
Report no implicit error when we return Promise<any>
Mar 21, 2017
f04f7b5
Add comment for comparable relationship
Mar 21, 2017
871d609
Report an error if dynamic import specifier is not assigable to type …
Mar 21, 2017
15ca0af
Add tests
Mar 21, 2017
2332ea3
small fix on indentation
Mar 21, 2017
91d9ecf
Simply parse dynamic import as call-expression....
Mar 22, 2017
c798489
Move error report of incorrect grammar in dynamic import to checker
Mar 22, 2017
c62f4f5
Update tests
Mar 22, 2017
e1ba855
Add declaration emit test and test to make sure we don't give dynamic…
Mar 23, 2017
265d0c0
Fix where we report noImplicitAny error in dynamic import
Mar 24, 2017
640f2c7
Error when dynamic import has type arguments
Mar 24, 2017
61f199e
Update tests
Mar 24, 2017
486dc91
Change comment to jsdoc
Mar 27, 2017
6080c3e
Remove error with noImplicitAny
Mar 28, 2017
32e5cf7
Fix AMD emit
Mar 28, 2017
249f446
Update tests
Mar 28, 2017
f3306db
Update baselines
Mar 28, 2017
11f6eae
Merge branch 'master' into master-dynamicImport
Mar 29, 2017
d4754db
Update baseline from merging with master
Mar 29, 2017
9715052
Merge branch 'master' into master-dynamicImport
Mar 29, 2017
ca65996
Merge branch 'master' into master-dynamicImport
Apr 4, 2017
faaa38d
Update PR
Apr 4, 2017
55430c4
Add boolean flag to not walk the tree if there is no dynamic import
Apr 4, 2017
78b8275
Fix linting error
Apr 4, 2017
d1d5cac
Address PR: Remove duplication
Apr 6, 2017
2b96374
fix no-bom linting rule
Apr 6, 2017
2f61d47
Address minor PR comment
Apr 26, 2017
a10e668
Move check for module kind is ES2015 into grammar check; Use checkNon…
Apr 26, 2017
e50667e
Address minor PR comment
Apr 26, 2017
feb41f7
Merge branch 'master' into master-dynamicImport
Apr 26, 2017
742d515
Fix baselines and function call from merging master
Apr 26, 2017
35e2289
Address PR:
Apr 28, 2017
a2d9fd4
Add target ES5 emit
Apr 28, 2017
1b0d020
Add tests and update baselines
Apr 28, 2017
1b7d3bf
Merge branch 'master' into master-dynamicImport
May 4, 2017
faab927
Merge branch 'master' into master-dynamicImport
May 24, 2017
a02edb1
Address PR: don't early exit when there are grammar errors
Jun 2, 2017
72ba23c
Address PR: change order of grammar check
Jun 2, 2017
e6d7327
Address PR: error message, fix capitalization, only allow functionLik…
Jun 2, 2017
d9e2033
Address PR: remove __resolved when emit for commonJs and just do Prom…
Jun 2, 2017
769f6ad
Update baselines
Jun 2, 2017
2f476bf
Merge branch 'master' into master-dynamicImport
Jun 2, 2017
9203f95
Update name change of isSpreadExpression
Jun 2, 2017
4733f0d
Fix linting
Jun 2, 2017
c2056c0
Address minor error messages
Jun 2, 2017
e386d65
Use ESNext instead of ES2018
Jun 5, 2017
3118afe
Remove ES2018 folder
Jun 5, 2017
75aa4bd
Rename test file with ES2018 to ESNext
Jun 5, 2017
7d64ec9
Update baselines from moving out of es2018
Jun 5, 2017
1729ea8
Update command line
Jun 5, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2317,7 +2317,7 @@ namespace ts {
// A common practice in node modules is to set 'export = module.exports = {}', this ensures that 'exports'
// is still pointing to 'module.exports'.
// We do not want to consider this as 'export=' since a module can have only one of these.
// Similarlly we do not want to treat 'module.exports = exports' as an 'export='.
// Similarly we do not want to treat 'module.exports = exports' as an 'export='.
const assignedExpression = getRightMostAssignedExpression(node.right);
if (isEmptyObjectLiteral(assignedExpression) || isExportsOrModuleExportsOrAlias(assignedExpression)) {
// Mark it as a module in case there are no other exports in the file
Expand Down Expand Up @@ -3495,6 +3495,9 @@ namespace ts {
case SyntaxKind.BreakStatement:
transformFlags |= TransformFlags.ContainsHoistedDeclarationOrCompletion;
break;

case SyntaxKind.ImportCallExpression:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a note: there were talks in the past about attaching meta-properties to the import keyword (similar to new.target), though there hasn't been a concrete proposal for it yet. It'll probably become needed as node iterates on their implementation.

Also, estree and babylon went with Import for this AST node.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sound like a good idea to prepare for that....

Copy link

@Jessidhia Jessidhia Mar 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that I'm back on a computer, I found the discussion in estree's repository about the AST node: estree/estree#137

And this is how it got merged in their spec: https://github.com/estree/estree/blob/8ab627557/experimental/import-expression.md

Thinking about it, (when it gets meta-properties) it'll behave pretty similarly to super, being both callable and "indexable".

Copy link
Contributor Author

@yuit yuit Mar 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Kovensky That is what I was thinking about doing. treat it similar to super -> which we either parse as callExpression or propertyAccessexpression

Update: extra benefit is that now for future, it is also possible to use type argument of callexpression to express the shape of return module object

transformFlags |= TransformFlags.ContainsDynamicImport;
}

node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
Expand Down
41 changes: 38 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7434,6 +7434,12 @@ namespace ts {
/**
* This is *not* a bi-directional relationship.
* If one needs to check both directions for comparability, use a second call to this function or 'checkTypeComparableTo'.
*
* 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.
* It is used to check following cases:
* - the types of the left and right sides of equality/inequality operators (`===`, `!==`, `==`, `!=`).
* - the types of `case` clause expressions and their respective `switch` expressions.
* - the type of an expression in a type assertion with the type being asserted.
*/
function isTypeComparableTo(source: Type, target: Type): boolean {
return isTypeRelatedTo(source, target, comparableRelation);
Expand Down Expand Up @@ -14862,6 +14868,29 @@ namespace ts {
return getReturnTypeOfSignature(signature);
}

function checkImportCallExpression(node: ImportCallExpression): Type {
if (modulekind === ModuleKind.ES2015) {
grammarErrorOnNode(node, Diagnostics.Dynamic_import_cannot_be_used_when_targeting_ECMAScript_2015_modules);
}

const specifierType = checkExpression(node.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));
}
// resolveExternalModuleName will return undefined if the moduleReferenceExpression is not a string literal
const moduleSymbol = resolveExternalModuleName(node, node.specifier);
if (moduleSymbol) {
const esModuleSymbol = resolveESModuleSymbol(moduleSymbol, node.specifier);
if (esModuleSymbol) {
return createPromiseReturnType(node, getTypeOfSymbol(esModuleSymbol));
}
}
if (noImplicitAny) {
error(node, Diagnostics.Cannot_resolve_dynamic_import_implicitly_has_a_Promise_any_type);
}
return createPromiseReturnType(node, anyType);
}

function isCommonJsRequire(node: Node) {
if (!isRequireCall(node, /*checkArgumentIsStringLiteral*/true)) {
return false;
Expand Down Expand Up @@ -15068,14 +15097,18 @@ namespace ts {
return emptyObjectType;
}

function createPromiseReturnType(func: FunctionLikeDeclaration, promisedType: Type) {
function createPromiseReturnType(func: FunctionLikeDeclaration | ImportCallExpression, promisedType: Type) {
const promiseType = createPromiseType(promisedType);
if (promiseType === emptyObjectType) {
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);
error(func, isImportCallExpression(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, 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);
error(func, isImportCallExpression(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);
}

return promiseType;
Expand Down Expand Up @@ -16398,6 +16431,8 @@ namespace ts {
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
return checkCallExpression(<CallExpression>node);
case SyntaxKind.ImportCallExpression:
return checkImportCallExpression(<ImportCallExpression>node);
case SyntaxKind.TaggedTemplateExpression:
return checkTaggedTemplateExpression(<TaggedTemplateExpression>node);
case SyntaxKind.ParenthesizedExpression:
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,9 @@ namespace ts {
"umd": ModuleKind.UMD,
"es6": ModuleKind.ES2015,
"es2015": ModuleKind.ES2015,
"es2018": ModuleKind.ES2018
}),
description: Diagnostics.Specify_module_code_generation_Colon_commonjs_amd_system_umd_or_es2015,
description: Diagnostics.Specify_module_code_generation_Colon_commonjs_amd_system_umd_es2015_or_es2018,
paramType: Diagnostics.KIND,
},
{
Expand Down
26 changes: 21 additions & 5 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,10 @@
"category": "Error",
"code": 1319
},
"Dynamic import cannot be used when targeting ECMAScript 2015 modules.": {
"category": "Error",
"code": 1320
},
"Duplicate identifier '{0}'.": {
"category": "Error",
"code": 2300
Expand Down Expand Up @@ -1863,10 +1867,6 @@
"category": "Error",
"code": 2649
},
"Cannot emit namespaced JSX elements in React.": {
"category": "Error",
"code": 2650
},
"A member initializer in a enum declaration cannot reference members declared after it, including members defined in other enums.": {
"category": "Error",
"code": 2651
Expand Down Expand Up @@ -2091,6 +2091,14 @@
"category": "Error",
"code": 2707
},
"A dynamic import call must return a 'Promise'. Make sure you have a declaration for 'Promise' or include 'ES2015' in your `--lib` option.": {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you cannot set the return type for import(), this message is a bit confusing with the "must" text. Consider this instead: "A dynamic import call returns a 'Promise'. Make sure you have a...

"category": "Error",
"code": 2708
},
"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.": {
"category": "Error",
"code": 2709
},

"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
Expand Down Expand Up @@ -2561,7 +2569,7 @@
"category": "Message",
"code": 6015
},
"Specify module code generation: 'commonjs', 'amd', 'system', 'umd' or 'es2015'.": {
"Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'es2018'.": {
"category": "Message",
"code": 6016
},
Expand Down Expand Up @@ -3153,6 +3161,14 @@
"category": "Error",
"code": 7034
},
"Cannot resolve dynamic import, implicitly has a 'Promise<any>' type.": {
"category": "Error",
"code": 7035
},
"Dynamic import's specifier must be of type 'string', but here has type '{0}'.": {
"category": "Error",
"code": 7036
},
"You cannot rename this element.": {
"category": "Error",
"code": 8000
Expand Down
9 changes: 9 additions & 0 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,8 @@ namespace ts {
return emitCallExpression(<CallExpression>node);
case SyntaxKind.NewExpression:
return emitNewExpression(<NewExpression>node);
case SyntaxKind.ImportCallExpression:
return emitImportCallExpression(<ImportCallExpression>node);
case SyntaxKind.TaggedTemplateExpression:
return emitTaggedTemplateExpression(<TaggedTemplateExpression>node);
case SyntaxKind.TypeAssertionExpression:
Expand Down Expand Up @@ -1153,6 +1155,13 @@ 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(" ");
Expand Down
12 changes: 12 additions & 0 deletions src/compiler/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,18 @@ namespace ts {
: node;
}

export function createImportCall(specifier: Expression): ImportCallExpression {
const node = <ImportCallExpression>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 = <TaggedTemplateExpression>createSynthesizedNode(SyntaxKind.TaggedTemplateExpression);
node.tag = parenthesizeForAccess(tag);
Expand Down
58 changes: 39 additions & 19 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ namespace ts {
return visitNode(cbNode, (<CallExpression>node).expression) ||
visitNodes(cbNodes, (<CallExpression>node).typeArguments) ||
visitNodes(cbNodes, (<CallExpression>node).arguments);
case SyntaxKind.ImportCallExpression:
return visitNode(cbNode, (<ImportCallExpression>node).specifier);
case SyntaxKind.TaggedTemplateExpression:
return visitNode(cbNode, (<TaggedTemplateExpression>node).tag) ||
visitNode(cbNode, (<TaggedTemplateExpression>node).template);
Expand Down Expand Up @@ -2776,6 +2778,7 @@ namespace ts {
case SyntaxKind.SlashToken:
case SyntaxKind.SlashEqualsToken:
case SyntaxKind.Identifier:
case SyntaxKind.ImportKeyword:
return true;
default:
return isIdentifier();
Expand Down Expand Up @@ -3509,10 +3512,10 @@ namespace ts {
* 5) --UnaryExpression[?Yield]
*/
if (isUpdateExpression()) {
const incrementExpression = parseIncrementExpression();
const UpdateExpression = parseUpdateExpression();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Capitalization. Variable name should be updateExpression

return token() === SyntaxKind.AsteriskAsteriskToken ?
<BinaryExpression>parseBinaryExpressionRest(getBinaryOperatorPrecedence(), incrementExpression) :
incrementExpression;
<BinaryExpression>parseBinaryExpressionRest(getBinaryOperatorPrecedence(), UpdateExpression) :
UpdateExpression;
}

/**
Expand Down Expand Up @@ -3577,7 +3580,7 @@ namespace ts {
return parseAwaitExpression();
}
default:
return parseIncrementExpression();
return parseUpdateExpression();
}
}

Expand All @@ -3593,7 +3596,7 @@ namespace ts {
*/
function isUpdateExpression(): boolean {
// This function is called inside parseUnaryExpression to decide
// whether to call parseSimpleUnaryExpression or call parseIncrementExpression directly
// whether to call parseSimpleUnaryExpression or call parseUpdateExpression directly
switch (token()) {
case SyntaxKind.PlusToken:
case SyntaxKind.MinusToken:
Expand All @@ -3617,17 +3620,17 @@ namespace ts {
}

/**
* Parse ES7 IncrementExpression. IncrementExpression is used instead of ES6's PostFixExpression.
* Parse ES7 UpdateExpression. UpdateExpression is used instead of ES6's PostFixExpression.
*
* ES7 IncrementExpression[yield]:
* ES7 UpdateExpression[yield]:
* 1) LeftHandSideExpression[?yield]
* 2) LeftHandSideExpression[?yield] [[no LineTerminator here]]++
* 3) LeftHandSideExpression[?yield] [[no LineTerminator here]]--
* 4) ++LeftHandSideExpression[?yield]
* 5) --LeftHandSideExpression[?yield]
* In TypeScript (2), (3) are parsed as PostfixUnaryExpression. (4), (5) are parsed as PrefixUnaryExpression
*/
function parseIncrementExpression(): IncrementExpression {
function parseUpdateExpression(): UpdateExpression {
if (token() === SyntaxKind.PlusPlusToken || token() === SyntaxKind.MinusMinusToken) {
const node = <PrefixUnaryExpression>createNode(SyntaxKind.PrefixUnaryExpression);
node.operator = <PrefixUnaryOperator>token();
Expand Down Expand Up @@ -3677,25 +3680,33 @@ namespace ts {
// CallExpression Arguments
// CallExpression[Expression]
// CallExpression.IdentifierName
// super ( ArgumentListopt )
// import (AssignmentExpression)
// super Arguments
// super.IdentifierName
//
// Because of the recursion in these calls, we need to bottom out first. There are two
// bottom out states we can run into. Either we see 'super' which must start either of
// the last two CallExpression productions. Or we have a MemberExpression which either
// completes the LeftHandSideExpression, or starts the beginning of the first four
// CallExpression productions.
const expression = token() === SyntaxKind.SuperKeyword
? parseSuperExpression()
: parseMemberExpressionOrHigher();
// Because of the recursion in these calls, we need to bottom out first. There are three
// bottom out states we can run into: 1) We see 'super' which must start either of
// 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.

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;
}
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.
return parseCallExpressionRest(expression);
}

function parseMemberExpressionOrHigher(): MemberExpression {
// Note: to make our lives simpler, we decompose the the NewExpression productions and
// Note: to make our lives simpler, we decompose the NewExpression productions and
// place ObjectCreationExpression and FunctionExpression into PrimaryExpression.
// like so:
//
Expand Down Expand Up @@ -3761,6 +3772,15 @@ namespace ts {
return finishNode(node);
}

function parseImportCallExpression(): ImportCallExpression {
const importCallExpr = <ImportCallExpression>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;
Expand Down Expand Up @@ -4776,11 +4796,11 @@ namespace ts {
// however, we say they are here so that we may gracefully parse them and error later.
case SyntaxKind.CatchKeyword:
case SyntaxKind.FinallyKeyword:
case SyntaxKind.ImportKeyword:
return true;

case SyntaxKind.ConstKeyword:
case SyntaxKind.ExportKeyword:
case SyntaxKind.ImportKeyword:
return isStartOfDeclaration();

case SyntaxKind.AsyncKeyword:
Expand Down
Loading