deno-snapshot 0.3.0

Generate snapshot for deno. Extracted some logic from main deno repo for better reusability.
Documentation
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
"use strict";

((window) => {
  const core = window.Deno.core;
  const { pathFromURL } = window.__bootstrap.util;
  const { illegalConstructorKey } = window.__bootstrap.webUtil;
  const {
    ArrayPrototypeMap,
    ObjectEntries,
    String,
    TypeError,
    Uint8Array,
    PromiseAll,
  } = window.__bootstrap.primordials;
  const { readableStreamForRid, writableStreamForRid } =
    window.__bootstrap.streamUtils;

  function spawnChild(command, {
    args = [],
    cwd = undefined,
    clearEnv = false,
    env = {},
    uid = undefined,
    gid = undefined,
    stdin = "null",
    stdout = "piped",
    stderr = "piped",
  } = {}) {
    const child = core.opSync("op_spawn_child", {
      cmd: pathFromURL(command),
      args: ArrayPrototypeMap(args, String),
      cwd: pathFromURL(cwd),
      clearEnv,
      env: ObjectEntries(env),
      uid,
      gid,
      stdin,
      stdout,
      stderr,
    });
    return new Child(illegalConstructorKey, child);
  }

  async function collectOutput(readableStream) {
    if (!(readableStream instanceof ReadableStream)) {
      return null;
    }

    const bufs = [];
    let size = 0;
    for await (const chunk of readableStream) {
      bufs.push(chunk);
      size += chunk.byteLength;
    }

    const buffer = new Uint8Array(size);
    let offset = 0;
    for (const chunk of bufs) {
      buffer.set(chunk, offset);
      offset += chunk.byteLength;
    }

    return buffer;
  }

  class Child {
    #rid;

    #pid;
    get pid() {
      return this.#pid;
    }

    #stdinRid;
    #stdin = null;
    get stdin() {
      return this.#stdin;
    }

    #stdoutRid;
    #stdout = null;
    get stdout() {
      return this.#stdout;
    }

    #stderrRid;
    #stderr = null;
    get stderr() {
      return this.#stderr;
    }

    constructor(key = null, {
      rid,
      pid,
      stdinRid,
      stdoutRid,
      stderrRid,
    } = null) {
      if (key !== illegalConstructorKey) {
        throw new TypeError("Illegal constructor.");
      }

      this.#rid = rid;
      this.#pid = pid;

      if (stdinRid !== null) {
        this.#stdinRid = stdinRid;
        this.#stdin = writableStreamForRid(stdinRid);
      }

      if (stdoutRid !== null) {
        this.#stdoutRid = stdoutRid;
        this.#stdout = readableStreamForRid(stdoutRid);
      }

      if (stderrRid !== null) {
        this.#stderrRid = stderrRid;
        this.#stderr = readableStreamForRid(stderrRid);
      }

      this.#status = core.opAsync("op_spawn_wait", this.#rid).then((res) => {
        this.#rid = null;
        return res;
      });
    }

    #status;
    get status() {
      return this.#status;
    }

    async output() {
      if (this.#rid === null) {
        throw new TypeError("Child process has already terminated.");
      }
      if (this.#stdout?.locked) {
        throw new TypeError(
          "Can't collect output because stdout is locked",
        );
      }
      if (this.#stderr?.locked) {
        throw new TypeError(
          "Can't collect output because stderr is locked",
        );
      }

      const [status, stdout, stderr] = await PromiseAll([
        this.#status,
        collectOutput(this.#stdout),
        collectOutput(this.#stderr),
      ]);

      return {
        status,
        stdout,
        stderr,
      };
    }

    kill(signo) {
      if (this.#rid === null) {
        throw new TypeError("Child process has already terminated.");
      }
      core.opSync("op_kill", this.#pid, signo);
    }
  }

  function spawn(command, options) { // TODO(@crowlKats): more options (like input)?
    return spawnChild(command, {
      ...options,
      stdin: "null",
      stdout: "piped",
      stderr: "piped",
    }).output();
  }

  function spawnSync(command, {
    args = [],
    cwd = undefined,
    clearEnv = false,
    env = {},
    uid = undefined,
    gid = undefined,
  } = {}) { // TODO(@crowlKats): more options (like input)?
    return core.opSync("op_spawn_sync", {
      cmd: pathFromURL(command),
      args: ArrayPrototypeMap(args, String),
      cwd: pathFromURL(cwd),
      clearEnv,
      env: ObjectEntries(env),
      uid,
      gid,
      stdin: "null",
      stdout: "piped",
      stderr: "piped",
    });
  }

  window.__bootstrap.spawn = {
    Child,
    spawnChild,
    spawn,
    spawnSync,
  };
})(this);