UNPKG

@xarc/run

Version:

concurrent or serial run npm scripts, javascript tasks, and more

169 lines (152 loc) 5.3 kB
"use strict"; const Path = require("path"); const env = require("./env"); const xsh = require("xsh"); const logger = require("../lib/logger"); const config = require("./config"); const ck = require("chalker"); const { searchUpTaskFile } = require("./search-up-task-file"); const WrapProcess = require("./wrap-process"); const npmLoader = require("./npm-loader"); const requireAt = require("require-at"); const optionalRequire = require("optional-require")(require); const instance = require("../lib/xrun-instance"); const TsRunner = require("./ts-runner"); /** * Update the current working directory * @param {string} [dir] - Directory to change to * @returns {string} New working directory */ function updateCwd(dir) { dir = dir || WrapProcess.cwd(); const newCwd = Path.isAbsolute(dir) ? dir : Path.resolve(dir); try { const cwd = WrapProcess.cwd(); if (newCwd !== cwd) { WrapProcess.chdir(newCwd); logger.log(ck`CWD changed to <magenta>${newCwd}</>`); } else if (env.get(env.xrunCwd) !== cwd) { logger.log(ck`CWD is <magenta>${cwd}</>`); } env.set(env.xrunCwd, newCwd); return newCwd; } catch (err) { logger.log(ck`chdir <magenta>${newCwd}</> <red>failed</>`); WrapProcess.exit(1); } } /** * Search for task files in the given directory * @param {boolean} search - Whether to search up directories * @param {ParseOptions} opts - Search options * @returns {SearchResult} Search result */ function searchTaskFile(search, opts) { const xrunDir = Path.join(opts.cwd, opts.dir || ""); const loadResult = searchUpTaskFile(xrunDir, search); if (!loadResult.found) { if (env.get(env.xrunTaskFile) !== "not found") { const x = xsh.pathCwd.replace(xrunDir, "./"); logger.log(ck`No <green>${config.taskFile}</> found in <magenta>${x}</>`); } // set env to let subsequent xrun calls know that the task file was not found // and avoid logging the same message again env.set(env.xrunTaskFile, "not found"); } else if (opts.updateCwd !== false) { // force CWD to where xrun task file was found loadResult.cwd = updateCwd(loadResult.dir); } return loadResult; } /** * Load a task file with TypeScript support * @param {string} name - Path to the task file * @returns {Object|Function|undefined} Loaded task module */ function loadTaskFile(name) { const ext = Path.extname(name); if (ext === ".ts" || ext === ".tsx" || ext === ".mts") { TsRunner.startRunner(); } return optionalRequire(name, { fail: e => { const errMsg = ck`<red>Unable to load ${xsh.pathCwd.replace(name, ".")}</>`; const msg2 = (e.code === "ERR_REQUIRE_ESM" && /* istanbul ignore next */ ` === This is an issue with ts-node/register, install and use tsx ===\n\n`) || ""; logger.error(`${errMsg}: ${msg2}${xsh.pathCwd.replace(e.stack, ".", "g")}`); } }); } /** * Process loaded tasks and register them with xrun * @param {Object|Function} tasks - Tasks to process * @param {string} loadMsg - Message to display when tasks are loaded * @param {string} [ns="xrun"] - Namespace to load tasks into */ function processTasks(tasks, loadMsg, ns = "xrun") { if (typeof tasks === "function") { tasks(instance.xrun); if (loadMsg) { logger.log(`Loaded tasks by calling export function from ${loadMsg}`); } } else if (typeof tasks === "object") { if (tasks.default) { processTasks(tasks.default, `${loadMsg} default export`, ns); } else if (Object.keys(tasks).length > 0) { instance.xrun.load(ns, tasks); logger.log(ck`Loaded tasks from ${loadMsg} into namespace <magenta>${ns}</>`); } else if (loadMsg) { logger.log(`Loaded ${loadMsg}`); } } else { logger.log(ck`Unknown export type <yellow>${typeof tasks}</> from ${loadMsg}`); } } /** * Load tasks from files or required modules * @param {ParseOptions} opts - Options for loading tasks * @param {SearchResult} searchResult - Result from searching for task files * @returns {boolean} Whether any tasks were loaded */ function loadTasks(opts, searchResult) { let loaded = false; npmLoader(instance.xrun, opts); if (opts.require) { opts.require.forEach(xmod => { let file; try { file = requireAt(WrapProcess.cwd()).resolve(xmod); } catch (err) { logger.log( ck`<red>ERROR:</> <yellow>Unable to require module</> <cyan>'${xmod}'</> - <red>${err.message}</>` ); return; } const tasks = loadTaskFile(file); /* istanbul ignore else */ if (tasks) { const loadMsg = ck`<green>${xmod}</>`; processTasks(tasks, loadMsg); return (loaded = true); } }); } else if (searchResult.xrunFile) { const tasks = loadTaskFile(searchResult.xrunFile); /* istanbul ignore else */ if (tasks) { processTasks( tasks, env.get(env.xrunTaskFile) !== searchResult.xrunFile ? ck`<green>${xsh.pathCwd.replace(searchResult.xrunFile, ".")}</>` : "" ); env.set(env.xrunTaskFile, searchResult.xrunFile); return (loaded = true); } } return loaded; } module.exports = { updateCwd, searchTaskFile, loadTaskFile, processTasks, loadTasks };