Skip to content

Commit 14f5ce4

Browse files
committed
Add oxlint plugin to @fedify/lint
Expose the existing rule set through oxlint's JS plugin API at the new `@fedify/lint/oxlint` subpath export. The same rules used by the ESLint plugin are reused verbatim — oxlint claims an ESLint-compatible API, so no rule logic is duplicated. Distributed via npm only since oxlint's JS plugin loader runs only under Node.js. Includes an integration test that spawns the oxlint binary against a tmpdir fixture, plus an example project under examples/lint/oxlint/ that demonstrates the configuration with both a violating and a corrected fixture. Assisted-by: Claude Code:claude-opus-4-7
1 parent 7415710 commit 14f5ce4

13 files changed

Lines changed: 1025 additions & 24 deletions

File tree

docs/manual/lint.md

Lines changed: 121 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
---
22
description: >-
3-
Fedify provides linting plugins for Deno Lint and ESLint to help you catch
4-
common mistakes and enforce best practices when building federated server
5-
apps.
3+
Fedify provides linting plugins for Deno Lint, ESLint, and oxlint to help you
4+
catch common mistakes and enforce best practices when building federated
5+
server apps.
66
---
77

88
Linting
@@ -15,8 +15,9 @@ _This package is available since Fedify 2.0.0._
1515
> app to catch common mistakes early and enforce best practices.
1616
1717
Fedify provides the [`@fedify/lint`] package, which includes lint rules
18-
specifically designed for Fedify applications. It supports both [Deno Lint] and
19-
[ESLint], so you can use it regardless of your JavaScript/TypeScript runtime.
18+
specifically designed for Fedify applications. It supports [Deno Lint],
19+
[ESLint], and [oxlint], so you can use it regardless of your
20+
JavaScript/TypeScript runtime.
2021

2122
The plugin includes rules that check for:
2223

@@ -29,6 +30,7 @@ The plugin includes rules that check for:
2930
[`@fedify/lint`]: https://jsr.io/@fedify/lint
3031
[Deno Lint]: https://docs.deno.com/runtime/reference/lint_plugins/
3132
[ESLint]: https://eslint.org/
33+
[oxlint]: https://oxc.rs/docs/guide/usage/linter/
3234

3335

3436
Installation
@@ -262,6 +264,118 @@ bunx eslint .
262264
:::
263265

264266

267+
Oxlint
268+
------
269+
270+
[oxlint] is a fast Rust-based linter that supports ESLint-compatible JS
271+
plugins. `@fedify/lint` exposes its rules through oxlint's [JS plugin API]
272+
via the `@fedify/lint/oxlint` subpath export.
273+
274+
> [!NOTE]
275+
> oxlint's JS plugin API is currently in alpha and the loader runs only under
276+
> Node.js. The `@fedify/lint/oxlint` subpath is therefore distributed via npm
277+
> only, not JSR.
278+
279+
[JS plugin API]: https://oxc.rs/docs/guide/usage/linter/writing-js-plugins.html
280+
281+
### Basic setup
282+
283+
Add the plugin to your _.oxlintrc.json_ via the `jsPlugins` field, then enable
284+
the rules you want:
285+
286+
~~~~ json
287+
{
288+
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
289+
"jsPlugins": ["@fedify/lint/oxlint"],
290+
"rules": {
291+
"@fedify/lint/actor-id-required": "error",
292+
"@fedify/lint/actor-id-mismatch": "error"
293+
}
294+
}
295+
~~~~
296+
297+
Rule IDs are namespaced under `@fedify/lint/`, matching the ESLint preset.
298+
299+
### Custom configuration
300+
301+
Each rule accepts `"error"`, `"warn"`, or `"off"`. Enable any subset listed in
302+
the [Rules] section below:
303+
304+
~~~~ json
305+
{
306+
"jsPlugins": ["@fedify/lint/oxlint"],
307+
"rules": {
308+
"@fedify/lint/actor-id-required": "error",
309+
"@fedify/lint/actor-id-mismatch": "error",
310+
"@fedify/lint/actor-inbox-property-required": "warn",
311+
"@fedify/lint/actor-outbox-property-required": "warn",
312+
"@fedify/lint/actor-followers-property-required": "warn",
313+
"@fedify/lint/actor-public-key-required": "warn",
314+
"@fedify/lint/actor-assertion-method-required": "warn",
315+
"@fedify/lint/collection-filtering-not-implemented": "warn"
316+
}
317+
}
318+
~~~~
319+
320+
[Rules]: #rules
321+
322+
### Running oxlint
323+
324+
Add a script to _package.json_:
325+
326+
~~~~ jsonc
327+
{
328+
"scripts": {
329+
"lint": "oxlint ."
330+
}
331+
}
332+
~~~~
333+
334+
Then run the linter:
335+
336+
::: code-group
337+
338+
~~~~ sh [npm]
339+
npm run lint
340+
~~~~
341+
342+
~~~~ sh [pnpm]
343+
pnpm lint
344+
~~~~
345+
346+
~~~~ sh [Yarn]
347+
yarn lint
348+
~~~~
349+
350+
~~~~ sh [Bun]
351+
bun lint
352+
~~~~
353+
354+
:::
355+
356+
Or invoke oxlint directly:
357+
358+
::: code-group
359+
360+
~~~~ sh [npm]
361+
npx oxlint .
362+
~~~~
363+
364+
~~~~ sh [pnpm]
365+
pnpx oxlint .
366+
~~~~
367+
368+
~~~~ sh [Yarn]
369+
yarn oxlint .
370+
~~~~
371+
372+
~~~~ sh [Bun]
373+
bunx oxlint .
374+
~~~~
375+
376+
:::
377+
378+
265379
Rules
266380
-----
267381

