feat(core): introduce @AppModule

Main part for #9726
Closes #9730
This commit is contained in:
Tobias Bosch
2016-06-28 09:54:42 -07:00
parent 1608d91728
commit 17e4cfc748
52 changed files with 2085 additions and 851 deletions

View File

@ -1,35 +0,0 @@
/**
* @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
*/
// ATTENTION: This file will be overwritten with generated code by main()
import {DartEmitter} from '@angular/compiler/src/output/dart_emitter';
import {DartImportGenerator} from '@angular/compiler/src/output/dart_imports';
import * as o from '@angular/compiler/src/output/output_ast';
import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter';
import {ComponentFactory} from '@angular/core/src/linker/component_factory';
import {IS_DART, print} from '../src/facade/lang';
import {compAMetadata, compileComp} from './offline_compiler_util';
import {CompA, SimpleJsImportGenerator} from './offline_compiler_util';
export const CompANgFactory: ComponentFactory<CompA> = null;
export function emit(): Promise<string> {
var emitter = IS_DART ? new DartEmitter(new DartImportGenerator()) :
new TypeScriptEmitter(new SimpleJsImportGenerator());
return compileComp(emitter, compAMetadata);
}
// Generator
export function main(args: string[]) {
emit().then((source) => {
// debug: console.error(source);
print(source);
});
}

View File

@ -1,31 +0,0 @@
/**
* @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
*/
// ATTENTION: This file will be overwritten with generated code by main()
import {JavaScriptEmitter} from '@angular/compiler/src/output/js_emitter';
import {ComponentFactory} from '@angular/core/src/linker/component_factory';
import {print} from '../src/facade/lang';
import {compAMetadata, compileComp} from './offline_compiler_util';
import {CompA, SimpleJsImportGenerator} from './offline_compiler_util';
export const CompANgFactory: ComponentFactory<CompA> = null;
export function emit() {
var emitter = new JavaScriptEmitter(new SimpleJsImportGenerator());
return compileComp(emitter, compAMetadata);
}
// Generator
export function main(args: string[]) {
emit().then((source) => {
// debug: console.error(source);
print(source);
});
}

View File

@ -1,10 +0,0 @@
/**
* @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
*/
export const styles = /*@ts2dart_const*/[`.greenStyle[_ngcontent-a-1] { color: green; }`];

View File

