@bilazo/oxc-config 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +451 -2
- package/dist/format.d.mts +13 -0
- package/dist/format.d.mts.map +1 -0
- package/dist/format.mjs +98 -0
- package/dist/format.mjs.map +1 -0
- package/dist/lint.d.mts +90 -0
- package/dist/lint.d.mts.map +1 -0
- package/dist/lint.mjs +608 -0
- package/dist/lint.mjs.map +1 -0
- package/package.json +77 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lint.d.mts","names":[],"sources":["../src/utils.ts","../src/lint.ts"],"mappings":";;;qBAgDa;;qBAGA;;qBAGA;;qBAGA;;qBAGA;;qBAMA;;qBAGA;;;;;;;wBCuCG,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;;;;;;;;;wBAuFnD,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
ADDED
|
@@ -0,0 +1,608 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { defu } from "defu";
|
|
5
|
+
import { defineConfig, defineConfig as defineConfig$1 } from "oxlint";
|
|
6
|
+
//#region src/utils.ts
|
|
7
|
+
/**
|
|
8
|
+
* Utility functions and constants
|
|
9
|
+
*/
|
|
10
|
+
const env = process.env;
|
|
11
|
+
function isCI() {
|
|
12
|
+
return !!(env["CI"] || env["CONTINUOUS_INTEGRATION"] || env["BUILD_ID"] || env["BUILD_NUMBER"] || env["RUN_ID"]);
|
|
13
|
+
}
|
|
14
|
+
function isInGitHooksOrLintStaged() {
|
|
15
|
+
return !!(env["GIT_PARAMS"] || env["VSCODE_GIT_COMMAND"]) || env["npm_lifecycle_script"]?.startsWith("lint-staged") === true;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Detects whether running in an editor environment.
|
|
19
|
+
* Returns false in CI, Git Hooks, or lint-staged.
|
|
20
|
+
*/
|
|
21
|
+
function isInEditorEnv() {
|
|
22
|
+
if (isCI()) return false;
|
|
23
|
+
if (isInGitHooksOrLintStaged()) return false;
|
|
24
|
+
return !!(env["VSCODE_PID"] || env["VSCODE_CWD"] || env["JETBRAINS_IDE"] || env["VIM"] || env["NVIM"]);
|
|
25
|
+
}
|
|
26
|
+
/** All JS/TS source files */
|
|
27
|
+
const GLOB_SRC = "**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx}";
|
|
28
|
+
/** JavaScript files only */
|
|
29
|
+
const GLOB_JS = "**/*.{js,mjs,cjs,jsx}";
|
|
30
|
+
/** TypeScript files only */
|
|
31
|
+
const GLOB_TS = "**/*.{ts,mts,cts,tsx}";
|
|
32
|
+
/** JSX/TSX files (React-related) */
|
|
33
|
+
const GLOB_JSX = "**/*.{jsx,tsx}";
|
|
34
|
+
/** Test files */
|
|
35
|
+
const GLOB_TESTS = ["**/*.{test,spec}.{js,mjs,cjs,jsx,ts,mts,cts,tsx}", "**/__tests__/**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx}"];
|
|
36
|
+
/** JSON files */
|
|
37
|
+
const GLOB_JSON = "**/*.json";
|
|
38
|
+
/** Markdown files */
|
|
39
|
+
const GLOB_MARKDOWN = "**/*.md";
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/lint.ts
|
|
42
|
+
/**
|
|
43
|
+
* Oxlint lint presets.
|
|
44
|
+
*
|
|
45
|
+
* Every preset is a function that accepts optional overrides, merged via `defu`.
|
|
46
|
+
* User overrides take priority over preset defaults.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* // oxlint.config.ts
|
|
51
|
+
* import { base, unicorn, react, tailwind, vitest } from '@bilazo/oxc-config/lint'
|
|
52
|
+
* import { defineConfig } from 'oxlint'
|
|
53
|
+
*
|
|
54
|
+
* export default defineConfig({
|
|
55
|
+
* extends: [base(), unicorn(), react(), tailwind(), vitest()],
|
|
56
|
+
* })
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
const require = createRequire(import.meta.url);
|
|
60
|
+
const DEFAULT_IGNORES = [
|
|
61
|
+
"**/node_modules/**",
|
|
62
|
+
"**/dist/**",
|
|
63
|
+
"**/build/**",
|
|
64
|
+
"**/out/**",
|
|
65
|
+
"**/.next/**",
|
|
66
|
+
"**/.cache/**",
|
|
67
|
+
"**/.turbo/**",
|
|
68
|
+
"**/.output/**",
|
|
69
|
+
"**/.nitro/**",
|
|
70
|
+
"**/.tanstack/**",
|
|
71
|
+
"**/.vinxi/**",
|
|
72
|
+
"**/.git/**",
|
|
73
|
+
"**/.svn/**",
|
|
74
|
+
"**/.hg/**",
|
|
75
|
+
"**/.pnp.*",
|
|
76
|
+
"**/public/**",
|
|
77
|
+
"**/routeTree.gen.ts",
|
|
78
|
+
"**/*.d.ts"
|
|
79
|
+
];
|
|
80
|
+
function loadGitignorePatterns() {
|
|
81
|
+
const gitignoreFile = path.resolve(process.cwd(), ".gitignore");
|
|
82
|
+
if (!existsSync(gitignoreFile)) return [];
|
|
83
|
+
return readFileSync(gitignoreFile, "utf8").split(/\r?\n/u).map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((pattern) => {
|
|
84
|
+
let p = pattern.trimEnd();
|
|
85
|
+
if (p.startsWith("/")) p = p.slice(1);
|
|
86
|
+
if (!p.includes("/") && !p.includes("*")) p = `**/${p}`;
|
|
87
|
+
if (p.endsWith("/")) p = `${p}**`;
|
|
88
|
+
return p;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function preset(defaults, overrides) {
|
|
92
|
+
return defineConfig$1(defu(overrides ?? {}, defaults));
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Resolve a jsPlugin package name to its absolute path.
|
|
96
|
+
* This allows oxlint to load plugins directly without relying on
|
|
97
|
+
* Node module resolution from the consumer's CWD — fixes pnpm strict mode.
|
|
98
|
+
*/
|
|
99
|
+
function resolvePlugin(name) {
|
|
100
|
+
try {
|
|
101
|
+
return require.resolve(name);
|
|
102
|
+
} catch {
|
|
103
|
+
return name;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function resolvePlugins(plugins) {
|
|
107
|
+
return plugins.map((p) => {
|
|
108
|
+
if (typeof p === "string") return resolvePlugin(p);
|
|
109
|
+
return {
|
|
110
|
+
...p,
|
|
111
|
+
specifier: resolvePlugin(p.specifier)
|
|
112
|
+
};
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Base lint preset — TypeScript + Import plugins, categories, env, ignores.
|
|
117
|
+
* Always include first.
|
|
118
|
+
*/
|
|
119
|
+
function base(overrides) {
|
|
120
|
+
return preset({
|
|
121
|
+
plugins: ["typescript", "import"],
|
|
122
|
+
categories: {
|
|
123
|
+
correctness: "error",
|
|
124
|
+
suspicious: "error",
|
|
125
|
+
perf: "error",
|
|
126
|
+
pedantic: "warn"
|
|
127
|
+
},
|
|
128
|
+
rules: {
|
|
129
|
+
"no-await-in-loop": "off",
|
|
130
|
+
"typescript/prefer-readonly-parameter-types": "off",
|
|
131
|
+
"typescript/strict-boolean-expressions": "off",
|
|
132
|
+
"max-lines": "off",
|
|
133
|
+
"max-lines-per-function": "off",
|
|
134
|
+
"no-inline-comments": "off",
|
|
135
|
+
"no-warning-comments": "off",
|
|
136
|
+
"import/max-dependencies": "off",
|
|
137
|
+
"react/no-unescaped-entities": "off",
|
|
138
|
+
"require-await": "off"
|
|
139
|
+
},
|
|
140
|
+
env: {
|
|
141
|
+
browser: true,
|
|
142
|
+
node: true,
|
|
143
|
+
es2026: true,
|
|
144
|
+
builtin: true
|
|
145
|
+
},
|
|
146
|
+
ignorePatterns: [...DEFAULT_IGNORES, ...loadGitignorePatterns()],
|
|
147
|
+
overrides: [
|
|
148
|
+
{
|
|
149
|
+
files: [GLOB_TS],
|
|
150
|
+
rules: {
|
|
151
|
+
"typescript/consistent-type-definitions": "off",
|
|
152
|
+
"typescript/consistent-type-imports": "error",
|
|
153
|
+
"no-unused-vars": "off",
|
|
154
|
+
"no-useless-rename": "error",
|
|
155
|
+
"uninvoked-array-callback": "error",
|
|
156
|
+
"typescript/array-type": "error",
|
|
157
|
+
"typescript/consistent-generic-constructors": "error",
|
|
158
|
+
"typescript/consistent-indexed-object-style": "error",
|
|
159
|
+
"typescript/consistent-type-assertions": "error",
|
|
160
|
+
"typescript/ban-tslint-comment": "error",
|
|
161
|
+
"typescript/no-empty-interface": "error",
|
|
162
|
+
"typescript/no-inferrable-types": "error",
|
|
163
|
+
"typescript/no-misused-new": "error",
|
|
164
|
+
"typescript/no-unnecessary-parameter-property-assignment": "error",
|
|
165
|
+
"typescript/no-useless-empty-export": "error",
|
|
166
|
+
"typescript/no-deprecated": "warn",
|
|
167
|
+
"typescript/prefer-for-of": "error",
|
|
168
|
+
"typescript/prefer-function-type": "error",
|
|
169
|
+
"typescript/unified-signatures": "error"
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
files: [GLOB_SRC],
|
|
174
|
+
rules: {
|
|
175
|
+
"import/no-unassigned-import": "off",
|
|
176
|
+
"import/no-relative-parent-imports": "error",
|
|
177
|
+
"import/first": "error",
|
|
178
|
+
"import/no-duplicates": "error",
|
|
179
|
+
"import/consistent-type-specifier-style": "error",
|
|
180
|
+
"import/no-mutable-exports": "error",
|
|
181
|
+
"import/no-named-default": "error"
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
files: ["**/*.config.{ts,mts,cts,js,mjs,cjs}"],
|
|
186
|
+
rules: { "import/no-relative-parent-imports": "off" }
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
files: GLOB_TESTS,
|
|
190
|
+
rules: {
|
|
191
|
+
"import/first": "off",
|
|
192
|
+
"import/no-duplicates": "off",
|
|
193
|
+
"import/consistent-type-specifier-style": "off",
|
|
194
|
+
"unicorn/no-useless-undefined": "off",
|
|
195
|
+
"typescript/no-empty-function": "off",
|
|
196
|
+
"unicorn/prefer-string-replace-all": "off",
|
|
197
|
+
"unicorn/no-array-callback-reference": "off",
|
|
198
|
+
"unicorn/prefer-spread": "off",
|
|
199
|
+
"typescript/no-non-null-assertion": "off",
|
|
200
|
+
"no-magic-numbers": "off",
|
|
201
|
+
"typescript/no-explicit-any": "off",
|
|
202
|
+
"typescript/no-unsafe-assignment": "off",
|
|
203
|
+
"typescript/no-unsafe-call": "off",
|
|
204
|
+
"typescript/no-unsafe-member-access": "off",
|
|
205
|
+
"typescript/no-unsafe-return": "off",
|
|
206
|
+
"typescript/no-unsafe-argument": "off",
|
|
207
|
+
"react/no-array-index-key": "off"
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
]
|
|
211
|
+
}, overrides);
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Type-aware lint preset — enables 59 type-aware rules via tsgolint + type checking.
|
|
215
|
+
* Requires TypeScript 7.0+ and `oxlint-tsgolint` (bundled as dependency).
|
|
216
|
+
*/
|
|
217
|
+
function typeAware(overrides) {
|
|
218
|
+
return preset({ options: {
|
|
219
|
+
typeAware: true,
|
|
220
|
+
typeCheck: true
|
|
221
|
+
} }, overrides);
|
|
222
|
+
}
|
|
223
|
+
/** Unicorn lint preset — 100+ code quality rules. */
|
|
224
|
+
function unicorn(overrides) {
|
|
225
|
+
return preset({
|
|
226
|
+
plugins: ["unicorn"],
|
|
227
|
+
rules: {
|
|
228
|
+
"unicorn/no-null": "off",
|
|
229
|
+
"unicorn/filename-case": ["error", { case: "kebabCase" }],
|
|
230
|
+
"unicorn/catch-error-name": "error",
|
|
231
|
+
"unicorn/error-message": "error",
|
|
232
|
+
"unicorn/prefer-optional-catch-binding": "error",
|
|
233
|
+
"unicorn/number-literal-case": "error",
|
|
234
|
+
"unicorn/numeric-separators-style": "error",
|
|
235
|
+
"unicorn/prefer-includes": "error",
|
|
236
|
+
"unicorn/prefer-spread": "error",
|
|
237
|
+
"unicorn/prefer-ternary": "error",
|
|
238
|
+
"unicorn/prefer-string-trim-start-end": "error",
|
|
239
|
+
"unicorn/prefer-structured-clone": "error",
|
|
240
|
+
"unicorn/prefer-default-parameters": "error",
|
|
241
|
+
"unicorn/prefer-string-starts-ends-with": "error",
|
|
242
|
+
"unicorn/prefer-set-size": "error",
|
|
243
|
+
"unicorn/throw-new-error": "error",
|
|
244
|
+
"unicorn/require-array-join-separator": "error",
|
|
245
|
+
"unicorn/no-zero-fractions": "error",
|
|
246
|
+
"unicorn/no-console-spaces": "error",
|
|
247
|
+
"unicorn/no-useless-spread": "error",
|
|
248
|
+
"unicorn/no-useless-fallback-in-spread": "error",
|
|
249
|
+
"unicorn/no-unnecessary-await": "error",
|
|
250
|
+
"unicorn/no-useless-length-check": "error",
|
|
251
|
+
"unicorn/no-single-promise-in-promise-methods": "error",
|
|
252
|
+
"unicorn/no-await-in-promise-methods": "error",
|
|
253
|
+
"unicorn/no-invalid-remove-event-listener": "error",
|
|
254
|
+
"unicorn/no-invalid-fetch-options": "error",
|
|
255
|
+
"unicorn/no-new-array": "error",
|
|
256
|
+
"unicorn/no-empty-file": "warn"
|
|
257
|
+
}
|
|
258
|
+
}, overrides);
|
|
259
|
+
}
|
|
260
|
+
/** Dependency optimization — flags packages replaceable with native APIs. */
|
|
261
|
+
function depend(overrides) {
|
|
262
|
+
return preset({
|
|
263
|
+
jsPlugins: resolvePlugins(["eslint-plugin-depend"]),
|
|
264
|
+
rules: { "depend/ban-dependencies": ["error", {
|
|
265
|
+
presets: [
|
|
266
|
+
"native",
|
|
267
|
+
"microutilities",
|
|
268
|
+
"preferred"
|
|
269
|
+
],
|
|
270
|
+
modules: [],
|
|
271
|
+
allowed: ["dotenv"]
|
|
272
|
+
}] }
|
|
273
|
+
}, overrides);
|
|
274
|
+
}
|
|
275
|
+
/** Node.js lint preset — native node plugin. */
|
|
276
|
+
function node(overrides) {
|
|
277
|
+
return preset({
|
|
278
|
+
plugins: ["node"],
|
|
279
|
+
rules: { "unicorn/prefer-node-protocol": "error" }
|
|
280
|
+
}, overrides);
|
|
281
|
+
}
|
|
282
|
+
/** Promise lint preset — native promise plugin. */
|
|
283
|
+
function promise(overrides) {
|
|
284
|
+
return preset({ plugins: ["promise"] }, overrides);
|
|
285
|
+
}
|
|
286
|
+
const reactRules = {
|
|
287
|
+
"react/rules-of-hooks": "error",
|
|
288
|
+
"react/exhaustive-deps": "warn",
|
|
289
|
+
"react/jsx-key": "error",
|
|
290
|
+
"react/jsx-no-duplicate-props": "error",
|
|
291
|
+
"react/jsx-no-undef": "error",
|
|
292
|
+
"react/jsx-no-target-blank": ["error", { allowReferrer: false }],
|
|
293
|
+
"react/jsx-no-comment-textnodes": "error",
|
|
294
|
+
"react/jsx-no-script-url": "error",
|
|
295
|
+
"react/jsx-props-no-spread-multi": "error",
|
|
296
|
+
"react/jsx-no-useless-fragment": "warn",
|
|
297
|
+
"react/jsx-boolean-value": ["error", "never"],
|
|
298
|
+
"react/jsx-curly-brace-presence": ["error", {
|
|
299
|
+
props: "never",
|
|
300
|
+
children: "never"
|
|
301
|
+
}],
|
|
302
|
+
"react/jsx-fragments": ["error", "syntax"],
|
|
303
|
+
"react/jsx-pascal-case": ["error", {
|
|
304
|
+
allowAllCaps: false,
|
|
305
|
+
allowNamespace: true
|
|
306
|
+
}],
|
|
307
|
+
"react/self-closing-comp": ["error", {
|
|
308
|
+
component: true,
|
|
309
|
+
html: true
|
|
310
|
+
}],
|
|
311
|
+
"react/hook-use-state": "error",
|
|
312
|
+
"react/no-children-prop": "error",
|
|
313
|
+
"react/no-danger-with-children": "error",
|
|
314
|
+
"react/no-direct-mutation-state": "error",
|
|
315
|
+
"react/no-find-dom-node": "error",
|
|
316
|
+
"react/no-is-mounted": "error",
|
|
317
|
+
"react/no-render-return-value": "error",
|
|
318
|
+
"react/no-string-refs": "error",
|
|
319
|
+
"react/no-unknown-property": "error",
|
|
320
|
+
"react/no-unsafe": "error",
|
|
321
|
+
"react/require-render-return": "error",
|
|
322
|
+
"react/void-dom-elements-no-children": "error",
|
|
323
|
+
"react/no-set-state": "error",
|
|
324
|
+
"react/no-did-mount-set-state": "error",
|
|
325
|
+
"react/no-did-update-set-state": "error",
|
|
326
|
+
"react/no-will-update-set-state": "error",
|
|
327
|
+
"react/no-this-in-sfc": "error",
|
|
328
|
+
"react/forward-ref-uses-ref": "error",
|
|
329
|
+
"react/no-unstable-nested-components": "warn",
|
|
330
|
+
"react/iframe-missing-sandbox": "error",
|
|
331
|
+
"react/set-state-in-render": "error",
|
|
332
|
+
"react/error-boundaries": "error",
|
|
333
|
+
"react/jsx-no-constructed-context-values": "warn",
|
|
334
|
+
"react/no-array-index-key": "warn",
|
|
335
|
+
"react/style-prop-object": "error",
|
|
336
|
+
"react/react-in-jsx-scope": "off",
|
|
337
|
+
"react/no-unescaped-entities": "off"
|
|
338
|
+
};
|
|
339
|
+
/** React lint preset — core React + Hooks rules. */
|
|
340
|
+
function react(overrides) {
|
|
341
|
+
return preset({
|
|
342
|
+
plugins: ["react"],
|
|
343
|
+
rules: reactRules
|
|
344
|
+
}, overrides);
|
|
345
|
+
}
|
|
346
|
+
/** React + react-refresh preset (for Vite, TanStack Start, etc.). */
|
|
347
|
+
function reactVite(overrides) {
|
|
348
|
+
return preset({
|
|
349
|
+
plugins: ["react"],
|
|
350
|
+
jsPlugins: resolvePlugins(["eslint-plugin-react-refresh"]),
|
|
351
|
+
rules: {
|
|
352
|
+
...reactRules,
|
|
353
|
+
"react-refresh/only-export-components": ["warn", { allowConstantExport: true }]
|
|
354
|
+
}
|
|
355
|
+
}, overrides);
|
|
356
|
+
}
|
|
357
|
+
/** Next.js lint preset — enables native nextjs plugin. */
|
|
358
|
+
function nextjs(overrides) {
|
|
359
|
+
return preset({ plugins: ["nextjs"] }, overrides);
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* TanStack Router lint preset.
|
|
363
|
+
*
|
|
364
|
+
* Loads @tanstack/eslint-plugin-router and applies conventions specific
|
|
365
|
+
* to file-based routing setups.
|
|
366
|
+
*
|
|
367
|
+
* Rules provided by the plugin:
|
|
368
|
+
* - `route-param-names` — validates `$param` names match between route path and params
|
|
369
|
+
* - `create-route-property-order` — enforces consistent property order in `createRoute()`
|
|
370
|
+
*/
|
|
371
|
+
function tanstackRouter(overrides) {
|
|
372
|
+
return preset({
|
|
373
|
+
jsPlugins: resolvePlugins([{
|
|
374
|
+
name: "tanstack-router",
|
|
375
|
+
specifier: "@tanstack/eslint-plugin-router"
|
|
376
|
+
}]),
|
|
377
|
+
rules: {
|
|
378
|
+
"tanstack-router/route-param-names": "error",
|
|
379
|
+
"tanstack-router/create-route-property-order": "error",
|
|
380
|
+
"import/no-default-export": "off"
|
|
381
|
+
},
|
|
382
|
+
overrides: [
|
|
383
|
+
{
|
|
384
|
+
files: [
|
|
385
|
+
"**/routeTree.gen.ts",
|
|
386
|
+
"**/routeTree.gen.tsx",
|
|
387
|
+
"**/*.generated.ts",
|
|
388
|
+
"**/*.gen.ts"
|
|
389
|
+
],
|
|
390
|
+
rules: { "unicorn/filename-case": "off" }
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
files: ["**/routes/**/*.{ts,tsx}"],
|
|
394
|
+
rules: { "no-restricted-globals": ["error", {
|
|
395
|
+
name: "window",
|
|
396
|
+
message: "Use useRouter() or useNavigate() instead of window in route files."
|
|
397
|
+
}] }
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
files: ["**/routes/**/*.{ts,tsx}"],
|
|
401
|
+
plugins: ["unicorn"],
|
|
402
|
+
rules: { "unicorn/filename-case": ["error", {
|
|
403
|
+
case: "kebabCase",
|
|
404
|
+
ignore: [
|
|
405
|
+
"\\$",
|
|
406
|
+
"^_",
|
|
407
|
+
"\\."
|
|
408
|
+
]
|
|
409
|
+
}] }
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
files: ["**/routes/__root.tsx"],
|
|
413
|
+
rules: { "no-restricted-globals": "off" }
|
|
414
|
+
}
|
|
415
|
+
]
|
|
416
|
+
}, overrides);
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* TanStack Start lint preset.
|
|
420
|
+
*
|
|
421
|
+
* Adds rules for server functions, client/server boundaries, and Start
|
|
422
|
+
* conventions. This preset assumes `reactVite()` and `tanstackRouter()`
|
|
423
|
+
* are already in the `extends` array.
|
|
424
|
+
*
|
|
425
|
+
*/
|
|
426
|
+
function tanstackStart(overrides) {
|
|
427
|
+
return preset({
|
|
428
|
+
rules: {
|
|
429
|
+
"no-async-promise-executor": "error",
|
|
430
|
+
"no-await-in-loop": "off",
|
|
431
|
+
"import/no-nodejs-modules": "error",
|
|
432
|
+
"consistent-return": "error",
|
|
433
|
+
"no-empty-function": "warn"
|
|
434
|
+
},
|
|
435
|
+
overrides: [
|
|
436
|
+
{
|
|
437
|
+
files: [
|
|
438
|
+
"**/*.server.{ts,tsx}",
|
|
439
|
+
"**/server/**/*.{ts,tsx}",
|
|
440
|
+
"**/server-fns/**/*.{ts,tsx}"
|
|
441
|
+
],
|
|
442
|
+
rules: {
|
|
443
|
+
"import/no-nodejs-modules": "off",
|
|
444
|
+
"no-console": "off"
|
|
445
|
+
}
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
files: ["**/*.client.{ts,tsx}", "**/components/**/*.{ts,tsx}"],
|
|
449
|
+
rules: { "import/no-nodejs-modules": "error" }
|
|
450
|
+
},
|
|
451
|
+
{
|
|
452
|
+
files: ["**/routes/**/*.{ts,tsx}"],
|
|
453
|
+
rules: { "no-restricted-syntax": ["warn", {
|
|
454
|
+
selector: "CallExpression[callee.name=\"fetch\"]",
|
|
455
|
+
message: "Prefer a loader or server function over direct fetch in route files."
|
|
456
|
+
}] }
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
files: ["**/*.server.{ts,tsx}", "**/*.client.{ts,tsx}"],
|
|
460
|
+
plugins: ["unicorn"],
|
|
461
|
+
rules: { "unicorn/filename-case": ["error", {
|
|
462
|
+
case: "kebabCase",
|
|
463
|
+
ignore: ["\\."]
|
|
464
|
+
}] }
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
files: [
|
|
468
|
+
"**/*.server.{ts,tsx}",
|
|
469
|
+
"**/server/**/*.{ts,tsx}",
|
|
470
|
+
"**/server-fns/**/*.{ts,tsx}"
|
|
471
|
+
],
|
|
472
|
+
rules: {
|
|
473
|
+
"import/no-nodejs-modules": "off",
|
|
474
|
+
"no-console": "off",
|
|
475
|
+
"no-restricted-globals": [
|
|
476
|
+
"error",
|
|
477
|
+
{
|
|
478
|
+
name: "window",
|
|
479
|
+
message: "Not available in server context. Use a server function or guard with typeof window."
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
name: "document",
|
|
483
|
+
message: "Not available in server context."
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
name: "localStorage",
|
|
487
|
+
message: "Not available in server context."
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
name: "sessionStorage",
|
|
491
|
+
message: "Not available in server context."
|
|
492
|
+
}
|
|
493
|
+
]
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
]
|
|
497
|
+
}, overrides);
|
|
498
|
+
}
|
|
499
|
+
/** Accessibility lint preset — enables native jsx-a11y plugin. */
|
|
500
|
+
function a11y(overrides) {
|
|
501
|
+
return preset({ plugins: ["jsx-a11y"] }, overrides);
|
|
502
|
+
}
|
|
503
|
+
/** JSDoc lint preset — enables native jsdoc plugin. */
|
|
504
|
+
function jsdoc(overrides) {
|
|
505
|
+
return preset({ plugins: ["jsdoc"] }, overrides);
|
|
506
|
+
}
|
|
507
|
+
/** Vitest lint preset — native vitest plugin with test-file scoping. */
|
|
508
|
+
function vitest(options) {
|
|
509
|
+
const { files, ...overrides } = options ?? {};
|
|
510
|
+
return preset({
|
|
511
|
+
plugins: ["vitest"],
|
|
512
|
+
settings: { vitest: { typecheck: true } },
|
|
513
|
+
overrides: [{
|
|
514
|
+
files: files ?? GLOB_TESTS,
|
|
515
|
+
rules: {
|
|
516
|
+
"vitest/expect-expect": "error",
|
|
517
|
+
"vitest/no-conditional-expect": "error",
|
|
518
|
+
"vitest/no-identical-title": "error",
|
|
519
|
+
"vitest/no-import-node-test": "error",
|
|
520
|
+
"vitest/no-interpolation-in-snapshots": "error",
|
|
521
|
+
"vitest/no-mocks-import": "error",
|
|
522
|
+
"vitest/no-standalone-expect": "error",
|
|
523
|
+
"vitest/valid-describe-callback": "error",
|
|
524
|
+
"vitest/valid-expect": "error",
|
|
525
|
+
"vitest/valid-title": "error",
|
|
526
|
+
"vitest/consistent-test-it": ["error", {
|
|
527
|
+
fn: "it",
|
|
528
|
+
withinDescribe: "it"
|
|
529
|
+
}],
|
|
530
|
+
"vitest/prefer-hooks-in-order": "error",
|
|
531
|
+
"vitest/prefer-lowercase-title": "error",
|
|
532
|
+
"vitest/no-disabled-tests": isInEditorEnv() ? "warn" : "error",
|
|
533
|
+
"vitest/no-focused-tests": isInEditorEnv() ? "warn" : "error",
|
|
534
|
+
"vitest/require-mock-type-parameters": "off",
|
|
535
|
+
"vitest/prefer-called-once": "error",
|
|
536
|
+
"vitest/prefer-expect-type-of": "error",
|
|
537
|
+
"vitest/prefer-to-be-object": "error",
|
|
538
|
+
"vitest/hoisted-apis-on-top": "error",
|
|
539
|
+
"vitest/require-awaited-expect-poll": "error",
|
|
540
|
+
"vitest/no-conditional-tests": "error",
|
|
541
|
+
"vitest/consistent-vitest-vi": "error",
|
|
542
|
+
"no-console": "off",
|
|
543
|
+
"unicorn/no-null": "off",
|
|
544
|
+
"typescript/ban-ts-comment": "off"
|
|
545
|
+
}
|
|
546
|
+
}]
|
|
547
|
+
}, overrides);
|
|
548
|
+
}
|
|
549
|
+
/** Tailwind CSS lint preset — eslint-plugin-better-tailwindcss. */
|
|
550
|
+
function tailwind(options = {}) {
|
|
551
|
+
const { entryPoint = "src/styles/globals.css", rootFontSize = 16, files, ...overrides } = options;
|
|
552
|
+
return preset({
|
|
553
|
+
jsPlugins: resolvePlugins(["eslint-plugin-better-tailwindcss"]),
|
|
554
|
+
settings: { "better-tailwindcss": {
|
|
555
|
+
entryPoint,
|
|
556
|
+
rootFontSize
|
|
557
|
+
} },
|
|
558
|
+
overrides: [{
|
|
559
|
+
files: files ?? ["**/*.{jsx,tsx}"],
|
|
560
|
+
rules: {
|
|
561
|
+
"better-tailwindcss/enforce-consistent-line-wrapping": ["error", { printWidth: 0 }],
|
|
562
|
+
"better-tailwindcss/enforce-canonical-classes": "error"
|
|
563
|
+
}
|
|
564
|
+
}]
|
|
565
|
+
}, overrides);
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* NestJS lint preset — loads @darraghor/eslint-plugin-nestjs-typed via jsPlugin.
|
|
569
|
+
* Covers DI validation, Swagger consistency, decorator bug prevention (19 AST rules).
|
|
570
|
+
*/
|
|
571
|
+
function nestjs(overrides) {
|
|
572
|
+
return preset({
|
|
573
|
+
jsPlugins: resolvePlugins([{
|
|
574
|
+
name: "nestjs-typed",
|
|
575
|
+
specifier: "@darraghor/eslint-plugin-nestjs-typed"
|
|
576
|
+
}]),
|
|
577
|
+
rules: {
|
|
578
|
+
"nestjs-typed/injectable-should-be-provided": "error",
|
|
579
|
+
"nestjs-typed/provided-injected-should-match-factory-parameters": "error",
|
|
580
|
+
"nestjs-typed/use-injectable-provided-token": "error",
|
|
581
|
+
"nestjs-typed/api-property-matches-property-optionality": "error",
|
|
582
|
+
"nestjs-typed/controllers-should-supply-api-tags": "error",
|
|
583
|
+
"nestjs-typed/api-method-should-specify-api-response": "error",
|
|
584
|
+
"nestjs-typed/api-property-returning-array-should-set-array": "error",
|
|
585
|
+
"nestjs-typed/api-property-should-have-api-extra-models": "error",
|
|
586
|
+
"nestjs-typed/api-operation-summary-description-capitalized": "error",
|
|
587
|
+
"nestjs-typed/param-decorator-name-matches-route-param": "error",
|
|
588
|
+
"nestjs-typed/validate-nested-of-array-should-set-each": "error",
|
|
589
|
+
"nestjs-typed/all-properties-are-whitelisted": "error",
|
|
590
|
+
"nestjs-typed/no-duplicate-decorators": "error",
|
|
591
|
+
"nestjs-typed/validation-pipe-should-use-forbid-unknown": "error"
|
|
592
|
+
}
|
|
593
|
+
}, overrides);
|
|
594
|
+
}
|
|
595
|
+
/** Drizzle ORM lint preset — eslint-plugin-drizzle. */
|
|
596
|
+
function drizzle(overrides) {
|
|
597
|
+
return preset({
|
|
598
|
+
jsPlugins: resolvePlugins(["eslint-plugin-drizzle"]),
|
|
599
|
+
rules: {
|
|
600
|
+
"drizzle/enforce-delete-with-where": "error",
|
|
601
|
+
"drizzle/enforce-update-with-where": "error"
|
|
602
|
+
}
|
|
603
|
+
}, overrides);
|
|
604
|
+
}
|
|
605
|
+
//#endregion
|
|
606
|
+
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 };
|
|
607
|
+
|
|
608
|
+
//# sourceMappingURL=lint.mjs.map
|