Skip to content

Linting

An ESLint plugin enforces App-Compose conventions in TypeScript code. It requires ESLint 9+.

Use your preferred package manager.

Terminal window
npm install --save-dev --save-exact @app-compose/eslint-plugin

Import 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',
},
}

❗ 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.

// ❌ Wrong
shape(user.result, async (u) => await fetchAvatar(u.id))
// ✅ Correct
shape(fetchAvatar.result, (a) => a.url)

❗ 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"
// ❌ Wrong
compose().step(login).step(debug(login)).run()
// ✅ Correct
compose().step(login).run()

⚠️ 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.

// ❌ Wrong
createTask({
name: "alpha",
enabled: {
fn: check,
context: { authorized: authorizedTag },
},
run: {
context: { timeout: timeoutTag },
fn: init,
},
})
// ✅ Correct
createTask({
name: "alpha",
run: {
context: { timeout: timeoutTag },
fn: init,
},
enabled: {
context: { authorized: authorizedTag },
fn: check,
},
})

⚠️ 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.

// ❌ Wrong
createWire({
to: apiUrl,
from: literal("https://api.example.com"),
})
// ✅ Correct
createWire({
from: literal("https://api.example.com"),
to: apiUrl,
})