Skip to content

JavaScript classes for creating high-performance task and build automation.

License

Notifications You must be signed in to change notification settings

jamesshore/automatopia

Repository files navigation

Automatopia

This repository contains JavaScript classes for creating high-performance task and build automation. Here are some of the highlights:

  • Task Automation

    • Tasks - Define and run tasks
    • TaskCli - A command-line interface to Tasks
    • Reporter - Provide tidy build progress
    • Shell - Execute programs
    • FileTree - Convert file globs to filenames
    • Colors - Color-code build output
  • 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

    • version Check the Node.js version
    • lint - Run ESLint on files that have changed
    • tests - Run tests whose dependencies have changed

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; 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

About

JavaScript classes for creating high-performance task and build automation.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published