@@ -12,21 +12,21 @@ import {
1212 JsonParseMode ,
1313 Path ,
1414 dirname ,
15+ experimental ,
1516 getSystemPath ,
1617 join ,
1718 logging ,
1819 normalize ,
1920 parseJson ,
2021 resolve ,
21- schema ,
2222 virtualFs ,
2323} from '@angular-devkit/core' ;
2424import { resolve as nodeResolve } from '@angular-devkit/core/node' ;
2525import { Observable } from 'rxjs/Observable' ;
2626import { forkJoin } from 'rxjs/observable/forkJoin' ;
2727import { of } from 'rxjs/observable/of' ;
2828import { _throw } from 'rxjs/observable/throw' ;
29- import { concatMap , map } from 'rxjs/operators' ;
29+ import { concatMap , map , tap } from 'rxjs/operators' ;
3030import {
3131 BuildEvent ,
3232 Builder ,
@@ -36,15 +36,6 @@ import {
3636 BuilderPaths ,
3737 BuilderPathsMap ,
3838} from './builder' ;
39- import { Workspace } from './workspace' ;
40-
41-
42- export class ProjectNotFoundException extends BaseException {
43- constructor ( name ?: string ) {
44- const nameOrDefault = name ? `Project '${ name } '` : `Default project` ;
45- super ( `${ nameOrDefault } could not be found in workspace.` ) ;
46- }
47- }
4839
4940export class TargetNotFoundException extends BaseException {
5041 constructor ( name ?: string ) {
@@ -59,21 +50,15 @@ export class ConfigurationNotFoundException extends BaseException {
5950 }
6051}
6152
62- export class SchemaValidationException extends BaseException {
63- constructor ( errors : string [ ] ) {
64- super ( `Schema validation failed with the following errors:\n ${ errors . join ( '\n ' ) } ` ) ;
65- }
66- }
67-
6853// TODO: break this exception apart into more granular ones.
6954export class BuilderCannotBeResolvedException extends BaseException {
7055 constructor ( builder : string ) {
7156 super ( `Builder '${ builder } ' cannot be resolved.` ) ;
7257 }
7358}
7459
75- export class WorkspaceNotYetLoadedException extends BaseException {
76- constructor ( ) { super ( `Workspace needs to be loaded before Architect is used.` ) ; }
60+ export class ArchitectNotYetLoadedException extends BaseException {
61+ constructor ( ) { super ( `Architect needs to be loaded before Architect is used.` ) ; }
7762}
7863
7964export class BuilderNotFoundException extends BaseException {
@@ -82,155 +67,151 @@ export class BuilderNotFoundException extends BaseException {
8267 }
8368}
8469
85- export interface Target < OptionsT = { } > {
70+ export interface BuilderConfiguration < OptionsT = { } > {
8671 root : Path ;
8772 projectType : string ;
8873 builder : string ;
8974 options : OptionsT ;
9075}
9176
92- export interface TargetOptions < OptionsT = { } > {
93- project ? : string ;
94- target ? : string ;
77+ export interface TargetSpecifier < OptionsT = { } > {
78+ project : string ;
79+ target : string ;
9580 configuration ?: string ;
9681 overrides ?: Partial < OptionsT > ;
9782}
83+
84+ export interface TargetsMap {
85+ [ k : string ] : Target ;
86+ }
87+
88+ export declare type TargetOptions < T = JsonObject > = T ;
89+ export declare type TargetConfiguration < T = JsonObject > = Partial < T > ;
90+
91+ export interface Target < T = JsonObject > {
92+ builder : string ;
93+ options : TargetOptions < T > ;
94+ configurations ?: { [ k : string ] : TargetConfiguration < T > } ;
95+ }
96+
9897export class Architect {
99- private readonly _workspaceSchemaPath = join ( normalize ( __dirname ) , 'workspace -schema.json' ) ;
98+ private readonly _targetsSchemaPath = join ( normalize ( __dirname ) , 'targets -schema.json' ) ;
10099 private readonly _buildersSchemaPath = join ( normalize ( __dirname ) , 'builders-schema.json' ) ;
101- private _workspaceSchema : JsonObject ;
100+ private _targetsSchema : JsonObject ;
102101 private _buildersSchema : JsonObject ;
103102 private _architectSchemasLoaded = false ;
104103 private _builderPathsMap = new Map < string , BuilderPaths > ( ) ;
105104 private _builderDescriptionMap = new Map < string , BuilderDescription > ( ) ;
106105 private _builderConstructorMap = new Map < string , BuilderConstructor < { } > > ( ) ;
107- private _workspace : Workspace ;
108-
109- constructor ( private _root : Path , private _host : virtualFs . Host < { } > ) { }
110-
111- loadWorkspaceFromHost ( workspacePath : Path ) {
112- return this . _loadArchitectSchemas ( ) . pipe (
113- concatMap ( ( ) => this . _loadJsonFile ( join ( this . _root , workspacePath ) ) ) ,
114- concatMap ( json => this . loadWorkspaceFromJson ( json as { } as Workspace ) ) ,
115- ) ;
116- }
117-
118- loadWorkspaceFromJson ( json : Workspace ) {
119- return this . _loadArchitectSchemas ( ) . pipe (
120- concatMap ( ( ) => this . _validateAgainstSchema ( json , this . _workspaceSchema ) ) ,
121- concatMap ( ( validatedWorkspace : Workspace ) => {
122- this . _workspace = validatedWorkspace ;
123106
124- return of ( this ) ;
125- } ) ,
126- ) ;
127- }
107+ constructor ( private _workspace : experimental . workspace . Workspace ) { }
128108
129- private _loadArchitectSchemas ( ) {
109+ loadArchitect ( ) {
130110 if ( this . _architectSchemasLoaded ) {
131- return of ( null ) ;
111+ return of ( this ) ;
132112 } else {
133113 return forkJoin (
134- this . _loadJsonFile ( this . _workspaceSchemaPath ) ,
114+ this . _loadJsonFile ( this . _targetsSchemaPath ) ,
135115 this . _loadJsonFile ( this . _buildersSchemaPath ) ,
136116 ) . pipe (
137117 concatMap ( ( [ workspaceSchema , buildersSchema ] ) => {
138- this . _workspaceSchema = workspaceSchema ;
118+ this . _targetsSchema = workspaceSchema ;
139119 this . _buildersSchema = buildersSchema ;
120+ this . _architectSchemasLoaded = true ;
140121
141- return of ( null ) ;
122+ return of ( this ) ;
142123 } ) ,
143124 ) ;
144125 }
145126 }
146127
147- getTarget < OptionsT > ( options : TargetOptions = { } ) : Target < OptionsT > {
148- let { project, target : targetName } = options ;
149- const { configuration, overrides } = options ;
150-
151- if ( ! this . _workspace ) {
152- throw new WorkspaceNotYetLoadedException ( ) ;
153- }
154-
155- project = project || this . _workspace . defaultProject as string ;
156- const workspaceProject = this . _workspace . projects [ project ] ;
157-
158- if ( ! workspaceProject ) {
159- throw new ProjectNotFoundException ( project ) ;
160- }
161-
162- targetName = targetName || workspaceProject . defaultTarget as string ;
163- const workspaceTarget = workspaceProject . targets [ targetName ] ;
164-
165- if ( ! workspaceTarget ) {
166- throw new TargetNotFoundException ( targetName ) ;
167- }
128+ getBuilderConfiguration < OptionsT > (
129+ targetSpec : TargetSpecifier ,
130+ ) : Observable < BuilderConfiguration < OptionsT > > {
131+ const {
132+ project : projectName ,
133+ target : targetName ,
134+ configuration : configurationName ,
135+ overrides,
136+ } = targetSpec ;
137+
138+ const project = this . _workspace . getProject ( projectName ) ;
139+ const targets = this . _workspace . getProjectArchitect ( projectName ) ;
140+ let configuration : TargetConfiguration = { } ;
141+ let target : Target , options : TargetOptions ;
142+
143+ return this . _workspace . validateAgainstSchema < TargetsMap > ( targets , this . _targetsSchema ) . pipe (
144+ concatMap ( ( validatedWorkspaceTargets ) => {
145+ target = validatedWorkspaceTargets [ targetName ] ;
146+
147+ if ( ! target ) {
148+ return _throw ( new TargetNotFoundException ( targetName ) ) ;
149+ }
168150
169- const workspaceTargetOptions = workspaceTarget . options ;
170- let workspaceConfiguration ;
151+ options = target . options ;
171152
172- if ( configuration ) {
173- workspaceConfiguration = workspaceTarget . configurations
174- && workspaceTarget . configurations [ configuration ] ;
153+ if ( configurationName ) {
154+ if ( ! target . configurations ) {
155+ return _throw ( new ConfigurationNotFoundException ( configurationName ) ) ;
156+ }
175157
176- if ( ! workspaceConfiguration ) {
177- throw new ConfigurationNotFoundException ( configuration ) ;
178- }
179- }
158+ configuration = target . configurations [ configurationName ] ;
180159
181- // Resolve root for the target.
182- // TODO: add Path format to JSON schemas
183- const target : Target < OptionsT > = {
184- root : resolve ( this . _root , normalize ( workspaceProject . root ) ) ,
185- projectType : workspaceProject . projectType ,
186- builder : workspaceTarget . builder ,
187- options : {
188- ...workspaceTargetOptions ,
189- ...workspaceConfiguration ,
190- ...overrides as { } ,
191- } as OptionsT ,
192- } ;
160+ if ( ! configuration ) {
161+ return _throw ( new ConfigurationNotFoundException ( configurationName ) ) ;
162+ }
163+ }
193164
194- // Return a copy of the target object, JSON validation changes objects and we don't
195- // want the original properties to be modified.
196- return JSON . parse ( JSON . stringify ( target ) ) ;
165+ const builderConfiguration : BuilderConfiguration < OptionsT > = {
166+ root : resolve ( this . _workspace . root , normalize ( project . root ) ) ,
167+ projectType : project . projectType ,
168+ builder : target . builder ,
169+ options : {
170+ ...options ,
171+ ...configuration ,
172+ ...overrides as { } ,
173+ } as OptionsT ,
174+ } ;
175+
176+ return of ( builderConfiguration ) ;
177+ } ) ,
178+ ) ;
197179 }
198180
199- // Will run the target using the target.
200181 run < OptionsT > (
201- target : Target < OptionsT > ,
182+ builderConfig : BuilderConfiguration < OptionsT > ,
202183 partialContext : Partial < BuilderContext > = { } ,
203184 ) : Observable < BuildEvent > {
204185 const context : BuilderContext = {
205186 logger : new logging . NullLogger ( ) ,
206187 architect : this ,
207- host : this . _host ,
188+ host : this . _workspace . host ,
208189 ...partialContext ,
209190 } ;
210191
211192 let builderDescription : BuilderDescription ;
212193
213- return this . getBuilderDescription ( target ) . pipe (
214- concatMap ( description => {
215- builderDescription = description ;
216-
217- return this . validateBuilderOptions ( target , builderDescription ) ;
218- } ) ,
194+ return this . getBuilderDescription ( builderConfig ) . pipe (
195+ tap ( description => builderDescription = description ) ,
196+ concatMap ( ( ) => this . validateBuilderOptions ( builderConfig , builderDescription ) ) ,
197+ tap ( validatedBuilderConfig => builderConfig = validatedBuilderConfig ) ,
219198 map ( ( ) => this . getBuilder ( builderDescription , context ) ) ,
220- concatMap ( builder => builder . run ( target ) ) ,
199+ concatMap ( builder => builder . run ( builderConfig ) ) ,
221200 ) ;
222201 }
223202
224- getBuilderDescription < OptionsT > ( target : Target < OptionsT > ) : Observable < BuilderDescription > {
203+ getBuilderDescription < OptionsT > (
204+ builderConfig : BuilderConfiguration < OptionsT > ,
205+ ) : Observable < BuilderDescription > {
225206 // Check cache for this builder description.
226- if ( this . _builderDescriptionMap . has ( target . builder ) ) {
227- return of ( this . _builderDescriptionMap . get ( target . builder ) as BuilderDescription ) ;
207+ if ( this . _builderDescriptionMap . has ( builderConfig . builder ) ) {
208+ return of ( this . _builderDescriptionMap . get ( builderConfig . builder ) as BuilderDescription ) ;
228209 }
229210
230211 return new Observable ( ( obs ) => {
231212 // TODO: this probably needs to be more like NodeModulesEngineHost.
232- const basedir = getSystemPath ( this . _root ) ;
233- const [ pkg , builderName ] = target . builder . split ( ':' ) ;
213+ const basedir = getSystemPath ( this . _workspace . root ) ;
214+ const [ pkg , builderName ] = builderConfig . builder . split ( ':' ) ;
234215 const pkgJsonPath = nodeResolve ( pkg , { basedir, resolvePackageJson : true } ) ;
235216 let buildersJsonPath : Path ;
236217 let builderPaths : BuilderPaths ;
@@ -240,21 +221,21 @@ export class Architect {
240221 concatMap ( ( pkgJson : JsonObject ) => {
241222 const pkgJsonBuildersentry = pkgJson [ 'builders' ] as string ;
242223 if ( ! pkgJsonBuildersentry ) {
243- throw new BuilderCannotBeResolvedException ( target . builder ) ;
224+ return _throw ( new BuilderCannotBeResolvedException ( builderConfig . builder ) ) ;
244225 }
245226
246227 buildersJsonPath = join ( dirname ( normalize ( pkgJsonPath ) ) , pkgJsonBuildersentry ) ;
247228
248229 return this . _loadJsonFile ( buildersJsonPath ) ;
249230 } ) ,
250231 // Validate builders json.
251- concatMap ( ( builderPathsMap ) =>
252- this . _validateAgainstSchema < BuilderPathsMap > ( builderPathsMap , this . _buildersSchema ) ) ,
232+ concatMap ( ( builderPathsMap ) => this . _workspace . validateAgainstSchema < BuilderPathsMap > (
233+ builderPathsMap , this . _buildersSchema ) ) ,
253234 concatMap ( ( builderPathsMap ) => {
254235 builderPaths = builderPathsMap . builders [ builderName ] ;
255236
256237 if ( ! builderPaths ) {
257- throw new BuilderCannotBeResolvedException ( target . builder ) ;
238+ return _throw ( new BuilderCannotBeResolvedException ( builderConfig . builder ) ) ;
258239 }
259240
260241 // Resolve paths in the builder paths.
@@ -263,14 +244,14 @@ export class Architect {
263244 builderPaths . class = join ( builderJsonDir , builderPaths . class ) ;
264245
265246 // Save the builder paths so that we can lazily load the builder.
266- this . _builderPathsMap . set ( target . builder , builderPaths ) ;
247+ this . _builderPathsMap . set ( builderConfig . builder , builderPaths ) ;
267248
268249 // Load the schema.
269250 return this . _loadJsonFile ( builderPaths . schema ) ;
270251 } ) ,
271252 map ( builderSchema => {
272253 const builderDescription = {
273- name : target . builder ,
254+ name : builderConfig . builder ,
274255 schema : builderSchema ,
275256 description : builderPaths . description ,
276257 } ;
@@ -285,9 +266,17 @@ export class Architect {
285266 }
286267
287268 validateBuilderOptions < OptionsT > (
288- target : Target < OptionsT > , builderDescription : BuilderDescription ,
289- ) : Observable < OptionsT > {
290- return this . _validateAgainstSchema < OptionsT > ( target . options , builderDescription . schema ) ;
269+ builderConfig : BuilderConfiguration < OptionsT > , builderDescription : BuilderDescription ,
270+ ) : Observable < BuilderConfiguration < OptionsT > > {
271+ return this . _workspace . validateAgainstSchema < OptionsT > (
272+ builderConfig . options , builderDescription . schema ,
273+ ) . pipe (
274+ map ( validatedOptions => {
275+ builderConfig . options = validatedOptions ;
276+
277+ return builderConfig ;
278+ } ) ,
279+ ) ;
291280 }
292281
293282 getBuilder < OptionsT > (
@@ -319,25 +308,8 @@ export class Architect {
319308 return builder ;
320309 }
321310
322- // Warning: this method changes contentJson in place.
323- // TODO: add transforms to resolve paths.
324- private _validateAgainstSchema < T = { } > ( contentJson : { } , schemaJson : JsonObject ) : Observable < T > {
325- const registry = new schema . CoreSchemaRegistry ( ) ;
326-
327- return registry . compile ( schemaJson ) . pipe (
328- concatMap ( validator => validator ( contentJson ) ) ,
329- concatMap ( validatorResult => {
330- if ( validatorResult . success ) {
331- return of ( contentJson as T ) ;
332- } else {
333- return _throw ( new SchemaValidationException ( validatorResult . errors as string [ ] ) ) ;
334- }
335- } ) ,
336- ) ;
337- }
338-
339311 private _loadJsonFile ( path : Path ) : Observable < JsonObject > {
340- return this . _host . read ( normalize ( path ) ) . pipe (
312+ return this . _workspace . host . read ( normalize ( path ) ) . pipe (
341313 map ( buffer => virtualFs . fileBufferToString ( buffer ) ) ,
342314 map ( str => parseJson ( str , JsonParseMode . Loose ) as { } as JsonObject ) ,
343315 ) ;
0 commit comments