diff --git a/lib/index.js b/lib/index.js index e147cb8..aa7b55d 100644 --- a/lib/index.js +++ b/lib/index.js @@ -131,9 +131,19 @@ const open = (_args, opts = {}, extra = {}) => { let platform = process.platform // process.platform === 'linux' may actually indicate WSL, if that's the case - // we want to treat things as win32 anyway so the host can open the argument + // open the argument with sensible-browser which is pre-installed + // In WSL, set the default browser using, for example, + // export BROWSER="/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe" + // or + // export BROWSER="/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe" + // To permanently set the default browser, add the appropriate entry to your shell's + // RC file, e.g. .bashrc or .zshrc. if (platform === 'linux' && os.release().toLowerCase().includes('microsoft')) { - platform = 'win32' + platform = 'wsl' + if (!process.env.BROWSER) { + return Promise.reject( + new Error('Set the BROWSER environment variable to your desired browser.')) + } } let command = options.command @@ -146,6 +156,8 @@ const open = (_args, opts = {}, extra = {}) => { // accidentally interpret the first arg as the title, we stick an empty // string immediately after the start command command = 'start ""' + } else if (platform === 'wsl') { + command = 'sensible-browser' } else if (platform === 'darwin') { command = 'open' } else { diff --git a/test/open.js b/test/open.js index 674e721..a5fa2ae 100644 --- a/test/open.js +++ b/test/open.js @@ -2,6 +2,7 @@ const spawk = require('spawk') const t = require('tap') +const os = require('node:os') const promiseSpawn = require('../lib/index.js') @@ -10,6 +11,8 @@ t.afterEach(() => { spawk.clean() }) +const isWSL = process.platform === 'linux' && os.release().toLowerCase().includes('microsoft') + t.test('process.platform === win32', (t) => { const comSpec = process.env.ComSpec const platformDesc = Object.getOwnPropertyDescriptor(process, 'platform') @@ -118,7 +121,8 @@ t.test('process.platform === linux', (t) => { Object.defineProperty(process, 'platform', platformDesc) }) - t.test('uses xdg-open in a shell', async (t) => { + // xdg-open is not installed in WSL by default + t.test('uses xdg-open in a shell', { skip: isWSL }, async (t) => { const proc = spawk.spawn('sh', ['-c', 'xdg-open https://google.com'], { shell: false }) const result = await promiseSpawn.open('https://google.com') @@ -130,7 +134,8 @@ t.test('process.platform === linux', (t) => { t.ok(proc.called) }) - t.test('ignores shell = false', async (t) => { + // xdg-open is not installed in WSL by default + t.test('ignores shell = false', { skip: isWSL }, async (t) => { const proc = spawk.spawn('sh', ['-c', 'xdg-open https://google.com'], { shell: false }) const result = await promiseSpawn.open('https://google.com', { shell: false }) @@ -154,22 +159,16 @@ t.test('process.platform === linux', (t) => { t.ok(proc.called) }) - t.test('when os.release() includes Microsoft treats as win32', async (t) => { - const comSpec = process.env.ComSpec - process.env.ComSpec = 'C:\\Windows\\System32\\cmd.exe' - t.teardown(() => { - process.env.ComSPec = comSpec - }) - + t.test('when os.release() includes Microsoft treats as WSL', async (t) => { const promiseSpawnMock = t.mock('../lib/index.js', { os: { release: () => 'Microsoft', }, }) + const browser = process.env.BROWSER + process.env.BROWSER = '/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe' - const proc = spawk.spawn('C:\\Windows\\System32\\cmd.exe', - ['/d', '/s', '/c', 'start "" https://google.com'], - { shell: false }) + const proc = spawk.spawn('sh', ['-c', 'sensible-browser https://google.com'], { shell: false }) const result = await promiseSpawnMock.open('https://google.com') t.hasStrict(result, { @@ -177,25 +176,23 @@ t.test('process.platform === linux', (t) => { signal: undefined, }) - t.ok(proc.called) - }) - - t.test('when os.release() includes microsoft treats as win32', async (t) => { - const comSpec = process.env.ComSpec - process.env.ComSpec = 'C:\\Windows\\System32\\cmd.exe' t.teardown(() => { - process.env.ComSPec = comSpec + process.env.BROWSER = browser }) + t.ok(proc.called) + }) + + t.test('when os.release() includes microsoft treats as WSL', async (t) => { const promiseSpawnMock = t.mock('../lib/index.js', { os: { release: () => 'microsoft', }, }) + const browser = process.env.BROWSER + process.env.BROWSER = '/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe' - const proc = spawk.spawn('C:\\Windows\\System32\\cmd.exe', - ['/d', '/s', '/c', 'start "" https://google.com'], - { shell: false }) + const proc = spawk.spawn('sh', ['-c', 'sensible-browser https://google.com'], { shell: false }) const result = await promiseSpawnMock.open('https://google.com') t.hasStrict(result, { @@ -203,9 +200,35 @@ t.test('process.platform === linux', (t) => { signal: undefined, }) + t.teardown(() => { + process.env.BROWSER = browser + }) + t.ok(proc.called) }) + t.test('fails on WSL if BROWSER is not set', async (t) => { + const promiseSpawnMock = t.mock('../lib/index.js', { + os: { + release: () => 'microsoft', + }, + }) + const browser = process.env.BROWSER + delete process.env.BROWSER + + const proc = spawk.spawn('sh', ['-c', 'sensible-browser https://google.com'], { shell: false }) + + await t.rejects(promiseSpawnMock.open('https://google.com'), { + message: 'Set the BROWSER environment variable to your desired browser.', + }) + + t.teardown(() => { + process.env.BROWSER = browser + }) + + t.notOk(proc.called) + }) + t.end() })