Skip to content

Commit b3520de

Browse files
committed
CDS extractor special case for index.cds
Implements special handling in the CDS extractor for the use case where an `index.cds` file exists in the base directory of a given CAP project, in which case we just compile the `index.cds` file instead of all the subdirectories of the project.
1 parent ce36be3 commit b3520de

File tree

4 files changed

+181
-9
lines changed

4 files changed

+181
-9
lines changed

extractors/cds/tools/dist/cds-extractor.bundle.js

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

extractors/cds/tools/dist/cds-extractor.bundle.js.map

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

extractors/cds/tools/src/cds/compiler/compile.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,16 @@ function parseCommandForSpawn(commandString: string): { executable: string; base
2828
function determineCompilationTargets(project: BasicCdsProject, sourceRoot: string): string[] {
2929
const projectAbsolutePath = join(sourceRoot, project.projectDir);
3030

31+
// Check for index.cds in the project root first, which takes precedence over CAP directories.
32+
const rootCdsFiles = project.cdsFiles
33+
.filter(file => dirname(join(sourceRoot, file)) === projectAbsolutePath)
34+
.map(file => basename(file));
35+
36+
if (rootCdsFiles.includes('index.cds')) {
37+
// Use only index.cds when it exists in the project root
38+
return ['index.cds'];
39+
}
40+
3141
// Check for standard CAP directories
3242
const capDirectories = ['db', 'srv', 'app'];
3343
const existingCapDirs = capDirectories.filter(dir => dirExists(join(projectAbsolutePath, dir)));
@@ -37,13 +47,8 @@ function determineCompilationTargets(project: BasicCdsProject, sourceRoot: strin
3747
return existingCapDirs;
3848
}
3949

40-
// Check for root-level CDS files
41-
const rootCdsFiles = project.cdsFiles
42-
.filter(file => dirname(join(sourceRoot, file)) === projectAbsolutePath)
43-
.map(file => basename(file));
44-
4550
if (rootCdsFiles.length > 0) {
46-
// Use root-level files
51+
// Use other root-level files
4752
return rootCdsFiles;
4853
}
4954

extractors/cds/tools/test/src/cds/compiler/compile.test.ts

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -937,5 +937,169 @@ describe('compile .cds to .cds.json', () => {
937937
}),
938938
);
939939
});
940+
941+
it('should use index.cds as single compilation target when index.cds exists in project root', () => {
942+
// Setup
943+
const sourceRoot = '/source/root';
944+
const projectDir = 'bookshop';
945+
const expectedOutputPath = `${sourceRoot}/${projectDir}/model.cds.json`;
946+
947+
// Create project dependency map with index.cds and CAP directories
948+
const projectMap = new Map();
949+
const projectInfo: BasicCdsProject = {
950+
projectDir,
951+
cdsFiles: [
952+
'bookshop/index.cds', // index.cds in root
953+
'bookshop/db/schema.cds', // CAP directory files
954+
'bookshop/srv/service.cds', // CAP directory files
955+
'bookshop/app/app.cds', // CAP directory files
956+
],
957+
compilationTargets: ['index.cds'], // Should only contain index.cds
958+
expectedOutputFile: 'model.cds.json',
959+
};
960+
projectMap.set(projectDir, projectInfo);
961+
962+
// Mock filesystem checks - index.cds exists and CAP directories exist
963+
(filesystem.fileExists as jest.Mock).mockImplementation(path => {
964+
if (path.includes('index.cds')) return true;
965+
if (path.includes('/resolved/')) return true; // Source file exists
966+
if (path === expectedOutputPath) return true; // Output file exists
967+
return false;
968+
});
969+
970+
// Mock directory existence for CAP directories
971+
(filesystem.dirExists as jest.Mock).mockImplementation(path => {
972+
// CAP directories exist but index.cds should take precedence
973+
if (path.includes('/db') || path.includes('/srv') || path.includes('/app')) return true;
974+
return false;
975+
});
976+
977+
// Mock successful spawn process
978+
(childProcess.spawnSync as jest.Mock).mockReturnValue({
979+
status: 0,
980+
stdout: Buffer.from('Compilation successful'),
981+
stderr: Buffer.from(''),
982+
});
983+
984+
// Execute
985+
const result = compileCdsToJson(
986+
'index.cds',
987+
sourceRoot,
988+
'cds',
989+
undefined,
990+
projectMap,
991+
projectDir,
992+
);
993+
994+
// Verify
995+
expect(result.success).toBe(true);
996+
expect(result.outputPath).toBe(expectedOutputPath);
997+
expect(result.compiledAsProject).toBe(true);
998+
999+
// Verify that only index.cds is passed to the compiler, not the CAP directories
1000+
expect(childProcess.spawnSync).toHaveBeenCalledWith(
1001+
'cds',
1002+
expect.arrayContaining([
1003+
'compile',
1004+
'index.cds', // Only index.cds should be compiled
1005+
'--to',
1006+
'json',
1007+
'--dest',
1008+
'model.cds.json',
1009+
'--locations',
1010+
'--log-level',
1011+
'warn',
1012+
]),
1013+
expect.any(Object),
1014+
);
1015+
1016+
// Ensure the args array does not contain CAP directories
1017+
const spawnCall = (childProcess.spawnSync as jest.Mock).mock.calls[0];
1018+
const args = spawnCall[1];
1019+
expect(args).not.toContain('db');
1020+
expect(args).not.toContain('srv');
1021+
expect(args).not.toContain('app');
1022+
});
1023+
1024+
it('should use CAP directories when no index.cds exists in project root', () => {
1025+
// Setup
1026+
const sourceRoot = '/source/root';
1027+
const projectDir = 'standard-cap';
1028+
const expectedOutputPath = `${sourceRoot}/${projectDir}/model.cds.json`;
1029+
1030+
// Create project dependency map without index.cds but with CAP directories
1031+
const projectMap = new Map();
1032+
const projectInfo: BasicCdsProject = {
1033+
projectDir,
1034+
cdsFiles: [
1035+
'standard-cap/db/schema.cds', // CAP directory files
1036+
'standard-cap/srv/service.cds', // CAP directory files
1037+
'standard-cap/app/app.cds', // CAP directory files
1038+
],
1039+
compilationTargets: ['db', 'srv', 'app'], // Should contain CAP directories
1040+
expectedOutputFile: 'model.cds.json',
1041+
};
1042+
projectMap.set(projectDir, projectInfo);
1043+
1044+
// Mock filesystem checks - no index.cds but CAP directories exist
1045+
(filesystem.fileExists as jest.Mock).mockImplementation(path => {
1046+
if (path.includes('/resolved/')) return true; // Source file exists
1047+
if (path === expectedOutputPath) return true; // Output file exists
1048+
return false;
1049+
});
1050+
1051+
// Mock directory existence for CAP directories
1052+
(filesystem.dirExists as jest.Mock).mockImplementation(path => {
1053+
// CAP directories exist
1054+
if (path.includes('/db') || path.includes('/srv') || path.includes('/app')) return true;
1055+
return false;
1056+
});
1057+
1058+
// Mock successful spawn process
1059+
(childProcess.spawnSync as jest.Mock).mockReturnValue({
1060+
status: 0,
1061+
stdout: Buffer.from('Compilation successful'),
1062+
stderr: Buffer.from(''),
1063+
});
1064+
1065+
// Execute
1066+
const result = compileCdsToJson(
1067+
'schema.cds',
1068+
sourceRoot,
1069+
'cds',
1070+
undefined,
1071+
projectMap,
1072+
projectDir,
1073+
);
1074+
1075+
// Verify
1076+
expect(result.success).toBe(true);
1077+
expect(result.outputPath).toBe(expectedOutputPath);
1078+
expect(result.compiledAsProject).toBe(true);
1079+
1080+
// Verify that CAP directories are passed to the compiler
1081+
expect(childProcess.spawnSync).toHaveBeenCalledWith(
1082+
'cds',
1083+
expect.arrayContaining([
1084+
'compile',
1085+
'db', // CAP directories should be used when no index.cds
1086+
'srv',
1087+
'app',
1088+
'--to',
1089+
'json',
1090+
'--dest',
1091+
'model.cds.json',
1092+
'--locations',
1093+
'--log-level',
1094+
'warn',
1095+
]),
1096+
expect.any(Object),
1097+
);
1098+
1099+
// Ensure index.cds is not in the args
1100+
const spawnCall = (childProcess.spawnSync as jest.Mock).mock.calls[0];
1101+
const args = spawnCall[1];
1102+
expect(args).not.toContain('index.cds');
1103+
});
9401104
});
9411105
});

0 commit comments

Comments
 (0)