Skip to content

Commit

Permalink
refactor(task-runner): format and simplify
Browse files Browse the repository at this point in the history
no --dev flag necessary, just explicit imports
runner is extensionless, so Prettier needs
to be called explicitely on it
  • Loading branch information
mxdvl committed Jun 24, 2024
1 parent 9820469 commit e22e044
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 145 deletions.
4 changes: 2 additions & 2 deletions makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ compile: install

# Compile all assets in development.
compile-dev: install
@NODE_ENV=development ./tools/task-runner/runner compile --dev
@NODE_ENV=development ./tools/task-runner/runner compile/index.dev

# Compile atom-specific JS
compile-atoms: install
Expand All @@ -58,7 +58,7 @@ compile-javascript: install # PRIVATE
@./tools/task-runner/runner compile/javascript

compile-javascript-dev: install # PRIVATE
@./tools/task-runner/runner compile/javascript --dev
@./tools/task-runner/runner compile/javascript/index.dev

compile-css: install # PRIVATE
@./tools/task-runner/runner compile/css
Expand Down
2 changes: 0 additions & 2 deletions tools/__tasks__/compile/javascript/index.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ const inlineSVGs = require('../inline-svgs/index.js');
const clean = require('./clean.js');
const copy = require('./copy.js');
const webpack = require('./webpack.dev');
const webpackDCR = require('./webpack-dcr.dev');
const bundlePolyfills = require('./bundle-polyfills');

const task = {
Expand All @@ -13,7 +12,6 @@ const task = {
clean,
copy,
webpack,
webpackDCR,
bundlePolyfills,
],
};
Expand Down
2 changes: 1 addition & 1 deletion tools/task-runner/run-task-verbose-formater.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,4 @@ class VerboseRenderer {
}
}

module.exports = VerboseRenderer;
module.exports = { VerboseRenderer };
319 changes: 179 additions & 140 deletions tools/task-runner/runner
Original file line number Diff line number Diff line change
Expand Up @@ -8,51 +8,61 @@ const path = require('path');
const os = require('os');

const yargs = require('yargs');
const { hideBin } = require('yargs/helpers');
const { Listr } = require('listr2');
const execa = require('execa');
const chalk = require('chalk');
const figures = require('figures');
const uniq = require('lodash.uniq');
const { VerboseRenderer } = require('./run-task-verbose-formater.js');

// name of the tasks directory
const tasksDirectory = '__tasks__';

// use yargs to get a useful CLI
const { dev: IS_DEV, debug: IS_DEBUG, verbose: IS_VERBOSE, stdout: IS_STDOUT, _: TASKS } = yargs
.option('dev', {
demand: false,
describe: 'Prefer the dev version of the task, if it exits.',
type: 'boolean',
nargs: 0,
})
.option('debug', {
demand: false,
describe: 'Log everything there is to log.',
type: 'boolean',
nargs: 0,
})
.option('verbose', {
demand: false,
describe: 'Log everything there is to log with a simple format for the output.',
type: 'boolean',
nargs: 0,
})
.option('stdout', {
demand: false,
describe: 'Log all stdout once the tasks are finished (errors are logged by default).',
type: 'boolean',
nargs: 0,
})
.usage('Usage: $0 <task> [<task>] [--dev]')
.command('task', `Run a task defined in '${tasksDirectory}'.`)
.example('$0 copy', 'Run all the copy tasks.')
.example('$0 javascript/copy', 'Run the javascript copy task.')
.example('$0 javascript/copy --dev', 'Run the javascript copy task, and prefer the development version, if it exists.')
.example('$0 javascript/copy css/copy --dev', 'Run the javascript and css copy tasks, and prefer the development versions, if they exist.')
.demand(1)
.help().alias('h', 'help') // eslint-disable-line newline-per-chained-call
.version().alias('v', 'version') // eslint-disable-line newline-per-chained-call
.argv;
const {
debug: IS_DEBUG,
verbose: IS_VERBOSE,
stdout: IS_STDOUT,
_: TASKS,
} = yargs(hideBin(process.argv))
.option('debug', {
demand: false,
describe: 'Log everything there is to log.',
type: 'boolean',
nargs: 0,
})
.option('verbose', {
demand: false,
describe:
'Log everything there is to log with a simple format for the output.',
type: 'boolean',
nargs: 0,
})
.option('stdout', {
demand: false,
describe:
'Log all stdout once the tasks are finished (errors are logged by default).',
type: 'boolean',
nargs: 0,
})
.usage('Usage: $0 <task> [<task>] [--dev]')
.command('task', `Run a task defined in '${tasksDirectory}'.`)
.example('$0 copy', 'Run all the copy tasks.')
.example('$0 javascript/copy', 'Run the javascript copy task.')
.example(
'$0 javascript/copy --dev',
'Run the javascript copy task, and prefer the development version, if it exists.',
)
.example(
'$0 javascript/copy css/copy --dev',
'Run the javascript and css copy tasks, and prefer the development versions, if they exist.',
)
.demand(1)
.help()
.alias('h', 'help') // eslint-disable-line newline-per-chained-call
.version()
.alias('v', 'version').argv; // eslint-disable-line newline-per-chained-call

