You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
243 lines
6.2 KiB
243 lines
6.2 KiB
/** |
|
* @author Toru Nagashima |
|
* @copyright 2016 Toru Nagashima. All rights reserved. |
|
* See LICENSE file in root directory for full license. |
|
*/ |
|
'use strict' |
|
|
|
// ------------------------------------------------------------------------------ |
|
// 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) { |
|
LOOP: // eslint-disable-line no-labels |
|
for (let i = 0; i < args.length; ++i) { |
|
const arg = args[i] |
|
|
|
switch (arg) { |
|
case '--': |
|
set.rest = args.slice(1 + i) |
|
break LOOP // eslint-disable-line no-labels |
|
|
|
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) |
|
}
|
|
|