Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/compiler-cli/integrationtest/src/basic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import {NgFor, NgIf} from '@angular/common';
import {Component, Inject, LOCALE_ID, TRANSLATIONS_FORMAT} from '@angular/core';

import {CUSTOM, Named} from './custom_token';

@Component({
selector: 'basic',
Expand All @@ -22,7 +23,8 @@ export class BasicComp {
ctxArr: any[] = [];
constructor(
@Inject(LOCALE_ID) public localeId: string,
@Inject(TRANSLATIONS_FORMAT) public translationsFormat: string) {
@Inject(TRANSLATIONS_FORMAT) public translationsFormat: string,
@Inject(CUSTOM) public custom: Named) {
this.ctxProp = 'initialValue';
}
}
13 changes: 13 additions & 0 deletions packages/compiler-cli/integrationtest/src/custom_token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import {InjectionToken} from '@angular/core';

export interface Named { name: string; }

export const CUSTOM = new InjectionToken<Named>('CUSTOM');
2 changes: 2 additions & 0 deletions packages/compiler-cli/integrationtest/src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {MultipleComponentsMyComp, NextComp} from './a/multiple_components';
import {AnimateCmp} from './animate';
import {BasicComp} from './basic';
import {ComponentUsingThirdParty} from './comp_using_3rdp';
import {CUSTOM, Named} from './custom_token';
import {CompWithAnalyzeEntryComponentsProvider, CompWithEntryComponents} from './entry_components';
import {CompConsumingEvents, CompUsingPipes, CompWithProviders, CompWithReferences, DirPublishingEvents, ModuleUsingCustomElements} from './features';
import {CompUsingRootModuleDirectiveAndPipe, SomeDirectiveInRootModule, SomeLibModule, SomePipeInRootModule, SomeService} from './module_fixtures';
Expand Down Expand Up @@ -75,6 +76,7 @@ export const SERVER_ANIMATIONS_PROVIDERS: Provider[] = [{
providers: [
SomeService,
SERVER_ANIMATIONS_PROVIDERS,
{provide: CUSTOM, useValue: {name: 'some name'}},
],
entryComponents: [
AnimateCmp,
Expand Down
25 changes: 25 additions & 0 deletions packages/compiler/test/aot/static_reflector_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,31 @@ describe('StaticReflector', () => {
expect(reflector.annotations(reflector.getStaticSymbol('/tmp/src/main.ts', 'Child')))
.toEqual([]);
});

it('should support constructor parameters with @Inject and an interface type', () => {
const data = Object.create(DEFAULT_TEST_DATA);
const file = '/tmp/src/inject_interface.ts';
data[file] = `
import {Injectable, Inject} from '@angular/core';
import {F} from './f';

export interface InjectedInterface {

}

export class Token {}

@Injectable()
export class SomeClass {
constructor (@Inject(Token) injected: InjectedInterface, t: Token, @Inject(Token) f: F) {}
}
`;

init(data);

expect(reflector.parameters(reflector.getStaticSymbol(file, 'SomeClass'))[0].length)
.toEqual(1);
});
});

});
Expand Down
4 changes: 3 additions & 1 deletion packages/compiler/test/aot/static_symbol_resolver_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,9 @@ export class MockStaticSymbolResolverHost implements StaticSymbolResolverHost {
filePath, this.data[filePath], ts.ScriptTarget.ES5, /* setParentNodes */ true);
const diagnostics: ts.Diagnostic[] = (<any>sf).parseDiagnostics;
if (diagnostics && diagnostics.length) {
throw Error(`Error encountered during parse of file ${filePath}`);
const errors = diagnostics.map(d => `(${d.start}-${d.start+d.length}): ${d.messageText}`)
.join('\n ');
throw Error(`Error encountered during parse of file ${filePath}\n${errors}`);
}
return [this.collector.getMetadata(sf)];
}
Expand Down
5 changes: 4 additions & 1 deletion tools/@angular/tsc-wrapped/src/bundler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import * as path from 'path';
import * as ts from 'typescript';

import {MetadataCollector} from './collector';
import {ClassMetadata, ConstructorMetadata, FunctionMetadata, MemberMetadata, MetadataArray, MetadataEntry, MetadataError, MetadataImportedSymbolReferenceExpression, MetadataMap, MetadataObject, MetadataSymbolicBinaryExpression, MetadataSymbolicCallExpression, MetadataSymbolicExpression, MetadataSymbolicIfExpression, MetadataSymbolicIndexExpression, MetadataSymbolicPrefixExpression, MetadataSymbolicReferenceExpression, MetadataSymbolicSelectExpression, MetadataSymbolicSpreadExpression, MetadataValue, MethodMetadata, ModuleMetadata, VERSION, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isInterfaceMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicExpression, isMetadataSymbolicReferenceExpression, isMethodMetadata} from './schema';