// if this is true, we log as much as we can
const VERBOSE = IS_VERBOSE || IS_DEBUG;
Expand All @@ -66,124 +76,153 @@ const cache = [];

// use exaca to run simple terminal commands
const exec = (task, onError, ctx) => {
const [cmd, ...args] = task.trim().split(' ');

return execa(cmd, args)
.then((result) => {
// store any stdout incase we need it later
if (result.stdout) ctx.stdouts.push(result.stdout);
})
.catch((e) => {
// if the task supplies an `onError` function, run it
if (typeof onError === 'function') onError(ctx);
// continue with a fake rejected promise
return Promise.reject(e);
});
const [cmd, ...args] = task.trim().split(' ');

return execa(cmd, args)
.then((result) => {
// store any stdout incase we need it later
if (result.stdout) ctx.stdouts.push(result.stdout);
})
.catch((e) => {
// if the task supplies an `onError` function, run it
if (typeof onError === 'function') onError(ctx);
// continue with a fake rejected promise
return Promise.reject(e);
});
};

const getCpuCount = () => os.cpus().length;

// turn a list of our tasks into objects listr can use
function listrify(steps, { concurrent = false } = {}) {
const listrTasks = steps
.map((step) => {
const { description: title, task, concurrent: isConcurrent, onError } = step;

// if another task has included this one, don't run it again
const skip = cache.indexOf(step) !== -1 ? () => 'Skipping: already run by another task' : false;
cache.push(step);

// if the task is a set of subtasks, prepare them
if (Array.isArray(task)) {
return {
title,
task: () => listrify(task.map((_task) => {
if (_task.task) return _task;
if (typeof _task === 'string') return { title, task: ctx => exec(_task, onError, ctx), skip };
return { title, task: _task, skip };
}), { concurrent: VERBOSE ? false : (isConcurrent ? getCpuCount() : false) }),
skip,
};
}

// treat tasks that are strings as terminal commands
if (typeof task === 'string') return { title, task: ctx => exec(task, onError, ctx), skip };

// assume the task is a function
// if it's not, listr will blow up anyway, which is fine
return {
title,
task: ctx => new Promise((resolve, reject) => {
try {
resolve(task(ctx));
} catch (e) {
if (typeof onError === 'function') onError(ctx);
if (VERBOSE) console.log(e);
reject(e);
}
}),
skip,
};
});

let renderer = 'default';
if (IS_VERBOSE) renderer = require('./run-task-verbose-formater');

return new Listr(listrTasks, {
concurrent: concurrent ? getCpuCount() : false,
collapse: true,
renderer,
});
const listrTasks = steps.map((step) => {
const {
description: title,
task,
concurrent: isConcurrent,
onError,
} = step;

// if another task has included this one, don't run it again
const skip =
cache.indexOf(step) !== -1
? () => 'Skipping: already run by another task'
: false;
cache.push(step);

// if the task is a set of subtasks, prepare them
if (Array.isArray(task)) {
return {
title,
task: () =>
listrify(
task.map((_task) => {
if (_task.task) return _task;
if (typeof _task === 'string')
return {
title,
task: (ctx) => exec(_task, onError, ctx),
skip,
};
return { title, task: _task, skip };
}),
{
concurrent: VERBOSE
? false
: isConcurrent
? getCpuCount()
: false,
},
),
skip,
};
}

// treat tasks that are strings as terminal commands
if (typeof task === 'string')
return { title, task: (ctx) => exec(task, onError, ctx), skip };

// assume the task is a function
// if it's not, listr will blow up anyway, which is fine
return {
title,
task: (ctx) =>
new Promise((resolve, reject) => {
try {
resolve(task(ctx));
} catch (e) {
if (typeof onError === 'function') onError(ctx);
if (VERBOSE) console.log(e);
reject(e);
}
}),
skip,
};
});

const renderer = IS_VERBOSE ? VerboseRenderer : 'default';

return new Listr(listrTasks, {
concurrent: concurrent ? getCpuCount() : false,
collapse: true,
renderer,
});
}

