Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 1 commit into from Jun 9, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions 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');

Expand All @@ -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]}},
]
})
Expand All @@ -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 {
}
60 changes: 38 additions & 22 deletions 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 {
Expand Down Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -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});
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});
}
};
const typeReference = getReference(typeNameNode);
if (isMetadataError(typeReference)) {
return typeReference;
}
if (typeReferenceNode.typeArguments && typeReferenceNode.typeArguments.length) {
if (!isMetadataModuleReferenceExpression(typeReference) &&
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
};
} else if (isMetadataGlobalReferenceExpression(typeReference)) {
return {__symbolic: 'reference', name: typeReference.name, arguments: args};
}
// 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:
Expand Down
1 change: 1 addition & 0 deletions tools/@angular/tsc-wrapped/src/schema.ts
Expand Up @@ -165,6 +165,7 @@ export interface MetadataImportedDefaultReferenceExpression extends MetadataSymb
module: string;
default:
boolean;
arguments?: MetadataValue[];
}
export function isMetadataImportDefaultReference(value: any):
value is MetadataImportedDefaultReferenceExpression {
Expand Down
64 changes: 30 additions & 34 deletions tools/@angular/tsc-wrapped/test/collector.spec.ts
Expand Up @@ -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'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also add an integration test to the compiler_cli that uses different kinds of imports, including import * as ...?

]);
service = ts.createLanguageService(host);
program = service.getProgram();
Expand Down Expand Up @@ -212,53 +212,40 @@ 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'];
let ctor = <ConstructorMetadata>barClass.members['__ctor__'][0];
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
Expand Down Expand Up @@ -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': `
Expand Down
22 changes: 10 additions & 12 deletions tools/@angular/tsc-wrapped/test/evaluator.spec.ts
Expand Up @@ -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
});
Expand Down