Skip to content
Open
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
8 changes: 4 additions & 4 deletions src/spawn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ const debug = makeDebug('@oclif/plugin-plugins:spawn')

export async function spawn(modulePath: string, args: string[] = [], {cwd, logLevel}: ExecOptions): Promise<Output> {
return new Promise((resolve, reject) => {
// On windows, the global path to npm could be .cmd, .exe, or .js. If it's a .js file, we need to run it with node.
if (process.platform === 'win32' && modulePath.endsWith('.js')) {
args.unshift(`"${modulePath}"`)
modulePath = 'node'
if (modulePath.endsWith('.js')) {
const quote = process.platform === 'win32' ? `"${modulePath}"` : modulePath
args.unshift(quote)
modulePath = process.execPath
}

debug('modulePath', modulePath)
Expand Down
60 changes: 60 additions & 0 deletions test/spawn.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import {expect} from 'chai'
import {chmodSync, mkdirSync, mkdtempSync, rmSync, writeFileSync} from 'node:fs'
import {tmpdir} from 'node:os'
import {join} from 'node:path'

import {spawn} from '../src/spawn.js'

describe('spawn', () => {
let tempDir: string

beforeEach(() => {
tempDir = mkdtempSync(join(tmpdir(), 'spawn-test-'))
})

afterEach(() => {
rmSync(tempDir, {force: true, recursive: true})
})

it('should invoke .js module paths via process.execPath', async () => {
const script = join(tempDir, 'test-script.js')
writeFileSync(script, '#!/usr/bin/env nonexistent-node-binary\nconsole.log("spawned-ok")\n')
chmodSync(script, '755')

const result = await spawn(script, [], {cwd: tempDir, logLevel: 'silent'})

expect(result.stdout).to.include('spawned-ok')
})

it('should pass args after the .js module path', async () => {
const script = join(tempDir, 'echo-args.js')
writeFileSync(script, 'console.log(JSON.stringify(process.argv.slice(2)))\n')
chmodSync(script, '755')

const result = await spawn(script, ['--flag', 'value'], {cwd: tempDir, logLevel: 'silent'})

expect(result.stdout).to.include('["--flag","value"]')
})

it('should handle .js module paths with spaces in the path', async () => {
const dir = join(tempDir, 'dir with spaces')
mkdirSync(dir)
const script = join(dir, 'my script.js')
writeFileSync(script, 'console.log("spaces-ok")\n')
chmodSync(script, '755')

const result = await spawn(script, [], {cwd: tempDir, logLevel: 'silent'})

expect(result.stdout).to.include('spaces-ok')
})

it('should not modify non-.js module paths', async () => {
const script = join(tempDir, 'test-bin')
writeFileSync(script, `#!/usr/bin/env bash\necho "bin-ok"\n`)
chmodSync(script, '755')

const result = await spawn(script, [], {cwd: tempDir, logLevel: 'silent'})

expect(result.stdout).to.include('bin-ok')
})
})