import {ClassMetadata, ConstructorMetadata, FunctionMetadata, MemberMetadata, MetadataArray, MetadataEntry, MetadataError, MetadataImportedSymbolReferenceExpression, MetadataMap, MetadataObject, MetadataSymbolicBinaryExpression, MetadataSymbolicCallExpression, MetadataSymbolicExpression, MetadataSymbolicIfExpression, MetadataSymbolicIndexExpression, MetadataSymbolicPrefixExpression, MetadataSymbolicReferenceExpression, MetadataSymbolicSelectExpression, MetadataSymbolicSpreadExpression, MetadataValue, MethodMetadata, ModuleMetadata, VERSION, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicExpression, isMetadataSymbolicReferenceExpression, isMethodMetadata} from './schema';

// The character set used to produce private names.
const PRIVATE_NAME_CHARS = [
Expand Down Expand Up @@ -268,6 +268,9 @@ export class MetadataBundler {
if (isFunctionMetadata(value)) {
return this.convertFunction(moduleName, value);
}
if (isInterfaceMetadata(value)) {
return value;
}
return this.convertValue(moduleName, value);
}

Expand Down
31 changes: 25 additions & 6 deletions tools/@angular/tsc-wrapped/src/collector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import * as ts from 'typescript';

import {Evaluator, errorSymbol} from './evaluator';
import {ClassMetadata, ConstructorMetadata, FunctionMetadata, MemberMetadata, MetadataEntry, MetadataError, MetadataMap, MetadataSymbolicBinaryExpression, MetadataSymbolicCallExpression, MetadataSymbolicExpression, MetadataSymbolicIfExpression, MetadataSymbolicIndexExpression, MetadataSymbolicPrefixExpression, MetadataSymbolicReferenceExpression, MetadataSymbolicSelectExpression, MetadataSymbolicSpreadExpression, MetadataValue, MethodMetadata, ModuleExportMetadata, ModuleMetadata, VERSION, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataSymbolicExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSelectExpression, isMethodMetadata} from './schema';
import {ClassMetadata, ConstructorMetadata, FunctionMetadata, InterfaceMetadata, MemberMetadata, MetadataEntry, MetadataError, MetadataMap, MetadataSymbolicBinaryExpression, MetadataSymbolicCallExpression, MetadataSymbolicExpression, MetadataSymbolicIfExpression, MetadataSymbolicIndexExpression, MetadataSymbolicPrefixExpression, MetadataSymbolicReferenceExpression, MetadataSymbolicSelectExpression, MetadataSymbolicSpreadExpression, MetadataValue, MethodMetadata, ModuleExportMetadata, ModuleMetadata, VERSION, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataSymbolicExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSelectExpression, isMethodMetadata} from './schema';
import {Symbols} from './symbols';

// In TypeScript 2.1 these flags moved
Expand Down Expand Up @@ -56,7 +56,8 @@ export class MetadataCollector {
*/
public getMetadata(sourceFile: ts.SourceFile, strict: boolean = false): ModuleMetadata {
const locals = new Symbols(sourceFile);
const nodeMap = new Map<MetadataValue|ClassMetadata|FunctionMetadata, ts.Node>();
const nodeMap =
new Map<MetadataValue|ClassMetadata|InterfaceMetadata|FunctionMetadata, ts.Node>();
const evaluator = new Evaluator(locals, nodeMap, this.options);
let metadata: {[name: string]: MetadataValue | ClassMetadata | FunctionMetadata}|undefined;
let exports: ModuleExportMetadata[];
Expand Down Expand Up @@ -264,13 +265,14 @@ export class MetadataCollector {
});

const isExportedIdentifier = (identifier: ts.Identifier) => exportMap.has(identifier.text);
const isExported = (node: ts.FunctionDeclaration | ts.ClassDeclaration | ts.EnumDeclaration) =>
isExport(node) || isExportedIdentifier(node.name);
const isExported =
(node: ts.FunctionDeclaration | ts.ClassDeclaration | ts.InterfaceDeclaration |
ts.EnumDeclaration) => isExport(node) || isExportedIdentifier(node.name);
const exportedIdentifierName = (identifier: ts.Identifier) =>
exportMap.get(identifier.text) || identifier.text;
const exportedName =
(node: ts.FunctionDeclaration | ts.ClassDeclaration | ts.EnumDeclaration) =>
exportedIdentifierName(node.name);
(node: ts.FunctionDeclaration | ts.ClassDeclaration | ts.InterfaceDeclaration |
ts.EnumDeclaration) => exportedIdentifierName(node.name);


// Predeclare classes and functions
Expand All @@ -290,6 +292,15 @@ export class MetadataCollector {
}
break;

case ts.SyntaxKind.InterfaceDeclaration:
const interfaceDeclaration = <ts.InterfaceDeclaration>node;
if (interfaceDeclaration.name) {
const interfaceName = interfaceDeclaration.name.text;
// All references to interfaces should be converted to references to `any`.
locals.define(interfaceName, {__symbolic: 'reference', name: 'any'});
}
break;

case ts.SyntaxKind.FunctionDeclaration:
const functionDeclaration = <ts.FunctionDeclaration>node;
if (!isExported(functionDeclaration)) {
Expand Down Expand Up @@ -356,6 +367,14 @@ export class MetadataCollector {
// Otherwise don't record metadata for the class.
break;

case ts.SyntaxKind.InterfaceDeclaration:
const interfaceDeclaration = <ts.InterfaceDeclaration>node;
if (interfaceDeclaration.name && isExported(interfaceDeclaration)) {
if (!metadata) metadata = {};
metadata[exportedName(interfaceDeclaration)] = {__symbolic: 'interface'};
}
break;

case ts.SyntaxKind.FunctionDeclaration:
// Record functions that return a single value. Record the parameter
// names substitution will be performed by the StaticReflector.
Expand Down
7 changes: 6 additions & 1 deletion tools/@angular/tsc-wrapped/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

export const VERSION = 3;

export type MetadataEntry = ClassMetadata | FunctionMetadata | MetadataValue;
export type MetadataEntry = ClassMetadata | InterfaceMetadata | FunctionMetadata | MetadataValue;

export interface ModuleMetadata {
__symbolic: 'module';
Expand Down Expand Up @@ -47,6 +47,11 @@ export function isClassMetadata(value: any): value is ClassMetadata {
return value && value.__symbolic === 'class';
}

export interface InterfaceMetadata { __symbolic: 'interface'; }
export function isInterfaceMetadata(value: any): value is InterfaceMetadata {
return value && value.__symbolic === 'interface';
}

export interface MetadataMap { [name: string]: MemberMetadata[]; }

export interface MemberMetadata {
Expand Down
43 changes: 41 additions & 2 deletions tools/@angular/tsc-wrapped/test/collector.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ describe('Collector', () => {
'static-method-with-default.ts',
'class-inheritance.ts',
'class-inheritance-parent.ts',
'class-inheritance-declarations.d.ts'
'class-inheritance-declarations.d.ts',
'interface-reference.ts'
]);
service = ts.createLanguageService(host, documentRegistry);
program = service.getProgram();
Expand All @@ -60,11 +61,18 @@ describe('Collector', () => {
it('should not have errors in test data', () => { expectValidSources(service, program); });

it('should return undefined for modules that have no metadata', () => {
const sourceFile = program.getSourceFile('app/hero.ts');
const sourceFile = program.getSourceFile('app/empty.ts');
const metadata = collector.getMetadata(sourceFile);
expect(metadata).toBeUndefined();
});

it('should return an interface reference for interfaces', () => {
const sourceFile = program.getSourceFile('app/hero.ts');
const metadata = collector.getMetadata(sourceFile);
expect(metadata).toEqual(
{__symbolic: 'module', version: 3, metadata: {Hero: {__symbolic: 'interface'}}});
});

it('should be able to collect a simple component\'s metadata', () => {
const sourceFile = program.getSourceFile('app/hero-detail.component.ts');
const metadata = collector.getMetadata(sourceFile);
Expand Down Expand Up @@ -609,6 +617,22 @@ describe('Collector', () => {
});
});

it('should collect any for interface parameter reference', () => {
const source = program.getSourceFile('/interface-reference.ts');
const metadata = collector.getMetadata(source);
expect((metadata.metadata['SomeClass'] as ClassMetadata).members).toEqual({
__ctor__: [{
__symbolic: 'constructor',
parameterDecorators: [[{
__symbolic: 'call',
expression: {__symbolic: 'reference', module: 'angular2/core', name: 'Inject'},
arguments: ['a']
}]],
parameters: [{__symbolic: 'reference', name: 'any'}]
}]
});
});

describe('in strict mode', () => {
it('should throw if an error symbol is collecting a reference to a non-exported symbol', () => {
const source = program.getSourceFile('/local-symbol-ref.ts');
Expand Down Expand Up @@ -759,6 +783,7 @@ const FILES: Directory = {
id: number;
name: string;
}`,
'empty.ts': ``,
'hero-detail.component.ts': `
import {Component, Input} from 'angular2/core';
import {Hero} from './hero';
Expand Down Expand Up @@ -927,6 +952,15 @@ const FILES: Directory = {
}
}
`,
'interface-reference.ts': `
import {Injectable, Inject} from 'angular2/core';
export interface Test {}

@Injectable()
export class SomeClass {
constructor(@Inject("a") test: Test) {}
}
`,
'import-star.ts': `
import {Injectable} from 'angular2/core';
import * as common from 'angular2/common';
Expand Down Expand Up @@ -1146,6 +1180,11 @@ const FILES: Directory = {
(): any;
}
export declare var Injectable: InjectableFactory;
export interface InjectFactory {
(binding?: any): any;
new (binding?: any): any;
}
export declare var Inject: InjectFactory;
export interface OnInit {
ngOnInit(): any;
}
Expand Down