diff --git a/modules/@angular/compiler-cli/integrationtest/src/features.ts b/modules/@angular/compiler-cli/integrationtest/src/features.ts index a9ef563254..62eec6107c 100644 --- a/modules/@angular/compiler-cli/integrationtest/src/features.ts +++ b/modules/@angular/compiler-cli/integrationtest/src/features.ts @@ -1,5 +1,5 @@ import {Component, Inject, OpaqueToken} from '@angular/core'; -import {NgIf} from '@angular/common'; +import * as common from '@angular/common'; export const SOME_OPAQUE_TOKEN = new OpaqueToken('opaqueToken'); @@ -9,7 +9,7 @@ export const SOME_OPAQUE_TOKEN = new OpaqueToken('opaqueToken'); providers: [ {provide: 'strToken', useValue: 'strValue'}, {provide: SOME_OPAQUE_TOKEN, useValue: 10}, - {provide: 'reference', useValue: NgIf}, + {provide: 'reference', useValue: common.NgIf}, {provide: 'complexToken', useValue: {a: 1, b: ['test', SOME_OPAQUE_TOKEN]}}, ] }) @@ -23,7 +23,7 @@ export class CompWithProviders { {{a.value}}
{{a.value}}
`, - directives: [NgIf] + directives: [common.NgIf] }) export class CompWithReferences { } diff --git a/tools/@angular/tsc-wrapped/src/evaluator.ts b/tools/@angular/tsc-wrapped/src/evaluator.ts index 3c9a6b5039..e6cdf29276 100644 --- a/tools/@angular/tsc-wrapped/src/evaluator.ts +++ b/tools/@angular/tsc-wrapped/src/evaluator.ts @@ -1,6 +1,6 @@ import * as ts from 'typescript'; -import {MetadataValue, MetadataSymbolicCallExpression, MetadataSymbolicReferenceExpression, MetadataError, isMetadataError, isMetadataModuleReferenceExpression, isMetadataImportedSymbolReferenceExpression, isMetadataGlobalReferenceExpression,} from './schema'; +import {MetadataError, MetadataGlobalReferenceExpression, MetadataImportedSymbolReferenceExpression, MetadataSymbolicCallExpression, MetadataSymbolicReferenceExpression, MetadataValue, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicReferenceExpression} from './schema'; import {Symbols} from './symbols'; function isMethodCallOf(callExpression: ts.CallExpression, memberName: string): boolean { @@ -66,14 +66,21 @@ function getSourceFileOfNode(node: ts.Node): ts.SourceFile { export function errorSymbol( message: string, node?: ts.Node, context?: {[name: string]: string}, sourceFile?: ts.SourceFile): MetadataError { + let result: MetadataError; if (node) { sourceFile = sourceFile || getSourceFileOfNode(node); if (sourceFile) { let {line, character} = ts.getLineAndCharacterOfPosition(sourceFile, node.pos); - return {__symbolic: 'error', message, line, character, context}; + result = {__symbolic: 'error', message, line, character}; }; } - return {__symbolic: 'error', message, context}; + if (!result) { + result = {__symbolic: 'error', message}; + } + if (context) { + result.context = context; + } + return result; } /** @@ -325,27 +332,36 @@ export class Evaluator { case ts.SyntaxKind.TypeReference: const typeReferenceNode = node; const typeNameNode = typeReferenceNode.typeName; - if (typeNameNode.kind != ts.SyntaxKind.Identifier) { - return errorSymbol('Qualified type names not supported', node); - } - const typeNameIdentifier = typeReferenceNode.typeName; - const typeName = typeNameIdentifier.text; - const typeReference = this.symbols.resolve(typeName); - if (!typeReference) { - return errorSymbol('Could not resolve type', node, {typeName}); - } - if (typeReferenceNode.typeArguments && typeReferenceNode.typeArguments.length) { - const args = typeReferenceNode.typeArguments.map(element => this.evaluateNode(element)); - if (isMetadataImportedSymbolReferenceExpression(typeReference)) { - return { - __symbolic: 'reference', - module: typeReference.module, - name: typeReference.name, - arguments: args + const getReference: (typeNameNode: ts.Identifier | ts.QualifiedName) => + MetadataSymbolicReferenceExpression | MetadataError = node => { + if (typeNameNode.kind === ts.SyntaxKind.QualifiedName) { + const qualifiedName = node; + const left = this.evaluateNode(qualifiedName.left); + if (isMetadataModuleReferenceExpression(left)) { + return { + __symbolic: 'reference', module: left.module, name: qualifiedName.right.text + } + } + return errorSymbol('Qualified type names not supported', node); + } else { + const identifier = typeNameNode; + let symbol = this.symbols.resolve(identifier.text); + if (isMetadataError(symbol) || isMetadataSymbolicReferenceExpression(symbol)) { + return symbol; + } + return errorSymbol('Could not resolve type', node, {typeName: identifier.text}); + } }; - } else if (isMetadataGlobalReferenceExpression(typeReference)) { - return {__symbolic: 'reference', name: typeReference.name, arguments: args}; - } + const typeReference = getReference(typeNameNode); + if (isMetadataError(typeReference)) { + return typeReference; + } + if (!isMetadataModuleReferenceExpression(typeReference) && + typeReferenceNode.typeArguments && typeReferenceNode.typeArguments.length) { + const args = typeReferenceNode.typeArguments.map(element => this.evaluateNode(element)); + // TODO: Remove typecast when upgraded to 2.0 as it will be corretly inferred. + // Some versions of 1.9 do not infer this correctly. + (typeReference).arguments = args; } return typeReference; case ts.SyntaxKind.NoSubstitutionTemplateLiteral: diff --git a/tools/@angular/tsc-wrapped/src/schema.ts b/tools/@angular/tsc-wrapped/src/schema.ts index e6a1fdcd8d..4bb94790e8 100644 --- a/tools/@angular/tsc-wrapped/src/schema.ts +++ b/tools/@angular/tsc-wrapped/src/schema.ts @@ -165,6 +165,7 @@ export interface MetadataImportedDefaultReferenceExpression extends MetadataSymb module: string; default: boolean; + arguments?: MetadataValue[]; } export function isMetadataImportDefaultReference(value: any): value is MetadataImportedDefaultReferenceExpression { diff --git a/tools/@angular/tsc-wrapped/test/collector.spec.ts b/tools/@angular/tsc-wrapped/test/collector.spec.ts index ec27d3225a..5a32785922 100644 --- a/tools/@angular/tsc-wrapped/test/collector.spec.ts +++ b/tools/@angular/tsc-wrapped/test/collector.spec.ts @@ -14,7 +14,7 @@ describe('Collector', () => { beforeEach(() => { host = new Host(FILES, [ '/app/app.component.ts', '/app/cases-data.ts', '/app/error-cases.ts', '/promise.ts', - '/unsupported-1.ts', '/unsupported-2.ts' + '/unsupported-1.ts', '/unsupported-2.ts', 'import-star.ts' ]); service = ts.createLanguageService(host); program = service.getProgram(); @@ -212,41 +212,16 @@ describe('Collector', () => { __symbolic: 'module', version: 1, metadata: { - a: { - __symbolic: 'error', - message: 'Destructuring declarations cannot be referenced statically', - line: 1, - character: 16 - }, - b: { - __symbolic: 'error', - message: 'Destructuring declarations cannot be referenced statically', - line: 1, - character: 18 - }, - c: { - __symbolic: 'error', - message: 'Destructuring declarations cannot be referenced statically', - line: 2, - character: 16 - }, - d: { - __symbolic: 'error', - message: 'Destructuring declarations cannot be referenced statically', - line: 2, - character: 18 - }, - e: { - __symbolic: 'error', - message: 'Only intialized variables and constants can be referenced statically', - line: 3, - character: 14 - } + a: {__symbolic: 'error', message: 'Destructuring not supported', line: 1, character: 16}, + b: {__symbolic: 'error', message: 'Destructuring not supported', line: 1, character: 18}, + c: {__symbolic: 'error', message: 'Destructuring not supported', line: 2, character: 16}, + d: {__symbolic: 'error', message: 'Destructuring not supported', line: 2, character: 18}, + e: {__symbolic: 'error', message: 'Variable not initialized', line: 3, character: 14} } }); }); - it('should report an error for refrences to unexpected types', () => { + it('should report an error for references to unexpected types', () => { let unsupported1 = program.getSourceFile('/unsupported-2.ts'); let metadata = collector.getMetadata(unsupported1); let barClass = metadata.metadata['Bar']; @@ -254,11 +229,23 @@ describe('Collector', () => { let parameter = ctor.parameters[0]; expect(parameter).toEqual({ __symbolic: 'error', - message: 'Reference to non-exported class Foo', + message: 'Reference to non-exported class', line: 1, - character: 45 + character: 45, + context: {className: 'Foo'} }); }); + + it('should be able to handle import star type references', () => { + let importStar = program.getSourceFile('/import-star.ts'); + let metadata = collector.getMetadata(importStar); + let someClass = metadata.metadata['SomeClass']; + let ctor = someClass.members['__ctor__'][0]; + let parameters = ctor.parameters; + expect(parameters).toEqual([ + {__symbolic: 'reference', module: 'angular2/common', name: 'NgFor'} + ]); + }); }); // TODO: Do not use \` in a template literal as it confuses clang-format @@ -468,6 +455,15 @@ const FILES: Directory = { constructor(private f: Foo) {} } `, + 'import-star.ts': ` + import {Injectable} from 'angular2/core'; + import * as common from 'angular2/common'; + + @Injectable() + export class SomeClass { + constructor(private f: common.NgFor) {} + } + `, 'node_modules': { 'angular2': { 'core.d.ts': ` diff --git a/tools/@angular/tsc-wrapped/test/evaluator.spec.ts b/tools/@angular/tsc-wrapped/test/evaluator.spec.ts index dd509f4b7c..f47841bf1f 100644 --- a/tools/@angular/tsc-wrapped/test/evaluator.spec.ts +++ b/tools/@angular/tsc-wrapped/test/evaluator.spec.ts @@ -156,31 +156,29 @@ describe('Evaluator', () => { character: 10 }); let fDecl = findVar(errors, 'f'); - expect(evaluator.evaluateNode(fDecl.initializer)).toEqual({ - __symbolic: 'error', - message: - 'Functions cannot be evaluated statically; consider replacing with a reference to an exported function', - line: 6, - character: 11 - }); + expect(evaluator.evaluateNode(fDecl.initializer)) + .toEqual( + {__symbolic: 'error', message: 'Function call not supported', line: 6, character: 11}); let eDecl = findVar(errors, 'e'); expect(evaluator.evaluateNode(eDecl.type)).toEqual({ __symbolic: 'error', - message: 'Could not resolve type NotFound', + message: 'Could not resolve type', line: 7, - character: 10 + character: 10, + context: {typeName: 'NotFound'} }); let sDecl = findVar(errors, 's'); expect(evaluator.evaluateNode(sDecl.initializer)).toEqual({ __symbolic: 'error', - message: 'Name expected a string or an identifier but received "1"', + message: 'Name expected', line: 8, - character: 13 + character: 13, + context: {received: '1'} }); let tDecl = findVar(errors, 't'); expect(evaluator.evaluateNode(tDecl.initializer)).toEqual({ __symbolic: 'error', - message: 'Expression form not supported statically', + message: 'Expression form not supported', line: 9, character: 11 });