@ -107,6 +107,9 @@ $ cp tools/@angular/tsc-wrapped/package.json dist/tools/@angular/tsc-wrapped
|
||||
$ ./scripts/ci-lite/offline_compiler_test.sh
|
||||
# Keep a package fresh in watch mode
|
||||
./node_modules/.bin/tsc -p modules/@angular/compiler/tsconfig-es5.json -w
|
||||
# Recompile @angular/core module (needs to use tsc-ext to keep the metadata)
|
||||
export NODE_PATH=${NODE_PATH}:$(pwd)/dist/all:$(pwd)/dist/tools
|
||||
node dist/tools/@angular/tsc-wrapped/src/main -p modules/@angular/core/tsconfig-es5.json
|
||||
# Iterate on the test
|
||||
cd /tmp/wherever/e2e_test.1464388257/
|
||||
./node_modules/.bin/ngc
|
||||
|
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* @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 {LowerCasePipe, NgIf} from '@angular/common';
|
||||
import {AppModule, Component, ComponentFactoryResolver, Injectable} from '@angular/core';
|
||||
|
||||
@Injectable()
|
||||
export class SomeService {
|
||||
public prop = 'someValue';
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class NestedService {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'cmp',
|
||||
template: `<div [title]="'HELLO' | lowercase"></div><div *ngIf="true"></div>`
|
||||
})
|
||||
export class SomeComp {
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
@Component({selector: 'parent', template: `<cmp></cmp>`, directives: [SomeComp]})
|
||||
export class ParentComp {
|
||||
}
|
||||
|
||||
@AppModule({providers: [NestedService]})
|
||||
export class NestedModule {
|
||||
}
|
||||
|
||||
@AppModule({
|
||||
directives: [NgIf],
|
||||
pipes: [LowerCasePipe],
|
||||
providers: [SomeService],
|
||||
precompile: [SomeComp],
|
||||
modules: [NestedModule]
|
||||
})
|
||||
export class SomeModule {
|
||||
}
|
||||
|
||||
@AppModule({
|
||||
directives: [NgIf],
|
||||
pipes: [LowerCasePipe],
|
||||
precompile: [ParentComp],
|
||||
})
|
||||
export class SomeModuleUsingParentComp {
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/**
|
||||
* @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 {ComponentFactoryResolver, DebugElement, ReflectiveInjector, getDebugNode, lockRunMode} from '@angular/core';
|
||||
import {BROWSER_APP_PROVIDERS, By} from '@angular/platform-browser';
|
||||
import {serverPlatform} from '@angular/platform-server';
|
||||
|
||||
import {NestedModule, NestedService, ParentComp, SomeComp, SomeModule, SomeService} from '../src/app_module';
|
||||
import {SomeModuleNgFactory, SomeModuleUsingParentCompNgFactory} from '../src/app_module.ngfactory';
|
||||
|
||||
|
||||
// Need to lock the mode explicitely as this test is not using Angular's testing framework.
|
||||
lockRunMode();
|
||||
|
||||
describe('AppModule', () => {
|
||||
it('should support providers', () => {
|
||||
var moduleRef = SomeModuleNgFactory.create();
|
||||
expect(moduleRef.instance instanceof SomeModule).toBe(true);
|
||||
expect(moduleRef.injector.get(SomeModule) instanceof SomeModule).toBe(true);
|
||||
expect(moduleRef.injector.get(SomeService) instanceof SomeService).toBe(true);
|
||||
});
|
||||
|
||||
it('should support precompile components', () => {
|
||||
const appInjector =
|
||||
ReflectiveInjector.resolveAndCreate(BROWSER_APP_PROVIDERS, serverPlatform().injector);
|
||||
var moduleRef = SomeModuleNgFactory.create(appInjector);
|
||||
var cf = moduleRef.injector.get(ComponentFactoryResolver).resolveComponentFactory(SomeComp);
|
||||
expect(cf.componentType).toBe(SomeComp);
|
||||
var comp = cf.create(moduleRef.injector);
|
||||
});
|
||||
|
||||
it('should support module directives and pipes', () => {
|
||||
const appInjector =
|
||||
ReflectiveInjector.resolveAndCreate(BROWSER_APP_PROVIDERS, serverPlatform().injector);
|
||||
var moduleRef = SomeModuleNgFactory.create(appInjector);
|
||||
var cf = moduleRef.injector.get(ComponentFactoryResolver).resolveComponentFactory(SomeComp);
|
||||
var comp = cf.create(moduleRef.injector);
|
||||
var debugElement = <DebugElement>getDebugNode(comp.location.nativeElement);
|
||||
|
||||
// NgIf should work, is being used as module directive
|
||||
expect(debugElement.children.length).toBe(1);
|
||||
comp.changeDetectorRef.detectChanges();
|
||||
expect(debugElement.children.length).toBe(2);
|
||||
expect(debugElement.children[0].properties['title']).toBe('hello');
|
||||
});
|
||||
|
||||
it('should support module directives and pipes on nested components', () => {
|
||||
const appInjector =
|
||||
ReflectiveInjector.resolveAndCreate(BROWSER_APP_PROVIDERS, serverPlatform().injector);
|
||||
var moduleRef = SomeModuleUsingParentCompNgFactory.create(appInjector);
|
||||
var cf = moduleRef.injector.get(ComponentFactoryResolver).resolveComponentFactory(ParentComp);
|
||||
var comp = cf.create(moduleRef.injector);
|
||||
var debugElement = <DebugElement>getDebugNode(comp.location.nativeElement);
|
||||
|
||||
debugElement = debugElement.children[0];
|
||||
// NgIf should work, is being used as module directive
|
||||
expect(debugElement.children.length).toBe(1);
|
||||
comp.changeDetectorRef.detectChanges();
|
||||
expect(debugElement.children.length).toBe(2);
|
||||
expect(debugElement.children[0].properties['title']).toBe('hello');
|
||||
});
|
||||
|
||||
it('should support child moduless', () => {
|
||||
var moduleRef = SomeModuleNgFactory.create();
|
||||
expect(moduleRef.instance instanceof SomeModule).toBe(true);
|
||||
expect(moduleRef.injector.get(NestedModule) instanceof NestedModule).toBe(true);
|
||||
expect(moduleRef.injector.get(NestedService) instanceof NestedService).toBe(true);
|
||||
});
|
||||
|
||||
});
|
@ -11,15 +11,15 @@
|
||||
* Intended to be used in a build step.
|
||||
*/
|
||||
import * as compiler from '@angular/compiler';
|
||||
import {ViewEncapsulation, lockRunMode} from '@angular/core';
|
||||
import {AppModuleMetadata, ComponentMetadata, ViewEncapsulation, lockRunMode} from '@angular/core';
|
||||
import {AngularCompilerOptions} from '@angular/tsc-wrapped';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {CompileMetadataResolver, DirectiveNormalizer, DomElementSchemaRegistry, HtmlParser, Lexer, Parser, StyleCompiler, TemplateParser, TypeScriptEmitter, ViewCompiler} from './compiler_private';
|
||||
import {AppModuleCompiler, CompileMetadataResolver, DirectiveNormalizer, DomElementSchemaRegistry, HtmlParser, Lexer, Parser, StyleCompiler, TemplateParser, TypeScriptEmitter, ViewCompiler} from './compiler_private';
|
||||
import {ReflectorHost, ReflectorHostContext} from './reflector_host';
|
||||
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
||||
import {StaticReflector} from './static_reflector';
|
||||
import {StaticReflector, StaticSymbol} from './static_reflector';
|
||||
|
||||
const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
||||
|
||||
@ -40,26 +40,9 @@ export class CodeGenerator {
|
||||
lockRunMode();
|
||||
}
|
||||
|
||||
private generateSource(metadatas: compiler.CompileDirectiveMetadata[]) {
|
||||
const normalize = (metadata: compiler.CompileDirectiveMetadata) => {
|
||||
const directiveType = metadata.type.runtime;
|
||||
const directives = this.resolver.getViewDirectivesMetadata(directiveType);
|
||||
return Promise.all(directives.map(d => this.compiler.normalizeDirectiveMetadata(d)))
|
||||
.then(normalizedDirectives => {
|
||||
const pipes = this.resolver.getViewPipesMetadata(directiveType);
|
||||
return new compiler.NormalizedComponentWithViewDirectives(
|
||||
metadata, normalizedDirectives, pipes);
|
||||
});
|
||||
};
|
||||
return Promise.all(metadatas.map(normalize))
|
||||
.then(
|
||||
normalizedCompWithDirectives =>
|
||||
this.compiler.compileTemplates(normalizedCompWithDirectives));
|
||||
}
|
||||
|
||||
private readComponents(absSourcePath: string) {
|
||||
const result: Promise<compiler.CompileDirectiveMetadata>[] = [];
|
||||
private readFileMetadata(absSourcePath: string): FileMetadata {
|
||||
const moduleMetadata = this.staticReflector.getModuleMetadata(absSourcePath);
|
||||
const result: FileMetadata = {components: [], appModules: [], fileUrl: absSourcePath};
|
||||
if (!moduleMetadata) {
|
||||
console.log(`WARNING: no metadata found for ${absSourcePath}`);
|
||||
return result;
|
||||
@ -75,13 +58,14 @@ export class CodeGenerator {
|
||||
continue;
|
||||
}
|
||||
const staticType = this.reflectorHost.findDeclaration(absSourcePath, symbol, absSourcePath);
|
||||
let directive: compiler.CompileDirectiveMetadata;
|
||||
directive = this.resolver.maybeGetDirectiveMetadata(<any>staticType);
|
||||
|
||||
if (!directive || !directive.isComponent) {
|
||||
continue;
|
||||
}
|
||||
result.push(this.compiler.normalizeDirectiveMetadata(directive));
|
||||
const annotations = this.staticReflector.annotations(staticType);
|
||||
annotations.forEach((annotation) => {
|
||||
if (annotation instanceof AppModuleMetadata) {
|
||||
result.appModules.push(staticType);
|
||||
} else if (annotation instanceof ComponentMetadata) {
|
||||
result.components.push(staticType);
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -102,30 +86,31 @@ export class CodeGenerator {
|
||||
}
|
||||
|
||||
codegen(): Promise<any> {
|
||||
const generateOneFile = (absSourcePath: string) =>
|
||||
Promise.all(this.readComponents(absSourcePath))
|
||||
.then((metadatas: compiler.CompileDirectiveMetadata[]) => {
|
||||
if (!metadatas || !metadatas.length) {
|
||||
return;
|
||||
}
|
||||
return this.generateSource(metadatas);
|
||||
})
|
||||
.then(generatedModules => {
|
||||
if (generatedModules) {
|
||||
generatedModules.forEach((generatedModule) => {
|
||||
const sourceFile = this.program.getSourceFile(absSourcePath);
|
||||
const emitPath = this.calculateEmitPath(generatedModule.moduleUrl);
|
||||
this.host.writeFile(
|
||||
emitPath, PREAMBLE + generatedModule.source, false, () => {}, [sourceFile]);
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((e) => { console.error(e.stack); });
|
||||
var compPromises = this.program.getSourceFiles()
|
||||
.map(sf => sf.fileName)
|
||||
.filter(f => !GENERATED_FILES.test(f))
|
||||
.map(generateOneFile);
|
||||
return Promise.all(compPromises);
|
||||
let filePaths =
|
||||
this.program.getSourceFiles().map(sf => sf.fileName).filter(f => !GENERATED_FILES.test(f));
|
||||
let fileMetas = filePaths.map((filePath) => this.readFileMetadata(filePath));
|
||||
let appModules = fileMetas.reduce((appModules, fileMeta) => {
|
||||
appModules.push(...fileMeta.appModules);
|
||||
return appModules;
|
||||
}, <StaticSymbol[]>[]);
|
||||
let analyzedAppModules = this.compiler.analyzeModules(appModules);
|
||||
return Promise
|
||||
.all(fileMetas.map(
|
||||
(fileMeta) => this.compiler
|
||||
.compile(
|
||||
fileMeta.fileUrl, analyzedAppModules, fileMeta.components,
|
||||
fileMeta.appModules)
|
||||
.then((generatedModules) => {
|
||||
generatedModules.forEach((generatedModule) => {
|
||||
const sourceFile = this.program.getSourceFile(fileMeta.fileUrl);
|
||||
const emitPath =
|
||||
this.calculateEmitPath(generatedModule.moduleUrl);
|
||||
this.host.writeFile(
|
||||
emitPath, PREAMBLE + generatedModule.source, false, () => {},
|
||||
[sourceFile]);
|
||||
});
|
||||
})))
|
||||
.catch((e) => { console.error(e.stack); });
|
||||
}
|
||||
|
||||
static create(
|
||||
@ -158,14 +143,20 @@ export class CodeGenerator {
|
||||
const tmplParser = new TemplateParser(
|
||||
parser, new DomElementSchemaRegistry(), htmlParser,
|
||||
/*console*/ null, []);
|
||||
const offlineCompiler = new compiler.OfflineCompiler(
|
||||
normalizer, tmplParser, new StyleCompiler(urlResolver), new ViewCompiler(config),
|
||||
new TypeScriptEmitter(reflectorHost));
|
||||
const resolver = new CompileMetadataResolver(
|
||||
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
||||
new compiler.ViewResolver(staticReflector), config, staticReflector);
|
||||
const offlineCompiler = new compiler.OfflineCompiler(
|
||||
resolver, normalizer, tmplParser, new StyleCompiler(urlResolver), new ViewCompiler(config),
|
||||
new AppModuleCompiler(), new TypeScriptEmitter(reflectorHost));
|
||||
|
||||
return new CodeGenerator(
|
||||
options, program, compilerHost, staticReflector, resolver, offlineCompiler, reflectorHost);
|
||||
}
|
||||
}
|
||||
|
||||
interface FileMetadata {
|
||||
fileUrl: string;
|
||||
components: StaticSymbol[];
|
||||
appModules: StaticSymbol[];
|
||||
}
|
@ -61,5 +61,8 @@ export var StyleCompiler: typeof _c.StyleCompiler = _c.StyleCompiler;
|
||||
export type ViewCompiler = _c.ViewCompiler;
|
||||
export var ViewCompiler: typeof _c.ViewCompiler = _c.ViewCompiler;
|
||||
|
||||
export type AppModuleCompiler = _c.AppModuleCompiler;
|
||||
export var AppModuleCompiler: typeof _c.AppModuleCompiler = _c.AppModuleCompiler;
|
||||
|
||||
export type TypeScriptEmitter = _c.TypeScriptEmitter;
|
||||
export var TypeScriptEmitter: typeof _c.TypeScriptEmitter = _c.TypeScriptEmitter;
|
||||
|
@ -22,7 +22,7 @@ import * as compiler from '@angular/compiler';
|
||||
import {ViewEncapsulation, lockRunMode} from '@angular/core';
|
||||
|
||||
import {StaticReflector} from './static_reflector';
|
||||
import {CompileMetadataResolver, HtmlParser, DirectiveNormalizer, Lexer, Parser, TemplateParser, DomElementSchemaRegistry, StyleCompiler, ViewCompiler, TypeScriptEmitter, MessageExtractor, removeDuplicates, ExtractionResult, Message, ParseError, serializeXmb,} from './compiler_private';
|
||||
import {CompileMetadataResolver, HtmlParser, DirectiveNormalizer, Lexer, Parser, DomElementSchemaRegistry, TypeScriptEmitter, MessageExtractor, removeDuplicates, ExtractionResult, Message, ParseError, serializeXmb,} from './compiler_private';
|
||||
|
||||
import {ReflectorHost} from './reflector_host';
|
||||
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
||||
@ -40,42 +40,27 @@ class Extractor {
|
||||
constructor(
|
||||
private _options: tsc.AngularCompilerOptions, private _program: ts.Program,
|
||||
public host: ts.CompilerHost, private staticReflector: StaticReflector,
|
||||
private _resolver: CompileMetadataResolver, private _compiler: compiler.OfflineCompiler,
|
||||
private _resolver: CompileMetadataResolver, private _normalizer: DirectiveNormalizer,
|
||||
private _reflectorHost: ReflectorHost, private _extractor: MessageExtractor) {
|
||||
lockRunMode();
|
||||
}
|
||||
|
||||
private _extractCmpMessages(metadatas: compiler.CompileDirectiveMetadata[]):
|
||||
Promise<ExtractionResult> {
|
||||
if (!metadatas || !metadatas.length) {
|
||||
private _extractCmpMessages(components: compiler.CompileDirectiveMetadata[]): ExtractionResult {
|
||||
if (!components || !components.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const normalize = (metadata: compiler.CompileDirectiveMetadata) => {
|
||||
const directiveType = metadata.type.runtime;
|
||||
const directives = this._resolver.getViewDirectivesMetadata(directiveType);
|
||||
return Promise.all(directives.map(d => this._compiler.normalizeDirectiveMetadata(d)))
|
||||
.then(normalizedDirectives => {
|
||||
const pipes = this._resolver.getViewPipesMetadata(directiveType);
|
||||
return new compiler.NormalizedComponentWithViewDirectives(
|
||||
metadata, normalizedDirectives, pipes);
|
||||
});
|
||||
};
|
||||
let messages: Message[] = [];
|
||||
let errors: ParseError[] = [];
|
||||
components.forEach(metadata => {
|
||||
let url = _dirPaths.get(metadata);
|
||||
let result = this._extractor.extract(metadata.template.template, url);
|
||||
errors = errors.concat(result.errors);
|
||||
messages = messages.concat(result.messages);
|
||||
});
|
||||
|
||||
return Promise.all(metadatas.map(normalize))
|
||||
.then((cmps: compiler.NormalizedComponentWithViewDirectives[]) => {
|
||||
let messages: Message[] = [];
|
||||
let errors: ParseError[] = [];
|
||||
cmps.forEach(cmp => {
|
||||
let url = _dirPaths.get(cmp.component);
|
||||
let result = this._extractor.extract(cmp.component.template.template, url);
|
||||
errors = errors.concat(result.errors);
|
||||
messages = messages.concat(result.messages);
|
||||
});
|
||||
|
||||
// Extraction Result might contain duplicate messages at this point
|
||||
return new ExtractionResult(messages, errors);
|
||||
});
|
||||
// Extraction Result might contain duplicate messages at this point
|
||||
return new ExtractionResult(messages, errors);
|
||||
}
|
||||
|
||||
private _readComponents(absSourcePath: string): Promise<compiler.CompileDirectiveMetadata>[] {
|
||||
@ -96,7 +81,7 @@ class Extractor {
|
||||
directive = this._resolver.maybeGetDirectiveMetadata(<any>staticType);
|
||||
|
||||
if (directive && directive.isComponent) {
|
||||
let promise = this._compiler.normalizeDirectiveMetadata(directive);
|
||||
let promise = this._normalizer.normalizeDirective(directive).asyncResult;
|
||||
promise.then(md => _dirPaths.set(md, absSourcePath));
|
||||
result.push(promise);
|
||||
}
|
||||
@ -165,12 +150,6 @@ class Extractor {
|
||||
});
|
||||
const normalizer = new DirectiveNormalizer(xhr, urlResolver, htmlParser, config);
|
||||
const parser = new Parser(new Lexer());
|
||||
const tmplParser = new TemplateParser(
|
||||
parser, new DomElementSchemaRegistry(), htmlParser,
|
||||
/*console*/ null, []);
|
||||
const offlineCompiler = new compiler.OfflineCompiler(
|
||||
normalizer, tmplParser, new StyleCompiler(urlResolver), new ViewCompiler(config),
|
||||
new TypeScriptEmitter(reflectorHost));
|
||||
const resolver = new CompileMetadataResolver(
|
||||
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
||||
new compiler.ViewResolver(staticReflector), config, staticReflector);
|
||||
@ -179,7 +158,7 @@ class Extractor {
|
||||
const extractor = new MessageExtractor(htmlParser, parser, [], {});
|
||||
|
||||
return new Extractor(
|
||||
options, program, compilerHost, staticReflector, resolver, offlineCompiler, reflectorHost,
|
||||
options, program, compilerHost, staticReflector, resolver, normalizer, reflectorHost,
|
||||
extractor);
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AttributeMetadata, ComponentMetadata, ContentChildMetadata, ContentChildrenMetadata, DirectiveMetadata, HostBindingMetadata, HostListenerMetadata, HostMetadata, InjectMetadata, InjectableMetadata, InputMetadata, OptionalMetadata, OutputMetadata, PipeMetadata, Provider, QueryMetadata, SelfMetadata, SkipSelfMetadata, ViewChildMetadata, ViewChildrenMetadata, ViewQueryMetadata, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
|
||||
import {AppModuleMetadata, AttributeMetadata, ComponentMetadata, ContentChildMetadata, ContentChildrenMetadata, DirectiveMetadata, HostBindingMetadata, HostListenerMetadata, HostMetadata, InjectMetadata, InjectableMetadata, InputMetadata, OptionalMetadata, OutputMetadata, PipeMetadata, Provider, QueryMetadata, SelfMetadata, SkipSelfMetadata, ViewChildMetadata, ViewChildrenMetadata, ViewQueryMetadata, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
|
||||
|
||||
import {ReflectorReader} from './core_private';
|
||||
|
||||
@ -216,6 +216,8 @@ export class StaticReflector implements ReflectorReader {
|
||||
this.host.findDeclaration(coreDecorators, 'Directive'), DirectiveMetadata);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'Component'), ComponentMetadata);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'AppModule'), AppModuleMetadata);
|
||||
|
||||
// Note: Some metadata classes can be used directly with Provider.deps.
|
||||
this.registerDecoratorOrConstructor(
|
||||
|
Reference in New Issue
Block a user