char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
7f4058a
main
avril.conf
avril.conf is written in "friendly JSON", with an implicit top-level object, // comments, and trailing + implicit commas.
name: "@app/demo",
version: "0.1.0", // optional
// dependencies on avril projects
deps: [
"jsr:@me/my-library@^1.0.0", // depend via jsr
"https://esm.example/my-library/1.0.0/", // depend via https
"./lib/my-cool-library", // depend on relative projects
// avoid name conflicts via explicit `name`:
{ from "../other/my-cool-library", name: "other/my-cool-library" },
],
// miscellaneous import map augmentations
imports: {
helper: './my-helper.ts'
}
Unknown fields are ignored, including in task and dependency objects, so other tools can store their own configuration alongside avril's. Recognized fields are still validated, and avril add preserves unrelated fields.
packages
- package metadata strings:
name,version,description exports: controls how importing from this project works- can be a plain string (e.g.
exports: "mod.ts") for a single . export - otherwise, can be a map of subpaths to files
- can be a plain string (e.g.
deps: a list of package dependenciesimports: map of bare specifiers to real specifiers (a la<script type="importmap">)scopes: it's likeimportsbut you can scope it to- ex.:
scopes: { './tests/': { assert: './test-assert.ts' } }mapsimport assert from "assert"only inside thetestsdirectory
- ex.:
compilerOptions: package compiler-option defaults — the JSX options (jsx,jsxImportSource,jsxFactory,jsxFragmentFactory) drive the runtime transform, and every key steers type checking; the nearesttsconfig.jsonoverrides these defaults per module
tasks
tasks maps names to commands to run with avril task, a la deno task:
tasks: {
build: "avril run build.ts",
serve: {
description: "Build, then run the server",
command: "avril run server.ts",
dependencies: ["build"],
},
all: { dependencies: ["build", "serve"] },
}
- tasks run with the cwd to the package root (
avril.conf's parent dir) - task commands run via the system shell (
sh -corcmd /Con Windows) when provided as a string - task commands run as direct invocations when provided as an array of strings
- dependencies run sequentially in listed order. a failure will halt execution
- a task with
parallel: trueruns its dependencies all at once instead; if any of them fail, the task fails with status 1 (the individual exit codes are not meaningful once failures race) - a task can set
envFile(e.g.envFile: ".env") to control environment defaults for itself and all its (recursive) dependencies- this affects
- a task with
envFile(e.g.build: {command: "...", envFile: ".env"}) loads a dotenv file, relative to the package root, as environment defaults for itself & recursively for all of its dependencies- a dependency's own
envFileoverrides values it inherited; the process environment overrides every file (same syntax & precedence as--env-file) - for a cached task, env-file values only enter its cache key for names listed in its
env
- a dependency's own
watching tasks
avril task --watch <name> runs a task, then reruns it whenever its inputs change:
- the task, or at least one of its (transitive) dependencies, must declare
files; without any input globs there is nothing to watch, and--watchis an error - changes are debounced over a short quiet period (50ms), so a burst of saves produces one rerun; a change whose size and modification time match the last run does not rerun anything, so a touch reruns while rewriting a file with identical size and mtime is missed
avril.confand anyenvFilein the task graph are watched too: editing either reloads it and reruns the task, even if the change is outside the task's cache key- if the task is still running when its inputs change, it is restarted: its process group is sent SIGINT, then SIGTERM after a grace period, then SIGKILL (on Windows the task's job is terminated instead)
- a task that fails does not end watch mode; it reruns on the next change. ctrl-c stops the watcher, terminating any running task the same way
task skipping / output caching
a task can opt into incrementality by declaring its input files / env vars & output files. useful for e.g. an ad-hoc incremental static site generator:
tasks: {
"build:pages": {
command: "avril run site/render-pages.ts",
files: ["content/**", "templates/"],
output: ["dist/**/*.html"],
},
"build:assets": {
command: "avril run site/bundle-assets.ts",
files: ["assets/**"],
output: ["dist/assets/"],
},
"build:feed": {
command: "avril run site/render-feed.ts",
files: ["content/posts/**", "site.json"],
output: ["dist/feed.xml", "dist/sitemap.xml"],
env: ["SITE_URL"],
},
build: { dependencies: ["build:pages", "build:assets", "build:feed"], parallel: true },
}
files: input glob patterns, relative to the package root - if any file has changed, the task can't be skipped- a
filespattern matching a directory covers everything underneath (recursively) - a pattern that matches no files is an error
- a
env: input environment variable names - if any var has changed, the task can't be skippedoutput: output files - only their sizes and modification times are kept in the task cache. a task can only be skipped while its outputs are still present & unchanged; missing or changed outputs will rerun it- you may omit
output, in which case skipping is based on the inputs alone
- you may omit
1# avril.conf 2 3`avril.conf` is written in "friendly JSON", with an implicit top-level object, `//` comments, and trailing + implicit commas. 4 5``` 6name: "@app/demo", 7version: "0.1.0", // optional 8 9// dependencies on avril projects 10deps: [ 11"jsr:@me/my-library@^1.0.0", // depend via jsr 12"https://esm.example/my-library/1.0.0/", // depend via https 13"./lib/my-cool-library", // depend on relative projects 14// avoid name conflicts via explicit `name`: 15{ from "../other/my-cool-library", name: "other/my-cool-library" }, 16], 17 18// miscellaneous import map augmentations 19imports: { 20helper: './my-helper.ts' 21} 22``` 23 24Unknown fields are ignored, including in task and dependency objects, so other tools can store their own configuration alongside avril's. Recognized fields are still validated, and `avril add` preserves unrelated fields. 25 26## packages 27 28- package metadata strings: `name`, `version`, `description` 29- `exports`: controls how importing from this project works 30- can be a plain string (e.g. `exports: "mod.ts"`) for a single . export 31- otherwise, can be a map of subpaths to files 32- `deps`: a list of package dependencies 33- `imports`: map of bare specifiers to real specifiers (a la `<script type="importmap">`) 34- `scopes`: it's like `imports` but you can scope it to 35- ex.: `scopes: { './tests/': { assert: './test-assert.ts' } }` maps `import assert from "assert"` only inside the `tests` directory 36- `compilerOptions`: package compiler-option defaults — the [JSX options](jsx.md) (`jsx`, `jsxImportSource`, `jsxFactory`, `jsxFragmentFactory`) drive the runtime transform, and every key steers type checking; the nearest `tsconfig.json` overrides these defaults per module 37## tasks 38 39`tasks` maps names to commands to run with `avril task`, a la `deno task`: 40 41``` 42tasks: { 43build: "avril run build.ts", 44serve: { 45description: "Build, then run the server", 46command: "avril run server.ts", 47dependencies: ["build"], 48}, 49all: { dependencies: ["build", "serve"] }, 50} 51``` 52 53- tasks run with the cwd to the package root (`avril.conf`'s parent dir) 54- task commands run via the system shell (`sh -c` or `cmd /C` on Windows) when provided as a string 55- task commands run as direct invocations when provided as an array of strings 56- dependencies run sequentially in listed order. a failure will halt execution 57- a task with `parallel: true` runs its dependencies all at once instead; if any of them fail, the task fails with status 1 (the individual exit codes are not meaningful once failures race) 58- a task can set `envFile` (e.g. `envFile: ".env"`) to control environment defaults for itself and all its (recursive) dependencies 59- this affects 60- a task with `envFile` (e.g. `build: {command: "...", envFile: ".env"}`) loads a dotenv file, relative to the package root, as environment defaults for itself & recursively for all of its dependencies 61- a dependency's own `envFile` overrides values it inherited; the process environment overrides every file (same syntax & precedence as `--env-file`) 62- for a cached task, env-file values only enter its cache key for names listed in its `env` 63 64### watching tasks 65 66`avril task --watch <name>` runs a task, then reruns it whenever its inputs change: 67 68- the task, or at least one of its (transitive) dependencies, must declare `files`; without any input globs there is nothing to watch, and `--watch` is an error 69- changes are debounced over a short quiet period (50ms), so a burst of saves produces one rerun; a change whose size and modification time match the last run does not rerun anything, so a touch reruns while rewriting a file with identical size and mtime is missed 70- `avril.conf` and any `envFile` in the task graph are watched too: editing either reloads it and reruns the task, even if the change is outside the task's cache key 71- if the task is still running when its inputs change, it is restarted: its process group is sent SIGINT, then SIGTERM after a grace period, then SIGKILL (on Windows the task's job is terminated instead) 72- a task that fails does not end watch mode; it reruns on the next change. ctrl-c stops the watcher, terminating any running task the same way 73 74### task skipping / output caching 75 76a task can opt into incrementality by declaring its input files / env vars & output files. useful for e.g. an ad-hoc incremental static site generator: 77 78``` 79tasks: { 80"build:pages": { 81command: "avril run site/render-pages.ts", 82files: ["content/**", "templates/"], 83output: ["dist/**/*.html"], 84}, 85"build:assets": { 86command: "avril run site/bundle-assets.ts", 87files: ["assets/**"], 88output: ["dist/assets/"], 89}, 90"build:feed": { 91command: "avril run site/render-feed.ts", 92files: ["content/posts/**", "site.json"], 93output: ["dist/feed.xml", "dist/sitemap.xml"], 94env: ["SITE_URL"], 95}, 96build: { dependencies: ["build:pages", "build:assets", "build:feed"], parallel: true }, 97} 98``` 99 100- `files`: input glob patterns, relative to the package root - if any file has changed, the task can't be skipped 101- a `files` pattern matching a directory covers everything underneath (recursively) 102- a pattern that matches no files is an error 103- `env`: input environment variable names - if any var has changed, the task can't be skipped 104- `output`: output files - only their sizes and modification times are kept in the task cache. a task can only be skipped while its outputs are still present & unchanged; missing or changed outputs will rerun it 105- you may omit `output`, in which case skipping is based on the inputs alone