Skip to content

Latest commit

 

History

History
150 lines (106 loc) · 4.88 KB

README.md

File metadata and controls

150 lines (106 loc) · 4.88 KB

booty: the biggest thing to happen to ECMAScript since Netscape

SUMMARY

booty is a convention for writing development tasks for JavaScript (Node.js) projects, via npm run hooks.

EXAMPLE

ABOUT

Tasks are implemented using modern JavaScript code. We can use a directory like tasks/ to house task definitions.

tasks/groceries

Here, a groceries task

#!/usr/bin/env node
'use strict';

import child_process from 'node:child_process';
import process from 'node:process';

export default function groceries() {
    child_process.execSync('echo milk...', { stdio: 'inherit' });
    child_process.execSync('echo eggs...', { stdio: 'inherit' });
    child_process.execSync('echo just browsing', { stdio: 'inherit' });
}

function main() {
    groceries();
}

if (import.meta.url === `file://${process.argv[1]}`) { main(); }

Take care to avoid colliding with conventional NPM life cycle task names.

tasks/clean

It's often a good idea to configure a clean task to automate resetting the development environment.

#!/usr/bin/env node
'use strict';

import fs from 'node:fs';
import process from 'node:process';

export default function clean() {
    console.log('removing junk files...');

    fs.rmSync('nosuchdirectory', { force: true, recursive: true });
    fs.rmSync('nosuchfile.dat', { force: true });
}

function main() {
    clean();
}

if (import.meta.url === `file://${process.argv[1]}`) { main(); }

tasks/all

Subtasks can be aggregated together into higher level tasks.

Let's prepare all our errands to trigger together.

#!/usr/bin/env node
'use strict';

import groceries from './groceries';
import clean from './clean';

import process from 'node:process';

function main() {
    groceries();
    clean();
}

if (import.meta.url === `file://${process.argv[1]}`) { main(); }

You can group or divide tasks into hierarchy shapes as needed, depending on the complexity of the tasks and the frequency of low level tasks.

Then, wire up each task to the npm run system.

package.json

{
    "name": "@mcandre/hello-booty",
    "scripts": {
        "all": "./tasks/all",
        "groceries": "./tasks/groceries",
        "clean": "./tasks/clean"
    },
    "type": "module"
}

NOTES

Compared to modern task runners, the npm run system has some quirks:

  • npm run accepts only one task name at a time. To trigger multiple tasks, either submit separate npm run <task a>, npm run <task b> commands, or create an aggregate task that invokes the subtasks.
  • npm run has no concept of a default task. We can adopt the convention npm run all, in reverence to traditional makefiles.

Why not _____?

  • Grunt is largely unmaintained, and increases the attack surface.
  • Gulp is terrible at CLI tasks, and increases the attack surface.
  • Make isn't JavaScript.

REQUIREMENTS

SEE ALSO

  • Inspiration from nobuild, a convention for C/C++ build systems
  • bashate, a shell script style linter
  • bb, a build system for (g)awk projects
  • Gradle, a build system for JVM projects
  • jelly, a JSON task runner
  • lake, a Lua task runner
  • Leiningen + lein-exec, a Clojure task runner
  • lichen, a sed task runner
  • Mage, a task runner for Go projects
  • mian, a task runner for (Chicken) Scheme Lisp
  • npm, Grunt, Node.js task runners
  • periscope, a linter for unscoped NPM packages
  • POSIX make, a task runner standard for C/C++ and various other software projects
  • Rake, a task runner for Ruby projects
  • Rebar3, a build system for Erlang projects
  • rez builds C/C++ projects
  • sbt, a build system for Scala projects
  • Shake, a task runner for Haskell projects
  • ShellCheck, a shell script linter with a rich collection of rules for promoting safer scripting
  • slick, a linter to enforce stricter, unextended POSIX sh syntax compliance
  • stank, a collection of POSIX-y shell script linters
  • tinyrick for Rust projects
  • yao, a task runner for Common LISP projects

🍑