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 all 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
6 changes: 5 additions & 1 deletion src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2333,7 +2333,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 @@ -2741,6 +2741,10 @@ namespace ts {
transformFlags |= TransformFlags.AssertES2015;
}

if (expression.kind === SyntaxKind.ImportKeyword) {
transformFlags |= TransformFlags.ContainsDynamicImport;
}

node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.ArrayLiteralOrCallOrNewExcludes;
}
Expand Down
70 changes: 67 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8356,6 +8356,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 @@ -16123,6 +16129,35 @@ namespace ts {
return getReturnTypeOfSignature(signature);
}

function checkImportCallExpression(node: ImportCall): Type {
// Check grammar of dynamic import
checkGrammarArguments(node, node.arguments) || checkGrammarImportCallExpression(node);

if (node.arguments.length === 0) {
return createPromiseReturnType(node, anyType);
}
const specifier = node.arguments[0];
const specifierType = checkExpressionCached(specifier);
// Even though multiple arugments is grammatically incorrect, type-check extra arguments for completion
for (let i = 1; i < node.arguments.length; ++i) {
checkExpressionCached(node.arguments[i]);
}

if (specifierType.flags & TypeFlags.Undefined || specifierType.flags & TypeFlags.Null || !isTypeAssignableTo(specifierType, stringType)) {
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, specifier);
if (moduleSymbol) {
const esModuleSymbol = resolveESModuleSymbol(moduleSymbol, specifier, /*dontRecursivelyResolve*/ true);
if (esModuleSymbol) {
return createPromiseReturnType(node, getTypeOfSymbol(esModuleSymbol));
}
}
return createPromiseReturnType(node, anyType);
}

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

function createPromiseReturnType(func: FunctionLikeDeclaration, promisedType: Type) {
function createPromiseReturnType(func: FunctionLikeDeclaration | ImportCall, 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, isImportCall(func) ?
Diagnostics.A_dynamic_import_call_returns_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, 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);
}

return promiseType;
Expand Down Expand Up @@ -17695,6 +17734,10 @@ namespace ts {
case SyntaxKind.ElementAccessExpression:
return checkIndexedAccess(<ElementAccessExpression>node);
case SyntaxKind.CallExpression:
if ((<CallExpression>node).expression.kind === SyntaxKind.ImportKeyword) {
return checkImportCallExpression(<ImportCall>node);
}
/* falls through */
case SyntaxKind.NewExpression:
return checkCallExpression(<CallExpression>node);
case SyntaxKind.TaggedTemplateExpression:
Expand Down Expand Up @@ -24620,6 +24663,27 @@ namespace ts {
});
return result;
}

function checkGrammarImportCallExpression(node: ImportCall): boolean {
if (modulekind === ModuleKind.ES2015) {
return grammarErrorOnNode(node, Diagnostics.Dynamic_import_cannot_be_used_when_targeting_ECMAScript_2015_modules);
}

if (node.typeArguments) {
return grammarErrorOnNode(node, Diagnostics.Dynamic_import_cannot_have_type_arguments);
}

const arguments = node.arguments;
if (arguments.length !== 1) {
return grammarErrorOnNode(node, Diagnostics.Dynamic_import_must_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 as specifier in dynamic import.
if (isSpreadElement(arguments[0])) {
return grammarErrorOnNode(arguments[0], Diagnostics.Specifier_of_dynamic_import_cannot_be_spread_element);
}
}
}

/** Like 'isDeclarationName', but returns true for LHS of `import { x as y }` or `export { x as y }`. */
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,12 @@ namespace ts {
"umd": ModuleKind.UMD,
"es6": ModuleKind.ES2015,
"es2015": ModuleKind.ES2015,
"esnext": ModuleKind.ESNext
}),
paramType: Diagnostics.KIND,
showInSimplifiedHelpView: true,
category: Diagnostics.Basic_Options,
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_ESNext,
},
{
name: "lib",
Expand Down
36 changes: 31 additions & 5 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -883,6 +883,23 @@
"category": "Error",
"code": 1322
},
"Dynamic import cannot be used when targeting ECMAScript 2015 modules.": {
"category": "Error",
"code": 1323
},
"Dynamic import must have one specifier as an argument.": {
"category": "Error",
"code": 1324
},
"Specifier of dynamic import cannot be spread element.": {
"category": "Error",
"code": 1325
},
"Dynamic import cannot have type arguments": {
"category": "Error",
"code": 1326
},

