Skip to content

Commit 24dc3b6

Browse files
committed
fix: removes open package and rolls open-in-browser util
1 parent 243548d commit 24dc3b6

File tree

6 files changed

+89
-161
lines changed

6 files changed

+89
-161
lines changed

package-lock.json

Lines changed: 0 additions & 149 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,11 @@
5252
"@oclif/plugin-update": "^4.7.14",
5353
"@oclif/table": "^0.5.1",
5454
"date-fns": "^4.1.0",
55-
"ora": "^9.0.0",
5655
"graphql": "^16.11.0",
5756
"inquirer": "^12.11.0",
5857
"keytar": "^7.9.0",
5958
"node-machine-id": "^1.1.12",
60-
"open": "^10.2.0",
59+
"ora": "^9.0.0",
6160
"packageurl-js": "^2.0.1",
6261
"terminal-link": "^5.0.0",
6362
"update-notifier": "^7.3.1"
@@ -123,4 +122,4 @@
123122
}
124123
},
125124
"types": "dist/index.d.ts"
126-
}
125+
}

src/commands/auth/login.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import http from 'node:http';
33
import { URL } from 'node:url';
44
import { Command } from '@oclif/core';
55
import inquirer from 'inquirer';
6-
import open from 'open';
76
import { persistTokenResponse } from '../../service/auth.svc.ts';
87
import { getClientId, getRealmUrl } from '../../service/auth-config.svc.ts';
98
import type { TokenResponse } from '../../types/auth.ts';
9+
import { openInBrowser } from '../../utils/open-in-browser.ts';
1010

1111
export default class AuthLogin extends Command {
1212
static description = 'OAuth CLI login';
@@ -94,7 +94,7 @@ export default class AuthLogin extends Command {
9494
});
9595

9696
try {
97-
await open(authUrl);
97+
await openInBrowser(authUrl);
9898
} catch (err) {
9999
this.warn(
100100
`Failed to open browser automatically. Please open this URL manually:\n${authUrl}\n${err instanceof Error ? err.message : err}`,

src/utils/open-in-browser.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { exec } from "node:child_process";
2+
import { platform } from "node:os";
3+
4+
export function openInBrowser(url: string): Promise<void> {
5+
return new Promise((resolve, reject) => {
6+
const escapedUrl = `"${url.replace(/"/g, '\\"')}"`;
7+
8+
const command = (() => {
9+
const plat = platform();
10+
11+
if (plat === "darwin") return `open ${escapedUrl}`; // macOS
12+
if (plat === "win32") return `start "" ${escapedUrl}`; // Windows
13+
return `xdg-open ${escapedUrl}`; // Linux
14+
})();
15+
16+
exec(command, (err) => {
17+
if (err) reject(new Error(`Failed to open browser: ${err.message}`));
18+
else resolve();
19+
});
20+
});
21+
}

test/commands/auth/login.test.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
import type { ChildProcess } from 'node:child_process';
2-
31
import type { Config } from '@oclif/core';
42
import inquirer from 'inquirer';
5-
import open from 'open';
63
import { type Mock, type MockedFunction, vi } from 'vitest';
74
import AuthLogin from '../../../src/commands/auth/login.ts';
85
import { persistTokenResponse } from '../../../src/service/auth.svc.ts';
6+
import { openInBrowser } from '../../../src/utils/open-in-browser.ts';
97

108
type ServerRequest = { url?: string };
119
type ServerResponse = { writeHead: Mock; end: Mock };
@@ -72,9 +70,9 @@ vi.mock('http', () => ({
7270
},
7371
}));
7472

75-
vi.mock('open', () => ({
73+
vi.mock('../../../src/utils/open-in-browser.ts', () => ({
7674
__esModule: true,
77-
default: vi.fn(),
75+
openInBrowser: vi.fn(),
7876
}));
7977

8078
vi.mock('inquirer', () => ({
@@ -89,7 +87,7 @@ vi.mock('../../../src/service/auth.svc.ts', () => ({
8987
persistTokenResponse: vi.fn().mockResolvedValue(undefined),
9088
}));
9189

92-
const openMock = vi.mocked(open) as MockedFunction<typeof open>;
90+
const openMock = vi.mocked(openInBrowser) as MockedFunction<typeof openInBrowser>;
9391
const promptMock = vi.mocked(inquirer.prompt) as MockedFunction<typeof inquirer.prompt>;
9492
const persistTokenResponseMock = vi.mocked(persistTokenResponse);
9593

@@ -126,7 +124,7 @@ const createCommand = (port: number) => {
126124
describe('AuthLogin', () => {
127125
beforeEach(() => {
128126
promptMock.mockResolvedValue({ confirm: true });
129-
openMock.mockResolvedValue({} as ChildProcess);
127+
openMock.mockResolvedValue(undefined);
130128
});
131129

132130
afterEach(() => {

test/utils/open-in-browser.test.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import type { ExecException } from 'node:child_process';
2+
3+
import { beforeEach, describe, expect, it, vi } from 'vitest';
4+
5+
const execMock = vi.fn<[string, ((err: ExecException | null) => void)?], void>();
6+
const platformMock = vi.fn<[], NodeJS.Platform>();
7+
8+
vi.mock('node:child_process', () => ({
9+
exec: execMock,
10+
}));
11+
12+
vi.mock('node:os', () => ({
13+
platform: platformMock,
14+
}));
15+
16+
describe('openInBrowser', () => {
17+
let openInBrowser: (url: string) => Promise<void>;
18+
19+
beforeEach(async () => {
20+
execMock.mockReset();
21+
platformMock.mockReset();
22+
vi.resetModules();
23+
({ openInBrowser } = await import('../../src/utils/open-in-browser.ts'));
24+
});
25+
26+
it('uses macOS open command when platform is darwin', async () => {
27+
platformMock.mockReturnValue('darwin');
28+
execMock.mockImplementation((_cmd, cb) => cb?.(null));
29+
30+
await expect(openInBrowser('https://example.com')).resolves.toBeUndefined();
31+
32+
expect(execMock).toHaveBeenCalledWith('open "https://example.com"', expect.any(Function));
33+
});
34+
35+
it('uses Windows start command when platform is win32', async () => {
36+
platformMock.mockReturnValue('win32');
37+
execMock.mockImplementation((_cmd, cb) => cb?.(null));
38+
39+
await expect(openInBrowser('https://example.com')).resolves.toBeUndefined();
40+
41+
expect(execMock).toHaveBeenCalledWith('start "" "https://example.com"', expect.any(Function));
42+
});
43+
44+
it('falls back to xdg-open on other platforms', async () => {
45+
platformMock.mockReturnValue('linux');
46+
execMock.mockImplementation((_cmd, cb) => cb?.(null));
47+
48+
await expect(openInBrowser('https://example.com')).resolves.toBeUndefined();
49+
50+
expect(execMock).toHaveBeenCalledWith('xdg-open "https://example.com"', expect.any(Function));
51+
});
52+
53+
it('rejects when the underlying command fails', async () => {
54+
platformMock.mockReturnValue('darwin');
55+
execMock.mockImplementation((_cmd, cb) => cb?.(new Error('boom')));
56+
57+
await expect(openInBrowser('https://example.com')).rejects.toThrow('Failed to open browser: boom');
58+
});
59+
});

0 commit comments

Comments
 (0)