diff --git a/index.d.ts b/index.d.ts index fdf9de1c3..bcf04f922 100644 --- a/index.d.ts +++ b/index.d.ts @@ -33,9 +33,11 @@ export interface CommonOptions { /** Preferred path to find locally installed binaries in (use with `preferLocal`). + Using a `URL` is only supported in Node.js `14.18.0`, `16.14.0` or above. + @default process.cwd() */ - readonly localDir?: string; + readonly localDir?: string | URL; /** Path to the Node.js executable to use in child processes. @@ -111,9 +113,11 @@ export interface CommonOptions { /** Current working directory of the child process. + Using a `URL` is only supported in Node.js `14.18.0`, `16.14.0` or above. + @default process.cwd() */ - readonly cwd?: string; + readonly cwd?: string | URL; /** Environment key-value pairs. Extends automatically from `process.env`. Set `extendEnv` to `false` if you don't want this. diff --git a/index.test-d.ts b/index.test-d.ts index 5e06366a5..e8d0a0d11 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -90,6 +90,7 @@ try { execa('unicorns', {cleanup: false}); execa('unicorns', {preferLocal: false}); execa('unicorns', {localDir: '.'}); +execa('unicorns', {localDir: new URL('file:///test')}); execa('unicorns', {execPath: '/path'}); execa('unicorns', {buffer: false}); execa('unicorns', {input: ''}); @@ -121,6 +122,7 @@ execa('unicorns', {reject: false}); execa('unicorns', {stripFinalNewline: false}); execa('unicorns', {extendEnv: false}); execa('unicorns', {cwd: '.'}); +execa('unicorns', {cwd: new URL('file:///test')}); // eslint-disable-next-line @typescript-eslint/naming-convention execa('unicorns', {env: {PATH: ''}}); execa('unicorns', {argv0: ''}); diff --git a/package.json b/package.json index 360439757..187138879 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "get-node": "^12.0.0", "is-running": "^2.1.0", "p-event": "^5.0.1", + "semver": "^7.3.5", "tempfile": "^4.0.0", "tsd": "^0.18.0", "xo": "^0.46.4" diff --git a/readme.md b/readme.md index a9db621b9..c1aa8ea30 100644 --- a/readme.md +++ b/readme.md @@ -357,11 +357,13 @@ If you `$ npm install foo`, you can then `execa('foo')`. #### localDir -Type: `string`\ +Type: `string | URL`\ Default: `process.cwd()` Preferred path to find locally installed binaries in (use with `preferLocal`). +Using a `URL` is only supported in Node.js `14.18.0`, `16.14.0` or above. + #### execPath Type: `string`\ @@ -446,11 +448,13 @@ Execa also accepts the below options which are the same as the options for [`chi #### cwd -Type: `string`\ +Type: `string | URL`\ Default: `process.cwd()` Current working directory of the child process. +Using a `URL` is only supported in Node.js `14.18.0`, `16.14.0` or above. + #### env Type: `object`\ diff --git a/test/test.js b/test/test.js index 23449f52b..d24607631 100644 --- a/test/test.js +++ b/test/test.js @@ -1,9 +1,10 @@ import path from 'node:path'; import process from 'node:process'; -import {fileURLToPath} from 'node:url'; +import {fileURLToPath, pathToFileURL} from 'node:url'; import test from 'ava'; import isRunning from 'is-running'; import getNode from 'get-node'; +import semver from 'semver'; import {execa, execaSync} from '../index.js'; process.env.PATH = fileURLToPath(new URL('./fixtures', import.meta.url)) + path.delimiter + process.env.PATH; @@ -198,6 +199,28 @@ test('do not extend environment with `extendEnv: false`', async t => { t.deepEqual(stdout.split('\n'), ['undefined', 'bar']); }); +test('can use `options.cwd` as a string', async t => { + const cwd = '/'; + const {stdout} = await execa('node', ['-p', 'process.cwd()'], {cwd}); + t.is(path.toNamespacedPath(stdout), path.toNamespacedPath(cwd)); +}); + +if (semver.satisfies(process.version, '^14.18.0 || >=16.4.0')) { + test('localDir option can be a URL', async t => { + const command = process.platform === 'win32' ? 'echo %PATH%' : 'echo $PATH'; + const {stdout} = await execa(command, {shell: true, preferLocal: true, localDir: pathToFileURL('/test')}); + const envPaths = stdout.split(path.delimiter); + t.true(envPaths.some(envPath => envPath.endsWith('.bin'))); + }); + + test('can use `options.cwd` as a URL', async t => { + const cwd = '/'; + const cwdUrl = pathToFileURL(cwd); + const {stdout} = await execa('node', ['-p', 'process.cwd()'], {cwd: cwdUrl}); + t.is(path.toNamespacedPath(stdout), path.toNamespacedPath(cwd)); + }); +} + test('can use `options.shell: true`', async t => { const {stdout} = await execa('node test/fixtures/noop.js foo', {shell: true}); t.is(stdout, 'foo');