fix(compiler): Added support for '* as m' style imports. (#9077)

Also includes fixes for broken test.
This commit is contained in:
Chuck Jazdzewski 2016-06-09 13:23:29 -07:00
parent 9f506cd330
commit e178ee4ba0
5 changed files with 83 additions and 72 deletions

View File

@ -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 {
<input #a>{{a.value}}
<div *ngIf="true">{{a.value}}</div>
`,
directives: [NgIf]
directives: [common.NgIf]
})
export class CompWithReferences {
}

View File

@ -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 = <ts.TypeReferenceNode>node;
const typeNameNode = typeReferenceNode.typeName;
if (typeNameNode.kind != ts.SyntaxKind.Identifier) {
return errorSymbol('Qualified type names not supported', node);
}
const typeNameIdentifier = <ts.Identifier>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 = <ts.QualifiedName>node;
const left = this.evaluateNode(qualifiedName.left);
if (isMetadataModuleReferenceExpression(left)) {
return <MetadataImportedSymbolReferenceExpression> {
__symbolic: 'reference', module: left.module, name: qualifiedName.right.text
}
}
return errorSymbol('Qualified type names not supported', node);
} else {
const identifier = <ts.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.
(<MetadataImportedSymbolReferenceExpression>typeReference).arguments = args;
}
return typeReference;
case ts.SyntaxKind.NoSubstitutionTemplateLiteral:

View File

@ -165,6 +165,7 @@ export interface MetadataImportedDefaultReferenceExpression extends MetadataSymb
module: string;
default:
boolean;
arguments?: MetadataValue[];
}
export function isMetadataImportDefaultReference(value: any):
value is MetadataImportedDefaultReferenceExpression {

View File

@ -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 = <ClassMetadata>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 = <ClassMetadata>metadata.metadata['SomeClass'];
let ctor = <ConstructorMetadata>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': `

View File

@ -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
});