Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for custom task names aka labels #167

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
@@ -1,3 +1,4 @@
/.idea
/.nyc_output
/coverage
/jsdoc
Expand Down
16 changes: 14 additions & 2 deletions lib/index.js
Expand Up @@ -205,6 +205,10 @@ function maxLength(length, name) {
* @param {string} options.npmPath -
* The path to npm.
* Default is `process.env.npm_execpath`.
* @param {string} options.labels -
* A comma separated string. Each item is used as a label for a task.
* The amount and order must match with the task list.
* Default is an empty string, which will use the command as name.
* @returns {Promise}
* A promise object which becomes fullfilled when all npm-scripts are completed.
*/
Expand All @@ -225,6 +229,7 @@ module.exports = function npmRunAll(patternOrPatterns, options) { //eslint-disab
const maxParallel = parallel ? ((options && options.maxParallel) || 0) : 1
const aggregateOutput = Boolean(options && options.aggregateOutput)
const npmPath = options && options.npmPath
const taskNameString = (config && config.labels) || null
try {
const patterns = parsePatterns(patternOrPatterns, args)
if (patterns.length === 0) {
Expand All @@ -243,6 +248,8 @@ module.exports = function npmRunAll(patternOrPatterns, options) { //eslint-disab
throw new Error("Invalid options.race; It requires options.parallel")
}

const taskNames = (taskNameString && taskNameString.split(/,/g)) || []

const prefixOptions = [].concat(
silent ? ["--silent"] : [],
packageConfig ? toOverwriteOptions(packageConfig) : [],
Expand All @@ -257,8 +264,13 @@ module.exports = function npmRunAll(patternOrPatterns, options) { //eslint-disab
return readPackageJson()
})
.then(x => {
const tasks = matchTasks(x.taskList, patterns)
const labelWidth = tasks.reduce(maxLength, 0)
const tasks = {}
matchTasks(x.taskList, patterns).forEach((command, index) => {
const taskName = taskNames.length - 1 >= index ? taskNames[index] : command

tasks[taskName] = command
})
const labelWidth = Object.keys(tasks).reduce(maxLength, 0)

return runTasks(tasks, {
stdin,
Expand Down
12 changes: 6 additions & 6 deletions lib/run-task.js
Expand Up @@ -107,7 +107,7 @@ function cleanTaskArg(arg) {
* 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} 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.
Expand Down Expand Up @@ -135,10 +135,10 @@ function cleanTaskArg(arg) {
*/
module.exports = function runTask(task, options) {
let cp = null
const promise = new Promise((resolve, reject) => {
const promise = new Promise((resolve, reject) => { //eslint-disable-line complexity
const stdin = options.stdin
const stdout = wrapLabeling(task, options.stdout, options.labelState)
const stderr = wrapLabeling(task, options.stderr, options.labelState)
const stdout = wrapLabeling(task.name, options.stdout, options.labelState)
const stderr = wrapLabeling(task.name, options.stderr, options.labelState)
const stdinKind = detectStreamKind(stdin, process.stdin)
const stdoutKind = detectStreamKind(stdout, process.stdout)
const stderrKind = detectStreamKind(stderr, process.stderr)
Expand All @@ -147,7 +147,7 @@ module.exports = function runTask(task, options) {
// Print task name.
if (options.printName && stdout != null) {
stdout.write(createHeader(
task,
task.name,
options.packageInfo,
options.stdout.isTTY
))
Expand All @@ -169,7 +169,7 @@ module.exports = function runTask(task, options) {
else if (options.prefixOptions.indexOf("--silent") !== -1) {
spawnArgs.push("--silent")
}
Array.prototype.push.apply(spawnArgs, parseArgs(task).map(cleanTaskArg))
Array.prototype.push.apply(spawnArgs, parseArgs(task.command).map(cleanTaskArg))

cp = spawn(execPath, spawnArgs, spawnOptions)

Expand Down
15 changes: 8 additions & 7 deletions lib/run-tasks.js
Expand Up @@ -41,20 +41,21 @@ function remove(array, x) {
*
* 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} 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) {
const taskNames = Object.keys(tasks)
if (taskNames.length === 0) {
resolve([])
return
}

const results = tasks.map(task => ({ name: task, code: undefined }))
const queue = tasks.map((task, index) => ({ name: task, index }))
const results = taskNames.map(taskName => ({ name: taskName, code: undefined }))
const queue = taskNames.map((taskName, index) => ({ name: taskName, command: tasks[taskName], index }))
const promises = []
let error = null
let aborted = false
Expand Down Expand Up @@ -119,7 +120,7 @@ module.exports = function runTasks(tasks, options) {
}

const task = queue.shift()
const promise = runTask(task.name, optionsClone)
const promise = runTask(task, optionsClone)

promises.push(promise)
promise.then(
Expand Down Expand Up @@ -168,8 +169,8 @@ module.exports = function runTasks(tasks, options) {

const max = options.maxParallel
const end = (typeof max === "number" && max > 0)
? Math.min(tasks.length, max)
: tasks.length
? Math.min(taskNames.length, max)
: taskNames.length
for (let i = 0; i < end; ++i) {
next()
}
Expand Down
110 changes: 110 additions & 0 deletions test/custom-labels.js
@@ -0,0 +1,110 @@
"use strict"

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const assert = require("power-assert")
const util = require("./lib/util")
const BufferStream = require("./lib/buffer-stream")
const removeResult = util.removeResult
const runPar = util.runPar

//------------------------------------------------------------------------------
// Test
//------------------------------------------------------------------------------

describe("[custom labels]", () => {
before(() => process.chdir("test-workspace"))
after(() => process.chdir(".."))

beforeEach(removeResult)

describe("should use the given label instead of script name", () => {
const EXPECTED_LINES = [
"[Unit Test ] abcabc",
"[Unit Test ] abc",
"[Unit Test ] abcabc",
"[Unit Test ] abc",
"[Unit Test ] abc",
"[Unit Test ] abc",
"[Unit Test ] abc",
"[Unit Test ] abc",
"[Unit Test ] ",
"[Unit Test ] abc",
"[Unit Test ] abcabc",
"[Unit Test ] ",
"[Unit Test ] ",
"[Unit Test ] ",
"[Unit Test ] abc",

"[test-task:echo def] defdef",
"[test-task:echo def] def",
"[test-task:echo def] defdef",
"[test-task:echo def] def",
"[test-task:echo def] def",
"[test-task:echo def] def",
"[test-task:echo def] def",
"[test-task:echo def] def",
"[test-task:echo def] ",
"[test-task:echo def] def",
"[test-task:echo def] defdef",
"[test-task:echo def] ",
"[test-task:echo def] ",
"[test-task:echo def] ",
"[test-task:echo def] def",
]

it("npm-run-all --labels=\"Unit Test\"", async () => {
const stdout = new BufferStream()
await runPar(["test-task:echo abc", "test-task:echo def", "--silent", "--print-label", "--labels=Unit Test"], stdout)
for (const line of stdout.value.split(/\n\r?/g)) {
assert(EXPECTED_LINES.includes(line), `Missing line: ${line}`)
}
})
})

describe("should use all given label instead of script name", () => {
const EXPECTED_LINES = [
"[Unit Test 1] abcabc",
"[Unit Test 1] abc",
"[Unit Test 1] abcabc",
"[Unit Test 1] abc",
"[Unit Test 1] abc",
"[Unit Test 1] abc",
"[Unit Test 1] abc",
"[Unit Test 1] abc",
"[Unit Test 1] ",
"[Unit Test 1] abc",
"[Unit Test 1] abcabc",
"[Unit Test 1] ",
"[Unit Test 1] ",
"[Unit Test 1] ",
"[Unit Test 1] abc",

"[Unit Test 2] defdef",
"[Unit Test 2] def",
"[Unit Test 2] defdef",
"[Unit Test 2] def",
"[Unit Test 2] def",
"[Unit Test 2] def",
"[Unit Test 2] def",
"[Unit Test 2] def",
"[Unit Test 2] ",
"[Unit Test 2] def",
"[Unit Test 2] defdef",
"[Unit Test 2] ",
"[Unit Test 2] ",
"[Unit Test 2] ",
"[Unit Test 2] def",
]

it("npm-run-all --labels=\"Unit Test 1,Unit Test 2\"", async () => {
const stdout = new BufferStream()
await runPar(["test-task:echo abc", "test-task:echo def", "--silent", "--print-label", "--labels=Unit Test 1,Unit Test 2"], stdout)
for (const line of stdout.value.split(/\n\r?/g)) {
assert(EXPECTED_LINES.includes(line), `Missing line: ${line}`)
}
})
})
})