Skip to content

Commit

Permalink
feat(compiler): introduce generated injectors
Browse files Browse the repository at this point in the history
This commit is just adding them, but not using them yet for bootstrap.

Related to angular#8997
  • Loading branch information
tbosch committed Jun 7, 2016
1 parent c3d2459 commit 6787fe1
Show file tree
Hide file tree
Showing 44 changed files with 1,571 additions and 171 deletions.
12 changes: 11 additions & 1 deletion modules/@angular/compiler-cli/integrationtest/src/features.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Component, Inject, OpaqueToken} from '@angular/core';
import {Component, Inject, OpaqueToken, InjectorConfig, Provides} from '@angular/core';
import {NgIf} from '@angular/common';

export const SOME_OPAQUE_TOKEN = new OpaqueToken('opaqueToken');
Expand Down Expand Up @@ -27,3 +27,13 @@ export class CompWithProviders {
})
export class CompWithReferences {
}

@InjectorConfig({
providers: [
{provide: 'strToken', useValue: 'strValue'}
]
})
export class SomeConfig {
@Provides('propToken')
someProp: string = 'propValue';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {SomeConfig} from '../src/features';
import {SomeConfigInjectorFactory} from '../src/features.ngfactory';

describe('injector codegen', () => {
it('should support providers in the annotation', () => {
var inj = SomeConfigInjectorFactory.create(null, new SomeConfig());
expect(inj.get('strToken')).toEqual('strValue');
});

it('should support property providers',
() => { var inj = SomeConfigInjectorFactory.create(null, new SomeConfig()); });
});
49 changes: 27 additions & 22 deletions modules/@angular/compiler-cli/src/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
DomElementSchemaRegistry,
StyleCompiler,
ViewCompiler,
InjectorCompiler,
TypeScriptEmitter
} from './compiler_private';

Expand All @@ -44,7 +45,10 @@ export class CodeGenerator {
private compiler: compiler.OfflineCompiler,
private reflectorHost: NodeReflectorHost) {}

private generateSource(metadatas: compiler.CompileDirectiveMetadata[]) {
private generateSource(components: compiler.CompileDirectiveMetadata[], injectorConfigs: compiler.CompileInjectorMetadata[]) {
if (components.length ===0 && injectorConfigs.length === 0) {
return null;
}
const normalize = (metadata: compiler.CompileDirectiveMetadata) => {
const directiveType = metadata.type.runtime;
const directives = this.resolver.getViewDirectivesMetadata(directiveType);
Expand All @@ -55,38 +59,41 @@ export class CodeGenerator {
normalizedDirectives, pipes);
});
};
return Promise.all(metadatas.map(normalize))
return Promise.all(components.map(normalize))
.then(normalizedCompWithDirectives =>
this.compiler.compileTemplates(normalizedCompWithDirectives));
this.compiler.compile(normalizedCompWithDirectives, injectorConfigs));
}

