diff --git a/src/$.mjs b/src/$.mjs index 46c7258..aa9aa75 100755 --- a/src/$.mjs +++ b/src/$.mjs @@ -641,6 +641,7 @@ let globalShellSettings = { function createResult({ code, stdout = '', stderr = '', stdin = '' }) { return { code, + exitCode: code, stdout, stderr, stdin, @@ -2053,6 +2054,7 @@ class ProcessRunner extends StreamEmitter { const resultData = { code: finalExitCode, + exitCode: finalExitCode, stdout: this.options.capture ? (this.outChunks && this.outChunks.length > 0 ? Buffer.concat(this.outChunks).toString('utf8') : '') : undefined, stderr: this.options.capture ? (this.errChunks && this.errChunks.length > 0 ? Buffer.concat(this.errChunks).toString('utf8') : '') : undefined, stdin: this.options.capture && this.inChunks ? Buffer.concat(this.inChunks).toString('utf8') : undefined, @@ -2443,6 +2445,7 @@ class ProcessRunner extends StreamEmitter { result = { code: 0, + exitCode: 0, stdout: this.options.capture ? Buffer.concat(chunks).toString('utf8') : undefined, stderr: this.options.capture ? '' : undefined, stdin: this.options.capture ? stdinData : undefined @@ -2494,6 +2497,7 @@ class ProcessRunner extends StreamEmitter { trace('ProcessRunner', () => `Virtual command cancelled with signal ${this._cancellationSignal}, exit code: ${exitCode}`); result = { code: exitCode, + exitCode: exitCode, stdout: '', stderr: '' }; @@ -2505,6 +2509,7 @@ class ProcessRunner extends StreamEmitter { result = { ...result, code: result.code ?? 0, + exitCode: result.code ?? 0, stdout: this.options.capture ? (result.stdout ?? '') : undefined, stderr: this.options.capture ? (result.stderr ?? '') : undefined, stdin: this.options.capture ? stdinData : undefined @@ -2553,6 +2558,7 @@ class ProcessRunner extends StreamEmitter { const result = { code: exitCode, + exitCode: exitCode, stdout: error.stdout ?? '', stderr: error.stderr ?? error.message, stdin: '' @@ -3217,6 +3223,7 @@ class ProcessRunner extends StreamEmitter { } result = { code: 0, + exitCode: 0, stdout: this.options.capture ? Buffer.concat(chunks).toString('utf8') : undefined, stderr: this.options.capture ? '' : undefined, stdin: this.options.capture ? currentInput : undefined @@ -3472,6 +3479,7 @@ class ProcessRunner extends StreamEmitter { let result = { code: proc.status || 0, + exitCode: proc.status || 0, stdout: proc.stdout || '', stderr: proc.stderr || '', stdin: currentInput @@ -3647,7 +3655,7 @@ class ProcessRunner extends StreamEmitter { operators: sequence.operators }, null, 2)}`); - let lastResult = { code: 0, stdout: '', stderr: '' }; + let lastResult = { code: 0, exitCode: 0, stdout: '', stderr: '' }; let combinedStdout = ''; let combinedStderr = ''; @@ -3689,6 +3697,7 @@ class ProcessRunner extends StreamEmitter { return { code: lastResult.code, + exitCode: lastResult.code, stdout: combinedStdout, stderr: combinedStderr, async text() { @@ -3715,7 +3724,7 @@ class ProcessRunner extends StreamEmitter { } else if (subshell.command.type === 'simple') { result = await this._runSimpleCommand(subshell.command); } else { - result = { code: 0, stdout: '', stderr: '' }; + result = { code: 0, exitCode: 0, stdout: '', stderr: '' }; } return result; diff --git a/tests/exitcode-alias.test.mjs b/tests/exitcode-alias.test.mjs new file mode 100644 index 0000000..771eed8 --- /dev/null +++ b/tests/exitcode-alias.test.mjs @@ -0,0 +1,50 @@ +import { test, expect, describe, beforeEach } from 'bun:test'; +import './test-helper.mjs'; // Automatically sets up beforeEach/afterEach cleanup +import { $, shell } from '../src/$.mjs'; + +describe('exitCode alias for code', () => { + beforeEach(() => { + // Reset all shell settings before each test + shell.errexit(false); + shell.verbose(false); + shell.xtrace(false); + shell.pipefail(false); + shell.nounset(false); + }); + + test('should provide exitCode as alias for code property', async () => { + const result = await $`exit 0`; + expect(result.code).toBe(0); + expect(result.exitCode).toBe(0); + expect(result.code).toBe(result.exitCode); + }); + + test('should have matching exitCode for non-zero exit codes', async () => { + const result = await $`exit 42`; + expect(result.code).toBe(42); + expect(result.exitCode).toBe(42); + expect(result.code).toBe(result.exitCode); + }); + + test('should have matching exitCode for successful commands', async () => { + const result = await $`echo "hello"`; + expect(result.code).toBe(0); + expect(result.exitCode).toBe(0); + expect(result.code).toBe(result.exitCode); + expect(result.stdout.trim()).toBe('hello'); + }); + + test('should maintain exitCode alias in pipeline operations', async () => { + const result = await $`echo "test" | grep "test"`; + expect(result.code).toBe(0); + expect(result.exitCode).toBe(0); + expect(result.code).toBe(result.exitCode); + }); + + test('should maintain exitCode alias for failed commands', async () => { + const result = await $`exit 42`; + expect(result.code).toBe(42); + expect(result.exitCode).toBe(42); + expect(result.code).toBe(result.exitCode); + }); +}); \ No newline at end of file