@hooshid/ox-config 0.3.0 → 1.0.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.
- package/README.md +144 -58
- package/dist/lint.d.mts +19 -4
- package/dist/lint.d.mts.map +1 -1
- package/dist/lint.mjs +208 -24
- package/dist/lint.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,11 +16,11 @@ pnpm add -D @hooshid/ox-config
|
|
|
16
16
|
Create `oxlint.config.ts` in your project root:
|
|
17
17
|
|
|
18
18
|
```ts
|
|
19
|
-
import { base, unicorn, depend,
|
|
19
|
+
import { base, unicorn, depend, reactVite, tailwind } from '@hooshid/ox-config/lint'
|
|
20
20
|
import { defineConfig } from 'oxlint'
|
|
21
21
|
|
|
22
22
|
export default defineConfig({
|
|
23
|
-
extends: [base(), unicorn(), depend(),
|
|
23
|
+
extends: [base(), unicorn(), depend(), reactVite(), tailwind()],
|
|
24
24
|
})
|
|
25
25
|
```
|
|
26
26
|
|
|
@@ -52,20 +52,21 @@ export default defineConfig({
|
|
|
52
52
|
|
|
53
53
|
#### Runtime
|
|
54
54
|
|
|
55
|
-
| Preset | Description
|
|
56
|
-
| ----------- |
|
|
57
|
-
| `node()` | Node.js specific rules
|
|
58
|
-
| `promise()` | Promise best practices (16 rules)
|
|
55
|
+
| Preset | Description |
|
|
56
|
+
| ----------- | --------------------------------- |
|
|
57
|
+
| `node()` | Node.js specific rules |
|
|
58
|
+
| `promise()` | Promise best practices (16 rules) |
|
|
59
59
|
|
|
60
60
|
#### Frontend
|
|
61
61
|
|
|
62
|
-
| Preset | Description
|
|
63
|
-
| ------------------ |
|
|
64
|
-
| `react()` | React + React Hooks
|
|
65
|
-
| `reactVite()` | React + React Hooks + React Refresh (for Vite)
|
|
66
|
-
| `nextjs()` | Next.js rules + Core Web Vitals
|
|
67
|
-
| `tanstackRouter()` | TanStack Router — route
|
|
68
|
-
| `
|
|
62
|
+
| Preset | Description |
|
|
63
|
+
| ------------------ | --------------------------------------------------------------- |
|
|
64
|
+
| `react()` | React + React Hooks — core rules for any React setup |
|
|
65
|
+
| `reactVite()` | React + React Hooks + React Refresh (for Vite) |
|
|
66
|
+
| `nextjs()` | Next.js rules + Core Web Vitals |
|
|
67
|
+
| `tanstackRouter()` | TanStack Router — routing rules + route folder naming convention |
|
|
68
|
+
| `tanstackStart()` | TanStack Start — server/client boundaries, SSR safety |
|
|
69
|
+
| `tailwind()` | Tailwind class consistency (better-tailwindcss plugin) |
|
|
69
70
|
|
|
70
71
|
#### Backend / ORM
|
|
71
72
|
|
|
@@ -89,53 +90,91 @@ export default defineConfig({
|
|
|
89
90
|
|
|
90
91
|
### Which React preset should I use?
|
|
91
92
|
|
|
92
|
-
| Project type
|
|
93
|
-
|
|
|
94
|
-
| Next.js
|
|
95
|
-
| Vite + React
|
|
96
|
-
| TanStack
|
|
97
|
-
|
|
|
93
|
+
| Project type | Presets to use |
|
|
94
|
+
| ------------------------- | ------------------------------------------------- |
|
|
95
|
+
| Next.js | `nextjs()` + `react()` |
|
|
96
|
+
| Vite + React | `reactVite()` |
|
|
97
|
+
| TanStack Router (SPA) | `reactVite()` + `tanstackRouter()` |
|
|
98
|
+
| TanStack Start | `reactVite()` + `tanstackRouter()` + `tanstackStart()` |
|
|
99
|
+
| Plain React (custom build)| `react()` |
|
|
98
100
|
|
|
99
101
|
> `reactVite()` adds `eslint-plugin-react-refresh` to warn about incorrect fast-refresh patterns — use it for any Vite-based setup.
|
|
100
102
|
|
|
101
|
-
###
|
|
103
|
+
### React rules
|
|
102
104
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
> files: ['**/*.{ts,mts,cts,tsx}'],
|
|
112
|
-
> rules: {
|
|
113
|
-
> 'typescript/consistent-type-imports': 'off',
|
|
114
|
-
> 'typescript/no-extraneous-class': ['error', { allowWithDecorator: true }],
|
|
115
|
-
> },
|
|
116
|
-
> },
|
|
117
|
-
> ],
|
|
118
|
-
> })
|
|
119
|
-
> ```
|
|
105
|
+
The `react()` and `reactVite()` presets enable **~27 rules** covering:
|
|
106
|
+
|
|
107
|
+
- **Hooks (critical):** `rules-of-hooks`, `exhaustive-deps`
|
|
108
|
+
- **JSX correctness:** `jsx-key`, `jsx-no-duplicate-props`, `jsx-no-undef`, `jsx-no-target-blank`, `jsx-no-script-url`, `jsx-no-comment-textnodes`
|
|
109
|
+
- **JSX style:** `jsx-boolean-value`, `jsx-curly-brace-presence`, `jsx-fragments`, `jsx-pascal-case`, `self-closing-comp`, `hook-use-state`
|
|
110
|
+
- **Runtime safety:** `no-children-prop`, `no-danger-with-children`, `no-deprecated`, `no-direct-mutation-state`, `no-find-dom-node`, `no-is-mounted`, `no-render-return-value`, `no-string-refs`, `no-unknown-property`, `no-unsafe`, `require-render-return`, `void-dom-elements-no-children`
|
|
111
|
+
|
|
112
|
+
Modern React 17+ is assumed — `react/react-in-jsx-scope` and `react/jsx-uses-react` are off.
|
|
120
113
|
|
|
121
114
|
### TanStack Router
|
|
122
115
|
|
|
123
|
-
The `tanstackRouter()` preset loads [`@tanstack/eslint-plugin-router`](https://www.npmjs.com/package/@tanstack/eslint-plugin-router)
|
|
116
|
+
The `tanstackRouter()` preset loads [`@tanstack/eslint-plugin-router`](https://www.npmjs.com/package/@tanstack/eslint-plugin-router) and enforces file naming for the `routes/` folder.
|
|
124
117
|
|
|
125
118
|
```ts
|
|
126
|
-
import { base, tanstackRouter } from '@hooshid/ox-config/lint'
|
|
119
|
+
import { base, reactVite, tanstackRouter, tailwind } from '@hooshid/ox-config/lint'
|
|
127
120
|
import { defineConfig } from 'oxlint'
|
|
128
121
|
|
|
129
122
|
export default defineConfig({
|
|
130
|
-
extends: [base(), tanstackRouter()],
|
|
123
|
+
extends: [base(), reactVite(), tanstackRouter(), tailwind()],
|
|
131
124
|
})
|
|
132
125
|
```
|
|
133
126
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
- `
|
|
127
|
+
**Rules provided:**
|
|
128
|
+
|
|
129
|
+
- `tanstack-router/route-param-names` — validates `$param` names between route path and params
|
|
130
|
+
- `tanstack-router/create-route-property-order` — enforces property order in `createRoute()`
|
|
137
131
|
|
|
138
|
-
|
|
132
|
+
**File naming convention:**
|
|
133
|
+
|
|
134
|
+
All files inside a `routes/` folder must be **kebab-case**:
|
|
135
|
+
|
|
136
|
+
| Filename | Valid? | Notes |
|
|
137
|
+
| --------------------------- | ------ | -------------------------------------- |
|
|
138
|
+
| `posts.tsx` | ✅ | |
|
|
139
|
+
| `posts-index.tsx` | ✅ | |
|
|
140
|
+
| `about-page.tsx` | ✅ | |
|
|
141
|
+
| `posts.$postId.tsx` | ✅ | exception: `$` dynamic params |
|
|
142
|
+
| `_auth.tsx` | ✅ | exception: `_` pathless routes |
|
|
143
|
+
| `__root.tsx` | ✅ | exception: `_` prefix |
|
|
144
|
+
| `posts.index.tsx` | ✅ | exception: multiple dots (file nesting)|
|
|
145
|
+
| `Posts.tsx` | ❌ | must be `posts.tsx` |
|
|
146
|
+
| `postsIndex.tsx` | ❌ | must be `posts-index.tsx` |
|
|
147
|
+
| `posts_index.tsx` | ❌ | must be `posts-index.tsx` |
|
|
148
|
+
|
|
149
|
+
> [!NOTE]
|
|
150
|
+
> Folder naming inside `routes/` is **not** covered by Oxlint (linters run file-by-file). To enforce kebab-case for folders, use a separate script or CI step.
|
|
151
|
+
|
|
152
|
+
### TanStack Start
|
|
153
|
+
|
|
154
|
+
The `tanstackStart()` preset adds rules for **server/client boundaries**, **server function safety**, and **SSR conventions**.
|
|
155
|
+
|
|
156
|
+
> [!IMPORTANT]
|
|
157
|
+
> This preset does **not** include `reactVite()` or `tanstackRouter()`. Add them in the correct order:
|
|
158
|
+
>
|
|
159
|
+
> ```ts
|
|
160
|
+
> extends: [base(), reactVite(), tanstackRouter(), tanstackStart(), tailwind(), vitest()]
|
|
161
|
+
> ```
|
|
162
|
+
|
|
163
|
+
**Rules added:**
|
|
164
|
+
|
|
165
|
+
- `no-async-promise-executor`, `consistent-return`, `no-empty-function`
|
|
166
|
+
- `import/no-nodejs-modules` — prevents Node.js APIs from leaking into client bundles
|
|
167
|
+
- `no-restricted-globals` for `window`/`document`/`localStorage`/`sessionStorage` in server files
|
|
168
|
+
|
|
169
|
+
**File scope conventions:**
|
|
170
|
+
|
|
171
|
+
| File pattern | Behavior |
|
|
172
|
+
| --------------------------------------------------------------- | ----------------------------------------------------- |
|
|
173
|
+
| `**/*.server.{ts,tsx}`, `**/server/**`, `**/server-fns/**` | Node.js APIs allowed, browser globals forbidden |
|
|
174
|
+
| `**/*.client.{ts,tsx}`, `**/components/**` | Node.js APIs forbidden |
|
|
175
|
+
| `**/routes/**` | Warning on direct `fetch()` — prefer loaders |
|
|
176
|
+
|
|
177
|
+
**Example:**
|
|
139
178
|
|
|
140
179
|
```ts
|
|
141
180
|
import {
|
|
@@ -144,6 +183,7 @@ import {
|
|
|
144
183
|
depend,
|
|
145
184
|
reactVite,
|
|
146
185
|
tanstackRouter,
|
|
186
|
+
tanstackStart,
|
|
147
187
|
tailwind,
|
|
148
188
|
vitest,
|
|
149
189
|
} from '@hooshid/ox-config/lint'
|
|
@@ -156,23 +196,25 @@ export default defineConfig({
|
|
|
156
196
|
depend(),
|
|
157
197
|
reactVite(),
|
|
158
198
|
tanstackRouter(),
|
|
159
|
-
|
|
199
|
+
tanstackStart(),
|
|
200
|
+
tailwind({ entryPoint: 'src/styles/globals.css' }),
|
|
160
201
|
vitest(),
|
|
161
202
|
],
|
|
162
203
|
})
|
|
163
204
|
```
|
|
164
205
|
|
|
165
|
-
###
|
|
206
|
+
### NestJS
|
|
207
|
+
|
|
208
|
+
> [!WARNING]
|
|
209
|
+
> NestJS must disable `typescript/consistent-type-imports` — NestJS DI uses runtime class references in constructor params, and without type-aware linting this rule incorrectly converts them to `import type`, breaking DI at runtime.
|
|
166
210
|
|
|
167
211
|
```ts
|
|
168
|
-
import { base,
|
|
212
|
+
import { base, node, promise, nestjs, drizzle, vitest } from '@hooshid/ox-config/lint'
|
|
169
213
|
import { defineConfig } from 'oxlint'
|
|
170
214
|
|
|
171
215
|
export default defineConfig({
|
|
172
216
|
extends: [
|
|
173
217
|
base(),
|
|
174
|
-
unicorn(),
|
|
175
|
-
depend(),
|
|
176
218
|
node(),
|
|
177
219
|
promise(),
|
|
178
220
|
nestjs(),
|
|
@@ -223,10 +265,10 @@ Each package's own `tsconfig.json` is auto-detected by oxlint — no extra confi
|
|
|
223
265
|
|
|
224
266
|
#### Do I still need a separate `tsc --noEmit` step?
|
|
225
267
|
|
|
226
|
-
|
|
268
|
+
**Yes, keep it.** With `typeCheck` enabled, `oxlint` already surfaces tsc diagnostics for the files it lints, so during local development you'll usually catch type errors from `lint` alone. But a dedicated `typecheck` script is still worth keeping because:
|
|
227
269
|
|
|
228
|
-
- **Different file scope.** `tsc --noEmit` honors the tsconfig's `include
|
|
229
|
-
- **Project-level diagnostics.** tsc catches errors that aren't attached to a single source file: `tsconfig.json` misconfiguration (`TS5xxx`), broken project `references`, `paths` alias typos.
|
|
270
|
+
- **Different file scope.** `tsc --noEmit` honors the tsconfig's `include`/`exclude`. `oxlint` walks the filesystem by its own rules and honors `ignorePatterns`. The two sets overlap but are not identical.
|
|
271
|
+
- **Project-level diagnostics.** tsc catches errors that aren't attached to a single source file: `tsconfig.json` misconfiguration (`TS5xxx`), broken project `references`, `paths` alias typos.
|
|
230
272
|
- **Clearer CI failures.** Running `typecheck` and `lint` as separate steps makes it obvious whether a red build is a type error or a lint rule violation.
|
|
231
273
|
|
|
232
274
|
### Monorepo (nested configs)
|
|
@@ -235,8 +277,6 @@ Oxlint supports [nested configuration](https://oxc.rs/docs/guide/usage/linter/ne
|
|
|
235
277
|
|
|
236
278
|
#### Root config
|
|
237
279
|
|
|
238
|
-
Keep shared baseline and `typeAware()` at the root:
|
|
239
|
-
|
|
240
280
|
```ts
|
|
241
281
|
// oxlint.config.ts (root)
|
|
242
282
|
import { base, typeAware, unicorn, depend } from '@hooshid/ox-config/lint'
|
|
@@ -252,11 +292,11 @@ export default defineConfig({
|
|
|
252
292
|
```ts
|
|
253
293
|
// packages/web/oxlint.config.ts
|
|
254
294
|
import rootConfig from '../../oxlint.config.ts'
|
|
255
|
-
import { reactVite, tanstackRouter, vitest, tailwind } from '@hooshid/ox-config/lint'
|
|
295
|
+
import { reactVite, tanstackRouter, tanstackStart, vitest, tailwind } from '@hooshid/ox-config/lint'
|
|
256
296
|
import { defineConfig } from 'oxlint'
|
|
257
297
|
|
|
258
298
|
export default defineConfig({
|
|
259
|
-
extends: [rootConfig, reactVite(), tanstackRouter(), vitest(), tailwind()],
|
|
299
|
+
extends: [rootConfig, reactVite(), tanstackRouter(), tanstackStart(), vitest(), tailwind()],
|
|
260
300
|
})
|
|
261
301
|
```
|
|
262
302
|
|
|
@@ -272,7 +312,7 @@ export default defineConfig({
|
|
|
272
312
|
```
|
|
273
313
|
|
|
274
314
|
> [!TIP]
|
|
275
|
-
> Packages without their own `oxlint.config.ts` automatically use the root config
|
|
315
|
+
> Packages without their own `oxlint.config.ts` automatically use the root config.
|
|
276
316
|
|
|
277
317
|
> [!WARNING]
|
|
278
318
|
> Passing `-c` or `--config` explicitly on the CLI **disables** nested config lookup. Let oxlint auto-detect configs by running without `-c`.
|
|
@@ -300,6 +340,7 @@ export default defineConfig({ ...format() })
|
|
|
300
340
|
| `endOfLine` | `lf` |
|
|
301
341
|
| Import sorting | Grouped with newlines |
|
|
302
342
|
| Package.json sorting | Enabled |
|
|
343
|
+
| Ignore patterns | Build outputs, lockfiles, generated files |
|
|
303
344
|
|
|
304
345
|
### Override
|
|
305
346
|
|
|
@@ -359,6 +400,51 @@ Most modern editors support EditorConfig natively or via a plugin. This ensures
|
|
|
359
400
|
}
|
|
360
401
|
```
|
|
361
402
|
|
|
403
|
+
## Full-stack example (TanStack Start + NestJS)
|
|
404
|
+
|
|
405
|
+
```ts
|
|
406
|
+
// apps/web/oxlint.config.ts
|
|
407
|
+
import rootConfig from '../../oxlint.config.ts'
|
|
408
|
+
import { reactVite, tanstackRouter, tanstackStart, tailwind, vitest } from '@hooshid/ox-config/lint'
|
|
409
|
+
import { defineConfig } from 'oxlint'
|
|
410
|
+
|
|
411
|
+
export default defineConfig({
|
|
412
|
+
extends: [
|
|
413
|
+
rootConfig,
|
|
414
|
+
reactVite(),
|
|
415
|
+
tanstackRouter(),
|
|
416
|
+
tanstackStart(),
|
|
417
|
+
tailwind(),
|
|
418
|
+
vitest(),
|
|
419
|
+
],
|
|
420
|
+
})
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
```ts
|
|
424
|
+
// apps/api/oxlint.config.ts
|
|
425
|
+
import rootConfig from '../../oxlint.config.ts'
|
|
426
|
+
import { node, promise, nestjs, drizzle, vitest } from '@hooshid/ox-config/lint'
|
|
427
|
+
import { defineConfig } from 'oxlint'
|
|
428
|
+
|
|
429
|
+
export default defineConfig({
|
|
430
|
+
extends: [rootConfig, node(), promise(), nestjs(), drizzle(), vitest()],
|
|
431
|
+
overrides: [
|
|
432
|
+
{
|
|
433
|
+
files: ['**/*.{ts,mts,cts,tsx}'],
|
|
434
|
+
rules: {
|
|
435
|
+
'typescript/consistent-type-imports': 'off',
|
|
436
|
+
'typescript/no-extraneous-class': ['error', { allowWithDecorator: true }],
|
|
437
|
+
},
|
|
438
|
+
},
|
|
439
|
+
],
|
|
440
|
+
})
|
|
441
|
+
```
|
|
442
|
+
|
|
362
443
|
## License
|
|
363
444
|
|
|
364
|
-
MIT
|
|
445
|
+
MIT
|
|
446
|
+
|
|
447
|
+
## Sources
|
|
448
|
+
|
|
449
|
+
1. [Oxlint: Writing JS Plugins](https://oxc.rs/docs/guide/usage/linter/writing-js-plugins.html)
|
|
450
|
+
3. [infra-code source](https://github.com/oNo500/infra-code/tree/master/packages/code-quality)
|
package/dist/lint.d.mts
CHANGED
|
@@ -34,17 +34,32 @@ export declare function depend(overrides?: Partial<OxlintConfig>): OxlintConfig;
|
|
|
34
34
|
export declare function node(overrides?: Partial<OxlintConfig>): OxlintConfig;
|
|
35
35
|
/** Promise lint preset — native promise plugin. */
|
|
36
36
|
export declare function promise(overrides?: Partial<OxlintConfig>): OxlintConfig;
|
|
37
|
-
/** React lint preset —
|
|
37
|
+
/** React lint preset — core React + Hooks rules. */
|
|
38
38
|
export declare function react(overrides?: Partial<OxlintConfig>): OxlintConfig;
|
|
39
|
-
/** React + react-refresh preset (for Vite
|
|
39
|
+
/** React + react-refresh preset (for Vite, TanStack Start, etc.). */
|
|
40
40
|
export declare function reactVite(overrides?: Partial<OxlintConfig>): OxlintConfig;
|
|
41
41
|
/** Next.js lint preset — enables native nextjs plugin. */
|
|
42
42
|
export declare function nextjs(overrides?: Partial<OxlintConfig>): OxlintConfig;
|
|
43
43
|
/**
|
|
44
|
-
* TanStack Router lint preset
|
|
45
|
-
*
|
|
44
|
+
* TanStack Router lint preset.
|
|
45
|
+
*
|
|
46
|
+
* Loads @tanstack/eslint-plugin-router and applies conventions specific
|
|
47
|
+
* to file-based routing setups.
|
|
48
|
+
*
|
|
49
|
+
* Rules provided by the plugin:
|
|
50
|
+
* - `route-param-names` — validates `$param` names match between route path and params
|
|
51
|
+
* - `create-route-property-order` — enforces consistent property order in `createRoute()`
|
|
46
52
|
*/
|
|
47
53
|
export declare function tanstackRouter(overrides?: Partial<OxlintConfig>): OxlintConfig;
|
|
54
|
+
/**
|
|
55
|
+
* TanStack Start lint preset.
|
|
56
|
+
*
|
|
57
|
+
* Adds rules for server functions, client/server boundaries, and Start
|
|
58
|
+
* conventions. This preset assumes `reactVite()` and `tanstackRouter()`
|
|
59
|
+
* are already in the `extends` array.
|
|
60
|
+
*
|
|
61
|
+
*/
|
|
62
|
+
export declare function tanstackStart(overrides?: Partial<OxlintConfig>): OxlintConfig;
|
|
48
63
|
/** Accessibility lint preset — enables native jsx-a11y plugin. */
|
|
49
64
|
export declare function a11y(overrides?: Partial<OxlintConfig>): OxlintConfig;
|
|
50
65
|
/** JSDoc lint preset — enables native jsdoc plugin. */
|
package/dist/lint.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lint.d.mts","names":[],"sources":["../src/utils.ts","../src/lint.ts"],"mappings":";;;qBAgDa;;qBAGA;;qBAGA;;qBAGA;;qBAGA;;qBAMA;;qBAGA;;;;;;;wBCsCG,KAAK,YAAY,QAAQ,gBAAgB;;;;;
|
|
1
|
+
{"version":3,"file":"lint.d.mts","names":[],"sources":["../src/utils.ts","../src/lint.ts"],"mappings":";;;qBAgDa;;qBAGA;;qBAGA;;qBAGA;;qBAGA;;qBAMA;;qBAGA;;;;;;;wBCsCG,KAAK,YAAY,QAAQ,gBAAgB;;;;;wBA6HzC,UAAU,YAAY,QAAQ,gBAAgB;;wBAa9C,QAAQ,YAAY,QAAQ,gBAAgB;;wBA2C5C,OAAO,YAAY,QAAQ,gBAAgB;;wBAwB3C,KAAK,YAAY,QAAQ,gBAAgB;;wBAazC,QAAQ,YAAY,QAAQ,gBAAgB;;wBAuE5C,MAAM,YAAY,QAAQ,gBAAgB;;wBAW1C,UAAU,YAAY,QAAQ,gBAAgB;;wBAe9C,OAAO,YAAY,QAAQ,gBAAgB;;;;;;;;;;;wBAc3C,eAAe,YAAY,QAAQ,gBAAgB;;;;;;;;;wBAwEnD,cAAc,YAAY,QAAQ,gBAAgB;;wBAkGlD,KAAK,YAAY,QAAQ,gBAAgB;;wBAKzC,MAAM,YAAY,QAAQ,gBAAgB;UAQhD,sBAAsB,QAAQ;;EAEtC;;;wBAIc,OAAO,UAAU,gBAAgB;UAkDvC,wBAAwB,QAAQ;EACxC;EACA;;EAEA;;;wBAIc,SAAS,UAAS,kBAAuB;;;;;wBA6BzC,OAAO,YAAY,QAAQ,gBAAgB;;wBA+B3C,QAAQ,YAAY,QAAQ,gBAAgB"}
|
package/dist/lint.mjs
CHANGED
|
@@ -150,6 +150,8 @@ function base(overrides) {
|
|
|
150
150
|
"typescript/consistent-type-definitions": "off",
|
|
151
151
|
"typescript/consistent-type-imports": "error",
|
|
152
152
|
"no-unused-vars": "off",
|
|
153
|
+
"no-useless-rename": "error",
|
|
154
|
+
"uninvoked-array-callback": "error",
|
|
153
155
|
"typescript/array-type": "error",
|
|
154
156
|
"typescript/consistent-generic-constructors": "error",
|
|
155
157
|
"typescript/consistent-indexed-object-style": "error",
|
|
@@ -157,6 +159,10 @@ function base(overrides) {
|
|
|
157
159
|
"typescript/ban-tslint-comment": "error",
|
|
158
160
|
"typescript/no-empty-interface": "error",
|
|
159
161
|
"typescript/no-inferrable-types": "error",
|
|
162
|
+
"typescript/no-misused-new": "error",
|
|
163
|
+
"typescript/no-unnecessary-parameter-property-assignment": "error",
|
|
164
|
+
"typescript/no-useless-empty-export": "error",
|
|
165
|
+
"typescript/no-deprecated": "warn",
|
|
160
166
|
"typescript/prefer-for-of": "error",
|
|
161
167
|
"typescript/prefer-function-type": "error",
|
|
162
168
|
"typescript/unified-signatures": "error"
|
|
@@ -223,7 +229,6 @@ function unicorn(overrides) {
|
|
|
223
229
|
"unicorn/catch-error-name": "error",
|
|
224
230
|
"unicorn/error-message": "error",
|
|
225
231
|
"unicorn/prefer-optional-catch-binding": "error",
|
|
226
|
-
"unicorn/no-zero-fractions": "error",
|
|
227
232
|
"unicorn/number-literal-case": "error",
|
|
228
233
|
"unicorn/numeric-separators-style": "error",
|
|
229
234
|
"unicorn/prefer-includes": "error",
|
|
@@ -232,9 +237,22 @@ function unicorn(overrides) {
|
|
|
232
237
|
"unicorn/prefer-string-trim-start-end": "error",
|
|
233
238
|
"unicorn/prefer-structured-clone": "error",
|
|
234
239
|
"unicorn/prefer-default-parameters": "error",
|
|
235
|
-
"unicorn/
|
|
240
|
+
"unicorn/prefer-string-starts-ends-with": "error",
|
|
241
|
+
"unicorn/prefer-set-size": "error",
|
|
236
242
|
"unicorn/throw-new-error": "error",
|
|
237
|
-
"unicorn/require-array-join-separator": "error"
|
|
243
|
+
"unicorn/require-array-join-separator": "error",
|
|
244
|
+
"unicorn/no-zero-fractions": "error",
|
|
245
|
+
"unicorn/no-console-spaces": "error",
|
|
246
|
+
"unicorn/no-useless-spread": "error",
|
|
247
|
+
"unicorn/no-useless-fallback-in-spread": "error",
|
|
248
|
+
"unicorn/no-unnecessary-await": "error",
|
|
249
|
+
"unicorn/no-useless-length-check": "error",
|
|
250
|
+
"unicorn/no-single-promise-in-promise-methods": "error",
|
|
251
|
+
"unicorn/no-await-in-promise-methods": "error",
|
|
252
|
+
"unicorn/no-invalid-remove-event-listener": "error",
|
|
253
|
+
"unicorn/no-invalid-fetch-options": "error",
|
|
254
|
+
"unicorn/no-new-array": "error",
|
|
255
|
+
"unicorn/no-empty-file": "warn"
|
|
238
256
|
}
|
|
239
257
|
}, overrides);
|
|
240
258
|
}
|
|
@@ -255,38 +273,83 @@ function depend(overrides) {
|
|
|
255
273
|
}
|
|
256
274
|
/** Node.js lint preset — native node plugin. */
|
|
257
275
|
function node(overrides) {
|
|
258
|
-
return preset({
|
|
276
|
+
return preset({
|
|
277
|
+
plugins: ["node"],
|
|
278
|
+
rules: { "unicorn/prefer-node-protocol": "error" }
|
|
279
|
+
}, overrides);
|
|
259
280
|
}
|
|
260
281
|
/** Promise lint preset — native promise plugin. */
|
|
261
282
|
function promise(overrides) {
|
|
262
283
|
return preset({ plugins: ["promise"] }, overrides);
|
|
263
284
|
}
|
|
264
|
-
const
|
|
265
|
-
"react/
|
|
266
|
-
"react/
|
|
267
|
-
"react/jsx-
|
|
268
|
-
"react/jsx-
|
|
269
|
-
"react/
|
|
270
|
-
"react/
|
|
285
|
+
const reactRules = {
|
|
286
|
+
"react/rules-of-hooks": "error",
|
|
287
|
+
"react/exhaustive-deps": "warn",
|
|
288
|
+
"react/jsx-key": "error",
|
|
289
|
+
"react/jsx-no-duplicate-props": "error",
|
|
290
|
+
"react/jsx-no-undef": "error",
|
|
291
|
+
"react/jsx-no-target-blank": ["error", { allowReferrer: false }],
|
|
292
|
+
"react/jsx-no-comment-textnodes": "error",
|
|
293
|
+
"react/jsx-no-script-url": "error",
|
|
294
|
+
"react/jsx-props-no-spread-multi": "error",
|
|
295
|
+
"react/jsx-no-useless-fragment": "warn",
|
|
296
|
+
"react/jsx-boolean-value": ["error", "never"],
|
|
297
|
+
"react/jsx-curly-brace-presence": ["error", {
|
|
298
|
+
props: "never",
|
|
299
|
+
children: "never"
|
|
300
|
+
}],
|
|
301
|
+
"react/jsx-fragments": ["error", "syntax"],
|
|
302
|
+
"react/jsx-pascal-case": ["error", {
|
|
303
|
+
allowAllCaps: false,
|
|
304
|
+
allowNamespace: true
|
|
305
|
+
}],
|
|
306
|
+
"react/self-closing-comp": ["error", {
|
|
307
|
+
component: true,
|
|
308
|
+
html: true
|
|
309
|
+
}],
|
|
310
|
+
"react/hook-use-state": "error",
|
|
311
|
+
"react/no-children-prop": "error",
|
|
312
|
+
"react/no-danger-with-children": "error",
|
|
313
|
+
"react/no-direct-mutation-state": "error",
|
|
314
|
+
"react/no-find-dom-node": "error",
|
|
315
|
+
"react/no-is-mounted": "error",
|
|
316
|
+
"react/no-render-return-value": "error",
|
|
317
|
+
"react/no-string-refs": "error",
|
|
318
|
+
"react/no-unknown-property": "error",
|
|
319
|
+
"react/no-unsafe": "error",
|
|
320
|
+
"react/require-render-return": "error",
|
|
321
|
+
"react/void-dom-elements-no-children": "error",
|
|
322
|
+
"react/no-set-state": "error",
|
|
323
|
+
"react/no-did-mount-set-state": "error",
|
|
324
|
+
"react/no-did-update-set-state": "error",
|
|
325
|
+
"react/no-will-update-set-state": "error",
|
|
326
|
+
"react/no-this-in-sfc": "error",
|
|
327
|
+
"react/forward-ref-uses-ref": "error",
|
|
328
|
+
"react/no-unstable-nested-components": "warn",
|
|
329
|
+
"react/iframe-missing-sandbox": "error",
|
|
330
|
+
"react/set-state-in-render": "error",
|
|
331
|
+
"react/error-boundaries": "error",
|
|
332
|
+
"react/jsx-no-constructed-context-values": "warn",
|
|
333
|
+
"react/no-array-index-key": "warn",
|
|
334
|
+
"react/style-prop-object": "error",
|
|
335
|
+
"react/react-in-jsx-scope": "off",
|
|
336
|
+
"react/no-unescaped-entities": "off"
|
|
271
337
|
};
|
|
272
|
-
/** React lint preset —
|
|
338
|
+
/** React lint preset — core React + Hooks rules. */
|
|
273
339
|
function react(overrides) {
|
|
274
340
|
return preset({
|
|
275
341
|
plugins: ["react"],
|
|
276
|
-
rules:
|
|
277
|
-
"react/react-in-jsx-scope": "off",
|
|
278
|
-
...reactStyleRules
|
|
279
|
-
}
|
|
342
|
+
rules: reactRules
|
|
280
343
|
}, overrides);
|
|
281
344
|
}
|
|
282
|
-
/** React + react-refresh preset (for Vite
|
|
345
|
+
/** React + react-refresh preset (for Vite, TanStack Start, etc.). */
|
|
283
346
|
function reactVite(overrides) {
|
|
284
347
|
return preset({
|
|
285
348
|
plugins: ["react"],
|
|
286
349
|
jsPlugins: resolvePlugins(["eslint-plugin-react-refresh"]),
|
|
287
350
|
rules: {
|
|
288
|
-
|
|
289
|
-
|
|
351
|
+
...reactRules,
|
|
352
|
+
"react-refresh/only-export-components": ["warn", { allowConstantExport: true }]
|
|
290
353
|
}
|
|
291
354
|
}, overrides);
|
|
292
355
|
}
|
|
@@ -295,8 +358,14 @@ function nextjs(overrides) {
|
|
|
295
358
|
return preset({ plugins: ["nextjs"] }, overrides);
|
|
296
359
|
}
|
|
297
360
|
/**
|
|
298
|
-
* TanStack Router lint preset
|
|
299
|
-
*
|
|
361
|
+
* TanStack Router lint preset.
|
|
362
|
+
*
|
|
363
|
+
* Loads @tanstack/eslint-plugin-router and applies conventions specific
|
|
364
|
+
* to file-based routing setups.
|
|
365
|
+
*
|
|
366
|
+
* Rules provided by the plugin:
|
|
367
|
+
* - `route-param-names` — validates `$param` names match between route path and params
|
|
368
|
+
* - `create-route-property-order` — enforces consistent property order in `createRoute()`
|
|
300
369
|
*/
|
|
301
370
|
function tanstackRouter(overrides) {
|
|
302
371
|
return preset({
|
|
@@ -306,8 +375,115 @@ function tanstackRouter(overrides) {
|
|
|
306
375
|
}]),
|
|
307
376
|
rules: {
|
|
308
377
|
"tanstack-router/route-param-names": "error",
|
|
309
|
-
"tanstack-router/create-route-property-order": "error"
|
|
310
|
-
|
|
378
|
+
"tanstack-router/create-route-property-order": "error",
|
|
379
|
+
"import/no-default-export": "off"
|
|
380
|
+
},
|
|
381
|
+
overrides: [
|
|
382
|
+
{
|
|
383
|
+
files: ["**/routes/**/*.{ts,tsx}"],
|
|
384
|
+
rules: { "no-restricted-globals": ["error", {
|
|
385
|
+
name: "window",
|
|
386
|
+
message: "Use useRouter() or useNavigate() instead of window in route files."
|
|
387
|
+
}] }
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
files: ["**/routes/**/*.{ts,tsx}"],
|
|
391
|
+
plugins: ["unicorn"],
|
|
392
|
+
rules: { "unicorn/filename-case": ["error", {
|
|
393
|
+
case: "kebabCase",
|
|
394
|
+
ignore: [
|
|
395
|
+
"\\$",
|
|
396
|
+
"^_",
|
|
397
|
+
"\\."
|
|
398
|
+
]
|
|
399
|
+
}] }
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
files: ["**/routes/__root.tsx"],
|
|
403
|
+
rules: { "no-restricted-globals": "off" }
|
|
404
|
+
}
|
|
405
|
+
]
|
|
406
|
+
}, overrides);
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* TanStack Start lint preset.
|
|
410
|
+
*
|
|
411
|
+
* Adds rules for server functions, client/server boundaries, and Start
|
|
412
|
+
* conventions. This preset assumes `reactVite()` and `tanstackRouter()`
|
|
413
|
+
* are already in the `extends` array.
|
|
414
|
+
*
|
|
415
|
+
*/
|
|
416
|
+
function tanstackStart(overrides) {
|
|
417
|
+
return preset({
|
|
418
|
+
rules: {
|
|
419
|
+
"no-async-promise-executor": "error",
|
|
420
|
+
"no-await-in-loop": "off",
|
|
421
|
+
"import/no-nodejs-modules": "error",
|
|
422
|
+
"consistent-return": "error",
|
|
423
|
+
"no-empty-function": "warn"
|
|
424
|
+
},
|
|
425
|
+
overrides: [
|
|
426
|
+
{
|
|
427
|
+
files: [
|
|
428
|
+
"**/*.server.{ts,tsx}",
|
|
429
|
+
"**/server/**/*.{ts,tsx}",
|
|
430
|
+
"**/server-fns/**/*.{ts,tsx}"
|
|
431
|
+
],
|
|
432
|
+
rules: {
|
|
433
|
+
"import/no-nodejs-modules": "off",
|
|
434
|
+
"no-console": "off"
|
|
435
|
+
}
|
|
436
|
+
},
|
|
437
|
+
{
|
|
438
|
+
files: ["**/*.client.{ts,tsx}", "**/components/**/*.{ts,tsx}"],
|
|
439
|
+
rules: { "import/no-nodejs-modules": "error" }
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
files: ["**/routes/**/*.{ts,tsx}"],
|
|
443
|
+
rules: { "no-restricted-syntax": ["warn", {
|
|
444
|
+
selector: "CallExpression[callee.name=\"fetch\"]",
|
|
445
|
+
message: "Prefer a loader or server function over direct fetch in route files."
|
|
446
|
+
}] }
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
files: ["**/*.server.{ts,tsx}", "**/*.client.{ts,tsx}"],
|
|
450
|
+
plugins: ["unicorn"],
|
|
451
|
+
rules: { "unicorn/filename-case": ["error", {
|
|
452
|
+
case: "kebabCase",
|
|
453
|
+
ignore: ["\\."]
|
|
454
|
+
}] }
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
files: [
|
|
458
|
+
"**/*.server.{ts,tsx}",
|
|
459
|
+
"**/server/**/*.{ts,tsx}",
|
|
460
|
+
"**/server-fns/**/*.{ts,tsx}"
|
|
461
|
+
],
|
|
462
|
+
rules: {
|
|
463
|
+
"import/no-nodejs-modules": "off",
|
|
464
|
+
"no-console": "off",
|
|
465
|
+
"no-restricted-globals": [
|
|
466
|
+
"error",
|
|
467
|
+
{
|
|
468
|
+
name: "window",
|
|
469
|
+
message: "Not available in server context. Use a server function or guard with typeof window."
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
name: "document",
|
|
473
|
+
message: "Not available in server context."
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
name: "localStorage",
|
|
477
|
+
message: "Not available in server context."
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
name: "sessionStorage",
|
|
481
|
+
message: "Not available in server context."
|
|
482
|
+
}
|
|
483
|
+
]
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
]
|
|
311
487
|
}, overrides);
|
|
312
488
|
}
|
|
313
489
|
/** Accessibility lint preset — enables native jsx-a11y plugin. */
|
|
@@ -346,6 +522,14 @@ function vitest(options) {
|
|
|
346
522
|
"vitest/no-disabled-tests": isInEditorEnv() ? "warn" : "error",
|
|
347
523
|
"vitest/no-focused-tests": isInEditorEnv() ? "warn" : "error",
|
|
348
524
|
"vitest/require-mock-type-parameters": "off",
|
|
525
|
+
"vitest/prefer-called-once": "error",
|
|
526
|
+
"vitest/prefer-called-times": "error",
|
|
527
|
+
"vitest/prefer-expect-type-of": "error",
|
|
528
|
+
"vitest/prefer-to-be-object": "error",
|
|
529
|
+
"vitest/hoisted-apis-on-top": "error",
|
|
530
|
+
"vitest/require-awaited-expect-poll": "error",
|
|
531
|
+
"vitest/no-conditional-tests": "error",
|
|
532
|
+
"vitest/consistent-vitest-vi": "error",
|
|
349
533
|
"no-console": "off",
|
|
350
534
|
"unicorn/no-null": "off",
|
|
351
535
|
"typescript/ban-ts-comment": "off"
|
|
@@ -410,6 +594,6 @@ function drizzle(overrides) {
|
|
|
410
594
|
}, overrides);
|
|
411
595
|
}
|
|
412
596
|
//#endregion
|
|
413
|
-
export { GLOB_JS, GLOB_JSON, GLOB_JSX, GLOB_MARKDOWN, GLOB_SRC, GLOB_TESTS, GLOB_TS, a11y, base, defineConfig, depend, drizzle, jsdoc, nestjs, nextjs, node, promise, react, reactVite, tailwind, tanstackRouter, typeAware, unicorn, vitest };
|
|
597
|
+
export { GLOB_JS, GLOB_JSON, GLOB_JSX, GLOB_MARKDOWN, GLOB_SRC, GLOB_TESTS, GLOB_TS, a11y, base, defineConfig, depend, drizzle, jsdoc, nestjs, nextjs, node, promise, react, reactVite, tailwind, tanstackRouter, tanstackStart, typeAware, unicorn, vitest };
|
|
414
598
|
|
|
415
599
|
//# sourceMappingURL=lint.mjs.map
|
package/dist/lint.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lint.mjs","names":["defineConfig"],"sources":["../src/utils.ts","../src/lint.ts"],"sourcesContent":["/**\n * Utility functions and constants\n */\n\n// ============================================================================\n// Environment detection\n// ============================================================================\n\nconst env = process.env\n\nfunction isCI(): boolean {\n return !!(\n env['CI'] ||\n env['CONTINUOUS_INTEGRATION'] ||\n env['BUILD_ID'] ||\n env['BUILD_NUMBER'] ||\n env['RUN_ID']\n )\n}\n\nfunction isInGitHooksOrLintStaged(): boolean {\n return (\n !!(env['GIT_PARAMS'] || env['VSCODE_GIT_COMMAND']) ||\n env['npm_lifecycle_script']?.startsWith('lint-staged') === true\n )\n}\n\n/**\n * Detects whether running in an editor environment.\n * Returns false in CI, Git Hooks, or lint-staged.\n */\nexport function isInEditorEnv(): boolean {\n if (isCI()) return false\n if (isInGitHooksOrLintStaged()) return false\n return !!(\n env['VSCODE_PID'] ||\n env['VSCODE_CWD'] ||\n env['JETBRAINS_IDE'] ||\n env['VIM'] ||\n env['NVIM']\n )\n}\n\n// ============================================================================\n// File matching patterns (Globs)\n// ============================================================================\n\n/** All JS/TS source files */\nexport const GLOB_SRC = '**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx}'\n\n/** JavaScript files only */\nexport const GLOB_JS = '**/*.{js,mjs,cjs,jsx}'\n\n/** TypeScript files only */\nexport const GLOB_TS = '**/*.{ts,mts,cts,tsx}'\n\n/** JSX/TSX files (React-related) */\nexport const GLOB_JSX = '**/*.{jsx,tsx}'\n\n/** Test files */\nexport const GLOB_TESTS: string[] = [\n '**/*.{test,spec}.{js,mjs,cjs,jsx,ts,mts,cts,tsx}',\n '**/__tests__/**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx}',\n]\n\n/** JSON files */\nexport const GLOB_JSON = '**/*.json'\n\n/** Markdown files */\nexport const GLOB_MARKDOWN = '**/*.md'\n","/**\n * Oxlint lint presets.\n *\n * Every preset is a function that accepts optional overrides, merged via `defu`.\n * User overrides take priority over preset defaults.\n *\n * @example\n * ```ts\n * // oxlint.config.ts\n * import { base, unicorn, react, tailwind, vitest } from '@hooshid/ox-config/lint'\n * import { defineConfig } from 'oxlint'\n *\n * export default defineConfig({\n * extends: [base(), unicorn(), react(), tailwind(), vitest()],\n * })\n * ```\n */\nimport { existsSync, readFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport path from 'node:path'\n\nimport { defu } from 'defu'\nimport { defineConfig } from 'oxlint'\n\nimport { GLOB_JSX, GLOB_SRC, GLOB_TESTS, GLOB_TS, isInEditorEnv } from './utils'\n\nimport type { ExternalPluginEntry, OxlintConfig } from 'oxlint'\n\nconst require = createRequire(import.meta.url)\n\n// ============================================================================\n// Ignore patterns\n// ============================================================================\n\nconst DEFAULT_IGNORES: string[] = [\n '**/node_modules/**',\n '**/dist/**',\n '**/build/**',\n '**/out/**',\n '**/.next/**',\n '**/.cache/**',\n '**/.turbo/**',\n '**/.output/**',\n '**/.nitro/**',\n '**/.tanstack/**',\n '**/.vinxi/**',\n '**/.git/**',\n '**/.svn/**',\n '**/.hg/**',\n '**/.pnp.*',\n '**/public/**',\n '**/*.d.ts',\n]\n\nfunction loadGitignorePatterns(): string[] {\n const gitignoreFile = path.resolve(process.cwd(), '.gitignore')\n if (!existsSync(gitignoreFile)) return []\n\n return readFileSync(gitignoreFile, 'utf8')\n .split(/\\r?\\n/u)\n .map((line) => line.trim())\n .filter((line) => line && !line.startsWith('#'))\n .map((pattern) => {\n let p = pattern.trimEnd()\n if (p.startsWith('/')) p = p.slice(1)\n if (!p.includes('/') && !p.includes('*')) p = `**/${p}`\n if (p.endsWith('/')) p = `${p}**`\n return p\n })\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction preset(defaults: OxlintConfig, overrides?: Partial<OxlintConfig>): OxlintConfig {\n return defineConfig(defu(overrides ?? {}, defaults))\n}\n\n/**\n * Resolve a jsPlugin package name to its absolute path.\n * This allows oxlint to load plugins directly without relying on\n * Node module resolution from the consumer's CWD — fixes pnpm strict mode.\n */\nfunction resolvePlugin(name: string): string {\n try {\n return require.resolve(name)\n } catch {\n return name\n }\n}\n\nfunction resolvePlugins(plugins: ExternalPluginEntry[]): ExternalPluginEntry[] {\n return plugins.map((p) => {\n if (typeof p === 'string') return resolvePlugin(p)\n return { ...p, specifier: resolvePlugin(p.specifier) }\n })\n}\n\n// ============================================================================\n// Core\n// ============================================================================\n\n/**\n * Base lint preset — TypeScript + Import plugins, categories, env, ignores.\n * Always include first.\n */\nexport function base(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset(\n {\n plugins: ['typescript', 'import'],\n categories: {\n correctness: 'error',\n suspicious: 'error',\n perf: 'error',\n pedantic: 'warn',\n },\n rules: {\n // Shape-only check; necessary in interactive CLI and sequential workflows.\n 'no-await-in-loop': 'off',\n // High-noise; readonly intent better expressed via `const generics` or call-site.\n 'typescript/prefer-readonly-parameter-types': 'off',\n // Turns `if (str)` into ceremony for marginal safety gain.\n 'typescript/strict-boolean-expressions': 'off',\n // File/function size is a reviewer call, not a lint rule.\n 'max-lines': 'off',\n 'max-lines-per-function': 'off',\n // Misfires on `/* @__PURE__ */` and similar bundler annotations.\n 'no-inline-comments': 'off',\n // TODO/FIXME triage belongs in the issue tracker.\n 'no-warning-comments': 'off',\n // Default cap of 10 is too tight for modern React/Next component files.\n 'import/max-dependencies': 'off',\n // Modern React renders apostrophes correctly; legacy noise.\n 'react/no-unescaped-entities': 'off',\n // Misfires on Next.js Server Actions and placeholder async functions.\n 'require-await': 'off',\n },\n env: {\n browser: true,\n node: true,\n es2026: true,\n builtin: true,\n },\n ignorePatterns: [...DEFAULT_IGNORES, ...loadGitignorePatterns()],\n overrides: [\n {\n files: [GLOB_TS],\n rules: {\n // Only list rules that DIFFER from categories defaults\n 'typescript/consistent-type-definitions': 'off',\n 'typescript/consistent-type-imports': 'error',\n 'no-unused-vars': 'off',\n 'typescript/array-type': 'error',\n 'typescript/consistent-generic-constructors': 'error',\n 'typescript/consistent-indexed-object-style': 'error',\n 'typescript/consistent-type-assertions': 'error',\n 'typescript/ban-tslint-comment': 'error',\n 'typescript/no-empty-interface': 'error',\n 'typescript/no-inferrable-types': 'error',\n 'typescript/prefer-for-of': 'error',\n 'typescript/prefer-function-type': 'error',\n 'typescript/unified-signatures': 'error',\n },\n },\n {\n files: [GLOB_SRC],\n rules: {\n // CSS/style side-effect imports are standard in all frontend frameworks\n 'import/no-unassigned-import': 'off',\n 'import/no-relative-parent-imports': 'error',\n 'import/first': 'error',\n 'import/no-duplicates': 'error',\n 'import/consistent-type-specifier-style': 'error',\n 'import/no-mutable-exports': 'error',\n 'import/no-named-default': 'error',\n },\n },\n {\n // Config files commonly import shared roots via `../../config.ts`\n // in monorepos — intended pattern, not a smell.\n files: ['**/*.config.{ts,mts,cts,js,mjs,cjs}'],\n rules: {\n 'import/no-relative-parent-imports': 'off',\n },\n },\n {\n // Test files use mock setup patterns (vi.mock/vi.hoisted before imports),\n // explicit mock signatures, and fixture data that\n // legitimately violate strict style/safety rules.\n files: GLOB_TESTS,\n rules: {\n // Mock setup pattern breaks standard import ordering\n 'import/first': 'off',\n 'import/no-duplicates': 'off',\n 'import/consistent-type-specifier-style': 'off',\n // Mock signatures need explicit values / async without await\n 'unicorn/no-useless-undefined': 'off',\n 'typescript/no-empty-function': 'off',\n // Modernization hints aren't useful in tests\n 'unicorn/prefer-string-replace-all': 'off',\n 'unicorn/no-array-callback-reference': 'off',\n 'unicorn/prefer-spread': 'off',\n // Tests deliberately use non-null assertions, magic numbers, any\n 'typescript/no-non-null-assertion': 'off',\n 'no-magic-numbers': 'off',\n 'typescript/no-explicit-any': 'off',\n 'typescript/no-unsafe-assignment': 'off',\n 'typescript/no-unsafe-call': 'off',\n 'typescript/no-unsafe-member-access': 'off',\n 'typescript/no-unsafe-return': 'off',\n 'typescript/no-unsafe-argument': 'off',\n // Index keys are fine when rendering fixture lists\n 'react/no-array-index-key': 'off',\n },\n },\n ],\n },\n overrides,\n )\n}\n\n/**\n * Type-aware lint preset — enables 59 type-aware rules via tsgolint + type checking.\n * Requires TypeScript 7.0+ and `oxlint-tsgolint` (bundled as dependency).\n */\nexport function typeAware(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset(\n {\n options: {\n typeAware: true,\n typeCheck: true,\n },\n },\n overrides,\n )\n}\n\n/** Unicorn lint preset — 100+ code quality rules. */\nexport function unicorn(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset(\n {\n plugins: ['unicorn'],\n rules: {\n 'unicorn/no-null': 'off',\n 'unicorn/filename-case': ['error', { case: 'kebabCase' }],\n 'unicorn/catch-error-name': 'error',\n 'unicorn/error-message': 'error',\n 'unicorn/prefer-optional-catch-binding': 'error',\n 'unicorn/no-zero-fractions': 'error',\n 'unicorn/number-literal-case': 'error',\n 'unicorn/numeric-separators-style': 'error',\n 'unicorn/prefer-includes': 'error',\n 'unicorn/prefer-spread': 'error',\n 'unicorn/prefer-ternary': 'error',\n 'unicorn/prefer-string-trim-start-end': 'error',\n 'unicorn/prefer-structured-clone': 'error',\n 'unicorn/prefer-default-parameters': 'error',\n 'unicorn/no-console-spaces': 'error',\n 'unicorn/throw-new-error': 'error',\n 'unicorn/require-array-join-separator': 'error',\n },\n },\n overrides,\n )\n}\n\n/** Dependency optimization — flags packages replaceable with native APIs. */\nexport function depend(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset(\n {\n jsPlugins: resolvePlugins(['eslint-plugin-depend']),\n rules: {\n 'depend/ban-dependencies': [\n 'error',\n {\n presets: ['native', 'microutilities', 'preferred'],\n modules: [],\n allowed: ['dotenv'],\n },\n ],\n },\n },\n overrides,\n )\n}\n\n// ============================================================================\n// Runtime environments\n// ============================================================================\n\n/** Node.js lint preset — native node plugin. */\nexport function node(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset({ plugins: ['node'] }, overrides)\n}\n\n/** Promise lint preset — native promise plugin. */\nexport function promise(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset({ plugins: ['promise'] }, overrides)\n}\n\n// ============================================================================\n// Frontend\n// ============================================================================\n\nconst reactStyleRules = {\n 'react/jsx-pascal-case': 'error',\n 'react/jsx-boolean-value': 'error',\n 'react/jsx-curly-brace-presence': 'error',\n 'react/jsx-fragments': 'error',\n 'react/self-closing-comp': 'error',\n 'react/hook-use-state': 'error',\n} as const\n\n/** React lint preset — enables native react + react-hooks plugin. */\nexport function react(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset(\n {\n plugins: ['react'],\n rules: {\n // Automatic JSX runtime (React 17+) does not require explicit React import\n 'react/react-in-jsx-scope': 'off',\n ...reactStyleRules,\n },\n },\n overrides,\n )\n}\n\n/** React + react-refresh preset (for Vite projects). Use instead of `react`. */\nexport function reactVite(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset(\n {\n plugins: ['react'],\n jsPlugins: resolvePlugins(['eslint-plugin-react-refresh']),\n rules: {\n 'react/react-in-jsx-scope': 'off',\n ...reactStyleRules,\n },\n },\n overrides,\n )\n}\n\n/** Next.js lint preset — enables native nextjs plugin. */\nexport function nextjs(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset({ plugins: ['nextjs'] }, overrides)\n}\n\n/**\n * TanStack Router lint preset — loads @tanstack/eslint-plugin-router.\n * Covers route-param-names and create-route-property-order.\n */\nexport function tanstackRouter(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset(\n {\n jsPlugins: resolvePlugins([\n { name: 'tanstack-router', specifier: '@tanstack/eslint-plugin-router' },\n ]),\n rules: {\n 'tanstack-router/route-param-names': 'error',\n 'tanstack-router/create-route-property-order': 'error',\n },\n },\n overrides,\n )\n}\n\n// ============================================================================\n// Quality\n// ============================================================================\n\n/** Accessibility lint preset — enables native jsx-a11y plugin. */\nexport function a11y(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset({ plugins: ['jsx-a11y'] }, overrides)\n}\n\n/** JSDoc lint preset — enables native jsdoc plugin. */\nexport function jsdoc(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset({ plugins: ['jsdoc'] }, overrides)\n}\n\n// ============================================================================\n// Testing\n// ============================================================================\n\ninterface VitestOptions extends Partial<OxlintConfig> {\n /** Test file glob patterns. Replaces the default GLOB_TESTS. */\n files?: string[]\n}\n\n/** Vitest lint preset — native vitest plugin with test-file scoping. */\nexport function vitest(options?: VitestOptions): OxlintConfig {\n const { files, ...overrides } = options ?? {}\n\n return preset(\n {\n plugins: ['vitest'],\n settings: { vitest: { typecheck: true } },\n overrides: [\n {\n files: files ?? GLOB_TESTS,\n rules: {\n 'vitest/expect-expect': 'error',\n 'vitest/no-conditional-expect': 'error',\n 'vitest/no-identical-title': 'error',\n 'vitest/no-import-node-test': 'error',\n 'vitest/no-interpolation-in-snapshots': 'error',\n 'vitest/no-mocks-import': 'error',\n 'vitest/no-standalone-expect': 'error',\n 'vitest/valid-describe-callback': 'error',\n 'vitest/valid-expect': 'error',\n 'vitest/valid-title': 'error',\n 'vitest/consistent-test-it': ['error', { fn: 'it', withinDescribe: 'it' }],\n 'vitest/prefer-hooks-in-order': 'error',\n 'vitest/prefer-lowercase-title': 'error',\n 'vitest/no-disabled-tests': isInEditorEnv() ? 'warn' : 'error',\n 'vitest/no-focused-tests': isInEditorEnv() ? 'warn' : 'error',\n 'vitest/require-mock-type-parameters': 'off',\n 'no-console': 'off',\n 'unicorn/no-null': 'off',\n 'typescript/ban-ts-comment': 'off',\n },\n },\n ],\n },\n overrides,\n )\n}\n\n// ============================================================================\n// Tailwind\n// ============================================================================\n\ninterface TailwindOptions extends Partial<OxlintConfig> {\n entryPoint?: string\n rootFontSize?: number\n /** File glob patterns for tailwind rules. @default GLOB_JSX */\n files?: string[]\n}\n\n/** Tailwind CSS lint preset — eslint-plugin-better-tailwindcss. */\nexport function tailwind(options: TailwindOptions = {}): OxlintConfig {\n const { entryPoint = 'src/styles/globals.css', rootFontSize = 16, files, ...overrides } = options\n\n return preset(\n {\n jsPlugins: resolvePlugins(['eslint-plugin-better-tailwindcss']),\n settings: { 'better-tailwindcss': { entryPoint, rootFontSize } },\n overrides: [\n {\n files: files ?? [GLOB_JSX],\n rules: {\n 'better-tailwindcss/enforce-consistent-line-wrapping': ['error', { printWidth: 0 }],\n 'better-tailwindcss/enforce-canonical-classes': 'error',\n },\n },\n ],\n },\n overrides,\n )\n}\n\n// ============================================================================\n// Backend / ORM\n// ============================================================================\n\n/**\n * NestJS lint preset — loads @darraghor/eslint-plugin-nestjs-typed via jsPlugin.\n * Covers DI validation, Swagger consistency, decorator bug prevention (19 AST rules).\n */\nexport function nestjs(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset(\n {\n jsPlugins: resolvePlugins([\n { name: 'nestjs-typed', specifier: '@darraghor/eslint-plugin-nestjs-typed' },\n ]),\n rules: {\n // DI\n 'nestjs-typed/injectable-should-be-provided': 'error',\n 'nestjs-typed/provided-injected-should-match-factory-parameters': 'error',\n 'nestjs-typed/use-injectable-provided-token': 'error',\n // Swagger\n 'nestjs-typed/api-property-matches-property-optionality': 'error',\n 'nestjs-typed/controllers-should-supply-api-tags': 'error',\n 'nestjs-typed/api-method-should-specify-api-response': 'error',\n 'nestjs-typed/api-property-returning-array-should-set-array': 'error',\n 'nestjs-typed/api-property-should-have-api-extra-models': 'error',\n 'nestjs-typed/api-operation-summary-description-capitalized': 'error',\n // Bug prevention\n 'nestjs-typed/param-decorator-name-matches-route-param': 'error',\n 'nestjs-typed/validate-nested-of-array-should-set-each': 'error',\n 'nestjs-typed/all-properties-are-whitelisted': 'error',\n 'nestjs-typed/no-duplicate-decorators': 'error',\n 'nestjs-typed/validation-pipe-should-use-forbid-unknown': 'error',\n },\n },\n overrides,\n )\n}\n\n/** Drizzle ORM lint preset — eslint-plugin-drizzle. */\nexport function drizzle(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset(\n {\n jsPlugins: resolvePlugins(['eslint-plugin-drizzle']),\n rules: {\n 'drizzle/enforce-delete-with-where': 'error',\n 'drizzle/enforce-update-with-where': 'error',\n },\n },\n overrides,\n )\n}\n\n// ============================================================================\n// Re-exports\n// ============================================================================\n\nexport { defineConfig } from 'oxlint'\nexport { GLOB_SRC, GLOB_JS, GLOB_TS, GLOB_JSX, GLOB_TESTS, GLOB_JSON, GLOB_MARKDOWN } from './utils'\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lint.mjs","names":["defineConfig"],"sources":["../src/utils.ts","../src/lint.ts"],"sourcesContent":["/**\n * Utility functions and constants\n */\n\n// ============================================================================\n// Environment detection\n// ============================================================================\n\nconst env = process.env\n\nfunction isCI(): boolean {\n return !!(\n env['CI'] ||\n env['CONTINUOUS_INTEGRATION'] ||\n env['BUILD_ID'] ||\n env['BUILD_NUMBER'] ||\n env['RUN_ID']\n )\n}\n\nfunction isInGitHooksOrLintStaged(): boolean {\n return (\n !!(env['GIT_PARAMS'] || env['VSCODE_GIT_COMMAND']) ||\n env['npm_lifecycle_script']?.startsWith('lint-staged') === true\n )\n}\n\n/**\n * Detects whether running in an editor environment.\n * Returns false in CI, Git Hooks, or lint-staged.\n */\nexport function isInEditorEnv(): boolean {\n if (isCI()) return false\n if (isInGitHooksOrLintStaged()) return false\n return !!(\n env['VSCODE_PID'] ||\n env['VSCODE_CWD'] ||\n env['JETBRAINS_IDE'] ||\n env['VIM'] ||\n env['NVIM']\n )\n}\n\n// ============================================================================\n// File matching patterns (Globs)\n// ============================================================================\n\n/** All JS/TS source files */\nexport const GLOB_SRC = '**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx}'\n\n/** JavaScript files only */\nexport const GLOB_JS = '**/*.{js,mjs,cjs,jsx}'\n\n/** TypeScript files only */\nexport const GLOB_TS = '**/*.{ts,mts,cts,tsx}'\n\n/** JSX/TSX files (React-related) */\nexport const GLOB_JSX = '**/*.{jsx,tsx}'\n\n/** Test files */\nexport const GLOB_TESTS: string[] = [\n '**/*.{test,spec}.{js,mjs,cjs,jsx,ts,mts,cts,tsx}',\n '**/__tests__/**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx}',\n]\n\n/** JSON files */\nexport const GLOB_JSON = '**/*.json'\n\n/** Markdown files */\nexport const GLOB_MARKDOWN = '**/*.md'\n","/**\n * Oxlint lint presets.\n *\n * Every preset is a function that accepts optional overrides, merged via `defu`.\n * User overrides take priority over preset defaults.\n *\n * @example\n * ```ts\n * // oxlint.config.ts\n * import { base, unicorn, react, tailwind, vitest } from '@hooshid/ox-config/lint'\n * import { defineConfig } from 'oxlint'\n *\n * export default defineConfig({\n * extends: [base(), unicorn(), react(), tailwind(), vitest()],\n * })\n * ```\n */\nimport { existsSync, readFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport path from 'node:path'\n\nimport { defu } from 'defu'\nimport { defineConfig } from 'oxlint'\n\nimport { GLOB_JSX, GLOB_SRC, GLOB_TESTS, GLOB_TS, isInEditorEnv } from './utils'\n\nimport type { ExternalPluginEntry, OxlintConfig } from 'oxlint'\n\nconst require = createRequire(import.meta.url)\n\n// ============================================================================\n// Ignore patterns\n// ============================================================================\n\nconst DEFAULT_IGNORES: string[] = [\n '**/node_modules/**',\n '**/dist/**',\n '**/build/**',\n '**/out/**',\n '**/.next/**',\n '**/.cache/**',\n '**/.turbo/**',\n '**/.output/**',\n '**/.nitro/**',\n '**/.tanstack/**',\n '**/.vinxi/**',\n '**/.git/**',\n '**/.svn/**',\n '**/.hg/**',\n '**/.pnp.*',\n '**/public/**',\n '**/*.d.ts',\n]\n\nfunction loadGitignorePatterns(): string[] {\n const gitignoreFile = path.resolve(process.cwd(), '.gitignore')\n if (!existsSync(gitignoreFile)) return []\n\n return readFileSync(gitignoreFile, 'utf8')\n .split(/\\r?\\n/u)\n .map((line) => line.trim())\n .filter((line) => line && !line.startsWith('#'))\n .map((pattern) => {\n let p = pattern.trimEnd()\n if (p.startsWith('/')) p = p.slice(1)\n if (!p.includes('/') && !p.includes('*')) p = `**/${p}`\n if (p.endsWith('/')) p = `${p}**`\n return p\n })\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction preset(defaults: OxlintConfig, overrides?: Partial<OxlintConfig>): OxlintConfig {\n return defineConfig(defu(overrides ?? {}, defaults))\n}\n\n/**\n * Resolve a jsPlugin package name to its absolute path.\n * This allows oxlint to load plugins directly without relying on\n * Node module resolution from the consumer's CWD — fixes pnpm strict mode.\n */\nfunction resolvePlugin(name: string): string {\n try {\n return require.resolve(name)\n } catch {\n return name\n }\n}\n\nfunction resolvePlugins(plugins: ExternalPluginEntry[]): ExternalPluginEntry[] {\n return plugins.map((p) => {\n if (typeof p === 'string') return resolvePlugin(p)\n return { ...p, specifier: resolvePlugin(p.specifier) }\n })\n}\n\n// ============================================================================\n// Core\n// ============================================================================\n\n/**\n * Base lint preset — TypeScript + Import plugins, categories, env, ignores.\n * Always include first.\n */\nexport function base(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset(\n {\n plugins: ['typescript', 'import'],\n categories: {\n correctness: 'error',\n suspicious: 'error',\n perf: 'error',\n pedantic: 'warn',\n },\n rules: {\n // Shape-only check; necessary in interactive CLI and sequential workflows.\n 'no-await-in-loop': 'off',\n // High-noise; readonly intent better expressed via `const generics` or call-site.\n 'typescript/prefer-readonly-parameter-types': 'off',\n // Turns `if (str)` into ceremony for marginal safety gain.\n 'typescript/strict-boolean-expressions': 'off',\n // File/function size is a reviewer call, not a lint rule.\n 'max-lines': 'off',\n 'max-lines-per-function': 'off',\n // Misfires on `/* @__PURE__ */` and similar bundler annotations.\n 'no-inline-comments': 'off',\n // TODO/FIXME triage belongs in the issue tracker.\n 'no-warning-comments': 'off',\n // Default cap of 10 is too tight for modern React/Next component files.\n 'import/max-dependencies': 'off',\n // Modern React renders apostrophes correctly; legacy noise.\n 'react/no-unescaped-entities': 'off',\n // Misfires on Next.js Server Actions and placeholder async functions.\n 'require-await': 'off',\n },\n env: {\n browser: true,\n node: true,\n es2026: true,\n builtin: true,\n },\n ignorePatterns: [...DEFAULT_IGNORES, ...loadGitignorePatterns()],\n overrides: [\n {\n files: [GLOB_TS],\n rules: {\n // Only list rules that DIFFER from categories defaults\n 'typescript/consistent-type-definitions': 'off',\n 'typescript/consistent-type-imports': 'error',\n 'no-unused-vars': 'off',\n 'no-useless-rename': 'error',\n 'uninvoked-array-callback': 'error',\n 'typescript/array-type': 'error',\n 'typescript/consistent-generic-constructors': 'error',\n 'typescript/consistent-indexed-object-style': 'error',\n 'typescript/consistent-type-assertions': 'error',\n 'typescript/ban-tslint-comment': 'error',\n 'typescript/no-empty-interface': 'error',\n 'typescript/no-inferrable-types': 'error',\n 'typescript/no-misused-new': 'error',\n 'typescript/no-unnecessary-parameter-property-assignment': 'error',\n 'typescript/no-useless-empty-export': 'error',\n 'typescript/no-deprecated': 'warn',\n 'typescript/prefer-for-of': 'error',\n 'typescript/prefer-function-type': 'error',\n 'typescript/unified-signatures': 'error',\n },\n },\n {\n files: [GLOB_SRC],\n rules: {\n // CSS/style side-effect imports are standard in all frontend frameworks\n 'import/no-unassigned-import': 'off',\n 'import/no-relative-parent-imports': 'error',\n 'import/first': 'error',\n 'import/no-duplicates': 'error',\n 'import/consistent-type-specifier-style': 'error',\n 'import/no-mutable-exports': 'error',\n 'import/no-named-default': 'error',\n },\n },\n {\n // Config files commonly import shared roots via `../../config.ts`\n // in monorepos — intended pattern, not a smell.\n files: ['**/*.config.{ts,mts,cts,js,mjs,cjs}'],\n rules: {\n 'import/no-relative-parent-imports': 'off',\n },\n },\n {\n // Test files use mock setup patterns (vi.mock/vi.hoisted before imports),\n // explicit mock signatures, and fixture data that\n // legitimately violate strict style/safety rules.\n files: GLOB_TESTS,\n rules: {\n // Mock setup pattern breaks standard import ordering\n 'import/first': 'off',\n 'import/no-duplicates': 'off',\n 'import/consistent-type-specifier-style': 'off',\n // Mock signatures need explicit values / async without await\n 'unicorn/no-useless-undefined': 'off',\n 'typescript/no-empty-function': 'off',\n // Modernization hints aren't useful in tests\n 'unicorn/prefer-string-replace-all': 'off',\n 'unicorn/no-array-callback-reference': 'off',\n 'unicorn/prefer-spread': 'off',\n // Tests deliberately use non-null assertions, magic numbers, any\n 'typescript/no-non-null-assertion': 'off',\n 'no-magic-numbers': 'off',\n 'typescript/no-explicit-any': 'off',\n 'typescript/no-unsafe-assignment': 'off',\n 'typescript/no-unsafe-call': 'off',\n 'typescript/no-unsafe-member-access': 'off',\n 'typescript/no-unsafe-return': 'off',\n 'typescript/no-unsafe-argument': 'off',\n // Index keys are fine when rendering fixture lists\n 'react/no-array-index-key': 'off',\n },\n },\n ],\n },\n overrides,\n )\n}\n\n/**\n * Type-aware lint preset — enables 59 type-aware rules via tsgolint + type checking.\n * Requires TypeScript 7.0+ and `oxlint-tsgolint` (bundled as dependency).\n */\nexport function typeAware(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset(\n {\n options: {\n typeAware: true,\n typeCheck: true,\n },\n },\n overrides,\n )\n}\n\n/** Unicorn lint preset — 100+ code quality rules. */\nexport function unicorn(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset(\n {\n plugins: ['unicorn'],\n rules: {\n 'unicorn/no-null': 'off',\n 'unicorn/filename-case': ['error', { case: 'kebabCase' }],\n 'unicorn/catch-error-name': 'error',\n 'unicorn/error-message': 'error',\n 'unicorn/prefer-optional-catch-binding': 'error',\n 'unicorn/number-literal-case': 'error',\n 'unicorn/numeric-separators-style': 'error',\n 'unicorn/prefer-includes': 'error',\n 'unicorn/prefer-spread': 'error',\n 'unicorn/prefer-ternary': 'error',\n 'unicorn/prefer-string-trim-start-end': 'error',\n 'unicorn/prefer-structured-clone': 'error',\n 'unicorn/prefer-default-parameters': 'error',\n 'unicorn/prefer-string-starts-ends-with': 'error',\n 'unicorn/prefer-set-size': 'error',\n 'unicorn/throw-new-error': 'error',\n 'unicorn/require-array-join-separator': 'error',\n\n // Correctness\n 'unicorn/no-zero-fractions': 'error',\n 'unicorn/no-console-spaces': 'error',\n 'unicorn/no-useless-spread': 'error',\n 'unicorn/no-useless-fallback-in-spread': 'error',\n 'unicorn/no-unnecessary-await': 'error',\n 'unicorn/no-useless-length-check': 'error',\n 'unicorn/no-single-promise-in-promise-methods': 'error',\n 'unicorn/no-await-in-promise-methods': 'error',\n 'unicorn/no-invalid-remove-event-listener': 'error',\n 'unicorn/no-invalid-fetch-options': 'error',\n 'unicorn/no-new-array': 'error',\n 'unicorn/no-empty-file': 'warn',\n },\n },\n overrides,\n )\n}\n\n/** Dependency optimization — flags packages replaceable with native APIs. */\nexport function depend(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset(\n {\n jsPlugins: resolvePlugins(['eslint-plugin-depend']),\n rules: {\n 'depend/ban-dependencies': [\n 'error',\n {\n presets: ['native', 'microutilities', 'preferred'],\n modules: [],\n allowed: ['dotenv'],\n },\n ],\n },\n },\n overrides,\n )\n}\n\n// ============================================================================\n// Runtime environments\n// ============================================================================\n\n/** Node.js lint preset — native node plugin. */\nexport function node(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset(\n {\n plugins: ['node'],\n rules: {\n 'unicorn/prefer-node-protocol': 'error',\n },\n },\n overrides,\n )\n}\n\n/** Promise lint preset — native promise plugin. */\nexport function promise(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset({ plugins: ['promise'] }, overrides)\n}\n\n// ============================================================================\n// Frontend\n// ============================================================================\n\nconst reactRules = {\n // Hooks (CRITICAL)\n 'react/rules-of-hooks': 'error',\n 'react/exhaustive-deps': 'warn',\n\n // JSX Correctness\n 'react/jsx-key': 'error',\n 'react/jsx-no-duplicate-props': 'error',\n 'react/jsx-no-undef': 'error',\n 'react/jsx-no-target-blank': ['error', { allowReferrer: false }],\n 'react/jsx-no-comment-textnodes': 'error',\n 'react/jsx-no-script-url': 'error',\n 'react/jsx-props-no-spread-multi': 'error',\n 'react/jsx-no-useless-fragment': 'warn',\n\n // JSX Style\n 'react/jsx-boolean-value': ['error', 'never'],\n 'react/jsx-curly-brace-presence': ['error', { props: 'never', children: 'never' }],\n 'react/jsx-fragments': ['error', 'syntax'],\n 'react/jsx-pascal-case': ['error', { allowAllCaps: false, allowNamespace: true }],\n 'react/self-closing-comp': ['error', { component: true, html: true }],\n 'react/hook-use-state': 'error',\n\n // Runtime Safety\n 'react/no-children-prop': 'error',\n 'react/no-danger-with-children': 'error',\n 'react/no-direct-mutation-state': 'error',\n 'react/no-find-dom-node': 'error',\n 'react/no-is-mounted': 'error',\n 'react/no-render-return-value': 'error',\n 'react/no-string-refs': 'error',\n 'react/no-unknown-property': 'error',\n 'react/no-unsafe': 'error',\n 'react/require-render-return': 'error',\n 'react/void-dom-elements-no-children': 'error',\n\n // Lifecycle Safety\n 'react/no-set-state': 'error',\n 'react/no-did-mount-set-state': 'error',\n 'react/no-did-update-set-state': 'error',\n 'react/no-will-update-set-state': 'error',\n 'react/no-this-in-sfc': 'error',\n\n // Refs & Boundaries\n 'react/forward-ref-uses-ref': 'error',\n 'react/no-unstable-nested-components': 'warn',\n 'react/iframe-missing-sandbox': 'error',\n 'react/set-state-in-render': 'error',\n 'react/error-boundaries': 'error',\n\n // Performance\n 'react/jsx-no-constructed-context-values': 'warn',\n 'react/no-array-index-key': 'warn',\n 'react/style-prop-object': 'error',\n\n // Modern React (17+)\n 'react/react-in-jsx-scope': 'off',\n\n // Noise Reduction\n 'react/no-unescaped-entities': 'off',\n} satisfies NonNullable<OxlintConfig['rules']>\n\n/** React lint preset — core React + Hooks rules. */\nexport function react(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset(\n {\n plugins: ['react'],\n rules: reactRules,\n },\n overrides,\n )\n}\n\n/** React + react-refresh preset (for Vite, TanStack Start, etc.). */\nexport function reactVite(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset(\n {\n plugins: ['react'],\n jsPlugins: resolvePlugins(['eslint-plugin-react-refresh']),\n rules: {\n ...reactRules,\n 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],\n },\n },\n overrides,\n )\n}\n\n/** Next.js lint preset — enables native nextjs plugin. */\nexport function nextjs(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset({ plugins: ['nextjs'] }, overrides)\n}\n\n/**\n * TanStack Router lint preset.\n *\n * Loads @tanstack/eslint-plugin-router and applies conventions specific\n * to file-based routing setups.\n *\n * Rules provided by the plugin:\n * - `route-param-names` — validates `$param` names match between route path and params\n * - `create-route-property-order` — enforces consistent property order in `createRoute()`\n */\nexport function tanstackRouter(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset(\n {\n jsPlugins: resolvePlugins([\n { name: 'tanstack-router', specifier: '@tanstack/eslint-plugin-router' },\n ]),\n rules: {\n // ─── Route Configuration ─────────────────────────\n 'tanstack-router/route-param-names': 'error',\n 'tanstack-router/create-route-property-order': 'error',\n\n // ─── Route File Conventions ──────────────────────\n // Route files should use named exports for route objects\n // (default export reserved for the component or route)\n 'import/no-default-export': 'off', // Router requires default exports\n },\n overrides: [\n {\n // Route files follow specific naming and structure\n files: ['**/routes/**/*.{ts,tsx}'],\n rules: {\n // Routes must not use window directly — use router hooks\n 'no-restricted-globals': [\n 'error',\n {\n name: 'window',\n message: 'Use useRouter() or useNavigate() instead of window in route files.',\n },\n ],\n },\n },\n {\n // ─── Filename Convention (kebab-case) ─────────\n // Applies only to files inside a `routes/` folder.\n files: ['**/routes/**/*.{ts,tsx}'],\n plugins: ['unicorn'],\n rules: {\n 'unicorn/filename-case': [\n 'error',\n {\n case: 'kebabCase',\n ignore: [\n '\\\\$', // dynamic params: $postId.tsx, posts.$postId.tsx\n '^_', // pathless/root: _auth.tsx, __root.tsx\n '\\\\.', // multi-segment: posts.index.tsx, route.lazy.tsx\n ],\n },\n ],\n },\n },\n {\n // Root route file convention\n files: ['**/routes/__root.tsx'],\n rules: {\n // __root.tsx can't use useNavigate (no router context yet)\n 'no-restricted-globals': 'off',\n },\n },\n ],\n },\n overrides,\n )\n}\n\n/**\n * TanStack Start lint preset.\n *\n * Adds rules for server functions, client/server boundaries, and Start\n * conventions. This preset assumes `reactVite()` and `tanstackRouter()`\n * are already in the `extends` array.\n *\n */\nexport function tanstackStart(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset(\n {\n rules: {\n // ─── Server Function Safety ──────────────────────\n // Prevent async promise executors (Start loaders must be sync wrapper)\n 'no-async-promise-executor': 'error',\n // Sequential awaits are fine in server functions\n 'no-await-in-loop': 'off',\n\n // ─── Client / Server Boundary ────────────────────\n // Node.js modules must not leak into client bundles\n 'import/no-nodejs-modules': 'error',\n\n // ─── Loader Conventions ──────────────────────────\n // Loaders must consistently return or throw\n 'consistent-return': 'error',\n\n // ─── Common Start Pitfalls ───────────────────────\n // Empty functions are common in placeholder server functions\n 'no-empty-function': 'warn',\n },\n\n overrides: [\n {\n // Server-only files — Node.js APIs allowed\n files: ['**/*.server.{ts,tsx}', '**/server/**/*.{ts,tsx}', '**/server-fns/**/*.{ts,tsx}'],\n rules: {\n 'import/no-nodejs-modules': 'off',\n 'no-console': 'off',\n },\n },\n {\n // Client-only files — Node.js APIs forbidden\n files: ['**/*.client.{ts,tsx}', '**/components/**/*.{ts,tsx}'],\n rules: {\n 'import/no-nodejs-modules': 'error',\n },\n },\n {\n // Route files — server-aware\n files: ['**/routes/**/*.{ts,tsx}'],\n rules: {\n // Routes use loaders, not direct fetch in component\n 'no-restricted-syntax': [\n 'warn',\n {\n selector: 'CallExpression[callee.name=\"fetch\"]',\n message: 'Prefer a loader or server function over direct fetch in route files.',\n },\n ],\n },\n },\n {\n files: ['**/*.server.{ts,tsx}', '**/*.client.{ts,tsx}'],\n plugins: ['unicorn'],\n rules: {\n 'unicorn/filename-case': [\n 'error',\n {\n case: 'kebabCase',\n ignore: [\n '\\\\.', // allow *.server.ts / *.client.ts (has dots)\n ],\n },\n ],\n },\n },\n {\n files: ['**/*.server.{ts,tsx}', '**/server/**/*.{ts,tsx}', '**/server-fns/**/*.{ts,tsx}'],\n rules: {\n 'import/no-nodejs-modules': 'off',\n 'no-console': 'off',\n // ← جدید: جلوگیری از crash توی SSR\n 'no-restricted-globals': [\n 'error',\n {\n name: 'window',\n message:\n 'Not available in server context. Use a server function or guard with typeof window.',\n },\n { name: 'document', message: 'Not available in server context.' },\n { name: 'localStorage', message: 'Not available in server context.' },\n { name: 'sessionStorage', message: 'Not available in server context.' },\n ],\n },\n },\n ],\n },\n overrides,\n )\n}\n\n// ============================================================================\n// Quality\n// ============================================================================\n\n/** Accessibility lint preset — enables native jsx-a11y plugin. */\nexport function a11y(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset({ plugins: ['jsx-a11y'] }, overrides)\n}\n\n/** JSDoc lint preset — enables native jsdoc plugin. */\nexport function jsdoc(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset({ plugins: ['jsdoc'] }, overrides)\n}\n\n// ============================================================================\n// Testing\n// ============================================================================\n\ninterface VitestOptions extends Partial<OxlintConfig> {\n /** Test file glob patterns. Replaces the default GLOB_TESTS. */\n files?: string[]\n}\n\n/** Vitest lint preset — native vitest plugin with test-file scoping. */\nexport function vitest(options?: VitestOptions): OxlintConfig {\n const { files, ...overrides } = options ?? {}\n\n return preset(\n {\n plugins: ['vitest'],\n settings: { vitest: { typecheck: true } },\n overrides: [\n {\n files: files ?? GLOB_TESTS,\n rules: {\n 'vitest/expect-expect': 'error',\n 'vitest/no-conditional-expect': 'error',\n 'vitest/no-identical-title': 'error',\n 'vitest/no-import-node-test': 'error',\n 'vitest/no-interpolation-in-snapshots': 'error',\n 'vitest/no-mocks-import': 'error',\n 'vitest/no-standalone-expect': 'error',\n 'vitest/valid-describe-callback': 'error',\n 'vitest/valid-expect': 'error',\n 'vitest/valid-title': 'error',\n 'vitest/consistent-test-it': ['error', { fn: 'it', withinDescribe: 'it' }],\n 'vitest/prefer-hooks-in-order': 'error',\n 'vitest/prefer-lowercase-title': 'error',\n 'vitest/no-disabled-tests': isInEditorEnv() ? 'warn' : 'error',\n 'vitest/no-focused-tests': isInEditorEnv() ? 'warn' : 'error',\n 'vitest/require-mock-type-parameters': 'off',\n 'vitest/prefer-called-once': 'error',\n 'vitest/prefer-called-times': 'error',\n 'vitest/prefer-expect-type-of': 'error',\n 'vitest/prefer-to-be-object': 'error',\n 'vitest/hoisted-apis-on-top': 'error',\n 'vitest/require-awaited-expect-poll': 'error',\n 'vitest/no-conditional-tests': 'error',\n 'vitest/consistent-vitest-vi': 'error',\n 'no-console': 'off',\n 'unicorn/no-null': 'off',\n 'typescript/ban-ts-comment': 'off',\n },\n },\n ],\n },\n overrides,\n )\n}\n\n// ============================================================================\n// Tailwind\n// ============================================================================\n\ninterface TailwindOptions extends Partial<OxlintConfig> {\n entryPoint?: string\n rootFontSize?: number\n /** File glob patterns for tailwind rules. @default GLOB_JSX */\n files?: string[]\n}\n\n/** Tailwind CSS lint preset — eslint-plugin-better-tailwindcss. */\nexport function tailwind(options: TailwindOptions = {}): OxlintConfig {\n const { entryPoint = 'src/styles/globals.css', rootFontSize = 16, files, ...overrides } = options\n\n return preset(\n {\n jsPlugins: resolvePlugins(['eslint-plugin-better-tailwindcss']),\n settings: { 'better-tailwindcss': { entryPoint, rootFontSize } },\n overrides: [\n {\n files: files ?? [GLOB_JSX],\n rules: {\n 'better-tailwindcss/enforce-consistent-line-wrapping': ['error', { printWidth: 0 }],\n 'better-tailwindcss/enforce-canonical-classes': 'error',\n },\n },\n ],\n },\n overrides,\n )\n}\n\n// ============================================================================\n// Backend / ORM\n// ============================================================================\n\n/**\n * NestJS lint preset — loads @darraghor/eslint-plugin-nestjs-typed via jsPlugin.\n * Covers DI validation, Swagger consistency, decorator bug prevention (19 AST rules).\n */\nexport function nestjs(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset(\n {\n jsPlugins: resolvePlugins([\n { name: 'nestjs-typed', specifier: '@darraghor/eslint-plugin-nestjs-typed' },\n ]),\n rules: {\n // DI\n 'nestjs-typed/injectable-should-be-provided': 'error',\n 'nestjs-typed/provided-injected-should-match-factory-parameters': 'error',\n 'nestjs-typed/use-injectable-provided-token': 'error',\n // Swagger\n 'nestjs-typed/api-property-matches-property-optionality': 'error',\n 'nestjs-typed/controllers-should-supply-api-tags': 'error',\n 'nestjs-typed/api-method-should-specify-api-response': 'error',\n 'nestjs-typed/api-property-returning-array-should-set-array': 'error',\n 'nestjs-typed/api-property-should-have-api-extra-models': 'error',\n 'nestjs-typed/api-operation-summary-description-capitalized': 'error',\n // Bug prevention\n 'nestjs-typed/param-decorator-name-matches-route-param': 'error',\n 'nestjs-typed/validate-nested-of-array-should-set-each': 'error',\n 'nestjs-typed/all-properties-are-whitelisted': 'error',\n 'nestjs-typed/no-duplicate-decorators': 'error',\n 'nestjs-typed/validation-pipe-should-use-forbid-unknown': 'error',\n },\n },\n overrides,\n )\n}\n\n/** Drizzle ORM lint preset — eslint-plugin-drizzle. */\nexport function drizzle(overrides?: Partial<OxlintConfig>): OxlintConfig {\n return preset(\n {\n jsPlugins: resolvePlugins(['eslint-plugin-drizzle']),\n rules: {\n 'drizzle/enforce-delete-with-where': 'error',\n 'drizzle/enforce-update-with-where': 'error',\n },\n },\n overrides,\n )\n}\n\n// ============================================================================\n// Re-exports\n// ============================================================================\n\nexport { defineConfig } from 'oxlint'\nexport { GLOB_SRC, GLOB_JS, GLOB_TS, GLOB_JSX, GLOB_TESTS, GLOB_JSON, GLOB_MARKDOWN } from './utils'\n"],"mappings":"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"}
|