recursive-exec
Version:
Run a command on each file in a folder and its subfolders (CLI tool designed for use in npm package.json scripts)
77 lines (75 loc) • 3.64 kB
JavaScript
//! recursive-exec v1.0.3 ~~ https://github.com/center-key/recursive-exec ~~ MIT License
import { globSync } from 'glob';
import { spawnSync } from 'node:child_process';
import chalk from 'chalk';
import fs from 'fs';
import log from 'fancy-log';
import path from 'path';
import slash from 'slash';
const recursiveExec = {
find(folder, command, options) {
const defaults = {
excludes: null,
extensions: null,
quiet: false,
};
const settings = { ...defaults, ...options };
const errorMessage = !folder ? 'Must specify the folder path.' :
!fs.existsSync(folder) ? 'Folder does not exist: ' + folder :
!fs.statSync(folder).isDirectory() ? 'Folder is not a folder: ' + folder :
!command ? 'Command template missing.' :
null;
if (errorMessage)
throw new Error('[recursive-exec] ' + errorMessage);
const startTime = Date.now();
const source = slash(path.normalize(folder)).replace(/\/$/, '');
const logName = chalk.gray('recursive-exec');
const getExts = () => settings.extensions.join('|');
const extensions = !settings.extensions ? '' : `@(${getExts()})`;
const files = globSync(source + '/**/*' + extensions, { ignore: '**/node_modules/**/*', nodir: true });
const excludes = settings.excludes || [];
const keep = (file) => !excludes.find(exclude => file.includes(exclude));
const toCamel = (token) => token.replace(/-./g, char => char[1].toUpperCase());
if (!settings.quiet)
log(logName, chalk.magenta(source), settings.echo ? chalk.yellow('(dry run)') : '');
const calcResult = (file) => {
const parts = path.parse(file);
const filename = file.substring(source.length + 1);
const relative = parts.dir.substring(source.length + 1);
const basename = filename.substring(0, filename.length - path.extname(filename).length);
const interpolate = (template) => template
.replaceAll('{{file}}', file)
.replaceAll('{{filename}}', filename)
.replaceAll('{{basename}}', basename)
.replaceAll('{{path}}', relative)
.replaceAll('{{name}}', parts.name)
.replaceAll('{{nameCamelCase}}', toCamel(parts.name));
return {
folder: source,
file: file,
path: relative,
filename: filename,
basename: basename,
name: parts.name,
command: interpolate(command),
};
};
const results = files.map(slash).filter(keep).sort().map(calcResult);
const previewCommand = (result) => {
log(logName, chalk.blue.bold('preview:'), chalk.yellow(result.command));
};
const execCommand = (result) => {
if (!settings.quiet)
log(logName, chalk.blue.bold('command:'), chalk.cyanBright(result.command));
const task = spawnSync(result.command, { shell: true, stdio: 'inherit' });
if (task.status !== 0)
throw new Error(`[recursive-exec] Status: ${task.status}\nCommand: ${result.command}`);
};
results.forEach(settings.echo ? previewCommand : execCommand);
const summary = `(files: ${results.length}, ${Date.now() - startTime}ms)`;
if (!settings.quiet)
log(logName, chalk.green('done'), chalk.white(summary));
return results;
},
};
export { recursiveExec };