private readComponents(absSourcePath: string) {
const result: Promise<compiler.CompileDirectiveMetadata>[] = [];
private readComponents(absSourcePath: string):Promise<{components: compiler.CompileDirectiveMetadata[], injectors: compiler.CompileInjectorMetadata[]}> {
const moduleMetadata = this.staticReflector.getModuleMetadata(absSourcePath);
if (!moduleMetadata) {
console.log(`WARNING: no metadata found for ${absSourcePath}`);
return result;
return Promise.resolve({components: [], injectors: []});
}
const metadata = moduleMetadata['metadata'];
const symbols = metadata && Object.keys(metadata);
if (!symbols || !symbols.length) {
return result;
return Promise.resolve({components: [], injectors: []});
}
const components: compiler.CompileDirectiveMetadata[] = [];
const injectorConfigs: compiler.CompileInjectorMetadata[] = [];
for (const symbol of symbols) {
if (metadata[symbol] && metadata[symbol].__symbolic == 'error') {
// Ignore symbols that are only included to record error information.
continue;
}
const staticType = this.reflectorHost.findDeclaration(absSourcePath, symbol, absSourcePath);
let directive: compiler.CompileDirectiveMetadata;
directive = this.resolver.maybeGetDirectiveMetadata(<any>staticType);

if (!directive || !directive.isComponent) {
continue;
let directive = this.resolver.maybeGetDirectiveMetadata(<any>staticType);
let injector = this.resolver.maybeGetInjectorMetadata(<any>staticType);
if (directive && directive.isComponent) {
components.push(directive);
} else if (injector) {
injectorConfigs.push(injector);
}
result.push(this.compiler.normalizeDirectiveMetadata(directive));
}
return result;
return Promise.all(components.map(comp => this.compiler.normalizeDirectiveMetadata(comp))).then( (normalizedComps) =>
Promise.resolve({components: normalizedComps, injectors: injectorConfigs})
);
}

// Write codegen in a directory structure matching the sources.
Expand Down Expand Up @@ -122,12 +129,9 @@ export class CodeGenerator {

let stylesheetPromises: Promise<any>[] = [];
const generateOneFile = (absSourcePath: string) =>
Promise.all(this.readComponents(absSourcePath))
.then((metadatas: compiler.CompileDirectiveMetadata[]) => {
if (!metadatas || !metadatas.length) {
return;
}
metadatas.forEach((metadata) => {
this.readComponents(absSourcePath)
.then(({components, injectors}) => {
components.forEach((metadata) => {
let stylesheetPaths = metadata && metadata.template && metadata.template.styleUrls;
if (stylesheetPaths) {
stylesheetPaths.forEach((path) => {
Expand All @@ -136,7 +140,7 @@ export class CodeGenerator {
});
}
});
return this.generateSource(metadatas);
return this.generateSource(components, injectors);
})
.then(generated => {
if (generated) {
Expand Down Expand Up @@ -170,10 +174,11 @@ export class CodeGenerator {
const offlineCompiler = new compiler.OfflineCompiler(
normalizer, tmplParser, new StyleCompiler(urlResolver),
new ViewCompiler(new compiler.CompilerConfig(true, true, true)),
new InjectorCompiler(),
new TypeScriptEmitter(reflectorHost), xhr);
const resolver = new CompileMetadataResolver(
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
new compiler.ViewResolver(staticReflector), null, null, staticReflector);
new compiler.ViewResolver(staticReflector), new compiler.InjectorResolver(staticReflector), null, null, staticReflector);

return new CodeGenerator(options, program, compilerHost, staticReflector, resolver,
offlineCompiler, reflectorHost);
Expand Down
3 changes: 3 additions & 0 deletions modules/@angular/compiler-cli/src/compiler_private.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,8 @@ export var StyleCompiler: typeof _c.StyleCompiler = _c.StyleCompiler;
export type ViewCompiler = _c.ViewCompiler;
export var ViewCompiler: typeof _c.ViewCompiler = _c.ViewCompiler;

export type InjectorCompiler = _c.InjectorCompiler;
export var InjectorCompiler: typeof _c.InjectorCompiler = _c.InjectorCompiler;

export type TypeScriptEmitter = _c.TypeScriptEmitter;
export var TypeScriptEmitter: typeof _c.TypeScriptEmitter = _c.TypeScriptEmitter;
6 changes: 6 additions & 0 deletions modules/@angular/compiler-cli/src/static_reflector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import {
AttributeMetadata,
DirectiveMetadata,
ComponentMetadata,
InjectorMetadata,
ProviderPropertyMetadata,
ContentChildrenMetadata,
ContentChildMetadata,
InputMetadata,
Expand Down Expand Up @@ -244,6 +246,10 @@ export class StaticReflector implements ReflectorReader {
DirectiveMetadata);
this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'Component'),
ComponentMetadata);
this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'InjectorConfig'),
InjectorMetadata);
this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'Provides'),
ProviderPropertyMetadata);

// Note: Some metadata classes can be used directly with Provider.deps.
this.registerDecoratorOrConstructor(this.host.findDeclaration(diMetadata, 'HostMetadata'),
Expand Down
48 changes: 47 additions & 1 deletion modules/@angular/compiler-cli/test/static_reflector_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import {
StaticSymbol
} from '@angular/compiler-cli/src/static_reflector';

import {transition, sequence, group, trigger, state, style, animate, keyframes} from '@angular/core';
import {transition, sequence, group, trigger, state, style, animate, keyframes,
InjectorMetadata, ProviderPropertyMetadata} from '@angular/core';

describe('StaticReflector', () => {
let noContext = new StaticSymbol('', '');
Expand Down Expand Up @@ -262,6 +263,51 @@ describe('StaticReflector', () => {
({__symbolic: "reference", module: "./extern", name: "nonExisting"})))
.toEqual(host.getStaticSymbol('/src/extern.d.ts', 'nonExisting'));
});

describe('decorator conversion', () => {
it('should convert InjectorConfig decorator', () => {
expect(simplify(new StaticSymbol('/src/cases', ''),
{
__symbolic: "call",
expression: {
__symbolic: "reference",
module: 'angular2/src/core/metadata',
name: 'InjectorConfig'
},
arguments: [{
providers: [{
__symbolic: "reference",
module: './extern',
name: 'SomeService'
}]
}]
}))
.toEqual(new InjectorMetadata({providers: [host.getStaticSymbol('/src/extern.d.ts', 'SomeService')]}));
});

it('should convert Provides decorator', () => {
expect(simplify(new StaticSymbol('/src/cases', ''),
{
__symbolic: "call",
expression: {
__symbolic: "reference",
module: 'angular2/src/core/metadata',
name: 'Provides'
},
arguments: [
{
__symbolic: "reference",
module: './extern',
name: 'SomeToken'
},
{
multi: true
}
]
}))
.toEqual(new ProviderPropertyMetadata(host.getStaticSymbol('/src/extern.d.ts', 'SomeToken'), {multi: true}));
});
});
});

class MockReflectorHost implements StaticReflectorHost {
Expand Down
2 changes: 2 additions & 0 deletions modules/@angular/compiler/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export {
ViewResolver,
DirectiveResolver,
PipeResolver,
InjectorResolver,
SourceModule,
NormalizedComponentWithViewDirectives,
OfflineCompiler,
Expand All @@ -31,6 +32,7 @@ export {
CompileQueryMetadata,
CompileTemplateMetadata,
CompileDirectiveMetadata,
CompileInjectorMetadata,
CompilePipeMetadata
} from './src/compiler';

Expand Down
1 change: 1 addition & 0 deletions modules/@angular/compiler/core_private.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,4 @@ export var flattenStyles: typeof t.flattenStyles = r.flattenStyles;
export var clearStyles: typeof t.clearStyles = r.clearStyles;
export var collectAndResolveStyles: typeof r.collectAndResolveStyles = r.collectAndResolveStyles;
export var renderStyles: typeof t.renderStyles = r.renderStyles;
export var CodegenInjector: typeof t.CodegenInjector = r.CodegenInjector;
4 changes: 4 additions & 0 deletions modules/@angular/compiler/private_export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import * as template_parser from './src/template_parser';
import * as dom_element_schema_registry from './src/schema/dom_element_schema_registry';
import * as style_compiler from './src/style_compiler';
import * as view_compiler from './src/view_compiler/view_compiler';
import * as injector_compiler from './src/view_compiler/injector_compiler';
import * as ts_emitter from './src/output/ts_emitter';

export namespace __compiler_private__ {
Expand Down Expand Up @@ -69,6 +70,9 @@ export namespace __compiler_private__ {
export type ViewCompiler = view_compiler.ViewCompiler;
export var ViewCompiler = view_compiler.ViewCompiler;

export type InjectorCompiler = injector_compiler.InjectorCompiler;
export var InjectorCompiler = injector_compiler.InjectorCompiler;

export type TypeScriptEmitter = ts_emitter.TypeScriptEmitter;
export var TypeScriptEmitter = ts_emitter.TypeScriptEmitter;
}
45 changes: 44 additions & 1 deletion modules/@angular/compiler/src/compile_metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,15 +330,17 @@ export class CompileProviderMetadata {
useValue: any;
useExisting: CompileTokenMetadata;
useFactory: CompileFactoryMetadata;
useProperty: string;
deps: CompileDiDependencyMetadata[];
multi: boolean;

constructor({token, useClass, useValue, useExisting, useFactory, deps, multi}: {
constructor({token, useClass, useValue, useExisting, useFactory, useProperty, deps, multi}: {
token?: CompileTokenMetadata,
useClass?: CompileTypeMetadata,
useValue?: any,
useExisting?: CompileTokenMetadata,
useFactory?: CompileFactoryMetadata,
useProperty?: string,
deps?: CompileDiDependencyMetadata[],
multi?: boolean
}) {
Expand All @@ -347,6 +349,7 @@ export class CompileProviderMetadata {
this.useValue = useValue;
this.useExisting = useExisting;
this.useFactory = useFactory;
this.useProperty = useProperty;
this.deps = normalizeBlank(deps);
this.multi = normalizeBool(multi);
}
Expand All @@ -358,6 +361,7 @@ export class CompileProviderMetadata {
useExisting: _objFromJson(data['useExisting'], CompileTokenMetadata.fromJson),
useValue: _objFromJson(data['useValue'], CompileIdentifierMetadata.fromJson),
useFactory: _objFromJson(data['useFactory'], CompileFactoryMetadata.fromJson),
useProperty: data['useProperty'],
multi: data['multi'],
deps: _arrayFromJson(data['deps'], CompileDiDependencyMetadata.fromJson)
});
Expand All @@ -372,6 +376,7 @@ export class CompileProviderMetadata {
'useExisting': _objToJson(this.useExisting),
'useValue': _objToJson(this.useValue),
'useFactory': _objToJson(this.useFactory),
'useProperty': this.useProperty,
'multi': this.multi,
'deps': _arrayToJson(this.deps)
};
Expand Down Expand Up @@ -504,12 +509,14 @@ export class CompileTokenMetadata implements CompileMetadataWithIdentifier {
export class CompileTokenMap<VALUE> {
private _valueMap = new Map<any, VALUE>();
private _values: VALUE[] = [];
private _tokens: CompileTokenMetadata[] = [];

add(token: CompileTokenMetadata, value: VALUE) {
var existing = this.get(token);
if (isPresent(existing)) {
throw new BaseException(`Can only add to a TokenMap! Token: ${token.name}`);
}
this._tokens.push(token);
this._values.push(value);
var rk = token.runtimeCacheKey;
if (isPresent(rk)) {
Expand All @@ -532,6 +539,7 @@ export class CompileTokenMap<VALUE> {
}
return result;
}
keys(): CompileTokenMetadata[] { return this._tokens; }
values(): VALUE[] { return this._values; }
get size(): number { return this._values.length; }
}
Expand Down Expand Up @@ -945,13 +953,48 @@ export class CompilePipeMetadata implements CompileMetadataWithType {
}
}

/**
* Metadata regarding compilation of a directive.
*/
export class CompileInjectorMetadata implements CompileMetadataWithType {
type: CompileTypeMetadata;
providers: Array<CompileProviderMetadata | CompileTypeMetadata | CompileIdentifierMetadata | any[]>;

constructor({type, providers}: {
type?: CompileTypeMetadata,
providers?:
Array<CompileProviderMetadata | CompileTypeMetadata | CompileIdentifierMetadata | any[]>
} = {}) {
this.type = type;
this.providers = _normalizeArray(providers);
}

get identifier(): CompileIdentifierMetadata { return this.type; }

static fromJson(data: {[key: string]: any}): CompileInjectorMetadata {
return new CompileInjectorMetadata({
type: isPresent(data['type']) ? CompileTypeMetadata.fromJson(data['type']) : data['type'],
providers: _arrayFromJson(data['providers'], metadataFromJson)
});
}

toJson(): {[key: string]: any} {
return {
'class': 'Injector',
'type': isPresent(this.type) ? this.type.toJson() : this.type,
'providers': _arrayToJson(this.providers)
};
}
}

var _COMPILE_METADATA_FROM_JSON = {
'Directive': CompileDirectiveMetadata.fromJson,
'Pipe': CompilePipeMetadata.fromJson,
'Type': CompileTypeMetadata.fromJson,
'Provider': CompileProviderMetadata.fromJson,
'Identifier': CompileIdentifierMetadata.fromJson,
'Factory': CompileFactoryMetadata.fromJson,
'Injector': CompileInjectorMetadata.fromJson,
'AnimationEntryMetadata': CompileAnimationEntryMetadata.fromJson,
'AnimationStateDeclarationMetadata': CompileAnimationStateDeclarationMetadata.fromJson,
'AnimationStateTransitionMetadata': CompileAnimationStateTransitionMetadata.fromJson,
Expand Down
Loading

0 comments on commit 6787fe1

Please sign in to comment.