@@ -3,7 +3,7 @@ import { basename, dirname, join, relative, sep } from 'path';
33
44import { sync } from 'glob' ;
55
6- import { CdsImport , PackageJson } from './types' ;
6+ import { CdsFilesToCompile , CdsImport , PackageJson } from './types' ;
77import { cdsExtractorLog } from '../../logging' ;
88
99/**
@@ -390,13 +390,19 @@ export function readPackageJsonFile(filePath: string): PackageJson | undefined {
390390}
391391
392392/**
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.
396402 *
397403 * @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
400406 */
401407export function determineCdsFilesToCompile (
402408 sourceRootDir : string ,
@@ -405,14 +411,21 @@ export function determineCdsFilesToCompile(
405411 imports ?: Map < string , CdsImport [ ] > ;
406412 projectDir : string ;
407413 } ,
408- ) : string [ ] {
414+ ) : CdsFilesToCompile {
409415 if ( ! project . cdsFiles || project . cdsFiles . length === 0 ) {
410- return [ ] ;
416+ return {
417+ filesToCompile : [ ] ,
418+ expectedOutputFiles : [ ] ,
419+ } ;
411420 }
412421
413422 // If there's only one CDS file, it should be compiled individually.
414423 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+ } ;
416429 }
417430
418431 const absoluteProjectDir = join ( sourceRootDir , project . projectDir ) ;
@@ -425,13 +438,21 @@ export function determineCdsFilesToCompile(
425438 if ( project . cdsFiles . length > 1 && ( hasCapStructure || hasCapDeps ) ) {
426439 // For CAP projects, we should use project-level compilation
427440 // 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+ } ;
429446 }
430447
431448 // For non-CAP projects or when we can't determine project type,
432449 // fall back to the original logic of identifying root files
433450 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+ } ;
435456 }
436457
437458 try {
@@ -475,18 +496,67 @@ export function determineCdsFilesToCompile(
475496 'warn' ,
476497 `No root CDS files identified in project ${ project . projectDir } , will compile all files` ,
477498 ) ;
478- return [ ...project . cdsFiles ] ;
499+ const filesToCompile = [ ...project . cdsFiles ] ;
500+ return {
501+ filesToCompile,
502+ expectedOutputFiles : computeExpectedOutputFiles ( filesToCompile , project . projectDir ) ,
503+ } ;
479504 }
480505
481- return rootFiles ;
506+ return {
507+ filesToCompile : rootFiles ,
508+ expectedOutputFiles : computeExpectedOutputFiles ( rootFiles , project . projectDir ) ,
509+ } ;
482510 } catch ( error ) {
483511 cdsExtractorLog (
484512 'warn' ,
485513 `Error determining files to compile for project ${ project . projectDir } : ${ String ( error ) } ` ,
486514 ) ;
487515 // 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+ ) ;
489545 }
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 ;
490560}
491561
492562/**
@@ -497,27 +567,33 @@ export function determineCdsFilesToCompile(
497567 * @returns Array of expected output file paths (relative to source root)
498568 */
499569export function determineExpectedOutputFiles ( project : {
500- cdsFiles : string [ ] ;
501570 cdsFilesToCompile : string [ ] ;
502571 projectDir : string ;
503572} ) : string [ ] {
504573 const expectedFiles : string [ ] = [ ] ;
505574
506- // Check if this project uses project-level compilation
575+ // Check if this project uses project-level compilation.
507576 const usesProjectLevelCompilation = project . cdsFilesToCompile . includes (
508577 '__PROJECT_LEVEL_COMPILATION__' ,
509578 ) ;
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+ }
510587
511588 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.
513591 const projectModelFile = join ( project . projectDir , 'model.cds.json' ) ;
514592 expectedFiles . push ( projectModelFile ) ;
515593 } 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.
517595 for ( const cdsFile of project . cdsFilesToCompile ) {
518- if ( cdsFile !== '__PROJECT_LEVEL_COMPILATION__' ) {
519- expectedFiles . push ( `${ cdsFile } .json` ) ;
520- }
596+ expectedFiles . push ( `${ cdsFile } .json` ) ;
521597 }
522598 }
523599
0 commit comments