@@ -1217,10 +1331,12 @@ See also
12171331
- [`@fedify/lint` on npm]
12181332
- [Deno Lint plugins documentation]
12191333
- [ESLint documentation]
1334+
- [oxlint documentation]
12201335
- [Example project]
12211336

12221337
[`@fedify/lint` on JSR]: https://jsr.io/@fedify/lint
12231338
[`@fedify/lint` on npm]: https://www.npmjs.com/package/@fedify/lint
12241339
[Deno Lint plugins documentation]: https://docs.deno.com/runtime/reference/lint_plugins/
12251340
[ESLint documentation]: https://eslint.org/
1341+
[oxlint documentation]: https://oxc.rs/docs/guide/usage/linter/
12261342
[Example project]: https://github.com/fedify-dev/fedify/tree/main/examples/lint
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
3+
"jsPlugins": ["@fedify/lint/oxlint"],
4+
"rules": {
5+
"@fedify/lint/actor-id-required": "error",
6+
"@fedify/lint/actor-id-mismatch": "error",
7+
"@fedify/lint/actor-inbox-property-required": "warn",
8+
"@fedify/lint/actor-outbox-property-required": "warn",
9+
"@fedify/lint/actor-followers-property-required": "warn"
10+
}
11+
}

examples/lint/oxlint/README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<!-- deno-fmt-ignore-file -->
2+
3+
@fedify/lint with oxlint
4+
========================
5+
6+
This example demonstrates how to use [`@fedify/lint`] together with [oxlint]
7+
to catch common Fedify federation mistakes.
8+
9+
[`@fedify/lint`]: https://www.npmjs.com/package/@fedify/lint
10+
[oxlint]: https://oxc.rs/docs/guide/usage/linter/
11+
12+
13+
Layout
14+
------
15+
16+
- *.oxlintrc.json* — oxlint configuration that enables `@fedify/lint`
17+
via the JS plugin API.
18+
- *federation.ts* — code that intentionally violates several rules
19+
(missing `id`, `inbox`, `outbox`, `followers`).
20+
- *federation.fixed.ts* — corrected version that passes all rules.
21+
22+
23+
Usage
24+
-----
25+
26+
Install dependencies and run the linter:
27+
28+
~~~~ sh
29+
pnpm install
30+
pnpm lint
31+
~~~~
32+
33+
You should see at least one `@fedify/lint(actor-id-required)` error on
34+
*federation.ts*. Running against *federation.fixed.ts* alone produces no
35+
diagnostics:
36+
37+
~~~~ sh
38+
pnpm lint:fixed
39+
~~~~
40+
41+
42+
How it works
43+
------------
44+
45+
The plugin is loaded via the `jsPlugins` field in *.oxlintrc.json*:
46+
47+
~~~~ json
48+
{
49+
"jsPlugins": ["@fedify/lint/oxlint"],
50+
"rules": {
51+
"@fedify/lint/actor-id-required": "error"
52+
}
53+
}
54+
~~~~
55+
56+
`@fedify/lint/oxlint` is a subpath export that exposes the same rules as the
57+
ESLint plugin in oxlint's plugin shape. Rule IDs are namespaced under
58+
`@fedify/lint/`.
59+
60+
See the [Linting] manual for the full rule reference.
61+
62+
[Linting]: https://fedify.dev/manual/lint
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import {
2+
createFederation,
3+
InProcessMessageQueue,
4+
MemoryKvStore,
5+
} from "@fedify/fedify";
6+
import { Person } from "@fedify/vocab";
7+
8+
const federation = createFederation<void>({
9+
kv: new MemoryKvStore(),
10+
queue: new InProcessMessageQueue(),
11+
});
12+
13+
federation.setActorDispatcher("/users/{identifier}", (ctx, identifier) => {
14+
return new Person({
15+
id: ctx.getActorUri(identifier),
16+
name: "Example User",
17+
inbox: ctx.getInboxUri(identifier),
18+
outbox: ctx.getOutboxUri(identifier),
19+
followers: ctx.getFollowersUri(identifier),
20+
});
21+
});

examples/lint/oxlint/federation.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import {
2+
createFederation,
3+
InProcessMessageQueue,
4+
MemoryKvStore,
5+
} from "@fedify/fedify";
6+
import { Person } from "@fedify/vocab";
7+
8+
const federation = createFederation<void>({
9+
kv: new MemoryKvStore(),
10+
queue: new InProcessMessageQueue(),
11+
});
12+
13+
federation.setActorDispatcher("/users/{identifier}", (_ctx, _identifier) => {
14+
return new Person({
15+
name: "Example User",
16+
});
17+
});

examples/lint/oxlint/package.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"name": "@fedify/example-lint-oxlint",
3+
"version": "0.0.0",
4+
"private": true,
5+
"description": "Example project demonstrating @fedify/lint with oxlint",
6+
"type": "module",
7+
"scripts": {
8+
"lint": "oxlint .",
9+
"lint:fixed": "oxlint federation.fixed.ts"
10+
},
11+
"dependencies": {
12+
"@fedify/fedify": "workspace:^",
13+
"@fedify/vocab": "workspace:^"
14+
},
15+
"devDependencies": {
16+
"@fedify/lint": "workspace:^",
17+
"oxlint": "catalog:"
18+
}
19+
}

0 commit comments

Comments
 (0)