From 4a260b5c1ad872d48c35df410ed21e3b8b07f028 Mon Sep 17 00:00:00 2001
From: Toru Nagashima <star.ctor@gmail.com>
Date: Sun, 13 May 2018 07:00:16 +0900
Subject: [PATCH] rewrite with TypeScript

---
 .babelrc                                   |   6 -
 .eslintignore                              |   3 +
 .eslintrc.json                             |   8 -
 .eslintrc.yml                              |  13 +
 .gitignore                                 |   7 +-
 .nycrc                                     |   8 +-
 .vscode/settings.json                      |   7 +
 bin/common/bootstrap.js                    |  51 ---
 bin/common/parse-cli-args.js               | 251 --------------
 bin/common/version.js                      |  25 --
 bin/npm-run-all/help.js                    |  71 ----
 bin/npm-run-all/index.js                   |  13 -
 bin/npm-run-all/main.js                    |  77 -----
 bin/run-p/index.js                         |  13 -
 bin/run-p/main.js                          |  74 -----
 bin/run-s/index.js                         |  13 -
 bin/run-s/main.js                          |  71 ----
 jsdoc.json                                 |  10 -
 lib/create-header.js                       |  48 ---
 lib/create-prefix-transform-stream.js      |  89 -----
 lib/index.js                               | 287 ----------------
 lib/match-tasks.js                         | 128 -------
 lib/npm-run-all-error.js                   |  47 ---
 lib/read-package-json.js                   |  31 --
 lib/run-task.js                            | 190 -----------
 lib/run-tasks.js                           | 177 ----------
 lib/spawn-posix.js                         |  64 ----
 lib/spawn-win32.js                         |  50 ---
 lib/spawn.js                               |  20 --
 package.json                               |  68 ++--
 rollup.config.js                           |  52 +++
 src/bin/cli-parameter-parser.ts            | 193 +++++++++++
 src/bin/npm-run-all.ts                     | 144 ++++++++
 src/bin/package.json.d.ts                  |   8 +
 bin/run-p/help.js => src/bin/run-p.ts      |  66 ++--
 bin/run-s/help.js => src/bin/run-s.ts      |  62 ++--
 src/lib/aggregate-stream.ts                |  27 ++
 src/lib/index.ts                           | 366 +++++++++++++++++++++
 src/lib/package-info.ts                    |  37 +++
 src/lib/script-error.ts                    |  22 ++
 src/lib/script-match.ts                    | 115 +++++++
 src/lib/script-result.ts                   |  12 +
 src/lib/scripts/label.ts                   |  64 ++++
 src/lib/scripts/name.ts                    |  35 ++
 src/lib/scripts/run.ts                     | 238 ++++++++++++++
 src/lib/scripts/spawn.ts                   |  22 ++
 src/lib/scripts/spawn/posix.ts             |  41 +++
 src/lib/scripts/spawn/win32.ts             |  73 ++++
 test/.eslintrc.json                        |   8 -
 tsconfig.json                              |  39 +++
 typings/abort-controller/index.d.ts        |  37 +++
 typings/ps-tree/index.d.ts                 |  16 +
 typings/string.prototype.padend/index.d.ts |  13 +
 53 files changed, 1708 insertions(+), 1902 deletions(-)
 delete mode 100644 .babelrc
 create mode 100644 .eslintignore
 delete mode 100644 .eslintrc.json
 create mode 100644 .eslintrc.yml
 create mode 100644 .vscode/settings.json
 delete mode 100644 bin/common/bootstrap.js
 delete mode 100644 bin/common/parse-cli-args.js
 delete mode 100644 bin/common/version.js
 delete mode 100644 bin/npm-run-all/help.js
 delete mode 100644 bin/npm-run-all/index.js
 delete mode 100644 bin/npm-run-all/main.js
 delete mode 100644 bin/run-p/index.js
 delete mode 100644 bin/run-p/main.js
 delete mode 100644 bin/run-s/index.js
 delete mode 100644 bin/run-s/main.js
 delete mode 100644 jsdoc.json
 delete mode 100644 lib/create-header.js
 delete mode 100644 lib/create-prefix-transform-stream.js
 delete mode 100644 lib/index.js
 delete mode 100644 lib/match-tasks.js
 delete mode 100644 lib/npm-run-all-error.js
 delete mode 100644 lib/read-package-json.js
 delete mode 100644 lib/run-task.js
 delete mode 100644 lib/run-tasks.js
 delete mode 100644 lib/spawn-posix.js
 delete mode 100644 lib/spawn-win32.js
 delete mode 100644 lib/spawn.js
 create mode 100644 rollup.config.js
 create mode 100644 src/bin/cli-parameter-parser.ts
 create mode 100644 src/bin/npm-run-all.ts
 create mode 100644 src/bin/package.json.d.ts
 rename bin/run-p/help.js => src/bin/run-p.ts (57%)
 rename bin/run-s/help.js => src/bin/run-s.ts (51%)
 create mode 100644 src/lib/aggregate-stream.ts
 create mode 100644 src/lib/index.ts
 create mode 100644 src/lib/package-info.ts
 create mode 100644 src/lib/script-error.ts
 create mode 100644 src/lib/script-match.ts
 create mode 100644 src/lib/script-result.ts
 create mode 100644 src/lib/scripts/label.ts
 create mode 100644 src/lib/scripts/name.ts
 create mode 100644 src/lib/scripts/run.ts
 create mode 100644 src/lib/scripts/spawn.ts
 create mode 100644 src/lib/scripts/spawn/posix.ts
 create mode 100644 src/lib/scripts/spawn/win32.ts
 delete mode 100644 test/.eslintrc.json
 create mode 100644 tsconfig.json
 create mode 100644 typings/abort-controller/index.d.ts
 create mode 100644 typings/ps-tree/index.d.ts
 create mode 100644 typings/string.prototype.padend/index.d.ts

diff --git a/.babelrc b/.babelrc
deleted file mode 100644
index 6454fa9..0000000
--- a/.babelrc
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-    "presets": ["power-assert"],
-    "plugins": ["transform-async-to-generator"],
-    "only": "/test/*.js",
-    "sourceMaps": "inline"
-}
diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000..dbe7c1f
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1,3 @@
+/.temp
+/dist
+/node_modules
diff --git a/.eslintrc.json b/.eslintrc.json
deleted file mode 100644
index 592522b..0000000
--- a/.eslintrc.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-    "root": true,
-    "extends": ["mysticatea", "mysticatea/node"],
-    "rules": {
-        "prefer-rest-params": "off",
-        "prefer-spread": "off"
-    }
-}
diff --git a/.eslintrc.yml b/.eslintrc.yml
new file mode 100644
index 0000000..53c9010
--- /dev/null
+++ b/.eslintrc.yml
@@ -0,0 +1,13 @@
+root: true
+extends:
+  - plugin:mysticatea/es2015
+  - plugin:mysticatea/+node
+overrides:
+  - files: ["*.ts"]
+    rules:
+      mysticatea/node/no-unsupported-features:
+        - "error"
+        - ignores:
+          - "asyncAwait"
+          - "modules"
+          - "trailingCommasInFunctions"
diff --git a/.gitignore b/.gitignore
index 6779c56..e5a351a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,9 @@
 /.nyc_output
+/.temp
 /coverage
-/jsdoc
+/dist
 /node_modules
 /test-workspace/tasks/lib
 /test-workspace/test.txt