@ -1,65 +0,0 @@
/**
* @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 {ddescribe, describe, xdescribe, it, iit, xit, expect, beforeEach, afterEach, inject, beforeEachProviders,} from '@angular/core/testing/testing_internal';
import {IS_DART} from '../src/facade/lang';
import {Injector} from '@angular/core';
import {ComponentFactory} from '@angular/core/src/linker/component_factory';
import * as typed from './offline_compiler_codegen_typed';
import * as untyped from './offline_compiler_codegen_untyped';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {SharedStylesHost} from '@angular/platform-browser/src/dom/shared_styles_host';
import {CompA} from './offline_compiler_util';
export function main() {
var typedComponentFactory = typed.CompANgFactory;
var untypedComponentFactory = untyped.CompANgFactory;
var fixtures: TestFixture[] = [];
if (IS_DART || !getDOM().supportsDOMEvents()) {
// Our generator only works on node.js and Dart...
fixtures.push(new TestFixture(typedComponentFactory, 'typed'));
}
if (!IS_DART) {
// Our generator only works on node.js and Dart...
if (!getDOM().supportsDOMEvents()) {
fixtures.push(new TestFixture(untypedComponentFactory, 'untyped'));
}
}
describe('OfflineCompiler', () => {
var injector: Injector;
var sharedStylesHost: SharedStylesHost;
beforeEach(inject(
[Injector, SharedStylesHost],
(_injector: Injector, _sharedStylesHost: SharedStylesHost) => {
injector = _injector;
sharedStylesHost = _sharedStylesHost;
}));
fixtures.forEach((fixture) => {
describe(`${fixture.name}`, () => {
it('should compile components', () => {
var hostEl = fixture.compFactory.create(injector);
expect(hostEl.instance).toBeAnInstanceOf(CompA);
var styles = sharedStylesHost.getAllStyles();
expect(styles[0]).toContain('.redStyle[_ngcontent');
expect(styles[1]).toContain('.greenStyle[_ngcontent');
});
});
});
});
}
class TestFixture {
constructor(public compFactory: ComponentFactory<CompA>, public name: string) {}
}

View File

@ -1,94 +0,0 @@
/**
* @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 {CompileDirectiveMetadata, CompileTemplateMetadata, CompileTypeMetadata} from '@angular/compiler/src/compile_metadata';
import {CompilerConfig} from '@angular/compiler/src/config';
import {DirectiveNormalizer} from '@angular/compiler/src/directive_normalizer';
import {Lexer} from '@angular/compiler/src/expression_parser/lexer';
import {Parser} from '@angular/compiler/src/expression_parser/parser';
import {HtmlParser} from '@angular/compiler/src/html_parser';
import {NormalizedComponentWithViewDirectives, OfflineCompiler, SourceModule} from '@angular/compiler/src/offline_compiler';
import {OutputEmitter} from '@angular/compiler/src/output/abstract_emitter';
import {ImportGenerator} from '@angular/compiler/src/output/path_util';
import {StyleCompiler} from '@angular/compiler/src/style_compiler';
import {TemplateParser} from '@angular/compiler/src/template_parser';
import {createOfflineCompileUrlResolver} from '@angular/compiler/src/url_resolver';
import {MODULE_SUFFIX} from '@angular/compiler/src/util';
import {ViewCompiler} from '@angular/compiler/src/view_compiler/view_compiler';
import {XHR} from '@angular/compiler/src/xhr';
import {Console} from '../core_private';
import {IS_DART, isPresent, print} from '../src/facade/lang';
import {MockSchemaRegistry} from '../testing/schema_registry_mock';
export class CompA { user: string; }
var THIS_MODULE_PATH = `asset:@angular/lib/compiler/test`;
var THIS_MODULE_URL = `${THIS_MODULE_PATH}/offline_compiler_util${MODULE_SUFFIX}`;
export var compAMetadata = CompileDirectiveMetadata.create({
isComponent: true,
selector: 'comp-a',
type: new CompileTypeMetadata(
{name: 'CompA', moduleUrl: THIS_MODULE_URL, runtime: CompA, diDeps: []}),
template: new CompileTemplateMetadata({
templateUrl: './offline_compiler_compa.html',
styles: ['.redStyle { color: red; }'],
styleUrls: ['./offline_compiler_compa.css']
})
});
function _createOfflineCompiler(xhr: XHR, emitter: OutputEmitter): OfflineCompiler {
var urlResolver = createOfflineCompileUrlResolver();
var htmlParser = new HtmlParser();
var config = new CompilerConfig({genDebugInfo: true, useJit: true});
var normalizer = new DirectiveNormalizer(xhr, urlResolver, htmlParser, config);
return new OfflineCompiler(
normalizer,
new TemplateParser(
new Parser(new Lexer()), new MockSchemaRegistry({}, {}), htmlParser, new Console(), []),
new StyleCompiler(urlResolver), new ViewCompiler(config), emitter);
}
export function compileComp(
emitter: OutputEmitter, comp: CompileDirectiveMetadata): Promise<string> {
var xhr = new MockXhr();
xhr.setResult(`${THIS_MODULE_PATH}/offline_compiler_compa.html`, '');
xhr.setResult(`${THIS_MODULE_PATH}/offline_compiler_compa.css`, '');
var compiler = _createOfflineCompiler(xhr, emitter);
var result = compiler.normalizeDirectiveMetadata(comp).then((normComp) => {
return compiler
.compileTemplates([new NormalizedComponentWithViewDirectives(normComp, [], [])])[0]
.source;
});
return result;
}
export class SimpleJsImportGenerator implements ImportGenerator {
getImportPath(moduleUrlStr: string, importedUrlStr: string): string {
var importedAssetUrl = ImportGenerator.parseAssetUrl(importedUrlStr);
if (isPresent(importedAssetUrl)) {
return `${importedAssetUrl.packageName}/${importedAssetUrl.modulePath}`;
} else {
return importedUrlStr;
}
}
}
class MockXhr implements XHR {
results: {[key: string]: string} = {};
setResult(url: string, content: string) { this.results[url] = content; }
get(url: string): Promise<string> {
if (url in this.results) {
return Promise.resolve(this.results[url]);
}
return Promise.reject<any>(`Unknown url ${url}`);
}
}

View File

@ -12,7 +12,7 @@ import {isBlank} from '../../src/facade/lang';
import {JavaScriptEmitter} from '@angular/compiler/src/output/js_emitter';
import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata';
import * as o from '@angular/compiler/src/output/output_ast';
import {SimpleJsImportGenerator} from '../offline_compiler_util';
import {SimpleJsImportGenerator} from './output_emitter_util';
var someModuleUrl = 'asset:somePackage/lib/somePath';
var anotherModuleUrl = 'asset:somePackage/lib/someOtherPath';

View File

@ -15,9 +15,8 @@ import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter';
import {unimplemented} from '../../src/facade/exceptions';
import {IS_DART, print} from '../../src/facade/lang';
import {assetUrl} from '../../src/util';
import {SimpleJsImportGenerator} from '../offline_compiler_util';
import {codegenExportsVars, codegenStmts} from './output_emitter_util';
import {SimpleJsImportGenerator, codegenExportsVars, codegenStmts} from './output_emitter_util';
export function getExpressions(): any {
return unimplemented();

View File

@ -12,9 +12,8 @@ import {JavaScriptEmitter} from '@angular/compiler/src/output/js_emitter';
import {unimplemented} from '../../src/facade/exceptions';
import {print} from '../../src/facade/lang';
import {assetUrl} from '../../src/util';
import {SimpleJsImportGenerator} from '../offline_compiler_util';
import {codegenExportsVars, codegenStmts} from './output_emitter_util';
import {SimpleJsImportGenerator, codegenExportsVars, codegenStmts} from './output_emitter_util';
export function getExpressions(): any {
return unimplemented();

View File

@ -14,7 +14,7 @@ import * as typed from './output_emitter_codegen_typed';
import * as untyped from './output_emitter_codegen_untyped';
import {jitStatements} from '@angular/compiler/src/output/output_jit';
import {interpretStatements} from '@angular/compiler/src/output/output_interpreter';
import {codegenStmts, ExternalClass, DynamicClassInstanceFactory} from './output_emitter_util';
import {codegenStmts, ExternalClass} from './output_emitter_util';
import {EventEmitter} from '@angular/core';
import {ViewType} from '@angular/core/src/linker/view_type';
import {BaseException} from '@angular/core';
@ -23,8 +23,7 @@ import {browserDetection} from '@angular/platform-browser/testing/browser_util'
export function main() {
var outputDefs: any[] /** TODO #9100 */ = []; outputDefs.push({
'getExpressions': () => interpretStatements(
codegenStmts, 'getExpressions', new DynamicClassInstanceFactory()),
'getExpressions': () => interpretStatements(codegenStmts, 'getExpressions'),
'name': 'interpreted'
});
if (IS_DART || !getDOM().supportsDOMEvents()) {

View File

@ -8,7 +8,7 @@
import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata';
import * as o from '@angular/compiler/src/output/output_ast';
import {DynamicInstance, InstanceFactory} from '@angular/compiler/src/output/output_interpreter';
import {ImportGenerator} from '@angular/compiler/src/output/path_util';
import {assetUrl} from '@angular/compiler/src/util';
import {EventEmitter} from '@angular/core';
import {ViewType} from '@angular/core/src/linker/view_type';
@ -253,22 +253,13 @@ function createOperatorFn(op: o.BinaryOperator) {
o.DYNAMIC_TYPE);
}
export class DynamicClassInstanceFactory implements InstanceFactory {
createInstance(
superClass: any, clazz: any, args: any[], props: Map<string, any>,
getters: Map<string, Function>, methods: Map<string, Function>): any {
if (superClass === ExternalClass) {
return new _InterpretiveDynamicClass(args, clazz, props, getters, methods);
export class SimpleJsImportGenerator implements ImportGenerator {
getImportPath(moduleUrlStr: string, importedUrlStr: string): string {
var importedAssetUrl = ImportGenerator.parseAssetUrl(importedUrlStr);
if (importedAssetUrl) {
return `${importedAssetUrl.packageName}/${importedAssetUrl.modulePath}`;
} else {
return importedUrlStr;
}
throw new BaseException(`Can't instantiate class ${superClass} in interpretative mode`);
}
}
class _InterpretiveDynamicClass extends ExternalClass implements DynamicInstance {
constructor(
args: any[], public clazz: any, public props: Map<string, any>,
public getters: Map<string, Function>, public methods: Map<string, Function>) {
super(args[0]);
}
childMethod(a: any /** TODO #9100 */) { return this.methods.get('childMethod')(a); }
}

View File

@ -12,7 +12,7 @@ import {isBlank} from '../../src/facade/lang';
import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter';
import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata';
import * as o from '@angular/compiler/src/output/output_ast';
import {SimpleJsImportGenerator} from '../offline_compiler_util';
import {SimpleJsImportGenerator} from './output_emitter_util';
var someModuleUrl = 'asset:somePackage/lib/somePath';
var anotherModuleUrl = 'asset:somePackage/lib/someOtherPath';

View File

@ -1,5 +1,5 @@
import {beforeEach, ddescribe, xdescribe, describe, expect, iit, inject, beforeEachProviders, it, xit,} from '@angular/core/testing/testing_internal';
import {Injectable, Component, Input, ViewMetadata, Compiler, ComponentFactory, Injector} from '@angular/core';
import {Injectable, Component, Input, ViewMetadata, Compiler, ComponentFactory, Injector, AppModule, AppModuleMetadata, AppModuleFactory} from '@angular/core';
import {ConcreteType, stringify} from '../src/facade/lang';
import {fakeAsync, tick, TestComponentBuilder, ComponentFixture} from '@angular/core/testing';
import {XHR, ViewResolver} from '@angular/compiler';
@ -19,6 +19,10 @@ class SomeComp {
class SomeCompWithUrlTemplate {
}
@AppModule({})
class SomeModule {
}
export function main() {
describe('RuntimeCompiler', () => {
let compiler: Compiler;
@ -104,5 +108,51 @@ export function main() {
expect(compFixture.nativeElement).toHaveText('hello');
}));
});
describe('compileAppModuleAsync', () => {
it('should allow to use templateUrl components', fakeAsync(() => {
xhr.spy('get').andCallFake(() => Promise.resolve('hello'));
let appModuleFactory: AppModuleFactory<any>;
compiler
.compileAppModuleAsync(
SomeModule, new AppModuleMetadata({precompile: [SomeCompWithUrlTemplate]}))
.then((f) => appModuleFactory = f);
tick();
expect(appModuleFactory.moduleType).toBe(SomeModule);
}));
});
describe('compileAppModuleSync', () => {
it('should throw when using a templateUrl that has not been compiled before', () => {
xhr.spy('get').andCallFake(() => Promise.resolve(''));
expect(
() => compiler.compileAppModuleSync(
SomeModule, new AppModuleMetadata({precompile: [SomeCompWithUrlTemplate]})))
.toThrowError(
`Can't compile synchronously as ${stringify(SomeCompWithUrlTemplate)} is still being loaded!`);
});
it('should throw when using a templateUrl in a nested component that has not been compiled before',
() => {
xhr.spy('get').andCallFake(() => Promise.resolve(''));
viewResolver.setView(
SomeComp, new ViewMetadata({template: '', directives: [ChildComp]}));
viewResolver.setView(ChildComp, new ViewMetadata({templateUrl: '/someTpl.html'}));
expect(
() => compiler.compileAppModuleSync(
SomeModule, new AppModuleMetadata({precompile: [SomeComp]})))
.toThrowError(
`Can't compile synchronously as ${stringify(ChildComp)} is still being loaded!`);
});
it('should allow to use templateUrl components that have been loaded before',
fakeAsync(() => {
xhr.spy('get').andCallFake(() => Promise.resolve('hello'));
tcb.createFakeAsync(SomeCompWithUrlTemplate);
let appModuleFactory = compiler.compileAppModuleSync(
SomeModule, new AppModuleMetadata({precompile: [SomeCompWithUrlTemplate]}));
expect(appModuleFactory).toBeTruthy();
}));
});
});
}