Linting
An ESLint plugin enforces App-Compose conventions in TypeScript code. It requires ESLint 9+.
Installation
Section titled “Installation”Use your preferred package manager.
npm install --save-dev --save-exact @app-compose/eslint-pluginpnpm add --save-dev --save-exact @app-compose/eslint-pluginyarn add --dev --exact @app-compose/eslint-pluginbun add --dev --exact @app-compose/eslint-pluginImport the plugin and add it to your config. Use the recommended preset for a quick start.
import appCompose from "@app-compose/eslint-plugin"import tseslint from "typescript-eslint"
export default tseslint.config(appCompose.configs.recommended)You can also configure it manually.
{ plugins: { 'app-compose': appCompose, }, rules: { 'app-compose/task-options-order': 'warn', },}no-async-shape-callback
Section titled “no-async-shape-callback”❗ This rule errors in the recommended config.
💭 This rule requires typed linting.
In a shape() callback, async work runs outside the chain, and App-Compose cannot order it. The plugin reports
every callback that returns a Promise.
// ❌ Wrongshape(user.result, async (u) => await fetchAvatar(u.id))
// ✅ Correctshape(fetchAvatar.result, (a) => a.url)no-coda-debug
Section titled “no-coda-debug”❗ This rule errors in the recommended config.
The debug() helper from @app-compose/coda prints task state to the console. The plugin reports every call to
it, so it stays out of committed code.
import { compose } from "@app-compose/core"import { debug } from "@app-compose/coda"
// ❌ Wrongcompose().step(login).step(debug(login)).run()
// ✅ Correctcompose().step(login).run()task-options-order
Section titled “task-options-order”⚠️ This rule warns in the recommended config.
🔧 This rule is automatically fixable by the --fix CLI option.
This order matches how a Task reads top-to-bottom: name first, then run before the optional enabled. Inside both,
context comes before fn. The plugin reports when the order differs.
// ❌ WrongcreateTask({ name: "alpha", enabled: { fn: check, context: { authorized: authorizedTag }, }, run: { context: { timeout: timeoutTag }, fn: init, },})
// ✅ CorrectcreateTask({ name: "alpha", run: { context: { timeout: timeoutTag }, fn: init, }, enabled: { context: { authorized: authorizedTag }, fn: check, },})wire-options-order
Section titled “wire-options-order”⚠️ This rule warns in the recommended config.
🔧 This rule is automatically fixable by the --fix CLI option.
A Wire carries data from a source to a destination tag, so from is listed before to. The plugin reports when
the order differs.
// ❌ WrongcreateWire({ to: apiUrl, from: literal("https://api.example.com"),})
// ✅ CorrectcreateWire({ from: literal("https://api.example.com"), to: apiUrl,})