Skip to content

Commit 89d5955

Browse files
feat: add welcome message and update check on no arguments in CLI (#89)
Co-authored-by: Felipe Freitag <felipe.freitag@resend.com> Co-authored-by: Felipe Freitag Vargas <ffvargas@gmail.com>
1 parent 6b677b8 commit 89d5955

4 files changed

Lines changed: 92 additions & 0 deletions

File tree

src/cli.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { updateCommand } from './commands/update';
2020
import { webhooksCommand } from './commands/webhooks/index';
2121
import { whoamiCommand } from './commands/whoami';
2222
import { setupCliExitHandler } from './lib/cli-exit';
23+
import { printBannerPlain } from './lib/logo';
2324
import { errorMessage, outputError } from './lib/output';
2425
import { trackCommand } from './lib/telemetry';
2526
import { checkForUpdates } from './lib/update-check';
@@ -116,6 +117,9 @@ ${pc.gray('Examples:')}
116117
`,
117118
)
118119
.action(() => {
120+
if (process.stdout.isTTY) {
121+
printBannerPlain();
122+
}
119123
const opts = program.opts();
120124
if (opts.apiKey) {
121125
outputError(

src/lib/logo.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const LOGO_LINES = [
2+
' ██████╗ ███████╗███████╗███████╗███╗ ██╗██████╗ ',
3+
' ██╔══██╗██╔════╝██╔════╝██╔════╝████╗ ██║██╔══██╗',
4+
' ██████╔╝█████╗ ███████╗█████╗ ██╔██╗ ██║██║ ██║',
5+
' ██╔══██╗██╔══╝ ╚════██║██╔══╝ ██║╚██╗██║██║ ██║',
6+
' ██║ ██║███████╗███████║███████╗██║ ╚████║██████╔╝',
7+
' ╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝╚═╝ ╚═══╝╚═════╝ ',
8+
];
9+
10+
export function printBannerPlain(): void {
11+
process.stdout.write('\n');
12+
for (const line of LOGO_LINES) {
13+
process.stdout.write(`${line}\n`);
14+
}
15+
process.stdout.write('\n');
16+
}

tests/lib/logo.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import {
2+
afterEach,
3+
describe,
4+
expect,
5+
type MockInstance,
6+
test,
7+
vi,
8+
} from 'vitest';
9+
import { printBannerPlain } from '../../src/lib/logo';
10+
11+
describe('printBannerPlain', () => {
12+
let writeSpy: MockInstance;
13+
14+
afterEach(() => {
15+
writeSpy?.mockRestore();
16+
});
17+
18+
test('writes ASCII logo to stdout', () => {
19+
const chunks: string[] = [];
20+
writeSpy = vi
21+
.spyOn(process.stdout, 'write')
22+
.mockImplementation((chunk: unknown) => {
23+
chunks.push(
24+
typeof chunk === 'string'
25+
? chunk
26+
: new TextDecoder().decode(chunk as Uint8Array),
27+
);
28+
return true;
29+
});
30+
31+
printBannerPlain();
32+
33+
const out = chunks.join('');
34+
expect(out).toContain('██████╗');
35+
expect(out).toContain('█');
36+
});
37+
});

tests/welcome.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { type ExecFileSyncOptions, execFileSync } from 'node:child_process';
2+
import { resolve } from 'node:path';
3+
import { describe, expect, test } from 'vitest';
4+
5+
const CLI = resolve(import.meta.dirname, '../src/cli.ts');
6+
7+
const noUpdateEnv = {
8+
...process.env,
9+
RESEND_NO_UPDATE_NOTIFIER: '1',
10+
NO_COLOR: '1',
11+
};
12+
13+
describe('no-args welcome', () => {
14+
test('exits 0 and shows help when invoked with no arguments', () => {
15+
const execOptions: ExecFileSyncOptions = {
16+
encoding: 'utf-8',
17+
timeout: 10_000,
18+
env: noUpdateEnv,
19+
...(process.platform === 'win32' ? { shell: true } : {}),
20+
};
21+
const stdout = execFileSync('npx', ['tsx', CLI], execOptions) as string;
22+
expect(stdout).toContain('Usage: resend');
23+
});
24+
25+
test('skips banner when stdout is not a TTY', () => {
26+
const execOptions: ExecFileSyncOptions = {
27+
encoding: 'utf-8',
28+
timeout: 10_000,
29+
env: noUpdateEnv,
30+
...(process.platform === 'win32' ? { shell: true } : {}),
31+
};
32+
const stdout = execFileSync('npx', ['tsx', CLI], execOptions) as string;
33+
expect(stdout).not.toContain('██████╗');
34+
});
35+
});

0 commit comments

Comments
 (0)