diff --git a/.hongdown.toml b/.hongdown.toml index 978213c36..2203b8fcf 100644 --- a/.hongdown.toml +++ b/.hongdown.toml @@ -88,6 +88,7 @@ proper_nouns = [ "ngrok", "Object Integrity Proofs", "OpenTelemetry", + "Oxlint", "Piefed", "Pixelfed", "Pleroma", diff --git a/docs/manual/lint.md b/docs/manual/lint.md index 2772ebb98..e1a9695ef 100644 --- a/docs/manual/lint.md +++ b/docs/manual/lint.md @@ -1,8 +1,8 @@ --- description: >- - Fedify provides linting plugins for Deno Lint and ESLint to help you catch - common mistakes and enforce best practices when building federated server - apps. + Fedify provides linting plugins for Deno Lint, ESLint, and Oxlint to help you + catch common mistakes and enforce best practices when building federated + server apps. --- Linting @@ -15,8 +15,9 @@ _This package is available since Fedify 2.0.0._ > app to catch common mistakes early and enforce best practices. Fedify provides the [`@fedify/lint`] package, which includes lint rules -specifically designed for Fedify applications. It supports both [Deno Lint] and -[ESLint], so you can use it regardless of your JavaScript/TypeScript runtime. +specifically designed for Fedify applications. It supports [Deno Lint], +[ESLint], and [Oxlint], so you can use it regardless of your +JavaScript/TypeScript runtime. The plugin includes rules that check for: @@ -29,6 +30,7 @@ The plugin includes rules that check for: [`@fedify/lint`]: https://jsr.io/@fedify/lint [Deno Lint]: https://docs.deno.com/runtime/reference/lint_plugins/ [ESLint]: https://eslint.org/ +[Oxlint]: https://oxc.rs/docs/guide/usage/linter/ Installation @@ -262,6 +264,114 @@ bunx eslint . ::: +Oxlint +------ + +[Oxlint] is a fast Rust-based linter that supports ESLint-compatible JS +plugins. `@fedify/lint` exposes its rules through Oxlint's [JS plugin API] +via the `@fedify/lint/oxlint` subpath export. + +> [!NOTE] +> Oxlint's JS plugin API is currently in alpha and not subject to semver. + +[JS plugin API]: https://oxc.rs/docs/guide/usage/linter/writing-js-plugins.html + +### Basic setup + +Add the plugin to your _.oxlintrc.json_ via the `jsPlugins` field, then enable +the rules you want: + +~~~~ json +{ + "$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json", + "jsPlugins": ["@fedify/lint/oxlint"], + "rules": { + "@fedify/lint/actor-id-required": "error", + "@fedify/lint/actor-id-mismatch": "error" + } +} +~~~~ + +Rule IDs are namespaced under `@fedify/lint/`, matching the ESLint preset. + +### Custom configuration + +Each rule accepts `"error"`, `"warn"`, or `"off"`. Enable any subset listed in +the [*Rules* section](#rules) below: + +~~~~ json +{ + "jsPlugins": ["@fedify/lint/oxlint"], + "rules": { + "@fedify/lint/actor-id-required": "error", + "@fedify/lint/actor-id-mismatch": "error", + "@fedify/lint/actor-inbox-property-required": "warn", + "@fedify/lint/actor-outbox-property-required": "warn", + "@fedify/lint/actor-followers-property-required": "warn", + "@fedify/lint/actor-public-key-required": "warn", + "@fedify/lint/actor-assertion-method-required": "warn", + "@fedify/lint/collection-filtering-not-implemented": "warn" + } +} +~~~~ + +### Running Oxlint + +Add a script to _package.json_: + +~~~~ jsonc +{ + "scripts": { + "lint": "oxlint ." + } +} +~~~~ + +Then run the linter: + +::: code-group + +~~~~ sh [npm] +npm run lint +~~~~ + +~~~~ sh [pnpm] +pnpm lint +~~~~ + +~~~~ sh [Yarn] +yarn lint +~~~~ + +~~~~ sh [Bun] +bun lint +~~~~ + +::: + +Or invoke Oxlint directly: + +::: code-group + +~~~~ sh [npm] +npx oxlint . +~~~~ + +~~~~ sh [pnpm] +pnpx oxlint . +~~~~ + +~~~~ sh [Yarn] +yarn oxlint . +~~~~ + +~~~~ sh [Bun] +bunx oxlint . +~~~~ + +::: + + Rules ----- @@ -1217,10 +1327,12 @@ See also - [`@fedify/lint` on npm] - [Deno Lint plugins documentation] - [ESLint documentation] + - [Oxlint documentation] - [Example project] [`@fedify/lint` on JSR]: https://jsr.io/@fedify/lint [`@fedify/lint` on npm]: https://www.npmjs.com/package/@fedify/lint [Deno Lint plugins documentation]: https://docs.deno.com/runtime/reference/lint_plugins/ [ESLint documentation]: https://eslint.org/ +[Oxlint documentation]: https://oxc.rs/docs/guide/usage/linter/ [Example project]: https://github.com/fedify-dev/fedify/tree/main/examples/lint diff --git a/examples/lint/oxlint/.oxlintrc.json b/examples/lint/oxlint/.oxlintrc.json new file mode 100644 index 000000000..fad2f581b --- /dev/null +++ b/examples/lint/oxlint/.oxlintrc.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json", + "jsPlugins": ["@fedify/lint/oxlint"], + "rules": { + "@fedify/lint/actor-id-required": "error", + "@fedify/lint/actor-id-mismatch": "error", + "@fedify/lint/actor-inbox-property-required": "warn", + "@fedify/lint/actor-outbox-property-required": "warn", + "@fedify/lint/actor-followers-property-required": "warn" + } +} diff --git a/examples/lint/oxlint/README.md b/examples/lint/oxlint/README.md new file mode 100644 index 000000000..8d9fa73e1 --- /dev/null +++ b/examples/lint/oxlint/README.md @@ -0,0 +1,63 @@ + + +@fedify/lint with Oxlint +======================== + +This example demonstrates how to use [`@fedify/lint`] together with [Oxlint] +to catch common Fedify federation mistakes. Note that Oxlint's JS plugin +support is upstream alpha and may be unstable. + +[`@fedify/lint`]: https://www.npmjs.com/package/@fedify/lint +[Oxlint]: https://oxc.rs/docs/guide/usage/linter/ + + +Layout +------ + + - *.oxlintrc.json* — Oxlint configuration that enables `@fedify/lint` + via the JS plugin API. + - *federation.ts* — code that intentionally violates several rules + (missing `id`, `inbox`, `outbox`, `followers`). + - *federation.fixed.ts* — corrected version that passes all rules. + + +Usage +----- + +Install dependencies and run the linter: + +~~~~ sh +pnpm install +pnpm lint +~~~~ + +You should see at least one `@fedify/lint(actor-id-required)` error on +*federation.ts*. Running against *federation.fixed.ts* alone produces no +diagnostics: + +~~~~ sh +pnpm lint:fixed +~~~~ + + +How it works +------------ + +The plugin is loaded via the `jsPlugins` field in *.oxlintrc.json*: + +~~~~ json +{ + "jsPlugins": ["@fedify/lint/oxlint"], + "rules": { + "@fedify/lint/actor-id-required": "error" + } +} +~~~~ + +`@fedify/lint/oxlint` is a subpath export that exposes the same rules as the +ESLint plugin in Oxlint's plugin shape. Rule IDs are namespaced under +`@fedify/lint/`. + +See the [Linting] manual for the full rule reference. + +[Linting]: https://fedify.dev/manual/lint diff --git a/examples/lint/oxlint/federation.fixed.ts b/examples/lint/oxlint/federation.fixed.ts new file mode 100644 index 000000000..5b4a22f56 --- /dev/null +++ b/examples/lint/oxlint/federation.fixed.ts @@ -0,0 +1,21 @@ +import { + createFederation, + InProcessMessageQueue, + MemoryKvStore, +} from "@fedify/fedify"; +import { Person } from "@fedify/vocab"; + +const federation = createFederation({ + kv: new MemoryKvStore(), + queue: new InProcessMessageQueue(), +}); + +federation.setActorDispatcher("/users/{identifier}", (ctx, identifier) => { + return new Person({ + id: ctx.getActorUri(identifier), + name: "Example User", + inbox: ctx.getInboxUri(identifier), + outbox: ctx.getOutboxUri(identifier), + followers: ctx.getFollowersUri(identifier), + }); +}); diff --git a/examples/lint/oxlint/federation.ts b/examples/lint/oxlint/federation.ts new file mode 100644 index 000000000..7704040a5 --- /dev/null +++ b/examples/lint/oxlint/federation.ts @@ -0,0 +1,17 @@ +import { + createFederation, + InProcessMessageQueue, + MemoryKvStore, +} from "@fedify/fedify"; +import { Person } from "@fedify/vocab"; + +const federation = createFederation({ + kv: new MemoryKvStore(), + queue: new InProcessMessageQueue(), +}); + +federation.setActorDispatcher("/users/{identifier}", (_ctx, _identifier) => { + return new Person({ + name: "Example User", + }); +}); diff --git a/examples/lint/oxlint/package.json b/examples/lint/oxlint/package.json new file mode 100644 index 000000000..5dbef1cd8 --- /dev/null +++ b/examples/lint/oxlint/package.json @@ -0,0 +1,19 @@ +{ + "name": "@fedify/example-lint-oxlint", + "version": "0.0.0", + "private": true, + "description": "Example project demonstrating @fedify/lint with oxlint", + "type": "module", + "scripts": { + "lint": "oxlint .", + "lint:fixed": "oxlint federation.fixed.ts" + }, + "dependencies": { + "@fedify/fedify": "workspace:^", + "@fedify/vocab": "workspace:^" + }, + "devDependencies": { + "@fedify/lint": "workspace:^", + "oxlint": "catalog:" + } +} diff --git a/packages/lint/README.md b/packages/lint/README.md index 3d3979340..499611475 100644 --- a/packages/lint/README.md +++ b/packages/lint/README.md @@ -1,7 +1,7 @@ -@fedify/lint: ESLint plugin for Fedify -====================================== +@fedify/lint: Lint plugins for Fedify +===================================== [![JSR][JSR badge]][JSR] [![npm][npm badge]][npm] @@ -9,10 +9,10 @@ *This package is available since Fedify 2.0.0.* -This package provides [Deno Lint] and [ESLint] plugin with lint rules -specifically designed for [Fedify] applications. It helps you catch common -mistakes and enforce best practices when building federated server apps with -Fedify. +This package provides [Deno Lint], [ESLint], and [Oxlint] plugins with lint +rules specifically designed for [Fedify] applications. It helps you catch +common mistakes and enforce best practices when building federated server apps +with Fedify. The plugin includes rules that check for: @@ -30,6 +30,7 @@ The plugin includes rules that check for: [@fedify@hollo.social]: https://hollo.social/@fedify [Deno Lint]: https://docs.deno.com/runtime/reference/lint_plugins/ [ESLint]: https://eslint.org/ +[Oxlint]: https://oxc.rs/docs/guide/usage/linter/ [Fedify]: https://fedify.dev/ ### Deno Lint configuration example @@ -62,6 +63,21 @@ import fedifyLint from "@fedify/lint"; export default fedifyLint; ~~~~ +### Oxlint configuration example + +~~~~ jsonc +// .oxlintrc.json + +{ + "jsPlugins": ["@fedify/lint/oxlint"], + "rules": { + "@fedify/lint/actor-id-required": "error", + "@fedify/lint/actor-id-mismatch": "error", + "@fedify/lint/actor-inbox-property-required": "warn" + } +} +~~~~ + Features -------- @@ -403,6 +419,114 @@ bunx eslint . ::: +Usage (Oxlint) +-------------- + +[Oxlint] is a fast Rust-based linter that supports ESLint-compatible JS plugins. +The `@fedify/lint/oxlint` subpath export plugs the Fedify rules into Oxlint's +[JS plugin API]. + +> [!NOTE] +> Oxlint's JS plugin API is currently in alpha and not subject to semver. + +[JS plugin API]: https://oxc.rs/docs/guide/usage/linter/writing-js-plugins.html + +### Basic setup + +Add the plugin and the rules you want to enable to your *.oxlintrc.json*: + +~~~~ json +{ + "$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json", + "jsPlugins": ["@fedify/lint/oxlint"], + "rules": { + "@fedify/lint/actor-id-required": "error", + "@fedify/lint/actor-id-mismatch": "error" + } +} +~~~~ + +Rule IDs are namespaced under `@fedify/lint/`, matching the ESLint +configuration above. + +### Custom configuration + +Enable any subset of the rules listed in the *Features* section above. Each +rule can be set to `"error"`, `"warn"`, or `"off"`: + +~~~~ json +{ + "jsPlugins": ["@fedify/lint/oxlint"], + "rules": { + "@fedify/lint/actor-id-required": "error", + "@fedify/lint/actor-id-mismatch": "error", + "@fedify/lint/actor-inbox-property-required": "warn", + "@fedify/lint/actor-outbox-property-required": "warn", + "@fedify/lint/actor-followers-property-required": "warn", + "@fedify/lint/actor-public-key-required": "warn", + "@fedify/lint/actor-assertion-method-required": "warn", + "@fedify/lint/collection-filtering-not-implemented": "warn" + } +} +~~~~ + +### Running Oxlint + +Add a script to *package.json*: + +~~~~ jsonc +{ + "scripts": { + "lint": "oxlint ." + } +} +~~~~ + +Then run: + +::: code-group + +~~~~ sh [npm] +npm run lint +~~~~ + +~~~~ sh [pnpm] +pnpm lint +~~~~ + +~~~~ sh [Yarn] +yarn lint +~~~~ + +~~~~ sh [Bun] +bun lint +~~~~ + +::: + +Or invoke Oxlint directly: + +::: code-group + +~~~~ sh [npm] +npx oxlint . +~~~~ + +~~~~ sh [pnpm] +pnpx oxlint . +~~~~ + +~~~~ sh [Yarn] +yarn oxlint . +~~~~ + +~~~~ sh [Bun] +bunx oxlint . +~~~~ + +::: + + See also -------- diff --git a/packages/lint/deno.json b/packages/lint/deno.json index cf6a0fa27..3416d13f3 100644 --- a/packages/lint/deno.json +++ b/packages/lint/deno.json @@ -3,7 +3,8 @@ "version": "2.3.0", "license": "MIT", "exports": { - ".": "./src/mod.ts" + ".": "./src/mod.ts", + "./oxlint": "./src/oxlint.ts" }, "imports": { "eslint": "npm:eslint@^9.0.0", @@ -16,6 +17,6 @@ ] }, "tasks": { - "test": "deno test --allow-env" + "test": "deno test --allow-env --allow-read --allow-run --allow-sys --allow-write" } } diff --git a/packages/lint/package.json b/packages/lint/package.json index 2e3d05bdd..9a2d8077b 100644 --- a/packages/lint/package.json +++ b/packages/lint/package.json @@ -8,7 +8,9 @@ "Fediverse", "Lint", "ESLint", - "ESLint Plugin" + "ESLint Plugin", + "oxlint", + "oxc" ], "author": { "name": "Chanhaeng Lee", @@ -38,6 +40,11 @@ "import": "./dist/index.js", "require": "./dist/index.cjs" }, + "./oxlint": { + "types": "./dist/oxlint.d.ts", + "import": "./dist/oxlint.js", + "require": "./dist/oxlint.cjs" + }, "./package.json": "./package.json" }, "files": [ @@ -60,9 +67,11 @@ "@typescript-eslint/utils": "^8.0.0" }, "devDependencies": { + "@fedify/fixture": "workspace:*", "@types/eslint": "^9.0.0", "@types/estree": "^1.0.8", "eslint": "^9.0.0", + "oxlint": "catalog:", "tsdown": "catalog:", "typescript": "catalog:" }, diff --git a/packages/lint/src/oxlint.ts b/packages/lint/src/oxlint.ts new file mode 100644 index 000000000..5b4bfc212 --- /dev/null +++ b/packages/lint/src/oxlint.ts @@ -0,0 +1,130 @@ +/** + * oxlint plugin for Fedify. + * + * Exposes the same rule set as the ESLint plugin in a shape suitable for + * oxlint's JS plugin API (https://oxc.rs/docs/guide/usage/linter/writing-js-plugins.html). + * + * Configure via `.oxlintrc.json`: + * + * ```json + * { + * "jsPlugins": ["@fedify/lint/oxlint"], + * "rules": { + * "@fedify/lint/actor-id-required": "error" + * } + * } + * ``` + */ +import type { Rule } from "eslint"; +import metadataRaw from "../deno.json" with { type: "json" }; +import { RULE_IDS } from "./lib/const.ts"; +import { + eslint as actorAssertionMethodRequired, +} from "./rules/actor-assertion-method-required.ts"; +import { + eslint as actorFeaturedPropertyMismatch, +} from "./rules/actor-featured-property-mismatch.ts"; +import { + eslint as actorFeaturedPropertyRequired, +} from "./rules/actor-featured-property-required.ts"; +import { + eslint as actorFeaturedTagsPropertyMismatch, +} from "./rules/actor-featured-tags-property-mismatch.ts"; +import { + eslint as actorFeaturedTagsPropertyRequired, +} from "./rules/actor-featured-tags-property-required.ts"; +import { + eslint as actorFollowersPropertyMismatch, +} from "./rules/actor-followers-property-mismatch.ts"; +import { + eslint as actorFollowersPropertyRequired, +} from "./rules/actor-followers-property-required.ts"; +import { + eslint as actorFollowingPropertyMismatch, +} from "./rules/actor-following-property-mismatch.ts"; +import { + eslint as actorFollowingPropertyRequired, +} from "./rules/actor-following-property-required.ts"; +import { eslint as actorIdMismatch } from "./rules/actor-id-mismatch.ts"; +import { eslint as actorIdRequired } from "./rules/actor-id-required.ts"; +import { + eslint as actorInboxPropertyMismatch, +} from "./rules/actor-inbox-property-mismatch.ts"; +import { + eslint as actorInboxPropertyRequired, +} from "./rules/actor-inbox-property-required.ts"; +import { + eslint as actorLikedPropertyMismatch, +} from "./rules/actor-liked-property-mismatch.ts"; +import { + eslint as actorLikedPropertyRequired, +} from "./rules/actor-liked-property-required.ts"; +import { + eslint as actorOutboxPropertyMismatch, +} from "./rules/actor-outbox-property-mismatch.ts"; +import { + eslint as actorOutboxPropertyRequired, +} from "./rules/actor-outbox-property-required.ts"; +import { + eslint as actorPublicKeyRequired, +} from "./rules/actor-public-key-required.ts"; +import { + eslint as actorSharedInboxPropertyMismatch, +} from "./rules/actor-shared-inbox-property-mismatch.ts"; +import { + eslint as actorSharedInboxPropertyRequired, +} from "./rules/actor-shared-inbox-property-required.ts"; +import { + eslint as collectionFiltering, +} from "./rules/collection-filtering-not-implemented.ts"; +import { + eslint as outboxListenerDeliveryRequired, +} from "./rules/outbox-listener-delivery-required.ts"; + +const rules: Record< + typeof RULE_IDS[keyof typeof RULE_IDS], + Rule.RuleModule +> = { + [RULE_IDS.actorIdMismatch]: actorIdMismatch, + [RULE_IDS.actorIdRequired]: actorIdRequired, + [RULE_IDS.actorFollowingPropertyRequired]: actorFollowingPropertyRequired, + [RULE_IDS.actorFollowingPropertyMismatch]: actorFollowingPropertyMismatch, + [RULE_IDS.actorFollowersPropertyRequired]: actorFollowersPropertyRequired, + [RULE_IDS.actorFollowersPropertyMismatch]: actorFollowersPropertyMismatch, + [RULE_IDS.actorOutboxPropertyRequired]: actorOutboxPropertyRequired, + [RULE_IDS.actorOutboxPropertyMismatch]: actorOutboxPropertyMismatch, + [RULE_IDS.actorLikedPropertyRequired]: actorLikedPropertyRequired, + [RULE_IDS.actorLikedPropertyMismatch]: actorLikedPropertyMismatch, + [RULE_IDS.actorFeaturedPropertyRequired]: actorFeaturedPropertyRequired, + [RULE_IDS.actorFeaturedPropertyMismatch]: actorFeaturedPropertyMismatch, + [RULE_IDS.actorFeaturedTagsPropertyRequired]: + actorFeaturedTagsPropertyRequired, + [RULE_IDS.actorFeaturedTagsPropertyMismatch]: + actorFeaturedTagsPropertyMismatch, + [RULE_IDS.actorInboxPropertyRequired]: actorInboxPropertyRequired, + [RULE_IDS.actorInboxPropertyMismatch]: actorInboxPropertyMismatch, + [RULE_IDS.actorSharedInboxPropertyRequired]: actorSharedInboxPropertyRequired, + [RULE_IDS.actorSharedInboxPropertyMismatch]: actorSharedInboxPropertyMismatch, + [RULE_IDS.actorPublicKeyRequired]: actorPublicKeyRequired, + [RULE_IDS.actorAssertionMethodRequired]: actorAssertionMethodRequired, + [RULE_IDS.collectionFilteringNotImplemented]: collectionFiltering, + [RULE_IDS.outboxListenerDeliveryRequired]: outboxListenerDeliveryRequired, +}; + +/** + * Plugin object compatible with oxlint's JS plugin API. + */ +export interface OxlintPlugin { + meta: { name: string; version: string }; + rules: Record; +} + +export const plugin: OxlintPlugin = { + meta: { + name: metadataRaw.name, + version: metadataRaw.version, + }, + rules, +}; + +export default plugin; diff --git a/packages/lint/src/tests/oxlint.test.ts b/packages/lint/src/tests/oxlint.test.ts new file mode 100644 index 000000000..106c9b156 --- /dev/null +++ b/packages/lint/src/tests/oxlint.test.ts @@ -0,0 +1,141 @@ +/** + * Integration test for the oxlint plugin entry. + * + * Spawns the oxlint binary against a tmpdir fixture that violates + * `actor-id-required`, parses the JSON diagnostics, and asserts the + * `@fedify/lint/actor-id-required` rule fires. + * + * Runtime notes: + * + * - The test uses `node:child_process`, `node:fs`, `node:os`, + * `node:path`, and `node:process`. Under Deno these resolve via + * the Node compatibility layer, so the same source runs under both + * `pnpm test` (via `node:test`) and `deno task test` (via + * `Deno.test`) — `@fedify/fixture` dispatches the test definition + * to the appropriate runtime. + * - Other rule tests in this package use the in-process linter APIs + * (`Deno.lint.runPlugin` / ESLint's `Linter`). This one is + * different on purpose: oxlint is a Rust binary, so we spawn it as + * a subprocess against a real config file. That's the only way to + * exercise the JS plugin loader end-to-end. + * - Two preconditions are checked at module load. If either is + * missing, the test is skipped via `{ ignore }`: + * * the built loader at `/dist/oxlint.js` + * * the oxlint binary, located under `/node_modules/.bin`, + * the workspace root, or anywhere on `PATH` + */ +import { test } from "@fedify/fixture"; +import { ok } from "node:assert/strict"; +import { spawnSync } from "node:child_process"; +import { existsSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { dirname, join, resolve } from "node:path"; +import process from "node:process"; +import { fileURLToPath } from "node:url"; + +const here = dirname(fileURLToPath(import.meta.url)); +const pluginPath = resolve(here, "../../dist/oxlint.js"); +const pluginBuilt = existsSync(pluginPath); + +function findOxlint(): string | null { + const candidates = [ + resolve(here, "../../node_modules/.bin/oxlint"), + resolve(here, "../../../../node_modules/.bin/oxlint"), + ]; + for (const candidate of candidates) { + if (existsSync(candidate)) return candidate; + } + const where = spawnSync( + process.platform === "win32" ? "where" : "which", + ["oxlint"], + { encoding: "utf8" }, + ); + if (where.status === 0 && where.stdout) { + return where.stdout.trim().split(/\r?\n/)[0]; + } + return null; +} + +const oxlintBin = findOxlint(); +const ignore = !pluginBuilt || !oxlintBin; + +const BAD_CODE = + `import { createFederation, InProcessMessageQueue, MemoryKvStore } from "@fedify/fedify"; +import { Person } from "@fedify/vocab"; + +const federation = createFederation({ + kv: new MemoryKvStore(), + queue: new InProcessMessageQueue(), +}); + +federation.setActorDispatcher("/users/{identifier}", (_ctx, _identifier) => { + return new Person({ + name: "Bad Actor", + }); +}); +`; + +interface OxlintJsonDiagnostic { + code?: string; + message?: string; + severity?: string; +} + +interface OxlintJsonReport { + diagnostics?: OxlintJsonDiagnostic[]; +} + +test( + "oxlint plugin: actor-id-required fires on missing id", + { ignore }, + () => { + const dir = mkdtempSync(join(tmpdir(), "fedify-lint-oxlint-")); + try { + writeFileSync( + join(dir, ".oxlintrc.json"), + JSON.stringify({ + jsPlugins: [pluginPath], + rules: { + "@fedify/lint/actor-id-required": "error", + }, + }), + ); + writeFileSync(join(dir, "federation.ts"), BAD_CODE); + + const result = spawnSync( + oxlintBin!, + ["--format=json", "."], + { cwd: dir, encoding: "utf8" }, + ); + + ok( + result.status !== 0, + `Expected non-zero exit, got ${result.status}. stderr: ${result.stderr}`, + ); + + let payload: OxlintJsonReport; + try { + payload = JSON.parse(result.stdout) as OxlintJsonReport; + } catch (err) { + throw new Error( + `Failed to parse oxlint JSON output: ${(err as Error).message}\n` + + `stdout: ${result.stdout}\nstderr: ${result.stderr}`, + ); + } + + const codes = (payload.diagnostics ?? []).map((d) => d.code ?? ""); + const matched = codes.some((code) => + code === "@fedify/lint(actor-id-required)" || + code.includes("actor-id-required") + ); + ok( + matched, + `Expected @fedify/lint(actor-id-required) diagnostic, got: ${ + codes.join(", ") || "(none)" + }`, + ); + } finally { + rmSync(dir, { recursive: true, force: true }); + } + }, +); diff --git a/packages/lint/tsdown.config.ts b/packages/lint/tsdown.config.ts index 41c45aead..7312e7fd0 100644 --- a/packages/lint/tsdown.config.ts +++ b/packages/lint/tsdown.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from "tsdown"; export default defineConfig({ - entry: ["src/index.ts"], + entry: ["src/index.ts", "src/oxlint.ts"], dts: { compilerOptions: { isolatedDeclarations: true, declaration: true } }, format: ["esm", "cjs"], platform: "node", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9b27433ed..d97f0a209 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -162,6 +162,9 @@ catalogs: nuxt: specifier: ^4.4.2 version: 4.4.2 + oxlint: + specifier: ^1.63.0 + version: 1.63.0 pkijs: specifier: ^3.3.3 version: 3.3.3 @@ -538,6 +541,22 @@ importers: specifier: ^5.5.4 version: 5.9.2 + examples/lint/oxlint: + dependencies: + '@fedify/fedify': + specifier: workspace:^ + version: link:../../../packages/fedify + '@fedify/vocab': + specifier: workspace:^ + version: link:../../../packages/vocab + devDependencies: + '@fedify/lint': + specifier: workspace:^ + version: link:../../../packages/lint + oxlint: + specifier: 'catalog:' + version: 1.63.0 + examples/next-integration: dependencies: '@fedify/fedify': @@ -707,7 +726,7 @@ importers: version: 1.15.11 nuxt: specifier: 'catalog:' - version: 4.4.2(dec37d7bbeba9bfb73f2911245d6c6e7) + version: 4.4.2(c713252a3ff52833e93512c626073ec7) devDependencies: '@types/node': specifier: 'catalog:' @@ -1375,6 +1394,9 @@ importers: specifier: ^8.0.0 version: 8.41.0(eslint@9.32.0(jiti@2.6.1))(typescript@5.9.3) devDependencies: + '@fedify/fixture': + specifier: workspace:* + version: link:../fixture '@types/eslint': specifier: ^9.0.0 version: 9.6.1 @@ -1384,6 +1406,9 @@ importers: eslint: specifier: ^9.0.0 version: 9.32.0(jiti@2.6.1) + oxlint: + specifier: 'catalog:' + version: 1.63.0 tsdown: specifier: 'catalog:' version: 0.21.6(typescript@5.9.3) @@ -1479,7 +1504,7 @@ importers: version: 1.15.11 nuxt: specifier: 'catalog:' - version: 4.4.2(dec37d7bbeba9bfb73f2911245d6c6e7) + version: 4.4.2(c713252a3ff52833e93512c626073ec7) devDependencies: '@fedify/fixture': specifier: workspace:^ @@ -4898,6 +4923,120 @@ packages: cpu: [x64] os: [win32] + '@oxlint/binding-android-arm-eabi@1.63.0': + resolution: {integrity: sha512-A9xLtQt7i0OA1PoB/meog6kikXI9CdwEp7ZwQqmgnpKn3G3b1orvTDy8CQ6T7w1HvDrgWGB78PkFKcWgibcTCg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxlint/binding-android-arm64@1.63.0': + resolution: {integrity: sha512-SQo+ZMvdR9l3CxZp5W5gFNxSiDxclY6lOzzNpKYLF8asESpm3Pwumx0gER5T7aHLF1/2BAAtLD3DiDkdgy4V1A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxlint/binding-darwin-arm64@1.63.0': + resolution: {integrity: sha512-6W82XjJDTmMnjg30427l0dufpnyLoq7wEukKdM6/g2VIybRVuQiBVh43EA4b+UxZ3+tLcKm+Or/pXGNgLCEU8g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxlint/binding-darwin-x64@1.63.0': + resolution: {integrity: sha512-CnWd/YCuVG5W1BYkjJEVbJG11o526O9qAwBEQM+nh8K19CRFUkFdROXCyYkGmroHEYQe4vgQ6+lh3550Lp35Xw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxlint/binding-freebsd-x64@1.63.0': + resolution: {integrity: sha512-a4eZAqrmtajqcxfdAzC+l7g3PaE3V8hpAYqqeD3fTxLXOMFdK3eNTZrU80n4dDEVm0JXy1aL5PqvqWldBl6zYA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxlint/binding-linux-arm-gnueabihf@1.63.0': + resolution: {integrity: sha512-tYUtU9TdbU3uXF5D62g5zXJ13iniFGhXQx5vp9cyEjGdbSAY3VdFBSaldYvyoDmgMZ0ZYuwQP1Y4t2Fhejwa0w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm-musleabihf@1.63.0': + resolution: {integrity: sha512-I5r3twFf776UZg9dmRo2xbrKt00tTkORXEVe0ctg4vdTkQvJAjiCHxnbAU2HL1AiJ9cqADA76MAliuilsAWnvg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm64-gnu@1.63.0': + resolution: {integrity: sha512-t7ltUkg6FFh4b564QyGir8xIj/QZbXu8FlcRkcyW9+ztr/mfRHlvUOFd95pJCXi9s/L5DrUeWWgpXRS+V+6igQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxlint/binding-linux-arm64-musl@1.63.0': + resolution: {integrity: sha512-Q5mmZy/XWjuYFUuQyYjOvZ5U/JkKEwnpir6hGxhh6HcdP0V/BKxLo8dqkfF/t7r7AguB17dfS/8+go5AQDRR6g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxlint/binding-linux-ppc64-gnu@1.63.0': + resolution: {integrity: sha512-uBGtuZ0TzLB4x5wVa82HGNvYqY8buwDhyCnCP0R0gkk9szqVsP0MeTtD5HX7EsEuFIt+aYmYxuxeVxs3nTSwtQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + + '@oxlint/binding-linux-riscv64-gnu@1.63.0': + resolution: {integrity: sha512-h4s6FwxE+9MeA181o0dnDwHP32Y/bG8EiB/vrD6Ib+AMt6haigDc/0bUtI/sLmQDBMJnUfaCmtSSrEAqjtEVrA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxlint/binding-linux-riscv64-musl@1.63.0': + resolution: {integrity: sha512-2EaNcCBR8Mcjl5ARtuN3BdEpVkX7KpjSjMGZ/mJMIeaXgTtdz5ytg2VwygMSStA/k0ixfvZFoZOfjDEcouV5vQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxlint/binding-linux-s390x-gnu@1.63.0': + resolution: {integrity: sha512-p4hlf/fd7TrYYl3QrWWD0GocqJefwMu3cHQhmi2FvEB/YOvFb5DZN3SMBaPi7B1TM5DeypkEtrVib674q1KKPg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@oxlint/binding-linux-x64-gnu@1.63.0': + resolution: {integrity: sha512-Vgq9rkRVcPcjbcH+ihYTfpeR7vCXfqpd+z5ItTGc0yYUV59L5ceHYN1iV4H9bKGV7Rn5hkVc7x3mSvHegduENA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxlint/binding-linux-x64-musl@1.63.0': + resolution: {integrity: sha512-3/Lkq/ncooA61rorrC+ZQed1Bc4VpGj+WnGsp58zmxKgvZ2vhreu+dcVyr3mX8NUpq7mfZ4gDDTou/yrF1Pd7A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxlint/binding-openharmony-arm64@1.63.0': + resolution: {integrity: sha512-0/EdD/6hDkx5Mfd769PTjvEM8mZ/6Dfukp1dBCL/2PjlIVGEtYdNZyok6ChqYPsT9JcFnlQnUeQzO0/1L/oC9w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxlint/binding-win32-arm64-msvc@1.63.0': + resolution: {integrity: sha512-wb0CUkN8ngwPiRQBjD1Cj0LsHeNvm+Xt6YBHDMtj2DVQVD6Oj8Ri7g6BD+KICf6LaBqZlmzOvy6nF9E/8yyGOg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxlint/binding-win32-ia32-msvc@1.63.0': + resolution: {integrity: sha512-BX5iq+ovdNlVYhSn5qPMUIT0uwAwt2lmEnCnzK+Gkhw4DovIvhGb96OFhV8yzQNUnQxn/xGkOR+X+BLrLDNm8w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxlint/binding-win32-x64-msvc@1.63.0': + resolution: {integrity: sha512-QeN/WELOfsXMeYwxvfgQrl6CbVftYUCZsGXHjXQd5Trccm8+i4gmtxaOui4xbJQaiDlviF8F3yLSBloQUeFsfA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@parcel/watcher-android-arm64@2.5.6': resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==} engines: {node: '>= 10.0.0'} @@ -10376,6 +10515,16 @@ packages: peerDependencies: oxc-parser: '>=0.98.0' + oxlint@1.63.0: + resolution: {integrity: sha512-9TGXetdjgIHOJ9OiReomP7nnrMkV9HxC1xM2ramJSLQpzxjsAJtQwa4wqkJN2f/uCrqZuJseFuSlWDdvcruveg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + oxlint-tsgolint: '>=0.22.1' + peerDependenciesMeta: + oxlint-tsgolint: + optional: true + p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -15268,7 +15417,7 @@ snapshots: transitivePeerDependencies: - magicast - '@nuxt/nitro-server@4.4.2(@babel/core@7.29.0)(better-sqlite3@12.9.0)(db0@0.3.4(better-sqlite3@12.9.0)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20251221.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.6.1)(better-sqlite3@12.9.0)(bun-types@1.3.3)(mysql2@3.22.2(@types/node@22.19.1))(postgres@3.4.7))(mysql2@3.22.2(@types/node@22.19.1)))(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20251221.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.6.1)(better-sqlite3@12.9.0)(bun-types@1.3.3)(mysql2@3.22.2(@types/node@22.19.1))(postgres@3.4.7))(ioredis@5.10.1)(magicast@0.5.2)(mysql2@3.22.2(@types/node@22.19.1))(nuxt@4.4.2(dec37d7bbeba9bfb73f2911245d6c6e7))(rolldown@1.0.0-rc.12)(typescript@5.9.3)': + '@nuxt/nitro-server@4.4.2(@babel/core@7.29.0)(better-sqlite3@12.9.0)(db0@0.3.4(better-sqlite3@12.9.0)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20251221.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.6.1)(better-sqlite3@12.9.0)(bun-types@1.3.3)(mysql2@3.22.2(@types/node@22.19.1))(postgres@3.4.7))(mysql2@3.22.2(@types/node@22.19.1)))(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20251221.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.6.1)(better-sqlite3@12.9.0)(bun-types@1.3.3)(mysql2@3.22.2(@types/node@22.19.1))(postgres@3.4.7))(ioredis@5.10.1)(magicast@0.5.2)(mysql2@3.22.2(@types/node@22.19.1))(nuxt@4.4.2(c713252a3ff52833e93512c626073ec7))(rolldown@1.0.0-rc.12)(typescript@5.9.3)': dependencies: '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) '@nuxt/devalue': 2.0.2 @@ -15287,7 +15436,7 @@ snapshots: klona: 2.0.6 mocked-exports: 0.1.1 nitropack: 2.13.3(better-sqlite3@12.9.0)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20251221.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.6.1)(better-sqlite3@12.9.0)(bun-types@1.3.3)(mysql2@3.22.2(@types/node@22.19.1))(postgres@3.4.7))(mysql2@3.22.2(@types/node@22.19.1))(rolldown@1.0.0-rc.12) - nuxt: 4.4.2(dec37d7bbeba9bfb73f2911245d6c6e7) + nuxt: 4.4.2(c713252a3ff52833e93512c626073ec7) nypm: 0.6.5 ohash: 2.0.11 pathe: 2.0.3 @@ -15354,7 +15503,7 @@ snapshots: rc9: 3.0.1 std-env: 4.1.0 - '@nuxt/vite-builder@4.4.2(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@types/node@22.19.1)(lightningcss@1.30.1)(magicast@0.5.2)(nuxt@4.4.2(dec37d7bbeba9bfb73f2911245d6c6e7))(optionator@0.9.4)(rolldown@1.0.0-rc.12)(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.12)(rollup@4.60.2))(rollup@4.60.2)(terser@5.46.1)(tsx@4.20.3)(typescript@5.9.3)(vue@3.5.33(typescript@5.9.3))(yaml@2.8.3)': + '@nuxt/vite-builder@4.4.2(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@types/node@22.19.1)(lightningcss@1.30.1)(magicast@0.5.2)(nuxt@4.4.2(c713252a3ff52833e93512c626073ec7))(optionator@0.9.4)(oxlint@1.63.0)(rolldown@1.0.0-rc.12)(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.12)(rollup@4.60.2))(rollup@4.60.2)(terser@5.46.1)(tsx@4.20.3)(typescript@5.9.3)(vue@3.5.33(typescript@5.9.3))(yaml@2.8.3)': dependencies: '@nuxt/kit': 4.4.2(magicast@0.5.2) '@rollup/plugin-replace': 6.0.3(rollup@4.60.2) @@ -15372,7 +15521,7 @@ snapshots: magic-string: 0.30.21 mlly: 1.8.2 mocked-exports: 0.1.1 - nuxt: 4.4.2(dec37d7bbeba9bfb73f2911245d6c6e7) + nuxt: 4.4.2(c713252a3ff52833e93512c626073ec7) nypm: 0.6.5 pathe: 2.0.3 pkg-types: 2.3.0 @@ -15383,7 +15532,7 @@ snapshots: unenv: 2.0.0-rc.24 vite: 7.3.2(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.46.1)(tsx@4.20.3)(yaml@2.8.3) vite-node: 5.3.0(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.46.1)(tsx@4.20.3)(yaml@2.8.3) - vite-plugin-checker: 0.12.0(optionator@0.9.4)(typescript@5.9.3)(vite@7.3.2(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.46.1)(tsx@4.20.3)(yaml@2.8.3)) + vite-plugin-checker: 0.12.0(optionator@0.9.4)(oxlint@1.63.0)(typescript@5.9.3)(vite@7.3.2(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.46.1)(tsx@4.20.3)(yaml@2.8.3)) vue: 3.5.33(typescript@5.9.3) vue-bundle-renderer: 2.2.0 optionalDependencies: @@ -16144,6 +16293,63 @@ snapshots: '@oxc-transform/binding-win32-x64-msvc@0.117.0': optional: true + '@oxlint/binding-android-arm-eabi@1.63.0': + optional: true + + '@oxlint/binding-android-arm64@1.63.0': + optional: true + + '@oxlint/binding-darwin-arm64@1.63.0': + optional: true + + '@oxlint/binding-darwin-x64@1.63.0': + optional: true + + '@oxlint/binding-freebsd-x64@1.63.0': + optional: true + + '@oxlint/binding-linux-arm-gnueabihf@1.63.0': + optional: true + + '@oxlint/binding-linux-arm-musleabihf@1.63.0': + optional: true + + '@oxlint/binding-linux-arm64-gnu@1.63.0': + optional: true + + '@oxlint/binding-linux-arm64-musl@1.63.0': + optional: true + + '@oxlint/binding-linux-ppc64-gnu@1.63.0': + optional: true + + '@oxlint/binding-linux-riscv64-gnu@1.63.0': + optional: true + + '@oxlint/binding-linux-riscv64-musl@1.63.0': + optional: true + + '@oxlint/binding-linux-s390x-gnu@1.63.0': + optional: true + + '@oxlint/binding-linux-x64-gnu@1.63.0': + optional: true + + '@oxlint/binding-linux-x64-musl@1.63.0': + optional: true + + '@oxlint/binding-openharmony-arm64@1.63.0': + optional: true + + '@oxlint/binding-win32-arm64-msvc@1.63.0': + optional: true + + '@oxlint/binding-win32-ia32-msvc@1.63.0': + optional: true + + '@oxlint/binding-win32-x64-msvc@1.63.0': + optional: true + '@parcel/watcher-android-arm64@2.5.6': optional: true @@ -22849,16 +23055,16 @@ snapshots: dependencies: boolbase: 1.0.0 - nuxt@4.4.2(dec37d7bbeba9bfb73f2911245d6c6e7): + nuxt@4.4.2(c713252a3ff52833e93512c626073ec7): dependencies: '@dxup/nuxt': 0.4.1(magicast@0.5.2)(typescript@5.9.3) '@nuxt/cli': 3.34.0(@nuxt/schema@4.4.2)(cac@6.7.14)(magicast@0.5.2) '@nuxt/devtools': 3.2.4(vite@7.3.2(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.46.1)(tsx@4.20.3)(yaml@2.8.3))(vue@3.5.33(typescript@5.9.3)) '@nuxt/kit': 4.4.2(magicast@0.5.2) - '@nuxt/nitro-server': 4.4.2(@babel/core@7.29.0)(better-sqlite3@12.9.0)(db0@0.3.4(better-sqlite3@12.9.0)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20251221.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.6.1)(better-sqlite3@12.9.0)(bun-types@1.3.3)(mysql2@3.22.2(@types/node@22.19.1))(postgres@3.4.7))(mysql2@3.22.2(@types/node@22.19.1)))(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20251221.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.6.1)(better-sqlite3@12.9.0)(bun-types@1.3.3)(mysql2@3.22.2(@types/node@22.19.1))(postgres@3.4.7))(ioredis@5.10.1)(magicast@0.5.2)(mysql2@3.22.2(@types/node@22.19.1))(nuxt@4.4.2(dec37d7bbeba9bfb73f2911245d6c6e7))(rolldown@1.0.0-rc.12)(typescript@5.9.3) + '@nuxt/nitro-server': 4.4.2(@babel/core@7.29.0)(better-sqlite3@12.9.0)(db0@0.3.4(better-sqlite3@12.9.0)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20251221.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.6.1)(better-sqlite3@12.9.0)(bun-types@1.3.3)(mysql2@3.22.2(@types/node@22.19.1))(postgres@3.4.7))(mysql2@3.22.2(@types/node@22.19.1)))(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20251221.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.6.1)(better-sqlite3@12.9.0)(bun-types@1.3.3)(mysql2@3.22.2(@types/node@22.19.1))(postgres@3.4.7))(ioredis@5.10.1)(magicast@0.5.2)(mysql2@3.22.2(@types/node@22.19.1))(nuxt@4.4.2(c713252a3ff52833e93512c626073ec7))(rolldown@1.0.0-rc.12)(typescript@5.9.3) '@nuxt/schema': 4.4.2 '@nuxt/telemetry': 2.8.0(@nuxt/kit@4.4.2(magicast@0.5.2)) - '@nuxt/vite-builder': 4.4.2(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@types/node@22.19.1)(lightningcss@1.30.1)(magicast@0.5.2)(nuxt@4.4.2(dec37d7bbeba9bfb73f2911245d6c6e7))(optionator@0.9.4)(rolldown@1.0.0-rc.12)(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.12)(rollup@4.60.2))(rollup@4.60.2)(terser@5.46.1)(tsx@4.20.3)(typescript@5.9.3)(vue@3.5.33(typescript@5.9.3))(yaml@2.8.3) + '@nuxt/vite-builder': 4.4.2(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@types/node@22.19.1)(lightningcss@1.30.1)(magicast@0.5.2)(nuxt@4.4.2(c713252a3ff52833e93512c626073ec7))(optionator@0.9.4)(oxlint@1.63.0)(rolldown@1.0.0-rc.12)(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.12)(rollup@4.60.2))(rollup@4.60.2)(terser@5.46.1)(tsx@4.20.3)(typescript@5.9.3)(vue@3.5.33(typescript@5.9.3))(yaml@2.8.3) '@unhead/vue': 2.1.13(vue@3.5.33(typescript@5.9.3)) '@vue/shared': 3.5.33 c12: 3.3.4(magicast@0.5.2) @@ -23207,6 +23413,28 @@ snapshots: magic-regexp: 0.10.0 oxc-parser: 0.117.0 + oxlint@1.63.0: + optionalDependencies: + '@oxlint/binding-android-arm-eabi': 1.63.0 + '@oxlint/binding-android-arm64': 1.63.0 + '@oxlint/binding-darwin-arm64': 1.63.0 + '@oxlint/binding-darwin-x64': 1.63.0 + '@oxlint/binding-freebsd-x64': 1.63.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.63.0 + '@oxlint/binding-linux-arm-musleabihf': 1.63.0 + '@oxlint/binding-linux-arm64-gnu': 1.63.0 + '@oxlint/binding-linux-arm64-musl': 1.63.0 + '@oxlint/binding-linux-ppc64-gnu': 1.63.0 + '@oxlint/binding-linux-riscv64-gnu': 1.63.0 + '@oxlint/binding-linux-riscv64-musl': 1.63.0 + '@oxlint/binding-linux-s390x-gnu': 1.63.0 + '@oxlint/binding-linux-x64-gnu': 1.63.0 + '@oxlint/binding-linux-x64-musl': 1.63.0 + '@oxlint/binding-openharmony-arm64': 1.63.0 + '@oxlint/binding-win32-arm64-msvc': 1.63.0 + '@oxlint/binding-win32-ia32-msvc': 1.63.0 + '@oxlint/binding-win32-x64-msvc': 1.63.0 + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -25730,7 +25958,7 @@ snapshots: - tsx - yaml - vite-plugin-checker@0.12.0(optionator@0.9.4)(typescript@5.9.3)(vite@7.3.2(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.46.1)(tsx@4.20.3)(yaml@2.8.3)): + vite-plugin-checker@0.12.0(optionator@0.9.4)(oxlint@1.63.0)(typescript@5.9.3)(vite@7.3.2(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.46.1)(tsx@4.20.3)(yaml@2.8.3)): dependencies: '@babel/code-frame': 7.29.0 chokidar: 4.0.3 @@ -25743,6 +25971,7 @@ snapshots: vscode-uri: 3.1.0 optionalDependencies: optionator: 0.9.4 + oxlint: 1.63.0 typescript: 5.9.3 vite-plugin-inspect@11.3.3(@nuxt/kit@4.4.2(magicast@0.5.2))(vite@7.3.2(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.46.1)(tsx@4.20.3)(yaml@2.8.3)): diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 394778464..0a7ec662a 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -36,6 +36,7 @@ packages: - examples/elysia - examples/express - examples/koa +- examples/lint/oxlint - examples/next-integration - examples/fastify - examples/next14-app-router @@ -98,6 +99,7 @@ catalog: pkijs: ^3.3.3 mysql2: ^3.18.0 nuxt: ^4.4.2 + oxlint: ^1.63.0 postgres: ^3.4.7 tsdown: ^0.21.6 typescript: ^5.9.2