Skip to content

Commit

Permalink
Added --:= style options.
Browse files Browse the repository at this point in the history
  • Loading branch information
mysticatea committed Nov 18, 2015
1 parent 8004ce4 commit 08aa833
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 23 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,21 +112,27 @@ Run npm-scripts.
- **options.parallel** `boolean` --
A flag to run tasks in parallel.
Default is `false`.
- **options.stdin** `stream.Readable` --
- **options.stdin** `stream.Readable|null` --
A readable stream to send to the stdin of npm-scripts.
Default is nothing.
Set `process.stdin` in order to send from stdin.
- **options.stdout** `stream.Writable` --
- **options.stdout** `stream.Writable|null` --
A writable stream to receive from the stdout of npm-scripts.
Default is nothing.
Set `process.stdout` in order to print to stdout.
- **options.stderr** `stream.Writable` --
- **options.stderr** `stream.Writable|null` --
A writable stream to receive from the stderr of npm-scripts
Default is nothing.
Set `process.stderr` in order to print to stderr.
- **options.taskList** `string[]` --
- **options.taskList** `string[]|null` --
A string array that is all task names.
By default, reads from `package.json` in the current directory.
- **options.packageConfig** `object|null` --
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, "test2": 333}}`
Default is `null`.

`runAll` returns a promise that becomes *fulfilled* when all tasks are completed.
The promise will become *rejected* when any of the tasks exit with a non-zero code.
56 changes: 46 additions & 10 deletions src/bin/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,70 @@
import runAll from "../lib/npm-run-all";

const START_PROMISE = Promise.resolve(null);
const OVERWRITE_OPTION = /^--([^:]+?):([^=]+?)(?:=(.+))?$/;

/**
* 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] = {}); // eslint-disable-line no-param-reassign
scope[variable] = value;
}

/**
* Parses arguments.
*
* @param {string[]} args - Arguments to parse.
* @returns {{parallel: boolean, patterns: string[]}[]} A running plan.
* @returns {{parallel: boolean, patterns: string[], packageConfig: object}[]} A running plan.
*/
function parse(args) {
return args.reduce((queue, arg) => {
const packageConfig = {};
const queue = [{parallel: false, patterns: [], packageConfig}];

for (let i = 0; i < args.length; ++i) {
const arg = args[i];

switch (arg) {
case "-s":
case "--sequential":
queue.push({parallel: false, patterns: []});
if (queue[queue.length - 1].parallel) {
queue.push({parallel: false, patterns: [], packageConfig});
}
break;

case "-p":
case "--parallel":
queue.push({parallel: true, patterns: []});
queue.push({parallel: true, patterns: [], packageConfig});
break;

default:
if (arg[0] === "-") {
default: {
const matched = OVERWRITE_OPTION.exec(arg);
if (matched) {
overwriteConfig(
packageConfig,
matched[1],
matched[2],
matched[3] || args[++i]
);
}
else if (arg[0] === "-") {
throw new Error(`Invalid Option: ${arg}`);
}
queue[queue.length - 1].patterns.push(arg);
else {
queue[queue.length - 1].patterns.push(arg);
}
break;
}
}
return queue;
}, [{parallel: false, patterns: []}]);
}

return queue;
}

/**
Expand All @@ -57,7 +92,8 @@ export default function npmRunAll(args, stdout, stderr) {
stdout,
stderr,
stdin: process.stdin,
parallel: group.parallel
parallel: group.parallel,
packageConfig: group.packageConfig
}
)),
START_PROMISE
Expand Down
41 changes: 38 additions & 3 deletions src/lib/npm-run-all.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,34 @@ function toArray(x) {
return Array.isArray(x) ? x : [x];
}

/**
* 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 = [];
if (config == null) {
return 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;
}

/**
* Runs npm-scripts which are matched with given patterns.
*
Expand Down Expand Up @@ -53,6 +81,12 @@ function toArray(x) {
* 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`.
* @returns {Promise}
* A promise object which becomes fullfilled when all npm-scripts are completed.
*/
Expand All @@ -63,7 +97,8 @@ export default function npmRunAll(
stdin = null,
stdout = null,
stderr = null,
taskList = null
taskList = null,
packageConfig = null
} = {}
) {
try {
Expand All @@ -81,8 +116,8 @@ export default function npmRunAll(
}

return (
parallel ? runTasksInParallel(tasks, stdin, stdout, stderr) :
/* else */ runTasksInSequencial(tasks, stdin, stdout, stderr)
parallel ? runTasksInParallel(tasks, stdin, stdout, stderr, toOverwriteOptions(packageConfig)) :
/* else */ runTasksInSequencial(tasks, stdin, stdout, stderr, toOverwriteOptions(packageConfig))
);
}
catch (err) {
Expand Down
6 changes: 4 additions & 2 deletions src/lib/run-task.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,14 @@ function detectStreamKind(stream, std) {
* If this is `null`, cannot send.
* If this is `process.stderr`, inherits it.
* Otherwise, makes a pipe.
* @param {string[]} packageConfigOptions -
* `--:=` style options to overwrite package configs.
* @returns {Promise}
* A promise object which becomes fullfilled when the npm-script is completed.
* This promise object has an extra method: `abort()`.
* @private
*/
export default function runTask(task, stdin, stdout, stderr) {
export default function runTask(task, stdin, stdout, stderr, packageConfigOptions) {
let cp = null;
const promise = whichNpm().then(npmPath => new Promise((resolve, reject) => {
const stdinKind = detectStreamKind(stdin, process.stdin);
Expand All @@ -58,7 +60,7 @@ export default function runTask(task, stdin, stdout, stderr) {
// Execute.
cp = spawn(
npmPath,
["run-script"].concat(parseArgs(task)),
["run-script"].concat(packageConfigOptions, parseArgs(task)),
{stdio: [stdinKind, stdoutKind, stderrKind]}
);

Expand Down
6 changes: 4 additions & 2 deletions src/lib/run-tasks-in-parallel.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,17 @@ import runTask from "./run-task";
* If this is `null`, cannot send.
* If this is `process.stderr`, inherits it.
* Otherwise, makes a pipe.
* @param {string[]} packageConfigOptions -
* `--:=` style options to overwrite package configs.
* @returns {Promise}
* A promise object which becomes fullfilled when all npm-scripts are completed.
* @private
*/
export default function runTasksInParallel(tasks, stdin, stdout, stderr) {
export default function runTasksInParallel(tasks, stdin, stdout, stderr, packageConfigOptions) {
// When one of tasks exited with non-zero, abort all tasks.
// And wait for all tasks exit.
let nonZeroExited = null;
const taskPromises = tasks.map(task => runTask(task, stdin, stdout, stderr));
const taskPromises = tasks.map(task => runTask(task, stdin, stdout, stderr, packageConfigOptions));
const parallelPromise = Promise.all(taskPromises.map(p =>
p.then(item => {
if (nonZeroExited == null && item.code) {
Expand Down
6 changes: 4 additions & 2 deletions src/lib/run-tasks-in-sequencial.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,17 @@ function rejectIfNonZeroExit(result) {
* If this is `null`, cannot send.
* If this is `process.stderr`, inherits it.
* Otherwise, makes a pipe.
* @param {string[]} packageConfigOptions -
* `--:=` style options to overwrite package configs.
* @returns {Promise}
* A promise object which becomes fullfilled when all npm-scripts are completed.
* @private
*/
export default function runTasksInSequencial(tasks, stdin, stdout, stderr) {
export default function runTasksInSequencial(tasks, stdin, stdout, stderr, packageConfigOptions) {
return tasks.reduce(
(prev, task) => prev.then(result => {
rejectIfNonZeroExit(result);
return runTask(task, stdin, stdout, stderr);
return runTask(task, stdin, stdout, stderr, packageConfigOptions);
}),
START_PROMISE
).then(rejectIfNonZeroExit);
Expand Down
1 change: 1 addition & 0 deletions test-workspace/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"start": "node tasks/append.js start",
"stop": "node tasks/append.js stop",
"test-task:env-check": "node tasks/env-check.js",
"test-task:env-check2": "node tasks/env-check2.js",
"test-task:append": "node tasks/append.js",
"test-task:append:a": "node tasks/append.js a",
"test-task:append:a:c": "node tasks/append.js ac",
Expand Down
4 changes: 4 additions & 0 deletions test-workspace/tasks/env-check2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"use strict";

var appendResult = require("../../test/lib/util").appendResult;
appendResult(process.env.npm_package_config_test + "\n" + process.env.npm_package_config_test2 + "\n" + process.env.npm_package_config_test3);
37 changes: 37 additions & 0 deletions test/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,41 @@ describe("[common] npm-run-all", () => {
it("lib version", () => runAll("env"));
it("command version", () => command(["env"]));
});

describe("should have an ability to overwrite package's config:", () => {
it("lib version should address \"packageConfig\" option", () =>
runAll("test-task:env-check", {packageConfig: {"npm-run-all-test": {test: "OVERWRITTEN"}}})
.then(() => {
assert(result() === "OVERWRITTEN");
})
);

it("lib version should address \"packageConfig\" option for multiple variables", () =>
runAll("test-task:env-check2", {packageConfig: {"npm-run-all-test": {test: "1", test2: "2", test3: "3"}}})
.then(() => {
assert(result() === "1\n2\n3");
})
);

it("command version should address \"--a:b=c\" style options", () =>
command(["test-task:env-check", "--npm-run-all-test:test=OVERWRITTEN"])
.then(() => {
assert(result() === "OVERWRITTEN");
})
);

it("command version should address \"--a:b=c\" style options for multiple variables", () =>
command(["test-task:env-check2", "--npm-run-all-test:test=1", "--npm-run-all-test:test2=2", "--npm-run-all-test:test3=3"])
.then(() => {
assert(result() === "1\n2\n3");
})
);

it("command version should address \"--a:b c\" style options", () =>
command(["test-task:env-check", "--npm-run-all-test:test", "OVERWRITTEN"])
.then(() => {
assert(result() === "OVERWRITTEN");
})
);
});
});

0 comments on commit 08aa833

Please sign in to comment.