fix(language-service): support TypeScript 2.1 (#13655)

@angular/language-service now supports using TypeScript 2.1 as the
the TypeScript host. TypeScript 2.1 is now also partially supported
in `ngc` but is not recommended as Tsickle does not yet support 2.1.
This commit is contained in:
Chuck Jazdzewski
2017-01-05 11:34:42 -08:00
committed by Igor Minar
parent 21030e9a1c
commit 8063b0d9a2
7 changed files with 84 additions and 28 deletions

View File

@ -12,6 +12,16 @@ import {Evaluator, errorSymbol, isPrimitive} from './evaluator';
import {ClassMetadata, ConstructorMetadata, FunctionMetadata, MemberMetadata, MetadataEntry, MetadataError, MetadataMap, MetadataSymbolicBinaryExpression, MetadataSymbolicCallExpression, MetadataSymbolicExpression, MetadataSymbolicIfExpression, MetadataSymbolicIndexExpression, MetadataSymbolicPrefixExpression, MetadataSymbolicReferenceExpression, MetadataSymbolicSelectExpression, MetadataSymbolicSpreadExpression, MetadataValue, MethodMetadata, ModuleExportMetadata, ModuleMetadata, VERSION, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataSymbolicExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSelectExpression, isMethodMetadata} from './schema';
import {Symbols} from './symbols';
// In TypeScript 2.1 these flags moved
// These helpers work for both 2.0 and 2.1.
const isExport = (ts as any).ModifierFlags ?
((node: ts.Node) =>
!!((ts as any).getCombinedModifierFlags(node) & (ts as any).ModifierFlags.Export)) :
((node: ts.Node) => !!((node.flags & (ts as any).NodeFlags.Export)));
const isStatic = (ts as any).ModifierFlags ?
((node: ts.Node) =>
!!((ts as any).getCombinedModifierFlags(node) & (ts as any).ModifierFlags.Static)) :
((node: ts.Node) => !!((node.flags & (ts as any).NodeFlags.Static)));
/**
* A set of collector options to use when collecting metadata.
@ -146,7 +156,7 @@ export class MetadataCollector {
case ts.SyntaxKind.MethodDeclaration:
isConstructor = member.kind === ts.SyntaxKind.Constructor;
const method = <ts.MethodDeclaration|ts.ConstructorDeclaration>member;
if (method.flags & ts.NodeFlags.Static) {
if (isStatic(method)) {
const maybeFunc = maybeGetSimpleFunction(<ts.MethodDeclaration>method);
if (maybeFunc) {
recordStaticMember(maybeFunc.name, maybeFunc.func);
@ -193,7 +203,7 @@ export class MetadataCollector {
case ts.SyntaxKind.GetAccessor:
case ts.SyntaxKind.SetAccessor:
const property = <ts.PropertyDeclaration>member;
if (property.flags & ts.NodeFlags.Static) {
if (isStatic(property)) {
const name = evaluator.nameOf(property.name);
if (!isMetadataError(name)) {
if (property.initializer) {
@ -244,7 +254,7 @@ export class MetadataCollector {
const isExportedIdentifier = (identifier: ts.Identifier) => exportMap.has(identifier.text);
const isExported = (node: ts.FunctionDeclaration | ts.ClassDeclaration | ts.EnumDeclaration) =>
(node.flags & ts.NodeFlags.Export) || isExportedIdentifier(node.name);
isExport(node) || isExportedIdentifier(node.name);
const exportedIdentifierName = (identifier: ts.Identifier) =>
exportMap.get(identifier.text) || identifier.text;
const exportedName =
@ -404,8 +414,7 @@ export class MetadataCollector {
varValue = recordEntry(errorSym('Variable not initialized', nameNode), nameNode);
}
let exported = false;
if (variableStatement.flags & ts.NodeFlags.Export ||
variableDeclaration.flags & ts.NodeFlags.Export ||
if (isExport(variableStatement) || isExport(variableDeclaration) ||
isExportedIdentifier(nameNode)) {
if (!metadata) metadata = {};
metadata[exportedIdentifierName(nameNode)] = recordEntry(varValue, node);
@ -430,13 +439,13 @@ export class MetadataCollector {
// or
// var [<identifier>[, <identifier}+] = <expression>;
// are not supported.
const report = (nameNode: ts.Node) => {
const report: (nameNode: ts.Node) => void = (nameNode: ts.Node) => {
switch (nameNode.kind) {
case ts.SyntaxKind.Identifier:
const name = <ts.Identifier>nameNode;
const varValue = errorSym('Destructuring not supported', nameNode);
const varValue = errorSym('Destructuring not supported', name);
locals.define(name.text, varValue);
if (node.flags & ts.NodeFlags.Export) {
if (isExport(node)) {
if (!metadata) metadata = {};
metadata[name.text] = varValue;
}
@ -448,7 +457,7 @@ export class MetadataCollector {
case ts.SyntaxKind.ObjectBindingPattern:
case ts.SyntaxKind.ArrayBindingPattern:
const bindings = <ts.BindingPattern>nameNode;
bindings.elements.forEach(report);
(bindings as any).elements.forEach(report);
break;
}
};
@ -630,7 +639,10 @@ function namesOf(parameters: ts.NodeArray<ts.ParameterDeclaration>): string[] {
} else {
const bindingPattern = <ts.BindingPattern>name;
for (const element of bindingPattern.elements) {
addNamesOf(element.name);
const name = (element as any).name;
if (name) {
addNamesOf(name);
}
}
}
}

View File

@ -119,9 +119,19 @@ export class MetadataWriterHost extends DelegatingHost {
// released
if (/*DTS*/ /\.js$/.test(emitFilePath)) {
const path = emitFilePath.replace(/*DTS*/ /\.js$/, '.metadata.json');
// Beginning with 2.1, TypeScript transforms the source tree before emitting it.
// We need the original, unmodified, tree which might be several levels back
// depending on the number of transforms performed. All SourceFile's prior to 2.1
// will appear to be the original source since they didn't include an original field.
let collectableFile = sourceFile;
while ((collectableFile as any).original) {
collectableFile = (collectableFile as any).original;
}
const metadata =
this.metadataCollector.getMetadata(sourceFile, !!this.ngOptions.strictMetadataEmit);
const metadata1 = this.metadataCollector1.getMetadata(sourceFile, false);
this.metadataCollector.getMetadata(collectableFile, !!this.ngOptions.strictMetadataEmit);
const metadata1 = this.metadataCollector1.getMetadata(collectableFile, false);
const metadatas: ModuleMetadata[] = [metadata, metadata1].filter(e => !!e);
if (metadatas.length) {
const metadataText = JSON.stringify(metadatas);

View File

@ -12,6 +12,10 @@ import {CollectorOptions} from './collector';
import {MetadataEntry, MetadataError, MetadataGlobalReferenceExpression, MetadataImportedSymbolReferenceExpression, MetadataSymbolicCallExpression, MetadataSymbolicReferenceExpression, MetadataValue, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSpreadExpression} from './schema';
import {Symbols} from './symbols';
// In TypeScript 2.1 the spread element kind was renamed.
const spreadElementSyntaxKind: ts.SyntaxKind =
(ts.SyntaxKind as any).SpreadElement || (ts.SyntaxKind as any).SpreadElementExpression;
function isMethodCallOf(callExpression: ts.CallExpression, memberName: string): boolean {
const expression = callExpression.expression;
if (expression.kind === ts.SyntaxKind.PropertyAccessExpression) {
@ -282,9 +286,8 @@ export class Evaluator {
});
if (error) return error;
return arr;
case ts.SyntaxKind.SpreadElementExpression:
let spread = <ts.SpreadElementExpression>node;
let spreadExpression = this.evaluateNode(spread.expression);
case spreadElementSyntaxKind:
let spreadExpression = this.evaluateNode((node as any).expression);
return recordEntry({__symbolic: 'spread', expression: spreadExpression}, node);
case ts.SyntaxKind.CallExpression:
const callExpression = <ts.CallExpression>node;