Repository: sindresorhus/execa
Pull request #435 (2020-09-14 10:47:00)

Remove `--inspect` & `--inspect-brk` from `execArgv`
When using `execa.node` and the parent process has --inspect or --inspect-brk defined, it will run the child process with those options as well. It will throw an error that the inspector already is running on port 9229.

I've added a filter for those options on the default execArgv value. I tried to test it but I'm not sure this is the correct way to do so.


Patch:
diff --git a/.travis.yml b/.travis.yml
index 6a9003f..f49c44e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,6 +3,7 @@ os:
   - osx
 language: node_js
 node_js:
+  - '15'
   - '14'
   - '12'
   - '10'
diff --git a/index.d.ts b/index.d.ts
index 76a20bf..8e1050a 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -425,7 +425,7 @@ declare const execa: {
 
 	@example
 	```
-	import execa from 'execa';
+	import execa = require('execa');
 
 	(async () => {
 		const {stdout} = await execa('echo', ['unicorns']);
@@ -499,7 +499,7 @@ declare const execa: {
 
 	@example
 	```
-	import execa from 'execa';
+	import execa = require('execa');
 
 	(async () => {
 		const {stdout} = await execa.command('echo unicorns');
diff --git a/index.js b/index.js
index 4835038..d941253 100644
--- a/index.js
+++ b/index.js
@@ -234,8 +234,12 @@ module.exports.node = (scriptPath, args, options = {}) => {
 	}
 
 	const stdio = normalizeStdio.node(options);
+	const defaultExecArgv = process.execArgv.filter(arg => !arg.startsWith('--inspect'));
 
-	const {nodePath = process.execPath, nodeOptions = process.execArgv} = options;
+	const {
+		nodePath = process.execPath,
+		nodeOptions = defaultExecArgv
+	} = options;
 
 	return execa(
 		nodePath,
diff --git a/lib/command.js b/lib/command.js
index 871a314..190ce16 100644
--- a/lib/command.js
+++ b/lib/command.js
@@ -9,27 +9,21 @@ const joinCommand = (file, args = []) => {
 	return [file, ...args].join(' ');
 };
 
-// Allow spaces to be escaped by a backslash if not meant as a delimiter
-const handleEscaping = (tokens, token, index) => {
-	if (index === 0) {
-		return [token];
-	}
-
-	const previousToken = tokens[tokens.length - 1];
-
-	if (previousToken.endsWith('\\')) {
-		return [...tokens.slice(0, -1), `${previousToken.slice(0, -1)} ${token}`];
-	}
-
-	return [...tokens, token];
-};
-
 // Handle `execa.command()`
 const parseCommand = command => {
-	return command
-		.trim()
-		.split(SPACES_REGEXP)
-		.reduce(handleEscaping, []);
+	const tokens = [];
+	for (const token of command.trim().split(SPACES_REGEXP)) {
+		// Allow spaces to be escaped by a backslash if not meant as a delimiter
+		const previousToken = tokens[tokens.length - 1];
+		if (previousToken && previousToken.endsWith('\\')) {
+			// Merge previous token with current one
+			tokens[tokens.length - 1] = `${previousToken.slice(0, -1)} ${token}`;
+		} else {
+			tokens.push(token);
+		}
+	}
+
+	return tokens;
 };
 
 module.exports = {
diff --git a/readme.md b/readme.md
index 132423d..5e3606d 100644
--- a/readme.md
+++ b/readme.md
@@ -5,6 +5,12 @@
 
 > Process execution for humans
 
+---
+
+Netlify is looking for a senior/expert Node.js backend engineer to join a fully remote, international, diverse (44% women and non-binary) and friendly team. This is for the team where one of the co-maintainers [@ehmicky](https://github.com/ehmicky) is working. The job description is [here](https://boards.greenhouse.io/netlify/jobs/4832483002).
+
+---
+
 ## Why
 
 This package improves [`child_process`](https://nodejs.org/api/child_process.html) methods with:


Test patch:
diff --git a/test/node.js b/test/node.js
index 838ab5d..51ca557 100644
--- a/test/node.js
+++ b/test/node.js
@@ -5,6 +5,23 @@ import execa from '..';
 
 process.env.PATH = path.join(__dirname, 'fixtures') + path.delimiter + process.env.PATH;
 
+async function inspectMacro(t, input) {
+	const originalArgv = process.execArgv;
+	process.execArgv = [input, '-e'];
+	try {
+		const subprocess = execa.node('console.log("foo")', {
+			reject: false
+		});
+
+		const {stdout, stderr} = await subprocess;
+
+		t.is(stdout, 'foo');
+		t.is(stderr, '');
+	} finally {
+		process.execArgv = originalArgv;
+	}
+}
+
 test('node()', async t => {
 	const {exitCode} = await execa.node('test/fixtures/noop');
 	t.is(exitCode, 0);
@@ -37,6 +54,43 @@ test('node pass on nodeOptions', async t => {
 	t.is(stdout, 'foo');
 });
 
+test.serial(
+	'node removes --inspect from nodeOptions when defined by parent process',
+	inspectMacro,
+	'--inspect'
+);
+
+test.serial(
+	'node removes --inspect=9222 from nodeOptions when defined by parent process',
+	inspectMacro,
+	'--inspect=9222'
+);
+
+test.serial(
+	'node removes --inspect-brk from nodeOptions when defined by parent process',
+	inspectMacro,
+	'--inspect-brk'
+);
+
+test.serial(
+	'node removes --inspect-brk=9222 from nodeOptions when defined by parent process',
+	inspectMacro,
+	'--inspect-brk=9222'
+);
+
+test.serial(
+	'node should not remove --inspect when passed through nodeOptions',
+	async t => {
+		const {stdout, stderr} = await execa.node('console.log("foo")', {
+			reject: false,
+			nodeOptions: ['--inspect', '-e']
+		});
+
+		t.is(stdout, 'foo');
+		t.true(stderr.includes('Debugger listening'));
+	}
+);
+
 test('node\'s forked script has a communication channel', async t => {
 	const subprocess = execa.node('test/fixtures/send');
 	subprocess.send('ping');