Skip to content

Commit 80608ed

Browse files
committed
feat(core/util/bundler): clean code and support nested arrays resolution
- export bundler function as Public API
1 parent 272e22a commit 80608ed

File tree

3 files changed

+130
-140
lines changed

3 files changed

+130
-140
lines changed

core.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export * from './src/core/di';
2+
export { bundle } from './src/core/util'
23
export {
34
Directive,
45
Component,

src/core/util/bundler.ts

+18-132
Original file line numberDiff line numberDiff line change
@@ -1,158 +1,44 @@
1-
import { global, isArray, isString, isType } from '../../facade/lang';
1+
import { global } from '../../facade/lang';
22
import { reflector } from '../reflection/reflection';
33
import { ComponentMetadata } from '../directives/metadata_directives';
44
import { getInjectableName, provide } from '../di/provider';
5-
import { isProviderLiteral, createProvider, ProviderLiteral } from '../di/provider_util';
6-
import { resolveReflectiveProvider } from '../di/reflective_provider';
7-
import { getNgModuleMethodByType } from '../di/provider';
5+
import { _isTypeRegistered, _normalizeProviders, _getNgModuleMetadataByType } from '../di/reflective_provider';
6+
import { ListWrapper } from '../../facade/collections';
87

9-
export function bundle( ComponentClass: Type, otherProviders: any[] = [], Module?: ng.IModule ): ng.IModule {
8+
export function bundle( ComponentClass: Type, otherProviders: any[] = [], NgModule?: ng.IModule ): ng.IModule {
109

1110
const ngModuleName = getInjectableName( ComponentClass );
12-
const ngModule = Module || global.angular.module( ngModuleName, [] );
11+
const ngModule = NgModule || global.angular.module( ngModuleName, [] );
1312
const annotations = reflector.annotations( ComponentClass );
1413
const cmpAnnotation: ComponentMetadata = annotations[ 0 ];
1514
const { directives = [], pipes = [], providers = [], viewProviders = [] }={} = cmpAnnotation;
1615

17-
// console.log( 'directives:', directives );
18-
// console.log( 'pipes:', pipes );
19-
// console.log( 'providers:', providers );
20-
// console.log( 'viewProviders:', viewProviders );
21-
22-
2316
// process component
24-
const cmpProvider = provide( ComponentClass );
17+
const [cmpName,cmpFactoryFn] = provide( ComponentClass );
18+
const { providerName, providerMethod, moduleMethod } = _getNgModuleMetadataByType( ComponentClass );
2519

26-
if ( isTypeRegistered( cmpProvider[ 0 ], ngModule, '$compileProvider', 'directive' ) ) {
20+
if ( _isTypeRegistered( cmpName, ngModule, providerName, providerMethod ) ) {
2721
return ngModule;
2822
}
2923

30-
ngModule.directive( cmpProvider[ 0 ], cmpProvider[ 1 ] );
31-
32-
33-
// 1. process component tree
34-
35-
// step through all providers
36-
providers.forEach( ( ProviderType ) => {
37-
38-
// @TODO
39-
// recursive
40-
if ( isArray( ProviderType ) ) {
41-
return;
42-
}
43-
44-
if ( isString( ProviderType ) ) {
45-
ngModule.requires.push( ProviderType );
46-
return;
47-
}
48-
49-
if ( isProviderLiteral( ProviderType ) ) {
50-
const provider = createProvider( ProviderType );
51-
const { method, name, value } = resolveReflectiveProvider( provider );
52-
if ( !isTypeRegistered( name, ngModule, '$provide', method ) ) {
53-
ngModule[ method ]( name, value );
54-
}
55-
return;
56-
}
57-
58-
const serviceProvider = provide( ProviderType );
59-
if ( !isTypeRegistered( serviceProvider[ 0 ], ngModule, '$provide', 'service' ) ) {
60-
ngModule.service( ...provide( ProviderType ) );
61-
}
62-
63-
} );
64-
// step through all viewProviders
65-
viewProviders.forEach( ( ViewProviderType ) => {
66-
67-
// @TODO
68-
// recursive
69-
if ( isArray( ViewProviderType ) ) {
70-
return;
71-
}
72-
73-
if ( isString( ViewProviderType ) ) {
74-
ngModule.requires.push( ViewProviderType );
75-
return;
76-
}
24+
ngModule[moduleMethod]( cmpName, cmpFactoryFn );
7725

78-
if ( isProviderLiteral( ViewProviderType ) ) {
79-
const provider = createProvider( ViewProviderType );
80-
const { method, name, value } = resolveReflectiveProvider( provider );
81-
if ( !isTypeRegistered( name, ngModule, '$provide', method ) ) {
82-
ngModule[ method ]( name, value );
83-
}
84-
return;
85-
}
26+
// 1. process component/directive decorator providers/viewProviders/pipes
27+
_normalizeProviders( ngModule, providers );
28+
_normalizeProviders( ngModule, viewProviders );
29+
_normalizeProviders( ngModule, pipes );
8630

87-
const serviceProvider = provide( ViewProviderType );
88-
if ( !isTypeRegistered( serviceProvider[ 0 ], ngModule, '$provide', 'service' ) ) {
89-
ngModule.service( ...provide( ViewProviderType ) );
90-
}
9131

92-
} );
93-
// step through all pipes
94-
pipes.forEach( ( PipeType: Type ) => {
95-
// @TODO
96-
// recursive
97-
if ( isArray( PipeType ) ) {
98-
return;
99-
}
100-
101-
const pipeProvider = provide( PipeType );
102-
if ( !isTypeRegistered( pipeProvider[ 0 ], ngModule, '$filterProvider', 'register' ) ) {
103-
ngModule.filter( ...provide( PipeType ) );
104-
}
105-
} );
10632
// step through all directives
107-
directives.forEach( ( directiveType: Type ) => {
108-
return bundle( directiveType, [], ngModule );
33+
ListWrapper.flattenDeep(directives).forEach( ( directiveType: Type ) => {
34+
bundle( directiveType, [], ngModule );
10935
} );
11036

11137
// 2. process otherProviders argument
11238
// - providers can be string(ngModule reference), Type, StringMap(providerLiteral)
113-
otherProviders.forEach( ( providerType: string|Type|ProviderLiteral|any[] ) => {
114-
if ( isString( providerType ) ) {
115-
ngModule.requires.push( providerType );
116-
}
117-
if ( isType( providerType ) ) {
118-
ngModule[ getNgModuleMethodByType(providerType) ]( ...provide(providerType) );
119-
}
120-
if ( isProviderLiteral( providerType ) ) {
121-
const provider = createProvider( providerType );
122-
const { method, name, value } = resolveReflectiveProvider( provider );
123-
ngModule[ method ]( name, value );
124-
}
125-
// @TODO
126-
// recursive
127-
if ( isArray( providerType ) ) {
128-
129-
}
130-
} );
39+
// - directives can't be registered as via global providers only @Injectable,@Pipe,{provide:any,use*:any}
40+
// registerProviders(ngModule, otherProviders);
41+
_normalizeProviders( ngModule, otherProviders );
13142

13243
return ngModule;
133-
134-
}
135-
136-
function isTypeRegistered(
137-
findRegisteredType: string,
138-
ngModule: ng.IModule,
139-
instanceType: string,
140-
methodName: string
141-
): boolean {
142-
const invokeQueue: any[] = (ngModule as any)._invokeQueue;
143-
const types = invokeQueue
144-
.filter( ( [type,fnName]:[string,string] ) => {
145-
return type === instanceType && fnName === methodName;
146-
} )
147-
.map( ( [type,fnName, registeredProvider]:[string,string,[string,any]] ) => {
148-
return registeredProvider
149-
} );
150-
151-
return types.some( ( [typeName,typeFn] )=> {
152-
return findRegisteredType === typeName;
153-
} )
154-
}
155-
156-
function registerProvider( ngModule: ng.IModule, provider: any ): void {
157-
15844
}

test/core/util/bundler.spec.ts

+111-8
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,14 @@ import { bundle } from '../../../src/core/util/bundler';
88
import { Pipe } from '../../../src/core/pipes/decorators';
99
import { Injectable } from '../../../src/core/di/decorators';
1010
import { OpaqueToken } from '../../../src/core/di/opaque_token';
11+
import { Directive } from '../../../src/core/directives/decorators';
1112

1213
describe( `util/bundler`, () => {
1314

1415
let sandbox: Sinon.SinonSandbox;
15-
global.angular = createNgModule() as any;
1616
beforeEach( () => {
17+
global.angular = createNgModule() as any;
1718
sandbox = sinon.sandbox.create();
18-
} );
19-
beforeEach( () => {
20-
21-
2219
} );
2320
afterEach( () => {
2421
sandbox.restore();
@@ -90,7 +87,7 @@ describe( `util/bundler`, () => {
9087
selector: 'app',
9188
directives: [ ChildOneComponent, ChildTwoComponent ],
9289
providers: [ MySingleton, { provide: 'tokenAsClass', useClass: ViaProviderLiteralService } ],
93-
template: `Hello App
90+
template: `Hello App
9491
<child-one></child-one>
9592
<child-two></child-two>
9693
`
@@ -130,17 +127,123 @@ describe( `util/bundler`, () => {
130127
const actual = _invokeQueueToCompare( actualInvokeQueue, false );
131128
const expected = _invokeQueueToCompare( expectedInvokeQueue, false );
132129

133-
// console.log( actual );
130+
// console.log( 'actual:',actual );
134131
// console.log( '========' );
135-
// console.log( expected );
132+
// console.log( 'expected:',expected );
136133

137134
expect( actual ).to.deep.equal( expected );
138135
expect( ngModule.requires ).to.deep.equal( [ 'ui.bootstrap.modal','3rdParty' ] );
139136

140137
} );
141138

139+
it( `should support nested providers and normalize them`, () => {
140+
141+
const PluginFooDirectives = [ NestedComponent ];
142+
const PluginFooProviders = [ MyService, MySingleton ];
143+
const PluginFooPipes = [ UpsPipe ];
144+
145+
@Directive({selector:'[yo]'})
146+
class YoDirective {}
147+
148+
@Component( {
149+
selector: 'app-with-plugin',
150+
template: 'hello',
151+
directives: [ PluginFooDirectives, YoDirective ],
152+
providers: [ PluginFooProviders, MyPrivateService ],
153+
pipes: [ PluginFooPipes ]
154+
} )
155+
class AppWithPluginComponent {}
156+
157+
const ngModule = bundle( AppWithPluginComponent );
158+
159+
const expectedInvokeQueue = [
160+
[ '$compileProvider', 'directive', provide( AppWithPluginComponent ) ],
161+
[ '$provide', 'service', provide( MyService ) ],
162+
[ '$provide', 'service', provide( MySingleton ) ],
163+
[ '$provide', 'service', provide( MyPrivateService ) ],
164+
[ '$filterProvider', 'register', provide( UpsPipe ) ],
165+
[ '$compileProvider', 'directive', provide( NestedComponent ) ],
166+
[ '$compileProvider', 'directive', provide( YoDirective ) ]
167+
];
168+
const actualInvokeQueue = (ngModule as any)._invokeQueue;
169+
const actual = _invokeQueueToCompare( actualInvokeQueue, false );
170+
const expected = _invokeQueueToCompare( expectedInvokeQueue, false );
171+
172+
// console.log( 'actual:',actual );
173+
// console.log( '========' );
174+
// console.log( 'expected:',expected );
175+
176+
expect( actual ).to.deep.equal( expected );
177+
178+
} );
179+
180+
it( `should allow ngModule.config within otherProviders setup`, () => {
181+
182+
@Injectable()
183+
class MyDynamicService {}
184+
185+
configPhase.$inject = ['$provide'];
186+
function configPhase($provide: ng.auto.IProvideService){
187+
$provide.service( ...provide( MyDynamicService ) );
188+
$provide.factory(
189+
...provide(
190+
'promiseWrapper',
191+
{ deps: [ '$q' ], useFactory: ( value )=>( $q )=>$q.resolve() }
192+
)
193+
);
194+
}
195+
196+
@Component( {
197+
selector: 'pure-app',
198+
template: 'hello'
199+
} )
200+
class PureAppComponent {}
201+
202+
const ngModule = bundle( PureAppComponent, [ configPhase ] );
203+
204+
const expectedInvokeQueue = [
205+
[ '$compileProvider', 'directive', provide( PureAppComponent ) ],
206+
[ '$provide', 'service', provide( MyDynamicService ) ],
207+
[
208+
'$provide', 'factory', provide(
209+
'promiseWrapper',
210+
{ deps: [ '$q' ], useFactory: ( value )=>( $q )=>$q.resolve() }
211+
)
212+
],
213+
];
214+
const expectedConfigBlocks = [
215+
[ '$injector', 'invoke', [ configPhase ] ]
216+
];
217+
218+
const actualConfigBlocks = (ngModule as any)._configBlocks;
219+
const actualInvokeQueue = (ngModule as any)._invokeQueue;
220+
221+
expect( actualConfigBlocks ).to.deep.equal( expectedConfigBlocks );
222+
223+
_execConfigBlocks(ngModule);
224+
225+
const actual = _invokeQueueToCompare( actualInvokeQueue, false );
226+
const expected = _invokeQueueToCompare( expectedInvokeQueue, false );
227+
228+
// console.log( 'actual:',actual );
229+
// console.log( '========' );
230+
// console.log( 'expected:',expected );
231+
232+
expect( actual ).to.deep.equal( expected );
233+
234+
} );
235+
142236
} );
143237

238+
function _execConfigBlocks( ngModule: any ) {
239+
const configBlocks = ngModule._configBlocks;
240+
configBlocks.forEach( ( config )=> {
241+
const registeredFnArr = config[ 2 ];
242+
const fn = registeredFnArr[ 0 ];
243+
fn(ngModule);
244+
} );
245+
}
246+
144247
function _invokeQueueToCompare( invokeQueue: any, shouldSort = true ): [string,string,string] {
145248

146249
const processedQueue = invokeQueue

0 commit comments

Comments
 (0)