From 8b782818f54d035b4ad26df75a15949cf60a62cf Mon Sep 17 00:00:00 2001 From: Alex Rickabaugh Date: Wed, 24 Aug 2016 16:54:42 -0700 Subject: [PATCH] feat(linker): Allow configurable module prefixes and suffixes. (#11049) --- modules/@angular/core/src/linker.ts | 2 +- .../system_js_ng_module_factory_loader.ts | 52 ++++++++++++++++--- .../system_ng_module_factory_loader_spec.ts | 49 +++++++++++++++++ tools/public_api_guard/core/index.d.ts | 8 ++- 4 files changed, 102 insertions(+), 9 deletions(-) create mode 100644 modules/@angular/core/test/linker/system_ng_module_factory_loader_spec.ts diff --git a/modules/@angular/core/src/linker.ts b/modules/@angular/core/src/linker.ts index 28f3441758..3b4badc92b 100644 --- a/modules/@angular/core/src/linker.ts +++ b/modules/@angular/core/src/linker.ts @@ -15,7 +15,7 @@ export {ExpressionChangedAfterItHasBeenCheckedException} from './linker/exceptio export {NgModuleFactory, NgModuleRef} from './linker/ng_module_factory'; export {NgModuleFactoryLoader} from './linker/ng_module_factory_loader'; export {QueryList} from './linker/query_list'; -export {SystemJsNgModuleLoader} from './linker/system_js_ng_module_factory_loader'; +export {SystemJsNgModuleLoader, SystemJsNgModuleLoaderConfig} from './linker/system_js_ng_module_factory_loader'; export {TemplateRef} from './linker/template_ref'; export {ViewContainerRef} from './linker/view_container_ref'; export {EmbeddedViewRef, ViewRef} from './linker/view_ref'; diff --git a/modules/@angular/core/src/linker/system_js_ng_module_factory_loader.ts b/modules/@angular/core/src/linker/system_js_ng_module_factory_loader.ts index 82da26b23c..bb0440809c 100644 --- a/modules/@angular/core/src/linker/system_js_ng_module_factory_loader.ts +++ b/modules/@angular/core/src/linker/system_js_ng_module_factory_loader.ts @@ -7,7 +7,7 @@ */ -import {Injectable} from '../di'; +import {Injectable, Optional} from '../di'; import {Compiler} from './compiler'; import {NgModuleFactory} from './ng_module_factory'; @@ -15,16 +15,48 @@ import {NgModuleFactoryLoader} from './ng_module_factory_loader'; const _SEPARATOR = '#'; -const FACTORY_MODULE_SUFFIX = '.ngfactory'; const FACTORY_CLASS_SUFFIX = 'NgFactory'; +/** + * Configuration for SystemJsNgModuleLoader. + * token. + * + * @experimental + */ +export abstract class SystemJsNgModuleLoaderConfig { + /** + * Prefix to add when computing the name of the factory module for a given module name. + */ + factoryPathPrefix: string; + + /** + * Suffix to add when computing the name of the factory module for a given module name. + */ + factoryPathSuffix: string; +} + +const DEFAULT_CONFIG: SystemJsNgModuleLoaderConfig = { + factoryPathPrefix: '', + factoryPathSuffix: '.ngfactory', +}; + /** * NgModuleFactoryLoader that uses SystemJS to load NgModuleFactory * @experimental */ @Injectable() export class SystemJsNgModuleLoader implements NgModuleFactoryLoader { - constructor(private _compiler: Compiler) {} + private _config: SystemJsNgModuleLoaderConfig; + + /** + * @internal + */ + _system: any; + + constructor(private _compiler: Compiler, @Optional() config?: SystemJsNgModuleLoaderConfig) { + this._system = () => System; + this._config = config || DEFAULT_CONFIG; + } load(path: string): Promise> { const offlineMode = this._compiler instanceof Compiler; @@ -35,7 +67,8 @@ export class SystemJsNgModuleLoader implements NgModuleFactoryLoader { let [module, exportName] = path.split(_SEPARATOR); if (exportName === undefined) exportName = 'default'; - return System.import(module) + return this._system() + .import(module) .then((module: any) => module[exportName]) .then((type: any) => checkNotEmpty(type, module, exportName)) .then((type: any) => this._compiler.compileModuleAsync(type)); @@ -43,10 +76,15 @@ export class SystemJsNgModuleLoader implements NgModuleFactoryLoader { private loadFactory(path: string): Promise> { let [module, exportName] = path.split(_SEPARATOR); - if (exportName === undefined) exportName = 'default'; + let factoryClassSuffix = FACTORY_CLASS_SUFFIX; + if (exportName === undefined) { + exportName = 'default'; + factoryClassSuffix = ''; + } - return System.import(module + FACTORY_MODULE_SUFFIX) - .then((module: any) => module[exportName + FACTORY_CLASS_SUFFIX]) + return this._system() + .import(this._config.factoryPathPrefix + module + this._config.factoryPathSuffix) + .then((module: any) => module[exportName + factoryClassSuffix]) .then((factory: any) => checkNotEmpty(factory, module, exportName)); } } diff --git a/modules/@angular/core/test/linker/system_ng_module_factory_loader_spec.ts b/modules/@angular/core/test/linker/system_ng_module_factory_loader_spec.ts new file mode 100644 index 0000000000..5bdd897c9c --- /dev/null +++ b/modules/@angular/core/test/linker/system_ng_module_factory_loader_spec.ts @@ -0,0 +1,49 @@ +/** + * @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 {Compiler, SystemJsNgModuleLoader} from '@angular/core'; +import {async, tick} from '@angular/core/testing'; +import {beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal'; + +function mockSystem(module: string, contents: any) { + return { + 'import': (target: string) => { + expect(target).toBe(module); + return Promise.resolve(contents); + } + }; +} + +export function main() { + describe('SystemJsNgModuleLoader', () => { + it('loads a default factory by appending the factory suffix', async(() => { + let loader = new SystemJsNgModuleLoader(new Compiler()); + loader._system = () => mockSystem('test.ngfactory', {'default': 'test module factory'}); + loader.load('test').then(contents => { expect(contents).toBe('test module factory'); }); + })); + it('loads a named factory by appending the factory suffix', async(() => { + let loader = new SystemJsNgModuleLoader(new Compiler()); + loader._system = () => + mockSystem('test.ngfactory', {'NamedNgFactory': 'test module factory'}); + loader.load('test#Named').then(contents => { + expect(contents).toBe('test module factory'); + }); + })); + it('loads a named factory with a configured prefix and suffix', async(() => { + let loader = new SystemJsNgModuleLoader(new Compiler(), { + factoryPathPrefix: 'prefixed/', + factoryPathSuffix: '/suffixed', + }); + loader._system = () => + mockSystem('prefixed/test/suffixed', {'NamedNgFactory': 'test module factory'}); + loader.load('test#Named').then(contents => { + expect(contents).toBe('test module factory'); + }); + })); + }); +}; diff --git a/tools/public_api_guard/core/index.d.ts b/tools/public_api_guard/core/index.d.ts index 72c1e66625..0351e6618f 100644 --- a/tools/public_api_guard/core/index.d.ts +++ b/tools/public_api_guard/core/index.d.ts @@ -1158,10 +1158,16 @@ export declare function style(tokens: string | { /** @experimental */ export declare class SystemJsNgModuleLoader implements NgModuleFactoryLoader { - constructor(_compiler: Compiler); + constructor(_compiler: Compiler, config?: SystemJsNgModuleLoaderConfig); load(path: string): Promise>; } +/** @experimental */ +export declare abstract class SystemJsNgModuleLoaderConfig { + factoryPathPrefix: string; + factoryPathSuffix: string; +} + /** @stable */ export declare abstract class TemplateRef { elementRef: ElementRef;