Repository: sindresorhus/execa
Pull request #314 (2019-06-24 11:49:55)

Change default value of the `preferLocal` option to `false`
This changes the default value of the `preferLocal` option to `false`.

This option causes several issues for some users: #196, #153.

ehmicky: CI tests are working now.
sindresorhus: For posterity, here's my reasoning for doing this change:
- Most cases don't actually spawn local binaries from `node_modules`.
    I realized that most of the cases are global binaries and local ones with `{cwd: __dirname}`.
- The few times you actually need it, it might be more readable to make it explicit.
- It causes surprising behavior then because:
    1. Yarn mocks with the PATH too.
    2. Some Node.js installation has a `node_modules` directory or `npm` directory that will be used instead of what the user actually wanted. This can happen silently and cause confusion.

So basically, I think we should default to the least surprising behavior.

Patch:
diff --git a/index.d.ts b/index.d.ts
index 526593f..76e3207 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -27,7 +27,7 @@ declare namespace execa {
 
 		If you `$ npm install foo`, you can then `execa('foo')`.
 
-		@default true
+		@default false
 		*/
 		readonly preferLocal?: boolean;
 
diff --git a/index.js b/index.js
index ab1240d..d0ad9d4 100644
--- a/index.js
+++ b/index.js
@@ -28,7 +28,7 @@ const handleArgs = (file, args, options = {}) => {
 		maxBuffer: DEFAULT_MAX_BUFFER,
 		buffer: true,
 		stripFinalNewline: true,
-		preferLocal: true,
+		preferLocal: false,
 		localDir: options.cwd || process.cwd(),
 		encoding: 'utf8',
 		reject: true,
diff --git a/readme.md b/readme.md
index 1416c2f..4359557 100644
--- a/readme.md
+++ b/readme.md
@@ -281,7 +281,7 @@ Kill the spawned process when the parent process exits unless either:
 #### preferLocal
 
 Type: `boolean`<br>
-Default: `true`
+Default: `false`
 
 Prefer locally installed binaries when looking for a binary to execute.<br>
 If you `$ npm install foo`, you can then `execa('foo')`.


Test patch:
diff --git a/test/test.js b/test/test.js
index 6fa7e84..1225978 100644
--- a/test/test.js
+++ b/test/test.js
@@ -12,6 +12,7 @@ process.env.PATH = path.join(__dirname, 'fixtures') + path.delimiter + process.e
 process.env.FOO = 'foo';
 
 const TIMEOUT_REGEXP = /timed out after/;
+const ENOENT_REGEXP = process.platform === 'win32' ? /failed with exit code 1/ : /spawn.* ENOENT/;
 
 test('execa()', async t => {
 	const {stdout} = await execa('noop', ['foo']);
@@ -75,7 +76,7 @@ test('execa.sync()', t => {
 test('execa.sync() throws error if written to stderr', t => {
 	t.throws(() => {
 		execa.sync('foo');
-	}, process.platform === 'win32' ? /failed with exit code 1/ : /spawnSync foo ENOENT/);
+	}, ENOENT_REGEXP);
 });
 
 test('skip throwing when using reject option', async t => {
@@ -181,15 +182,21 @@ test('stripFinalNewline in sync mode on failure', t => {
 	t.is(stderr, 'foo');
 });
 
-test('preferLocal option', async t => {
-	await execa('ava', ['--version'], {env: {PATH: ''}});
-	const errorRegExp = process.platform === 'win32' ? /failed with exit code 1/ : /spawn ava ENOENT/;
-	await t.throwsAsync(execa('ava', ['--version'], {preferLocal: false, env: {PATH: ''}}), errorRegExp);
+test('preferLocal: true', async t => {
+	await t.notThrowsAsync(execa('ava', ['--version'], {preferLocal: true, env: {PATH: ''}}));
+});
+
+test('preferLocal: false', async t => {
+	await t.throwsAsync(execa('ava', ['--version'], {preferLocal: false, env: {PATH: ''}}), ENOENT_REGEXP);
+});
+
+test('preferLocal: undefined', async t => {
+	await t.throwsAsync(execa('ava', ['--version'], {env: {PATH: ''}}), ENOENT_REGEXP);
 });
 
 test('localDir option', async t => {
 	const command = process.platform === 'win32' ? 'echo %PATH%' : 'echo $PATH';
-	const {stdout} = await execa(command, {shell: true, localDir: '/test'});
+	const {stdout} = await execa(command, {shell: true, preferLocal: true, localDir: '/test'});
 	const envPaths = stdout.split(path.delimiter).map(envPath =>
 		envPath.replace(/\\/g, '/').replace(/^[^/]+/, '')
 	);