This repository contains JavaScript classes for creating high-performance task and build automation. Here are some of the highlights:
-
Task Automation
-
Incremental Builds
- Tasks - Define tasks that only run when their source files change
- FileSystem - Determine if a file has changed since the last time you operated on it, or if a file is newer than a set of source files
- DependencyTree - Determine if a file or anything in its dependency tree has changed since the last time you operated on it
-
Watching
- FileSystem - Wait until a set of files change
- Clock - Wait for various conditions
- sounds - Pleasant sounds to play for different build outcomes
-
Example Tools
This repo is designed for you to copy and customize. Everything's documented with JSDoc comments, so look at the files in _build to get started, or check out the highlights above.
To see the example build in action, run ./build.sh
or ./watch.sh quick
from the root of this repo. Use the --help
option for command-line help.
You can find example source code in the _build directory. Check out watch.js and build.js in particular.
The above examples are full-featured and derived from production builds. See below for simpler but complete examples:
"use strict";
const Tasks = require("tasks/tasks");
const TaskCli = require("tasks/task_cli");
const Reporter = require("tasks/reporter");
const FileSystem = require("infrastructure/file_system");
const Version = require("./tools/version");
const Lint = require("./tools/lint");
const Tests = require("./tools/tests");
const lintConfig = require("./config/eslint.conf");
const testConfig = require("./config/tests.conf");
const path = require("node:path");
const rootDir = path.resolve(__dirname, "..");
const generatedDir = `${rootDir}/generated`;
const incrementalDir = `${rootDir}/generated/incremental`;
const timestampDir = `${rootDir}/generated/timestamps`;
const universalExcludes = [ `${rootDir}/node_modules/**`, `${rootDir}/_build/node_modules/tests/vendor/**` ];
const packageJson = `${rootDir}/package.json`;
const lintGlobs = `${rootDir}/**/*.js`;
const testGlobs = `${rootDir}/**/_*_test.js`;
main();
async function main() {
const tasks = await defineTasksAsync();
tasks.setDescriptions({
"default": "Build everything",
"clean": "Erase incremental files",
"version": "Check Node.js version",
"lint": "Lint code",
"unittest": "Run unit tests",
});
TaskCli.create().runAsync(tasks, "BUILD OK", "BUILD FAILURE", async (taskNames, options) => {
await tasks.runTasksAsync(taskNames, options);
});
}
async function defineTasksAsync() {
const fileSystem = FileSystem.create(rootDir, timestampDir);
const fileTree = await fileSystem.readFileTreeAsync(rootDir, universalExcludes);
const tasks = Tasks.create({ fileSystem, incrementalDir });
const version = Version.create(fileSystem);
const lint = Lint.create(fileSystem);
const tests = Tests.create(fileSystem, universalExcludes);
const reporter = Reporter.create();
tasks.defineTask("default", async () => {
await tasks.runTasksAsync([ "version", "lint", "unittest" ]);
});
tasks.defineTask("clean", async () => {
await fileSystem.deleteAsync(generatedDir);
});
tasks.defineTask("version", async () => {
await version.checkAsync({ packageJson, reporter });
});
tasks.defineTask("lint", async () => {
await lint.validateAsync({
description: "JavaScript",
files: fileTree.matchingFiles(lintGlobs),
config: lintConfig,
reporter,
});
});
tasks.defineTask("unittest", async () => {
await tests.runAsync({
description: "unit tests",
files: fileTree.matchingFiles(testGlobs),
config: testConfig,
reporter,
});
});
return tasks;
}
"use strict";
const Build = require("./build");
const FileSystem = require("infrastructure/file_system");
const path = require("node:path");
const rootDir = path.resolve(__dirname, "..");
const timestampDir = `${rootDir}/generated/timestamps`;
const watchGlobs = [ `${rootDir}/_build/**`, `${rootDir}/src/**` ];
main();
async function main() {
const fileSystem = FileSystem.create(rootDir, timestampDir);
const build = Build.create();
while (true) {
console.log("\n*** Building...\n");
const waitPromise = fileSystem.waitForChangeAsync(watchGlobs);
await build.runAsync();
await waitPromise;
}
}
MIT License. See LICENSE.TXT.
- 9 Nov 2024: Prevent dangling Node processes by waiting until build finishes before restarting when build files change
- 14 Oct 2024: TaskCli provides runs build function and provides command-line options; add detailed debug logs
- 13 Oct 2024: Remove test runner into separate "ergotest" npm package
- 6 Oct 2024: Convert to ES Modules
- 1 Oct 2024: More beautification (especially file names and dependency errors)
- 30 Sep 2024: Make type-checking more strict, beautify shell output (particularly TypeScript errors)
- 29 Sep 2024: Add TypeScript support, stop vendoring
node_modules
- 23 Sep 2024: Rebuild from current production codebase
- 21 Sep 2016: Latest npm dependencies; Node LTS v4.5.0
- 13 Jun 2016: Latest npm dependencies; Node LTS v4.4.5
- 22 Jan 2015: Front-end modules;
watch
script; improved documentation;jake run
; latest npm dependencies; integration commit messages; general script improvements - 29 Jul 2014: Replaced NodeUnit with Mocha; updated npm dependencies to latest versions; documented process for installing npm packages; replaced JSHint runner with simplebuild-jshint module
- 22 Dec 2013: Removed unneeded Karma plugins; cleaned up package.json; updated npm dependencies to latest versions
- 24 Sep 2013: Upgraded to Karma 0.10 (also updated all other npm dependencies to latest versions)
- 30 Nov 2012: Initial release