fix(compiler): Added support for '* as m' style imports. (#9077)
Also includes fixes for broken test.
This commit is contained in:
parent
9f506cd330
commit
e178ee4ba0
|
@ -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 {
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -165,6 +165,7 @@ export interface MetadataImportedDefaultReferenceExpression extends MetadataSymb
|
|||
module: string;
|
||||
default:
|
||||
boolean;
|
||||
arguments?: MetadataValue[];
|
||||
}
|
||||
export function isMetadataImportDefaultReference(value: any):
|
||||
value is MetadataImportedDefaultReferenceExpression {
|
||||
|
|
|
@ -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': `
|
||||
|
|
|
@ -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
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue