Skip to content

Commit a1d1c5d

Browse files
committed
CDS extractor rewrite project cleanup
Cleanup remaining, known problems with the CDS extractor rewrite for PR #195 , including: - Fixes a bug that was introduced to the `index-files.sh` script by the previous commit; - Removes dead `src/**/*.ts` code, where found; - Replaces hardcoded, system-local paths in CDS project files; - Improves the organization, logic, and testing of `src/cds/compiler` code.
1 parent 6aa4032 commit a1d1c5d

File tree

15 files changed

+577
-1483
lines changed

15 files changed

+577
-1483
lines changed

extractors/cds/tools/cds-extractor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ cdsExtractorLog(
212212
startPerformanceTracking('CDS Compilation');
213213
try {
214214
// Use the new orchestrated compilation approach (autobuild mode, no debug)
215-
orchestrateCompilation(dependencyGraph, projectCacheDirMap, codeqlExePath, false);
215+
orchestrateCompilation(dependencyGraph, projectCacheDirMap, codeqlExePath);
216216

217217
// Handle compilation failures for normal mode
218218
if (!dependencyGraph.statusSummary.overallSuccess) {

extractors/cds/tools/eslint.config.mjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export default defineConfig([
5050
...globals.node,
5151
},
5252
// @ts-expect-error - tsParser is a valid parser
53+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
5354
parser: tsParser,
5455
ecmaVersion: 2018,
5556
sourceType: 'module',
@@ -141,7 +142,7 @@ export default defineConfig([
141142
sourceType: 'script',
142143
parserOptions: {
143144
project: './tsconfig.json',
144-
tsconfigRootDir: '/Users/data-douser/Git/data-douser/codeql-sap-js/extractors/cds/tools',
145+
tsconfigRootDir: __dirname,
145146
},
146147
},
147148
},
@@ -156,7 +157,7 @@ export default defineConfig([
156157
sourceType: 'module',
157158
parserOptions: {
158159
project: './tsconfig.json',
159-
tsconfigRootDir: '/Users/data-douser/Git/data-douser/codeql-sap-js/extractors/cds/tools',
160+
tsconfigRootDir: __dirname,
160161
},
161162
},
162163
rules: {

extractors/cds/tools/index-files.sh

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,6 @@ _script_dir=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
2727
_cds_extractor_js_path="${_script_dir}/dist/cds-extractor.js"
2828
_cds_extractor_node_modules_dir="${_script_dir}/node_modules"
2929

30-
if [ ! -f "${_cds_extractor_js_path}" ]; then
31-
echo "Error: The 'cds-extractor.js' script does not exist at the expected path: ${_cds_extractor_js_path}"
32-
echo "Please ensure that the script has been built and is available in the 'dist' directory."
33-
exit 3
34-
fi
35-
3630
# Change to the directory of this shell script to ensure that npm looks up the
3731
# package.json file in the correct directory and installs the dependencies
3832
# (i.e. node_modules) relative to this directory. This is technically a violation

extractors/cds/tools/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"build:all": "npm run lint:fix && npm run test:coverage && npm run build",
99
"clean": "rm -rf coverage dist",
1010
"prebuild": "npm run clean",
11-
"lint": "eslint --ext .ts src/",
11+
"lint": "eslint --ext .ts cds-extractor.ts src/ test/src/",
1212
"lint:fix": "eslint --ext .ts --fix cds-extractor.ts src/ test/src/",
1313
"format": "prettier --write 'src/**/*.ts'",
1414
"test": "jest",

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

Lines changed: 134 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -43,85 +43,22 @@ const cdsCommandCache: CdsCommandCache = {
4343
};
4444

4545
/**
46-
* Reset the command cache - primarily for testing
47-
*/
48-
export function resetCdsCommandCache(): void {
49-
cdsCommandCache.commandResults.clear();
50-
cdsCommandCache.availableCacheDirs = [];
51-
cdsCommandCache.globalCommand = undefined;
52-
cdsCommandCache.initialized = false;
53-
}
54-
55-
/**
56-
* Check if a CDS command is available and working
57-
* @param command The command to test
58-
* @param sourceRoot The source root directory to use as cwd when testing the command
59-
* @param silent Whether to suppress logging of test failures
60-
* @returns Object with test result and version information
46+
* Determine the `cds` command to use based on the environment and cache directory.
47+
*
48+
* This function uses a caching strategy to minimize repeated CLI command testing:
49+
* - Initializes a global cache on first call
50+
* - Tests global commands once and caches results
51+
* - Discovers all available cache directories upfront
52+
* - Reuses test results across multiple calls
6153
*/
62-
function testCdsCommand(
63-
command: string,
64-
sourceRoot: string,
65-
silent: boolean = false,
66-
): { works: boolean; version?: string; error?: string } {
67-
// Check cache first
68-
const cachedResult = cdsCommandCache.commandResults.get(command);
69-
if (cachedResult) {
70-
return cachedResult;
71-
}
72-
54+
export function determineCdsCommand(cacheDir: string | undefined, sourceRoot: string): string {
7355
try {
74-
// Try to run the command with --version to see if it works
75-
// CRITICAL: Use sourceRoot as cwd and clean environment to avoid conflicts
76-
let result: string;
77-
78-
const cleanEnv = {
79-
...process.env,
80-
// Remove any CodeQL-specific environment variables that might interfere
81-
CODEQL_EXTRACTOR_CDS_WIP_DATABASE: undefined,
82-
CODEQL_RUNNER: undefined,
83-
};
84-
85-
if (command.includes('node ')) {
86-
// For node commands, we need to split and execute properly
87-
const parts = command.split(' ');
88-
const nodeExecutable = parts[0]; // 'node'
89-
const scriptPath = parts[1].replace(/"/g, ''); // Remove quotes from path
90-
result = execFileSync(nodeExecutable, [scriptPath, '--version'], {
91-
encoding: 'utf8',
92-
stdio: 'pipe',
93-
timeout: 5000, // Reduced timeout for faster failure
94-
cwd: sourceRoot,
95-
env: cleanEnv,
96-
}).toString();
97-
} else {
98-
// Use shell-quote to properly escape the command and prevent injection
99-
const escapedCommand = quote([command, '--version']);
100-
result = execFileSync('sh', ['-c', escapedCommand], {
101-
encoding: 'utf8',
102-
stdio: 'pipe',
103-
timeout: 5000, // Reduced timeout for faster failure
104-
cwd: sourceRoot,
105-
env: cleanEnv,
106-
}).toString();
107-
}
108-
109-
// Extract version from output (typically in format "@sap/cds-dk: 6.1.3" or just "6.1.3")
110-
const versionMatch = result.match(/(\d+\.\d+\.\d+)/);
111-
const version = versionMatch ? versionMatch[1] : undefined;
112-
113-
const testResult = { works: true, version };
114-
cdsCommandCache.commandResults.set(command, testResult);
115-
return testResult;
56+
// Always use the efficient path - debug information is collected separately
57+
return getBestCdsCommand(cacheDir, sourceRoot);
11658
} catch (error) {
117-
const errorMessage = String(error);
118-
if (!silent) {
119-
cdsExtractorLog('debug', `CDS command test failed for '${command}': ${errorMessage}`);
120-
}
121-
122-
const testResult = { works: false, error: errorMessage };
123-
cdsCommandCache.commandResults.set(command, testResult);
124-
return testResult;
59+
const errorMessage = `Failed to determine CDS command: ${String(error)}`;
60+
cdsExtractorLog('error', errorMessage);
61+
throw new Error(errorMessage);
12562
}
12663
}
12764

@@ -159,44 +96,6 @@ function discoverAvailableCacheDirs(sourceRoot: string): string[] {
15996
return availableDirs;
16097
}
16198

162-
/**
163-
* Initialize the CDS command cache by testing global commands
164-
* @param sourceRoot The source root directory
165-
*/
166-
function initializeCdsCommandCache(sourceRoot: string): void {
167-
if (cdsCommandCache.initialized) {
168-
return;
169-
}
170-
171-
cdsExtractorLog('info', 'Initializing CDS command cache...');
172-
173-
// Test global commands first (most commonly used)
174-
const globalCommands = ['cds', 'npx -y --package @sap/cds-dk cds'];
175-
176-
for (const command of globalCommands) {
177-
const result = testCdsCommand(command, sourceRoot, true); // Silent testing
178-
if (result.works) {
179-
cdsCommandCache.globalCommand = command;
180-
cdsExtractorLog(
181-
'info',
182-
`Found working global CDS command: ${command} (v${result.version ?? 'unknown'})`,
183-
);
184-
break;
185-
}
186-
}
187-
188-
// Discover available cache directories
189-
const cacheDirs = discoverAvailableCacheDirs(sourceRoot);
190-
if (cacheDirs.length > 0) {
191-
cdsExtractorLog(
192-
'info',
193-
`Discovered ${cacheDirs.length} CDS cache director${cacheDirs.length === 1 ? 'y' : 'ies'}`,
194-
);
195-
}
196-
197-
cdsCommandCache.initialized = true;
198-
}
199-
20099
/**
201100
* Get the best CDS command for a specific cache directory
202101
* @param cacheDir Optional specific cache directory
@@ -246,26 +145,6 @@ function getBestCdsCommand(cacheDir: string | undefined, sourceRoot: string): st
246145
return 'npx -y --package @sap/cds-dk cds';
247146
}
248147

249-
/**
250-
* Determine the `cds` command to use based on the environment and cache directory.
251-
*
252-
* This function uses a caching strategy to minimize repeated CLI command testing:
253-
* - Initializes a global cache on first call
254-
* - Tests global commands once and caches results
255-
* - Discovers all available cache directories upfront
256-
* - Reuses test results across multiple calls
257-
*/
258-
export function determineCdsCommand(cacheDir: string | undefined, sourceRoot: string): string {
259-
try {
260-
// Always use the efficient path - debug information is collected separately
261-
return getBestCdsCommand(cacheDir, sourceRoot);
262-
} catch (error) {
263-
const errorMessage = `Failed to determine CDS command: ${String(error)}`;
264-
cdsExtractorLog('error', errorMessage);
265-
throw new Error(errorMessage);
266-
}
267-
}
268-
269148
/**
270149
* Get detailed command analysis for debug purposes
271150
* This replaces the old performComprehensiveAnalysis function
@@ -353,3 +232,124 @@ export function getCommandAnalysisForDebug(
353232
throw new Error(`Failed to analyze CDS commands: ${String(error)}`);
354233
}
355234
}
235+
236+
/**
237+
* Initialize the CDS command cache by testing global commands
238+
* @param sourceRoot The source root directory
239+
*/
240+
function initializeCdsCommandCache(sourceRoot: string): void {
241+
if (cdsCommandCache.initialized) {
242+
return;
243+
}
244+
245+
cdsExtractorLog('info', 'Initializing CDS command cache...');
246+
247+
// Test global commands first (most commonly used)
248+
const globalCommands = ['cds', 'npx -y --package @sap/cds-dk cds'];
249+
250+
for (const command of globalCommands) {
251+
const result = testCdsCommand(command, sourceRoot, true); // Silent testing
252+
if (result.works) {
253+
cdsCommandCache.globalCommand = command;
254+
cdsExtractorLog(
255+
'info',
256+
`Found working global CDS command: ${command} (v${result.version ?? 'unknown'})`,
257+
);
258+
break;
259+
}
260+
}
261+
262+
// Discover available cache directories
263+
const cacheDirs = discoverAvailableCacheDirs(sourceRoot);
264+
if (cacheDirs.length > 0) {
265+
cdsExtractorLog(
266+
'info',
267+
`Discovered ${cacheDirs.length} CDS cache director${cacheDirs.length === 1 ? 'y' : 'ies'}`,
268+
);
269+
}
270+
271+
cdsCommandCache.initialized = true;
272+
}
273+
274+
/**
275+
* Reset the command cache - primarily for testing
276+
*/
277+
export function resetCdsCommandCache(): void {
278+
cdsCommandCache.commandResults.clear();
279+
cdsCommandCache.availableCacheDirs = [];
280+
cdsCommandCache.globalCommand = undefined;
281+
cdsCommandCache.initialized = false;
282+
}
283+
284+
/**
285+
* Check if a CDS command is available and working
286+
* @param command The command to test
287+
* @param sourceRoot The source root directory to use as cwd when testing the command
288+
* @param silent Whether to suppress logging of test failures
289+
* @returns Object with test result and version information
290+
*/
291+
function testCdsCommand(
292+
command: string,
293+
sourceRoot: string,
294+
silent: boolean = false,
295+
): { works: boolean; version?: string; error?: string } {
296+
// Check cache first
297+
const cachedResult = cdsCommandCache.commandResults.get(command);
298+
if (cachedResult) {
299+
return cachedResult;
300+
}
301+
302+
try {
303+
// Try to run the command with --version to see if it works
304+
// CRITICAL: Use sourceRoot as cwd and clean environment to avoid conflicts
305+
let result: string;
306+
307+
const cleanEnv = {
308+
...process.env,
309+
// Remove any CodeQL-specific environment variables that might interfere
310+
CODEQL_EXTRACTOR_CDS_WIP_DATABASE: undefined,
311+
CODEQL_RUNNER: undefined,
312+
};
313+
314+
if (command.includes('node ')) {
315+
// For node commands, we need to split and execute properly
316+
const parts = command.split(' ');
317+
const nodeExecutable = parts[0]; // 'node'
318+
const scriptPath = parts[1].replace(/"/g, ''); // Remove quotes from path
319+
result = execFileSync(nodeExecutable, [scriptPath, '--version'], {
320+
encoding: 'utf8',
321+
stdio: 'pipe',
322+
timeout: 5000, // Reduced timeout for faster failure
323+
cwd: sourceRoot,
324+
env: cleanEnv,
325+
}).toString();
326+
} else {
327+
// Use shell-quote to properly escape the command and prevent injection
328+
const escapedCommand = quote([command, '--version']);
329+
result = execFileSync('sh', ['-c', escapedCommand], {
330+
encoding: 'utf8',
331+
stdio: 'pipe',
332+
timeout: 5000, // Reduced timeout for faster failure
333+
cwd: sourceRoot,
334+
env: cleanEnv,
335+
}).toString();
336+
}
337+
338+
// Extract version from output (typically in format "@sap/cds-dk: 6.1.3" or just "6.1.3")
339+
const versionMatch = result.match(/(\d+\.\d+\.\d+)/);
340+
const version = versionMatch ? versionMatch[1] : undefined;
341+
342+
const testResult = { works: true, version };
343+
cdsCommandCache.commandResults.set(command, testResult);
344+
return testResult;
345+
} catch (error) {
346+
const errorMessage = String(error);
347+
if (!silent) {
348+
cdsExtractorLog('debug', `CDS command test failed for '${command}': ${errorMessage}`);
349+
}
350+
351+
const testResult = { works: false, error: errorMessage };
352+
cdsCommandCache.commandResults.set(command, testResult);
353+
return testResult;
354+
}
355+
}

0 commit comments

Comments
 (0)