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

feat(compiler): introduce generated injectors #9053

Closed
wants to merge 2 commits into from
Closed
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
12 changes: 11 additions & 1 deletion modules/@angular/compiler-cli/integrationtest/src/features.ts
@@ -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';
}
@@ -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()); });
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Missing ing.get??

});
49 changes: 27 additions & 22 deletions modules/@angular/compiler-cli/src/codegen.ts
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
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
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
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
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
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
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
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