refactor(compiler): split compiler and core (#18683)

After this, neither @angular/compiler nor @angular/comnpiler-cli depend
on @angular/core.

This add a duplication of some interfaces and enums which is stored
in @angular/compiler/src/core.ts

BREAKING CHANGE:
- `@angular/platform-server` now additionally depends on
  `@angular/platform-browser-dynamic` as a peer dependency.


PR Close #18683
This commit is contained in:
Tobias Bosch
2017-08-16 09:00:03 -07:00
committed by Miško Hevery
parent a0ca01d580
commit 0cc77b4a69
107 changed files with 1504 additions and 1564 deletions

View File

@ -5,144 +5,28 @@
* 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 {CompileReflector, DirectiveResolver} from '@angular/compiler';
import {Compiler, Component, Directive, Injectable, Injector, Provider, Type, resolveForwardRef, ɵViewMetadata as ViewMetadata} from '@angular/core';
import {CompileReflector, DirectiveResolver, core} from '@angular/compiler';
/**
* An implementation of {@link DirectiveResolver} that allows overriding
* various properties of directives.
*/
@Injectable()
export class MockDirectiveResolver extends DirectiveResolver {
private _directives = new Map<Type<any>, Directive>();
private _providerOverrides = new Map<Type<any>, any[]>();
private _viewProviderOverrides = new Map<Type<any>, any[]>();
private _views = new Map<Type<any>, ViewMetadata>();
private _inlineTemplates = new Map<Type<any>, string>();
private _directives = new Map<core.Type, core.Directive>();
constructor(private _injector: Injector, reflector: CompileReflector) { super(reflector); }
constructor(reflector: CompileReflector) { super(reflector); }
private get _compiler(): Compiler { return this._injector.get(Compiler); }
private _clearCacheFor(component: Type<any>) { this._compiler.clearCacheFor(component); }
resolve(type: Type<any>): Directive;
resolve(type: Type<any>, throwIfNotFound: true): Directive;
resolve(type: Type<any>, throwIfNotFound: boolean): Directive|null;
resolve(type: Type<any>, throwIfNotFound = true): Directive|null {
let metadata = this._directives.get(type) || null;
if (!metadata) {
metadata = super.resolve(type, throwIfNotFound);
}
if (!metadata) {
return null;
}
const providerOverrides = this._providerOverrides.get(type);
const viewProviderOverrides = this._viewProviderOverrides.get(type);
let providers = metadata.providers;
if (providerOverrides != null) {
const originalViewProviders: Provider[] = metadata.providers || [];
providers = originalViewProviders.concat(providerOverrides);
}
if (metadata instanceof Component) {
let viewProviders = metadata.viewProviders;
if (viewProviderOverrides != null) {
const originalViewProviders: Provider[] = metadata.viewProviders || [];
viewProviders = originalViewProviders.concat(viewProviderOverrides);
}
let view = this._views.get(type) || metadata;
let animations = view.animations;
let templateUrl: string|undefined = view.templateUrl;
let inlineTemplate = this._inlineTemplates.get(type);
if (inlineTemplate) {
templateUrl = undefined;
} else {
inlineTemplate = view.template;
}
return new Component({
selector: metadata.selector,
inputs: metadata.inputs,
outputs: metadata.outputs,
host: metadata.host,
exportAs: metadata.exportAs,
moduleId: metadata.moduleId,
queries: metadata.queries,
changeDetection: metadata.changeDetection,
providers: providers,
viewProviders: viewProviders,
entryComponents: metadata.entryComponents,
template: inlineTemplate,
templateUrl: templateUrl,
animations: animations,
styles: view.styles,
styleUrls: view.styleUrls,
encapsulation: view.encapsulation,
interpolation: view.interpolation,
preserveWhitespaces: view.preserveWhitespaces,
});
}
return new Directive({
selector: metadata.selector,
inputs: metadata.inputs,
outputs: metadata.outputs,
host: metadata.host,
providers: providers,
exportAs: metadata.exportAs,
queries: metadata.queries
});
resolve(type: core.Type): core.Directive;
resolve(type: core.Type, throwIfNotFound: true): core.Directive;
resolve(type: core.Type, throwIfNotFound: boolean): core.Directive|null;
resolve(type: core.Type, throwIfNotFound = true): core.Directive|null {
return this._directives.get(type) || super.resolve(type, throwIfNotFound);
}
/**
* Overrides the {@link Directive} for a directive.
* Overrides the {@link core.Directive} for a directive.
*/
setDirective(type: Type<any>, metadata: Directive): void {
setDirective(type: core.Type, metadata: core.Directive): void {
this._directives.set(type, metadata);
this._clearCacheFor(type);
}
setProvidersOverride(type: Type<any>, providers: Provider[]): void {
this._providerOverrides.set(type, providers);
this._clearCacheFor(type);
}
setViewProvidersOverride(type: Type<any>, viewProviders: Provider[]): void {
this._viewProviderOverrides.set(type, viewProviders);
this._clearCacheFor(type);
}
/**
* Overrides the {@link ViewMetadata} for a component.
*/
setView(component: Type<any>, view: ViewMetadata): void {
this._views.set(component, view);
this._clearCacheFor(component);
}
/**
* Overrides the inline template for a component - other configuration remains unchanged.
*/
setInlineTemplate(component: Type<any>, template: string): void {
this._inlineTemplates.set(component, template);
this._clearCacheFor(component);
}
}
function flattenArray(tree: any[], out: Array<Type<any>|any[]>): void {
if (tree == null) return;
for (let i = 0; i < tree.length; i++) {
const item = resolveForwardRef(tree[i]);
if (Array.isArray(item)) {
flattenArray(item, out);
} else {
out.push(item);
}
}
}

View File

@ -1,131 +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 {ɵstringify as stringify} from '@angular/core';
import {MetadataOverride} from '@angular/core/testing';
type StringMap = {
[key: string]: any
};
let _nextReferenceId = 0;
export class MetadataOverrider {
private _references = new Map<any, string>();
/**
* Creates a new instance for the given metadata class
* based on an old instance and overrides.
*/
overrideMetadata<C extends T, T>(
metadataClass: {new (options: T): C;}, oldMetadata: C, override: MetadataOverride<T>): C {
const props: StringMap = {};
if (oldMetadata) {
_valueProps(oldMetadata).forEach((prop) => props[prop] = (<any>oldMetadata)[prop]);
}
if (override.set) {
if (override.remove || override.add) {
throw new Error(`Cannot set and add/remove ${stringify(metadataClass)} at the same time!`);
}
setMetadata(props, override.set);
}
if (override.remove) {
removeMetadata(props, override.remove, this._references);
}
if (override.add) {
addMetadata(props, override.add);
}
return new metadataClass(<any>props);
}
}
function removeMetadata(metadata: StringMap, remove: any, references: Map<any, string>) {
const removeObjects = new Set<string>();
for (const prop in remove) {
const removeValue = remove[prop];
if (removeValue instanceof Array) {
removeValue.forEach(
(value: any) => { removeObjects.add(_propHashKey(prop, value, references)); });
} else {
removeObjects.add(_propHashKey(prop, removeValue, references));
}
}
for (const prop in metadata) {
const propValue = metadata[prop];
if (propValue instanceof Array) {
metadata[prop] = propValue.filter(
(value: any) => !removeObjects.has(_propHashKey(prop, value, references)));
} else {
if (removeObjects.has(_propHashKey(prop, propValue, references))) {
metadata[prop] = undefined;
}
}
}
}
function addMetadata(metadata: StringMap, add: any) {
for (const prop in add) {
const addValue = add[prop];
const propValue = metadata[prop];
if (propValue != null && propValue instanceof Array) {
metadata[prop] = propValue.concat(addValue);
} else {
metadata[prop] = addValue;
}
}
}
function setMetadata(metadata: StringMap, set: any) {
for (const prop in set) {
metadata[prop] = set[prop];
}
}
function _propHashKey(propName: any, propValue: any, references: Map<any, string>): string {
const replacer = (key: any, value: any) => {
if (typeof value === 'function') {
value = _serializeReference(value, references);
}
return value;
};
return `${propName}:${JSON.stringify(propValue, replacer)}`;
}
function _serializeReference(ref: any, references: Map<any, string>): string {
let id = references.get(ref);
if (!id) {
id = `${stringify(ref)}${_nextReferenceId++}`;
references.set(ref, id);
}
return id;
}
function _valueProps(obj: any): string[] {
const props: string[] = [];
// regular public props
Object.keys(obj).forEach((prop) => {
if (!prop.startsWith('_')) {
props.push(prop);
}
});
// getters
let proto = obj;
while (proto = Object.getPrototypeOf(proto)) {
Object.keys(proto).forEach((protoProp) => {
const desc = Object.getOwnPropertyDescriptor(proto, protoProp);
if (!protoProp.startsWith('_') && desc && 'get' in desc) {
props.push(protoProp);
}
});
}
return props;
}

View File

@ -6,21 +6,18 @@
* found in the LICENSE file at https://angular.io/license
*/
import {CompileReflector, NgModuleResolver} from '@angular/compiler';
import {Compiler, Injectable, Injector, NgModule, Type} from '@angular/core';
import {CompileReflector, NgModuleResolver, core} from '@angular/compiler';
@Injectable()
export class MockNgModuleResolver extends NgModuleResolver {
private _ngModules = new Map<Type<any>, NgModule>();
private _ngModules = new Map<core.Type, core.NgModule>();
constructor(private _injector: Injector, reflector: CompileReflector) { super(reflector); }
constructor(reflector: CompileReflector) { super(reflector); }
/**
* Overrides the {@link NgModule} for a module.
*/
setNgModule(type: Type<any>, metadata: NgModule): void {
setNgModule(type: core.Type, metadata: core.NgModule): void {
this._ngModules.set(type, metadata);
this._clearCacheFor(type);
}
/**
@ -29,11 +26,7 @@ export class MockNgModuleResolver extends NgModuleResolver {
* default
* `NgModuleResolver`, see `setNgModule`.
*/
resolve(type: Type<any>, throwIfNotFound = true): NgModule {
resolve(type: core.Type, throwIfNotFound = true): core.NgModule {
return this._ngModules.get(type) || super.resolve(type, throwIfNotFound) !;
}
private get _compiler(): Compiler { return this._injector.get(Compiler); }
private _clearCacheFor(component: Type<any>) { this._compiler.clearCacheFor(component); }
}

View File

@ -6,26 +6,17 @@
* found in the LICENSE file at https://angular.io/license
*/
import {CompileReflector, PipeResolver} from '@angular/compiler';
import {Compiler, Injectable, Injector, Pipe, Type} from '@angular/core';
import {CompileReflector, PipeResolver, core} from '@angular/compiler';
@Injectable()
export class MockPipeResolver extends PipeResolver {
private _pipes = new Map<Type<any>, Pipe>();
private _pipes = new Map<core.Type, core.Pipe>();
constructor(private _injector: Injector, refector: CompileReflector) { super(refector); }
private get _compiler(): Compiler { return this._injector.get(Compiler); }
private _clearCacheFor(pipe: Type<any>) { this._compiler.clearCacheFor(pipe); }
constructor(refector: CompileReflector) { super(refector); }
/**
* Overrides the {@link Pipe} for a pipe.
*/
setPipe(type: Type<any>, metadata: Pipe): void {
this._pipes.set(type, metadata);
this._clearCacheFor(type);
}
setPipe(type: core.Type, metadata: core.Pipe): void { this._pipes.set(type, metadata); }
/**
* Returns the {@link Pipe} for a pipe:
@ -33,7 +24,7 @@ export class MockPipeResolver extends PipeResolver {
* default
* `PipeResolver`, see `setPipe`.
*/
resolve(type: Type<any>, throwIfNotFound = true): Pipe {
resolve(type: core.Type, throwIfNotFound = true): core.Pipe {
let metadata = this._pipes.get(type);
if (!metadata) {
metadata = super.resolve(type, throwIfNotFound) !;

View File

@ -6,8 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ElementSchemaRegistry} from '@angular/compiler';
import {SchemaMetadata, SecurityContext} from '@angular/core';
import {ElementSchemaRegistry, core} from '@angular/compiler';
export class MockSchemaRegistry implements ElementSchemaRegistry {
constructor(
@ -16,20 +15,20 @@ export class MockSchemaRegistry implements ElementSchemaRegistry {
public existingElements: {[key: string]: boolean}, public invalidProperties: Array<string>,
public invalidAttributes: Array<string>) {}
hasProperty(tagName: string, property: string, schemas: SchemaMetadata[]): boolean {
hasProperty(tagName: string, property: string, schemas: core.SchemaMetadata[]): boolean {
const value = this.existingProperties[property];
return value === void 0 ? true : value;
}
hasElement(tagName: string, schemaMetas: SchemaMetadata[]): boolean {
hasElement(tagName: string, schemaMetas: core.SchemaMetadata[]): boolean {
const value = this.existingElements[tagName.toLowerCase()];
return value === void 0 ? true : value;
}
allKnownElementNames(): string[] { return Object.keys(this.existingElements); }
securityContext(selector: string, property: string, isAttribute: boolean): SecurityContext {
return SecurityContext.NONE;
securityContext(selector: string, property: string, isAttribute: boolean): core.SecurityContext {
return core.SecurityContext.NONE;
}
getMappedPropName(attrName: string): string { return this.attrPropMapping[attrName] || attrName; }

View File

@ -1,25 +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 {ElementSchemaRegistry, ResourceLoader, UrlResolver} from '@angular/compiler';
import {Provider} from '@angular/core';
import {MockResourceLoader} from './resource_loader_mock';
import {MockSchemaRegistry} from './schema_registry_mock';
export function createUrlResolverWithoutPackagePrefix(): UrlResolver {
return new UrlResolver();
}
// This provider is put here just so that we can access it from multiple
// internal test packages.
// TODO: get rid of it or move to a separate @angular/internal_testing package
export const TEST_COMPILER_PROVIDERS: Provider[] = [
{provide: ElementSchemaRegistry, useValue: new MockSchemaRegistry({}, {}, {}, [], [])},
{provide: ResourceLoader, useClass: MockResourceLoader, deps: []},
{provide: UrlResolver, useFactory: createUrlResolverWithoutPackagePrefix, deps: []}
];

View File

@ -21,122 +21,8 @@
* </p>
* </div>
*/
export * from './resource_loader_mock';
export * from './schema_registry_mock';
export * from './directive_resolver_mock';
export * from './ng_module_resolver_mock';
export * from './pipe_resolver_mock';
import {createPlatformFactory, ModuleWithComponentFactories, Injectable, CompilerOptions, COMPILER_OPTIONS, CompilerFactory, ComponentFactory, NgModuleFactory, Injector, NgModule, Component, Directive, Pipe, Type, PlatformRef, ɵstringify} from '@angular/core';
import {MetadataOverride, ɵTestingCompilerFactory as TestingCompilerFactory, ɵTestingCompiler as TestingCompiler} from '@angular/core/testing';
import {platformCoreDynamic, JitCompiler, DirectiveResolver, NgModuleResolver, PipeResolver, CompileMetadataResolver, CompileReflector} from '@angular/compiler';
import {MockDirectiveResolver} from './directive_resolver_mock';
import {MockNgModuleResolver} from './ng_module_resolver_mock';
import {MockPipeResolver} from './pipe_resolver_mock';
import {MetadataOverrider} from './metadata_overrider';
@Injectable()
export class TestingCompilerFactoryImpl implements TestingCompilerFactory {
constructor(private _compilerFactory: CompilerFactory) {}
createTestingCompiler(options: CompilerOptions[]): TestingCompiler {
const compiler = <JitCompiler>this._compilerFactory.createCompiler(options);
return new TestingCompilerImpl(
compiler, compiler.injector.get(MockDirectiveResolver),
compiler.injector.get(MockPipeResolver), compiler.injector.get(MockNgModuleResolver),
compiler.injector.get(CompileMetadataResolver));
}
}
export class TestingCompilerImpl implements TestingCompiler {
private _overrider = new MetadataOverrider();
constructor(
private _compiler: JitCompiler, private _directiveResolver: MockDirectiveResolver,
private _pipeResolver: MockPipeResolver, private _moduleResolver: MockNgModuleResolver,
private _metadataResolver: CompileMetadataResolver) {}
get injector(): Injector { return this._compiler.injector; }
compileModuleSync<T>(moduleType: Type<T>): NgModuleFactory<T> {
return this._compiler.compileModuleSync(moduleType);
}
compileModuleAsync<T>(moduleType: Type<T>): Promise<NgModuleFactory<T>> {
return this._compiler.compileModuleAsync(moduleType);
}
compileModuleAndAllComponentsSync<T>(moduleType: Type<T>): ModuleWithComponentFactories<T> {
return this._compiler.compileModuleAndAllComponentsSync(moduleType);
}
compileModuleAndAllComponentsAsync<T>(moduleType: Type<T>):
Promise<ModuleWithComponentFactories<T>> {
return this._compiler.compileModuleAndAllComponentsAsync(moduleType);
}
getNgContentSelectors(component: Type<any>): string[] {
return this._compiler.getNgContentSelectors(component);
}
getComponentFactory<T>(component: Type<T>): ComponentFactory<T> {
return this._compiler.getComponentFactory(component);
}
checkOverrideAllowed(type: Type<any>) {
if (this._compiler.hasAotSummary(type)) {
throw new Error(`${ɵstringify(type)} was AOT compiled, so its metadata cannot be changed.`);
}
}
overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): void {
this.checkOverrideAllowed(ngModule);
const oldMetadata = this._moduleResolver.resolve(ngModule, false);
this._moduleResolver.setNgModule(
ngModule, this._overrider.overrideMetadata(NgModule, oldMetadata, override));
}
overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): void {
this.checkOverrideAllowed(directive);
const oldMetadata = this._directiveResolver.resolve(directive, false);
this._directiveResolver.setDirective(
directive, this._overrider.overrideMetadata(Directive, oldMetadata !, override));
}
overrideComponent(component: Type<any>, override: MetadataOverride<Component>): void {
this.checkOverrideAllowed(component);
const oldMetadata = this._directiveResolver.resolve(component, false);
this._directiveResolver.setDirective(
component, this._overrider.overrideMetadata(Component, oldMetadata !, override));
}
overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): void {
this.checkOverrideAllowed(pipe);
const oldMetadata = this._pipeResolver.resolve(pipe, false);
this._pipeResolver.setPipe(pipe, this._overrider.overrideMetadata(Pipe, oldMetadata, override));
}
loadAotSummaries(summaries: () => any[]) { this._compiler.loadAotSummaries(summaries); }
clearCache(): void { this._compiler.clearCache(); }
clearCacheFor(type: Type<any>) { this._compiler.clearCacheFor(type); }
}
/**
* Platform for dynamic tests
*
* @experimental
*/
export const platformCoreDynamicTesting: (extraProviders?: any[]) => PlatformRef =
createPlatformFactory(platformCoreDynamic, 'coreDynamicTesting', [
{
provide: COMPILER_OPTIONS,
useValue: {
providers: [
{provide: MockPipeResolver, deps: [Injector, CompileReflector]},
{provide: PipeResolver, useExisting: MockPipeResolver},
{provide: MockDirectiveResolver, deps: [Injector, CompileReflector]},
{provide: DirectiveResolver, useExisting: MockDirectiveResolver},
{provide: MockNgModuleResolver, deps: [Injector, CompileReflector]},
{provide: NgModuleResolver, useExisting: MockNgModuleResolver},
]
},
multi: true
},
{
provide: TestingCompilerFactory,
useClass: TestingCompilerFactoryImpl,
deps: [CompilerFactory]
}
]);

View File

@ -5,8 +5,6 @@
// Test that we rely on decorator downleveling
"emitDecoratorMetadata": false,
"paths": {
"@angular/core": ["../../dist/packages/core"],
"@angular/core/testing": ["../../dist/packages/core/testing"],
"@angular/compiler": ["../../dist/packages/compiler"]
}
},