-/test.js
-npm-debug.log
+/test.*
+/npm-debug.log
diff --git a/.nycrc b/.nycrc
index cf99a56..5142563 100644
--- a/.nycrc
+++ b/.nycrc
@@ -1,5 +1,5 @@
 {
-    "include": [
-        "{bin,lib}/**/*.js"
-    ]
-}
\ No newline at end of file
+    "include": ["src/**/*.js"],
+    "require": ["ts-node/register"],
+    "cache": true
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..cac0f9c
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,7 @@
+{
+    "eslint.validate": [
+        "javascript",
+        { "autoFix": true, "language": "typescript" }
+    ],
+    "typescript.tsdk": "node_modules\\typescript\\lib"
+}
diff --git a/bin/common/bootstrap.js b/bin/common/bootstrap.js
deleted file mode 100644
index e73b093..0000000
--- a/bin/common/bootstrap.js
+++ /dev/null
@@ -1,51 +0,0 @@
-/**
- * @author Toru Nagashima
- * @copyright 2016 Toru Nagashima. All rights reserved.
- * See LICENSE file in root directory for full license.
- */
-"use strict"
-
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
-/*eslint-disable no-process-exit */
-
-module.exports = function bootstrap(name) {
-    const argv = process.argv.slice(2)
-
-    switch (argv[0]) {
-        case undefined:
-        case "-h":
-        case "--help":
-            return require(`../${name}/help`)(process.stdout)
-
-        case "-v":
-        case "--version":
-            return require("./version")(process.stdout)
-
-        default:
-            // https://github.com/mysticatea/npm-run-all/issues/105
-            // Avoid MaxListenersExceededWarnings.
-            process.stdout.setMaxListeners(0)
-            process.stderr.setMaxListeners(0)
-            process.stdin.setMaxListeners(0)
-
-            // Main
-            return require(`../${name}/main`)(
-                argv,
-                process.stdout,
-                process.stderr
-            ).then(
-                () => {
-                    // I'm not sure why, but maybe the process never exits
-                    // on Git Bash (MINGW64)
-                    process.exit(0)
-                },
-                () => {
-                    process.exit(1)
-                }
-            )
-    }
-}
-
-/*eslint-enable */
diff --git a/bin/common/parse-cli-args.js b/bin/common/parse-cli-args.js
deleted file mode 100644
index 7f056fc..0000000
--- a/bin/common/parse-cli-args.js
+++ /dev/null
@@ -1,251 +0,0 @@
-/**
- * @author Toru Nagashima
- * @copyright 2016 Toru Nagashima. All rights reserved.
- * See LICENSE file in root directory for full license.
- */
-"use strict"
-
-/*eslint-disable no-process-env */
-
-//------------------------------------------------------------------------------
-// Helpers
-//------------------------------------------------------------------------------
-
-const OVERWRITE_OPTION = /^--([^:]+?):([^=]+?)(?:=(.+))?$/
-const CONFIG_OPTION = /^--([^=]+?)(?:=(.+))$/
-const PACKAGE_CONFIG_PATTERN = /^npm_package_config_(.+)$/
-const CONCAT_OPTIONS = /^-[clnprs]+$/
-
-/**
- * Overwrites a specified package config.
- *
- * @param {object} config - A config object to be overwritten.
- * @param {string} packageName - A package name to overwrite.
- * @param {string} variable - A variable name to overwrite.
- * @param {string} value - A new value to overwrite.
- * @returns {void}
- */
-function overwriteConfig(config, packageName, variable, value) {
-    const scope = config[packageName] || (config[packageName] = {})
-    scope[variable] = value
-}
-
-/**
- * Creates a package config object.
- * This checks `process.env` and creates the default value.
- *
- * @returns {object} Created config object.
- */
-function createPackageConfig() {
-    const retv = {}
-    const packageName = process.env.npm_package_name
-    if (!packageName) {
-        return retv
-    }
-
-    for (const key of Object.keys(process.env)) {
-        const m = PACKAGE_CONFIG_PATTERN.exec(key)
-        if (m != null) {
-            overwriteConfig(retv, packageName, m[1], process.env[key])
-        }
-    }
-
-    return retv
-}
-
-/**
- * Adds a new group into a given list.
- *
- * @param {object[]} groups - A group list to add.
- * @param {object} initialValues - A key-value map for the default of new value.
- * @returns {void}
- */
-function addGroup(groups, initialValues) {
-    groups.push(Object.assign(
-        { parallel: false, patterns: [] },
-        initialValues || {}
-    ))
-}
-
-/**
- * ArgumentSet is values of parsed CLI arguments.
- * This class provides the getter to get the last group.
- */
-class ArgumentSet {
-    /**
-     * @param {object} initialValues - A key-value map for the default of new value.
-     * @param {object} options - A key-value map for the options.
-     */
-    constructor(initialValues, options) {
-        this.config = {}
-        this.continueOnError = false
-        this.groups = []
-        this.maxParallel = 0
-        this.npmPath = null
-        this.packageConfig = createPackageConfig()
-        this.printLabel = false
-        this.printName = false
-        this.race = false
-        this.rest = []
-        this.silent = process.env.npm_config_loglevel === "silent"
-        this.singleMode = Boolean(options && options.singleMode)
-
-        addGroup(this.groups, initialValues)
-    }
-
-    /**
-     * Gets the last group.
-     */
-    get lastGroup() {
-        return this.groups[this.groups.length - 1]
-    }
-
-    /**
-     * Gets "parallel" flag.
-     */
-    get parallel() {
-        return this.groups.some(g => g.parallel)
-    }
-}
-
-/**
- * Parses CLI arguments.
- *
- * @param {ArgumentSet} set - The parsed CLI arguments.
- * @param {string[]} args - CLI arguments.
- * @returns {ArgumentSet} set itself.
- */
-function parseCLIArgsCore(set, args) {    // eslint-disable-line complexity
-    LOOP:
-    for (let i = 0; i < args.length; ++i) {
-        const arg = args[i]
-
-        switch (arg) {
-            case "--":
-                set.rest = args.slice(1 + i)
-                break LOOP
-
-            case "--color":
-            case "--no-color":
-                // do nothing.
-                break
-
-            case "-c":
-            case "--continue-on-error":
-                set.continueOnError = true
-                break
-
-            case "-l":
-            case "--print-label":
-                set.printLabel = true
-                break
-
-            case "-n":
-            case "--print-name":
-                set.printName = true
-                break
-
-            case "-r":
-            case "--race":
-                set.race = true
-                break
-
-            case "--silent":
-                set.silent = true
-                break
-
-            case "--max-parallel":
-                set.maxParallel = parseInt(args[++i], 10)
-                if (!Number.isFinite(set.maxParallel) || set.maxParallel <= 0) {
-                    throw new Error(`Invalid Option: --max-parallel ${args[i]}`)
-                }
-                break
-
-            case "-s":
-            case "--sequential":
-            case "--serial":
-                if (set.singleMode && arg === "-s") {
-                    set.silent = true
-                    break
-                }
-                if (set.singleMode) {
-                    throw new Error(`Invalid Option: ${arg}`)
-                }
-                addGroup(set.groups)
-                break
-
-            case "--aggregate-output":
-                set.aggregateOutput = true
-                break
-
-            case "-p":
-            case "--parallel":
-                if (set.singleMode) {
-                    throw new Error(`Invalid Option: ${arg}`)
-                }
-                addGroup(set.groups, { parallel: true })
-                break
-
-            case "--npm-path":
-                set.npmPath = args[++i] || null
-                break
-
-            default: {
-                let matched = null
-                if ((matched = OVERWRITE_OPTION.exec(arg))) {
-                    overwriteConfig(
-                        set.packageConfig,
-                        matched[1],
-                        matched[2],
-                        matched[3] || args[++i]
-                    )
-                }
-                else if ((matched = CONFIG_OPTION.exec(arg))) {
-                    set.config[matched[1]] = matched[2]
-                }
-                else if (CONCAT_OPTIONS.test(arg)) {
-                    parseCLIArgsCore(
-                        set,
-                        arg.slice(1).split("").map(c => `-${c}`)
-                    )
-                }
-                else if (arg[0] === "-") {
-                    throw new Error(`Invalid Option: ${arg}`)
-                }
-                else {
-                    set.lastGroup.patterns.push(arg)
-                }
-
-                break
-            }
-        }
-    }
-
-    if (!set.parallel && set.aggregateOutput) {
-        throw new Error("Invalid Option: --aggregate-output (without parallel)")
-    }
-    if (!set.parallel && set.race) {
-        const race = args.indexOf("--race") !== -1 ? "--race" : "-r"
-        throw new Error(`Invalid Option: ${race} (without parallel)`)
-    }
-    if (!set.parallel && set.maxParallel !== 0) {
-        throw new Error("Invalid Option: --max-parallel (without parallel)")
-    }
-
-    return set
-}
-
-/**
- * Parses CLI arguments.
- *
- * @param {string[]} args - CLI arguments.
- * @param {object} initialValues - A key-value map for the default of new value.
- * @param {object} options - A key-value map for the options.
- * @param {boolean} options.singleMode - The flag to be single group mode.
- * @returns {ArgumentSet} The parsed CLI arguments.
- */
-module.exports = function parseCLIArgs(args, initialValues, options) {
-    return parseCLIArgsCore(new ArgumentSet(initialValues, options), args)
-}
-
-/*eslint-enable */
diff --git a/bin/common/version.js b/bin/common/version.js
deleted file mode 100644
index 06afb8f..0000000
--- a/bin/common/version.js
+++ /dev/null
@@ -1,25 +0,0 @@
-/**
- * @author Toru Nagashima
- * @copyright 2016 Toru Nagashima. All rights reserved.
- * See LICENSE file in root directory for full license.
- */
-"use strict"
-
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
-
-/**
- * Print a version text.
- *
- * @param {stream.Writable} output - A writable stream to print.
- * @returns {Promise} Always a fulfilled promise.
- * @private
- */
-module.exports = function printVersion(output) {
-    const version = require("../../package.json").version
-
-    output.write(`v${version}\n`)
-
-    return Promise.resolve(null)
-}
diff --git a/bin/npm-run-all/help.js b/bin/npm-run-all/help.js
deleted file mode 100644
index 0300bfe..0000000
--- a/bin/npm-run-all/help.js
+++ /dev/null
@@ -1,71 +0,0 @@
-/**
- * @author Toru Nagashima
- * @copyright 2015 Toru Nagashima. All rights reserved.
- * See LICENSE file in root directory for full license.
- */
-"use strict"
-
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
-
-/**
- * Print a help text.
- *
- * @param {stream.Writable} output - A writable stream to print.
- * @returns {Promise} Always a fulfilled promise.
- * @private
- */
-module.exports = function printHelp(output) {
-    output.write(`
-Usage:
-    $ npm-run-all [--help | -h | --version | -v]
-    $ npm-run-all [tasks] [OPTIONS]
-
-    Run given npm-scripts in parallel or sequential.
-
-    <tasks> : A list of npm-scripts' names and Glob-like patterns.
-
-Options:
-    --aggregate-output   - - - Avoid interleaving output by delaying printing of
-                               each command's output until it has finished.
-    -c, --continue-on-error  - Set the flag to continue executing
-                               other/subsequent tasks even if a task threw an
-                               error. 'npm-run-all' itself will exit with
-                               non-zero code if one or more tasks threw error(s)
-    --max-parallel <number>  - Set the maximum number of parallelism. Default is
-                               unlimited.
-    --npm-path <string>  - - - Set the path to npm. Default is the value of
-                               environment variable npm_execpath.
-                               If the variable is not defined, then it's "npm".
-                               In this case, the "npm" command must be found in
-                               environment variable PATH.
-    -l, --print-label  - - - - Set the flag to print the task name as a prefix
-                               on each line of output. Tools in tasks may stop
-                               coloring their output if this option was given.
-    -n, --print-name   - - - - Set the flag to print the task name before
-                               running each task.
-    -p, --parallel <tasks>   - Run a group of tasks in parallel.
-                               e.g. 'npm-run-all -p foo bar' is similar to
-                                    'npm run foo & npm run bar'.
-    -r, --race   - - - - - - - Set the flag to kill all tasks when a task
-                               finished with zero. This option is valid only
-                               with 'parallel' option.
-    -s, --sequential <tasks> - Run a group of tasks sequentially.
-        --serial <tasks>       e.g. 'npm-run-all -s foo bar' is similar to
-                                    'npm run foo && npm run bar'.
-                               '--serial' is a synonym of '--sequential'.
-    --silent   - - - - - - - - Set 'silent' to the log level of npm.
-
-Examples:
-    $ npm-run-all --serial clean lint build:**
-    $ npm-run-all --parallel watch:**
-    $ npm-run-all clean lint --parallel "build:** -- --watch"
-    $ npm-run-all -l -p start-server start-browser start-electron
-
-See Also:
-    https://github.com/mysticatea/npm-run-all#readme
-`)
-
-    return Promise.resolve(null)
-}
diff --git a/bin/npm-run-all/index.js b/bin/npm-run-all/index.js
deleted file mode 100644
index b405238..0000000
--- a/bin/npm-run-all/index.js
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/usr/bin/env node
-/**
- * @author Toru Nagashima
- * @copyright 2015 Toru Nagashima. All rights reserved.
- * See LICENSE file in root directory for full license.
- */
-"use strict"
-
-//------------------------------------------------------------------------------
-// Main
-//------------------------------------------------------------------------------
-
-require("../common/bootstrap")("npm-run-all")
diff --git a/bin/npm-run-all/main.js b/bin/npm-run-all/main.js
deleted file mode 100644
index 2782468..0000000
--- a/bin/npm-run-all/main.js
+++ /dev/null
@@ -1,77 +0,0 @@
-/**
- * @author Toru Nagashima
- * @copyright 2015 Toru Nagashima. All rights reserved.
- * See LICENSE file in root directory for full license.
- */
-"use strict"
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const runAll = require("../../lib")
-const parseCLIArgs = require("../common/parse-cli-args")
-
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
-
-/**
- * Parses arguments, then run specified npm-scripts.
- *
- * @param {string[]} args - Arguments to parse.
- * @param {stream.Writable} stdout - A writable stream to print logs.
- * @param {stream.Writable} stderr - A writable stream to print errors.
- * @returns {Promise} A promise which comes to be fulfilled when all npm-scripts are completed.
- * @private
- */
-module.exports = function npmRunAll(args, stdout, stderr) {
-    try {
-        const stdin = process.stdin
-        const argv = parseCLIArgs(args)
-
-        const promise = argv.groups.reduce(
-            (prev, group) => {
-                if (group.patterns.length === 0) {
-                    return prev
-                }
-                return prev.then(() => runAll(
-                    group.patterns,
-                    {
-                        stdout,
-                        stderr,
-                        stdin,
-                        parallel: group.parallel,
-                        maxParallel: group.parallel ? argv.maxParallel : 1,
-                        continueOnError: argv.continueOnError,
-                        printLabel: argv.printLabel,
-                        printName: argv.printName,
-                        config: argv.config,
-                        packageConfig: argv.packageConfig,
-                        silent: argv.silent,
-                        arguments: argv.rest,
-                        race: group.parallel && argv.race,
-                        npmPath: argv.npmPath,
-                        aggregateOutput: group.parallel && argv.aggregateOutput,
-                    }
-                ))
-            },
-            Promise.resolve(null)
-        )
-
-        if (!argv.silent) {
-            promise.catch(err => {
-                //eslint-disable-next-line no-console
-                console.error("ERROR:", err.message)
-            })
-        }
-
-        return promise
-    }
-    catch (err) {
-        //eslint-disable-next-line no-console
-        console.error("ERROR:", err.message)
-
-        return Promise.reject(err)
-    }
-}
diff --git a/bin/run-p/index.js b/bin/run-p/index.js
deleted file mode 100644
index b7ca754..0000000
--- a/bin/run-p/index.js
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/usr/bin/env node
-/**
- * @author Toru Nagashima
- * @copyright 2015 Toru Nagashima. All rights reserved.
- * See LICENSE file in root directory for full license.
- */
-"use strict"
-
-//------------------------------------------------------------------------------
-// Main
-//------------------------------------------------------------------------------
-
-require("../common/bootstrap")("run-p")
diff --git a/bin/run-p/main.js b/bin/run-p/main.js
deleted file mode 100644
index e44f2f2..0000000
--- a/bin/run-p/main.js
+++ /dev/null
@@ -1,74 +0,0 @@
-/**
- * @author Toru Nagashima
- * @copyright 2016 Toru Nagashima. All rights reserved.
- * See LICENSE file in root directory for full license.
- */
-"use strict"
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const runAll = require("../../lib")
-const parseCLIArgs = require("../common/parse-cli-args")
-
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
-
-/**
- * Parses arguments, then run specified npm-scripts.
- *
- * @param {string[]} args - Arguments to parse.
- * @param {stream.Writable} stdout - A writable stream to print logs.
- * @param {stream.Writable} stderr - A writable stream to print errors.
- * @returns {Promise} A promise which comes to be fulfilled when all npm-scripts are completed.
- * @private
- */
-module.exports = function npmRunAll(args, stdout, stderr) {
-    try {
-        const stdin = process.stdin
-        const argv = parseCLIArgs(args, { parallel: true }, { singleMode: true })
-        const group = argv.lastGroup
-
-        if (group.patterns.length === 0) {
-            return Promise.resolve(null)
-        }
-
-        const promise = runAll(
-            group.patterns,
-            {
-                stdout,
-                stderr,
-                stdin,
-                parallel: group.parallel,
-                maxParallel: argv.maxParallel,
-                continueOnError: argv.continueOnError,
-                printLabel: argv.printLabel,
-                printName: argv.printName,
-                config: argv.config,
-                packageConfig: argv.packageConfig,
-                silent: argv.silent,
-                arguments: argv.rest,
-                race: argv.race,
-                npmPath: argv.npmPath,
-                aggregateOutput: argv.aggregateOutput,
-            }
-        )
-
-        if (!argv.silent) {
-            promise.catch(err => {
-                //eslint-disable-next-line no-console
-                console.error("ERROR:", err.message)
-            })
-        }
-
-        return promise
-    }
-    catch (err) {
-        //eslint-disable-next-line no-console
-        console.error("ERROR:", err.message)
-
-        return Promise.reject(err)
-    }
-}
diff --git a/bin/run-s/index.js b/bin/run-s/index.js
deleted file mode 100644
index f3cf012..0000000
--- a/bin/run-s/index.js
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/usr/bin/env node
-/**
- * @author Toru Nagashima
- * @copyright 2015 Toru Nagashima. All rights reserved.
- * See LICENSE file in root directory for full license.
- */
-"use strict"
-
-//------------------------------------------------------------------------------
-// Main
-//------------------------------------------------------------------------------
-
-require("../common/bootstrap")("run-s")
diff --git a/bin/run-s/main.js b/bin/run-s/main.js
deleted file mode 100644
index d1bd6da..0000000
--- a/bin/run-s/main.js
+++ /dev/null
@@ -1,71 +0,0 @@
-/**
- * @author Toru Nagashima
- * @copyright 2016 Toru Nagashima. All rights reserved.
- * See LICENSE file in root directory for full license.
- */
-"use strict"
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const runAll = require("../../lib")
-const parseCLIArgs = require("../common/parse-cli-args")
-
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
-
-/**
- * Parses arguments, then run specified npm-scripts.
- *
- * @param {string[]} args - Arguments to parse.
- * @param {stream.Writable} stdout - A writable stream to print logs.
- * @param {stream.Writable} stderr - A writable stream to print errors.
- * @returns {Promise} A promise which comes to be fulfilled when all npm-scripts are completed.
- * @private
- */
-module.exports = function npmRunAll(args, stdout, stderr) {
-    try {
-        const stdin = process.stdin
-        const argv = parseCLIArgs(args, { parallel: false }, { singleMode: true })
-        const group = argv.lastGroup
-
-        if (group.patterns.length === 0) {
-            return Promise.resolve(null)
-        }
-
-        const promise = runAll(
-            group.patterns,
-            {
-                stdout,
-                stderr,
-                stdin,
-                parallel: group.parallel,
-                continueOnError: argv.continueOnError,
-                printLabel: argv.printLabel,
-                printName: argv.printName,
-                config: argv.config,
-                packageConfig: argv.packageConfig,
-                silent: argv.silent,
-                arguments: argv.rest,
-                npmPath: argv.npmPath,
-            }
-        )
-
-        if (!argv.silent) {
-            promise.catch(err => {
-                //eslint-disable-next-line no-console
-                console.error("ERROR:", err.message)
-            })
-        }
-
-        return promise
-    }
-    catch (err) {
-        //eslint-disable-next-line no-console
-        console.error("ERROR:", err.message)
-
-        return Promise.reject(err)
-    }
-}
diff --git a/jsdoc.json b/jsdoc.json
deleted file mode 100644
index d813cdb..0000000
--- a/jsdoc.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-    "source": {
-        "include": ["src/lib", "README.md"]
-    },
-    "opts": {
-        "destination": "jsdoc",
-        "encoding": "utf8",
-        "recurse": true
-    }
-}
diff --git a/lib/create-header.js b/lib/create-header.js
deleted file mode 100644
index cdf52df..0000000
--- a/lib/create-header.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * @module create-header
- * @author Toru Nagashima
- * @copyright 2016 Toru Nagashima. All rights reserved.
- * See LICENSE file in root directory for full license.
- */
-"use strict"
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const ansiStyles = require("ansi-styles")
-
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
-
-/**
- * Creates the header text for a given task.
- *
- * @param {string} nameAndArgs - A task name and arguments.
- * @param {object} packageInfo - A package.json's information.
- * @param {object} packageInfo.body - A package.json's JSON object.
- * @param {string} packageInfo.path - A package.json's file path.
- * @param {boolean} isTTY - The flag to color the header.
- * @returns {string} The header of a given task.
- */
-module.exports = function createHeader(nameAndArgs, packageInfo, isTTY) {
-    if (!packageInfo) {
-        return `\n> ${nameAndArgs}\n\n`
-    }
-
-    const index = nameAndArgs.indexOf(" ")
-    const name = (index === -1) ? nameAndArgs : nameAndArgs.slice(0, index)
-    const args = (index === -1) ? "" : nameAndArgs.slice(index + 1)
-    const packageName = packageInfo.body.name
-    const packageVersion = packageInfo.body.version
-    const scriptBody = packageInfo.body.scripts[name]
-    const packagePath = packageInfo.path
-    const color = isTTY ? ansiStyles.gray : { open: "", close: "" }
-
-    return `
-${color.open}> ${packageName}@${packageVersion} ${name} ${packagePath}${color.close}
-${color.open}> ${scriptBody} ${args}${color.close}
-
-`
-}
diff --git a/lib/create-prefix-transform-stream.js b/lib/create-prefix-transform-stream.js
deleted file mode 100644
index cb2c360..0000000
--- a/lib/create-prefix-transform-stream.js
+++ /dev/null
@@ -1,89 +0,0 @@
-/**
- * @module create-prefix-transform-stream
- * @author Toru Nagashima
- * @copyright 2016 Toru Nagashima. All rights reserved.
- * See LICENSE file in root directory for full license.
- */
-"use strict"
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const stream = require("stream")
-
-//------------------------------------------------------------------------------
-// Helpers
-//------------------------------------------------------------------------------
-
-const ALL_BR = /\n/g
-
-/**
- * The transform stream to insert a specific prefix.
- *
- * Several streams can exist for the same output stream.
- * This stream will insert the prefix if the last output came from other instance.
- * To do that, this stream is using a shared state object.
- *
- * @private
- */
-class PrefixTransform extends stream.Transform {
-    /**
-     * @param {string} prefix - A prefix text to be inserted.
-     * @param {object} state - A state object.
-     * @param {string} state.lastPrefix - The last prefix which is printed.
-     * @param {boolean} state.lastIsLinebreak -The flag to check whether the last output is a line break or not.
-     */
-    constructor(prefix, state) {
-        super()
-
-        this.prefix = prefix
-        this.state = state
-    }
-
-    /**
-     * Transforms the output chunk.
-     *
-     * @param {string|Buffer} chunk - A chunk to be transformed.
-     * @param {string} _encoding - The encoding of the chunk.
-     * @param {function} callback - A callback function that is called when done.
-     * @returns {void}
-     */
-    _transform(chunk, _encoding, callback) {
-        const prefix = this.prefix
-        const nPrefix = `\n${prefix}`
-        const state = this.state
-        const firstPrefix =
-            state.lastIsLinebreak ? prefix :
-            (state.lastPrefix !== prefix) ? "\n" :
-            /* otherwise */ ""
-        const prefixed = `${firstPrefix}${chunk}`.replace(ALL_BR, nPrefix)
-        const index = prefixed.indexOf(prefix, Math.max(0, prefixed.length - prefix.length))
-
-        state.lastPrefix = prefix
-        state.lastIsLinebreak = (index !== -1)
-
-        callback(null, (index !== -1) ? prefixed.slice(0, index) : prefixed)
-    }
-}
-
-//------------------------------------------------------------------------------
-// Public API
-//------------------------------------------------------------------------------
-
-/**
- * Create a transform stream to insert the specific prefix.
- *
- * Several streams can exist for the same output stream.
- * This stream will insert the prefix if the last output came from other instance.
- * To do that, this stream is using a shared state object.
- *
- * @param {string} prefix - A prefix text to be inserted.
- * @param {object} state - A state object.
- * @param {string} state.lastPrefix - The last prefix which is printed.
- * @param {boolean} state.lastIsLinebreak -The flag to check whether the last output is a line break or not.
- * @returns {stream.Transform} The created transform stream.
- */
-module.exports = function createPrefixTransform(prefix, state) {
-    return new PrefixTransform(prefix, state)
-}
diff --git a/lib/index.js b/lib/index.js
deleted file mode 100644
index e36a605..0000000
--- a/lib/index.js
+++ /dev/null
@@ -1,287 +0,0 @@
-/**
- * @module index
- * @author Toru Nagashima
- * @copyright 2015 Toru Nagashima. All rights reserved.
- * See LICENSE file in root directory for full license.
- */
-"use strict"
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const shellQuote = require("shell-quote")
-const matchTasks = require("./match-tasks")
-const readPackageJson = require("./read-package-json")
-const runTasks = require("./run-tasks")
-
-//------------------------------------------------------------------------------
-// Helpers
-//------------------------------------------------------------------------------
-
-const ARGS_PATTERN = /\{(!)?([*@]|\d+)([^}]+)?}/g
-
-/**
- * Converts a given value to an array.
- *
- * @param {string|string[]|null|undefined} x - A value to convert.
- * @returns {string[]} An array.
- */
-function toArray(x) {
-    if (x == null) {
-        return []
-    }
-    return Array.isArray(x) ? x : [x]
-}
-
-/**
- * Replaces argument placeholders (such as `{1}`) by arguments.
- *
- * @param {string[]} patterns - Patterns to replace.
- * @param {string[]} args - Arguments to replace.
- * @returns {string[]} replaced
- */
-function applyArguments(patterns, args) {
-    const defaults = Object.create(null)
-
-    return patterns.map(pattern => pattern.replace(ARGS_PATTERN, (whole, indirectionMark, id, options) => {
-        if (indirectionMark != null) {
-            throw Error(`Invalid Placeholder: ${whole}`)
-        }
-        if (id === "@") {
-            return shellQuote.quote(args)
-        }
-        if (id === "*") {
-            return shellQuote.quote([args.join(" ")])
-        }
-
-        const position = parseInt(id, 10)
-        if (position >= 1 && position <= args.length) {
-            return shellQuote.quote([args[position - 1]])
-        }
-
-        // Address default values
-        if (options != null) {
-            const prefix = options.slice(0, 2)
-
-            if (prefix === ":=") {
-                defaults[id] = shellQuote.quote([options.slice(2)])
-                return defaults[id]
-            }
-            if (prefix === ":-") {
-                return shellQuote.quote([options.slice(2)])
-            }
-
-            throw Error(`Invalid Placeholder: ${whole}`)
-        }
-        if (defaults[id] != null) {
-            return defaults[id]
-        }
-
-        return ""
-    }))
-}
-
-/**
- * Parse patterns.
- * In parsing process, it replaces argument placeholders (such as `{1}`) by arguments.
- *
- * @param {string|string[]} patternOrPatterns - Patterns to run.
- *      A pattern is a npm-script name or a Glob-like pattern.
- * @param {string[]} args - Arguments to replace placeholders.
- * @returns {string[]} Parsed patterns.
- */
-function parsePatterns(patternOrPatterns, args) {
-    const patterns = toArray(patternOrPatterns)
-    const hasPlaceholder = patterns.some(pattern => ARGS_PATTERN.test(pattern))
-
-    return hasPlaceholder ? applyArguments(patterns, args) : patterns
-}
-
-/**
- * Converts a given config object to an `--:=` style option array.
- *
- * @param {object|null} config -
- *   A map-like object to overwrite package configs.
- *   Keys are package names.
- *   Every value is a map-like object (Pairs of variable name and value).
- * @returns {string[]} `--:=` style options.
- */
-function toOverwriteOptions(config) {
-    const options = []
-
-    for (const packageName of Object.keys(config)) {
-        const packageConfig = config[packageName]
-
-        for (const variableName of Object.keys(packageConfig)) {
-            const value = packageConfig[variableName]
-
-            options.push(`--${packageName}:${variableName}=${value}`)
-        }
-    }
-
-    return options
-}
-
-/**
- * Converts a given config object to an `--a=b` style option array.
- *
- * @param {object|null} config -
- *   A map-like object to set configs.
- * @returns {string[]} `--a=b` style options.
- */
-function toConfigOptions(config) {
-    return Object.keys(config).map(key => `--${key}=${config[key]}`)
-}
-
-/**
- * Gets the maximum length.
- *
- * @param {number} length - The current maximum length.
- * @param {string} name - A name.
- * @returns {number} The maximum length.
- */
-function maxLength(length, name) {
-    return Math.max(name.length, length)
-}
-
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
-
-/**
- * Runs npm-scripts which are matched with given patterns.
- *
- * @param {string|string[]} patternOrPatterns - Patterns to run.
- *   A pattern is a npm-script name or a Glob-like pattern.
- * @param {object|undefined} [options] Optional.
- * @param {boolean} options.parallel -
- *   If this is `true`, run scripts in parallel.
- *   Otherwise, run scripts in sequencial.
- *   Default is `false`.
- * @param {stream.Readable|null} options.stdin -
- *   A readable stream to send messages to stdin of child process.
- *   If this is `null`, ignores it.
- *   If this is `process.stdin`, inherits it.
- *   Otherwise, makes a pipe.
- *   Default is `null`.
- * @param {stream.Writable|null} options.stdout -
- *   A writable stream to receive messages from stdout of child process.
- *   If this is `null`, cannot send.
- *   If this is `process.stdout`, inherits it.
- *   Otherwise, makes a pipe.
- *   Default is `null`.
- * @param {stream.Writable|null} options.stderr -
- *   A writable stream to receive messages from stderr of child process.
- *   If this is `null`, cannot send.
- *   If this is `process.stderr`, inherits it.
- *   Otherwise, makes a pipe.
- *   Default is `null`.
- * @param {string[]} options.taskList -
- *   Actual name list of npm-scripts.
- *   This function search npm-script names in this list.
- *   If this is `null`, this function reads `package.json` of current directly.
- * @param {object|null} options.packageConfig -
- *   A map-like object to overwrite package configs.
- *   Keys are package names.
- *   Every value is a map-like object (Pairs of variable name and value).
- *   e.g. `{"npm-run-all": {"test": 777}}`
- *   Default is `null`.
- * @param {boolean} options.silent -
- *   The flag to set `silent` to the log level of npm.
- *   Default is `false`.
- * @param {boolean} options.continueOnError -
- *   The flag to ignore errors.
- *   Default is `false`.
- * @param {boolean} options.printLabel -
- *   The flag to print task names at the head of each line.
- *   Default is `false`.
- * @param {boolean} options.printName -
- *   The flag to print task names before running each task.
- *   Default is `false`.
- * @param {number} options.maxParallel -
- *   The maximum number of parallelism.
- *   Default is unlimited.
- * @param {string} options.npmPath -
- *   The path to npm.
- *   Default is `process.env.npm_execpath`.
- * @returns {Promise}
- *   A promise object which becomes fullfilled when all npm-scripts are completed.
- */
-module.exports = function npmRunAll(patternOrPatterns, options) { //eslint-disable-line complexity
-    const stdin = (options && options.stdin) || null
-    const stdout = (options && options.stdout) || null
-    const stderr = (options && options.stderr) || null
-    const taskList = (options && options.taskList) || null
-    const config = (options && options.config) || null
-    const packageConfig = (options && options.packageConfig) || null
-    const args = (options && options.arguments) || []
-    const parallel = Boolean(options && options.parallel)
-    const silent = Boolean(options && options.silent)
-    const continueOnError = Boolean(options && options.continueOnError)
-    const printLabel = Boolean(options && options.printLabel)
-    const printName = Boolean(options && options.printName)
-    const race = Boolean(options && options.race)
-    const maxParallel = parallel ? ((options && options.maxParallel) || 0) : 1
-    const aggregateOutput = Boolean(options && options.aggregateOutput)
-    const npmPath = options && options.npmPath
-    try {
-        const patterns = parsePatterns(patternOrPatterns, args)
-        if (patterns.length === 0) {
-            return Promise.resolve(null)
-        }
-        if (taskList != null && Array.isArray(taskList) === false) {
-            throw new Error("Invalid options.taskList")
-        }
-        if (typeof maxParallel !== "number" || !(maxParallel >= 0)) {
-            throw new Error("Invalid options.maxParallel")
-        }
-        if (!parallel && aggregateOutput) {
-            throw new Error("Invalid options.aggregateOutput; It requires options.parallel")
-        }
-        if (!parallel && race) {
-            throw new Error("Invalid options.race; It requires options.parallel")
-        }
-
-        const prefixOptions = [].concat(
-            silent ? ["--silent"] : [],
-            packageConfig ? toOverwriteOptions(packageConfig) : [],
-            config ? toConfigOptions(config) : []
-        )
-
-        return Promise.resolve()
-            .then(() => {
-                if (taskList != null) {
-                    return { taskList, packageInfo: null }
-                }
-                return readPackageJson()
-            })
-            .then(x => {
-                const tasks = matchTasks(x.taskList, patterns)
-                const labelWidth = tasks.reduce(maxLength, 0)
-
-                return runTasks(tasks, {
-                    stdin,
-                    stdout,
-                    stderr,
-                    prefixOptions,
-                    continueOnError,
-                    labelState: {
-                        enabled: printLabel,
-                        width: labelWidth,
-                        lastPrefix: null,
-                        lastIsLinebreak: true,
-                    },
-                    printName,
-                    packageInfo: x.packageInfo,
-                    race,
-                    maxParallel,
-                    npmPath,
-                    aggregateOutput,
-                })
-            })
-    }
-    catch (err) {
-        return Promise.reject(new Error(err.message))
-    }
-}
diff --git a/lib/match-tasks.js b/lib/match-tasks.js
deleted file mode 100644
index 63a0de3..0000000
--- a/lib/match-tasks.js
+++ /dev/null
@@ -1,128 +0,0 @@
-/**
- * @module match-tasks
- * @author Toru Nagashima
- * @copyright 2015 Toru Nagashima. All rights reserved.
- * See LICENSE file in root directory for full license.
- */
-"use strict"
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const Minimatch = require("minimatch").Minimatch
-
-//------------------------------------------------------------------------------
-// Helpers
-//------------------------------------------------------------------------------
-
-const COLON_OR_SLASH = /[:/]/g
-const CONVERT_MAP = { ":": "/", "/": ":" }
-
-/**
- * Swaps ":" and "/", in order to use ":" as the separator in minimatch.
- *
- * @param {string} s - A text to swap.
- * @returns {string} The text which was swapped.
- */
-function swapColonAndSlash(s) {
-    return s.replace(COLON_OR_SLASH, (matched) => CONVERT_MAP[matched])
-}
-
-/**
- * Creates a filter from user-specified pattern text.
- *
- * The task name is the part until the first space.
- * The rest part is the arguments for this task.
- *
- * @param {string} pattern - A pattern to create filter.
- * @returns {{match: function, task: string, args: string}} The filter object of the pattern.
- */
-function createFilter(pattern) {
-    const trimmed = pattern.trim()
-    const spacePos = trimmed.indexOf(" ")
-    const task = spacePos < 0 ? trimmed : trimmed.slice(0, spacePos)
-    const args = spacePos < 0 ? "" : trimmed.slice(spacePos)
-    const matcher = new Minimatch(swapColonAndSlash(task))
-    const match = matcher.match.bind(matcher)
-
-    return { match, task, args }
-}
-
-/**
- * The set to remove overlapped task.
- */
-class TaskSet {
-    /**
-     * Creates a instance.
-     */
-    constructor() {
-        this.result = []
-        this.sourceMap = Object.create(null)
-    }
-
-    /**
-     * Adds a command (a pattern) into this set if it's not overlapped.
-     * "Overlapped" is meaning that the command was added from a different source.
-     *
-     * @param {string} command - A pattern text to add.
-     * @param {string} source - A task name to check.
-     * @returns {void}
-     */
-    add(command, source) {
-        const sourceList = this.sourceMap[command] || (this.sourceMap[command] = [])
-        if (sourceList.length === 0 || sourceList.indexOf(source) !== -1) {
-            this.result.push(command)
-        }
-        sourceList.push(source)
-    }
-}
-
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
-
-/**
- * Enumerates tasks which matches with given patterns.
- *
- * @param {string[]} taskList - A list of actual task names.
- * @param {string[]} patterns - Pattern texts to match.
- * @returns {string[]} Tasks which matches with the patterns.
- * @private
- */
-module.exports = function matchTasks(taskList, patterns) {
-    const filters = patterns.map(createFilter)
-    const candidates = taskList.map(swapColonAndSlash)
-    const taskSet = new TaskSet()
-    const unknownSet = Object.create(null)
-
-    // Take tasks while keep the order of patterns.
-    for (const filter of filters) {
-        let found = false
-
-        for (const candidate of candidates) {
-            if (filter.match(candidate)) {
-                found = true
-                taskSet.add(
-                    swapColonAndSlash(candidate) + filter.args,
-                    filter.task
-                )
-            }
-        }
-
-        // Built-in tasks should be allowed.
-        if (!found && (filter.task === "restart" || filter.task === "env")) {
-            taskSet.add(filter.task + filter.args, filter.task)
-            found = true
-        }
-        if (!found) {
-            unknownSet[filter.task] = true
-        }
-    }
-
-    const unknownTasks = Object.keys(unknownSet)
-    if (unknownTasks.length > 0) {
-        throw new Error(`Task not found: "${unknownTasks.join("\", ")}"`)
-    }
-    return taskSet.result
-}
diff --git a/lib/npm-run-all-error.js b/lib/npm-run-all-error.js
deleted file mode 100644
index af08b09..0000000
--- a/lib/npm-run-all-error.js
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
- * @module npm-run-all-error
- * @author Toru Nagashima
- * @copyright 2016 Toru Nagashima. All rights reserved.
- * See LICENSE file in root directory for full license.
- */
-"use strict"
-
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
-
-/**
- * Error object with some additional info.
- */
-module.exports = class NpmRunAllError extends Error {
-    /**
-     * Constructor.
-     *
-     * @param {{name: string, code: number}} causeResult -
-     *      The result item of the npm-script which causes an error.
-     * @param {Array.<{name: string, code: (number|undefined)}>} allResults -
-     *      All result items of npm-scripts.
-     */
-    constructor(causeResult, allResults) {
-        super(`"${causeResult.task}" exited with ${causeResult.code}.`)
-
-        /**
-         * The name of a npm-script which exited with a non-zero code.
-         * @type {string}
-         */
-        this.name = causeResult.name
-
-        /**
-         * The code of a npm-script which exited with a non-zero code.
-         * This can be `undefined`.
-         * @type {number}
-         */
-        this.code = causeResult.code
-
-        /**
-         * All result items of npm-scripts.
-         * @type {Array.<{name: string, code: (number|undefined)}>}
-         */
-        this.results = allResults
-    }
-}
diff --git a/lib/read-package-json.js b/lib/read-package-json.js
deleted file mode 100644
index 1497ebf..0000000
--- a/lib/read-package-json.js
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * @module read-package-json
- * @author Toru Nagashima
- * @copyright 2016 Toru Nagashima. All rights reserved.
- * See LICENSE file in root directory for full license.
- */
-"use strict"
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const joinPath = require("path").join
-const readPkg = require("read-pkg")
-
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
-
-/**
- * Reads the package.json in the current directory.
- *
- * @returns {object} package.json's information.
- */
-module.exports = function readPackageJson() {
-    const path = joinPath(process.cwd(), "package.json")
-    return readPkg(path).then(body => ({
-        taskList: Object.keys(body.scripts || {}),
-        packageInfo: { path, body },
-    }))
-}
diff --git a/lib/run-task.js b/lib/run-task.js
deleted file mode 100644
index ee70000..0000000
--- a/lib/run-task.js
+++ /dev/null
@@ -1,190 +0,0 @@
-/**
- * @module run-task
- * @author Toru Nagashima
- * @copyright 2015 Toru Nagashima. All rights reserved.
- * See LICENSE file in root directory for full license.
- */
-"use strict"
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const path = require("path")
-const chalk = require("chalk")
-const parseArgs = require("shell-quote").parse
-const padEnd = require("string.prototype.padend")
-const createHeader = require("./create-header")
-const createPrefixTransform = require("./create-prefix-transform-stream")
-const spawn = require("./spawn")
-
-//------------------------------------------------------------------------------
-// Helpers
-//------------------------------------------------------------------------------
-
-const colors = [chalk.cyan, chalk.green, chalk.magenta, chalk.yellow, chalk.red]
-
-let colorIndex = 0
-const taskNamesToColors = new Map()
-
-/**
- * Select a color from given task name.
- *
- * @param {string} taskName - The task name.
- * @returns {function} A colorize function that provided by `chalk`
- */
-function selectColor(taskName) {
-    let color = taskNamesToColors.get(taskName)
-    if (!color) {
-        color = colors[colorIndex]
-        colorIndex = (colorIndex + 1) % colors.length
-        taskNamesToColors.set(taskName, color)
-    }
-    return color
-}
-
-/**
- * Wraps stdout/stderr with a transform stream to add the task name as prefix.
- *
- * @param {string} taskName - The task name.
- * @param {stream.Writable} source - An output stream to be wrapped.
- * @param {object} labelState - An label state for the transform stream.
- * @returns {stream.Writable} `source` or the created wrapped stream.
- */
-function wrapLabeling(taskName, source, labelState) {
-    if (source == null || !labelState.enabled) {
-        return source
-    }
-
-    const label = padEnd(taskName, labelState.width)
-    const color = source.isTTY ? selectColor(taskName) : (x) => x
-    const prefix = color(`[${label}] `)
-    const stream = createPrefixTransform(prefix, labelState)
-
-    stream.pipe(source)
-
-    return stream
-}
-
-/**
- * Converts a given stream to an option for `child_process.spawn`.
- *
- * @param {stream.Readable|stream.Writable|null} stream - An original stream to convert.
- * @param {process.stdin|process.stdout|process.stderr} std - A standard stream for this option.
- * @returns {string|stream.Readable|stream.Writable} An option for `child_process.spawn`.
- */
-function detectStreamKind(stream, std) {
-    return (
-        stream == null ? "ignore" :
-        // `|| !std.isTTY` is needed for the workaround of https://github.com/nodejs/node/issues/5620
-        stream !== std || !std.isTTY ? "pipe" :
-        /* else */ stream
-    )
-}
-
-//------------------------------------------------------------------------------
-// Interface
-//------------------------------------------------------------------------------
-
-/**
- * Run a npm-script of a given name.
- * The return value is a promise which has an extra method: `abort()`.
- * The `abort()` kills the child process to run the npm-script.
- *
- * @param {string} task - A npm-script name to run.
- * @param {object} options - An option object.
- * @param {stream.Readable|null} options.stdin -
- *   A readable stream to send messages to stdin of child process.
- *   If this is `null`, ignores it.
- *   If this is `process.stdin`, inherits it.
- *   Otherwise, makes a pipe.
- * @param {stream.Writable|null} options.stdout -
- *   A writable stream to receive messages from stdout of child process.
- *   If this is `null`, cannot send.
- *   If this is `process.stdout`, inherits it.
- *   Otherwise, makes a pipe.
- * @param {stream.Writable|null} options.stderr -
- *   A writable stream to receive messages from stderr of child process.
- *   If this is `null`, cannot send.
- *   If this is `process.stderr`, inherits it.
- *   Otherwise, makes a pipe.
- * @param {string[]} options.prefixOptions -
- *   An array of options which are inserted before the task name.
- * @param {object} options.labelState - A state object for printing labels.
- * @param {boolean} options.printName - The flag to print task names before running each task.
- * @returns {Promise}
- *   A promise object which becomes fullfilled when the npm-script is completed.
- *   This promise object has an extra method: `abort()`.
- * @private
- */
-module.exports = function runTask(task, options) {
-    let cp = null
-    const promise = new Promise((resolve, reject) => {
-        const stdin = options.stdin
-        const stdout = wrapLabeling(task, options.stdout, options.labelState)
-        const stderr = wrapLabeling(task, options.stderr, options.labelState)
-        const stdinKind = detectStreamKind(stdin, process.stdin)
-        const stdoutKind = detectStreamKind(stdout, process.stdout)
-        const stderrKind = detectStreamKind(stderr, process.stderr)
-        const spawnOptions = { stdio: [stdinKind, stdoutKind, stderrKind] }
-
-        // Print task name.
-        if (options.printName && stdout != null) {
-            stdout.write(createHeader(
-                task,
-                options.packageInfo,
-                options.stdout.isTTY
-            ))
-        }
-
-        // Execute.
-        const npmPath = options.npmPath || process.env.npm_execpath //eslint-disable-line no-process-env
-        const npmPathIsJs = typeof npmPath === "string" && /\.m?js/.test(path.extname(npmPath))
-        const execPath = (npmPathIsJs ? process.execPath : npmPath || "npm")
-        const isYarn = path.basename(npmPath || "npm").startsWith("yarn")
-        const spawnArgs = ["run"]
-
-        if (npmPathIsJs) {
-            spawnArgs.unshift(npmPath)
-        }
-        if (!isYarn) {
-            Array.prototype.push.apply(spawnArgs, options.prefixOptions)
-        }
-        else if (options.prefixOptions.indexOf("--silent") !== -1) {
-            spawnArgs.push("--silent")
-        }
-        Array.prototype.push.apply(spawnArgs, parseArgs(task))
-
-        cp = spawn(execPath, spawnArgs, spawnOptions)
-
-        // Piping stdio.
-        if (stdinKind === "pipe") {
-            stdin.pipe(cp.stdin)
-        }
-        if (stdoutKind === "pipe") {
-            cp.stdout.pipe(stdout, { end: false })
-        }
-        if (stderrKind === "pipe") {
-            cp.stderr.pipe(stderr, { end: false })
-        }
-
-        // Register
-        cp.on("error", (err) => {
-            cp = null
-            reject(err)
-        })
-        cp.on("close", (code) => {
-            cp = null
-            resolve({ task, code })
-        })
-    })
-
-    promise.abort = function abort() {
-        if (cp != null) {
-            cp.kill()
-            cp = null
-        }
-    }
-
-    return promise
-}
diff --git a/lib/run-tasks.js b/lib/run-tasks.js
deleted file mode 100644
index 64a4506..0000000
--- a/lib/run-tasks.js
+++ /dev/null
@@ -1,177 +0,0 @@
-/**
- * @module run-tasks-in-parallel
- * @author Toru Nagashima
- * @copyright 2015 Toru Nagashima. All rights reserved.
- * See LICENSE file in root directory for full license.
- */
-"use strict"
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const MemoryStream = require("memorystream")
-const NpmRunAllError = require("./npm-run-all-error")
-const runTask = require("./run-task")
-
-//------------------------------------------------------------------------------
-// Helpers
-//------------------------------------------------------------------------------
-
-/**
- * Remove the given value from the array.
- * @template T
- * @param {T[]} array - The array to remove.
- * @param {T} x - The item to be removed.
- * @returns {void}
- */
-function remove(array, x) {
-    const index = array.indexOf(x)
-    if (index !== -1) {
-        array.splice(index, 1)
-    }
-}
-
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
-
-/**
- * Run npm-scripts of given names in parallel.
- *
- * If a npm-script exited with a non-zero code, this aborts other all npm-scripts.
- *
- * @param {string} tasks - A list of npm-script name to run in parallel.
- * @param {object} options - An option object.
- * @returns {Promise} A promise object which becomes fullfilled when all npm-scripts are completed.
- * @private
- */
-module.exports = function runTasks(tasks, options) {
-    return new Promise((resolve, reject) => {
-        if (tasks.length === 0) {
-            resolve([])
-            return
-        }
-
-        const results = tasks.map(task => ({ name: task, code: undefined }))
-        const queue = tasks.map((task, index) => ({ name: task, index }))
-        const promises = []
-        let error = null
-        let aborted = false
-
-        /**
-         * Done.
-         * @returns {void}
-         */
-        function done() {
-            if (error == null) {
-                resolve(results)
-            }
-            else {
-                reject(error)
-            }
-        }
-
-        /**
-         * Aborts all tasks.
-         * @returns {void}
-         */
-        function abort() {
-            if (aborted) {
-                return
-            }
-            aborted = true
-
-            if (promises.length === 0) {
-                done()
-            }
-            else {
-                for (const p of promises) {
-                    p.abort()
-                }
-                Promise.all(promises).then(done, reject)
-            }
-        }
-
-        /**
-         * Runs a next task.
-         * @returns {void}
-         */
-        function next() {
-            if (aborted) {
-                return
-            }
-            if (queue.length === 0) {
-                if (promises.length === 0) {
-                    done()
-                }
-                return
-            }
-
-            const originalOutputStream = options.stdout
-            const optionsClone = Object.assign({}, options)
-            const writer = new MemoryStream(null, {
-                readable: false,
-            })
-
-            if (options.aggregateOutput) {
-                optionsClone.stdout = writer
-            }
-
-            const task = queue.shift()
-            const promise = runTask(task.name, optionsClone)
-
-            promises.push(promise)
-            promise.then(
-                (result) => {
-                    remove(promises, promise)
-                    if (aborted) {
-                        return
-                    }
-
-                    if (options.aggregateOutput) {
-                        originalOutputStream.write(writer.toString())
-                    }
-
-                    // Save the result.
-                    results[task.index].code = result.code
-
-                    // Aborts all tasks if it's an error.
-                    if (result.code) {
-                        error = new NpmRunAllError(result, results)
-                        if (!options.continueOnError) {
-                            abort()
-                            return
-                        }
-                    }
-
-                    // Aborts all tasks if options.race is true.
-                    if (options.race && !result.code) {
-                        abort()
-                        return
-                    }
-
-                    // Call the next task.
-                    next()
-                },
-                (thisError) => {
-                    remove(promises, promise)
-                    if (!options.continueOnError || options.race) {
-                        error = thisError
-                        abort()
-                        return
-                    }
-                    next()
-                }
-            )
-        }
-
-        const max = options.maxParallel
-        const end = (typeof max === "number" && max > 0)
-            ? Math.min(tasks.length, max)
-            : tasks.length
-        for (let i = 0; i < end; ++i) {
-            next()
-        }
-    })
-}
diff --git a/lib/spawn-posix.js b/lib/spawn-posix.js
deleted file mode 100644
index 1fb5de0..0000000
--- a/lib/spawn-posix.js
+++ /dev/null
@@ -1,64 +0,0 @@
-/**
- * @module spawn-posix
- * @author Toru Nagashima
- * @copyright 2015 Toru Nagashima. All rights reserved.
- * See LICENSE file in root directory for full license.
- */
-"use strict"
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const crossSpawn = require("cross-spawn")
-const getDescendentProcessInfo = require("ps-tree")
-
-//------------------------------------------------------------------------------
-// Helpers
-//------------------------------------------------------------------------------
-
-/**
- * Kills the new process and its sub processes.
- * @this ChildProcess
- * @returns {void}
- */
-function kill() {
-    getDescendentProcessInfo(this.pid, (err, descendent) => {
-        if (err) {
-            return
-        }
-
-        for (const child of descendent) {
-            try {
-                process.kill(child.PID)
-            }
-            catch (_err) {
-                // ignore.
-            }
-        }
-    })
-}
-
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
-
-/**
- * Launches a new process with the given command.
- * This is almost same as `child_process.spawn`.
- *
- * This returns a `ChildProcess` instance.
- * `kill` method of the instance kills the new process and its sub processes.
- *
- * @param {string} command - The command to run.
- * @param {string[]} args - List of string arguments.
- * @param {object} options - Options.
- * @returns {ChildProcess} A ChildProcess instance of new process.
- * @private
- */
-module.exports = function spawn(command, args, options) {
-    const child = crossSpawn(command, args, options)
-    child.kill = kill
-
-    return child
-}
diff --git a/lib/spawn-win32.js b/lib/spawn-win32.js
deleted file mode 100644
index 3743a1d..0000000
--- a/lib/spawn-win32.js
+++ /dev/null
@@ -1,50 +0,0 @@
-/**
- * @module spawn-win32
- * @author Toru Nagashima
- * @copyright 2015 Toru Nagashima. All rights reserved.
- * See LICENSE file in root directory for full license.
- */
-"use strict"
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const crossSpawn = require("cross-spawn")
-
-//------------------------------------------------------------------------------
-// Helpers
-//------------------------------------------------------------------------------
-
-/**
- * Kills the new process and its sub processes forcibly.
- * @this ChildProcess
- * @returns {void}
- */
-function kill() {
-    crossSpawn("taskkill", ["/F", "/T", "/PID", this.pid])
-}
-
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
-
-/**
- * Launches a new process with the given command.
- * This is almost same as `child_process.spawn`.
- *
- * This returns a `ChildProcess` instance.
- * `kill` method of the instance kills the new process and its sub processes forcibly.
- *
- * @param {string} command - The command to run.
- * @param {string[]} args - List of string arguments.
- * @param {object} options - Options.
- * @returns {ChildProcess} A ChildProcess instance of new process.
- * @private
- */
-module.exports = function spawn(command, args, options) {
-    const child = crossSpawn(command, args, options)
-    child.kill = kill
-
-    return child
-}
diff --git a/lib/spawn.js b/lib/spawn.js
deleted file mode 100644
index 1392817..0000000
--- a/lib/spawn.js
+++ /dev/null
@@ -1,20 +0,0 @@
-/**
- * @module spawn
- * @author Toru Nagashima
- * @copyright 2015 Toru Nagashima. All rights reserved.
- * See LICENSE file in root directory for full license.
- */
-"use strict"
-
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
-
-/**
- * Launches a new process with the given command.
- * This is {@link ./spawn-posix.js:spawn} or {@link ./spawn-win32.js:spawn}
- * @private
- */
-module.exports = require(
-    process.platform === "win32" ? "./spawn-win32" : "./spawn-posix"
-)
diff --git a/package.json b/package.json
index 36f6f66..2886687 100644
--- a/package.json
+++ b/package.json
@@ -3,22 +3,21 @@
   "version": "4.1.2",
   "description": "A CLI tool to run multiple npm-scripts in parallel or sequential.",
   "bin": {
-    "run-p": "bin/run-p/index.js",
-    "run-s": "bin/run-s/index.js",
-    "npm-run-all": "bin/npm-run-all/index.js"
+    "run-p": "dist/run-p.js",
+    "run-s": "dist/run-s.js",
+    "npm-run-all": "dist/npm-run-all.js"
   },
