@@ -3,8 +3,9 @@ const fs = require("graceful-fs");
3
3
const path = require ( "path" ) ;
4
4
const { promisify} = require ( "util" ) ;
5
5
const readFile = promisify ( fs . readFile ) ;
6
- const parseYaml = require ( "js-yaml" ) . safeLoadAll ;
6
+ const jsyaml = require ( "js-yaml" ) ;
7
7
const typeRepository = require ( "@ui5/builder" ) . types . typeRepository ;
8
+ const { validate} = require ( "./validation/validator" ) ;
8
9
9
10
class ProjectPreprocessor {
10
11
constructor ( { tree} ) {
@@ -80,7 +81,7 @@ class ProjectPreprocessor {
80
81
return this . applyExtension ( extProject ) ;
81
82
} ) ) ;
82
83
}
83
- this . applyShims ( project ) ;
84
+ await this . applyShims ( project ) ;
84
85
if ( this . isConfigValid ( project ) ) {
85
86
// Do not apply transparent projects.
86
87
// Their only purpose might be to have their dependencies processed
@@ -194,26 +195,19 @@ class ProjectPreprocessor {
194
195
async loadProjectConfiguration ( project ) {
195
196
if ( project . specVersion ) { // Project might already be configured
196
197
// Currently, specVersion is the indicator for configured projects
197
- this . normalizeConfig ( project ) ;
198
- return { } ;
199
- }
200
198
201
- let configs ;
199
+ if ( project . _transparentProject ) {
200
+ // Assume that project is already processed
201
+ return { } ;
202
+ }
202
203
203
- // A projects configPath property takes precedence over the default "<projectPath>/ui5.yaml" path
204
- const configPath = project . configPath || path . join ( project . path , "/ui5.yaml" ) ;
205
- try {
206
- configs = await this . readConfigFile ( configPath ) ;
207
- } catch ( err ) {
208
- const errorText = "Failed to read configuration for project " +
209
- `${ project . id } at "${ configPath } ". Error: ${ err . message } ` ;
204
+ await this . validateAndNormalizeExistingProject ( project ) ;
210
205
211
- if ( err . code !== "ENOENT" ) { // Something else than "File or directory does not exist"
212
- throw new Error ( errorText ) ;
213
- }
214
- log . verbose ( errorText ) ;
206
+ return { } ;
215
207
}
216
208
209
+ const configs = await this . readConfigFile ( project ) ;
210
+
217
211
if ( ! configs || ! configs . length ) {
218
212
return { } ;
219
213
}
@@ -384,11 +378,77 @@ class ProjectPreprocessor {
384
378
}
385
379
}
386
380
387
- async readConfigFile ( configPath ) {
388
- const configFile = await readFile ( configPath ) ;
389
- return parseYaml ( configFile , {
390
- filename : path
391
- } ) ;
381
+ async readConfigFile ( project ) {
382
+ // A projects configPath property takes precedence over the default "<projectPath>/ui5.yaml" path
383
+ const configPath = project . configPath || path . join ( project . path , "ui5.yaml" ) ;
384
+ let configFile ;
385
+ try {
386
+ configFile = await readFile ( configPath , { encoding : "utf8" } ) ;
387
+ } catch ( err ) {
388
+ const errorText = "Failed to read configuration for project " +
389
+ `${ project . id } at "${ configPath } ". Error: ${ err . message } ` ;
390
+
391
+ // Something else than "File or directory does not exist" or root project
392
+ if ( err . code !== "ENOENT" || project . _level === 0 ) {
393
+ throw new Error ( errorText ) ;
394
+ } else {
395
+ log . verbose ( errorText ) ;
396
+ return null ;
397
+ }
398
+ }
399
+
400
+ let configs ;
401
+
402
+ try {
403
+ // Using loadAll with DEFAULT_SAFE_SCHEMA instead of safeLoadAll to pass "filename".
404
+ // safeLoadAll doesn't handle its parameters properly.
405
+ // See https://github.com/nodeca/js-yaml/issues/456 and https://github.com/nodeca/js-yaml/pull/381
406
+ configs = jsyaml . loadAll ( configFile , undefined , {
407
+ filename : configPath ,
408
+ schema : jsyaml . DEFAULT_SAFE_SCHEMA
409
+ } ) ;
410
+ } catch ( err ) {
411
+ if ( err . name === "YAMLException" ) {
412
+ throw new Error ( "Failed to parse configuration for project " +
413
+ `${ project . id } at "${ configPath } "\nError: ${ err . message } ` ) ;
414
+ } else {
415
+ throw err ;
416
+ }
417
+ }
418
+
419
+ if ( ! configs || ! configs . length ) {
420
+ return configs ;
421
+ }
422
+
423
+ const validationResults = await Promise . all (
424
+ configs . map ( async ( config , documentIndex ) => {
425
+ // Catch validation errors to ensure proper order of rejections within Promise.all
426
+ try {
427
+ await validate ( {
428
+ config,
429
+ project : {
430
+ id : project . id
431
+ } ,
432
+ yaml : {
433
+ path : configPath ,
434
+ source : configFile ,
435
+ documentIndex
436
+ }
437
+ } ) ;
438
+ } catch ( error ) {
439
+ return error ;
440
+ }
441
+ } )
442
+ ) ;
443
+
444
+ const validationErrors = validationResults . filter ( ( $ ) => $ ) ;
445
+
446
+ if ( validationErrors . length > 0 ) {
447
+ // For now just throw the error of the first invalid document
448
+ throw validationErrors [ 0 ] ;
449
+ }
450
+
451
+ return configs ;
392
452
}
393
453
394
454
handleShim ( extension ) {
@@ -451,7 +511,7 @@ class ProjectPreprocessor {
451
511
}
452
512
}
453
513
454
- applyShims ( project ) {
514
+ async applyShims ( project ) {
455
515
const configShim = this . configShims [ project . id ] ;
456
516
// Apply configuration shims
457
517
if ( configShim ) {
@@ -482,6 +542,8 @@ class ProjectPreprocessor {
482
542
483
543
Object . assign ( project , configShim ) ;
484
544
delete project . shimDependenciesResolved ; // Remove shim processing metadata from project
545
+
546
+ await this . validateAndNormalizeExistingProject ( project ) ;
485
547
}
486
548
487
549
// Apply collections
@@ -539,6 +601,31 @@ class ProjectPreprocessor {
539
601
const middlewarePath = path . join ( extension . path , extension . middleware . path ) ;
540
602
middlewareRepository . addMiddleware ( extension . metadata . name , middlewarePath ) ;
541
603
}
604
+
605
+ async validateAndNormalizeExistingProject ( project ) {
606
+ // Validate project config, but exclude additional properties
607
+ const excludedProperties = [
608
+ "id" ,
609
+ "version" ,
610
+ "path" ,
611
+ "dependencies" ,
612
+ "_level"
613
+ ] ;
614
+ const config = { } ;
615
+ for ( const key in project ) {
616
+ if ( project . hasOwnProperty ( key ) && ! excludedProperties . includes ( key ) ) {
617
+ config [ key ] = project [ key ] ;
618
+ }
619
+ }
620
+ await validate ( {
621
+ config,
622
+ project : {
623
+ id : project . id
624
+ }
625
+ } ) ;
626
+
627
+ this . normalizeConfig ( project ) ;
628
+ }
542
629
}
543
630
544
631
/**
0 commit comments