oxlint-plugin-arch 0.2.5 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +241 -102
  3. package/dist/index.js +2214 -269
  4. package/package.json +4 -6
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.1
4
+
5
+ - Fix `key-value-same-line` reporting `key: (` values whose parenthesized expression starts on the next line.
6
+
7
+ ## 0.3.0
8
+
9
+ - Add `no-promise-all-mutation`, `no-literal-in`, and `test-title-pattern` to replace hand-written `no-restricted-syntax` selectors with scope-aware, configurable rules.
10
+ - Add `no-module-mutable-state`, `no-restricted-constructor`, and `no-comments` (directives, bundler annotations, and `Why:` comments kept; one-pass autofix that skips JSX comments and directive-adjacent lines).
11
+ - Add layout rules `padding-between-statements`, `object-multiline`, `key-value-same-line`, `chain-newline`, `call-array-multiline`, and `jsx-attributes-multiline`. Fixes only change whitespace and are skipped when comments or unusual syntax make them unsafe.
12
+ - Extend `no-extra-exports` with regex `patterns`, `no-restricted-token` with multiple `restrictions` and `member` matching, and `require-paired-call` with `pairs`.
13
+ - Extend `require-object-params` with `maxParams`, `allDeclarations`, and `allowPattern`; `only-export-components` with `allowTypeExports` and `denyTypePattern`; and `no-extra-factory-keys` with `requireKeys` and `keySets`.
14
+ - Extend `no-runtime-in-types` with opt-in `runtimeImports`, `allowImportSources`, and `banReExports`; `no-trivial-functions` with `allowCallees` and `allowAsync`; and `no-inline-types` with `minMembers`.
15
+ - All option changes are backward compatible; existing configs keep today's behavior.
16
+ - Speed up `declaration-name`, `no-type-declarations`, `no-inline-types`, and `no-promise-all-mutation` by using native visitors and per-file option setup instead of whole-AST JavaScript walks.
17
+ - Add `examples/monorepo.oxlint.config.ts`, a strict copy-ready config for `apps/server` + `apps/web` monorepos.
18
+ - Remove the `install-oxlint-arch` agent skill and vendored copy; install the npm package instead.
19
+
3
20
  ## 0.2.5
4
21
 
5
22
  - Add `no-inline-types` to require named object and function types in function signatures, with separate parameter, return, and function-type options.
package/README.md CHANGED
@@ -1,16 +1,26 @@
1
- # oxlint-plugin-arch
1
+ # 🏛️ oxlint-plugin-arch
2
2
 
