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
44 changes: 44 additions & 0 deletions examples/quiet-method-demo.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env node

/**
* Demonstration of the .quiet() method
* Similar to zx's .quiet() functionality
*
* This example shows how .quiet() suppresses console output
* while still capturing the command's stdout/stderr
*/

import { $ } from '../src/$.mjs';

console.log('=== Example 1: Without .quiet() - output is shown ===');
const result1 = await $`echo "This will be printed to console"`;
console.log('Captured stdout:', result1.stdout.trim());
console.log('');

console.log('=== Example 2: With .quiet() - output is suppressed ===');
const result2 = await $`echo "This will NOT be printed to console"`.quiet();
console.log('Captured stdout:', result2.stdout.trim());
console.log('');

console.log('=== Example 3: Similar to the issue example ===');
// This simulates the use case from the issue:
// await $`gh api gists/${gistId} --jq '{owner: .owner.login, files: .files, history: .history}'`.quiet();

// Using a simple command instead of gh api for demonstration
const jsonData = JSON.stringify({
owner: 'test-user',
files: { 'file.txt': { content: 'Hello World' } },
history: []
});

const result3 = await $`echo ${jsonData}`.quiet();
const parsed = JSON.parse(result3.stdout.trim());
console.log('Parsed data (without console noise):', parsed);
console.log('');

console.log('=== Example 4: Chaining with other methods ===');
const result4 = await $`echo "Line 1\nLine 2\nLine 3"`.quiet();
console.log('Lines captured:', result4.stdout.split('\n').length);
console.log('');

console.log('=== All examples completed successfully! ===');
6 changes: 6 additions & 0 deletions src/$.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4137,6 +4137,12 @@ class ProcessRunner extends StreamEmitter {
throw new Error('pipe() destination must be a ProcessRunner or $`command` result');
}

quiet() {
trace('ProcessRunner', () => `quiet() called - disabling console output`);
this.options.mirror = false;
return this;
}

// Promise interface (for await)
then(onFulfilled, onRejected) {
trace('ProcessRunner', () => `then() called | ${JSON.stringify({
Expand Down
150 changes: 150 additions & 0 deletions tests/quiet-method.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { test, expect, describe, beforeEach, afterEach } from 'bun:test';
import './test-helper.mjs'; // Automatically sets up beforeEach/afterEach cleanup
import { $, shell } from '../src/$.mjs';

// Reset shell settings before each test to prevent interference
beforeEach(() => {
shell.errexit(false);
shell.verbose(false);
shell.xtrace(false);
shell.pipefail(false);
shell.nounset(false);
});

// Reset shell settings after each test to prevent interference with other test files
afterEach(() => {
shell.errexit(false);
shell.verbose(false);
shell.xtrace(false);
shell.pipefail(false);
shell.nounset(false);
});

describe('.quiet() method', () => {
test('should suppress console output when .quiet() is called', async () => {
// Capture stdout to verify output is suppressed
let capturedStdout = '';
const originalWrite = process.stdout.write;
process.stdout.write = (chunk) => {
capturedStdout += chunk.toString();
return true;
};

try {
const result = await $`echo "test output"`.quiet();

// Restore original stdout
process.stdout.write = originalWrite;

// The result should still contain the output
expect(result.stdout.trim()).toBe('test output');

// But nothing should have been written to console
expect(capturedStdout).toBe('');
} finally {
// Ensure stdout is restored even if test fails
process.stdout.write = originalWrite;
}
});

test('should work with chaining after .quiet()', async () => {
let capturedStdout = '';
const originalWrite = process.stdout.write;
process.stdout.write = (chunk) => {
capturedStdout += chunk.toString();
return true;
};

try {
const result = await $`echo "chained test"`.quiet();

process.stdout.write = originalWrite;

expect(result.stdout.trim()).toBe('chained test');
expect(capturedStdout).toBe('');
} finally {
process.stdout.write = originalWrite;
}
});

test('should allow normal output without .quiet()', async () => {
// Capture stdout to verify output is shown
let capturedStdout = '';
const originalWrite = process.stdout.write;
process.stdout.write = (chunk) => {
capturedStdout += chunk.toString();
return true;
};

try {
const result = await $`echo "normal output"`;

process.stdout.write = originalWrite;

// The result should contain the output
expect(result.stdout.trim()).toBe('normal output');

// And it should have been written to console (mirrored)
expect(capturedStdout).toContain('normal output');
} finally {
process.stdout.write = originalWrite;
}
});

test('should return ProcessRunner instance for chaining', () => {
const runner = $`echo "test"`.quiet();

// Should return a ProcessRunner that can be awaited
expect(runner).toBeDefined();
expect(typeof runner.then).toBe('function');
expect(typeof runner.quiet).toBe('function');
});

test('should work with stderr output', async () => {
let capturedStderr = '';
const originalWrite = process.stderr.write;
process.stderr.write = (chunk) => {
capturedStderr += chunk.toString();
return true;
};

try {
const result = await $`node -e "console.error('error message')"`.quiet();

process.stderr.write = originalWrite;

// The result should still contain stderr
expect(result.stderr.trim()).toContain('error message');

// But nothing should have been written to console
expect(capturedStderr).toBe('');
} finally {
process.stderr.write = originalWrite;
}
});

test('should work similar to zx quiet() behavior', async () => {
// Test the example from the issue
let capturedStdout = '';
const originalWrite = process.stdout.write;
process.stdout.write = (chunk) => {
capturedStdout += chunk.toString();
return true;
};

try {
// Simulate the gh api command from the issue (using echo as substitute)
const result = await $`echo '{"owner": "test", "files": {}}'`.quiet();

process.stdout.write = originalWrite;

// Should capture the output
expect(result.stdout).toContain('owner');

// But not print to console
expect(capturedStdout).toBe('');
} finally {
process.stdout.write = originalWrite;
}
});
});