Skip to content

Commit 6921c2b

Browse files
authored
Address review feedback for PR #359 (#361)
1 parent 2001913 commit 6921c2b

File tree

10 files changed

+5956
-5884
lines changed

10 files changed

+5956
-5884
lines changed

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

Lines changed: 5865 additions & 5860 deletions
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: 4 additions & 4 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: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { basename, delimiter, dirname, join, relative, resolve, sep } from 'path
44
import { CdsCompilationResult } from './types';
55
import { getCdsVersion } from './version';
66
import { modelCdsJsonFile } from '../../constants';
7+
import { getPlatformInfo } from '../../environment';
78
import {
89
fileExists,
910
dirExists,
@@ -248,19 +249,23 @@ function createSpawnOptions(
248249
cdsCommand: string,
249250
cacheDir?: string,
250251
): SpawnSyncOptions {
251-
const spawnOptions: SpawnSyncOptions = {
252-
cwd: projectBaseDir, // CRITICAL: Always use project base directory as cwd to ensure correct path generation
253-
shell: true, // Required on Windows where npm/npx are .cmd files
254-
stdio: 'pipe',
255-
env: { ...process.env },
256-
};
257-
258252
// Check if we're using a direct binary path (contains node_modules/.bin/ or node_modules\.bin\) or npx-style command
259253
// Check both platform-native separator and forward slash for cross-platform compatibility
260254
const binPathNative = `node_modules${sep}.bin${sep}`;
261255
const binPathPosix = 'node_modules/.bin/';
262256
const isDirectBinary = cdsCommand.includes(binPathNative) || cdsCommand.includes(binPathPosix);
263257

258+
// Only enable shell on Windows for npx-style commands where .cmd resolution is needed.
259+
// Direct binary paths (node_modules/.bin/cds) don't need shell on any platform.
260+
const useShell = getPlatformInfo().isWindows && !isDirectBinary;
261+
262+
const spawnOptions: SpawnSyncOptions = {
263+
cwd: projectBaseDir, // CRITICAL: Always use project base directory as cwd to ensure correct path generation
264+
shell: useShell,
265+
stdio: 'pipe',
266+
env: { ...process.env },
267+
};
268+
264269
// Only set up Node.js environment for npx-style commands, not for direct binary execution
265270
if (cacheDir && !isDirectBinary) {
266271
const nodePath = join(cacheDir, 'node_modules');

extractors/cds/tools/src/cds/indexer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { spawnSync } from 'child_process';
44
import { delimiter, join } from 'path';
55

66
import { addCdsIndexerDiagnostic } from '../diagnostics';
7+
import { npxExecutable } from '../environment';
78
import { cdsExtractorLog } from '../logging';
89
import { projectInstallDependencies } from '../packageManager';
910
import type { CdsDependencyGraph, CdsProject } from './parser/types';
@@ -110,12 +111,11 @@ export function runCdsIndexer(
110111
`Running ${CDS_INDEXER_PACKAGE} for project '${project.projectDir}'...`,
111112
);
112113

113-
const spawnResult = spawnSync('npx', ['--yes', CDS_INDEXER_PACKAGE], {
114+
const spawnResult = spawnSync(npxExecutable(), ['--yes', CDS_INDEXER_PACKAGE], {
114115
cwd: projectAbsPath,
115116
env,
116117
stdio: 'pipe',
117118
timeout: CDS_INDEXER_TIMEOUT_MS,
118-
shell: true,
119119
});
120120

121121
result.durationMs = Date.now() - startTime;

extractors/cds/tools/src/environment.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,28 @@ export function getPlatformInfo(): PlatformInfo {
4848
};
4949
}
5050

51+
/**
52+
* Returns the platform-specific `npm` executable name.
53+
* On Windows, npm is a `.cmd` script that requires the full name for
54+
* `execFileSync`/`spawnSync` when `shell` is `false`.
55+
*
56+
* @returns `'npm.cmd'` on Windows, `'npm'` elsewhere.
57+
*/
58+
export function npmExecutable(): string {
59+
return getPlatformInfo().isWindows ? 'npm.cmd' : 'npm';
60+
}
61+
62+
/**
63+
* Returns the platform-specific `npx` executable name.
64+
* On Windows, npx is a `.cmd` script that requires the full name for
65+
* `execFileSync`/`spawnSync` when `shell` is `false`.
66+
*
67+
* @returns `'npx.cmd'` on Windows, `'npx'` elsewhere.
68+
*/
69+
export function npxExecutable(): string {
70+
return getPlatformInfo().isWindows ? 'npx.cmd' : 'npx';
71+
}
72+
5173
/**
5274
* Get the path to the CodeQL executable.
5375
* Prioritizes CODEQL_DIST if set and valid. Otherwise, tries to find CodeQL via system PATH.

extractors/cds/tools/src/packageManager/cacheInstaller.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { dirname, join, resolve } from 'path';
66
import type { CdsDependencyCombination } from './types';
77
import { CdsDependencyGraph, CdsProject } from '../cds/parser/types';
88
import { DiagnosticSeverity } from '../diagnostics';
9+
import { npmExecutable } from '../environment';
910
import { cdsExtractorLog } from '../logging';
1011
import { resolveCdsVersions } from './versionResolver';
1112

@@ -494,10 +495,9 @@ function installDependenciesInCache(
494495
}
495496

496497
try {
497-
execFileSync('npm', ['install', '--quiet', '--no-audit', '--no-fund'], {
498+
execFileSync(npmExecutable(), ['install', '--quiet', '--no-audit', '--no-fund'], {
498499
cwd: cacheDir,
499500
stdio: 'inherit',
500-
shell: true,
501501
});
502502

503503
// Add warning diagnostic if using fallback versions

extractors/cds/tools/src/packageManager/projectInstaller.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { execFileSync } from 'child_process';
44
import { join } from 'path';
55

6+
import { npmExecutable } from '../environment';
67
import type { FullDependencyInstallationResult } from './types';
78
import type { CdsProject } from '../cds/parser';
89
import { cdsExtractorLog } from '../logging';
@@ -71,12 +72,15 @@ export function projectInstallDependencies(
7172
);
7273

7374
try {
74-
execFileSync('npm', ['install', '--quiet', '--no-audit', '--no-fund'], {
75-
cwd: projectPath,
76-
stdio: 'inherit',
77-
timeout: 120000, // 2-minute timeout
78-
shell: true,
79-
});
75+
execFileSync(
76+
npmExecutable(),
77+
['install', '--ignore-scripts', '--quiet', '--no-audit', '--no-fund'],
78+
{
79+
cwd: projectPath,
80+
stdio: 'inherit',
81+
timeout: 120000, // 2-minute timeout
82+
},
83+
);
8084

8185
result.success = true;
8286
cdsExtractorLog(

extractors/cds/tools/test/src/environment.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import * as path from 'path';
66

77
import {
88
getPlatformInfo,
9+
npmExecutable,
10+
npxExecutable,
911
getCodeQLExePath,
1012
getJavaScriptExtractorRoot,
1113
setupJavaScriptExtractorEnv,
@@ -125,6 +127,40 @@ describe('environment', () => {
125127
});
126128
});
127129

130+
describe('npmExecutable', () => {
131+
it('should return npm.cmd on Windows', () => {
132+
(os.platform as jest.Mock).mockReturnValue('win32');
133+
expect(npmExecutable()).toBe('npm.cmd');
134+
});
135+
136+
it('should return npm on Linux', () => {
137+
(os.platform as jest.Mock).mockReturnValue('linux');
138+
expect(npmExecutable()).toBe('npm');
139+
});
140+
141+
it('should return npm on macOS', () => {
142+
(os.platform as jest.Mock).mockReturnValue('darwin');
143+
expect(npmExecutable()).toBe('npm');
144+
});
145+
});
146+
147+
describe('npxExecutable', () => {
148+
it('should return npx.cmd on Windows', () => {
149+
(os.platform as jest.Mock).mockReturnValue('win32');
150+
expect(npxExecutable()).toBe('npx.cmd');
151+
});
152+
153+
it('should return npx on Linux', () => {
154+
(os.platform as jest.Mock).mockReturnValue('linux');
155+
expect(npxExecutable()).toBe('npx');
156+
});
157+
158+
it('should return npx on macOS', () => {
159+
(os.platform as jest.Mock).mockReturnValue('darwin');
160+
expect(npxExecutable()).toBe('npx');
161+
});
162+
});
163+
128164
describe('getCodeQLExePath', () => {
129165
it('should resolve codeql.exe path on Windows when CODEQL_DIST is set and valid', () => {
130166
// Setup platform

extractors/cds/tools/test/src/packageManager/projectInstaller.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,11 @@ describe('CDS Compiler Installer', () => {
6464

6565
expect(mockExecFileSync).toHaveBeenCalledWith(
6666
'npm',
67-
['install', '--quiet', '--no-audit', '--no-fund'],
67+
['install', '--ignore-scripts', '--quiet', '--no-audit', '--no-fund'],
6868
{
6969
cwd: '/test/source/test-project',
7070
stdio: 'inherit',
7171
timeout: 120000,
72-
shell: true,
7372
},
7473
);
7574
});

extractors/cds/tools/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
"noImplicitReturns": true,
1616
"noFallthroughCasesInSwitch": true,
1717
"resolveJsonModule": true,
18-
"skipLibCheck": true
18+
"skipLibCheck": true,
19+
"types": ["jest", "node"]
1920
},
2021
"include": [
2122
"*.ts",

0 commit comments

Comments
 (0)