"Duplicate identifier '{0}'.": {
"category": "Error",
"code": 2300
Expand Down Expand Up @@ -1923,10 +1940,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 @@ -2159,6 +2172,14 @@
"category": "Error",
"code": 2710
},
"A dynamic import call returns a 'Promise'. Make sure you have a declaration for 'Promise' or include 'ES2015' in your `--lib` option.": {
"category": "Error",
"code": 2711
},
"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": 2712
},

"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
Expand Down Expand Up @@ -2625,7 +2646,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 'ESNext'.": {
"category": "Message",
"code": 6016
},
Expand Down Expand Up @@ -3361,6 +3382,11 @@
"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
1 change: 1 addition & 0 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,7 @@ namespace ts {
case SyntaxKind.SuperKeyword:
case SyntaxKind.TrueKeyword:
case SyntaxKind.ThisKeyword:
case SyntaxKind.ImportKeyword:
writeTokenNode(node);
return;

Expand Down
67 changes: 42 additions & 25 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,16 @@ namespace ts {
}
}

// Invokes a callback for each child of the given node. The 'cbNode' callback is invoked for all child nodes
// stored in properties. If a 'cbNodes' callback is specified, it is invoked for embedded arrays; otherwise,
// embedded arrays are flattened and the 'cbNode' callback is invoked for each element. If a callback returns
// a truthy value, iteration stops and that value is returned. Otherwise, undefined is returned.
/**
* Invokes a callback for each child of the given node. The 'cbNode' callback is invoked for all child nodes
* stored in properties. If a 'cbNodes' callback is specified, it is invoked for embedded arrays; otherwise,
* embedded arrays are flattened and the 'cbNode' callback is invoked for each element. If a callback returns
* a truthy value, iteration stops and that value is returned. Otherwise, undefined is returned.
*
* @param node a given node to visit its children
* @param cbNode a callback to be invoked for all child nodes
* @param cbNodeArray a callback to be invoked for embedded array
*/
export function forEachChild<T>(node: Node, cbNode: (node: Node) => T | undefined, cbNodeArray?: (nodes: NodeArray<Node>) => T | undefined): T | undefined {
if (!node) {
return;
Expand Down Expand Up @@ -2409,7 +2415,7 @@ namespace ts {
if (token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.LessThanToken) {
return parseSignatureMember(SyntaxKind.CallSignature);
}
if (token() === SyntaxKind.NewKeyword && lookAhead(isStartOfConstructSignature)) {
if (token() === SyntaxKind.NewKeyword && lookAhead(nextTokenIsOpenParenOrLessThan)) {
return parseSignatureMember(SyntaxKind.ConstructSignature);
}
const fullStart = getNodePos();
Expand All @@ -2420,7 +2426,7 @@ namespace ts {
return parsePropertyOrMethodSignature(fullStart, modifiers);
}

function isStartOfConstructSignature() {
function nextTokenIsOpenParenOrLessThan() {
nextToken();
return token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.LessThanToken;
}
Expand Down Expand Up @@ -2776,6 +2782,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 +3516,10 @@ namespace ts {
* 5) --UnaryExpression[?Yield]
*/
if (isUpdateExpression()) {
const incrementExpression = parseIncrementExpression();
const updateExpression = parseUpdateExpression();
return token() === SyntaxKind.AsteriskAsteriskToken ?
<BinaryExpression>parseBinaryExpressionRest(getBinaryOperatorPrecedence(), incrementExpression) :
incrementExpression;
<BinaryExpression>parseBinaryExpressionRest(getBinaryOperatorPrecedence(), updateExpression) :
updateExpression;
}

/**
Expand Down Expand Up @@ -3578,7 +3585,7 @@ namespace ts {
}
// falls through
default:
return parseIncrementExpression();
return parseUpdateExpression();
}
}

Expand All @@ -3594,7 +3601,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 @@ -3618,17 +3625,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 @@ -3678,25 +3685,35 @@ 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.
let expression: MemberExpression;
if (token() === SyntaxKind.ImportKeyword && lookAhead(nextTokenIsOpenParenOrLessThan)) {
// 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
sourceFile.flags |= NodeFlags.PossiblyContainDynamicImport;
expression = parseTokenNode<PrimaryExpression>();
}
else {
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 @@ -4790,11 +4807,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