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

Fix child process #7

Merged
merged 2 commits into from
Jun 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package io.scalajs.nodejs
package child_process

import io.scalajs.RawOptions
import io.scalajs.nodejs
import io.scalajs.nodejs.events.IEventEmitter
import io.scalajs.nodejs.buffer.Buffer

import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
Expand All @@ -12,91 +11,56 @@ import scala.scalajs.js.|
/**
* The child_process module provides the ability to spawn child processes in a manner that is similar,
* but not identical, to popen(3). This capability is primarily provided by the child_process.spawn() function.
*
* @see https://nodejs.org/api/child_process.html
* @author [email protected]
*/
@js.native
trait ChildProcess extends IEventEmitter {
def kill(signal: js.UndefOr[Int | String] = js.native): Unit = js.native
}

/**
* Spawns a shell then executes the command within that shell, buffering any generated output.
* @param command <String> The command to run, with space-separated arguments
* @param options the execution [[ExecOptions options]]
* @return the [[ChildProcess]]
* @example {{{ child_process.exec(command[, options][, callback]) }}}
*/
def exec(command: String, options: ExecOptions | RawOptions): this.type = js.native
/**
* @see https://nodejs.org/api/child_process.html
*/
@JSImport("child_process", JSImport.Namespace)
@js.native
object ChildProcess extends scala.scalajs.js.Object {
def exec(
command: String,
options: js.UndefOr[ExecOptions | io.scalajs.RawOptions] = js.undefined,
callback: js.Function3[
nodejs.Error,
Output,
Output,
Any
]
): ChildProcess = js.native

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

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

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

/**
* The child_process.spawn() method spawns a new process using the given command, with command line arguments
* in args. If omitted, args defaults to an empty array.
* @param command <String> The command to run
* @param args <Array> List of string arguments
* @param options the spawn [[SpawnOptions options]]
* @return the [[ChildProcess]]
* @example {{{ child_process.spawn(command[, args][, options]) }}}
*/
def spawn(command: String,
args: js.Array[String] = js.native,
options: SpawnOptions | RawOptions = js.native): this.type = js.native
def spawnSync(
command: String,
args: js.Array[String] = js.native,
options: SpawnOptions | io.scalajs.RawOptions = js.native
): SpawnResult = js.native

def spawnSync(
command: String,
options: SpawnOptions | io.scalajs.RawOptions
): SpawnResult = js.native
}

/**
* ChildProcess Singleton
* @author [email protected]
*/
@js.native
@JSImport("child_process", JSImport.Namespace)
object ChildProcess extends ChildProcess
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package io.scalajs.nodejs.child_process

import io.scalajs.JsNumber

import scala.scalajs.js

import scala.scalajs.js.|

/**
* Spawn Options
*
* @param cwd <String> Current working directory of the child process
* @param env <Object> Environment key-value pairs
* @param argv0 <String> Explicitly set the value of argv[0] sent to the child process.
Expand All @@ -22,12 +20,15 @@ import scala.scalajs.js.|
* The shell should understand the -c switch on UNIX, or /d /s /c on Windows. Defaults to false (no shell).
* @author [email protected]
*/
class SpawnOptions(val cwd: js.UndefOr[String] = js.undefined,
val env: js.Any = js.undefined,
val argv0: js.UndefOr[String] = js.undefined,
val stdio: js.UndefOr[Array[String] | String] = js.undefined,
val detached: js.UndefOr[Boolean] = js.undefined,
val uid: js.UndefOr[JsNumber] = js.undefined,
val gid: js.UndefOr[JsNumber] = js.undefined,
val shell: js.UndefOr[Boolean | String] = js.undefined)
extends js.Object
class SpawnOptions(
val cwd: js.UndefOr[String] = js.undefined,
val env: js.Any = js.undefined,
val argv0: js.UndefOr[String] = js.undefined,
val stdio: js.UndefOr[js.Array[String | io.scalajs.nodejs.FileDescriptor] | js.Array[
io.scalajs.nodejs.FileDescriptor
] | js.Array[String] | String] = js.undefined,
val detached: js.UndefOr[Boolean] = js.undefined,
val uid: js.UndefOr[Int] = js.undefined,
val gid: js.UndefOr[Int] = js.undefined,
val shell: js.UndefOr[Boolean | String] = js.undefined
) extends js.Object
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.scalajs.nodejs.child_process

import scala.scalajs.js

class SpawnResult(
val pid: Int,
val output: js.Array[Output],
val stdout: Output,
val stderr: Output,
val status: js.UndefOr[Int],
val signal: js.UndefOr[String],
val error: js.UndefOr[js.Error]
) extends js.Object
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.scalajs.nodejs

import io.scalajs.nodejs
import io.scalajs.util.PromiseHelper._

import scala.concurrent.Future
import scala.scalajs.js
import scala.scalajs.js.|

package object child_process {
type Output = nodejs.buffer.Buffer | String

implicit final class ChildProcessExtensions(val cp: ChildProcess.type) extends AnyVal {
@inline
def execFuture(
command: String,
options: js.UndefOr[ExecOptions | io.scalajs.RawOptions] = js.undefined
): Future[(Output, Output)] = {
promiseWithError2[nodejs.Error, Output, Output](cp.exec(command, options, _))
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.scalajs.nodejs.child_process

import io.scalajs.nodejs.buffer.Buffer
import org.scalatest.AsyncFunSpec

import scala.concurrent.ExecutionContext

class ChildProcessAsyncTest extends AsyncFunSpec {
override implicit val executionContext = ExecutionContext.Implicits.global

describe("ChildProcess") {
it("supports execFuture(...)") {
for {
r <- ChildProcess.execFuture("cat ./package.json | wc -l")
} yield {
assert(r._1.asInstanceOf[Buffer].toString().trim.toInt > 0)
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import scala.scalajs.js.|

/**
* ChildProcess Test
*
* @author [email protected]
*/
class ChildProcessTest extends FunSpec {
Expand All @@ -18,7 +19,7 @@ class ChildProcessTest extends FunSpec {
it("supports exec(...)") {
ChildProcess.exec(
"cat ./package.json | wc -l",
(error: Error, stdout: Buffer | String, stderr: Buffer | String) => {
callback = (error: Error, stdout: Buffer | String, stderr: Buffer | String) => {
if (isDefined(error)) {
console.error(s"exec error: $error")
}
Expand All @@ -28,6 +29,10 @@ class ChildProcessTest extends FunSpec {
)
}

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

}