Automatopia
JavaScript classes for creating high-performance task and build automation.
Install / Use
/learn @jamesshore/AutomatopiaREADME
Automatopia
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.
Examples
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:
Define and run a build
"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;
}
Automatically run the build on changes
"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;
}
}
License
MIT License. See LICENSE.TXT.
Change History
- 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;
watchscript; 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
Related Skills
node-connect
345.4kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
104.6kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
345.4kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
345.4kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