// resolve the tasks from yargs to actual files
const getTasksFromModule = (taskName) => {
try {
const modulePath = path.resolve(taskSrc, taskName);
if (IS_DEV) {
try {
return require(`${modulePath}.dev`);
} catch (e) { /* do nothing */ }
try {
return require(`${modulePath}/index.dev`);
} catch (e) { /* do nothing */ }
}
return require(modulePath);
} catch (e) {
// we can't find any modules, or something else has gone wrong in resolving it
// so output an erroring task
return {
description: `${chalk.red(taskName)} failed:`,
task: () => Promise.reject(e),
};
}
try {
const modulePath = path.resolve(taskSrc, taskName);
return require(modulePath);
} catch (e) {
// we can't find any modules, or something else has gone wrong in resolving it
// so output an erroring task
return {
description: `${chalk.red(taskName)} failed:`,
task: () => Promise.reject(e),
};
}
};

// get a list of the tasks we're going to run
const taskModules = TASKS.map(getTasksFromModule);

// run them!
listrify(taskModules).run({
// we're adding these to the [listr context](https://github.com/SamVerschueren/listr#context)
messages: [],
stdouts: [],
}).catch((e) => {
// something went wrong, so log whatever we have
if (!e.stderr && !e.stdout) console.log(e);
if (e.stderr) console.error(`\n${e.stderr.trim()}`);
if (e.stdout) console.log(`\n${e.stdout.trim()}`);
return Object.assign(e.context, { error: true });
}).then((ctx) => {
if (IS_STDOUT) ctx.stdouts.forEach(stdout => console.log(stdout.toString().trim()));

if (ctx.messages.length) {
console.log('');
uniq(ctx.messages).forEach(message => console.log(chalk.dim(`${figures.arrowRight} ${message.split('\n').join('\n ')}`)));
}

if (ctx.error) {
console.log('');
// if something's gone wrong, fail hard
process.exit(1);
}
});
listrify(taskModules)
.run({
// we're adding these to the [listr context](https://github.com/SamVerschueren/listr#context)
messages: [],
stdouts: [],
})
.catch((e) => {
// something went wrong, so log whatever we have
if (!e.stderr && !e.stdout) console.log(e);
if (e.stderr) console.error(`\n${e.stderr.trim()}`);
if (e.stdout) console.log(`\n${e.stdout.trim()}`);
return Object.assign(e.context, { error: true });
})
.then((ctx) => {
if (IS_STDOUT)
ctx.stdouts.forEach((stdout) =>
console.log(stdout.toString().trim()),
);

if (ctx.messages.length) {
console.log('');
uniq(ctx.messages).forEach((message) =>
console.log(
chalk.dim(
`${figures.arrowRight} ${message
.split('\n')
.join('\n ')}`,
),
),
);
}

if (ctx.error) {
console.log('');
// if something's gone wrong, fail hard
process.exit(1);
}
});

0 comments on commit e22e044

Please sign in to comment.