UNPKG

@coat/cli

Version:

TODO: See #3

116 lines (113 loc) 4.37 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.run = run; var _fs = require("fs"); var _path = _interopRequireDefault(require("path")); var _constants = require("../constants"); var _runSingleScript = require("./run-single-script"); var _runMultipleScripts = require("./run-multiple-scripts"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * Runs the provided script patterns in parallel * from the specified working directory. * * Script patterns refer to script names in the working directory's * package.json file. * * Patterns can include wildcards, example: * package.json * { * "scripts": { * "build:tsc": "tsc", * "build:babel": "babel" * } * } * Using "build:*" as a script pattern will run both the * build:tsc and build:babel scripts in parallel. * * @param options.cwd The working directory of the current project * @param options.scriptPatterns The script patterns that will be evaluated and run */ async function run({ cwd, scriptPatterns }) { // Split patterns by script names and arguments that // will be forwarded to each script. // // All patterns that follow the first dash ("-") // will be forwarded as arguments. // // Example: // scriptPatterns: ["build:* test --watch files"] // scriptInputs: ["build:*", "test"] // args: ["--watch", "files"] const { scriptInputs, args } = scriptPatterns.reduce((accumulator, pattern) => { if (accumulator.args.length || pattern.startsWith("-")) { accumulator.args.push(pattern); } else { accumulator.scriptInputs.push(pattern); } return accumulator; }, { scriptInputs: [], args: [] }); // It is possible that all provided patterns are arguments // without a single script name. In this case an error should // thrown, because no script will be run if (!scriptInputs.length) { throw new Error("No script pattern provided."); } // Get all script names from package.json const packageJsonRaw = await _fs.promises.readFile(_path.default.join(cwd, _constants.PACKAGE_JSON_FILENAME), "utf8"); const packageJson = JSON.parse(packageJsonRaw); const packageJsonScriptNames = Object.keys(packageJson.scripts ?? {}); const packageJsonScriptNamesSet = new Set(packageJsonScriptNames); const scriptsToRun = scriptInputs.reduce((accumulator, scriptPattern) => { // Script patterns can either be a simple script name // or multiple scripts using a wildcard (*). if (scriptPattern.includes("*")) { // run should use simplistic logic and match all scripts // that start with the part until the first '*' character. // // More sophisticated matching is not needed at this time // since the main use case is for scripts that are merged // by coat. There are other cli tools that can provide // more sophisticated behavior, e.g. concurrently. const scriptPatternPrefix = scriptPattern.split("*")[0]; const scriptsForPattern = packageJsonScriptNames.filter(scriptName => scriptName.startsWith(scriptPatternPrefix)); if (!scriptsForPattern.length) { // Throw an error when a pattern yields no result to // prevent unintended behavior where a user expected a script // to be run but it did not match any scripts. throw new Error(`No scripts found in package.json for pattern: ${scriptPattern}`); } accumulator.push(...scriptsForPattern); } else { if (!packageJsonScriptNamesSet.has(scriptPattern)) { // Throw an error when a script name is not found to // prevent unintended behavior where a user expected a script // to be run but it did not match any scripts. throw new Error(`No script named "${scriptPattern}" found in package.json`); } accumulator.push(scriptPattern); } return accumulator; }, []); if (scriptsToRun.length === 1) { // If the provided input resolves to a single // script, it should be called in a special // way to inherit the stdio and allow advanced // interaction patterns // e.g. using jest in watch mode await (0, _runSingleScript.runSingleScript)(cwd, scriptsToRun[0], args); } else { await (0, _runMultipleScripts.runMultipleScripts)(cwd, scriptsToRun, args); } }