UNPKG

@drovp/run

Version:

Execute one or multiple console commands on dropped items.

246 lines (245 loc) 9.05 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.commonPathsRoot = void 0; const OS = require("os"); const Path = require("path"); const CP = require("child_process"); const util_1 = require("util"); const fs_1 = require("fs"); const dayjs = require("dayjs"); const platform_paths_1 = require("platform-paths"); const expand_template_literal_1 = require("expand-template-literal"); const prettyMs = require("pretty-ms"); const exec = (0, util_1.promisify)(CP.exec); async function default_1(payload, { stage, output }) { var _a, _b, _c; const { id, input, options } = payload; const stdouts = []; const stderrs = []; const commonVariables = { starttime: Date.now(), Path, Time: dayjs, uid, }; const { commands, outputs, outputMode } = options; const inputs = []; for (const input of payload.inputs) { switch (input.kind) { case 'directory': case 'file': { inputs.push({ type: input.kind, payload: input.path, ...extractPathVariables(input.path), }); break; } case 'string': inputs.push({ type: input.kind, contents: input.contents, payload: input.contents, }); break; case 'url': { const url = new URL(input.url); inputs.push({ type: input.kind, url: input.url, hostname: url.hostname, pathname: url.pathname, username: url.username, password: url.password, payload: input.url, }); break; } } } if (options.bulk) { commonVariables.inputs = inputs; } else if (inputs.length > 0) { Object.assign(commonVariables, inputs[0]); commonVariables.type = input.kind; } let onlyFiles = inputs.filter((input) => input.type === 'file' || input.type === 'directory'); let commondir = ((_a = onlyFiles[0]) === null || _a === void 0 ? void 0 : _a.dirname) || 'undefined'; for (let i = 1; i < onlyFiles.length; i++) commondir = commonPathsRoot(commondir, onlyFiles[i].path); commonVariables.commondir = commondir; const allTemplates = [ ...commands.map(({ template }) => template), ...commands.map(({ cwd }) => cwd), ...outputs.map(({ template }) => template), ].join(';'); for (const name of Object.keys(platform_paths_1.platformPaths)) { if (allTemplates.includes(name)) commonVariables[name] = await platform_paths_1.platformPaths[name](); } if (!options.parallelMode) { commonVariables.stdouts = stdouts; commonVariables.stderrs = stderrs; } const tmpDir = Path.join(OS.tmpdir(), `drovp-run-operation-${id}`); const tmpDirIsNeeded = commands.find(({ cwd }) => cwd.trim().length === 0) != null; if (tmpDirIsNeeded) { console.log(`creating temporary working directory`); console.log(`path: "${tmpDir}"`); await fs_1.promises.mkdir(tmpDir, { recursive: true }); } const cleanup = async () => { if (!tmpDirIsNeeded) return; console.log(`deleting temporary working directory`); await fs_1.promises.rm(tmpDir, { recursive: true }); }; const parallelPromises = []; for (let i = 0; i < commands.length; i++) { let { template, cwd, ignoreErrors } = commands[i]; const variables = { ...commonVariables, cwd }; if (options.parallelMode) { variables.stdout = stdouts[stdouts.length - 1]; variables.stderr = stderrs[stderrs.length - 1]; } else { stage(`${i + 1}/${options.commands.length}`); } cwd = `${cwd}`.trim(); let cwdPath; if (cwd.length > 0) { try { cwdPath = await (0, expand_template_literal_1.expandTemplateLiteral)(cwd.replace(/\r?\n/g, '').trim(), commonVariables); } catch (error) { output.error(`command[${i}] cwd template error: ${eem(error)}`); return; } try { const stat = await pathExists(cwdPath); if (!stat || stat.isDirectory()) await fs_1.promises.mkdir(cwdPath, { recursive: true }); } catch (error) { output.error(`command[${i}]: Error creating cwd "${cwd}"${cwd !== cwdPath ? `, expanded into "${cwdPath}":` : ''}: ${eem(error)}`); return; } } else { cwdPath = tmpDir; } variables.cwd = cwdPath; console.log(`===== COMMAND[${i}]: ==========\n${template}`); let command; try { command = await (0, expand_template_literal_1.expandTemplateLiteral)(template.replace(/\s*(\^|\\)?\n\s*/g, ' ').trim(), variables).trim(); } catch (error) { console.log(`----- TEMPLATE ERROR: ------`); output.error(eem(error)); console.log(`============================`); return; } console.log(`----- FILLED: --------------\n${command}\n----- CWD: -----------------\n"${cwdPath}"\n============================`); if (!command) { output.error(`command[${i}]: template produced an empty command`); return; } const commandStartTime = Date.now(); const reportTime = () => console.log(`command[${i}] time: ${prettyMs(Date.now() - commandStartTime)}`); const resolve = ({ stdout, stderr }) => { stdouts[i] = `${stdout}`; stderrs[i] = `${stderr}`; }; const reject = (error) => { output.error(`COMMAND[${i}] failed with exit code ${error.code}.`); }; try { const process = exec(command, { cwd: cwdPath }); (_b = process.child.stdout) === null || _b === void 0 ? void 0 : _b.on('data', (buffer) => console.log(buffer.toString())); (_c = process.child.stderr) === null || _c === void 0 ? void 0 : _c.on('data', (buffer) => console.log(buffer.toString())); if (options.parallelMode) { parallelPromises.push(process.then(resolve).catch(reject).finally(reportTime)); } else { const result = await process; reportTime(); resolve(result); } } catch (error) { reject(error); if (!ignoreErrors) break; } } if (parallelPromises.length > 0) await Promise.all(parallelPromises); await cleanup(); commonVariables.stdouts = stdouts; for (let i = 0; i < options.outputs.length; i++) { let { template, type } = outputs[i]; let payload; try { payload = await (0, expand_template_literal_1.expandTemplateLiteral)(template.replace(/\s*(\^|\\)?\n\s*/g, ' ').trim(), commonVariables); } catch (error) { throw new Error(`output[${i}] template error: ${eem(error)}`); } if (payload) { output[type](payload || ''); if (outputMode === 'first') break; } else { if (outputMode === 'all') output.error(`output[${i}] template produced an empty string.`); } } } exports.default = default_1; function eem(error, preferStack = false) { return error instanceof Error ? (preferStack ? error.stack || error.message : error.message) : `${error}`; } const uid = (size = 10) => Array(size) .fill(0) .map(() => Math.floor(Math.random() * 36).toString(36)) .join(''); async function pathExists(path) { try { return await fs_1.promises.stat(path); } catch (error) { if ((error === null || error === void 0 ? void 0 : error.code) === 'ENOENT') return false; throw error; } } function extractPathVariables(path) { const extname = Path.extname(path); const dirname = Path.dirname(path); return { path, extname, ext: extname.slice(1), dirname, basename: Path.basename(path), filename: Path.basename(path, extname), dirbasename: Path.basename(dirname), }; } function commonPathsRoot(a, b) { let sameParts = []; const aParts = a.split(/[\\\/]+/); const bParts = b.split(/[\\\/]+/); const loopSize = Math.min(aParts.length, bParts.length); for (let i = 0; i < loopSize; i++) { if (aParts[i] === bParts[i]) sameParts.push(aParts[i]); else break; } return sameParts.join(Path.sep); } exports.commonPathsRoot = commonPathsRoot;