From 66d16b4276d75bfe2a4ee13c3a74523a74bc8a70 Mon Sep 17 00:00:00 2001 From: Ruben Romero Montes Date: Wed, 21 May 2025 15:31:59 +0200 Subject: [PATCH 1/3] fix: add better compatibility for yarn and pnpm binaries Signed-off-by: Ruben Romero Montes --- src/providers/base_javascript.js | 52 ++++++++++++++++++++++++++++++-- src/tools.js | 20 ++++++++++-- 2 files changed, 67 insertions(+), 5 deletions(-) diff --git a/src/providers/base_javascript.js b/src/providers/base_javascript.js index 9a767c1..277fc2a 100644 --- a/src/providers/base_javascript.js +++ b/src/providers/base_javascript.js @@ -2,7 +2,7 @@ import fs from 'node:fs' import os from "node:os"; import path from 'node:path' -import { getCustomPath, invokeCommand, toPurl, toPurlFromString } from "../tools.js"; +import { getCustom, getCustomPath, invokeCommand, toPurl, toPurlFromString } from "../tools.js"; import Sbom from '../sbom.js' import Manifest from './manifest.js'; @@ -314,10 +314,56 @@ export default class Base_javascript { if(!opts.cwd) { opts.cwd = path.dirname(this.#manifest.manifestPath); } - return invokeCommand(this.#cmd, args, opts); + + // Add version manager paths for JavaScript package managers + if (process.platform !== 'win32') { + const versionManagerPaths = []; + + // Add fnm path if available + const fnmDir = getCustom('FNM_DIR', null, opts); + if (fnmDir) { + versionManagerPaths.push(`${fnmDir}/current/bin`); + } + + // Add nvm path if available + const nvmDir = getCustom('NVM_DIR', null, opts); + if (nvmDir) { + versionManagerPaths.push(`${nvmDir}/current/bin`); + } + + // Add local node_modules/.bin path + const localBinPath = path.join(opts.cwd, 'node_modules', '.bin'); + if (fs.existsSync(localBinPath)) { + versionManagerPaths.push(localBinPath); + } + + if (versionManagerPaths.length > 0) { + opts = { + ...opts, + env: { + ...opts.env, + PATH: `${versionManagerPaths.join(':')}:${process.env.PATH}` + } + }; + } + } + + // Try to find the command in the following order: + // 1. Custom path from environment/opts (via getCustomPath) + // 2. Local node_modules/.bin + // 3. Global installation + let cmd = this.#cmd; + if (!fs.existsSync(cmd)) { + const localCmd = path.join(opts.cwd, 'node_modules', '.bin', this._cmdName()); + if (fs.existsSync(localCmd)) { + cmd = localCmd; + } + } + + return invokeCommand(cmd, args, opts); } catch (error) { if (error.code === 'ENOENT') { - throw new Error(`${this.#cmd} is not accessible.`); + throw new Error(`${this.#cmd} is not accessible. Please ensure it is installed via npm, corepack, or your version manager.`); } if (error.code === 'EACCES') { throw new Error(`Permission denied when executing ${this.#cmd}. Please check file permissions.`); diff --git a/src/tools.js b/src/tools.js index 207223f..05d1a58 100644 --- a/src/tools.js +++ b/src/tools.js @@ -161,9 +161,25 @@ export function invokeCommand(bin, args, opts={}) { // https://github.com/nodejs/node/issues/52681#issuecomment-2076426887 if (process.platform === 'win32') { opts = {...opts, shell: true} - args = args.map(arg => handleSpacesInPath(arg)) - bin = handleSpacesInPath(bin) } + // Handle spaces in paths for all platforms + args = args.map(arg => handleSpacesInPath(arg)) + bin = handleSpacesInPath(bin) + + opts = { + ...opts, + env: { + ...process.env, + PATH: process.env.PATH + } + }; + + + // Add maxBuffer option to handle large outputs + opts = { + ...opts, + maxBuffer: 10 * 1024 * 1024 // 10MB buffer + }; return execFileSync(bin, args, {...{stdio: 'pipe', encoding: 'utf-8'}, ...opts}) } From 2ba0c8006f22db638f2d824f2b4fc3526c096ad5 Mon Sep 17 00:00:00 2001 From: Ruben Romero Montes Date: Wed, 21 May 2025 16:03:16 +0200 Subject: [PATCH 2/3] fix: use pathdelimiter Signed-off-by: Ruben Romero Montes --- src/providers/base_javascript.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/base_javascript.js b/src/providers/base_javascript.js index 277fc2a..50eb38c 100644 --- a/src/providers/base_javascript.js +++ b/src/providers/base_javascript.js @@ -342,7 +342,7 @@ export default class Base_javascript { ...opts, env: { ...opts.env, - PATH: `${versionManagerPaths.join(':')}:${process.env.PATH}` + PATH: `${versionManagerPaths.join(path.delimiter)}${path.delimiter}${process.env.PATH}` } }; } From cd15c35b7b5cf4c1de3e0abdcd1fe4a2b533d957 Mon Sep 17 00:00:00 2001 From: Ruben Romero Montes Date: Thu, 22 May 2025 10:43:58 +0200 Subject: [PATCH 3/3] fix: don't handle spaces in unix Signed-off-by: Ruben Romero Montes --- src/tools.js | 20 +++++--------------- test/tools.test.js | 17 ----------------- 2 files changed, 5 insertions(+), 32 deletions(-) diff --git a/src/tools.js b/src/tools.js index 05d1a58..e6a77bb 100644 --- a/src/tools.js +++ b/src/tools.js @@ -1,5 +1,4 @@ import { EOL } from "os"; -import os from 'os'; import { execFileSync } from "child_process"; import { PackageURL } from "packageurl-js"; @@ -75,7 +74,7 @@ export function environmentVariableIsPopulated(envVariableName) { } /** - * + * Utility function for handling spaces in paths on Windows * @param {string} path - path to be checked if contains spaces * @return {string} a path with all spaces escaped or manipulated so it will be able to be part * of commands that will be invoked without errors in os' shell. @@ -83,16 +82,8 @@ export function environmentVariableIsPopulated(envVariableName) { export function handleSpacesInPath(path) { let transformedPath = path // if operating system is windows - if (os.platform() === "win32") { - if(hasSpaces(path)) { - transformedPath = `"${path}"` - } - } - // linux, darwin.. - else { - if(hasSpaces(path)) { - transformedPath = path.replaceAll(" ", "\\ ") - } + if(hasSpaces(path)) { + transformedPath = `"${path}"` } return transformedPath } @@ -161,10 +152,9 @@ export function invokeCommand(bin, args, opts={}) { // https://github.com/nodejs/node/issues/52681#issuecomment-2076426887 if (process.platform === 'win32') { opts = {...opts, shell: true} + args = args.map(arg => handleSpacesInPath(arg)) + bin = handleSpacesInPath(bin) } - // Handle spaces in paths for all platforms - args = args.map(arg => handleSpacesInPath(arg)) - bin = handleSpacesInPath(bin) opts = { ...opts, diff --git a/test/tools.test.js b/test/tools.test.js index b6088d5..119131c 100644 --- a/test/tools.test.js +++ b/test/tools.test.js @@ -68,7 +68,6 @@ suite('testing the various tools and utility functions', () => { test('Windows Path with spaces', async () => { const tools = await mockToolsPartial("win32") - let path = "c:\\users\\john doe\\pom.xml" let expectedPath = "\"c:\\users\\john doe\\pom.xml\"" let actualPath = tools.handleSpacesInPath(path) @@ -83,22 +82,6 @@ suite('testing the various tools and utility functions', () => { expect(actualPath).to.equal(expectedPath) }) - test('Linux Path with spaces', async () => { - const tools = await mockToolsPartial("linux") - let path = "/usr/john doe/pom.xml" - let expectedPath = "/usr/john\\ doe/pom.xml" - let actualPath = tools.handleSpacesInPath(path) - expect(actualPath).to.equal(expectedPath) - }) - - test('Linux Path with no spaces', async () => { - const tools = await mockToolsPartial("linux") - let path = "/usr/john/pom.xml" - let expectedPath = "/usr/john/pom.xml" - let actualPath = tools.handleSpacesInPath(path) - expect(actualPath).to.equal(expectedPath) - }) - })