@@ -3,7 +3,7 @@ import { basename, dirname, join, relative, sep } from 'path';
3
3
4
4
import { sync } from 'glob' ;
5
5
6
- import { CdsImport , PackageJson } from './types' ;
6
+ import { CdsFilesToCompile , CdsImport , PackageJson } from './types' ;
7
7
import { cdsExtractorLog } from '../../logging' ;
8
8
9
9
/**
@@ -390,13 +390,19 @@ export function readPackageJsonFile(filePath: string): PackageJson | undefined {
390
390
}
391
391
392
392
/**
393
- * Determines which CDS files in a project should be compiled to JSON.
394
- * For CAP projects with typical directory structure (db/, srv/), we should use project-aware compilation.
395
- * For other projects, we fall back to the previous approach of identifying root files.
393
+ * Determines which CDS files should be compiled for a given project and what output files to expect.
394
+ * This function analyzes the project structure and dependencies to decide
395
+ * whether to use project-level compilation or individual file compilation.
396
+ *
397
+ * For CAP projects (identified by either having @sap/cds dependencies or
398
+ * typical CAP directory structure), it returns a special marker indicating
399
+ * project-level compilation should be used. For other projects, it attempts
400
+ * to identify root files (files that are not imported by others) and returns
401
+ * those for individual compilation.
396
402
*
397
403
* @param sourceRootDir - The source root directory
398
- * @param project - The CDS project to analyze
399
- * @returns Array of CDS file paths (relative to source root) that should be compiled
404
+ * @param project - The project to analyze, containing cdsFiles, imports, and projectDir
405
+ * @returns Object containing files to compile and expected output files
400
406
*/
401
407
export function determineCdsFilesToCompile (
402
408
sourceRootDir : string ,
@@ -405,14 +411,21 @@ export function determineCdsFilesToCompile(
405
411
imports ?: Map < string , CdsImport [ ] > ;
406
412
projectDir : string ;
407
413
} ,
408
- ) : string [ ] {
414
+ ) : CdsFilesToCompile {
409
415
if ( ! project . cdsFiles || project . cdsFiles . length === 0 ) {
410
- return [ ] ;
416
+ return {
417
+ filesToCompile : [ ] ,
418
+ expectedOutputFiles : [ ] ,
419
+ } ;
411
420
}
412
421
413
422
// If there's only one CDS file, it should be compiled individually.
414
423
if ( project . cdsFiles . length === 1 ) {
415
- return [ ...project . cdsFiles ] ;
424
+ const filesToCompile = [ ...project . cdsFiles ] ;
425
+ return {
426
+ filesToCompile,
427
+ expectedOutputFiles : computeExpectedOutputFiles ( filesToCompile , project . projectDir ) ,
428
+ } ;
416
429
}
417
430
418
431
const absoluteProjectDir = join ( sourceRootDir , project . projectDir ) ;
@@ -425,13 +438,21 @@ export function determineCdsFilesToCompile(
425
438
if ( project . cdsFiles . length > 1 && ( hasCapStructure || hasCapDeps ) ) {
426
439
// For CAP projects, we should use project-level compilation
427
440
// Return a special marker that indicates the entire project should be compiled together
428
- return [ '__PROJECT_LEVEL_COMPILATION__' ] ;
441
+ const filesToCompile = [ '__PROJECT_LEVEL_COMPILATION__' ] ;
442
+ return {
443
+ filesToCompile,
444
+ expectedOutputFiles : computeExpectedOutputFiles ( filesToCompile , project . projectDir ) ,
445
+ } ;
429
446
}
430
447
431
448
// For non-CAP projects or when we can't determine project type,
432
449
// fall back to the original logic of identifying root files
433
450
if ( ! project . imports || project . imports . size === 0 ) {
434
- return [ ...project . cdsFiles ] ;
451
+ const filesToCompile = [ ...project . cdsFiles ] ;
452
+ return {
453
+ filesToCompile,
454
+ expectedOutputFiles : computeExpectedOutputFiles ( filesToCompile , project . projectDir ) ,
455
+ } ;
435
456
}
436
457
437
458
try {
@@ -475,18 +496,67 @@ export function determineCdsFilesToCompile(
475
496
'warn' ,
476
497
`No root CDS files identified in project ${ project . projectDir } , will compile all files` ,
477
498
) ;
478
- return [ ...project . cdsFiles ] ;
499
+ const filesToCompile = [ ...project . cdsFiles ] ;
500
+ return {
501
+ filesToCompile,
502
+ expectedOutputFiles : computeExpectedOutputFiles ( filesToCompile , project . projectDir ) ,
503
+ } ;
479
504
}
480
505
481
- return rootFiles ;
506
+ return {
507
+ filesToCompile : rootFiles ,
508
+ expectedOutputFiles : computeExpectedOutputFiles ( rootFiles , project . projectDir ) ,
509
+ } ;
482
510
} catch ( error ) {
483
511
cdsExtractorLog (
484
512
'warn' ,
485
513
`Error determining files to compile for project ${ project . projectDir } : ${ String ( error ) } ` ,
486
514
) ;
487
515
// Fall back to compiling all files on error
488
- return [ ...project . cdsFiles ] ;
516
+ const filesToCompile = [ ...project . cdsFiles ] ;
517
+ return {
518
+ filesToCompile,
519
+ expectedOutputFiles : computeExpectedOutputFiles ( filesToCompile , project . projectDir ) ,
520
+ } ;
521
+ }
522
+ }
523
+
524
+ /**
525
+ * Computes the expected output files for a given set of files to compile.
526
+ * This function predicts what .cds.json files will be generated during compilation.
527
+ *
528
+ * @param filesToCompile - Array of files to compile (may include special markers)
529
+ * @param projectDir - The project directory
530
+ * @returns Array of expected output file paths (relative to source root)
531
+ */
532
+ function computeExpectedOutputFiles ( filesToCompile : string [ ] , projectDir : string ) : string [ ] {
533
+ const expectedFiles : string [ ] = [ ] ;
534
+
535
+ // Check if this project uses project-level compilation.
536
+ const usesProjectLevelCompilation = filesToCompile . includes ( '__PROJECT_LEVEL_COMPILATION__' ) ;
537
+
538
+ // Validate that __PROJECT_LEVEL_COMPILATION__ element does not coexist with other
539
+ // files. We either expect a single project-level compilation marker or a list of
540
+ // individual files to compile, not both.
541
+ if ( usesProjectLevelCompilation && filesToCompile . length !== 1 ) {
542
+ throw new Error (
543
+ `Invalid compilation configuration: '__PROJECT_LEVEL_COMPILATION__' must be the only element in filesToCompile array, but found ${ filesToCompile . length } elements: ${ filesToCompile . join ( ', ' ) } ` ,
544
+ ) ;
489
545
}
546
+
547
+ if ( usesProjectLevelCompilation ) {
548
+ // For project-level compilation, expect a single model.cds.json file in the project
549
+ // root directory.
550
+ const projectModelFile = join ( projectDir , 'model.cds.json' ) ;
551
+ expectedFiles . push ( projectModelFile ) ;
552
+ } else {
553
+ // For individual file compilation, expect a .cds.json file for each .cds file compiled.
554
+ for ( const cdsFile of filesToCompile ) {
555
+ expectedFiles . push ( `${ cdsFile } .json` ) ;
556
+ }
557
+ }
558
+
559
+ return expectedFiles ;
490
560
}
491
561
492
562
/**
@@ -497,27 +567,33 @@ export function determineCdsFilesToCompile(
497
567
* @returns Array of expected output file paths (relative to source root)
498
568
*/
499
569
export function determineExpectedOutputFiles ( project : {
500
- cdsFiles : string [ ] ;
501
570
cdsFilesToCompile : string [ ] ;
502
571
projectDir : string ;
503
572
} ) : string [ ] {
504
573
const expectedFiles : string [ ] = [ ] ;
505
574
506
- // Check if this project uses project-level compilation
575
+ // Check if this project uses project-level compilation.
507
576
const usesProjectLevelCompilation = project . cdsFilesToCompile . includes (
508
577
'__PROJECT_LEVEL_COMPILATION__' ,
509
578
) ;
579
+ // Validate that __PROJECT_LEVEL_COMPILATION__ element does not coexist with other
580
+ // files. We either expect a single project-level compilation marker or a list of
581
+ // individual files to compile, not both.
582
+ if ( usesProjectLevelCompilation && project . cdsFilesToCompile . length !== 1 ) {
583
+ throw new Error (
584
+ `Invalid compilation configuration: '__PROJECT_LEVEL_COMPILATION__' must be the only element in cdsFilesToCompile array, but found ${ project . cdsFilesToCompile . length } elements: ${ project . cdsFilesToCompile . join ( ', ' ) } ` ,
585
+ ) ;
586
+ }
510
587
511
588
if ( usesProjectLevelCompilation ) {
512
- // For project-level compilation, expect a single model.cds.json file in the project root
589
+ // For project-level compilation, expect a single model.cds.json file in the project
590
+ // root directory.
513
591
const projectModelFile = join ( project . projectDir , 'model.cds.json' ) ;
514
592
expectedFiles . push ( projectModelFile ) ;
515
593
} else {
516
- // For individual file compilation, expect a .cds.json file for each file to compile
594
+ // For individual file compilation, expect a .cds.json file for each .cds file compiled.
517
595
for ( const cdsFile of project . cdsFilesToCompile ) {
518
- if ( cdsFile !== '__PROJECT_LEVEL_COMPILATION__' ) {
519
- expectedFiles . push ( `${ cdsFile } .json` ) ;
520
- }
596
+ expectedFiles . push ( `${ cdsFile } .json` ) ;
521
597
}
522
598
}
523
599
0 commit comments