Skip to content

Commit 7869daa

Browse files
authored
fix: add better compatibility for yarn and pnpm binaries (#206)
* fix: add better compatibility for yarn and pnpm binaries Signed-off-by: Ruben Romero Montes <rromerom@redhat.com>
1 parent 0d8c7d4 commit 7869daa

File tree

3 files changed

+67
-32
lines changed

3 files changed

+67
-32
lines changed

src/providers/base_javascript.js

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import fs from 'node:fs'
22
import os from "node:os";
33
import path from 'node:path'
44

5-
import { getCustomPath, invokeCommand, toPurl, toPurlFromString } from "../tools.js";
5+
import { getCustom, getCustomPath, invokeCommand, toPurl, toPurlFromString } from "../tools.js";
66
import Sbom from '../sbom.js'
77
import Manifest from './manifest.js';
88

@@ -314,10 +314,56 @@ export default class Base_javascript {
314314
if(!opts.cwd) {
315315
opts.cwd = path.dirname(this.#manifest.manifestPath);
316316
}
317-
return invokeCommand(this.#cmd, args, opts);
317+
318+
// Add version manager paths for JavaScript package managers
319+
if (process.platform !== 'win32') {
320+
const versionManagerPaths = [];
321+
322+
// Add fnm path if available
323+
const fnmDir = getCustom('FNM_DIR', null, opts);
324+
if (fnmDir) {
325+
versionManagerPaths.push(`${fnmDir}/current/bin`);
326+
}
327+
328+
// Add nvm path if available
329+
const nvmDir = getCustom('NVM_DIR', null, opts);
330+
if (nvmDir) {
331+
versionManagerPaths.push(`${nvmDir}/current/bin`);
332+
}
333+
334+
// Add local node_modules/.bin path
335+
const localBinPath = path.join(opts.cwd, 'node_modules', '.bin');
336+
if (fs.existsSync(localBinPath)) {
337+
versionManagerPaths.push(localBinPath);
338+
}
339+
340+
if (versionManagerPaths.length > 0) {
341+
opts = {
342+
...opts,
343+
env: {
344+
...opts.env,
345+
PATH: `${versionManagerPaths.join(path.delimiter)}${path.delimiter}${process.env.PATH}`
346+
}
347+
};
348+
}
349+
}
350+
351+
// Try to find the command in the following order:
352+
// 1. Custom path from environment/opts (via getCustomPath)
353+
// 2. Local node_modules/.bin
354+
// 3. Global installation
355+
let cmd = this.#cmd;
356+
if (!fs.existsSync(cmd)) {
357+
const localCmd = path.join(opts.cwd, 'node_modules', '.bin', this._cmdName());
358+
if (fs.existsSync(localCmd)) {
359+
cmd = localCmd;
360+
}
361+
}
362+
363+
return invokeCommand(cmd, args, opts);
318364
} catch (error) {
319365
if (error.code === 'ENOENT') {
320-
throw new Error(`${this.#cmd} is not accessible.`);
366+
throw new Error(`${this.#cmd} is not accessible. Please ensure it is installed via npm, corepack, or your version manager.`);
321367
}
322368
if (error.code === 'EACCES') {
323369
throw new Error(`Permission denied when executing ${this.#cmd}. Please check file permissions.`);

src/tools.js

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { EOL } from "os";
2-
import os from 'os';
32
import { execFileSync } from "child_process";
43
import { PackageURL } from "packageurl-js";
54

@@ -75,24 +74,16 @@ export function environmentVariableIsPopulated(envVariableName) {
7574
}
7675

7776
/**
78-
*
77+
* Utility function for handling spaces in paths on Windows
7978
* @param {string} path - path to be checked if contains spaces
8079
* @return {string} a path with all spaces escaped or manipulated so it will be able to be part
8180
* of commands that will be invoked without errors in os' shell.
8281
*/
8382
export function handleSpacesInPath(path) {
8483
let transformedPath = path
8584
// if operating system is windows
86-
if (os.platform() === "win32") {
87-
if(hasSpaces(path)) {
88-
transformedPath = `"${path}"`
89-
}
90-
}
91-
// linux, darwin..
92-
else {
93-
if(hasSpaces(path)) {
94-
transformedPath = path.replaceAll(" ", "\\ ")
95-
}
85+
if(hasSpaces(path)) {
86+
transformedPath = `"${path}"`
9687
}
9788
return transformedPath
9889
}
@@ -165,5 +156,20 @@ export function invokeCommand(bin, args, opts={}) {
165156
bin = handleSpacesInPath(bin)
166157
}
167158

159+
opts = {
160+
...opts,
161+
env: {
162+
...process.env,
163+
PATH: process.env.PATH
164+
}
165+
};
166+
167+
168+
// Add maxBuffer option to handle large outputs
169+
opts = {
170+
...opts,
171+
maxBuffer: 10 * 1024 * 1024 // 10MB buffer
172+
};
173+
168174
return execFileSync(bin, args, {...{stdio: 'pipe', encoding: 'utf-8'}, ...opts})
169175
}

test/tools.test.js

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ suite('testing the various tools and utility functions', () => {
6868

6969
test('Windows Path with spaces', async () => {
7070
const tools = await mockToolsPartial("win32")
71-
7271
let path = "c:\\users\\john doe\\pom.xml"
7372
let expectedPath = "\"c:\\users\\john doe\\pom.xml\""
7473
let actualPath = tools.handleSpacesInPath(path)
@@ -83,22 +82,6 @@ suite('testing the various tools and utility functions', () => {
8382
expect(actualPath).to.equal(expectedPath)
8483
})
8584

86-
test('Linux Path with spaces', async () => {
87-
const tools = await mockToolsPartial("linux")
88-
let path = "/usr/john doe/pom.xml"
89-
let expectedPath = "/usr/john\\ doe/pom.xml"
90-
let actualPath = tools.handleSpacesInPath(path)
91-
expect(actualPath).to.equal(expectedPath)
92-
})
93-
94-
test('Linux Path with no spaces', async () => {
95-
const tools = await mockToolsPartial("linux")
96-
let path = "/usr/john/pom.xml"
97-
let expectedPath = "/usr/john/pom.xml"
98-
let actualPath = tools.handleSpacesInPath(path)
99-
expect(actualPath).to.equal(expectedPath)
100-
})
101-
10285
})
10386

10487

0 commit comments

Comments
 (0)