summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/api/child_process.md15
-rw-r--r--lib/internal/child_process.js7
-rw-r--r--test/parallel/test-child-process-spawn-error.js2
-rw-r--r--test/parallel/test-child-process-spawn-event.js27
4 files changed, 51 insertions, 0 deletions
diff --git a/doc/api/child_process.md b/doc/api/child_process.md
index bfe7850ad0..0f78ba9d44 100644
--- a/doc/api/child_process.md
+++ b/doc/api/child_process.md
@@ -1036,6 +1036,21 @@ child process, the `message` argument can contain data that JSON is not able
to represent.
See [Advanced serialization][] for more details.
+### Event: `'spawn'`
+<!-- YAML
+added: REPLACEME
+-->
+
+The `'spawn'` event is emitted once the child process has spawned successfully.
+
+If emitted, the `'spawn'` event comes before all other events and before any
+data is received via `stdout` or `stderr`.
+
+The `'spawn'` event will fire regardless of whether an error occurs **within**
+the spawned process. For example, if `bash some-command` spawns successfully,
+the `'spawn'` event will fire, though `bash` may fail to spawn `some-command`.
+This caveat also applies when using `{ shell: true }`.
+
### `subprocess.channel`
<!-- YAML
added: v7.1.0
diff --git a/lib/internal/child_process.js b/lib/internal/child_process.js
index 092922f956..76e67317b9 100644
--- a/lib/internal/child_process.js
+++ b/lib/internal/child_process.js
@@ -401,6 +401,8 @@ ChildProcess.prototype.spawn = function(options) {
this._handle.close();
this._handle = null;
throw errnoException(err, 'spawn');
+ } else {
+ process.nextTick(onSpawnNT, this);
}
this.pid = this._handle.pid;
@@ -466,6 +468,11 @@ function onErrorNT(self, err) {
}
+function onSpawnNT(self) {
+ self.emit('spawn');
+}
+
+
ChildProcess.prototype.kill = function(sig) {
const signal = sig === 0 ? sig :
diff --git a/test/parallel/test-child-process-spawn-error.js b/test/parallel/test-child-process-spawn-error.js
index d6560ee9cc..a3464a505d 100644
--- a/test/parallel/test-child-process-spawn-error.js
+++ b/test/parallel/test-child-process-spawn-error.js
@@ -41,6 +41,8 @@ assert.strictEqual(enoentChild.stdio[0], enoentChild.stdin);
assert.strictEqual(enoentChild.stdio[1], enoentChild.stdout);
assert.strictEqual(enoentChild.stdio[2], enoentChild.stderr);
+enoentChild.on('spawn', common.mustNotCall());
+
enoentChild.on('error', common.mustCall(function(err) {
assert.strictEqual(err.code, 'ENOENT');
assert.strictEqual(getSystemErrorName(err.errno), 'ENOENT');
diff --git a/test/parallel/test-child-process-spawn-event.js b/test/parallel/test-child-process-spawn-event.js
new file mode 100644
index 0000000000..c025d86286
--- /dev/null
+++ b/test/parallel/test-child-process-spawn-event.js
@@ -0,0 +1,27 @@
+'use strict';
+const common = require('../common');
+const spawn = require('child_process').spawn;
+const assert = require('assert');
+
+const subprocess = spawn('echo', ['ok']);
+
+let didSpawn = false;
+subprocess.on('spawn', function() {
+ didSpawn = true;
+});
+function mustCallAfterSpawn() {
+ return common.mustCall(function() {
+ assert.ok(didSpawn);
+ });
+}
+
+subprocess.on('error', common.mustNotCall());
+subprocess.on('spawn', common.mustCall());
+subprocess.stdout.on('data', mustCallAfterSpawn());
+subprocess.stdout.on('end', mustCallAfterSpawn());
+subprocess.stdout.on('close', mustCallAfterSpawn());
+subprocess.stderr.on('data', common.mustNotCall());
+subprocess.stderr.on('end', mustCallAfterSpawn());
+subprocess.stderr.on('close', mustCallAfterSpawn());
+subprocess.on('exit', mustCallAfterSpawn());
+subprocess.on('close', mustCallAfterSpawn());