Skip to content

Commit c865d94

Browse files
committed
First attempt at project-aware CDS compilation
Initial attempt to use the `cds compile` CLI command in a way that allows for de-duplication of individual `.cds` files that are already included by another `.cds` file in the project.
1 parent 09fa955 commit c865d94

File tree

6 files changed

+331
-36
lines changed

6 files changed

+331
-36
lines changed

extractors/cds/tools/cds-extractor.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,15 @@ for (const rawCdsFilePath of cdsFilePathsToProcess) {
111111
const cdsCommand = determineCdsCommand(cacheDir);
112112

113113
// Use resolved path directly instead of passing through getArg
114-
const compilationResult = compileCdsToJson(rawCdsFilePath, sourceRoot, cdsCommand, cacheDir);
114+
// Pass the project dependency information to enable project-aware compilation
115+
const compilationResult = compileCdsToJson(
116+
rawCdsFilePath,
117+
sourceRoot,
118+
cdsCommand,
119+
cacheDir,
120+
projectMap,
121+
projectDir,
122+
);
115123

116124
if (!compilationResult.success && compilationResult.message) {
117125
console.error(

extractors/cds/tools/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"clean": "rm -rf out coverage",
1010
"prebuild": "npm run clean",
1111
"lint": "eslint --ext .ts src/",
12-
"lint:fix": "eslint --ext .ts --fix src/",
12+
"lint:fix": "eslint --ext .ts --fix cds-extractor.ts src/",
1313
"format": "prettier --write 'src/**/*.ts'",
1414
"test": "jest",
1515
"test:watch": "jest --watch",

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

Lines changed: 101 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { execFileSync, spawnSync, SpawnSyncOptions } from 'child_process';
2-
import { resolve, join, delimiter } from 'path';
2+
import { resolve, join, delimiter, relative } from 'path';
33

44
import { CdsCompilationResult } from './types';
55
import { fileExists, dirExists, recursivelyRenameJsonFiles } from '../../filesystem';
@@ -87,13 +87,24 @@ export function getCdsVersion(cdsCommand: string, cacheDir?: string): string | u
8787
* @param sourceRoot The source root directory
8888
* @param cdsCommand The CDS command to use
8989
* @param cacheDir Optional path to a directory containing installed dependencies
90+
* @param projectMap Optional map of project directories to project objects with dependency information
91+
* @param projectDir Optional project directory this CDS file belongs to
9092
* @returns Result of the compilation
9193
*/
9294
export function compileCdsToJson(
9395
cdsFilePath: string,
9496
sourceRoot: string,
9597
cdsCommand: string,
9698
cacheDir?: string,
99+
projectMap?: Map<
100+
string,
101+
{
102+
projectDir: string;
103+
cdsFiles: string[];
104+
imports?: Map<string, Array<{ resolvedPath?: string }>>;
105+
}
106+
>,
107+
projectDir?: string,
97108
): CdsCompilationResult {
98109
try {
99110
const resolvedCdsFilePath = resolve(cdsFilePath);
@@ -106,7 +117,6 @@ export function compileCdsToJson(
106117
// Get and log the CDS version
107118
const cdsVersion = getCdsVersion(cdsCommand, cacheDir);
108119
const versionInfo = cdsVersion ? `with CDS v${cdsVersion}` : '';
109-
console.log(`Processing CDS file ${resolvedCdsFilePath} ${versionInfo}...`);
110120

111121
// Prepare spawn options
112122
const spawnOptions: SpawnSyncOptions = {
@@ -128,25 +138,93 @@ export function compileCdsToJson(
128138
// Add NPM configuration to ensure dependencies are resolved from the cache directory
129139
npm_config_prefix: cacheDir,
130140
};
141+
}
142+
143+
// Determine if we should compile this file directly or as part of a project
144+
// Check if the projectMap and projectDir are provided
145+
const isProjectAware = projectMap && projectDir && projectMap.get(projectDir);
146+
let isRootCdsFile = false;
147+
let projectBasedCompilation = false;
148+
149+
if (isProjectAware) {
150+
const project = projectMap.get(projectDir);
151+
const relativePath = relative(sourceRoot, resolvedCdsFilePath);
152+
153+
// Check if this is a root CDS file (not imported by other files)
154+
if (project?.cdsFiles) {
155+
// Create a map to track imported files in the project
156+
const importedFiles = new Map<string, boolean>();
131157

132-
console.log(`Using cached dependencies from: ${cacheDir}`);
158+
// First pass: collect all imported files in the project
159+
for (const file of project.cdsFiles) {
160+
try {
161+
const absoluteFilePath = join(sourceRoot, file);
162+
if (fileExists(absoluteFilePath)) {
163+
// Get imports for this file
164+
const imports = project.imports?.get(file) ?? [];
165+
166+
// Mark imported files
167+
for (const importInfo of imports) {
168+
if (importInfo.resolvedPath) {
169+
importedFiles.set(importInfo.resolvedPath, true);
170+
}
171+
}
172+
}
173+
} catch (error) {
174+
console.warn(`Warning: Error processing imports for ${file}: ${String(error)}`);
175+
}
176+
}
177+
178+
// Check if current file is not imported by any other file in the project
179+
isRootCdsFile = !importedFiles.has(relativePath);
180+
181+
// If the file is a root CDS file, we'll use a project-based compilation approach
182+
if (isRootCdsFile) {
183+
projectBasedCompilation = true;
184+
console.log(
185+
`${resolvedCdsFilePath} identified as a root CDS file - using project-aware compilation ${versionInfo}...`,
186+
);
187+
} else {
188+
console.log(
189+
`${resolvedCdsFilePath} is imported by other files - will be compiled as part of a project ${versionInfo}...`,
190+
);
191+
}
192+
}
193+
} else {
194+
// Fallback to individual file compilation if project information is not available
195+
console.log(`Processing individual CDS file ${resolvedCdsFilePath} ${versionInfo}...`);
196+
}
197+
198+
// If it's not a root file and project-based compilation is enabled,
199+
// return success without additional processing since it will be compiled as part of its root file
200+
if (isProjectAware && !isRootCdsFile) {
201+
return {
202+
success: true,
203+
outputPath: cdsJsonOutPath,
204+
compiledAsProject: true,
205+
message: 'File was compiled as part of a project-based compilation',
206+
};
133207
}
134208

135-
const result = spawnSync(
136-
cdsCommand,
137-
[
138-
'compile',
139-
resolvedCdsFilePath,
140-
'--to',
141-
'json',
142-
'--dest',
143-
cdsJsonOutPath,
144-
'--locations',
145-
'--log-level',
146-
'warn',
147-
],
148-
spawnOptions,
149-
);
209+
// Compile the file
210+
const compileArgs = [
211+
'compile',
212+
resolvedCdsFilePath,
213+
'--to',
214+
'json',
215+
'--dest',
216+
cdsJsonOutPath,
217+
'--locations',
218+
'--log-level',
219+
'warn',
220+
];
221+
222+
// For root CDS files in project-aware mode, add --parse flag to ensure complete model
223+
if (projectBasedCompilation) {
224+
compileArgs.push('--parse');
225+
}
226+
227+
const result = spawnSync(cdsCommand, compileArgs, spawnOptions);
150228

151229
if (result.error) {
152230
throw new Error(`Error executing CDS compiler: ${result.error.message}`);
@@ -175,7 +253,11 @@ export function compileCdsToJson(
175253
console.log(`CDS compiler generated JSON to file: ${cdsJsonOutPath}`);
176254
}
177255

178-
return { success: true, outputPath: cdsJsonOutPath };
256+
return {
257+
success: true,
258+
outputPath: cdsJsonOutPath,
259+
compiledAsProject: projectBasedCompilation,
260+
};
179261
} catch (error) {
180262
return { success: false, message: String(error) };
181263
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@ export interface CdsCompilationResult {
55
success: boolean;
66
message?: string;
77
outputPath?: string;
8+
/** Flag indicating if this file was compiled directly or as part of a project */
9+
compiledAsProject?: boolean;
810
}

0 commit comments

Comments
 (0)