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 {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');
|
export const SOME_OPAQUE_TOKEN = new OpaqueToken('opaqueToken');
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ export const SOME_OPAQUE_TOKEN = new OpaqueToken('opaqueToken');
|
||||||
providers: [
|
providers: [
|
||||||
{provide: 'strToken', useValue: 'strValue'},
|
{provide: 'strToken', useValue: 'strValue'},
|
||||||
{provide: SOME_OPAQUE_TOKEN, useValue: 10},
|
{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]}},
|
{provide: 'complexToken', useValue: {a: 1, b: ['test', SOME_OPAQUE_TOKEN]}},
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
@ -23,7 +23,7 @@ export class CompWithProviders {
|
||||||
<input #a>{{a.value}}
|
<input #a>{{a.value}}
|
||||||
<div *ngIf="true">{{a.value}}</div>
|
<div *ngIf="true">{{a.value}}</div>
|
||||||
`,
|
`,
|
||||||
directives: [NgIf]
|
directives: [common.NgIf]
|
||||||
})
|
})
|
||||||
export class CompWithReferences {
|
export class CompWithReferences {
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as ts from 'typescript';
|
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';
|
import {Symbols} from './symbols';
|
||||||
|
|
||||||
function isMethodCallOf(callExpression: ts.CallExpression, memberName: string): boolean {
|
function isMethodCallOf(callExpression: ts.CallExpression, memberName: string): boolean {
|
||||||
|
@ -66,14 +66,21 @@ function getSourceFileOfNode(node: ts.Node): ts.SourceFile {
|
||||||
export function errorSymbol(
|
export function errorSymbol(
|
||||||
message: string, node?: ts.Node, context?: {[name: string]: string},
|
message: string, node?: ts.Node, context?: {[name: string]: string},
|
||||||
sourceFile?: ts.SourceFile): MetadataError {
|
sourceFile?: ts.SourceFile): MetadataError {
|
||||||
|
let result: MetadataError;
|
||||||
if (node) {
|
if (node) {
|
||||||
sourceFile = sourceFile || getSourceFileOfNode(node);
|
sourceFile = sourceFile || getSourceFileOfNode(node);
|
||||||
if (sourceFile) {
|
if (sourceFile) {
|
||||||
let {line, character} = ts.getLineAndCharacterOfPosition(sourceFile, node.pos);
|
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:
|
case ts.SyntaxKind.TypeReference:
|
||||||
const typeReferenceNode = <ts.TypeReferenceNode>node;
|
const typeReferenceNode = <ts.TypeReferenceNode>node;
|
||||||
const typeNameNode = typeReferenceNode.typeName;
|
const typeNameNode = typeReferenceNode.typeName;
|
||||||
if (typeNameNode.kind != ts.SyntaxKind.Identifier) {
|
const getReference: (typeNameNode: ts.Identifier | ts.QualifiedName) =>
|
||||||
return errorSymbol('Qualified type names not supported', node);
|
MetadataSymbolicReferenceExpression | MetadataError = node => {
|
||||||
}
|
if (typeNameNode.kind === ts.SyntaxKind.QualifiedName) {
|
||||||
const typeNameIdentifier = <ts.Identifier>typeReferenceNode.typeName;
|
const qualifiedName = <ts.QualifiedName>node;
|
||||||
const typeName = typeNameIdentifier.text;
|
const left = this.evaluateNode(qualifiedName.left);
|
||||||
const typeReference = this.symbols.resolve(typeName);
|
if (isMetadataModuleReferenceExpression(left)) {
|
||||||
if (!typeReference) {
|
return <MetadataImportedSymbolReferenceExpression> {
|
||||||
return errorSymbol('Could not resolve type', node, {typeName});
|
__symbolic: 'reference', module: left.module, name: qualifiedName.right.text
|
||||||
}
|
}
|
||||||
if (typeReferenceNode.typeArguments && typeReferenceNode.typeArguments.length) {
|
}
|
||||||
const args = typeReferenceNode.typeArguments.map(element => this.evaluateNode(element));
|
return errorSymbol('Qualified type names not supported', node);
|
||||||
if (isMetadataImportedSymbolReferenceExpression(typeReference)) {
|
} else {
|
||||||
return {
|
const identifier = <ts.Identifier>typeNameNode;
|
||||||
__symbolic: 'reference',
|
let symbol = this.symbols.resolve(identifier.text);
|
||||||
module: typeReference.module,
|
if (isMetadataError(symbol) || isMetadataSymbolicReferenceExpression(symbol)) {
|
||||||
name: typeReference.name,
|
return symbol;
|
||||||
arguments: args
|
}
|
||||||
|
return errorSymbol('Could not resolve type', node, {typeName: identifier.text});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} else if (isMetadataGlobalReferenceExpression(typeReference)) {
|
const typeReference = getReference(typeNameNode);
|
||||||
return {__symbolic: 'reference', name: typeReference.name, arguments: args};
|
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;
|
return typeReference;
|
||||||
case ts.SyntaxKind.NoSubstitutionTemplateLiteral:
|
case ts.SyntaxKind.NoSubstitutionTemplateLiteral:
|
||||||
|
|
|
@ -165,6 +165,7 @@ export interface MetadataImportedDefaultReferenceExpression extends MetadataSymb
|
||||||
module: string;
|
module: string;
|
||||||
default:
|
default:
|
||||||
boolean;
|
boolean;
|
||||||
|
arguments?: MetadataValue[];
|
||||||
}
|
}
|
||||||
export function isMetadataImportDefaultReference(value: any):
|
export function isMetadataImportDefaultReference(value: any):
|
||||||
value is MetadataImportedDefaultReferenceExpression {
|
value is MetadataImportedDefaultReferenceExpression {
|
||||||
|
|
|
@ -14,7 +14,7 @@ describe('Collector', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
host = new Host(FILES, [
|
host = new Host(FILES, [
|
||||||
'/app/app.component.ts', '/app/cases-data.ts', '/app/error-cases.ts', '/promise.ts',
|
'/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);
|
service = ts.createLanguageService(host);
|
||||||
program = service.getProgram();
|
program = service.getProgram();
|
||||||
|
@ -212,41 +212,16 @@ describe('Collector', () => {
|
||||||
__symbolic: 'module',
|
__symbolic: 'module',
|
||||||
version: 1,
|
version: 1,
|
||||||
metadata: {
|
metadata: {
|
||||||
a: {
|
a: {__symbolic: 'error', message: 'Destructuring not supported', line: 1, character: 16},
|
||||||
__symbolic: 'error',
|
b: {__symbolic: 'error', message: 'Destructuring not supported', line: 1, character: 18},
|
||||||
message: 'Destructuring declarations cannot be referenced statically',
|
c: {__symbolic: 'error', message: 'Destructuring not supported', line: 2, character: 16},
|
||||||
line: 1,
|
d: {__symbolic: 'error', message: 'Destructuring not supported', line: 2, character: 18},
|
||||||
character: 16
|
e: {__symbolic: 'error', message: 'Variable not initialized', line: 3, character: 14}
|
||||||
},
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
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 unsupported1 = program.getSourceFile('/unsupported-2.ts');
|
||||||
let metadata = collector.getMetadata(unsupported1);
|
let metadata = collector.getMetadata(unsupported1);
|
||||||
let barClass = <ClassMetadata>metadata.metadata['Bar'];
|
let barClass = <ClassMetadata>metadata.metadata['Bar'];
|
||||||
|
@ -254,11 +229,23 @@ describe('Collector', () => {
|
||||||
let parameter = ctor.parameters[0];
|
let parameter = ctor.parameters[0];
|
||||||
expect(parameter).toEqual({
|
expect(parameter).toEqual({
|
||||||
__symbolic: 'error',
|
__symbolic: 'error',
|
||||||
message: 'Reference to non-exported class Foo',
|
message: 'Reference to non-exported class',
|
||||||
line: 1,
|
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
|
// TODO: Do not use \` in a template literal as it confuses clang-format
|
||||||
|
@ -468,6 +455,15 @@ const FILES: Directory = {
|
||||||
constructor(private f: Foo) {}
|
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': {
|
'node_modules': {
|
||||||
'angular2': {
|
'angular2': {
|
||||||
'core.d.ts': `
|
'core.d.ts': `
|
||||||
|
|
|
@ -156,31 +156,29 @@ describe('Evaluator', () => {
|
||||||
character: 10
|
character: 10
|
||||||
});
|
});
|
||||||
let fDecl = findVar(errors, 'f');
|
let fDecl = findVar(errors, 'f');
|
||||||
expect(evaluator.evaluateNode(fDecl.initializer)).toEqual({
|
expect(evaluator.evaluateNode(fDecl.initializer))
|
||||||
__symbolic: 'error',
|
.toEqual(
|
||||||
message:
|
{__symbolic: 'error', message: 'Function call not supported', line: 6, character: 11});
|
||||||
'Functions cannot be evaluated statically; consider replacing with a reference to an exported function',
|
|
||||||
line: 6,
|
|
||||||
character: 11
|
|
||||||
});
|
|
||||||
let eDecl = findVar(errors, 'e');
|
let eDecl = findVar(errors, 'e');
|
||||||
expect(evaluator.evaluateNode(eDecl.type)).toEqual({
|
expect(evaluator.evaluateNode(eDecl.type)).toEqual({
|
||||||
__symbolic: 'error',
|
__symbolic: 'error',
|
||||||
message: 'Could not resolve type NotFound',
|
message: 'Could not resolve type',
|
||||||
line: 7,
|
line: 7,
|
||||||
character: 10
|
character: 10,
|
||||||
|
context: {typeName: 'NotFound'}
|
||||||
});
|
});
|
||||||
let sDecl = findVar(errors, 's');
|
let sDecl = findVar(errors, 's');
|
||||||
expect(evaluator.evaluateNode(sDecl.initializer)).toEqual({
|
expect(evaluator.evaluateNode(sDecl.initializer)).toEqual({
|
||||||
__symbolic: 'error',
|
__symbolic: 'error',
|
||||||
message: 'Name expected a string or an identifier but received "1"',
|
message: 'Name expected',
|
||||||
line: 8,
|
line: 8,
|
||||||
character: 13
|
character: 13,
|
||||||
|
context: {received: '1'}
|
||||||
});
|
});
|
||||||
let tDecl = findVar(errors, 't');
|
let tDecl = findVar(errors, 't');
|
||||||
expect(evaluator.evaluateNode(tDecl.initializer)).toEqual({
|
expect(evaluator.evaluateNode(tDecl.initializer)).toEqual({
|
||||||
__symbolic: 'error',
|
__symbolic: 'error',
|
||||||
message: 'Expression form not supported statically',
|
message: 'Expression form not supported',
|
||||||
line: 9,
|
line: 9,
|
||||||
character: 11
|
character: 11
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue