Skip to content
This repository was archived by the owner on Jul 30, 2024. It is now read-only.

Commit 2d1c5b9

Browse files
author
TATSUNO Yasuhiro
authored
Merge pull request #7 from exoego/fix-child-process
Fix child process
2 parents f0da3ce + e0d27ad commit 2d1c5b9

File tree

6 files changed

+119
-92
lines changed

6 files changed

+119
-92
lines changed
Lines changed: 43 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
package io.scalajs.nodejs
22
package child_process
33

4-
import io.scalajs.RawOptions
4+
import io.scalajs.nodejs
55
import io.scalajs.nodejs.events.IEventEmitter
6-
import io.scalajs.nodejs.buffer.Buffer
76

87
import scala.scalajs.js
98
import scala.scalajs.js.annotation.JSImport
@@ -12,91 +11,56 @@ import scala.scalajs.js.|
1211
/**
1312
* The child_process module provides the ability to spawn child processes in a manner that is similar,
1413
* but not identical, to popen(3). This capability is primarily provided by the child_process.spawn() function.
14+
*
1515
* @see https://nodejs.org/api/child_process.html
16-
1716
*/
1817
@js.native
1918
trait ChildProcess extends IEventEmitter {
19+
def kill(signal: js.UndefOr[Int | String] = js.native): Unit = js.native
20+
}
2021

21-
/**
22-
* Spawns a shell then executes the command within that shell, buffering any generated output.
23-
* @param command <String> The command to run, with space-separated arguments
24-
* @param options the execution [[ExecOptions options]]
25-
* @return the [[ChildProcess]]
26-
* @example {{{ child_process.exec(command[, options][, callback]) }}}
27-
*/
28-
def exec(command: String, options: ExecOptions | RawOptions): this.type = js.native
22+
/**
23+
* @see https://nodejs.org/api/child_process.html
24+
*/
25+
@JSImport("child_process", JSImport.Namespace)
26+
@js.native
27+
object ChildProcess extends scala.scalajs.js.Object {
28+
def exec(
29+
command: String,
30+
options: js.UndefOr[ExecOptions | io.scalajs.RawOptions] = js.undefined,
31+
callback: js.Function3[
32+
nodejs.Error,
33+
Output,
34+
Output,
35+
Any
36+
]
37+
): ChildProcess = js.native
2938

30-
/**
31-
* Spawns a shell then executes the command within that shell, buffering any generated output.
32-
* @param command <String> The command to run, with space-separated arguments
33-
* @param callback called with the output when process terminates
34-
* @return the [[ChildProcess]]
35-
* @example {{{ child_process.exec(command[, options][, callback]) }}}
36-
*/
37-
def exec(command: String, callback: js.Function3[Error, Buffer | String, Buffer | String, Any]): this.type =
38-
js.native
39+
def execSync(
40+
command: String,
41+
options: ExecOptions | io.scalajs.RawOptions = js.native
42+
): Output = js.native
3943

40-
/**
41-
* Spawns a shell then executes the command within that shell, buffering any generated output.
42-
* @param command <String> The command to run, with space-separated arguments
43-
* @param options the execution [[ExecOptions options]]
44-
* @param callback called with the output when process terminates
45-
* @return the [[ChildProcess]]
46-
* @example {{{ child_process.exec(command[, options][, callback]) }}}
47-
*/
48-
def exec(command: String,
49-
options: ExecOptions | RawOptions,
50-
callback: js.Function3[Error, Buffer | String, Buffer | String, Any]): this.type = js.native
44+
def fork(
45+
modulePath: String,
46+
args: js.Array[String] = js.native,
47+
options: ForkOptions | io.scalajs.RawOptions = js.native
48+
): ChildProcess = js.native
5149

52-
/**
53-
* The child_process.fork() method is a special case of child_process.spawn() used specifically to spawn new
54-
* Node.js processes. Like child_process.spawn(), a ChildProcess object is returned. The returned ChildProcess
55-
* will have an additional communication channel built-in that allows messages to be passed back and forth between
56-
* the parent and child. See child.send() for details.
57-
*
58-
* It is important to keep in mind that spawned Node.js child processes are independent of the parent with exception
59-
* of the IPC communication channel that is established between the two. Each process has its own memory, with their
60-
* own V8 instances. Because of the additional resource allocations required, spawning a large number of child Node.js
61-
* processes is not recommended.
62-
*
63-
* By default, child_process.fork() will spawn new Node.js instances using the process.execPath of the parent process.
64-
* The execPath property in the options object allows for an alternative execution path to be used.
65-
*
66-
* Node.js processes launched with a custom execPath will communicate with the parent process using the
67-
* file descriptor (fd) identified using the environment variable NODE_CHANNEL_FD on the child process.
68-
* The input and output on this fd is expected to be line delimited JSON objects.
69-
*
70-
* Note: Unlike the fork(2) POSIX system call, child_process.fork() does not clone the current process.
71-
* @param modulePath <String> The module to run in the child
72-
* @param args <Array> List of string arguments
73-
* @param options the fork [[ForkOptions options]]
74-
* @return the [[ChildProcess]]
75-
* @example {{{ child_process.fork(modulePath[, args][, options]) }}}
76-
*/
77-
def fork(modulePath: String,
78-
args: js.Array[String] = js.native,
79-
options: ForkOptions | RawOptions = js.native): this.type = js.native
50+
def spawn(
51+
command: String,
52+
args: js.Array[String] = js.native,
53+
options: SpawnOptions | io.scalajs.RawOptions = js.native
54+
): ChildProcess = js.native
8055

81-
/**
82-
* The child_process.spawn() method spawns a new process using the given command, with command line arguments
83-
* in args. If omitted, args defaults to an empty array.
84-
* @param command <String> The command to run
85-
* @param args <Array> List of string arguments
86-
* @param options the spawn [[SpawnOptions options]]
87-
* @return the [[ChildProcess]]
88-
* @example {{{ child_process.spawn(command[, args][, options]) }}}
89-
*/
90-
def spawn(command: String,
91-
args: js.Array[String] = js.native,
92-
options: SpawnOptions | RawOptions = js.native): this.type = js.native
56+
def spawnSync(
57+
command: String,
58+
args: js.Array[String] = js.native,
59+
options: SpawnOptions | io.scalajs.RawOptions = js.native
60+
): SpawnResult = js.native
9361

62+
def spawnSync(
63+
command: String,
64+
options: SpawnOptions | io.scalajs.RawOptions
65+
): SpawnResult = js.native
9466
}
95-
96-
/**
97-
* ChildProcess Singleton
98-
99-
*/
100-
@js.native
101-
@JSImport("child_process", JSImport.Namespace)
102-
object ChildProcess extends ChildProcess
Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
package io.scalajs.nodejs.child_process
22

3-
import io.scalajs.JsNumber
4-
53
import scala.scalajs.js
6-
74
import scala.scalajs.js.|
85

96
/**
107
* Spawn Options
8+
*
119
* @param cwd <String> Current working directory of the child process
1210
* @param env <Object> Environment key-value pairs
1311
* @param argv0 <String> Explicitly set the value of argv[0] sent to the child process.
@@ -22,12 +20,15 @@ import scala.scalajs.js.|
2220
* The shell should understand the -c switch on UNIX, or /d /s /c on Windows. Defaults to false (no shell).
2321
2422
*/
25-
class SpawnOptions(val cwd: js.UndefOr[String] = js.undefined,
26-
val env: js.Any = js.undefined,
27-
val argv0: js.UndefOr[String] = js.undefined,
28-
val stdio: js.UndefOr[Array[String] | String] = js.undefined,
29-
val detached: js.UndefOr[Boolean] = js.undefined,
30-
val uid: js.UndefOr[JsNumber] = js.undefined,
31-
val gid: js.UndefOr[JsNumber] = js.undefined,
32-
val shell: js.UndefOr[Boolean | String] = js.undefined)
33-
extends js.Object
23+
class SpawnOptions(
24+
val cwd: js.UndefOr[String] = js.undefined,
25+
val env: js.Any = js.undefined,
26+
val argv0: js.UndefOr[String] = js.undefined,
27+
val stdio: js.UndefOr[js.Array[String | io.scalajs.nodejs.FileDescriptor] | js.Array[
28+
io.scalajs.nodejs.FileDescriptor
29+
] | js.Array[String] | String] = js.undefined,
30+
val detached: js.UndefOr[Boolean] = js.undefined,
31+
val uid: js.UndefOr[Int] = js.undefined,
32+
val gid: js.UndefOr[Int] = js.undefined,
33+
val shell: js.UndefOr[Boolean | String] = js.undefined
34+
) extends js.Object
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package io.scalajs.nodejs.child_process
2+
3+
import scala.scalajs.js
4+
5+
class SpawnResult(
6+
val pid: Int,
7+
val output: js.Array[Output],
8+
val stdout: Output,
9+
val stderr: Output,
10+
val status: js.UndefOr[Int],
11+
val signal: js.UndefOr[String],
12+
val error: js.UndefOr[js.Error]
13+
) extends js.Object
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package io.scalajs.nodejs
2+
3+
import io.scalajs.nodejs
4+
import io.scalajs.util.PromiseHelper._
5+
6+
import scala.concurrent.Future
7+
import scala.scalajs.js
8+
import scala.scalajs.js.|
9+
10+
package object child_process {
11+
type Output = nodejs.buffer.Buffer | String
12+
13+
implicit final class ChildProcessExtensions(val cp: ChildProcess.type) extends AnyVal {
14+
@inline
15+
def execFuture(
16+
command: String,
17+
options: js.UndefOr[ExecOptions | io.scalajs.RawOptions] = js.undefined
18+
): Future[(Output, Output)] = {
19+
promiseWithError2[nodejs.Error, Output, Output](cp.exec(command, options, _))
20+
}
21+
}
22+
23+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package io.scalajs.nodejs.child_process
2+
3+
import io.scalajs.nodejs.buffer.Buffer
4+
import org.scalatest.AsyncFunSpec
5+
6+
import scala.concurrent.ExecutionContext
7+
8+
class ChildProcessAsyncTest extends AsyncFunSpec {
9+
override implicit val executionContext = ExecutionContext.Implicits.global
10+
11+
describe("ChildProcess") {
12+
it("supports execFuture(...)") {
13+
for {
14+
r <- ChildProcess.execFuture("cat ./package.json | wc -l")
15+
} yield {
16+
assert(r._1.asInstanceOf[Buffer].toString().trim.toInt > 0)
17+
}
18+
}
19+
}
20+
21+
}

app/common/src/test/scala/io/scalajs/nodejs/child_process/ChildProcessTest.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import scala.scalajs.js.|
99

1010
/**
1111
* ChildProcess Test
12+
*
1213
1314
*/
1415
class ChildProcessTest extends FunSpec {
@@ -18,7 +19,7 @@ class ChildProcessTest extends FunSpec {
1819
it("supports exec(...)") {
1920
ChildProcess.exec(
2021
"cat ./package.json | wc -l",
21-
(error: Error, stdout: Buffer | String, stderr: Buffer | String) => {
22+
callback = (error: Error, stdout: Buffer | String, stderr: Buffer | String) => {
2223
if (isDefined(error)) {
2324
console.error(s"exec error: $error")
2425
}
@@ -28,6 +29,10 @@ class ChildProcessTest extends FunSpec {
2829
)
2930
}
3031

32+
it("supports execSync(...)") {
33+
val r = ChildProcess.execSync("cat ./package.json | wc -l")
34+
assert(r.asInstanceOf[Buffer].toString().trim.toInt > 0)
35+
}
3136
}
3237

3338
}

0 commit comments

Comments
 (0)