diff --git a/src/commands.ts b/src/commands.ts index 57b522b..e84464d 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -8,6 +8,7 @@ import { getStatsFromTree, processTraceFiles, showTree, treeIdNodes } from './tr import { getTracePanel, prepareWebView } from './webview' import { getCurrentConfig } from './configuration' import { log } from './logger' +import { createTraceCommand } from './shell' import type { CommandId } from './constants' import { addTraceFile, getWorkspacePath, openTerminal, openTraceDirectoryExternal, setLastMessageTrigger } from './storage' import { addTraceDiagnostics, clearTaceDiagnostics } from './traceDiagnostics' @@ -128,9 +129,7 @@ async function runTrace(args?: unknown[]) { return } - const quotedTraceDir = `'${traceDir}'` - // eslint-disable-next-line no-template-curly-in-string - const fullCmd = `(cd '${newDirName ?? workspacePath}'; ${traceCmd.replace('${traceDir}', quotedTraceDir)})` + const fullCmd = createTraceCommand(traceCmd, traceDir) log(fullCmd) @@ -147,8 +146,9 @@ async function runTrace(args?: unknown[]) { setStatusBarState('traceError', false) - log(`shell: ${process.env.SHELL}`) - const cmdProcess = spawn(fullCmd, [], { cwd: newProjectPath, shell: process.env.SHELL }) + log(`cwd: ${newProjectPath}`) + log(`shell: ${process.env.SHELL ?? 'default'}`) + const cmdProcess = spawn(fullCmd, [], { cwd: newProjectPath, shell: process.env.SHELL || true }) let err = '' cmdProcess.stderr.on('data', data => err += data.toString()) diff --git a/src/shell.ts b/src/shell.ts new file mode 100644 index 0000000..69fdea0 --- /dev/null +++ b/src/shell.ts @@ -0,0 +1,15 @@ +import * as process from 'node:process' + +export function quoteShellArg(value: string, platform: NodeJS.Platform = process.platform) { + if (platform === 'win32') { + return `"${value.replace(/"/g, '\\"')}"` + } + + return `'${value.replace(/'/g, `'\\''`)}'` +} + +export function createTraceCommand(traceCmd: string, traceDir: string, platform: NodeJS.Platform = process.platform) { + const traceDirPlaceholder = '$' + '{traceDir}' + + return traceCmd.replace(traceDirPlaceholder, quoteShellArg(traceDir, platform)) +} diff --git a/test/index.test.ts b/test/index.test.ts index 401553c..e1dad76 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -1,7 +1,27 @@ import { describe, expect, it } from 'vitest' +import { createTraceCommand, quoteShellArg } from '../src/shell' describe('should', () => { it('exported', () => { expect(1).toEqual(1) }) }) + +describe('shell command helpers', () => { + it('quotes trace directories for posix shells', () => { + expect(quoteShellArg('/tmp/a b/trace', 'darwin')).toBe('\'/tmp/a b/trace\'') + expect(quoteShellArg('/tmp/it\'s/trace', 'linux')).toBe('\'/tmp/it\'\\\'\'s/trace\'') + }) + + it('quotes trace directories for windows shells', () => { + expect(quoteShellArg('c:\\Users\\Name With Space\\trace', 'win32')).toBe('"c:\\Users\\Name With Space\\trace"') + }) + + it('builds the trace command without embedding a cd command', () => { + const traceDirPlaceholder = '$' + '{traceDir}' + const command = createTraceCommand(`npx tsc --noEmit --generateTrace ${traceDirPlaceholder}`, 'p:\\_godot\\trace', 'win32') + + expect(command).toBe('npx tsc --noEmit --generateTrace "p:\\_godot\\trace"') + expect(command).not.toContain('cd ') + }) +})