3
- Configurable, filename-aware architecture rules for Oxlint.
3
+ > Architecture rules for [Oxlint](https://oxc.rs), driven by filenames and fully configurable.
4
4
 
5
- Install the package, then put repository paths and policy in your Oxlint config. The npm package ships compiled JavaScript; `src/` stays TypeScript for development. Rules contain no application-specific names or folders.
5
+ Keep a codebase shaped the way you designed it: files in the right folders, exports named after their file, types where they belong, and no shortcuts across boundaries. Every rule is generic, so **you** decide the paths, names, and policy in your own Oxlint config.
6
6
 
7
- ## Install
7
+ - 🗂️ **Filename-aware.** Rules derive expected names from the file they run on (`user-action.create.ts` → `makeUserActionCreate`).
8
+ - 🎛️ **Configurable.** No app names, folders, or libraries are baked in. Globs and policy live in your config.
9
+ - 🛟 **Safe autofix.** Fixable rules only touch whitespace or comments, and never guess when a fix could change your code.
10
+ - ⚡ **Fast.** Built on Oxlint's JS plugin API and ESTree, with no extra parser.
11
+
12
+ ---
13
+
14
+ ## 📦 Install
8
15
 
9
16
  ```bash
10
17
  bun add -d oxlint-plugin-arch oxlint @oxlint/plugins
11
18
  ```
12
19
 
13
- Use matching `oxlint` and `@oxlint/plugins` versions. Then register the package:
20
+ > [!NOTE]
21
+ > Keep `oxlint` and `@oxlint/plugins` on matching versions.
22
+
23
+ Register the plugin in `oxlint.config.ts`:
14
24
 
15
25
  ```ts
16
26
  import { defineConfig } from 'oxlint'
@@ -22,141 +32,270 @@ export default defineConfig({
22
32
  })
23
33
  ```
24
34
 
25
- ## Recommended companion: anti-slop
35
+ ## 🚀 Quick start
26
36
 
27
- We recommend [Dillon Mulroy's anti-slop](https://github.com/dmmulroy/anti-slop) alongside this plugin. Anti-slop catches low-evidence TypeScript and JavaScript implementation patterns; oxlint-plugin-arch enforces configurable file, export, boundary, and API structure.
37
+ Rules are switched on per folder with `overrides`. Here are three small ones:
28
38
 
29
- Register both in the same Oxlint config under their separate `anti-slop/*` and `arch/*` rule namespaces.
39
+ ```ts
40
+ export default defineConfig({
41
+ jsPlugins: [{ name: 'arch', specifier: 'oxlint-plugin-arch' }],
42
+ rules: {
43
+ 'arch/no-literal-in': 'error',
44
+ },
45
+ overrides: [
46
+ {
47
+ files: ['**/src/components/**/*.tsx'],
48
+ rules: {
49
+ 'arch/only-export-components': ['error', { matchFileName: true }],
50
+ },
51
+ },
52
+ {
53
+ files: ['**/*.test.ts'],
54
+ rules: {
55
+ 'arch/test-title-pattern': ['error', { forbid: '^should\\b', flags: 'i' }],
56
+ },
57
+ },
58
+ ],
59
+ })
60
+ ```
30
61
 
31
- ## Optional: vendor a local copy
62
+ ## 📚 Examples
63
+
64
+ | File | Use it when |
65
+ | --- | --- |
66
+ | [`minimal.oxlint.config.ts`](examples/minimal.oxlint.config.ts) | You want the smallest possible starting point. |
67
+ | [`monorepo.oxlint.config.ts`](examples/monorepo.oxlint.config.ts) | ⭐ You are starting an `apps/server` + `apps/web` monorepo and want a strict, copy-ready setup. |
68
+ | [`full.oxlint.config.ts`](examples/full.oxlint.config.ts) | You want to see every one of the 42 rules with its options. |
69
+
70
+ > [!TIP]
71
+ > `monorepo.oxlint.config.ts` already loads `oxlint-plugin-arch`, so you can copy it as-is and adjust globs and names. The other two load `../src/index.ts`; change that specifier to `oxlint-plugin-arch` when you copy them.
32
72
 
33
- If you want to inspect or fork the rules in-tree, copy `src/index.ts`, `src/rules/`, and `src/utils/`, or use the agent skill:
73
+ ---
34
74
 
35
- ```bash
36
- npx skills add nikuscs/oxlint-plugin-arch --skill install-oxlint-arch
37
- ```
75
+ ## 🧩 Rules
76
+
77
+ 42 rules in 9 groups. 🔧 means the rule can autofix. Each rule file in [`src/rules/`](src/rules) explains its behavior in plain English, and the tests in [`src/tests/`](src/tests) show every option shape.
78
+
79
+ ### 📁 Files & folders
80
+
81
+ | Rule | What it enforces |
82
+ | --- | --- |
83
+ | `filename-match` | Filenames match a pattern you configure. |
84
+ | `folder-prefix` | Filenames start with their parent folder name, or the folders after a root you choose. |
85
+ | `no-restricted-files` | Files matching a glob you forbid are reported, for example misplaced `*.types.ts`. |
86
+
87
+ ### 🏷️ Naming & exports
88
+
89
+ | Rule | What it enforces |
90
+ | --- | --- |
91
+ | `declaration-name` | Selected declarations start with a prefix taken from the filename, or match a pattern. |
92
+ | `export-file-prefix` | Export names (or every function and type) start with the filename prefix. `trailingRoles` treats `onchain-utils.ts` like `onchain.utils.ts`. |
93
+ | `export-name-pattern` | Export names (or every function and type) match a regular expression. |
94
+ | `filename-export-name` | Function names follow a template built from the filename. |
95
+ | `no-extra-exports` | A file exports only the names you allow. `patterns` accepts families such as `{Domain}[A-Z]\w*`. |
96
+ | `only-export-constants` | Constant files export only local `const` values. [Details ↓](#only-export-constants) |
97
+
98
+ ### 🏭 Functions & factories
99
+
100
+ | Rule | What it enforces |
101
+ | --- | --- |
102
+ | `require-file-factory` | The file defines the factory function its filename promises. |
103
+ | `no-extra-factory-keys` | A factory returns only allowed keys. `requireKeys` makes keys mandatory; `keySets` sets keys per factory pattern. |
104
+ | `require-object-params` | Functions take object parameters instead of long positional lists. `maxParams: 2` allows `(deps, params)`. |
105
+ | `no-file-level-helpers` | No stray helper functions at module scope. |
106
+ | `no-top-level-functions` | No top-level functions (and optionally no re-exports) in matched files. |
107
+ | `no-trivial-functions` | No empty or pass-through wrapper functions. `allowCallees` keeps intentional wrappers. |
108
+ | `no-module-mutable-state` | No module-level `let`/`var`, which is shared across requests and tests. |
38
109
 
39
- Then point `jsPlugins[].specifier` at the copied entry file instead of the package name.
40
-
41
- ## Configuration examples
42
-
43
- - [`examples/minimal.oxlint.config.ts`](examples/minimal.oxlint.config.ts) — three small rules showing plugin registration, glob ownership, and options.
44
- - [`examples/full.oxlint.config.ts`](examples/full.oxlint.config.ts) — all 30 rules across representative component, action, service, constant, route, API, schema, and database scopes.
45
-
46
- Copy the shapes that match your repository; do not copy globs or naming policy blindly.
47
-
48
- ## Rules
49
-
50
- - `declaration-name` — require selected declarations to match a filename-derived prefix or a consumer pattern. Optional `trailingRoles` treat `onchain-utils.ts` like `onchain.utils.ts`.
51
- - `export-file-prefix` — require export names, or every function and type, to start with a filename-derived prefix, optionally singularized. Optional `trailingRoles` drop a configured role suffix from that prefix.
52
- - `export-name-pattern` — require export names, or every function and type, to match a configured regular expression.
53
- - `filename-export-name` — derive expected function names from filename templates, optionally including locals.
54
- - `filename-match` — require filenames to match a configured pattern.
55
- - `folder-prefix` — require filenames to start with their parent folder name, or the folders after a configured root.
56
- - `no-extra-exports` — restrict files to configured export templates. Optional `trailingRoles` apply to `{Domain}` the same way as the prefix rules.
57
- - `no-extra-factory-keys` — restrict direct factory return keys.
58
- - `no-file-level-helpers` — keep unapproved helpers out of module scope.
59
- - `no-imported-type-alias` — reject exported aliases that only rename imported types.
60
- - `no-inline-schema-elements` — require named schemas inside configured combinators.
61
- - `no-inline-types` — require named object and function types in parameter and return annotations. See [options and scope](#no-inline-types).
62
- - `no-local-schema-construction` — reject runtime schema-library imports and local schema construction, with consumer-chosen severity, messages, and path exceptions.
63
- - `no-rederive-schema` — reject type derivation from configured imported schemas.
64
- - `no-restricted-files` — reject files selected by a consumer-owned forbidden glob.
65
- - `no-restricted-token` — restrict an identifier to configured owner paths.
66
- - `no-runtime-in-types` — keep selected type modules runtime-free.
67
- - `no-single-use-scalar-schema` — reject local scalar Zod aliases used once where they can be safely inlined.
68
- - `no-top-level-functions` — reject top-level functions and optional re-exports.
69
- - `no-trivial-functions` — reject empty or passthrough top-level functions.
70
- - `no-type-declarations` — reject type aliases and interfaces in matched files.
71
- - `no-member-comments` — remove leading and same-line trailing comments on object-type members: interface properties and methods, index/call/construct signatures, type-literal members (including inline function parameter types), object literal properties, and object spreads. Autofix removes the comment and any blank line that followed it, consumes the spacing in front of a trailing comment so no line is left with trailing whitespace, and never removes the member. A contiguous run of comments is reported once and removed by a single fix, so `--fix` converges in one pass. `allowWhy` defaults to `true`: comments beginning with `Why: ` (after whitespace/JSDoc decoration), including contiguous `//` continuation lines, are preserved. Set `{ allowWhy: false }` to ban those too. The rule checks the prefix, not whether the rationale is meaningful. Directive comments are always preserved, whatever `allowWhy` says: `@ts-ignore`, `@ts-expect-error`, `@ts-nocheck`, `eslint-`/`oxlint-disable`/`enable`, `prettier-ignore`, `biome-ignore`, `c8`/`v8`/`istanbul ignore`, and webpack magic comments. Declaration, function-body, class-field, enum-member, union-member, and plain function-parameter comments are out of scope, as are comments inside destructuring patterns and array literals. Enable with `'arch/no-member-comments': ['error', { allowWhy: true }]` in a `**/*.{ts,tsx}` override.
72
- - `no-unescaped-like` — require configured sanitizers for configured query methods.
73
- - `only-export-components` — allow only React component and type exports.
74
- - `only-export-constants` — allow only local `const` exports, with opt-ins for function values, types, and re-exports. See [options and scope](#only-export-constants).
75
- - `require-file-factory` — derive and require a filename-based factory function.
76
- - `require-object-params` — require exported functions to use object-shaped parameters.
77
- - `require-orpc-output` — require named oRPC output schemas.
78
- - `require-paired-call` — require one configured call when another appears.
79
- - `route-surface` — constrain route exports, hooks, and intrinsic JSX.
80
-
81
- Every rule file includes plain-English behavior and examples. Rule tests under `src/tests/` show complete option shapes.
110
+ ### 🔷 Types
111
+
112
+ | Rule | What it enforces |
113
+ | --- | --- |
114
+ | `no-inline-types` | Function signatures use named types instead of inline `{ ... }` objects. [Details ↓](#no-inline-types) |
115
+ | `no-type-declarations` | No type aliases or interfaces in matched files, so types live in one place. |
116
+ | `no-runtime-in-types` | Type modules stay free of runtime code. Opt in to also ban runtime imports and re-exports. |
117
+ | `no-imported-type-alias` | No exported aliases that only rename an imported type. |
118
+
119
+ ### 🧪 Schemas & APIs
120
+
121
+ | Rule | What it enforces |
122
+ | --- | --- |
123
+ | `no-local-schema-construction` | No schemas built outside the files that own them. |
124
+ | `no-inline-schema-elements` | Named schemas inside combinators like `z.array(...)`. |
125
+ | `no-rederive-schema` | No re-deriving types from schemas imported from elsewhere. |
126
+ | `no-single-use-scalar-schema` | Scalar Zod aliases used once get inlined. |
127
+ | `require-orpc-output` | oRPC procedures declare a named `.output()` schema. |
128
+ | `no-unescaped-like` | `LIKE` / `ILIKE` values pass through your sanitizer. |
129
+
130
+ ### 🚧 Boundaries & safety
131
+
132
+ | Rule | What it enforces |
133
+ | --- | --- |
134
+ | `no-restricted-token` | Identifiers or member calls appear only in owner files. `member: '*.insertInto'` keeps writes out of query files. [Details ↓](#no-restricted-token) |
135
+ | `no-restricted-constructor` | No `new Error()` (or any constructor you list) where typed errors are required. |
136
+ | `require-paired-call` | Calling one function requires calling another, for example `useForm` with a schema resolver. |
137
+ | `no-promise-all-mutation` | No `results.push(...)` or `total += x` on outer variables inside `Promise.all`. Return values instead. |
138
+ | `no-literal-in` | No `'key' in value`. Use `Object.hasOwn()` or a discriminated union. |
139
+
140
+ ### ⚛️ React
141
+
142
+ | Rule | What it enforces |
143
+ | --- | --- |
144
+ | `only-export-components` | Component files export only components. `denyTypePattern: 'Props$'` keeps props private. |
145
+ | `route-surface` | Route files export the route only, with no banned hooks or raw JSX. |
146
+
147
+ ### 💬 Comments & tests
148
+
149
+ | Rule | What it enforces |
150
+ | --- | --- |
151
+ | `no-comments` 🔧 | No comments in matched files. Directives, bundler annotations, and `Why:` notes are kept; JSX comments and comments next to `disable-next-line` are reported without an autofix. |
152
+ | `no-member-comments` 🔧 | No comments on interface, type, and object members. [Details ↓](#no-member-comments) |
153
+ | `test-title-pattern` | Test titles match your rules, for example no leading "should". |
154
+
155
+ ### 🎨 Layout
156
+
157
+ | Rule | What it enforces |
158
+ | --- | --- |
159
+ | `padding-between-statements` 🔧 | Blank lines around functions and classes, around control flow, and before `return` in longer blocks. |
160
+ | `object-multiline` 🔧 | Objects with 3+ properties passed to a call go one property per line (`scope: 'all'` for every object). |
161
+ | `key-value-same-line` 🔧 | An object key and the start of its value stay on the same line. |
162
+ | `chain-newline` 🔧 | Long method chains go one call per line. You pick which chains with `groups`. |
163
+ | `call-array-multiline` 🔧 | Arrays passed to `Promise.all` (or callees you list) go one element per line. |
164
+ | `jsx-attributes-multiline` 🔧 | JSX tags with 3+ attributes go one attribute per line. |
165
+
166
+ > [!WARNING]
167
+ > Oxfmt and Prettier collapse short chains, arrays, and JSX tags back onto one line. Don't run `chain-newline`, `call-array-multiline`, or `jsx-attributes-multiline` on files a formatter also rewrites, or they will undo each other forever.
168
+
169
+ ---
170
+
171
+ ## 🔍 Rule details
82
172
 
83
173
  ### `no-inline-types`
84
174
 
85
- Require named types in function signatures, including local functions, callbacks, methods, constructors, and overloads. Checks explicit annotations only; inferred types, variable annotations, generic constraints, and named type/interface definitions are not changed or banned. Use `no-type-declarations` separately to control where named types may live.
175
+ Requires named types in function signatures: local functions, callbacks, methods, constructors, and overloads. It checks explicit annotations only; inferred types, variable annotations, and generic constraints are left alone.
86
176
 
87
- | Option | Default | Behavior |
177
+ | Option | Default | What it does |
88
178
  | --- | --- | --- |
89
- | `parameters` | `true` | Check parameter annotations, including destructuring, defaults, rest parameters, and constructor parameter properties. |
179
+ | `parameters` | `true` | Check parameter annotations, including destructuring, defaults, rest, and constructor parameter properties. |
90
180
  | `returns` | `true` | Check explicit return annotations. |
91
- | `functionTypes` | `true` | Also reject inline function and constructor types. When `false`, object types nested inside those annotations are still checked. |
181
+ | `functionTypes` | `true` | Also reject inline function and constructor types. |
182
+ | `minMembers` | `1` | Allow inline object types with fewer members than this. `1` also rejects `{}`. |
92
183
 
93
- Object/function types nested in arrays, tuples, unions, intersections, or generic arguments are checked too (`Promise<{ id: string }>` fails). A rejected outer type is reported once, not again for each nested member.
184
+ Nested types count too, so `Promise<{ id: string }>` fails. An outer type is reported once, not again for each member inside it.
94
185
 
95
186
  ```ts
96
- // In the consumer's override for service files:
97
- 'arch/no-inline-types': ['error', {
98
- parameters: true,
99
- returns: true,
100
- functionTypes: true,
101
- }]
102
-
103
- // Pass
187
+ // ✅ Pass
104
188
  function save(input: SaveInput): SaveResult { return persist(input) }
105
- // Fail
189
+
190
+ // ❌ Fail
106
191
  function save(input: { id: string }): { ok: boolean } { return persist(input) }
107
192
  ```
108
193
 
109
- ### `only-export-constants`
194
+ <details>
195
+ <summary>More about <code>no-inline-types</code></summary>
110
196
 
111
- Require every runtime export to reference a local `const` binding. Supports direct exports, destructuring, renamed exports, and default exports of local constants. Non-exported declarations are unrestricted.
197
+ Use `no-type-declarations` separately to control where named types may live. With `functionTypes: false`, object types nested inside function types are still checked.
112
198
 
113
- | Option | Default | Behavior |
114
- | --- | --- | --- |
115
- | `allowFunctionValues` | `false` | Allow function-valued `const` bindings, not exported function declarations. |
116
- | `allowTypeExports` | `false` | Allow type aliases, interfaces, and type-only exports, including type-only re-exports. |
117
- | `allowReExports` | `false` | Allow runtime re-exports (`export ... from`, `export *`) and exports of imported bindings. Their declaration kind cannot be verified locally. |
199
+ </details>
118
200
 
119
- `let`, `var`, function/class/enum/namespace declarations, class-valued constants, bare default expressions, and TypeScript `export =` / `export as namespace` are rejected. Name the default value with a local `const` first. Type-only re-exports use `allowTypeExports`, independently of `allowReExports`.
201
+ ### `only-export-constants`
120
202
 
121
- This is a syntax rule, not a deep-immutability or runtime-type check. It recognizes direct function/class values, TypeScript expression wrappers, and local identifier aliases. It does not infer values returned by calls, imported values, member accesses, destructuring, or object contents. Objects and arrays do not need `as const` or `Object.freeze()`.
203
+ Every runtime export must point to a local `const`. Direct, destructured, renamed, and default exports of local constants all work.
122
204
 
123
- ```ts
124
- // In the consumer's override for constant files:
125
- 'arch/only-export-constants': ['error', {
126
- allowFunctionValues: false,
127
- allowTypeExports: false,
128
- allowReExports: false,
129
- }]
205
+ | Option | Default | What it does |
206
+ | --- | --- | --- |
207
+ | `allowFunctionValues` | `false` | Allow function-valued `const` bindings (not function declarations). |
208
+ | `allowTypeExports` | `false` | Allow type aliases, interfaces, and type-only exports. |
209
+ | `allowReExports` | `false` | Allow `export ... from`, `export *`, and exports of imported bindings. |
130
210
 
131
- // Pass
211
+ ```ts
212
+ // ✅ Pass
132
213
  const retryLimit = 3
133
214
  export { retryLimit }
134
- // Fail
215
+
216
+ // ❌ Fail
135
217
  export let retryCount = 0
136
218
  export const retry = () => run()
137
219
  ```
138
220
 
139
- ## Development
221
+ <details>
222
+ <summary>More about <code>only-export-constants</code></summary>
140
223
 
141
- ```bash
142
- bun install
143
- bun run check
144
- ```
224
+ `let`, `var`, function, class, enum, and namespace declarations, class-valued constants, bare default expressions, and TypeScript `export =` / `export as namespace` are rejected. Name a default value with a local `const` first.
145
225
 
146
- After production changes, run `bun run sync:skill-assets`; CI verifies the bundled installer copy matches `src/`.
226
+ This is a syntax rule, not a deep-immutability check. It recognizes direct function and class values, TypeScript expression wrappers, and local aliases. It does not follow values returned by calls, imports, member access, or destructuring. Objects and arrays do not need `as const` or `Object.freeze()`.
147
227
 
148
- ## Release
228
+ </details>
149
229
 
150
- Update `CHANGELOG.md`, then run the full lifecycle from a clean `main`:
230
+ ### `no-restricted-token`
151
231
 
152
- ```bash
153
- bun run release # prompt for patch/minor/major
154
- bun run release:patch # check, bump, tag, push, npm publish
155
- bun run release:dry-run # check + npm pack dry-run only
232
+ Keeps identifiers and member calls inside the files that own them.
233
+
234
+ ```ts
235
+ 'arch/no-restricted-token': ['error', {
236
+ restrictions: [
237
+ { token: 'RouterClient', allowIn: ['/src/services/rpc.client.ts'] },
238
+ { member: 'process.platform', allowIn: ['/src/runtime.ts'] },
239
+ { member: '*.insertInto', message: 'Queries are read-only; move writes to an action.' },
240
+ ],
241
+ }]
156
242
  ```
157
243
 
158
- `scripts/release.sh` refuses dirty trees and requires `npm login`. `prepublishOnly` runs `bun run check` again before the tarball goes out.
244
+ | Field | What it does |
245
+ | --- | --- |
246
+ | `token` | An identifier name to restrict. Type-only imports are ignored. |
247
+ | `member` | A dotted member path such as `process.platform`. `*.name` matches any receiver, including `this.db` and chained calls. |
248
+ | `allowIn` | Filename suffixes where the token is allowed. |
249
+ | `allowPathPatterns` | Regular expressions for allowed paths. |
250
+ | `message` | Extra guidance added to the report. |
251
+
252
+ The older single form `{ token, allowIn }` still works.
253
+
254
+ ### `no-member-comments`
255
+
256
+ Removes comments on object-type members: interface properties and methods, index, call, and construct signatures, type-literal members, object literal properties, and spreads.
257
+
258
+ | Kept ✅ | Removed ❌ |
259
+ | --- | --- |
260
+ | `Why: ...` notes (with `allowWhy: true`, the default) | Other leading and same-line trailing member comments |
261
+ | Directives such as `@ts-expect-error`, `eslint-disable`, `prettier-ignore` | |
262
+
263
+ <details>
264
+ <summary>Autofix and scope details</summary>
265
+
266
+ The autofix removes the comment and any blank line after it, drops the spaces before a trailing comment, and never removes the member itself. A run of stacked comments is removed by one fix, so `--fix` finishes in one pass.
267
+
268
+ `Why:` detection checks the prefix (after whitespace or JSDoc decoration) and includes contiguous `//` continuation lines. Set `{ allowWhy: false }` to remove those too. Always-kept directives: `@ts-ignore`, `@ts-expect-error`, `@ts-nocheck`, `eslint-`/`oxlint-disable`/`enable`, `prettier-ignore`, `biome-ignore`, `c8`/`v8`/`istanbul ignore`, and webpack magic comments.
269
+
270
+ Out of scope: declaration, function-body, class-field, enum-member, union-member, and plain parameter comments, plus comments inside destructuring patterns and array literals. For whole-file comment policy, use `no-comments`.
271
+
272
+ </details>
273
+
274
+ ---
275
+
276
+ ## 🤝 Works well with anti-slop
277
+
278
+ We recommend [Dillon Mulroy's anti-slop](https://github.com/dmmulroy/anti-slop) alongside this plugin:
279
+
280
+ | Plugin | Catches |
281
+ | --- | --- |
282
+ | **anti-slop** | Low-evidence TypeScript and JavaScript patterns inside functions. |
283
+ | **oxlint-plugin-arch** | File, export, boundary, API, and layout structure. |
284
+
285
+ Register both in the same config; they use separate `anti-slop/*` and `arch/*` namespaces.
286
+
287
+ ## ⚡ Benchmarks
288
+
289
+ Wall time for a full `oxlint` run over three real TypeScript monorepos, best of 5, including process startup.
290
+
291
+ | Project | Files | Lines | Plugin loaded, no rules | [Monorepo example](examples/monorepo.oxlint.config.ts) | All 42 rules on every file |
292
+ | --- | ---: | ---: | ---: | ---: | ---: |
293
+ | Small | 246 | 14.5k | 0.09s | **0.20s** | 0.23s |
294
+ | Medium | 792 | 100.5k | 0.10s | **0.49s** | 0.69s |
295
+ | Large | 1,880 | 190k | 0.10s | **0.72s** | 1.08s |
296
+
297
+ <sub>Apple M5 Pro, Oxlint 1.78.0, default thread count. Most of the gap between "no rules" and the other columns is Oxlint's one-time cost of handing the AST to JavaScript plugins, paid once however many rules are on.</sub>
159
298
 
160
- ## License
299
+ ## 📄 License
161
300
 
162
301
  MIT