@ghettoddos/eslint-config 4.1.1 → 5.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/dist/index.d.mts +13296 -10107
- package/dist/index.mjs +319 -335
- package/dist/{lib-C63e_zBF.mjs → lib-C1Uxp5ZW.mjs} +2879 -2331
- package/package.json +53 -67
package/dist/index.mjs
CHANGED
|
@@ -5,18 +5,21 @@ import { fileURLToPath } from "node:url";
|
|
|
5
5
|
import fs$1 from "node:fs";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { isPackageExists } from "local-pkg";
|
|
8
|
-
import createCommand from "eslint-plugin-command/config";
|
|
9
8
|
import pluginE18e from "@e18e/eslint-plugin";
|
|
10
9
|
import pluginComments from "@eslint-community/eslint-plugin-eslint-comments";
|
|
11
10
|
import pluginAntfu from "eslint-plugin-antfu";
|
|
11
|
+
import pluginBaselineJs from "eslint-plugin-baseline-js";
|
|
12
|
+
import pluginDeMorgan from "eslint-plugin-de-morgan";
|
|
12
13
|
import pluginImportLite from "eslint-plugin-import-lite";
|
|
13
14
|
import pluginNode from "eslint-plugin-n";
|
|
14
15
|
import pluginPerfectionist from "eslint-plugin-perfectionist";
|
|
15
16
|
import pluginUnicorn from "eslint-plugin-unicorn";
|
|
16
17
|
import pluginUnusedImports from "eslint-plugin-unused-imports";
|
|
18
|
+
import createCommand from "eslint-plugin-command/config";
|
|
17
19
|
import globals from "globals";
|
|
18
20
|
import { mergeProcessors, processorPassThrough } from "eslint-merge-processors";
|
|
19
21
|
import { configs } from "eslint-plugin-regexp";
|
|
22
|
+
import pluginSonarJs from "eslint-plugin-sonarjs";
|
|
20
23
|
//#region node_modules/.pnpm/find-up-simple@1.0.1/node_modules/find-up-simple/index.js
|
|
21
24
|
const toPath = (urlOrPath) => urlOrPath instanceof URL ? fileURLToPath(urlOrPath) : urlOrPath;
|
|
22
25
|
async function findUp(name, { cwd = process.cwd(), type = "file", stopAt } = {}) {
|
|
@@ -50,6 +53,26 @@ function findUpSync(name, { cwd = process.cwd(), type = "file", stopAt } = {}) {
|
|
|
50
53
|
}
|
|
51
54
|
}
|
|
52
55
|
//#endregion
|
|
56
|
+
//#region src/configs/baseline.ts
|
|
57
|
+
async function baseline(options = {}) {
|
|
58
|
+
const { available, baseline, ignoreFeatures = ["functions-caller-arguments"], overrides = {} } = options;
|
|
59
|
+
return [{
|
|
60
|
+
name: "baseline/rules",
|
|
61
|
+
plugins: { "baseline-js": pluginBaselineJs },
|
|
62
|
+
rules: {
|
|
63
|
+
"baseline-js/use-baseline": ["warn", {
|
|
64
|
+
available,
|
|
65
|
+
baseline,
|
|
66
|
+
ignoreFeatures,
|
|
67
|
+
includeJsBuiltins: { preset: "auto" },
|
|
68
|
+
includeWebApis: { preset: "auto" },
|
|
69
|
+
...options
|
|
70
|
+
}],
|
|
71
|
+
...overrides
|
|
72
|
+
}
|
|
73
|
+
}];
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
53
76
|
//#region src/configs/command.ts
|
|
54
77
|
async function command() {
|
|
55
78
|
return [{
|
|
@@ -72,6 +95,14 @@ async function comments() {
|
|
|
72
95
|
}];
|
|
73
96
|
}
|
|
74
97
|
//#endregion
|
|
98
|
+
//#region src/configs/de-morgan.ts
|
|
99
|
+
async function deMorgan() {
|
|
100
|
+
return [{
|
|
101
|
+
...pluginDeMorgan.configs.recommended,
|
|
102
|
+
name: "de-morgan/rules"
|
|
103
|
+
}];
|
|
104
|
+
}
|
|
105
|
+
//#endregion
|
|
75
106
|
//#region src/globs.ts
|
|
76
107
|
const GLOB_SRC_EXT = "?([cm])[jt]s?(x)";
|
|
77
108
|
const GLOB_SRC = "**/*.?([cm])[jt]s?(x)";
|
|
@@ -82,6 +113,7 @@ const GLOB_TSX = "**/*.?([cm])tsx";
|
|
|
82
113
|
const GLOB_STYLE = "**/*.{c,le,sc}ss";
|
|
83
114
|
const GLOB_CSS = "**/*.css";
|
|
84
115
|
const GLOB_POSTCSS = "**/*.{p,post}css";
|
|
116
|
+
const GLOB_LESS = "**/*.less";
|
|
85
117
|
const GLOB_SCSS = "**/*.scss";
|
|
86
118
|
const GLOB_JSON = "**/*.json";
|
|
87
119
|
const GLOB_JSON5 = "**/*.json5";
|
|
@@ -94,6 +126,7 @@ const GLOB_TOML = "**/*.toml";
|
|
|
94
126
|
const GLOB_XML = "**/*.xml";
|
|
95
127
|
const GLOB_SVG = "**/*.svg";
|
|
96
128
|
const GLOB_HTML = "**/*.htm?(l)";
|
|
129
|
+
const GLOB_GRAPHQL = "**/*.{g,graph}ql";
|
|
97
130
|
const GLOB_MARKDOWN_CODE = `${GLOB_MARKDOWN}/${GLOB_SRC}`;
|
|
98
131
|
const GLOB_TESTS = [
|
|
99
132
|
`**/__tests__/**/*.${GLOB_SRC_EXT}`,
|
|
@@ -108,6 +141,7 @@ const GLOB_ALL_SRC = [
|
|
|
108
141
|
GLOB_JSON,
|
|
109
142
|
GLOB_JSON5,
|
|
110
143
|
GLOB_MARKDOWN,
|
|
144
|
+
GLOB_VUE,
|
|
111
145
|
GLOB_YAML,
|
|
112
146
|
GLOB_XML,
|
|
113
147
|
GLOB_HTML
|
|
@@ -137,13 +171,17 @@ const GLOB_EXCLUDE = [
|
|
|
137
171
|
"**/.output",
|
|
138
172
|
"**/.vite-inspect",
|
|
139
173
|
"**/.yarn",
|
|
140
|
-
"**/vite.config.*.timestamp-*",
|
|
141
174
|
"**/CHANGELOG*.md",
|
|
142
|
-
"**/*.min.*",
|
|
143
175
|
"**/LICENSE*",
|
|
176
|
+
"**/*.min.*",
|
|
144
177
|
"**/__snapshots__",
|
|
178
|
+
"**/vite.config.*.timestamp-*",
|
|
145
179
|
"**/auto-import?(s).d.ts",
|
|
146
|
-
"**/components.d.ts"
|
|
180
|
+
"**/components.d.ts",
|
|
181
|
+
"**/.context",
|
|
182
|
+
"**/.claude",
|
|
183
|
+
"**/.agents",
|
|
184
|
+
"**/.*/skills"
|
|
147
185
|
];
|
|
148
186
|
//#endregion
|
|
149
187
|
//#region src/configs/disables.ts
|
|
@@ -154,10 +192,28 @@ async function disables() {
|
|
|
154
192
|
name: "disables/scripts",
|
|
155
193
|
rules: {
|
|
156
194
|
"antfu/no-top-level-await": "off",
|
|
195
|
+
"baseline-js/use-baseline": "off",
|
|
157
196
|
"no-console": "off",
|
|
158
197
|
"ts/explicit-function-return-type": "off"
|
|
159
198
|
}
|
|
160
199
|
},
|
|
200
|
+
{
|
|
201
|
+
files: [`**/cli/${GLOB_SRC}`, `**/cli.${GLOB_SRC_EXT}`],
|
|
202
|
+
name: "disables/cli",
|
|
203
|
+
rules: {
|
|
204
|
+
"antfu/no-top-level-await": "off",
|
|
205
|
+
"baseline-js/use-baseline": "off",
|
|
206
|
+
"no-console": "off"
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
files: ["**/bin/**/*", `**/bin.${GLOB_SRC_EXT}`],
|
|
211
|
+
name: "disables/bin",
|
|
212
|
+
rules: {
|
|
213
|
+
"antfu/no-import-dist": "off",
|
|
214
|
+
"antfu/no-import-node-modules-by-path": "off"
|
|
215
|
+
}
|
|
216
|
+
},
|
|
161
217
|
{
|
|
162
218
|
files: ["**/*.d.?([cm])ts"],
|
|
163
219
|
name: "disables/dts",
|
|
@@ -171,10 +227,43 @@ async function disables() {
|
|
|
171
227
|
files: ["**/*.js", "**/*.cjs"],
|
|
172
228
|
name: "disables/cjs",
|
|
173
229
|
rules: { "ts/no-require-imports": "off" }
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
files: [`**/*.config.${GLOB_SRC_EXT}`, `**/*.config.*.${GLOB_SRC_EXT}`],
|
|
233
|
+
name: "disables/config-files",
|
|
234
|
+
rules: {
|
|
235
|
+
"antfu/no-top-level-await": "off",
|
|
236
|
+
"baseline-js/use-baseline": "off",
|
|
237
|
+
"no-console": "off",
|
|
238
|
+
"ts/explicit-function-return-type": "off"
|
|
239
|
+
}
|
|
174
240
|
}
|
|
175
241
|
];
|
|
176
242
|
}
|
|
177
243
|
//#endregion
|
|
244
|
+
//#region src/configs/e18e.ts
|
|
245
|
+
async function e18e(options = {}) {
|
|
246
|
+
const { isInEditor = false, modernization = true, type = "app", moduleReplacements = type === "lib" && isInEditor, overrides = {}, performanceImprovements = true } = options;
|
|
247
|
+
const configs = pluginE18e.configs;
|
|
248
|
+
return [{
|
|
249
|
+
name: "e18e/rules",
|
|
250
|
+
plugins: { e18e: pluginE18e },
|
|
251
|
+
rules: {
|
|
252
|
+
...modernization ? { ...configs.modernization.rules } : {},
|
|
253
|
+
...moduleReplacements ? { ...configs.moduleReplacements.rules } : {},
|
|
254
|
+
...performanceImprovements ? { ...configs.performanceImprovements.rules } : {},
|
|
255
|
+
...type === "lib" ? {} : { "e18e/prefer-static-regex": "off" },
|
|
256
|
+
"e18e/prefer-array-at": "off",
|
|
257
|
+
"e18e/prefer-array-from-map": "off",
|
|
258
|
+
"e18e/prefer-array-to-reversed": "off",
|
|
259
|
+
"e18e/prefer-array-to-sorted": "off",
|
|
260
|
+
"e18e/prefer-array-to-spliced": "off",
|
|
261
|
+
"e18e/prefer-spread-syntax": "off",
|
|
262
|
+
...overrides
|
|
263
|
+
}
|
|
264
|
+
}];
|
|
265
|
+
}
|
|
266
|
+
//#endregion
|
|
178
267
|
//#region src/utils.ts
|
|
179
268
|
const scopeUrl = fileURLToPath(new URL(".", import.meta.url));
|
|
180
269
|
const isCwdInScope = isPackageExists("@ghettoddos/eslint-config");
|
|
@@ -198,12 +287,6 @@ const parserPlain = {
|
|
|
198
287
|
})
|
|
199
288
|
};
|
|
200
289
|
/**
|
|
201
|
-
* Combine array and non-array configs into a single array.
|
|
202
|
-
*/
|
|
203
|
-
async function combine(...configs) {
|
|
204
|
-
return (await Promise.all(configs)).flat();
|
|
205
|
-
}
|
|
206
|
-
/**
|
|
207
290
|
* Rename plugin prefixes in a rule object.
|
|
208
291
|
* Accepts a map of prefixes to rename.
|
|
209
292
|
*
|
|
@@ -238,7 +321,10 @@ async function ensurePackages(packages) {
|
|
|
238
321
|
if (process.env.CI || process.stdout.isTTY === false || isCwdInScope === false) return;
|
|
239
322
|
const nonExistingPackages = packages.filter((i) => i && !isPackageInScope(i));
|
|
240
323
|
if (nonExistingPackages.length === 0) return;
|
|
241
|
-
|
|
324
|
+
const p = await import("@clack/prompts");
|
|
325
|
+
const packagePlural = nonExistingPackages.length === 1 ? "Package is" : "Packages are";
|
|
326
|
+
const pkgs = nonExistingPackages.join(", ");
|
|
327
|
+
if (await p.confirm({ message: `${packagePlural} required for this config: ${pkgs}. Do you want to install them?` })) await import("@antfu/install-pkg").then((i) => i.installPackage(nonExistingPackages, { dev: true }));
|
|
242
328
|
}
|
|
243
329
|
function isInEditorEnv() {
|
|
244
330
|
if (process.env.CI) return false;
|
|
@@ -248,32 +334,26 @@ function isInEditorEnv() {
|
|
|
248
334
|
function isInGitHooksOrLintStaged() {
|
|
249
335
|
return !!(process.env.GIT_PARAMS || process.env.VSCODE_GIT_COMMAND || process.env.npm_lifecycle_script?.startsWith("lint-staged"));
|
|
250
336
|
}
|
|
251
|
-
function resolveSubOptions(options, key) {
|
|
252
|
-
return typeof options[key] === "boolean" ? {} : options[key] || {};
|
|
253
|
-
}
|
|
254
|
-
function getOverrides(options, key) {
|
|
255
|
-
const sub = resolveSubOptions(options, key);
|
|
256
|
-
return {
|
|
257
|
-
...options.overrides?.[key],
|
|
258
|
-
..."overrides" in sub ? sub.overrides : {}
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
337
|
//#endregion
|
|
262
338
|
//#region src/configs/stylistic.ts
|
|
263
339
|
const StylisticConfigDefaults = {
|
|
340
|
+
braceStyle: "stroustrup",
|
|
264
341
|
experimental: false,
|
|
265
342
|
indent: 2,
|
|
266
343
|
jsx: true,
|
|
344
|
+
printWidth: 100,
|
|
267
345
|
quotes: "single",
|
|
268
|
-
semi: false
|
|
346
|
+
semi: false,
|
|
347
|
+
tabWidth: 4
|
|
269
348
|
};
|
|
270
349
|
async function stylistic(options = {}) {
|
|
271
|
-
const { experimental, indent, jsx, overrides = {}, quotes, semi } = {
|
|
350
|
+
const { braceStyle, experimental, indent, jsx, lessOpinionated = false, overrides = {}, printWidth, quotes, semi, tabWidth } = {
|
|
272
351
|
...StylisticConfigDefaults,
|
|
273
352
|
...options
|
|
274
353
|
};
|
|
275
354
|
const pluginStylistic = await interopDefault(import("@stylistic/eslint-plugin"));
|
|
276
355
|
const config = pluginStylistic.configs.customize({
|
|
356
|
+
braceStyle,
|
|
277
357
|
experimental,
|
|
278
358
|
indent,
|
|
279
359
|
jsx,
|
|
@@ -291,13 +371,23 @@ async function stylistic(options = {}) {
|
|
|
291
371
|
...config.rules,
|
|
292
372
|
...experimental ? {} : { "antfu/consistent-list-newline": "error" },
|
|
293
373
|
"antfu/consistent-chaining": "error",
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
374
|
+
...lessOpinionated ? { curly: ["error", "all"] } : {
|
|
375
|
+
"antfu/curly": "error",
|
|
376
|
+
"antfu/if-newline": "error",
|
|
377
|
+
"antfu/top-level-function": "error"
|
|
378
|
+
},
|
|
297
379
|
"style/generator-star-spacing": ["error", {
|
|
298
380
|
after: true,
|
|
299
381
|
before: false
|
|
300
382
|
}],
|
|
383
|
+
"style/max-len": ["warn", {
|
|
384
|
+
code: printWidth,
|
|
385
|
+
ignoreComments: true,
|
|
386
|
+
ignoreRegExpLiterals: true,
|
|
387
|
+
ignoreTrailingComments: true,
|
|
388
|
+
ignoreUrls: true,
|
|
389
|
+
tabWidth: typeof indent === "number" ? indent : indent === "tab" ? tabWidth : 2
|
|
390
|
+
}],
|
|
301
391
|
"style/yield-star-spacing": ["error", {
|
|
302
392
|
after: true,
|
|
303
393
|
before: false
|
|
@@ -320,6 +410,7 @@ async function formatters(options = {}, stylistic = {}) {
|
|
|
320
410
|
const isPrettierPluginXmlInScope = isPackageInScope("@prettier/plugin-xml");
|
|
321
411
|
options = {
|
|
322
412
|
css: true,
|
|
413
|
+
graphql: true,
|
|
323
414
|
html: true,
|
|
324
415
|
markdown: true,
|
|
325
416
|
svg: isPrettierPluginXmlInScope,
|
|
@@ -327,13 +418,13 @@ async function formatters(options = {}, stylistic = {}) {
|
|
|
327
418
|
};
|
|
328
419
|
}
|
|
329
420
|
await ensurePackages(["eslint-plugin-format", options.xml || options.svg ? "@prettier/plugin-xml" : void 0]);
|
|
330
|
-
const { indent, quotes, semi } = {
|
|
421
|
+
const { indent, printWidth, quotes, semi } = {
|
|
331
422
|
...StylisticConfigDefaults,
|
|
332
423
|
...stylistic
|
|
333
424
|
};
|
|
334
425
|
const prettierOptions = Object.assign({
|
|
335
426
|
endOfLine: "auto",
|
|
336
|
-
printWidth:
|
|
427
|
+
printWidth: typeof printWidth === "number" ? printWidth : 100,
|
|
337
428
|
semi,
|
|
338
429
|
singleQuote: quotes === "single",
|
|
339
430
|
tabWidth: typeof indent === "number" ? indent : 2,
|
|
@@ -360,6 +451,11 @@ async function formatters(options = {}, stylistic = {}) {
|
|
|
360
451
|
languageOptions: { parser: parserPlain },
|
|
361
452
|
name: "formatter/scss",
|
|
362
453
|
rules: { "format/prettier": ["error", mergePrettierOptions(prettierOptions, { parser: "scss" })] }
|
|
454
|
+
}, {
|
|
455
|
+
files: [GLOB_LESS],
|
|
456
|
+
languageOptions: { parser: parserPlain },
|
|
457
|
+
name: "formatter/less",
|
|
458
|
+
rules: { "format/prettier": ["error", mergePrettierOptions(prettierOptions, { parser: "less" })] }
|
|
363
459
|
});
|
|
364
460
|
if (options.html) configs.push({
|
|
365
461
|
files: [GLOB_HTML],
|
|
@@ -400,6 +496,12 @@ async function formatters(options = {}, stylistic = {}) {
|
|
|
400
496
|
parser: "markdown"
|
|
401
497
|
})] }
|
|
402
498
|
});
|
|
499
|
+
if (options.graphql) configs.push({
|
|
500
|
+
files: [GLOB_GRAPHQL],
|
|
501
|
+
languageOptions: { parser: parserPlain },
|
|
502
|
+
name: "formatter/graphql",
|
|
503
|
+
rules: { "format/prettier": ["error", mergePrettierOptions(prettierOptions, { parser: "graphql" })] }
|
|
504
|
+
});
|
|
403
505
|
return configs;
|
|
404
506
|
}
|
|
405
507
|
//#endregion
|
|
@@ -761,37 +863,6 @@ async function jsonc(options = {}) {
|
|
|
761
863
|
}];
|
|
762
864
|
}
|
|
763
865
|
//#endregion
|
|
764
|
-
//#region src/constants.ts
|
|
765
|
-
const ReactRefreshAllowConstantExportPackages = ["vite"];
|
|
766
|
-
const RemixPackages = [
|
|
767
|
-
"@remix-run/node",
|
|
768
|
-
"@remix-run/react",
|
|
769
|
-
"@remix-run/serve",
|
|
770
|
-
"@remix-run/dev"
|
|
771
|
-
];
|
|
772
|
-
const ReactRouterPackages = [
|
|
773
|
-
"@react-router/node",
|
|
774
|
-
"@react-router/react",
|
|
775
|
-
"@react-router/serve",
|
|
776
|
-
"@react-router/dev"
|
|
777
|
-
];
|
|
778
|
-
const NextJsPackages = ["next"];
|
|
779
|
-
const ReactNativePackages = ["react-native", "expo"];
|
|
780
|
-
const ReactPackages = [
|
|
781
|
-
...RemixPackages,
|
|
782
|
-
...ReactRouterPackages,
|
|
783
|
-
...NextJsPackages,
|
|
784
|
-
...ReactNativePackages,
|
|
785
|
-
"react"
|
|
786
|
-
];
|
|
787
|
-
const ReactCompilerPackages = ["babel-plugin-react-compiler"];
|
|
788
|
-
const VuePackages = [
|
|
789
|
-
"vue",
|
|
790
|
-
"nuxt",
|
|
791
|
-
"vitepress",
|
|
792
|
-
"@slidev/cli"
|
|
793
|
-
];
|
|
794
|
-
//#endregion
|
|
795
866
|
//#region src/configs/jsx.ts
|
|
796
867
|
async function jsx(options = {}) {
|
|
797
868
|
const { a11y } = options;
|
|
@@ -806,13 +877,13 @@ async function jsx(options = {}) {
|
|
|
806
877
|
await ensurePackages(["eslint-plugin-jsx-a11y"]);
|
|
807
878
|
const jsxA11yPlugin = await interopDefault(import("eslint-plugin-jsx-a11y"));
|
|
808
879
|
const a11yConfig = jsxA11yPlugin.flatConfigs.recommended;
|
|
809
|
-
const isUsingNext =
|
|
880
|
+
const isUsingNext = isPackageExists("next");
|
|
810
881
|
const a11yRules = {
|
|
811
882
|
...a11yConfig.rules || {},
|
|
812
|
-
"jsx-a11y/alt-text": ["
|
|
883
|
+
...isUsingNext ? { "jsx-a11y/alt-text": ["warn", {
|
|
813
884
|
elements: ["img"],
|
|
814
|
-
img: [
|
|
815
|
-
}],
|
|
885
|
+
img: ["Image"]
|
|
886
|
+
}] } : {},
|
|
816
887
|
...typeof a11y === "object" && a11y.overrides ? a11y.overrides : {}
|
|
817
888
|
};
|
|
818
889
|
return [{
|
|
@@ -865,21 +936,6 @@ async function markdown(options = {}) {
|
|
|
865
936
|
...overridesMarkdown
|
|
866
937
|
}
|
|
867
938
|
},
|
|
868
|
-
{
|
|
869
|
-
files,
|
|
870
|
-
name: "markdown/disables/markdown",
|
|
871
|
-
rules: {
|
|
872
|
-
"command/command": "off",
|
|
873
|
-
"no-irregular-whitespace": "off",
|
|
874
|
-
"perfectionist/sort-exports": "off",
|
|
875
|
-
"perfectionist/sort-imports": "off",
|
|
876
|
-
"regexp/no-legacy-features": "off",
|
|
877
|
-
"regexp/no-missing-g-flag": "off",
|
|
878
|
-
"regexp/no-useless-dollar-replacements": "off",
|
|
879
|
-
"regexp/no-useless-flag": "off",
|
|
880
|
-
"style/indent": "off"
|
|
881
|
-
}
|
|
882
|
-
},
|
|
883
939
|
{
|
|
884
940
|
files: [GLOB_MARKDOWN_CODE, ...componentExts.map((ext) => `${GLOB_MARKDOWN}/**/*.${ext}`)],
|
|
885
941
|
languageOptions: { parserOptions: { ecmaFeatures: { impliedStrict: true } } },
|
|
@@ -918,41 +974,32 @@ async function markdown(options = {}) {
|
|
|
918
974
|
}
|
|
919
975
|
//#endregion
|
|
920
976
|
//#region src/configs/nextjs.ts
|
|
977
|
+
function normalizeRules(rules) {
|
|
978
|
+
return Object.fromEntries(Object.entries(rules).map(([key, value]) => [key, typeof value === "string" ? [value] : value]));
|
|
979
|
+
}
|
|
921
980
|
async function nextjs(options = {}) {
|
|
922
981
|
const { files = [GLOB_SRC], overrides = {} } = options;
|
|
923
982
|
await ensurePackages(["@next/eslint-plugin-next"]);
|
|
983
|
+
const pluginNextJS = await interopDefault(import("@next/eslint-plugin-next"));
|
|
984
|
+
function getRules(name) {
|
|
985
|
+
const rules = pluginNextJS.configs?.[name]?.rules;
|
|
986
|
+
if (!rules) throw new Error(`[@ghettoddos/eslint-config] Failed to find config ${name} in @next/eslint-plugin-next`);
|
|
987
|
+
return normalizeRules(rules);
|
|
988
|
+
}
|
|
924
989
|
return [{
|
|
925
|
-
name: "
|
|
926
|
-
plugins: { next:
|
|
990
|
+
name: "nextjs/setup",
|
|
991
|
+
plugins: { next: pluginNextJS }
|
|
927
992
|
}, {
|
|
928
993
|
files,
|
|
929
994
|
languageOptions: {
|
|
930
995
|
parserOptions: { ecmaFeatures: { jsx: true } },
|
|
931
996
|
sourceType: "module"
|
|
932
997
|
},
|
|
933
|
-
name: "
|
|
998
|
+
name: "nextjs/rules",
|
|
934
999
|
rules: {
|
|
935
|
-
"
|
|
936
|
-
"
|
|
937
|
-
"
|
|
938
|
-
"next/next-script-for-ga": "warn",
|
|
939
|
-
"next/no-assign-module-variable": "error",
|
|
940
|
-
"next/no-async-client-component": "warn",
|
|
941
|
-
"next/no-before-interactive-script-outside-document": "warn",
|
|
942
|
-
"next/no-css-tags": "warn",
|
|
943
|
-
"next/no-document-import-in-page": "error",
|
|
944
|
-
"next/no-duplicate-head": "error",
|
|
945
|
-
"next/no-head-element": "warn",
|
|
946
|
-
"next/no-head-import-in-document": "error",
|
|
947
|
-
"next/no-html-link-for-pages": "warn",
|
|
948
|
-
"next/no-img-element": "warn",
|
|
949
|
-
"next/no-page-custom-font": "warn",
|
|
950
|
-
"next/no-script-component-in-head": "error",
|
|
951
|
-
"next/no-styled-jsx-in-document": "warn",
|
|
952
|
-
"next/no-sync-scripts": "warn",
|
|
953
|
-
"next/no-title-in-document-head": "warn",
|
|
954
|
-
"next/no-typos": "warn",
|
|
955
|
-
"next/no-unwanted-polyfillio": "warn",
|
|
1000
|
+
...getRules("recommended"),
|
|
1001
|
+
...getRules("core-web-vitals"),
|
|
1002
|
+
"node/prefer-global/process": "off",
|
|
956
1003
|
...overrides
|
|
957
1004
|
},
|
|
958
1005
|
settings: { react: { version: "detect" } }
|
|
@@ -986,7 +1033,8 @@ async function node() {
|
|
|
986
1033
|
*
|
|
987
1034
|
* @see https://github.com/azat-io/eslint-plugin-perfectionist
|
|
988
1035
|
*/
|
|
989
|
-
async function perfectionist() {
|
|
1036
|
+
async function perfectionist(options) {
|
|
1037
|
+
const { overrides = {} } = options;
|
|
990
1038
|
return [{
|
|
991
1039
|
name: "perfectionist/setup",
|
|
992
1040
|
plugins: { perfectionist: pluginPerfectionist },
|
|
@@ -1028,7 +1076,8 @@ async function perfectionist() {
|
|
|
1028
1076
|
"perfectionist/sort-named-imports": ["error", {
|
|
1029
1077
|
order: "asc",
|
|
1030
1078
|
type: "natural"
|
|
1031
|
-
}]
|
|
1079
|
+
}],
|
|
1080
|
+
...overrides
|
|
1032
1081
|
}
|
|
1033
1082
|
}];
|
|
1034
1083
|
}
|
|
@@ -1159,41 +1208,87 @@ async function pnpm(options) {
|
|
|
1159
1208
|
return configs;
|
|
1160
1209
|
}
|
|
1161
1210
|
//#endregion
|
|
1211
|
+
//#region src/configs/prefer-early-return.ts
|
|
1212
|
+
const defaultMaximumStatements = 1;
|
|
1213
|
+
const pluginPreferEarlyReturn = { rules: { "prefer-early-return": {
|
|
1214
|
+
create(context) {
|
|
1215
|
+
const maxStatements = (context.options[0] || { maximumStatements: defaultMaximumStatements }).maximumStatements;
|
|
1216
|
+
function isLonelyIfStatement(statement) {
|
|
1217
|
+
return statement.type === "IfStatement" && statement.alternate == null;
|
|
1218
|
+
}
|
|
1219
|
+
function isOffendingConsequent(consequent) {
|
|
1220
|
+
return consequent.type === "ExpressionStatement" && maxStatements === 0 || consequent.type === "BlockStatement" && consequent.body.length > maxStatements;
|
|
1221
|
+
}
|
|
1222
|
+
function isOffendingIfStatement(statement) {
|
|
1223
|
+
return isLonelyIfStatement(statement) && isOffendingConsequent(statement.consequent);
|
|
1224
|
+
}
|
|
1225
|
+
function hasSimplifiableConditionalBody(functionBody) {
|
|
1226
|
+
const body = functionBody.body;
|
|
1227
|
+
return functionBody.type === "BlockStatement" && body.length === 1 && isOffendingIfStatement(body[0]);
|
|
1228
|
+
}
|
|
1229
|
+
function checkFunctionBody(functionNode) {
|
|
1230
|
+
const body = functionNode.body;
|
|
1231
|
+
if (hasSimplifiableConditionalBody(body)) context.report(body, "Prefer an early return to a conditionally-wrapped function body");
|
|
1232
|
+
}
|
|
1233
|
+
return {
|
|
1234
|
+
ArrowFunctionExpression: checkFunctionBody,
|
|
1235
|
+
FunctionDeclaration: checkFunctionBody,
|
|
1236
|
+
FunctionExpression: checkFunctionBody
|
|
1237
|
+
};
|
|
1238
|
+
},
|
|
1239
|
+
meta: {
|
|
1240
|
+
docs: {
|
|
1241
|
+
category: "Best Practices",
|
|
1242
|
+
description: "Prefer early returns over full-body conditional wrapping in function declarations.",
|
|
1243
|
+
recommended: false,
|
|
1244
|
+
uri: "https://github.com/Shopify/web-configs/blob/main/packages/eslint-plugin/docs/rules/prefer-early-return.md"
|
|
1245
|
+
},
|
|
1246
|
+
schema: [{
|
|
1247
|
+
additionalProperties: false,
|
|
1248
|
+
properties: { maximumStatements: { type: "integer" } },
|
|
1249
|
+
type: "object"
|
|
1250
|
+
}]
|
|
1251
|
+
}
|
|
1252
|
+
} } };
|
|
1253
|
+
async function preferEarlyReturn() {
|
|
1254
|
+
return [{
|
|
1255
|
+
name: "prefer-early-return/rules",
|
|
1256
|
+
plugins: { "prefer-early-return": pluginPreferEarlyReturn },
|
|
1257
|
+
rules: { "prefer-early-return/prefer-early-return": "error" }
|
|
1258
|
+
}];
|
|
1259
|
+
}
|
|
1260
|
+
//#endregion
|
|
1162
1261
|
//#region src/configs/react.ts
|
|
1262
|
+
const ReactRefreshAllowConstantExportPackages = ["vite"];
|
|
1263
|
+
const RemixPackages = [
|
|
1264
|
+
"@remix-run/node",
|
|
1265
|
+
"@remix-run/react",
|
|
1266
|
+
"@remix-run/serve",
|
|
1267
|
+
"@remix-run/dev"
|
|
1268
|
+
];
|
|
1269
|
+
const ReactRouterPackages = [
|
|
1270
|
+
"@react-router/node",
|
|
1271
|
+
"@react-router/react",
|
|
1272
|
+
"@react-router/serve",
|
|
1273
|
+
"@react-router/dev"
|
|
1274
|
+
];
|
|
1275
|
+
const NextJsPackages = ["next"];
|
|
1163
1276
|
async function react(options = {}) {
|
|
1164
|
-
const { files = [GLOB_SRC], filesTypeAware = [GLOB_TS, GLOB_TSX], ignoresTypeAware = [`${GLOB_MARKDOWN}/**`], overrides = {}, tsconfigPath
|
|
1165
|
-
await ensurePackages([
|
|
1166
|
-
"@eslint-react/eslint-plugin",
|
|
1167
|
-
"eslint-plugin-react-hooks",
|
|
1168
|
-
"eslint-plugin-react-refresh"
|
|
1169
|
-
]);
|
|
1277
|
+
const { files = [GLOB_SRC], filesTypeAware = [GLOB_TS, GLOB_TSX], ignoresTypeAware = [`${GLOB_MARKDOWN}/**`], overrides = {}, tsconfigPath } = options;
|
|
1278
|
+
await ensurePackages(["@eslint-react/eslint-plugin", "eslint-plugin-react-refresh"]);
|
|
1170
1279
|
const isTypeAware = !!tsconfigPath;
|
|
1171
|
-
const typeAwareRules = {
|
|
1172
|
-
|
|
1173
|
-
"react/no-implicit-key": "error"
|
|
1174
|
-
};
|
|
1175
|
-
const [pluginReact, pluginReactHooks, pluginReactRefresh] = await Promise.all([
|
|
1176
|
-
interopDefault(import("@eslint-react/eslint-plugin")),
|
|
1177
|
-
interopDefault(import("eslint-plugin-react-hooks")),
|
|
1178
|
-
interopDefault(import("eslint-plugin-react-refresh"))
|
|
1179
|
-
]);
|
|
1280
|
+
const typeAwareRules = { "react/no-leaked-conditional-rendering": "error" };
|
|
1281
|
+
const [pluginReact, pluginReactRefresh] = await Promise.all([interopDefault(import("@eslint-react/eslint-plugin")), interopDefault(import("eslint-plugin-react-refresh"))]);
|
|
1180
1282
|
const isAllowConstantExport = ReactRefreshAllowConstantExportPackages.some((i) => isPackageExists(i));
|
|
1181
1283
|
const isUsingRemix = RemixPackages.some((i) => isPackageExists(i));
|
|
1182
1284
|
const isUsingReactRouter = ReactRouterPackages.some((i) => isPackageExists(i));
|
|
1183
1285
|
const isUsingNext = NextJsPackages.some((i) => isPackageExists(i));
|
|
1184
|
-
const plugins = pluginReact.configs.all.plugins;
|
|
1185
1286
|
return [
|
|
1186
1287
|
{
|
|
1187
1288
|
name: "react/setup",
|
|
1188
1289
|
plugins: {
|
|
1189
|
-
"react": plugins["@eslint-react"],
|
|
1190
|
-
"react-
|
|
1191
|
-
"react-hooks": pluginReactHooks,
|
|
1192
|
-
"react-hooks-extra": plugins["@eslint-react/hooks-extra"],
|
|
1193
|
-
"react-naming-convention": plugins["@eslint-react/naming-convention"],
|
|
1194
|
-
"react-refresh": pluginReactRefresh,
|
|
1195
|
-
"react-rsc": plugins["@eslint-react/rsc"],
|
|
1196
|
-
"react-web-api": plugins["@eslint-react/web-api"]
|
|
1290
|
+
"react": pluginReact.configs.all.plugins["@eslint-react"],
|
|
1291
|
+
"react-refresh": pluginReactRefresh
|
|
1197
1292
|
}
|
|
1198
1293
|
},
|
|
1199
1294
|
{
|
|
@@ -1204,89 +1299,7 @@ async function react(options = {}) {
|
|
|
1204
1299
|
},
|
|
1205
1300
|
name: "react/rules",
|
|
1206
1301
|
rules: {
|
|
1207
|
-
|
|
1208
|
-
"react/jsx-no-comment-textnodes": "warn",
|
|
1209
|
-
"react/jsx-no-duplicate-props": "warn",
|
|
1210
|
-
"react/jsx-uses-react": "warn",
|
|
1211
|
-
"react/jsx-uses-vars": "warn",
|
|
1212
|
-
"react/no-access-state-in-setstate": "error",
|
|
1213
|
-
"react/no-array-index-key": "warn",
|
|
1214
|
-
"react/no-children-count": "warn",
|
|
1215
|
-
"react/no-children-for-each": "warn",
|
|
1216
|
-
"react/no-children-map": "warn",
|
|
1217
|
-
"react/no-children-only": "warn",
|
|
1218
|
-
"react/no-children-to-array": "warn",
|
|
1219
|
-
"react/no-clone-element": "warn",
|
|
1220
|
-
"react/no-component-will-mount": "error",
|
|
1221
|
-
"react/no-component-will-receive-props": "error",
|
|
1222
|
-
"react/no-component-will-update": "error",
|
|
1223
|
-
"react/no-context-provider": "warn",
|
|
1224
|
-
"react/no-create-ref": "error",
|
|
1225
|
-
"react/no-default-props": "error",
|
|
1226
|
-
"react/no-direct-mutation-state": "error",
|
|
1227
|
-
"react/no-forward-ref": "warn",
|
|
1228
|
-
"react/no-missing-key": "error",
|
|
1229
|
-
"react/no-nested-component-definitions": "error",
|
|
1230
|
-
"react/no-nested-lazy-component-declarations": "error",
|
|
1231
|
-
"react/no-prop-types": "error",
|
|
1232
|
-
"react/no-redundant-should-component-update": "error",
|
|
1233
|
-
"react/no-set-state-in-component-did-mount": "warn",
|
|
1234
|
-
"react/no-set-state-in-component-did-update": "warn",
|
|
1235
|
-
"react/no-set-state-in-component-will-update": "warn",
|
|
1236
|
-
"react/no-string-refs": "error",
|
|
1237
|
-
"react/no-unnecessary-use-prefix": "warn",
|
|
1238
|
-
"react/no-unsafe-component-will-mount": "warn",
|
|
1239
|
-
"react/no-unsafe-component-will-receive-props": "warn",
|
|
1240
|
-
"react/no-unsafe-component-will-update": "warn",
|
|
1241
|
-
"react/no-unused-class-component-members": "warn",
|
|
1242
|
-
"react/no-use-context": "warn",
|
|
1243
|
-
"react/no-useless-forward-ref": "warn",
|
|
1244
|
-
"react/prefer-use-state-lazy-initialization": "warn",
|
|
1245
|
-
"react/prefer-namespace-import": "error",
|
|
1246
|
-
"react/jsx-shorthand-boolean": "warn",
|
|
1247
|
-
"react/prefer-destructuring-assignment": "warn",
|
|
1248
|
-
"react/no-missing-context-display-name": "warn",
|
|
1249
|
-
"react/no-missing-component-display-name": "warn",
|
|
1250
|
-
"react-rsc/function-definition": "error",
|
|
1251
|
-
"react-dom/no-dangerously-set-innerhtml": "warn",
|
|
1252
|
-
"react-dom/no-dangerously-set-innerhtml-with-children": "error",
|
|
1253
|
-
"react-dom/no-find-dom-node": "error",
|
|
1254
|
-
"react-dom/no-flush-sync": "error",
|
|
1255
|
-
"react-dom/no-hydrate": "error",
|
|
1256
|
-
"react-dom/no-namespace": "error",
|
|
1257
|
-
"react-dom/no-render": "error",
|
|
1258
|
-
"react-dom/no-render-return-value": "error",
|
|
1259
|
-
"react-dom/no-script-url": "warn",
|
|
1260
|
-
"react-dom/no-unsafe-iframe-sandbox": "warn",
|
|
1261
|
-
"react-dom/no-use-form-state": "error",
|
|
1262
|
-
"react-dom/no-void-elements-with-children": "error",
|
|
1263
|
-
"react-hooks-extra/no-direct-set-state-in-use-effect": "warn",
|
|
1264
|
-
"react-naming-convention/context-name": "warn",
|
|
1265
|
-
"react-naming-convention/ref-name": "warn",
|
|
1266
|
-
"react-naming-convention/use-state": "warn",
|
|
1267
|
-
"react-web-api/no-leaked-event-listener": "warn",
|
|
1268
|
-
"react-web-api/no-leaked-interval": "warn",
|
|
1269
|
-
"react-web-api/no-leaked-resize-observer": "warn",
|
|
1270
|
-
"react-web-api/no-leaked-timeout": "warn",
|
|
1271
|
-
"react-hooks/rules-of-hooks": "error",
|
|
1272
|
-
"react-hooks/exhaustive-deps": "warn",
|
|
1273
|
-
...reactCompiler ? {
|
|
1274
|
-
"react-hooks/config": "error",
|
|
1275
|
-
"react-hooks/error-boundaries": "error",
|
|
1276
|
-
"react-hooks/component-hook-factories": "error",
|
|
1277
|
-
"react-hooks/gating": "error",
|
|
1278
|
-
"react-hooks/globals": "error",
|
|
1279
|
-
"react-hooks/immutability": "error",
|
|
1280
|
-
"react-hooks/preserve-manual-memoization": "error",
|
|
1281
|
-
"react-hooks/purity": "error",
|
|
1282
|
-
"react-hooks/refs": "error",
|
|
1283
|
-
"react-hooks/set-state-in-effect": "error",
|
|
1284
|
-
"react-hooks/set-state-in-render": "error",
|
|
1285
|
-
"react-hooks/static-components": "error",
|
|
1286
|
-
"react-hooks/unsupported-syntax": "warn",
|
|
1287
|
-
"react-hooks/use-memo": "error",
|
|
1288
|
-
"react-hooks/incompatible-library": "warn"
|
|
1289
|
-
} : {},
|
|
1302
|
+
...pluginReact.configs.recommended.rules,
|
|
1290
1303
|
"react-refresh/only-export-components": ["error", {
|
|
1291
1304
|
allowConstantExport: isAllowConstantExport,
|
|
1292
1305
|
allowExportNames: [...isUsingNext ? [
|
|
@@ -1323,12 +1336,8 @@ async function react(options = {}) {
|
|
|
1323
1336
|
files: filesTypeAware,
|
|
1324
1337
|
name: "react/typescript",
|
|
1325
1338
|
rules: {
|
|
1326
|
-
"react-
|
|
1327
|
-
"react-
|
|
1328
|
-
"react/jsx-no-duplicate-props": "off",
|
|
1329
|
-
"react/jsx-no-undef": "off",
|
|
1330
|
-
"react/jsx-uses-react": "off",
|
|
1331
|
-
"react/jsx-uses-vars": "off"
|
|
1339
|
+
"react/dom-no-string-style-prop": "off",
|
|
1340
|
+
"react/dom-no-unknown-property": "off"
|
|
1332
1341
|
}
|
|
1333
1342
|
},
|
|
1334
1343
|
...isTypeAware ? [{
|
|
@@ -1340,71 +1349,6 @@ async function react(options = {}) {
|
|
|
1340
1349
|
];
|
|
1341
1350
|
}
|
|
1342
1351
|
//#endregion
|
|
1343
|
-
//#region src/configs/react-native.ts
|
|
1344
|
-
async function reactNative(options = {}) {
|
|
1345
|
-
const { expo = isPackageExists("expo"), files = [GLOB_SRC], overrides = {} } = options;
|
|
1346
|
-
await ensurePackages([
|
|
1347
|
-
"@react-native/eslint-plugin",
|
|
1348
|
-
"eslint-plugin-react-native",
|
|
1349
|
-
...expo ? ["eslint-plugin-expo"] : []
|
|
1350
|
-
]);
|
|
1351
|
-
const [pluginReactNative, pluginReactNativeCommunity, pluginExpo] = await Promise.all([
|
|
1352
|
-
interopDefault(import("@react-native/eslint-plugin")),
|
|
1353
|
-
interopDefault(import("eslint-plugin-react-native")),
|
|
1354
|
-
...expo ? [interopDefault(import("eslint-plugin-expo"))] : []
|
|
1355
|
-
]);
|
|
1356
|
-
return [{
|
|
1357
|
-
name: "react-native/setup",
|
|
1358
|
-
plugins: {
|
|
1359
|
-
"react-native": pluginReactNative,
|
|
1360
|
-
"react-native-community": pluginReactNativeCommunity,
|
|
1361
|
-
...expo ? { expo: pluginExpo } : {}
|
|
1362
|
-
}
|
|
1363
|
-
}, {
|
|
1364
|
-
files,
|
|
1365
|
-
languageOptions: {
|
|
1366
|
-
globals: {
|
|
1367
|
-
...globals.browser,
|
|
1368
|
-
"__DEV__": "readonly",
|
|
1369
|
-
"alert": false,
|
|
1370
|
-
"cancelAnimationFrame": false,
|
|
1371
|
-
"cancelIdleCallback": false,
|
|
1372
|
-
"clearImmediate": false,
|
|
1373
|
-
"ErrorUtils": false,
|
|
1374
|
-
"fetch": false,
|
|
1375
|
-
"FormData": false,
|
|
1376
|
-
"navigator": false,
|
|
1377
|
-
"process": false,
|
|
1378
|
-
"requestAnimationFrame": false,
|
|
1379
|
-
"requestIdleCallback": false,
|
|
1380
|
-
"setImmediate": false,
|
|
1381
|
-
"shared-node-browser": true,
|
|
1382
|
-
"window": false,
|
|
1383
|
-
"XMLHttpRequest": false
|
|
1384
|
-
},
|
|
1385
|
-
parserOptions: { ecmaFeatures: { jsx: true } },
|
|
1386
|
-
sourceType: "module"
|
|
1387
|
-
},
|
|
1388
|
-
name: "react-native/rules",
|
|
1389
|
-
rules: {
|
|
1390
|
-
"node/prefer-global/process": "off",
|
|
1391
|
-
"react-native-community/no-inline-styles": "warn",
|
|
1392
|
-
"react-native-community/no-raw-text": "warn",
|
|
1393
|
-
"react-native-community/no-single-element-style-arrays": "warn",
|
|
1394
|
-
"react-native-community/no-unused-styles": "warn",
|
|
1395
|
-
"react-native/no-deep-imports": "error",
|
|
1396
|
-
"ts/no-require-imports": ["warn", { allow: ["\\.(aac|aiff|avif|bmp|caf|db|gif|heic|html|jpeg|jpg|json|m4a|m4v|mov|mp3|mp4|mpeg|mpg|otf|pdf|png|psd|svg|ttf|wav|webm|webp|xml|yaml|yml|zip)$"] }],
|
|
1397
|
-
...expo ? {
|
|
1398
|
-
"expo/no-dynamic-env-var": "error",
|
|
1399
|
-
"expo/no-env-var-destructuring": "error",
|
|
1400
|
-
"expo/use-dom-exports": "error"
|
|
1401
|
-
} : {},
|
|
1402
|
-
...overrides
|
|
1403
|
-
},
|
|
1404
|
-
settings: { react: { version: "detect" } }
|
|
1405
|
-
}];
|
|
1406
|
-
}
|
|
1407
|
-
//#endregion
|
|
1408
1352
|
//#region src/configs/regexp.ts
|
|
1409
1353
|
async function regexp(options = {}) {
|
|
1410
1354
|
const config = configs["flat/recommended"];
|
|
@@ -1422,6 +1366,18 @@ async function regexp(options = {}) {
|
|
|
1422
1366
|
}];
|
|
1423
1367
|
}
|
|
1424
1368
|
//#endregion
|
|
1369
|
+
//#region src/configs/sonarjs.ts
|
|
1370
|
+
async function sonarjs(options = {}) {
|
|
1371
|
+
return [{
|
|
1372
|
+
name: "sonarjs/rules",
|
|
1373
|
+
plugins: { sonarjs: pluginSonarJs },
|
|
1374
|
+
rules: {
|
|
1375
|
+
...pluginSonarJs.configs.recommended.rules,
|
|
1376
|
+
...options.overrides
|
|
1377
|
+
}
|
|
1378
|
+
}];
|
|
1379
|
+
}
|
|
1380
|
+
//#endregion
|
|
1425
1381
|
//#region src/configs/sort.ts
|
|
1426
1382
|
/**
|
|
1427
1383
|
* Sort package.json
|
|
@@ -1656,29 +1612,63 @@ function sortTsconfig() {
|
|
|
1656
1612
|
}
|
|
1657
1613
|
//#endregion
|
|
1658
1614
|
//#region src/configs/tailwindcss.ts
|
|
1659
|
-
|
|
1660
|
-
|
|
1615
|
+
let _pluginTailwindCSS;
|
|
1616
|
+
async function tailwindcss(options) {
|
|
1617
|
+
const { detectComponentClasses, entryPoint, overrides = {}, rootFontSize, selectors, stylistic = true, tsconfigPath } = options;
|
|
1661
1618
|
await ensurePackages(["eslint-plugin-better-tailwindcss"]);
|
|
1662
1619
|
const [pluginTailwindCSS] = await Promise.all([interopDefault(import("eslint-plugin-better-tailwindcss"))]);
|
|
1663
|
-
|
|
1620
|
+
_pluginTailwindCSS = _pluginTailwindCSS || {
|
|
1621
|
+
...pluginTailwindCSS,
|
|
1622
|
+
rules: {
|
|
1623
|
+
...pluginTailwindCSS.rules,
|
|
1624
|
+
"multiline-classname": {
|
|
1625
|
+
create(context) {
|
|
1626
|
+
return { JSXAttribute(node) {
|
|
1627
|
+
if (node.name?.type !== "JSXIdentifier") return;
|
|
1628
|
+
if (node.name.name !== "className") return;
|
|
1629
|
+
if (node.value?.type === "Literal" && typeof node.value.value === "string" && node.value.value.includes("\n")) {
|
|
1630
|
+
const raw = node.value.value;
|
|
1631
|
+
context.report({
|
|
1632
|
+
fix(fixer) {
|
|
1633
|
+
const escaped = raw.replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
1634
|
+
return fixer.replaceText(node.value, `{\`${escaped}\`}`);
|
|
1635
|
+
},
|
|
1636
|
+
message: "Multiline className strings can cause hydration errors. Use a template literal/expression instead.",
|
|
1637
|
+
node: node.value
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1640
|
+
} };
|
|
1641
|
+
},
|
|
1642
|
+
meta: {
|
|
1643
|
+
fixable: "code",
|
|
1644
|
+
schema: [],
|
|
1645
|
+
type: "suggestion"
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
};
|
|
1650
|
+
const { indent = 2, printWidth = 100, tabWidth = 4 } = typeof stylistic === "boolean" ? {} : stylistic;
|
|
1664
1651
|
return [{
|
|
1665
1652
|
name: "tailwindcss",
|
|
1666
|
-
plugins: { tailwindcss:
|
|
1653
|
+
plugins: { "better-tailwindcss": _pluginTailwindCSS },
|
|
1667
1654
|
rules: {
|
|
1668
1655
|
...stylistic ? {
|
|
1669
|
-
|
|
1670
|
-
"tailwindcss/enforce-consistent-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1656
|
+
...pluginTailwindCSS.configs.stylistic.rules,
|
|
1657
|
+
"better-tailwindcss/enforce-consistent-line-wrapping": ["warn", {
|
|
1658
|
+
indent: typeof indent === "number" ? indent : indent === "tab" ? "tab" : 2,
|
|
1659
|
+
printWidth,
|
|
1660
|
+
tabWidth: typeof indent === "number" ? indent : indent === "tab" ? tabWidth : 2
|
|
1661
|
+
}],
|
|
1662
|
+
"better-tailwindcss/multiline-classname": "error"
|
|
1675
1663
|
} : {},
|
|
1676
|
-
|
|
1677
|
-
"tailwindcss/no-unknown-classes": "error",
|
|
1664
|
+
...pluginTailwindCSS.configs.correctness.rules,
|
|
1678
1665
|
...overrides
|
|
1679
1666
|
},
|
|
1680
1667
|
settings: { "better-tailwindcss": {
|
|
1668
|
+
detectComponentClasses,
|
|
1681
1669
|
entryPoint,
|
|
1670
|
+
rootFontSize,
|
|
1671
|
+
selectors,
|
|
1682
1672
|
tsconfig: tsconfigPath
|
|
1683
1673
|
} }
|
|
1684
1674
|
}];
|
|
@@ -1713,6 +1703,7 @@ async function test(options = {}) {
|
|
|
1713
1703
|
"test/prefer-hooks-in-order": "error",
|
|
1714
1704
|
"test/prefer-lowercase-title": "error",
|
|
1715
1705
|
"antfu/no-top-level-await": "off",
|
|
1706
|
+
"baseline-js/use-baseline": "off",
|
|
1716
1707
|
"e18e/prefer-static-regex": "off",
|
|
1717
1708
|
"no-unused-expressions": "off",
|
|
1718
1709
|
"node/prefer-global/process": "off",
|
|
@@ -1893,8 +1884,8 @@ async function typescript(options = {}) {
|
|
|
1893
1884
|
}
|
|
1894
1885
|
}] : [],
|
|
1895
1886
|
...erasableOnly ? [{
|
|
1896
|
-
name: "
|
|
1897
|
-
plugins: { "erasable-syntax-only": await interopDefault(import("./lib-
|
|
1887
|
+
name: "typescript/erasable-syntax-only",
|
|
1888
|
+
plugins: { "erasable-syntax-only": await interopDefault(import("./lib-C1Uxp5ZW.mjs")) },
|
|
1898
1889
|
rules: {
|
|
1899
1890
|
"erasable-syntax-only/enums": "error",
|
|
1900
1891
|
"erasable-syntax-only/import-aliases": "error",
|
|
@@ -1955,12 +1946,7 @@ async function unocss(options = {}) {
|
|
|
1955
1946
|
async function vue(options = {}) {
|
|
1956
1947
|
const { a11y = false, files = [GLOB_VUE], overrides = {}, stylistic = true, vueVersion = 3 } = options;
|
|
1957
1948
|
const sfcBlocks = options.sfcBlocks === true ? {} : options.sfcBlocks ?? {};
|
|
1958
|
-
const { indent = 2 } = typeof stylistic === "boolean" ? {} : stylistic;
|
|
1959
|
-
await ensurePackages([
|
|
1960
|
-
"eslint-plugin-vue",
|
|
1961
|
-
"vue-eslint-parser",
|
|
1962
|
-
"eslint-processor-vue-blocks"
|
|
1963
|
-
]);
|
|
1949
|
+
const { braceStyle = "stroustrup", indent = 2 } = typeof stylistic === "boolean" ? {} : stylistic;
|
|
1964
1950
|
if (a11y) await ensurePackages(["eslint-plugin-vuejs-accessibility"]);
|
|
1965
1951
|
const [pluginVue, parserVue, processorVueBlocks, pluginVueA11y] = await Promise.all([
|
|
1966
1952
|
interopDefault(import("eslint-plugin-vue")),
|
|
@@ -2101,7 +2087,7 @@ async function vue(options = {}) {
|
|
|
2101
2087
|
}],
|
|
2102
2088
|
"vue/brace-style": [
|
|
2103
2089
|
"error",
|
|
2104
|
-
|
|
2090
|
+
braceStyle,
|
|
2105
2091
|
{ allowSingleLine: true }
|
|
2106
2092
|
],
|
|
2107
2093
|
"vue/comma-dangle": ["error", "always-multiline"],
|
|
@@ -2203,22 +2189,6 @@ async function yaml(options = {}) {
|
|
|
2203
2189
|
}];
|
|
2204
2190
|
}
|
|
2205
2191
|
//#endregion
|
|
2206
|
-
//#region src/configs/e18e.ts
|
|
2207
|
-
async function e18e(options = {}) {
|
|
2208
|
-
const { isInEditor = false, modernization = true, type = "app", moduleReplacements = type === "lib" && isInEditor, overrides = {}, performanceImprovements = true } = options;
|
|
2209
|
-
const configs = pluginE18e.configs;
|
|
2210
|
-
return [{
|
|
2211
|
-
name: "antfu/e18e/rules",
|
|
2212
|
-
plugins: { e18e: pluginE18e },
|
|
2213
|
-
rules: {
|
|
2214
|
-
...modernization ? { ...configs.modernization.rules } : {},
|
|
2215
|
-
...moduleReplacements ? { ...configs.moduleReplacements.rules } : {},
|
|
2216
|
-
...performanceImprovements ? { ...configs.performanceImprovements.rules } : {},
|
|
2217
|
-
...overrides
|
|
2218
|
-
}
|
|
2219
|
-
}];
|
|
2220
|
-
}
|
|
2221
|
-
//#endregion
|
|
2222
2192
|
//#region src/factory.ts
|
|
2223
2193
|
const flatConfigProps = [
|
|
2224
2194
|
"name",
|
|
@@ -2229,15 +2199,17 @@ const flatConfigProps = [
|
|
|
2229
2199
|
"rules",
|
|
2230
2200
|
"settings"
|
|
2231
2201
|
];
|
|
2202
|
+
const VuePackages = [
|
|
2203
|
+
"vue",
|
|
2204
|
+
"nuxt",
|
|
2205
|
+
"vitepress",
|
|
2206
|
+
"@slidev/cli"
|
|
2207
|
+
];
|
|
2232
2208
|
const defaultPluginRenaming = {
|
|
2233
2209
|
"@eslint-react": "react",
|
|
2234
|
-
"@eslint-react/dom": "react-dom",
|
|
2235
|
-
"@eslint-react/hooks-extra": "react-hooks-extra",
|
|
2236
|
-
"@eslint-react/naming-convention": "react-naming-convention",
|
|
2237
2210
|
"@next/next": "next",
|
|
2238
2211
|
"@stylistic": "style",
|
|
2239
2212
|
"@typescript-eslint": "ts",
|
|
2240
|
-
"better-tailwindcss": "tailwindcss",
|
|
2241
2213
|
"import-lite": "import",
|
|
2242
2214
|
"n": "node",
|
|
2243
2215
|
"vitest": "test",
|
|
@@ -2254,7 +2226,7 @@ const defaultPluginRenaming = {
|
|
|
2254
2226
|
* The merged ESLint configurations.
|
|
2255
2227
|
*/
|
|
2256
2228
|
function config(options = {}, ...userConfigs) {
|
|
2257
|
-
const { autoRenamePlugins = true, componentExts = [], e18e: enableE18e = true, gitignore: enableGitignore = true, ignores: userIgnores = [], imports: enableImports = true, jsdoc: enableJsdoc = true, jsx: enableJsx = true, nextjs:
|
|
2229
|
+
const { autoRenamePlugins = true, baseline: enableBaseLineJs = false, componentExts = [], e18e: enableE18e = true, gitignore: enableGitignore = true, ignores: userIgnores = [], imports: enableImports = true, jsdoc: enableJsdoc = true, jsx: enableJsx = true, nextjs: enableNextjs = isPackageExists("next"), node: enableNode = true, perfectionist: enablePerfectionist = true, pnpm: enableCatalogs = !!findUpSync("pnpm-workspace.yaml"), react: enableReact = false, regexp: enableRegexp = true, sonarjs: enableSonarJs = false, tailwindcss: enableTailwindCSS = false, type: appType = "app", typescript: enableTypeScript = isPackageExists("typescript") || isPackageExists("@typescript/native-preview"), unicorn: enableUnicorn = true, unocss: enableUnoCSS = false, vue: enableVue = VuePackages.some((i) => isPackageExists(i)) } = options;
|
|
2258
2230
|
let isInEditor = options.isInEditor;
|
|
2259
2231
|
if (isInEditor == null) {
|
|
2260
2232
|
isInEditor = isInEditorEnv();
|
|
@@ -2276,17 +2248,20 @@ function config(options = {}, ...userConfigs) {
|
|
|
2276
2248
|
configs.push(ignores(userIgnores, !enableTypeScript), javascript({
|
|
2277
2249
|
isInEditor,
|
|
2278
2250
|
overrides: getOverrides(options, "javascript")
|
|
2279
|
-
}), comments(), command(),
|
|
2251
|
+
}), comments(), command(), deMorgan(), preferEarlyReturn());
|
|
2252
|
+
if (enablePerfectionist) configs.push(perfectionist({ overrides: getOverrides(options, "perfectionist") }));
|
|
2280
2253
|
if (enableNode) configs.push(node());
|
|
2281
2254
|
if (enableJsdoc) configs.push(jsdoc({ stylistic: stylisticOptions }));
|
|
2282
2255
|
if (enableImports) configs.push(imports({
|
|
2283
2256
|
stylistic: stylisticOptions,
|
|
2284
2257
|
...resolveSubOptions(options, "imports")
|
|
2285
2258
|
}));
|
|
2259
|
+
if (enableBaseLineJs) configs.push(baseline({ ...resolveSubOptions(options, "baseline") }));
|
|
2286
2260
|
if (enableE18e) configs.push(e18e({
|
|
2287
2261
|
isInEditor,
|
|
2288
2262
|
...enableE18e === true ? {} : enableE18e
|
|
2289
2263
|
}));
|
|
2264
|
+
if (enableSonarJs) configs.push(sonarjs({ ...resolveSubOptions(options, "sonarjs") }));
|
|
2290
2265
|
if (enableUnicorn) configs.push(unicorn(enableUnicorn === true ? {} : enableUnicorn));
|
|
2291
2266
|
if (enableVue) componentExts.push("vue");
|
|
2292
2267
|
if (enableJsx) configs.push(jsx(enableJsx === true ? {} : enableJsx));
|
|
@@ -2298,6 +2273,7 @@ function config(options = {}, ...userConfigs) {
|
|
|
2298
2273
|
}));
|
|
2299
2274
|
if (stylisticOptions) configs.push(stylistic({
|
|
2300
2275
|
...stylisticOptions,
|
|
2276
|
+
lessOpinionated: options.lessOpinionated,
|
|
2301
2277
|
overrides: getOverrides(options, "stylistic")
|
|
2302
2278
|
}));
|
|
2303
2279
|
if (enableRegexp) configs.push(regexp(typeof enableRegexp === "boolean" ? {} : enableRegexp));
|
|
@@ -2317,11 +2293,7 @@ function config(options = {}, ...userConfigs) {
|
|
|
2317
2293
|
overrides: getOverrides(options, "react"),
|
|
2318
2294
|
tsconfigPath
|
|
2319
2295
|
}));
|
|
2320
|
-
if (
|
|
2321
|
-
...resolveSubOptions(options, "reactNative"),
|
|
2322
|
-
overrides: getOverrides(options, "reactNative")
|
|
2323
|
-
}));
|
|
2324
|
-
if (enableNext) configs.push(nextjs({ overrides: getOverrides(options, "nextjs") }));
|
|
2296
|
+
if (enableNextjs) configs.push(nextjs({ overrides: getOverrides(options, "nextjs") }));
|
|
2325
2297
|
if (enableUnoCSS) configs.push(unocss({
|
|
2326
2298
|
...resolveSubOptions(options, "unocss"),
|
|
2327
2299
|
overrides: getOverrides(options, "unocss")
|
|
@@ -2367,6 +2339,7 @@ function config(options = {}, ...userConfigs) {
|
|
|
2367
2339
|
if (Object.keys(fusedConfig).length) configs.push([fusedConfig]);
|
|
2368
2340
|
let composer = new FlatConfigComposer();
|
|
2369
2341
|
composer = composer.append(...configs, ...userConfigs);
|
|
2342
|
+
if (options.markdown ?? true) composer = composer.setDefaultIgnores((prev) => [...prev, GLOB_MARKDOWN]);
|
|
2370
2343
|
if (autoRenamePlugins) composer = composer.renamePlugins(defaultPluginRenaming);
|
|
2371
2344
|
if (isInEditor) composer = composer.disableRulesFix([
|
|
2372
2345
|
"unused-imports/no-unused-imports",
|
|
@@ -2375,9 +2348,17 @@ function config(options = {}, ...userConfigs) {
|
|
|
2375
2348
|
], { builtinRules: () => import(["eslint", "use-at-your-own-risk"].join("/")).then((r) => r.builtinRules) });
|
|
2376
2349
|
return composer;
|
|
2377
2350
|
}
|
|
2351
|
+
function resolveSubOptions(options, key) {
|
|
2352
|
+
return typeof options[key] === "boolean" ? {} : options[key] || {};
|
|
2353
|
+
}
|
|
2354
|
+
function getOverrides(options, key) {
|
|
2355
|
+
const sub = resolveSubOptions(options, key);
|
|
2356
|
+
return { ..."overrides" in sub ? sub.overrides : {} };
|
|
2357
|
+
}
|
|
2378
2358
|
//#endregion
|
|
2379
2359
|
//#region src/config-presets.ts
|
|
2380
2360
|
const CONFIG_PRESET_FULL_ON = {
|
|
2361
|
+
baseline: true,
|
|
2381
2362
|
formatters: true,
|
|
2382
2363
|
gitignore: true,
|
|
2383
2364
|
imports: true,
|
|
@@ -2387,12 +2368,13 @@ const CONFIG_PRESET_FULL_ON = {
|
|
|
2387
2368
|
markdown: true,
|
|
2388
2369
|
nextjs: true,
|
|
2389
2370
|
node: true,
|
|
2371
|
+
perfectionist: true,
|
|
2390
2372
|
pnpm: true,
|
|
2391
|
-
react:
|
|
2392
|
-
reactNative: { expo: true },
|
|
2373
|
+
react: true,
|
|
2393
2374
|
regexp: true,
|
|
2375
|
+
sonarjs: true,
|
|
2394
2376
|
stylistic: { experimental: true },
|
|
2395
|
-
tailwindcss:
|
|
2377
|
+
tailwindcss: { entryPoint: "fixtures/stub.css" },
|
|
2396
2378
|
test: true,
|
|
2397
2379
|
toml: true,
|
|
2398
2380
|
typescript: {
|
|
@@ -2405,6 +2387,7 @@ const CONFIG_PRESET_FULL_ON = {
|
|
|
2405
2387
|
yaml: true
|
|
2406
2388
|
};
|
|
2407
2389
|
const CONFIG_PRESET_FULL_OFF = {
|
|
2390
|
+
baseline: false,
|
|
2408
2391
|
formatters: false,
|
|
2409
2392
|
gitignore: false,
|
|
2410
2393
|
imports: false,
|
|
@@ -2414,10 +2397,11 @@ const CONFIG_PRESET_FULL_OFF = {
|
|
|
2414
2397
|
markdown: false,
|
|
2415
2398
|
nextjs: false,
|
|
2416
2399
|
node: false,
|
|
2400
|
+
perfectionist: false,
|
|
2417
2401
|
pnpm: false,
|
|
2418
2402
|
react: false,
|
|
2419
|
-
reactNative: false,
|
|
2420
2403
|
regexp: false,
|
|
2404
|
+
sonarjs: false,
|
|
2421
2405
|
stylistic: false,
|
|
2422
2406
|
tailwindcss: false,
|
|
2423
2407
|
test: false,
|
|
@@ -2432,4 +2416,4 @@ const CONFIG_PRESET_FULL_OFF = {
|
|
|
2432
2416
|
//#region src/index.ts
|
|
2433
2417
|
var src_default = config;
|
|
2434
2418
|
//#endregion
|
|
2435
|
-
export { CONFIG_PRESET_FULL_OFF, CONFIG_PRESET_FULL_ON, GLOB_ALL_SRC, GLOB_CSS, GLOB_EXCLUDE, GLOB_HTML, GLOB_JS, GLOB_JSON, GLOB_JSON5, GLOB_JSONC, GLOB_JSX, GLOB_MARKDOWN, GLOB_MARKDOWN_CODE, GLOB_MARKDOWN_IN_MARKDOWN, GLOB_POSTCSS, GLOB_SCSS, GLOB_SRC, GLOB_SRC_EXT, GLOB_STYLE, GLOB_SVG, GLOB_TESTS, GLOB_TOML, GLOB_TS, GLOB_TSX, GLOB_VUE, GLOB_XML, GLOB_YAML, StylisticConfigDefaults,
|
|
2419
|
+
export { CONFIG_PRESET_FULL_OFF, CONFIG_PRESET_FULL_ON, GLOB_ALL_SRC, GLOB_CSS, GLOB_EXCLUDE, GLOB_GRAPHQL, GLOB_HTML, GLOB_JS, GLOB_JSON, GLOB_JSON5, GLOB_JSONC, GLOB_JSX, GLOB_LESS, GLOB_MARKDOWN, GLOB_MARKDOWN_CODE, GLOB_MARKDOWN_IN_MARKDOWN, GLOB_POSTCSS, GLOB_SCSS, GLOB_SRC, GLOB_SRC_EXT, GLOB_STYLE, GLOB_SVG, GLOB_TESTS, GLOB_TOML, GLOB_TS, GLOB_TSX, GLOB_VUE, GLOB_XML, GLOB_YAML, StylisticConfigDefaults, baseline, command, comments, config, deMorgan, src_default as default, defaultPluginRenaming, disables, e18e, ensurePackages, formatters, getOverrides, ignores, imports, interopDefault, isInEditorEnv, isInGitHooksOrLintStaged, isPackageInScope, javascript, jsdoc, jsonc, jsx, markdown, nextjs, node, parserPlain, perfectionist, pnpm, preferEarlyReturn, react, regexp, renameRules, resolveSubOptions, sonarjs, sortPackageJson, sortTsconfig, stylistic, tailwindcss, test, toml, typescript, unicorn, unocss, vue, yaml };
|