-  "main": "lib/index.js",
+  "main": "dist/index.js",
   "files": [
-    "bin",
-    "lib",
-    "docs"
+    "dist"
   ],
   "engines": {
-    "node": ">= 4"
+    "node": ">=6.5.0"
   },
   "scripts": {
     "_mocha": "mocha \"test/*.js\" --compilers js:babel-register --timeout 60000",
-    "clean": "rimraf .nyc_output coverage jsdoc \"test-workspace/{build,test.txt}\"",
+    "build": "rimraf .temp dist && tsc && rollup -c",
+    "clean": "rimraf .nyc_output .temp coverage dist \"test-workspace/{build,test.txt}\"",
     "docs": "jsdoc -c jsdoc.json",
     "lint": "eslint bin lib scripts test \"test-workspace/tasks/*.js\"",
     "pretest": "node scripts/make-slink.js && npm run lint",
@@ -29,32 +28,43 @@
     "codecov": "nyc report -r lcovonly && codecov"
   },
   "dependencies": {
-    "ansi-styles": "^3.2.0",
-    "chalk": "^2.1.0",
-    "cross-spawn": "^5.1.0",
-    "memorystream": "^0.3.1",
+    "abort-controller": "^1.0.2",
+    "ansi-styles": "^3.2.1",
+    "chalk": "^2.4.1",
+    "cross-spawn": "^6.0.5",
+    "fs-extra": "^6.0.1",
     "minimatch": "^3.0.4",
+    "p-queue": "^2.4.2",
     "ps-tree": "^1.1.0",
-    "read-pkg": "^3.0.0",
     "shell-quote": "^1.6.1",
     "string.prototype.padend": "^3.0.0"
   },
   "devDependencies": {
-    "@types/node": "^4.2.20",
-    "babel-plugin-transform-async-to-generator": "^6.24.1",
-    "babel-preset-power-assert": "^1.0.0",
-    "babel-register": "^6.26.0",
-    "codecov": "^2.3.0",
-    "eslint": "^4.5.0",
-    "eslint-config-mysticatea": "^12.0.0",
-    "fs-extra": "^4.0.2",
-    "jsdoc": "^3.5.4",
-    "mocha": "^3.5.0",
-    "nyc": "^11.1.0",
-    "p-queue": "^2.2.0",
-    "power-assert": "^1.4.4",
-    "rimraf": "^2.6.1",
-    "yarn": "^1.2.1"
+    "@babel/core": "^7.0.0-beta.46",
+    "@babel/plugin-syntax-dynamic-import": "^7.0.0-beta.46",
+    "@babel/preset-env": "^7.0.0-beta.46",
+    "@types/ansi-styles": "^3.2.0",
+    "@types/cross-spawn": "^6.0.0",
+    "@types/fs-extra": "^5.0.2",
+    "@types/minimatch": "^3.0.3",
+    "@types/mocha": "^5.2.0",
+    "@types/node": "^10.0.8",
+    "@types/p-queue": "^2.3.1",
+    "@types/shell-quote": "^1.6.0",
+    "codecov": "^3.0.2",
+    "eslint": "^4.19.1",
+    "eslint-plugin-mysticatea": "^5.0.0-beta.8",
+    "mocha": "^5.1.1",
+    "nyc": "^11.7.3",
+    "rimraf": "^2.6.2",
+    "rollup": "^0.58.2",
+    "rollup-plugin-babel": "^4.0.0-beta.4",
+    "rollup-plugin-json": "^3.0.0",
+    "rollup-plugin-node-resolve": "^3.3.0",
+    "rollup-plugin-sourcemaps": "^0.4.2",
+    "ts-node": "^6.0.3",
+    "typescript": "^2.8.3",
+    "yarn": "^1.6.0"
   },
   "repository": "mysticatea/npm-run-all",
   "keywords": [
diff --git a/rollup.config.js b/rollup.config.js
new file mode 100644
index 0000000..ec8daf0
--- /dev/null
+++ b/rollup.config.js
@@ -0,0 +1,52 @@
+/**
+ * @author Toru Nagashima <https://github.com/mysticatea>
+ * See LICENSE file in root directory for full license.
+ */
+import fs from "fs-extra"
+import babel from "rollup-plugin-babel"
+import json from "rollup-plugin-json"
+import nodeResolve from "rollup-plugin-node-resolve"
+import sourcemaps from "rollup-plugin-sourcemaps"
+
+const { dependencies } = fs.readJSONSync("package.json")
+
+export default {
+    experimentalCodeSplitting: true,
+    experimentalDynamicImport: true,
+    external: Object.keys(dependencies).concat(["path", "readline", "stream"]),
+
+    input: [
+        ".temp/bin/npm-run-all.js",
+        ".temp/bin/run-s.js",
+        ".temp/bin/run-p.js",
+        ".temp/lib/index.js",
+    ],
+    output: {
+        dir: "dist",
+        format: "cjs",
+        sourcemap: true,
+        strict: true,
+        banner: `/*! @author Toru Nagashima <https://github.com/mysticatea> */\n`,
+    },
+
+    plugins: [
+        json(),
+        nodeResolve(),
+        babel({
+            babelrc: false,
+            plugins: ["@babel/plugin-syntax-dynamic-import"],
+            presets: [
+                [
+                    "@babel/preset-env",
+                    {
+                        modules: false,
+                        targets: {
+                            node: "6.5.0",
+                        },
+                    },
+                ],
+            ],
+        }),
+        sourcemaps(),
+    ],
+}
diff --git a/src/bin/cli-parameter-parser.ts b/src/bin/cli-parameter-parser.ts
new file mode 100644
index 0000000..49ff0de
--- /dev/null
+++ b/src/bin/cli-parameter-parser.ts
@@ -0,0 +1,193 @@
+/**
+ * @author Toru Nagashima
+ * See LICENSE file in root directory for full license.
+ */
+import { Options } from "../lib"
+
+/*eslint-disable no-process-env */
+
+const OVERWRITE_OPTION = /^--([^:]+?):([^=]+?)(?:=(.+))?$/
+const CONFIG_OPTION = /^--([^=]+?)(?:=(.+))$/
+const PACKAGE_CONFIG_PATTERN = /^npm_package_config_(.+)$/
+const CONCAT_OPTIONS = /^-[clnprs]+$/
+
+/**
+ * Overwrites a specified package config.
+ */
+function overwriteConfig(
+    config: { [key: string]: { [key: string]: string } },
+    packageName: string,
+    variable: string,
+    value: string,
+): void {
+    const scope = config[packageName] || (config[packageName] = {})
+    scope[variable] = value
+}
+
+/**
+ * Creates a package config object.
+ * This checks `process.env` and creates the default value.
+ */
+function createPackageConfig(): { [key: string]: { [key: string]: string } } {
+    const retv: { [key: string]: { [key: string]: string } } = {}
+    const packageName = process.env.npm_package_name
+    if (!packageName) {
+        return retv
+    }
+
+    for (const key of Object.keys(process.env)) {
+        const m = PACKAGE_CONFIG_PATTERN.exec(key)
+        if (m != null) {
+            overwriteConfig(retv, packageName, m[1], process.env[key] as string)
+        }
+    }
+
+    return retv
+}
+
+/**
+ * Common CLI parameters of npm-run-all, run-s, and run-p.
+ */
+export default class CLIParameterParser implements Options {
+    protected args: string[]
+    protected index = 0
+
+    public patterns: string[] = []
+
+    public config: { [key: string]: string } = {}
+    public packageConfig = createPackageConfig()
+    public arguments: string[] = []
+    public parallel: boolean
+    public silent = process.env.npm_config_loglevel === "silent"
+    public continueOnError = false
+    public printLabel = false
+    public printName = false
+    public race = false
+    public aggregateOutput = false
+    public maxParallel = 1
+    public npmPath: string | undefined = undefined
+
+    /** Initialize this parser */
+    public constructor(args: string[], parallel = false) {
+        this.args = args
+        this.parallel = parallel
+
+        Object.defineProperties(this, {
+            args: { enumerable: false },
+            index: { enumerable: false },
+            patterns: { enumerable: false },
+        })
+
+        while (this.index < this.args.length) {
+            this.next()
+        }
+
+        if (!this.parallel) {
+            if (this.aggregateOutput) {
+                throw new Error(
+                    "Invalid Option: --aggregate-output (without parallel)",
+                )
+            }
+            if (this.race) {
+                throw new Error(`Invalid Option: --race (without parallel)`)
+            }
+            if (this.maxParallel !== 0) {
+                throw new Error(
+                    "Invalid Option: --max-parallel (without parallel)",
+                )
+            }
+        }
+    }
+
+    //eslint-disable-next-line complexity, require-jsdoc
+    protected next(): void {
+        const arg = this.args[this.index++]
+        switch (arg) {
+            case "--":
+                this.arguments = this.args.slice(this.index)
+                this.index = this.args.length
+                break
+
+            case "--aggregate-output":
+                this.aggregateOutput = true
+                break
+
+            case "--color":
+            case "--no-color":
+                // passthrough for chalk package.
+                break
+
+            case "-c":
+            case "--continue-on-error":
+                this.continueOnError = true
+                break
+
+            case "-l":
+            case "--print-label":
+                this.printLabel = true
+                break
+
+            case "-n":
+            case "--print-name":
+                this.printName = true
+                break
+
+            case "-r":
+            case "--race":
+                this.race = true
+                break
+
+            case "-s":
+            case "--silent":
+                this.silent = true
+                break
+
+            case "--max-parallel": {
+                const value = this.args[this.index++]
+                this.maxParallel = parseInt(value, 10)
+                if (
+                    !Number.isFinite(this.maxParallel) ||
+                    this.maxParallel <= 0
+                ) {
+                    throw new Error(`Invalid Option: --max-parallel ${value}`)
+                }
+                break
+            }
+
+            case "--npm-path":
+                this.npmPath = this.args[this.index++]
+                break
+
+            default: {
+                let matched = null
+                if ((matched = OVERWRITE_OPTION.exec(arg))) {
+                    overwriteConfig(
+                        this.packageConfig,
+                        matched[1],
+                        matched[2],
+                        matched[3] || this.args[this.index++],
+                    )
+                } else if ((matched = CONFIG_OPTION.exec(arg))) {
+                    this.config[matched[1]] = matched[2]
+                } else if (CONCAT_OPTIONS.test(arg)) {
+                    this.args.splice(
+                        this.index,
+                        0,
+                        ...arg
+                            .slice(1)
+                            .split("")
+                            .map(c => `-${c}`),
+                    )
+                } else if (arg[0] === "-") {
+                    throw new Error(`Invalid Option: ${arg}`)
+                } else {
+                    this.patterns.push(arg)
+                }
+
+                break
+            }
+        }
+    }
+}
+
+/*eslint-enable */
diff --git a/src/bin/npm-run-all.ts b/src/bin/npm-run-all.ts
new file mode 100644
index 0000000..252a543
--- /dev/null
+++ b/src/bin/npm-run-all.ts
@@ -0,0 +1,144 @@
+/**
+ * @author Toru Nagashima
+ * See LICENSE file in root directory for full license.
+ */
+import PQueue from "p-queue"
+import { version } from "../../package.json"
+import runScripts from "../lib"
+import CLIParameterParserBase from "./cli-parameter-parser"
+
+/**
+ * CLI parameter parser.
+ */
+class CLIParameterParser extends CLIParameterParserBase {
+    public groups: { patterns: string[]; parallel: boolean }[] = [
+        { patterns: this.patterns, parallel: false },
+    ]
+
+    /** Initialize this parser. */
+    public constructor(args: string[]) {
+        super(args)
+        Object.defineProperty(this, "groups", { enumerable: false })
+    }
+
+    /** Interprit the next argument */
+    protected next(): void {
+        const arg = this.args[this.index]
+        switch (arg) {
+            case "-s":
+            case "--sequential":
+            case "--serial": {
+                const patterns = (this.patterns = [])
+                this.index += 1
+                this.groups.push({ patterns, parallel: false })
+                break
+            }
+
+            case "-p":
+            case "--parallel": {
+                const patterns = (this.patterns = [])
+                this.index += 1
+                this.parallel = true
+                this.groups.push({ patterns, parallel: true })
+                break
+            }
+
+            default:
+                super.next()
+        }
+    }
+}
+
+/*eslint-disable no-console, no-process-exit */
+;(async (args: string[]) => {
+    const arg = args[0]
+
+    if (arg === undefined || arg === "-h" || arg === "--help") {
+        console.log(`
+Usage:
+    $ npm-run-all [--help | -h | --version | -v]
+    $ npm-run-all [scripts] [OPTIONS]
+
+    Run given npm-scripts in parallel or sequential.
+
+    <scripts> : A list of npm-scripts' names and Glob-like patterns.
+
+Options:
+    --aggregate-output   - - - Avoid interleaving output by delaying printing of
+                               each command's output until it has finished.
+    -c, --continue-on-error  - Set the flag to continue executing
+                               other/subsequent scripts even if a task threw an
+                               error. 'npm-run-all' itself will exit with
+                               non-zero code if one or more scripts threw
+                               error(s).
+    --max-parallel <number>  - Set the maximum number of parallelism. Default is
+                               unlimited.
+    --npm-path <string>  - - - Set the path to npm. Default is the value of
+                               environment variable npm_execpath.
+                               If the variable is not defined, then it's "npm".
+                               In this case, the "npm" command must be found in
+                               environment variable PATH.
+    -l, --print-label  - - - - Set the flag to print the task name as a prefix
+                               on each line of output. Tools in scripts may stop
+                               coloring their output if this option was given.
+    -n, --print-name   - - - - Set the flag to print the task name before
+                               running each task.
+    -p, --parallel <scripts> - Run a group of scripts in parallel.
+                               e.g. 'npm-run-all -p foo bar' is similar to
+                                    'npm run foo & npm run bar'.
+    -r, --race   - - - - - - - Set the flag to kill all scripts when a task
+                               finished with zero. This option is valid only
+                               with 'parallel' option.
+    -s, --sequential <scripts> Run a group of scripts sequentially.
+        --serial <scripts>     e.g. 'npm-run-all -s foo bar' is similar to
+                               'npm run foo && npm run bar'.
+                               '--serial' is a synonym of '--sequential'.
+    --silent   - - - - - - - - Set 'silent' to the log level of npm.
+
+Examples:
+    $ npm-run-all --serial clean lint build:**
+    $ npm-run-all --parallel watch:**
+    $ npm-run-all clean lint --parallel "build:** -- --watch"
+    $ npm-run-all -l -p start-server start-browser start-electron
+
+See Also:
+    https://github.com/mysticatea/npm-run-all#readme
+`)
+        process.exitCode = arg === undefined ? 1 : 0
+        return
+    }
+    if (arg === "-v" || arg === "--version") {
+        console.log(`v${version}`)
+        return
+    }
+
+    // https://github.com/mysticatea/npm-run-all/issues/105
+    // Avoid MaxListenersExceededWarnings.
+    process.stdout.setMaxListeners(0)
+    process.stderr.setMaxListeners(0)
+    process.stdin.setMaxListeners(0)
+
+    // Main
+    const queue = new PQueue({ concurrency: 1 })
+    const options = new CLIParameterParser(args)
+
+    await queue.addAll(
+        options.groups.map(g => () =>
+            runScripts(
+                g.patterns,
+                Object.assign({}, options, { parallel: g.parallel }),
+            ),
+        ),
+    )
+})(process.argv.slice(2)).then(
+    () => {
+        // I'm not sure why, but maybe the process never exits on Git Bash (MINGW64)
+        process.exit(0)
+    },
+    error => {
+        console.error(error.stack)
+        process.exit(1)
+    },
+)
+
+/*eslint-enable */
diff --git a/src/bin/package.json.d.ts b/src/bin/package.json.d.ts
new file mode 100644
index 0000000..879d981
--- /dev/null
+++ b/src/bin/package.json.d.ts
@@ -0,0 +1,8 @@
+/**
+ * @author Toru Nagashima <https://github.com/mysticatea>
+ * See LICENSE file in root directory for full license.
+ */
+
+declare module "*/package.json" {
+    export const version: string
+}
diff --git a/bin/run-p/help.js b/src/bin/run-p.ts
similarity index 57%
rename from bin/run-p/help.js
rename to src/bin/run-p.ts
index 873568f..d9f2df2 100644
--- a/bin/run-p/help.js
+++ b/src/bin/run-p.ts
@@ -1,38 +1,32 @@
 /**
  * @author Toru Nagashima
- * @copyright 2016 Toru Nagashima. All rights reserved.
  * See LICENSE file in root directory for full license.
  */
-"use strict"
+import { version } from "../../package.json"
+import runScripts from "../lib"
+import CLIParameterParser from "./cli-parameter-parser"
 
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
+/*eslint-disable no-console, no-process-exit */
+;(async (args: string[]) => {
+    const arg = args[0]
 
-/**
- * Print a help text.
- *
- * @param {stream.Writable} output - A writable stream to print.
- * @returns {Promise} Always a fulfilled promise.
- * @private
- */
-module.exports = function printHelp(output) {
-    output.write(`
+    if (arg === undefined || arg === "-h" || arg === "--help") {
+        console.log(`
 Usage:
     $ run-p [--help | -h | --version | -v]
-    $ run-p [OPTIONS] <tasks>
+    $ run-p [OPTIONS] <scripts>
 
     Run given npm-scripts in parallel.
 
-    <tasks> : A list of npm-scripts' names and Glob-like patterns.
+    <scripts> : A list of npm-scripts' names and Glob-like patterns.
 
 Options:
     --aggregate-output   - - - Avoid interleaving output by delaying printing of
                                each command's output until it has finished.
-    -c, --continue-on-error  - Set the flag to continue executing other tasks
+    -c, --continue-on-error  - Set the flag to continue executing other scripts
                                even if a task threw an error. 'run-p' itself
-                               will exit with non-zero code if one or more tasks
-                               threw error(s).
+                               will exit with non-zero code if one or more
+                               scripts threw error(s).
     --max-parallel <number>  - Set the maximum number of parallelism. Default is
                                unlimited.
     --npm-path <string>  - - - Set the path to npm. Default is the value of
@@ -41,11 +35,11 @@ Options:
                                In this case, the "npm" command must be found in
                                environment variable PATH.
     -l, --print-label  - - - - Set the flag to print the task name as a prefix
-                               on each line of output. Tools in tasks may stop
+                               on each line of output. Tools in scripts may stop
                                coloring their output if this option was given.
     -n, --print-name   - - - - Set the flag to print the task name before
                                running each task.
-    -r, --race   - - - - - - - Set the flag to kill all tasks when a task
+    -r, --race   - - - - - - - Set the flag to kill all scripts when a task
                                finished with zero.
     -s, --silent   - - - - - - Set 'silent' to the log level of npm.
 
@@ -61,6 +55,32 @@ Examples:
 See Also:
     https://github.com/mysticatea/npm-run-all#readme
 `)
+        process.exitCode = arg === undefined ? 1 : 0
+        return
+    }
+    if (arg === "-v" || arg === "--version") {
+        console.log(`v${version}`)
+        return
+    }
+
+    // https://github.com/mysticatea/npm-run-all/issues/105
+    // Avoid MaxListenersExceededWarnings.
+    process.stdout.setMaxListeners(0)
+    process.stderr.setMaxListeners(0)
+    process.stdin.setMaxListeners(0)
+
+    // Main
+    const options = new CLIParameterParser(args, true)
+    await runScripts(options.patterns, options)
+})(process.argv.slice(2)).then(
+    () => {
+        // I'm not sure why, but maybe the process never exits on Git Bash (MINGW64)
+        process.exit(0)
+    },
+    error => {
+        console.error(error.stack)
+        process.exit(1)
+    },
+)
 
-    return Promise.resolve(null)
-}
+/*eslint-enable */
diff --git a/bin/run-s/help.js b/src/bin/run-s.ts
similarity index 51%
rename from bin/run-s/help.js
rename to src/bin/run-s.ts
index 6dfa6a1..9f5ddce 100644
--- a/bin/run-s/help.js
+++ b/src/bin/run-s.ts
@@ -1,43 +1,37 @@
 /**
  * @author Toru Nagashima
- * @copyright 2016 Toru Nagashima. All rights reserved.
  * See LICENSE file in root directory for full license.
  */
-"use strict"
+import { version } from "../../package.json"
+import runScripts from "../lib"
+import CLIParameterParser from "./cli-parameter-parser"
 
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
+/*eslint-disable no-console, no-process-exit */
+;(async (args: string[]) => {
+    const arg = args[0]
 
-/**
- * Print a help text.
- *
- * @param {stream.Writable} output - A writable stream to print.
- * @returns {Promise} Always a fulfilled promise.
- * @private
- */
-module.exports = function printHelp(output) {
-    output.write(`
+    if (arg === undefined || arg === "-h" || arg === "--help") {
+        console.log(`
 Usage:
     $ run-s [--help | -h | --version | -v]
-    $ run-s [OPTIONS] <tasks>
+    $ run-s [OPTIONS] <scripts>
 
     Run given npm-scripts sequentially.
 
-    <tasks> : A list of npm-scripts' names and Glob-like patterns.
+    <scripts> : A list of npm-scripts' names and Glob-like patterns.
 
 Options:
     -c, --continue-on-error  - Set the flag to continue executing subsequent
-                               tasks even if a task threw an error. 'run-s'
+                               scripts even if a task threw an error. 'run-s'
                                itself will exit with non-zero code if one or
-                               more tasks threw error(s).
+                               more scripts threw error(s).
     --npm-path <string>  - - - Set the path to npm. Default is the value of
                                environment variable npm_execpath.
                                If the variable is not defined, then it's "npm."
                                In this case, the "npm" command must be found in
                                environment variable PATH.
     -l, --print-label  - - - - Set the flag to print the task name as a prefix
-                               on each line of output. Tools in tasks may stop
+                               on each line of output. Tools in scripts may stop
                                coloring their output if this option was given.
     -n, --print-name   - - - - Set the flag to print the task name before
                                running each task.
@@ -55,6 +49,32 @@ Examples:
 See Also:
     https://github.com/mysticatea/npm-run-all#readme
 `)
+        process.exitCode = arg === undefined ? 1 : 0
+        return
+    }
+    if (arg === "-v" || arg === "--version") {
+        console.log(`v${version}`)
+        return
+    }
+
+    // https://github.com/mysticatea/npm-run-all/issues/105
+    // Avoid MaxListenersExceededWarnings.
+    process.stdout.setMaxListeners(0)
+    process.stderr.setMaxListeners(0)
+    process.stdin.setMaxListeners(0)
+
+    // Main
+    const options = new CLIParameterParser(args, false)
+    await runScripts(options.patterns, options)
+})(process.argv.slice(2)).then(
+    () => {
+        // I'm not sure why, but maybe the process never exits on Git Bash (MINGW64)
+        process.exit(0)
+    },
+    error => {
+        console.error(error.stack)
+        process.exit(1)
+    },
+)
 
-    return Promise.resolve(null)
-}
+/*eslint-enable */
diff --git a/src/lib/aggregate-stream.ts b/src/lib/aggregate-stream.ts
new file mode 100644
index 0000000..a92393e
--- /dev/null
+++ b/src/lib/aggregate-stream.ts
@@ -0,0 +1,27 @@
+/**
+ * @author Toru Nagashima
+ * See LICENSE file in root directory for full license.
+ */
+import stream from "stream"
+
+/**
+ * The writable stream to aggregate data.
+ */
+export class AggregateStream extends stream.Writable {
+    private chunks: Buffer[] = []
+
+    /**
+     * The aggregated data.
+     */
+    public result() {
+        return Buffer.concat(this.chunks).toString()
+    }
+
+    /**
+     * Handle a written data.
+     */
+    public _write(chunk: Buffer, _encoding: string, callback: Function): void {
+        this.chunks.push(chunk)
+        callback(null)
+    }
+}
diff --git a/src/lib/index.ts b/src/lib/index.ts
new file mode 100644
index 0000000..8a54579
--- /dev/null
+++ b/src/lib/index.ts
@@ -0,0 +1,366 @@
+/**
+ * @author Toru Nagashima
+ * See LICENSE file in root directory for full license.
+ */
+import { AbortController, AbortSignal } from "abort-controller"
+import PQueue from "p-queue"
+import shellQuote from "shell-quote"
+import { runScript, RunScriptOptions } from "./scripts/run"
+import { AggregateStream } from "./aggregate-stream"
+import { readPackageInfo, PackageInfo } from "./package-info"
+import { ScriptError } from "./script-error"
+import { getMatchedScriptNames } from "./script-match"
+import { ScriptResult } from "./script-result"
+
+const ARGS_PATTERN = /\{(!)?([*@]|\d+)([^}]+)?}/g
+
+/**
+ * Normalized options.
+ */
+interface NormalizedOptions extends RunScriptOptions {
+    aggregateOutput: boolean
+    continueOnError: boolean
+    maxParallel: number
+    race: boolean
+    signal: AbortSignal | null
+}
+
+/**
+ * Converts a given value to an array.
+ */
+function ensureArray(x: string | string[] | null | undefined): string[] {
+    if (x == null) {
+        return []
+    }
+    return Array.isArray(x) ? x : [x]
+}
+
+/**
+ * Replaces argument placeholders (such as `{1}`) by arguments.
+ */
+function applyArguments(patterns: string[], args: string[]): string[] {
+    const defaults = Object.create(null)
+
+    return patterns.map(pattern =>
+        pattern.replace(
+            ARGS_PATTERN,
+            (
+                whole: string,
+                indirectionMark: string,
+                id: string,
+                options: string,
+            ): string => {
+                if (indirectionMark != null) {
+                    throw Error(`Invalid Placeholder: ${whole}`)
+                }
+                if (id === "@") {
+                    return shellQuote.quote(args)
+                }
+                if (id === "*") {
+                    return shellQuote.quote([args.join(" ")])
+                }
+
+                const position = parseInt(id, 10)
+                if (position >= 1 && position <= args.length) {
+                    return shellQuote.quote([args[position - 1]])
+                }
+
+                // Address default values
+                if (options != null) {
+                    const prefix = options.slice(0, 2)
+
+                    if (prefix === ":=") {
+                        defaults[id] = shellQuote.quote([options.slice(2)])
+                        return defaults[id]
+                    }
+                    if (prefix === ":-") {
+                        return shellQuote.quote([options.slice(2)])
+                    }
+
+                    throw Error(`Invalid Placeholder: ${whole}`)
+                }
+                if (defaults[id] != null) {
+                    return defaults[id]
+                }
+
+                return ""
+            },
+        ),
+    )
+}
+
+/**
+ * Parse patterns.
+ * In parsing process, it replaces argument placeholders (such as `{1}`) by arguments.
+ */
+function parsePatterns(
+    patternOrPatterns: string | string[],
+    args: string[],
+): string[] {
+    const patterns = ensureArray(patternOrPatterns)
+    const hasPlaceholder = patterns.some(pattern => ARGS_PATTERN.test(pattern))
+
+    return hasPlaceholder ? applyArguments(patterns, args) : patterns
+}
+
+/**
+ * Converts a given config object to an `--:=` style option array.
+ */
+function toOverwriteOptions(config: {
+    [key: string]: { [key: string]: string }
+}): string[] {
+    const options = []
+
+    for (const packageName of Object.keys(config)) {
+        const packageConfig = config[packageName]
+
+        for (const variableName of Object.keys(packageConfig)) {
+            const value = packageConfig[variableName]
+
+            options.push(`--${packageName}:${variableName}=${value}`)
+        }
+    }
+
+    return options
+}
+
+/**
+ * Converts a given config object to an `--a=b` style option array.
+ */
+function toConfigOptions(config: { [key: string]: any }): string[] {
+    return Object.keys(config).map(key => `--${key}=${config[key]}`)
+}
+
+/**
+ * Gets the maximum length.
+ */
+function maxLength(length: number, name: string): number {
+    return Math.max(name.length, length)
+}
+
+/**
+ * Normalize options.
+ */
+//eslint-disable-next-line complexity, require-jsdoc
+async function normalizeOptions(
+    patternOrPatterns: string | string[],
+    options?: Options,
+): Promise<{ scriptNames: string[]; config: NormalizedOptions }> {
+    const stdin = (options && options.stdin) || null
+    const stdout = (options && options.stdout) || null
+    const stderr = (options && options.stderr) || null
+    const taskList = (options && options.taskList) || null
+    const config = (options && options.config) || {}
+    const packageConfig = (options && options.packageConfig) || {}
+    const ddArgs = (options && options.arguments) || []
+    const parallel = Boolean(options && options.parallel)
+    const silent = Boolean(options && options.silent)
+    const continueOnError = Boolean(options && options.continueOnError)
+    const printLabel = Boolean(options && options.printLabel)
+    const printName = Boolean(options && options.printName)
+    const race = Boolean(options && options.race)
+    const maxParallel = parallel
+        ? (options && options.maxParallel) || Number.POSITIVE_INFINITY
+        : 1
+    const aggregateOutput = Boolean(options && options.aggregateOutput)
+    const npmPath = (options && options.npmPath) || null
+    const signal = (options && options.signal) || null
+    const patterns = parsePatterns(patternOrPatterns, ddArgs)
+
+    if (taskList != null && Array.isArray(taskList) === false) {
+        throw new Error("Invalid options.taskList")
+    }
+    if (typeof maxParallel !== "number" || !(maxParallel >= 0)) {
+        throw new Error("Invalid options.maxParallel")
+    }
+    if (!parallel && aggregateOutput) {
+        throw new Error(
+            "Invalid options.aggregateOutput; It requires options.parallel",
+        )
+    }
+    if (!parallel && race) {
+        throw new Error("Invalid options.race; It requires options.parallel")
+    }
+
+    const args = ([] as string[]).concat(
+        silent ? ["--silent"] : [],
+        packageConfig ? toOverwriteOptions(packageConfig) : [],
+        config ? toConfigOptions(config) : [],
+    )
+    const packageInfo: PackageInfo =
+        taskList != null ? { taskList, rawData: {} } : await readPackageInfo()
+    const scriptNames = getMatchedScriptNames(packageInfo.taskList, patterns)
+    const labelWidth = scriptNames.reduce(maxLength, 0)
+
+    return {
+        scriptNames,
+        config: {
+            stdin,
+            stdout,
+            stderr,
+            args,
+            continueOnError,
+            aggregateOutput,
+            race,
+            maxParallel,
+            npmPath,
+            signal,
+            scriptHeader: printName,
+            scriptLabel: {
+                enabled: printLabel,
+                width: labelWidth,
+                lastPrefix: "",
+                lastIsLinebreak: true,
+            },
+            packageInfo,
+        },
+    }
+}
+
+/**
+ * Create the child abort controller of a given signal.
+ */
+function inheritSignal(
+    signal: AbortSignal | null,
+): AbortController & { dispose(): void } {
+    const ac = new AbortController() as AbortController & { dispose(): void }
+    ac.dispose = () => {
+        // do nothing.
+    }
+
+    if (signal) {
+        if (signal.aborted) {
+            ac.abort()
+        } else {
+            const onAbort = function(): void {
+                signal.removeEventListener("abort", onAbort)
+                ac.abort()
+            }
+
+            signal.addEventListener("abort", onAbort)
+            ac.dispose = () => {
+                signal.removeEventListener("abort", onAbort)
+            }
+        }
+    }
+
+    return ac
+}
+
+/**
+ * Wrap for the aggregateOutput option.
+ * @param options The options.
+ * @param f The main logic.
+ */
+function wrapToAggregateOutput(
+    options: NormalizedOptions,
+    f: (options: NormalizedOptions) => Promise<ScriptResult>,
+): () => Promise<ScriptResult> {
+    if (options.aggregateOutput && options.stdout) {
+        const stdout = options.stdout
+        return async () => {
+            const thisOptions = Object.assign({}, options, {
+                stdout: new AggregateStream(),
+            })
+            try {
+                return await f(thisOptions)
+            } finally {
+                try {
+                    stdout.write(thisOptions.stdout.result())
+                } catch (_err) {
+                    // ignore.
+                }
+            }
+        }
+    }
+    return () => f(options)
+}
+
+/**
+ * Runs npm-scripts which are matched with given patterns.
+ */
+export default async function runScripts(
+    patternOrPatterns: string | string[],
+    options?: Options,
+): Promise<ScriptResult[]> {
+    const { scriptNames, config } = await normalizeOptions(
+        patternOrPatterns,
+        options,
+    )
+    if (scriptNames.length === 0) {
+        return []
+    }
+
+    const queue = new PQueue({ concurrency: config.maxParallel })
+    const ac = inheritSignal(config.signal)
+    try {
+        let error: Error | null = null
+
+        // Run scripts.
+        const results = await queue.addAll(
+            scriptNames.map(name =>
+                wrapToAggregateOutput(config, async thisOptions => {
+                    try {
+                        const ret = await runScript(
+                            name,
+                            thisOptions,
+                            ac.signal,
+                        )
+
+                        // Aborts all scripts if it's an error.
+                        if (ret.code) {
+                            error = error || new ScriptError(ret, results)
+                            if (!config.continueOnError) {
+                                ac.abort()
+                            }
+                        }
+
+                        // Aborts all scripts if options.race is true.
+                        if (config.race && !ret.code) {
+                            ac.abort()
+                        }
+
+                        return ret
+                    } catch (e) {
+                        error = error || e
+                        if (!config.continueOnError) {
+                            ac.abort()
+                        }
+                        return { name, code: undefined }
+                    }
+                }),
+            ),
+        )
+
+        if (error) {
+            throw error
+        }
+        return results
+    } finally {
+        ac.dispose()
+    }
+}
+Object.assign(runScripts, { AbortController, AbortSignal, default: runScripts })
+
+/**
+ * Options.
+ */
+export interface Options {
+    stdin?: NodeJS.ReadableStream
+    stdout?: NodeJS.WritableStream & { isTTY?: boolean }
+    stderr?: NodeJS.WritableStream & { isTTY?: boolean }
+    taskList?: string[]
+    config?: { [key: string]: string }
+    packageConfig?: { [key: string]: { [key: string]: string } }
+    arguments?: string[]
+    parallel?: boolean
+    silent?: boolean
+    continueOnError?: boolean
+    printLabel?: boolean
+    printName?: boolean
+    race?: boolean
+    aggregateOutput?: boolean
+    maxParallel?: number
+    npmPath?: string
+    signal?: AbortSignal
+}
diff --git a/src/lib/package-info.ts b/src/lib/package-info.ts
new file mode 100644
index 0000000..685e57d
--- /dev/null
+++ b/src/lib/package-info.ts
@@ -0,0 +1,37 @@
+/**
+ * @author Toru Nagashima
+ * See LICENSE file in root directory for full license.
+ */
+import path from "path"
+import fs from "fs-extra"
+
+/**
+ * Ensure x is an object.
+ */
+//eslint-disable-next-line require-jsdoc, mysticatea/ts/no-explicit-any
+function ensureObject(x: any): { [key: string]: any } {
+    return typeof x === "object" && x !== null ? x : {}
+}
+
+/**
+ * Information of package.json.
+ */
+export interface PackageInfo {
+    filePath?: string
+    taskList: string[]
+
+    //eslint-disable-next-line mysticatea/ts/no-explicit-any
+    rawData: { [key: string]: any }
+}
+
+/**
+ * Reads the package.json in the current directory.
+ */
+export async function readPackageInfo(): Promise<PackageInfo> {
+    const filePath = path.join(process.cwd(), "package.json")
+    const rawData = ensureObject(await fs.readJSON(filePath))
+    const scripts = ensureObject(rawData.scripts)
+    const taskList = Object.keys(scripts)
+
+    return { filePath, taskList, rawData }
+}
diff --git a/src/lib/script-error.ts b/src/lib/script-error.ts
new file mode 100644
index 0000000..b0e980b
--- /dev/null
+++ b/src/lib/script-error.ts
@@ -0,0 +1,22 @@
+/**
+ * @author Toru Nagashima
+ * See LICENSE file in root directory for full license.
+ */
+import { ScriptResult } from "./script-result"
+
+/**
+ * Error object with some additional info.
+ */
+export class ScriptError extends Error {
+    public causeResult: ScriptResult
+    public allResults: ScriptResult[]
+
+    /**
+     * Initialize this error.
+     */
+    public constructor(causeResult: ScriptResult, allResults: ScriptResult[]) {
+        super(`"${causeResult.name}" exited with ${causeResult.code}.`)
+        this.causeResult = causeResult
+        this.allResults = allResults
+    }
+}
diff --git a/src/lib/script-match.ts b/src/lib/script-match.ts
new file mode 100644
index 0000000..537a6da
--- /dev/null
+++ b/src/lib/script-match.ts
@@ -0,0 +1,115 @@
+/**
+ * @author Toru Nagashima
+ * See LICENSE file in root directory for full license.
+ */
+import { Minimatch } from "minimatch"
+
+const COLON_OR_SLASH = /[:/]/g
+const CONVERT_MAP = { ":": "/", "/": ":" }
+
+/**
+ * Swaps ":" and "/", in order to use ":" as the separator in minimatch.
+ */
+function swapColonAndSlash(s: string): string {
+    return s.replace(
+        COLON_OR_SLASH,
+        matched => CONVERT_MAP[matched as ":" | "/"],
+    )
+}
+
+/**
+ * The filter of glob-like pattern.
+ */
+class PatternFilter {
+    public readonly name: string
+    public readonly args: string
+    public readonly match: (name: string) => boolean
+
+    /**
+     * Create new filter.
+     */
+    public static new(pattern: string): PatternFilter {
+        return new PatternFilter(pattern)
+    }
+
+    /**
+     * Initialize this filter.
+     */
+    public constructor(pattern: string) {
+        const trimmed = pattern.trim()
+        const spacePos = trimmed.indexOf(" ")
+        const name = spacePos < 0 ? trimmed : trimmed.slice(0, spacePos)
+        const matcher = new Minimatch(swapColonAndSlash(name))
+
+        this.name = name
+        this.args = spacePos < 0 ? "" : trimmed.slice(spacePos)
+        this.match = matcher.match.bind(matcher)
+    }
+}
+
+/**
+ * The set to remove overlapped task.
+ */
+class ScriptSet {
+    private nameMap: {
+        [name: string]: Set<string> | undefined
+    } = Object.create(null)
+    public commands: string[] = []
+
+    /**
+     * Adds a command (a pattern) into this set if it's not overlapped.
+     * "Overlapped" is meaning that the command was added from a different source.
+     */
+    public add(command: string, nameOfSourcePattern: string): void {
+        const names =
+            this.nameMap[command] || (this.nameMap[command] = new Set<string>())
+        if (names.has(nameOfSourcePattern)) {
+            this.commands.push(command)
+        }
+        names.add(nameOfSourcePattern)
+    }
+}
+
+/**
+ * Enumerates tasks which matches with given patterns.
+ */
+export function getMatchedScriptNames(
+    taskList: string[],
+    patterns: string[],
+): string[] {
+    const filters = patterns.map(PatternFilter.new)
+    const candidates = taskList.map(swapColonAndSlash)
+    const scriptSet = new ScriptSet()
+    const unknownSet = new Set<string>()
+
+    // Take tasks while keep the order of patterns.
+    for (const filter of filters) {
+        let found = false
+
+        for (const candidate of candidates) {
+            if (filter.match(candidate)) {
+                found = true
+                scriptSet.add(
+                    swapColonAndSlash(candidate) + filter.args,
+                    filter.name,
+                )
+            }
+        }
+
+        // Built-in tasks should be allowed.
+        if (!found && (filter.name === "restart" || filter.name === "env")) {
+            scriptSet.add(filter.name + filter.args, filter.name)
+            found = true
+        }
+        if (!found) {
+            unknownSet.add(filter.name)
+        }
+    }
+
+    if (unknownSet.size > 0) {
+        throw new Error(
+            `Script not found: "${Array.from(unknownSet).join('", ')}"`,
+        )
+    }
+    return scriptSet.commands
+}
diff --git a/src/lib/script-result.ts b/src/lib/script-result.ts
new file mode 100644
index 0000000..e1e79a2
--- /dev/null
+++ b/src/lib/script-result.ts
@@ -0,0 +1,12 @@
+/**
+ * @author Toru Nagashima
+ * See LICENSE file in root directory for full license.
+ */
+
+/**
+ * The result of scripts.
+ */
+export interface ScriptResult {
+    name: string
+    code: number | undefined
+}
diff --git a/src/lib/scripts/label.ts b/src/lib/scripts/label.ts
new file mode 100644
index 0000000..8729b80
--- /dev/null
+++ b/src/lib/scripts/label.ts
@@ -0,0 +1,64 @@
+/**
+ * @author Toru Nagashima
+ * See LICENSE file in root directory for full license.
+ */
+import stream from "stream"
+
+const ALL_BR = /\n/g
+
+/**
+ * Shared state in multiple streams.
+ */
+export interface ScriptLabelTransformState {
+    lastPrefix: string
+    lastIsLinebreak: boolean
+}
+
+/**
+ * The transform stream to insert a specific prefix.
+ *
+ * Several streams can exist for the same output stream.
+ * This stream will insert the prefix if the last output came from other instance.
+ * To do that, this stream is using a shared state object.
+ */
+export class ScriptLabelTransform extends stream.Transform {
+    private readonly prefix: string
+    private readonly state: ScriptLabelTransformState
+
+    /**
+     * Initialize this stream.
+     */
+    public constructor(prefix: string, state: ScriptLabelTransformState) {
+        super()
+        this.prefix = prefix
+        this.state = state
+    }
+
+    /**
+     * Transforms the output chunk.
+     */
+    public _transform(
+        chunk: any, //eslint-disable-line mysticatea/ts/no-explicit-any
+        _encoding: string,
+        callback: stream.TransformCallback,
+    ) {
+        const prefix = this.prefix
+        const nPrefix = `\n${prefix}`
+        const state = this.state
+        const firstPrefix = state.lastIsLinebreak
+            ? prefix
+            : state.lastPrefix !== prefix
+                ? "\n"
+                : /* otherwise */ ""
+        const prefixed = `${firstPrefix}${chunk}`.replace(ALL_BR, nPrefix)
+        const index = prefixed.indexOf(
+            prefix,
+            Math.max(0, prefixed.length - prefix.length),
+        )
+
+        state.lastPrefix = prefix
+        state.lastIsLinebreak = index !== -1
+
+        callback(undefined, index !== -1 ? prefixed.slice(0, index) : prefixed)
+    }
+}
diff --git a/src/lib/scripts/name.ts b/src/lib/scripts/name.ts
new file mode 100644
index 0000000..c16b032
--- /dev/null
+++ b/src/lib/scripts/name.ts
@@ -0,0 +1,35 @@
+/**
+ * @author Toru Nagashima
+ * See LICENSE file in root directory for full license.
+ */
+import ansiStyles from "ansi-styles"
+import { PackageInfo } from "../package-info"
+
+/**
+ * Creates the header text for a given script name.
+ */
+export function createScriptHeader(
+    nameAndArgs: string,
+    packageInfo: PackageInfo,
+    isTTY: boolean,
+): string {
+    const color = isTTY ? ansiStyles.gray : { open: "", close: "" }
+    if (!packageInfo) {
+        return `\n${color.open}> ${nameAndArgs}${color.close}\n\n`
+    }
+
+    const index = nameAndArgs.indexOf(" ")
+    const scriptName = index === -1 ? nameAndArgs : nameAndArgs.slice(0, index)
+    const args = index === -1 ? "" : nameAndArgs.slice(index + 1)
+    const rawData = packageInfo.rawData
+    const name = rawData.name
+    const version = rawData.version
+    const scriptBody = rawData.scripts && rawData.scripts[scriptName]
+    const packagePath = packageInfo.filePath
+
+    return `
+${color.open}> ${name}@${version} ${scriptName} ${packagePath}${color.close}
+${color.open}> ${scriptBody} ${args}${color.close}
+
+`
+}
diff --git a/src/lib/scripts/run.ts b/src/lib/scripts/run.ts
new file mode 100644
index 0000000..644df4d
--- /dev/null
+++ b/src/lib/scripts/run.ts
@@ -0,0 +1,238 @@
+/**
+ * @author Toru Nagashima
+ * See LICENSE file in root directory for full license.
+ */
+import { ChildProcess } from "child_process"
+import path from "path"
+import { AbortSignal } from "abort-controller"
+import chalk from "chalk"
+import { parse as parseArgs } from "shell-quote"
+import padEnd from "string.prototype.padend"
+import { PackageInfo } from "../package-info"
+import { ScriptResult } from "../script-result"
+import { ScriptLabelTransform, ScriptLabelTransformState } from "./label"
+import { createScriptHeader } from "./name"
+import { spawn } from "./spawn"
+
+// Color definitions
+type Color = (...text: string[]) => string
+const colors: Color[] = [
+    chalk.cyan,
+    chalk.green,
+    chalk.magenta,
+    chalk.yellow,
+    chalk.red,
+]
+const defaultColor = ((s: string) => s) as Color
+
+// Data to rotate colors
+let colorIndex = 0
+const scriptNamesToColors = new Map<string, Color>()
+
+/**
+ * Select a color from given script name.
+ */
+function selectColor(name: string): typeof colors[0] {
+    let color = scriptNamesToColors.get(name)
+    if (!color) {
+        color = colors[colorIndex]
+        colorIndex = (colorIndex + 1) % colors.length
+        scriptNamesToColors.set(name, color)
+    }
+    return color
+}
+
+/**
+ * Wraps stdout/stderr with a transform stream to add the script name as prefix.
+ */
+function wrapLabeling(
+    name: string,
+    outputStream: (NodeJS.WritableStream & { isTTY?: boolean }) | null,
+    labelOptions: ScriptLabelOptions,
+): NodeJS.WritableStream | null {
+    if (outputStream == null || !labelOptions.enabled) {
+        return outputStream
+    }
+
+    const label = padEnd(name, labelOptions.width)
+    const color = outputStream.isTTY ? selectColor(name) : defaultColor
+    const prefix = color(`[${label}] `)
+    const transform = new ScriptLabelTransform(prefix, labelOptions)
+
+    transform.pipe(outputStream)
+
+    return transform
+}
+
+/**
+ * Converts a given stream to an option for `child_process.spawn`.
+ */
+function detectStreamKind<
+    T extends
+        | (NodeJS.WritableStream & { isTTY?: boolean })
+        | (NodeJS.ReadableStream & { isTTY?: boolean })
+>(targetStream: T | null, std: T): T | "ignore" | "pipe" {
+    if (targetStream == null) {
+        return "ignore"
+    }
+    if (targetStream !== std) {
+        return "pipe"
+    }
+
+    // For the workaround of https://github.com/nodejs/node/issues/5620
+    if (!std.isTTY) {
+        return "pipe"
+    }
+
+    return targetStream
+}
+
+/**
+ * Normalize spawn arguments.
+ * @param scriptName The script name to run.
+ * @param npmPath0 The path to npm or yarn.
+ * @param additionalArgs The additional arguments to exec.
+ * @returns The normalized arguments.
+ */
+function normalizeSpawnArgs(
+    scriptName: string,
+    npmPath0: string | null,
+    additionalArgs: string[],
+): { execPath: string; execArgs: string[] } {
+    const npmPath = npmPath0 || process.env.npm_execpath || "npm" //eslint-disable-line no-process-env
+    const npmPathIsJs = /^\.m?js$/.test(path.extname(npmPath))
+    const execPath = npmPathIsJs ? process.execPath : npmPath
+    const isYarn = path.basename(npmPath).startsWith("yarn")
+    const execArgs = ["run"]
+
+    if (npmPathIsJs) {
+        execArgs.unshift(npmPath)
+    }
+
+    if (!isYarn) {
+        Array.prototype.push.apply(execArgs, additionalArgs)
+    } else if (additionalArgs.indexOf("--silent") !== -1) {
+        execArgs.push("--silent")
+    }
+
+    Array.prototype.push.apply(execArgs, parseArgs(scriptName))
+
+    return { execPath, execArgs }
+}
+
+/**
+ * Options for `--label` option.
+ */
+export interface ScriptLabelOptions extends ScriptLabelTransformState {
+    enabled: boolean
+    width: number
+}
+
+/**
+ * Options for runScript().
+ */
+export interface RunScriptOptions {
+    stdin: NodeJS.ReadableStream | null
+    stdout: (NodeJS.WritableStream & { isTTY?: boolean }) | null
+    stderr: (NodeJS.WritableStream & { isTTY?: boolean }) | null
+    args: string[]
+    packageInfo: PackageInfo
+    scriptHeader: boolean
+    scriptLabel: ScriptLabelOptions
+    npmPath: string | null
+}
+
+/**
+ * Run a npm-script of a given name.
+ * The return value is a promise which has an extra method: `abort()`.
+ * The `abort()` kills the child process to run the npm-script.
+ */
+export function runScript(
+    name: string,
+    options: RunScriptOptions,
+    signal: AbortSignal,
+): Promise<ScriptResult> {
+    if (signal.aborted) {
+        return Promise.resolve<ScriptResult>({ name, code: undefined })
+    }
+    return new Promise<ScriptResult>(async (resolve, reject) => {
+        let cp: ChildProcess | null = null
+        let error: Error | null = null
+
+        const stdin = options.stdin
+        const stdout = wrapLabeling(name, options.stdout, options.scriptLabel)
+        const stderr = wrapLabeling(name, options.stderr, options.scriptLabel)
+        const stdinKind = detectStreamKind(stdin, process.stdin)
+        const stdoutKind = detectStreamKind(stdout, process.stdout)
+        const stderrKind = detectStreamKind(stderr, process.stderr)
+
+        // Print script name.
+        if (options.scriptHeader && stdout && options.stdout) {
+            stdout.write(
+                createScriptHeader(
+                    name,
+                    options.packageInfo,
+                    Boolean(options.stdout.isTTY),
+                ),
+            )
+        }
+
+        // Register to abort signal.
+        //eslint-disable-next-line require-jsdoc
+        function onAbort(): void {
+            if (cp) {
+                cp.kill()
+            }
+        }
+
+        signal.addEventListener("abort", onAbort)
+
+        // Execute.
+        const { execPath, execArgs } = normalizeSpawnArgs(
+            name,
+            options.npmPath,
+            options.args,
+        )
+        const execOptions = { stdio: [stdinKind, stdoutKind, stderrKind] }
+        cp = await spawn(execPath, execArgs, execOptions)
+
+        // Piping stdio.
+        if (stdinKind === "pipe" && stdin) {
+            stdin.pipe(cp.stdin)
+        }
+        if (stdoutKind === "pipe" && stdout) {
+            cp.stdout.pipe(stdout, { end: false })
+        }
+        if (stderrKind === "pipe" && stderr) {
+            cp.stderr.pipe(stderr, { end: false })
+        }
+
+        // Register handlers.
+        cp.on("error", (e: Error) => {
+            error = e
+        })
+        cp.on("close", (code: number | undefined) => {
+            signal.removeEventListener("abort", onAbort)
+
+            if (cp) {
+                if (stdinKind === "pipe" && stdin) {
+                    stdin.unpipe(cp.stdin)
+                }
+                if (stdoutKind === "pipe" && stdout) {
+                    cp.stdout.unpipe(stdout)
+                }
+                if (stderrKind === "pipe" && stderr) {
+                    cp.stderr.unpipe(stderr)
+                }
+                cp.removeAllListeners()
+                cp = null
+            }
+
+            if (error) {
+                reject(error)
+            } else {
+                resolve({ name, code })
+            }
+        })
+    })
+}
diff --git a/src/lib/scripts/spawn.ts b/src/lib/scripts/spawn.ts
new file mode 100644
index 0000000..dad544f
--- /dev/null
+++ b/src/lib/scripts/spawn.ts
@@ -0,0 +1,22 @@
+/**
+ * @author Toru Nagashima
+ * See LICENSE file in root directory for full license.
+ */
+import { ChildProcess, SpawnOptions } from "child_process"
+
+const implPromise =
+    process.platform === "win32"
+        ? import("./spawn/win32")
+        : import("./spawn/posix")
+
+/**
+ * Spawn a child process.
+ * This is almost same as `child_process.spawn`, but the `kill` method kills all descendant processes.
+ */
+export async function spawn(
+    command: string,
+    args: string[],
+    options: SpawnOptions,
+): Promise<ChildProcess> {
+    return (await implPromise).spawn(command, args, options)
+}
diff --git a/src/lib/scripts/spawn/posix.ts b/src/lib/scripts/spawn/posix.ts
new file mode 100644
index 0000000..b341ac0
--- /dev/null
+++ b/src/lib/scripts/spawn/posix.ts
@@ -0,0 +1,41 @@
+/**
+ * @author Toru Nagashima
+ * See LICENSE file in root directory for full license.
+ */
+import { ChildProcess, SpawnOptions } from "child_process"
+import spawn0 from "cross-spawn"
+import getDescendentProcessInfo from "ps-tree"
+
+/**
+ * Kill child processes recursively for POSIX.
+ */
+function kill(this: ChildProcess): void {
+    getDescendentProcessInfo(this.pid, (err, descendent) => {
+        if (err || !descendent) {
+            return
+        }
+
+        for (const child of descendent) {
+            try {
+                process.kill(Number(child.PID))
+            } catch (_err) {
+                // ignore.
+            }
+        }
+    })
+}
+
+/**
+ * Spawn a child process.
+ * This is almost same as `child_process.spawn`, but the `kill` method kills all descendant processes.
+ */
+export function spawn(
+    command: string,
+    args: string[],
+    options: SpawnOptions,
+): ChildProcess {
+    const child = spawn0(command, args, options)
+    child.kill = kill
+
+    return child
+}
diff --git a/src/lib/scripts/spawn/win32.ts b/src/lib/scripts/spawn/win32.ts
new file mode 100644
index 0000000..3d8e4a0
--- /dev/null
+++ b/src/lib/scripts/spawn/win32.ts
@@ -0,0 +1,73 @@
+/**
+ * @author Toru Nagashima
+ * See LICENSE file in root directory for full license.
+ */
+import { ChildProcess, SpawnOptions } from "child_process"
+import readline from "readline"
+import spawn0 from "cross-spawn"
+
+// Catch SIGINT.
+let rl: readline.ReadLine | null = null
+process.on("newListener", type => {
+    if (type === "SIGINT" && process.listenerCount("SIGINT") === 1) {
+        rl = readline.createInterface({
+            input: process.stdin,
+            output: process.stdout,
+        })
+        rl.on("SIGINT", process.emit.bind(process, "SIGINT"))
+    }
+})
+process.on("removeListener", type => {
+    if (type === "SIGINT" && rl && process.listenerCount("SIGINT") === 0) {
+        rl.close()
+        rl = null
+    }
+})
+
+/**
+ * Register a given child process to transfar SIGINT.
+ */
+function register(cp: ChildProcess): void {
+    /*eslint-disable require-jsdoc */
+    function onSIGINT(): void {
+        cp.kill()
+    }
+
+    function onDead(): void {
+        process.removeListener("SIGINT", onSIGINT)
+        cp.removeListener("exit", onDead)
+        cp.removeListener("error", onDead)
+    }
+    /*eslint-enable require-jsdoc */
+
+    process.on("SIGINT", onSIGINT)
+    cp.on("exit", onDead)
+    cp.on("error", onDead)
+}
+
+/**
+ * Kill child processes recursively for Windows.
+ */
+function kill(this: ChildProcess): void {
+    spawn0("taskkill", ["/F", "/T", "/PID", String(this.pid)])
+}
+
+/**
+ * Spawn a child process.
+ * This is almost same as `child_process.spawn`, but the `kill` method kills all descendant processes.
+ */
+export function spawn(
+    command: string,
+    args: string[],
+    options: SpawnOptions,
+): ChildProcess {
+    const child = spawn0(command, args, options)
+    child.kill = kill
+
+    // If stdin is not inherited, transfar signals to child process.
+    if (options.stdio && options.stdio[0] !== process.stdin) {
+        register(child)
+    }
+
+    return child
+}
diff --git a/test/.eslintrc.json b/test/.eslintrc.json
deleted file mode 100644
index 407983a..0000000
--- a/test/.eslintrc.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-    "extends": "mysticatea/mocha",
-    "rules": {
-        "node/no-unsupported-features": ["error", {
-            "ignores": ["asyncAwait"]
-        }]
-    }
-}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..fc4b656
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,39 @@
+{
+    "compileOnSave": true,
+    "compilerOptions": {
+        "allowSyntheticDefaultImports": true,
+        "allowUnreachableCode": false,
+        "allowUnusedLabels": false,
+        "baseUrl": "typings",
+        "declaration": false,
+        "diagnostics": false,
+        "emitBOM": false,
+        "inlineSources": true,
+        "lib": [
+            "es2015"
+        ],
+        "module": "esnext",
+        "moduleResolution": "node",
+        "newLine": "LF",
+        "noFallthroughCasesInSwitch": true,
+        "noImplicitAny": true,
+        "noImplicitReturns": true,
+        "noImplicitThis": true,
+        "noStrictGenericChecks": false,
+        "noUnusedLocals": true,
+        "noUnusedParameters": true,
+        "outDir": ".temp",
+        "pretty": true,
+        "sourceMap": true,
+        "sourceRoot": "src",
+        "strict": true,
+        "strictFunctionTypes": true,
+        "strictNullChecks": true,
+        "strictPropertyInitialization": true,
+        "target": "es2018",
+        "locale": "en"
+    },
+    "include": [
+        "src/**/*.ts"
+    ]
+}
diff --git a/typings/abort-controller/index.d.ts b/typings/abort-controller/index.d.ts
new file mode 100644
index 0000000..5c1dd34
--- /dev/null
+++ b/typings/abort-controller/index.d.ts
@@ -0,0 +1,37 @@
+/**
+ * @author Toru Nagashima
+ * See LICENSE file in root directory for full license.
+ */
+declare module "abort-controller" {
+    interface AbortController {
+        readonly signal: AbortSignal
+        abort(): void
+    }
+
+    interface AbortSignal {
+        readonly aborted: boolean
+        onabort: Function | null
+        addEventListener(
+            type: string,
+            listener: Function,
+            options?:
+                | boolean
+                | { capture?: boolean; once?: boolean; passive?: boolean },
+        ): void
+        removeEventListener(
+            type: string,
+            listener: Function,
+            options?: boolean | { capture?: boolean },
+        ): void
+    }
+
+    export const AbortController: {
+        prototype: AbortController
+        new (): AbortController
+    }
+
+    export const AbortSignal: {
+        prototype: AbortSignal
+        new (): AbortSignal
+    }
+}
diff --git a/typings/ps-tree/index.d.ts b/typings/ps-tree/index.d.ts
new file mode 100644
index 0000000..0971220
--- /dev/null
+++ b/typings/ps-tree/index.d.ts
@@ -0,0 +1,16 @@
+/**
+ * @author Toru Nagashima
+ * See LICENSE file in root directory for full license.
+ */
+declare module "ps-tree" {
+    function psTree(
+        pid: number | string,
+        callback: (err: null | null, children: { PID: string }[]) => void,
+    ): void
+
+    namespace psTree {
+
+    }
+
+    export = psTree
+}
diff --git a/typings/string.prototype.padend/index.d.ts b/typings/string.prototype.padend/index.d.ts
new file mode 100644
index 0000000..2ca6e9a
--- /dev/null
+++ b/typings/string.prototype.padend/index.d.ts
@@ -0,0 +1,13 @@
+/**
+ * @author Toru Nagashima
+ * See LICENSE file in root directory for full license.
+ */
+declare module "string.prototype.padend" {
+    function padEnd(s: string, maxLength: number, fillString?: string): string
+
+    namespace padEnd {
+
+    }
+
+    export = padEnd
+}