Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/guybedford/chomp

'JS Make' - parallel task runner for the frontend ecosystem with a JS extension system.
https://github.com/guybedford/chomp

Last synced: about 1 month ago
JSON representation

'JS Make' - parallel task runner for the frontend ecosystem with a JS extension system.

Awesome Lists containing this project

README

        

# CHOMP

[![Crates.io](https://img.shields.io/badge/crates.io-chompbuild-green.svg)](https://crates.io/crates/chompbuild)
[![Discord](https://img.shields.io/badge/chat-on%20discord-green.svg?logo=discord)](https://discord.gg/5E9zrhguTy)

Chomp is a frontend task runner with advanced features focused on _ease-of-use_ and _not getting in the way_!

1. An advanced task runner with a single command!
1. Easily adapt existing projects / task systems - no need for a rewrite.
1. You enable and manage advanced task runner features with single-line updates.

Chomp is a great option for frontend projects where the goal is getting advanced task runner features (like smart caching) without complexity and overhead.

## One-line migration from npm scripts

Chomp can import a project's established `package.json` scripts without breaking them, as it supports the same features:

```bash
chomp --init --import-scripts
```

Now you can run your npm scripts using Chomp!

> i.e `npm run ` becomes `chomp ` and behaves the same, and you can opt in to further features as needed.

The only difference is, with Chomp — it's faster. And, with a few more tweaks, you can enable smart caching, parallelism, and more!

## What features does Chomp provide?

Chomp is an advanced task runner. It provides features similar to [turbo](https://turbo.build/repo) and [nx](https://nx.dev/) but focuses on ease of use, *not monorepos. It's based on the same principles as traditional make files.

### Parallelism

Chomp [runs tasks in parallel](./docs/task.md#serial-dependencies), based on an extecuted task's dependencies!

### Watch/Serve

Chomp [watches any task](./docs/task.md#watched-rebuilds) by including a `--watch` or `--serve` option! Read more about the power of [`--watch`](./docs/task.md#watched-rebuilds) and [`--serve`](./docs/task.md#static-server).

### A JS extension system

Chomp has a [JS extension system](./docs/extensions.md) that allows you to extend Chomp with your own custom tasks

### Smart caching

Chomp [caches tasks](./docs/task.md#task-caching) based on task dependencies like other tasks or updated files. You don't have to worry about it!

> \*Chomp works for monrepos but it's architected for ease of use and not getting in the way first.

## Install

If you use [Cargo](https://rustup.rs/), run:

```
cargo install chompbuild
```

If you don't use Cargo, run:

```
npm install -g chomp
```

> Note: npm scripts add over 100ms to the script run time.

Common platform binaries are also available for [all releases](https://github.com/guybedford/chomp/releases).

To quickly set up Chomp in a GitHub Actions CI workflow, see the [Chomp GitHub Action](https://github.com/guybedford/chomp-action).

## Documentation

* [CLI Usage](https://github.com/guybedford/chomp/blob/main/docs/cli.md)
* [Chompfile Definitions](https://github.com/guybedford/chomp/blob/main/docs/chompfile.md)
* [Task Definitions](https://github.com/guybedford/chomp/blob/main/docs/task.md)
* [Extensions](https://github.com/guybedford/chomp/blob/main/docs/extensions.md)

## Getting Started

### Migrating from npm Scripts

To convert an existing project using npm `"scripts"` to Chomp, run:

```sh
$ chomp --init --import-scripts
√ chompfile.toml created with 2 package.json script tasks imported.
```

or the shorter version:

```sh
$ chomp -Ii
√ chompfile.toml created with 2 package.json script tasks imported.
```

Then use `chomp ` instead of `npm run `, and enjoy the new features of task dependence, incremental builds, and parallelism!

### Hello World

`chomp` works against a [`chompfile.toml`](https://github.com/guybedford/chomp/blob/main/docs/chompfile.md) [TOML configuration](https://toml.io/) in the same directory as the `chomp` command is run.

Chomp builds up tasks as trees of files which depend on other files, then runs those tasks with [maximum parallelism](https://github.com/guybedford/chomp/blob/main/docs/task.md#task-dependence).

For example, here's a task called `hello` which builds `hello.txt` based on the contents of `name.txt`, which itself is built by another command:

chompfile.toml
```toml
version = 0.1

[[task]]
target = 'name.txt'
run = '''
echo "No name.txt, writing one."
echo "World" > name.txt
'''

[[task]]
name = 'hello'
target = 'hello.txt'
dep = 'name.txt'
run = '''
echo "Hello $(cat name.txt)" > hello.txt
'''
```

with this file saved, the hello command will run all dependency commands before executing its own command:

```sh
$ chomp hello

🞂 name.txt
No name.txt, writing one.
√ name.txt [4.4739ms]
🞂 hello.txt
√ hello.txt [5.8352ms]

$ cat hello.txt
Hello World
```

Finally it populates the `hello.txt` file with the combined output.

Subsequent runs use the mtime of the target files to determine what needs to be rerun.

Rerunning the `hello` command will see that the `hello.txt` target is defined, and that the `name.txt` dependency didn't change, so it will skip running the command again:

```sh
chomp hello

● name.txt [cached]
● hello.txt [cached]
```

Changing the contents of `name.txt` will then invalidate the `hello.txt` target only, not rerunning the `name.txt` command:

```sh
$ echo "Chomp" > name.txt
$ chomp hello

● name.txt [cached]
hello.txt invalidated by name.txt
🞂 hello.txt
√ hello.txt [5.7243ms]

$ cat hello.txt
Hello Chomp
```

The [`deps`](https://github.com/guybedford/chomp/blob/main/docs/task.md#task-dependence) array can be defined for targets, whose targets will then be run first with [invalidation based on target / deps mtime comparisons](https://github.com/guybedford/chomp/blob/main/docs/task.md#task-caching) per the standard Makefile approach.

Powershell is used on Windows, while Bash is used on POSIX systems. Since both `echo` and `>` are defined on both systems, the examples above work cross-platform (Powershell is automatically put into UTF-8 mode for `>` to work similarly).

Note that `&&` and `||` are not supported in Powershell, so multiline scripts and `;` are preferred instead.

#### JS Tasks

Alternatively we can use `engine = 'node'` or `engine = 'deno'` to write JavaScript in the `run` function instead:

chompfile.toml
```toml
version = 0.1

[[task]]
target = 'name.txt'
engine = 'node'
run = '''
import { writeFile } from 'fs/promises';
console.log("No name.txt, writing one.");
await writeFile(process.env.TARGET, 'World');
'''

[[task]]
name = 'hello'
target = 'hello.txt'
deps = ['name.txt']
engine = 'node'
run = '''
import { readFile, writeFile } from 'fs/promises';
const name = (await readFile(process.env.DEP, 'utf8')).trim();
await writeFile(process.env.TARGET, `Hello ${name}`);
'''
```

Tasks are run with maximum parallelism as permitted by the task graph, which can be controlled via the [`-j` flag](https://github.com/guybedford/chomp/blob/main/docs/cli.md#jobs) to limit the number of simultaneous executions.

Using the [`--watch` flag](https://github.com/guybedford/chomp/blob/main/docs/cli.md#watch) watches all dependencies and applies incremental rebuilds over invalidations only.

Or, using `chomp hello --serve` runs a [static file server](https://github.com/guybedford/chomp/blob/main/docs/task.md#static-server) with watched rebuilds.

See the [task documentation](https://github.com/guybedford/chomp/blob/main/docs/task.md) for further details.

#### Monorepos

There is no first-class monorepo support in chomp, but some simple techniques can achieve the same result.

For example, consider a monorepo where `packages/[pkgname]/chompfile.toml` defines per-package tasks.

A base-level `chompfile.toml` could run the `test` task of all the sub-packages with the following `chompfile.toml`:

```toml
[[task]]
name = 'test'
dep = 'packages/#/chompfile.toml'
run = 'chomp -c $DEP test'
```

`chomp test` will then use [task interpolation](https://github.com/guybedford/chomp/blob/main/docs/task.md#task-interpolation) to run the multiple sub-package test tasks in parallel. A similar approach can also be used for a [basic unit testing](https://github.com/guybedford/chomp/blob/main/docs/task.md#testing).

By adding [`serial = 'true'`](https://github.com/guybedford/chomp/blob/main/docs/task.md#serial-dependencies), the interpolation can be made to run in series rather than in parallel.

Cross-project dependencies are [not currently supported](https://github.com/guybedford/chomp/issues/119). Instead, if `packages/a/chompfile.toml`'s build task depends on `packages/b/chompfile.toml`'s build task to run first, then `packages/a/chompfile.toml` might look like:

```toml
[[task]]
name = 'build'
run = 'cargo build'
dep = 'build:deps'

[[task]]
name = 'build:deps'
run = 'chomp -c ../a build'
```

This would still be fast, so long as `packages/a/chompfile.toml`'s `build` task has its targets and dependencies properly configured to do zero work if the all target mtimes are greater than their dependencies.

### Extensions

Extensions are able to register task templates for use in Chompfiles.

Extensions are loaded using the `extensions` list, which can be any local or remote JS file:

```toml
version = 0.1
extensions = [
"./local.js",
"https://remote.com/extension.js"
]
```

A core extensions library is provided with useful templates for the JS ecosystem, with
the short protocol `chomp:ext`, a shorthand for the `@chompbuild/extensions` package contents.

A simple example is included below.

_See the [@chompbuild/extensions package](https://github.com/guybedford/chomp-extensions) for extension descriptions and examples._

#### Example: TypeScript with SWC

To compile TypeScript with the SWC template:

```toml
version = 0.1
extensions = ['[email protected]:swc']

[[task]]
name = 'build:typescript'
template = 'swc'
target = 'lib/##.js'
deps = ['src/##.ts']
```

In the above, all `src/**/*.ts` files will be globbed, have SWC run on them, and output into `lib/[file].js` along with their source maps.

The `##` and `#` interpolation syntax is special because, unlike glob dependencies (which are also supported), there must be a 1:1 relationship between a dependency and its target.

Only non-existent files, or files whose `src` mtimes are invalidated will be rebuilt. If SWC itself is updated, all files that depend on it will be re-built.

Specific files or patterns can be built directly by name as well, skipping all other build work:

```sh
chomp lib/main.js lib/dep.js

🞂 lib/dep.js
🞂 lib/app.js
√ lib/dep.js [317.2838ms]
√ lib/app.js [310.0831ms]
```

Patterns are also supported for building tasks by name or filename (the below two commands are equivalent):

```sh
$ chomp lib/*.js
$ chomp :build:*
```

To remove the template magic, run `chomp --eject` to convert the `chompfile.toml` into its untemplated form:

```sh
$ chomp --eject

√ chompfile.toml template tasks ejected
```

Resulting in the updated _chompfile.toml_:

```toml
version = 0.1

[[task]]
name = 'build:typescript'
target = 'lib/##.js'
dep = 'src/##.ts'
stdio = 'stderr-only'
run = 'node ./node_modules/@swc/cli/bin/swc.js $DEP -o $TARGET --no-swcrc --source-maps -C jsc.parser.syntax=typescript -C jsc.parser.importAssertions=true -C jsc.parser.topLevelAwait=true -C jsc.parser.importMeta=true -C jsc.parser.privateMethod=true -C jsc.parser.dynamicImport=true -C jsc.target=es2016 -C jsc.experimental.keepImportAssertions=true'
```

# License

Apache-2.0