Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 49 additions & 3 deletions src/providers/base_javascript.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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(path.delimiter)}${path.delimiter}${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.`);
Expand Down
30 changes: 18 additions & 12 deletions src/tools.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { EOL } from "os";
import os from 'os';
import { execFileSync } from "child_process";
import { PackageURL } from "packageurl-js";

Expand Down Expand Up @@ -75,24 +74,16 @@ 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.
*/
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
}
Expand Down Expand Up @@ -165,5 +156,20 @@ export function invokeCommand(bin, args, opts={}) {
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})
}
17 changes: 0 additions & 17 deletions test/tools.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
})

})


Expand Down
Loading