@ghettoddos/eslint-config 4.1.0 → 5.0.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 +13276 -10093
- package/dist/index.mjs +312 -333
- package/dist/{lib-C63e_zBF.mjs → lib-C1Uxp5ZW.mjs} +2879 -2331
- package/package.json +53 -68
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,37 +321,32 @@ 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;
|
|
245
331
|
if (isInGitHooksOrLintStaged()) return false;
|
|
246
|
-
return !!(process.env.VSCODE_PID || process.env.VSCODE_CWD || process.env.JETBRAINS_IDE || process.env.VIM || process.env.NVIM || process.env.ZED_ENVIRONMENT);
|
|
332
|
+
return !!(process.env.VSCODE_PID || process.env.VSCODE_CWD || process.env.JETBRAINS_IDE || process.env.VIM || process.env.NVIM || process.env.ZED_ENVIRONMENT && !process.env.ZED_TERM);
|
|
247
333
|
}
|
|
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 = {
|
|
264
340
|
experimental: false,
|
|
265
341
|
indent: 2,
|
|
266
342
|
jsx: true,
|
|
343
|
+
printWidth: 100,
|
|
267
344
|
quotes: "single",
|
|
268
|
-
semi: false
|
|
345
|
+
semi: false,
|
|
346
|
+
tabWidth: 4
|
|
269
347
|
};
|
|
270
348
|
async function stylistic(options = {}) {
|
|
271
|
-
const { experimental, indent, jsx, overrides = {}, quotes, semi } = {
|
|
349
|
+
const { experimental, indent, jsx, lessOpinionated = false, overrides = {}, printWidth, quotes, semi, tabWidth } = {
|
|
272
350
|
...StylisticConfigDefaults,
|
|
273
351
|
...options
|
|
274
352
|
};
|
|
@@ -291,13 +369,23 @@ async function stylistic(options = {}) {
|
|
|
291
369
|
...config.rules,
|
|
292
370
|
...experimental ? {} : { "antfu/consistent-list-newline": "error" },
|
|
293
371
|
"antfu/consistent-chaining": "error",
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
372
|
+
...lessOpinionated ? { curly: ["error", "all"] } : {
|
|
373
|
+
"antfu/curly": "error",
|
|
374
|
+
"antfu/if-newline": "error",
|
|
375
|
+
"antfu/top-level-function": "error"
|
|
376
|
+
},
|
|
297
377
|
"style/generator-star-spacing": ["error", {
|
|
298
378
|
after: true,
|
|
299
379
|
before: false
|
|
300
380
|
}],
|
|
381
|
+
"style/max-len": ["warn", {
|
|
382
|
+
code: printWidth,
|
|
383
|
+
ignoreComments: true,
|
|
384
|
+
ignoreRegExpLiterals: true,
|
|
385
|
+
ignoreTrailingComments: true,
|
|
386
|
+
ignoreUrls: true,
|
|
387
|
+
tabWidth: typeof indent === "number" ? indent : indent === "tab" ? tabWidth : 2
|
|
388
|
+
}],
|
|
301
389
|
"style/yield-star-spacing": ["error", {
|
|
302
390
|
after: true,
|
|
303
391
|
before: false
|
|
@@ -320,6 +408,7 @@ async function formatters(options = {}, stylistic = {}) {
|
|
|
320
408
|
const isPrettierPluginXmlInScope = isPackageInScope("@prettier/plugin-xml");
|
|
321
409
|
options = {
|
|
322
410
|
css: true,
|
|
411
|
+
graphql: true,
|
|
323
412
|
html: true,
|
|
324
413
|
markdown: true,
|
|
325
414
|
svg: isPrettierPluginXmlInScope,
|
|
@@ -327,13 +416,13 @@ async function formatters(options = {}, stylistic = {}) {
|
|
|
327
416
|
};
|
|
328
417
|
}
|
|
329
418
|
await ensurePackages(["eslint-plugin-format", options.xml || options.svg ? "@prettier/plugin-xml" : void 0]);
|
|
330
|
-
const { indent, quotes, semi } = {
|
|
419
|
+
const { indent, printWidth, quotes, semi } = {
|
|
331
420
|
...StylisticConfigDefaults,
|
|
332
421
|
...stylistic
|
|
333
422
|
};
|
|
334
423
|
const prettierOptions = Object.assign({
|
|
335
424
|
endOfLine: "auto",
|
|
336
|
-
printWidth:
|
|
425
|
+
printWidth: typeof printWidth === "number" ? printWidth : 100,
|
|
337
426
|
semi,
|
|
338
427
|
singleQuote: quotes === "single",
|
|
339
428
|
tabWidth: typeof indent === "number" ? indent : 2,
|
|
@@ -360,6 +449,11 @@ async function formatters(options = {}, stylistic = {}) {
|
|
|
360
449
|
languageOptions: { parser: parserPlain },
|
|
361
450
|
name: "formatter/scss",
|
|
362
451
|
rules: { "format/prettier": ["error", mergePrettierOptions(prettierOptions, { parser: "scss" })] }
|
|
452
|
+
}, {
|
|
453
|
+
files: [GLOB_LESS],
|
|
454
|
+
languageOptions: { parser: parserPlain },
|
|
455
|
+
name: "formatter/less",
|
|
456
|
+
rules: { "format/prettier": ["error", mergePrettierOptions(prettierOptions, { parser: "less" })] }
|
|
363
457
|
});
|
|
364
458
|
if (options.html) configs.push({
|
|
365
459
|
files: [GLOB_HTML],
|
|
@@ -400,6 +494,12 @@ async function formatters(options = {}, stylistic = {}) {
|
|
|
400
494
|
parser: "markdown"
|
|
401
495
|
})] }
|
|
402
496
|
});
|
|
497
|
+
if (options.graphql) configs.push({
|
|
498
|
+
files: [GLOB_GRAPHQL],
|
|
499
|
+
languageOptions: { parser: parserPlain },
|
|
500
|
+
name: "formatter/graphql",
|
|
501
|
+
rules: { "format/prettier": ["error", mergePrettierOptions(prettierOptions, { parser: "graphql" })] }
|
|
502
|
+
});
|
|
403
503
|
return configs;
|
|
404
504
|
}
|
|
405
505
|
//#endregion
|
|
@@ -761,37 +861,6 @@ async function jsonc(options = {}) {
|
|
|
761
861
|
}];
|
|
762
862
|
}
|
|
763
863
|
//#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
864
|
//#region src/configs/jsx.ts
|
|
796
865
|
async function jsx(options = {}) {
|
|
797
866
|
const { a11y } = options;
|
|
@@ -806,13 +875,13 @@ async function jsx(options = {}) {
|
|
|
806
875
|
await ensurePackages(["eslint-plugin-jsx-a11y"]);
|
|
807
876
|
const jsxA11yPlugin = await interopDefault(import("eslint-plugin-jsx-a11y"));
|
|
808
877
|
const a11yConfig = jsxA11yPlugin.flatConfigs.recommended;
|
|
809
|
-
const isUsingNext =
|
|
878
|
+
const isUsingNext = isPackageExists("next");
|
|
810
879
|
const a11yRules = {
|
|
811
880
|
...a11yConfig.rules || {},
|
|
812
|
-
"jsx-a11y/alt-text": ["
|
|
881
|
+
...isUsingNext ? { "jsx-a11y/alt-text": ["warn", {
|
|
813
882
|
elements: ["img"],
|
|
814
|
-
img: [
|
|
815
|
-
}],
|
|
883
|
+
img: ["Image"]
|
|
884
|
+
}] } : {},
|
|
816
885
|
...typeof a11y === "object" && a11y.overrides ? a11y.overrides : {}
|
|
817
886
|
};
|
|
818
887
|
return [{
|
|
@@ -865,27 +934,13 @@ async function markdown(options = {}) {
|
|
|
865
934
|
...overridesMarkdown
|
|
866
935
|
}
|
|
867
936
|
},
|
|
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
937
|
{
|
|
884
938
|
files: [GLOB_MARKDOWN_CODE, ...componentExts.map((ext) => `${GLOB_MARKDOWN}/**/*.${ext}`)],
|
|
885
939
|
languageOptions: { parserOptions: { ecmaFeatures: { impliedStrict: true } } },
|
|
886
940
|
name: "markdown/disables/code",
|
|
887
941
|
rules: {
|
|
888
942
|
"antfu/no-top-level-await": "off",
|
|
943
|
+
"e18e/prefer-static-regex": "off",
|
|
889
944
|
"no-alert": "off",
|
|
890
945
|
"no-console": "off",
|
|
891
946
|
"no-labels": "off",
|
|
@@ -917,41 +972,32 @@ async function markdown(options = {}) {
|
|
|
917
972
|
}
|
|
918
973
|
//#endregion
|
|
919
974
|
//#region src/configs/nextjs.ts
|
|
975
|
+
function normalizeRules(rules) {
|
|
976
|
+
return Object.fromEntries(Object.entries(rules).map(([key, value]) => [key, typeof value === "string" ? [value] : value]));
|
|
977
|
+
}
|
|
920
978
|
async function nextjs(options = {}) {
|
|
921
979
|
const { files = [GLOB_SRC], overrides = {} } = options;
|
|
922
980
|
await ensurePackages(["@next/eslint-plugin-next"]);
|
|
981
|
+
const pluginNextJS = await interopDefault(import("@next/eslint-plugin-next"));
|
|
982
|
+
function getRules(name) {
|
|
983
|
+
const rules = pluginNextJS.configs?.[name]?.rules;
|
|
984
|
+
if (!rules) throw new Error(`[@ghettoddos/eslint-config] Failed to find config ${name} in @next/eslint-plugin-next`);
|
|
985
|
+
return normalizeRules(rules);
|
|
986
|
+
}
|
|
923
987
|
return [{
|
|
924
|
-
name: "
|
|
925
|
-
plugins: { next:
|
|
988
|
+
name: "nextjs/setup",
|
|
989
|
+
plugins: { next: pluginNextJS }
|
|
926
990
|
}, {
|
|
927
991
|
files,
|
|
928
992
|
languageOptions: {
|
|
929
993
|
parserOptions: { ecmaFeatures: { jsx: true } },
|
|
930
994
|
sourceType: "module"
|
|
931
995
|
},
|
|
932
|
-
name: "
|
|
996
|
+
name: "nextjs/rules",
|
|
933
997
|
rules: {
|
|
934
|
-
"
|
|
935
|
-
"
|
|
936
|
-
"
|
|
937
|
-
"next/next-script-for-ga": "warn",
|
|
938
|
-
"next/no-assign-module-variable": "error",
|
|
939
|
-
"next/no-async-client-component": "warn",
|
|
940
|
-
"next/no-before-interactive-script-outside-document": "warn",
|
|
941
|
-
"next/no-css-tags": "warn",
|
|
942
|
-
"next/no-document-import-in-page": "error",
|
|
943
|
-
"next/no-duplicate-head": "error",
|
|
944
|
-
"next/no-head-element": "warn",
|
|
945
|
-
"next/no-head-import-in-document": "error",
|
|
946
|
-
"next/no-html-link-for-pages": "warn",
|
|
947
|
-
"next/no-img-element": "warn",
|
|
948
|
-
"next/no-page-custom-font": "warn",
|
|
949
|
-
"next/no-script-component-in-head": "error",
|
|
950
|
-
"next/no-styled-jsx-in-document": "warn",
|
|
951
|
-
"next/no-sync-scripts": "warn",
|
|
952
|
-
"next/no-title-in-document-head": "warn",
|
|
953
|
-
"next/no-typos": "warn",
|
|
954
|
-
"next/no-unwanted-polyfillio": "warn",
|
|
998
|
+
...getRules("recommended"),
|
|
999
|
+
...getRules("core-web-vitals"),
|
|
1000
|
+
"node/prefer-global/process": "off",
|
|
955
1001
|
...overrides
|
|
956
1002
|
},
|
|
957
1003
|
settings: { react: { version: "detect" } }
|
|
@@ -1158,41 +1204,87 @@ async function pnpm(options) {
|
|
|
1158
1204
|
return configs;
|
|
1159
1205
|
}
|
|
1160
1206
|
//#endregion
|
|
1207
|
+
//#region src/configs/prefer-early-return.ts
|
|
1208
|
+
const defaultMaximumStatements = 1;
|
|
1209
|
+
const pluginPreferEarlyReturn = { rules: { "prefer-early-return": {
|
|
1210
|
+
create(context) {
|
|
1211
|
+
const maxStatements = (context.options[0] || { maximumStatements: defaultMaximumStatements }).maximumStatements;
|
|
1212
|
+
function isLonelyIfStatement(statement) {
|
|
1213
|
+
return statement.type === "IfStatement" && statement.alternate == null;
|
|
1214
|
+
}
|
|
1215
|
+
function isOffendingConsequent(consequent) {
|
|
1216
|
+
return consequent.type === "ExpressionStatement" && maxStatements === 0 || consequent.type === "BlockStatement" && consequent.body.length > maxStatements;
|
|
1217
|
+
}
|
|
1218
|
+
function isOffendingIfStatement(statement) {
|
|
1219
|
+
return isLonelyIfStatement(statement) && isOffendingConsequent(statement.consequent);
|
|
1220
|
+
}
|
|
1221
|
+
function hasSimplifiableConditionalBody(functionBody) {
|
|
1222
|
+
const body = functionBody.body;
|
|
1223
|
+
return functionBody.type === "BlockStatement" && body.length === 1 && isOffendingIfStatement(body[0]);
|
|
1224
|
+
}
|
|
1225
|
+
function checkFunctionBody(functionNode) {
|
|
1226
|
+
const body = functionNode.body;
|
|
1227
|
+
if (hasSimplifiableConditionalBody(body)) context.report(body, "Prefer an early return to a conditionally-wrapped function body");
|
|
1228
|
+
}
|
|
1229
|
+
return {
|
|
1230
|
+
ArrowFunctionExpression: checkFunctionBody,
|
|
1231
|
+
FunctionDeclaration: checkFunctionBody,
|
|
1232
|
+
FunctionExpression: checkFunctionBody
|
|
1233
|
+
};
|
|
1234
|
+
},
|
|
1235
|
+
meta: {
|
|
1236
|
+
docs: {
|
|
1237
|
+
category: "Best Practices",
|
|
1238
|
+
description: "Prefer early returns over full-body conditional wrapping in function declarations.",
|
|
1239
|
+
recommended: false,
|
|
1240
|
+
uri: "https://github.com/Shopify/web-configs/blob/main/packages/eslint-plugin/docs/rules/prefer-early-return.md"
|
|
1241
|
+
},
|
|
1242
|
+
schema: [{
|
|
1243
|
+
additionalProperties: false,
|
|
1244
|
+
properties: { maximumStatements: { type: "integer" } },
|
|
1245
|
+
type: "object"
|
|
1246
|
+
}]
|
|
1247
|
+
}
|
|
1248
|
+
} } };
|
|
1249
|
+
async function preferEarlyReturn() {
|
|
1250
|
+
return [{
|
|
1251
|
+
name: "prefer-early-return/rules",
|
|
1252
|
+
plugins: { "prefer-early-return": pluginPreferEarlyReturn },
|
|
1253
|
+
rules: { "prefer-early-return/prefer-early-return": "error" }
|
|
1254
|
+
}];
|
|
1255
|
+
}
|
|
1256
|
+
//#endregion
|
|
1161
1257
|
//#region src/configs/react.ts
|
|
1258
|
+
const ReactRefreshAllowConstantExportPackages = ["vite"];
|
|
1259
|
+
const RemixPackages = [
|
|
1260
|
+
"@remix-run/node",
|
|
1261
|
+
"@remix-run/react",
|
|
1262
|
+
"@remix-run/serve",
|
|
1263
|
+
"@remix-run/dev"
|
|
1264
|
+
];
|
|
1265
|
+
const ReactRouterPackages = [
|
|
1266
|
+
"@react-router/node",
|
|
1267
|
+
"@react-router/react",
|
|
1268
|
+
"@react-router/serve",
|
|
1269
|
+
"@react-router/dev"
|
|
1270
|
+
];
|
|
1271
|
+
const NextJsPackages = ["next"];
|
|
1162
1272
|
async function react(options = {}) {
|
|
1163
|
-
const { files = [GLOB_SRC], filesTypeAware = [GLOB_TS, GLOB_TSX], ignoresTypeAware = [`${GLOB_MARKDOWN}/**`], overrides = {}, tsconfigPath
|
|
1164
|
-
await ensurePackages([
|
|
1165
|
-
"@eslint-react/eslint-plugin",
|
|
1166
|
-
"eslint-plugin-react-hooks",
|
|
1167
|
-
"eslint-plugin-react-refresh"
|
|
1168
|
-
]);
|
|
1273
|
+
const { files = [GLOB_SRC], filesTypeAware = [GLOB_TS, GLOB_TSX], ignoresTypeAware = [`${GLOB_MARKDOWN}/**`], overrides = {}, tsconfigPath } = options;
|
|
1274
|
+
await ensurePackages(["@eslint-react/eslint-plugin", "eslint-plugin-react-refresh"]);
|
|
1169
1275
|
const isTypeAware = !!tsconfigPath;
|
|
1170
|
-
const typeAwareRules = {
|
|
1171
|
-
|
|
1172
|
-
"react/no-implicit-key": "error"
|
|
1173
|
-
};
|
|
1174
|
-
const [pluginReact, pluginReactHooks, pluginReactRefresh] = await Promise.all([
|
|
1175
|
-
interopDefault(import("@eslint-react/eslint-plugin")),
|
|
1176
|
-
interopDefault(import("eslint-plugin-react-hooks")),
|
|
1177
|
-
interopDefault(import("eslint-plugin-react-refresh"))
|
|
1178
|
-
]);
|
|
1276
|
+
const typeAwareRules = { "react/no-leaked-conditional-rendering": "error" };
|
|
1277
|
+
const [pluginReact, pluginReactRefresh] = await Promise.all([interopDefault(import("@eslint-react/eslint-plugin")), interopDefault(import("eslint-plugin-react-refresh"))]);
|
|
1179
1278
|
const isAllowConstantExport = ReactRefreshAllowConstantExportPackages.some((i) => isPackageExists(i));
|
|
1180
1279
|
const isUsingRemix = RemixPackages.some((i) => isPackageExists(i));
|
|
1181
1280
|
const isUsingReactRouter = ReactRouterPackages.some((i) => isPackageExists(i));
|
|
1182
1281
|
const isUsingNext = NextJsPackages.some((i) => isPackageExists(i));
|
|
1183
|
-
const plugins = pluginReact.configs.all.plugins;
|
|
1184
1282
|
return [
|
|
1185
1283
|
{
|
|
1186
1284
|
name: "react/setup",
|
|
1187
1285
|
plugins: {
|
|
1188
|
-
"react": plugins["@eslint-react"],
|
|
1189
|
-
"react-
|
|
1190
|
-
"react-hooks": pluginReactHooks,
|
|
1191
|
-
"react-hooks-extra": plugins["@eslint-react/hooks-extra"],
|
|
1192
|
-
"react-naming-convention": plugins["@eslint-react/naming-convention"],
|
|
1193
|
-
"react-refresh": pluginReactRefresh,
|
|
1194
|
-
"react-rsc": plugins["@eslint-react/rsc"],
|
|
1195
|
-
"react-web-api": plugins["@eslint-react/web-api"]
|
|
1286
|
+
"react": pluginReact.configs.all.plugins["@eslint-react"],
|
|
1287
|
+
"react-refresh": pluginReactRefresh
|
|
1196
1288
|
}
|
|
1197
1289
|
},
|
|
1198
1290
|
{
|
|
@@ -1203,89 +1295,7 @@ async function react(options = {}) {
|
|
|
1203
1295
|
},
|
|
1204
1296
|
name: "react/rules",
|
|
1205
1297
|
rules: {
|
|
1206
|
-
|
|
1207
|
-
"react/jsx-no-comment-textnodes": "warn",
|
|
1208
|
-
"react/jsx-no-duplicate-props": "warn",
|
|
1209
|
-
"react/jsx-uses-react": "warn",
|
|
1210
|
-
"react/jsx-uses-vars": "warn",
|
|
1211
|
-
"react/no-access-state-in-setstate": "error",
|
|
1212
|
-
"react/no-array-index-key": "warn",
|
|
1213
|
-
"react/no-children-count": "warn",
|
|
1214
|
-
"react/no-children-for-each": "warn",
|
|
1215
|
-
"react/no-children-map": "warn",
|
|
1216
|
-
"react/no-children-only": "warn",
|
|
1217
|
-
"react/no-children-to-array": "warn",
|
|
1218
|
-
"react/no-clone-element": "warn",
|
|
1219
|
-
"react/no-component-will-mount": "error",
|
|
1220
|
-
"react/no-component-will-receive-props": "error",
|
|
1221
|
-
"react/no-component-will-update": "error",
|
|
1222
|
-
"react/no-context-provider": "warn",
|
|
1223
|
-
"react/no-create-ref": "error",
|
|
1224
|
-
"react/no-default-props": "error",
|
|
1225
|
-
"react/no-direct-mutation-state": "error",
|
|
1226
|
-
"react/no-forward-ref": "warn",
|
|
1227
|
-
"react/no-missing-key": "error",
|
|
1228
|
-
"react/no-nested-component-definitions": "error",
|
|
1229
|
-
"react/no-nested-lazy-component-declarations": "error",
|
|
1230
|
-
"react/no-prop-types": "error",
|
|
1231
|
-
"react/no-redundant-should-component-update": "error",
|
|
1232
|
-
"react/no-set-state-in-component-did-mount": "warn",
|
|
1233
|
-
"react/no-set-state-in-component-did-update": "warn",
|
|
1234
|
-
"react/no-set-state-in-component-will-update": "warn",
|
|
1235
|
-
"react/no-string-refs": "error",
|
|
1236
|
-
"react/no-unnecessary-use-prefix": "warn",
|
|
1237
|
-
"react/no-unsafe-component-will-mount": "warn",
|
|
1238
|
-
"react/no-unsafe-component-will-receive-props": "warn",
|
|
1239
|
-
"react/no-unsafe-component-will-update": "warn",
|
|
1240
|
-
"react/no-unused-class-component-members": "warn",
|
|
1241
|
-
"react/no-use-context": "warn",
|
|
1242
|
-
"react/no-useless-forward-ref": "warn",
|
|
1243
|
-
"react/prefer-use-state-lazy-initialization": "warn",
|
|
1244
|
-
"react/prefer-namespace-import": "error",
|
|
1245
|
-
"react/jsx-shorthand-boolean": "warn",
|
|
1246
|
-
"react/prefer-destructuring-assignment": "warn",
|
|
1247
|
-
"react/no-missing-context-display-name": "warn",
|
|
1248
|
-
"react/no-missing-component-display-name": "warn",
|
|
1249
|
-
"react-rsc/function-definition": "error",
|
|
1250
|
-
"react-dom/no-dangerously-set-innerhtml": "warn",
|
|
1251
|
-
"react-dom/no-dangerously-set-innerhtml-with-children": "error",
|
|
1252
|
-
"react-dom/no-find-dom-node": "error",
|
|
1253
|
-
"react-dom/no-flush-sync": "error",
|
|
1254
|
-
"react-dom/no-hydrate": "error",
|
|
1255
|
-
"react-dom/no-namespace": "error",
|
|
1256
|
-
"react-dom/no-render": "error",
|
|
1257
|
-
"react-dom/no-render-return-value": "error",
|
|
1258
|
-
"react-dom/no-script-url": "warn",
|
|
1259
|
-
"react-dom/no-unsafe-iframe-sandbox": "warn",
|
|
1260
|
-
"react-dom/no-use-form-state": "error",
|
|
1261
|
-
"react-dom/no-void-elements-with-children": "error",
|
|
1262
|
-
"react-hooks-extra/no-direct-set-state-in-use-effect": "warn",
|
|
1263
|
-
"react-naming-convention/context-name": "warn",
|
|
1264
|
-
"react-naming-convention/ref-name": "warn",
|
|
1265
|
-
"react-naming-convention/use-state": "warn",
|
|
1266
|
-
"react-web-api/no-leaked-event-listener": "warn",
|
|
1267
|
-
"react-web-api/no-leaked-interval": "warn",
|
|
1268
|
-
"react-web-api/no-leaked-resize-observer": "warn",
|
|
1269
|
-
"react-web-api/no-leaked-timeout": "warn",
|
|
1270
|
-
"react-hooks/rules-of-hooks": "error",
|
|
1271
|
-
"react-hooks/exhaustive-deps": "warn",
|
|
1272
|
-
...reactCompiler ? {
|
|
1273
|
-
"react-hooks/config": "error",
|
|
1274
|
-
"react-hooks/error-boundaries": "error",
|
|
1275
|
-
"react-hooks/component-hook-factories": "error",
|
|
1276
|
-
"react-hooks/gating": "error",
|
|
1277
|
-
"react-hooks/globals": "error",
|
|
1278
|
-
"react-hooks/immutability": "error",
|
|
1279
|
-
"react-hooks/preserve-manual-memoization": "error",
|
|
1280
|
-
"react-hooks/purity": "error",
|
|
1281
|
-
"react-hooks/refs": "error",
|
|
1282
|
-
"react-hooks/set-state-in-effect": "error",
|
|
1283
|
-
"react-hooks/set-state-in-render": "error",
|
|
1284
|
-
"react-hooks/static-components": "error",
|
|
1285
|
-
"react-hooks/unsupported-syntax": "warn",
|
|
1286
|
-
"react-hooks/use-memo": "error",
|
|
1287
|
-
"react-hooks/incompatible-library": "warn"
|
|
1288
|
-
} : {},
|
|
1298
|
+
...pluginReact.configs.recommended.rules,
|
|
1289
1299
|
"react-refresh/only-export-components": ["error", {
|
|
1290
1300
|
allowConstantExport: isAllowConstantExport,
|
|
1291
1301
|
allowExportNames: [...isUsingNext ? [
|
|
@@ -1322,12 +1332,8 @@ async function react(options = {}) {
|
|
|
1322
1332
|
files: filesTypeAware,
|
|
1323
1333
|
name: "react/typescript",
|
|
1324
1334
|
rules: {
|
|
1325
|
-
"react-
|
|
1326
|
-
"react-
|
|
1327
|
-
"react/jsx-no-duplicate-props": "off",
|
|
1328
|
-
"react/jsx-no-undef": "off",
|
|
1329
|
-
"react/jsx-uses-react": "off",
|
|
1330
|
-
"react/jsx-uses-vars": "off"
|
|
1335
|
+
"react/dom-no-string-style-prop": "off",
|
|
1336
|
+
"react/dom-no-unknown-property": "off"
|
|
1331
1337
|
}
|
|
1332
1338
|
},
|
|
1333
1339
|
...isTypeAware ? [{
|
|
@@ -1339,71 +1345,6 @@ async function react(options = {}) {
|
|
|
1339
1345
|
];
|
|
1340
1346
|
}
|
|
1341
1347
|
//#endregion
|
|
1342
|
-
//#region src/configs/react-native.ts
|
|
1343
|
-
async function reactNative(options = {}) {
|
|
1344
|
-
const { expo = isPackageExists("expo"), files = [GLOB_SRC], overrides = {} } = options;
|
|
1345
|
-
await ensurePackages([
|
|
1346
|
-
"@react-native/eslint-plugin",
|
|
1347
|
-
"eslint-plugin-react-native",
|
|
1348
|
-
...expo ? ["eslint-plugin-expo"] : []
|
|
1349
|
-
]);
|
|
1350
|
-
const [pluginReactNative, pluginReactNativeCommunity, pluginExpo] = await Promise.all([
|
|
1351
|
-
interopDefault(import("@react-native/eslint-plugin")),
|
|
1352
|
-
interopDefault(import("eslint-plugin-react-native")),
|
|
1353
|
-
...expo ? [interopDefault(import("eslint-plugin-expo"))] : []
|
|
1354
|
-
]);
|
|
1355
|
-
return [{
|
|
1356
|
-
name: "react-native/setup",
|
|
1357
|
-
plugins: {
|
|
1358
|
-
"react-native": pluginReactNative,
|
|
1359
|
-
"react-native-community": pluginReactNativeCommunity,
|
|
1360
|
-
...expo ? { expo: pluginExpo } : {}
|
|
1361
|
-
}
|
|
1362
|
-
}, {
|
|
1363
|
-
files,
|
|
1364
|
-
languageOptions: {
|
|
1365
|
-
globals: {
|
|
1366
|
-
...globals.browser,
|
|
1367
|
-
"__DEV__": "readonly",
|
|
1368
|
-
"alert": false,
|
|
1369
|
-
"cancelAnimationFrame": false,
|
|
1370
|
-
"cancelIdleCallback": false,
|
|
1371
|
-
"clearImmediate": false,
|
|
1372
|
-
"ErrorUtils": false,
|
|
1373
|
-
"fetch": false,
|
|
1374
|
-
"FormData": false,
|
|
1375
|
-
"navigator": false,
|
|
1376
|
-
"process": false,
|
|
1377
|
-
"requestAnimationFrame": false,
|
|
1378
|
-
"requestIdleCallback": false,
|
|
1379
|
-
"setImmediate": false,
|
|
1380
|
-
"shared-node-browser": true,
|
|
1381
|
-
"window": false,
|
|
1382
|
-
"XMLHttpRequest": false
|
|
1383
|
-
},
|
|
1384
|
-
parserOptions: { ecmaFeatures: { jsx: true } },
|
|
1385
|
-
sourceType: "module"
|
|
1386
|
-
},
|
|
1387
|
-
name: "react-native/rules",
|
|
1388
|
-
rules: {
|
|
1389
|
-
"node/prefer-global/process": "off",
|
|
1390
|
-
"react-native-community/no-inline-styles": "warn",
|
|
1391
|
-
"react-native-community/no-raw-text": "warn",
|
|
1392
|
-
"react-native-community/no-single-element-style-arrays": "warn",
|
|
1393
|
-
"react-native-community/no-unused-styles": "warn",
|
|
1394
|
-
"react-native/no-deep-imports": "error",
|
|
1395
|
-
"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)$"] }],
|
|
1396
|
-
...expo ? {
|
|
1397
|
-
"expo/no-dynamic-env-var": "error",
|
|
1398
|
-
"expo/no-env-var-destructuring": "error",
|
|
1399
|
-
"expo/use-dom-exports": "error"
|
|
1400
|
-
} : {},
|
|
1401
|
-
...overrides
|
|
1402
|
-
},
|
|
1403
|
-
settings: { react: { version: "detect" } }
|
|
1404
|
-
}];
|
|
1405
|
-
}
|
|
1406
|
-
//#endregion
|
|
1407
1348
|
//#region src/configs/regexp.ts
|
|
1408
1349
|
async function regexp(options = {}) {
|
|
1409
1350
|
const config = configs["flat/recommended"];
|
|
@@ -1421,6 +1362,18 @@ async function regexp(options = {}) {
|
|
|
1421
1362
|
}];
|
|
1422
1363
|
}
|
|
1423
1364
|
//#endregion
|
|
1365
|
+
//#region src/configs/sonarjs.ts
|
|
1366
|
+
async function sonarjs(options = {}) {
|
|
1367
|
+
return [{
|
|
1368
|
+
name: "sonarjs/rules",
|
|
1369
|
+
plugins: { sonarjs: pluginSonarJs },
|
|
1370
|
+
rules: {
|
|
1371
|
+
...pluginSonarJs.configs.recommended.rules,
|
|
1372
|
+
...options.overrides
|
|
1373
|
+
}
|
|
1374
|
+
}];
|
|
1375
|
+
}
|
|
1376
|
+
//#endregion
|
|
1424
1377
|
//#region src/configs/sort.ts
|
|
1425
1378
|
/**
|
|
1426
1379
|
* Sort package.json
|
|
@@ -1655,29 +1608,63 @@ function sortTsconfig() {
|
|
|
1655
1608
|
}
|
|
1656
1609
|
//#endregion
|
|
1657
1610
|
//#region src/configs/tailwindcss.ts
|
|
1658
|
-
|
|
1659
|
-
|
|
1611
|
+
let _pluginTailwindCSS;
|
|
1612
|
+
async function tailwindcss(options) {
|
|
1613
|
+
const { detectComponentClasses, entryPoint, overrides = {}, rootFontSize, selectors, stylistic = true, tsconfigPath } = options;
|
|
1660
1614
|
await ensurePackages(["eslint-plugin-better-tailwindcss"]);
|
|
1661
1615
|
const [pluginTailwindCSS] = await Promise.all([interopDefault(import("eslint-plugin-better-tailwindcss"))]);
|
|
1662
|
-
|
|
1616
|
+
_pluginTailwindCSS = _pluginTailwindCSS || {
|
|
1617
|
+
...pluginTailwindCSS,
|
|
1618
|
+
rules: {
|
|
1619
|
+
...pluginTailwindCSS.rules,
|
|
1620
|
+
"multiline-classname": {
|
|
1621
|
+
create(context) {
|
|
1622
|
+
return { JSXAttribute(node) {
|
|
1623
|
+
if (node.name?.type !== "JSXIdentifier") return;
|
|
1624
|
+
if (node.name.name !== "className") return;
|
|
1625
|
+
if (node.value?.type === "Literal" && typeof node.value.value === "string" && node.value.value.includes("\n")) {
|
|
1626
|
+
const raw = node.value.value;
|
|
1627
|
+
context.report({
|
|
1628
|
+
fix(fixer) {
|
|
1629
|
+
const escaped = raw.replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
1630
|
+
return fixer.replaceText(node.value, `{\`${escaped}\`}`);
|
|
1631
|
+
},
|
|
1632
|
+
message: "Multiline className strings can cause hydration errors. Use a template literal/expression instead.",
|
|
1633
|
+
node: node.value
|
|
1634
|
+
});
|
|
1635
|
+
}
|
|
1636
|
+
} };
|
|
1637
|
+
},
|
|
1638
|
+
meta: {
|
|
1639
|
+
fixable: "code",
|
|
1640
|
+
schema: [],
|
|
1641
|
+
type: "suggestion"
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
};
|
|
1646
|
+
const { indent = 2, printWidth = 100, tabWidth = 4 } = typeof stylistic === "boolean" ? {} : stylistic;
|
|
1663
1647
|
return [{
|
|
1664
1648
|
name: "tailwindcss",
|
|
1665
|
-
plugins: { tailwindcss:
|
|
1649
|
+
plugins: { "better-tailwindcss": _pluginTailwindCSS },
|
|
1666
1650
|
rules: {
|
|
1667
1651
|
...stylistic ? {
|
|
1668
|
-
|
|
1669
|
-
"tailwindcss/enforce-consistent-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1652
|
+
...pluginTailwindCSS.configs.stylistic.rules,
|
|
1653
|
+
"better-tailwindcss/enforce-consistent-line-wrapping": ["warn", {
|
|
1654
|
+
indent: typeof indent === "number" ? indent : indent === "tab" ? "tab" : 2,
|
|
1655
|
+
printWidth,
|
|
1656
|
+
tabWidth: typeof indent === "number" ? indent : indent === "tab" ? tabWidth : 2
|
|
1657
|
+
}],
|
|
1658
|
+
"better-tailwindcss/multiline-classname": "error"
|
|
1674
1659
|
} : {},
|
|
1675
|
-
|
|
1676
|
-
"tailwindcss/no-unknown-classes": "error",
|
|
1660
|
+
...pluginTailwindCSS.configs.correctness.rules,
|
|
1677
1661
|
...overrides
|
|
1678
1662
|
},
|
|
1679
1663
|
settings: { "better-tailwindcss": {
|
|
1664
|
+
detectComponentClasses,
|
|
1680
1665
|
entryPoint,
|
|
1666
|
+
rootFontSize,
|
|
1667
|
+
selectors,
|
|
1681
1668
|
tsconfig: tsconfigPath
|
|
1682
1669
|
} }
|
|
1683
1670
|
}];
|
|
@@ -1712,6 +1699,8 @@ async function test(options = {}) {
|
|
|
1712
1699
|
"test/prefer-hooks-in-order": "error",
|
|
1713
1700
|
"test/prefer-lowercase-title": "error",
|
|
1714
1701
|
"antfu/no-top-level-await": "off",
|
|
1702
|
+
"baseline-js/use-baseline": "off",
|
|
1703
|
+
"e18e/prefer-static-regex": "off",
|
|
1715
1704
|
"no-unused-expressions": "off",
|
|
1716
1705
|
"node/prefer-global/process": "off",
|
|
1717
1706
|
"ts/explicit-function-return-type": "off",
|
|
@@ -1891,8 +1880,8 @@ async function typescript(options = {}) {
|
|
|
1891
1880
|
}
|
|
1892
1881
|
}] : [],
|
|
1893
1882
|
...erasableOnly ? [{
|
|
1894
|
-
name: "
|
|
1895
|
-
plugins: { "erasable-syntax-only": await interopDefault(import("./lib-
|
|
1883
|
+
name: "typescript/erasable-syntax-only",
|
|
1884
|
+
plugins: { "erasable-syntax-only": await interopDefault(import("./lib-C1Uxp5ZW.mjs")) },
|
|
1896
1885
|
rules: {
|
|
1897
1886
|
"erasable-syntax-only/enums": "error",
|
|
1898
1887
|
"erasable-syntax-only/import-aliases": "error",
|
|
@@ -1954,11 +1943,6 @@ async function vue(options = {}) {
|
|
|
1954
1943
|
const { a11y = false, files = [GLOB_VUE], overrides = {}, stylistic = true, vueVersion = 3 } = options;
|
|
1955
1944
|
const sfcBlocks = options.sfcBlocks === true ? {} : options.sfcBlocks ?? {};
|
|
1956
1945
|
const { indent = 2 } = typeof stylistic === "boolean" ? {} : stylistic;
|
|
1957
|
-
await ensurePackages([
|
|
1958
|
-
"eslint-plugin-vue",
|
|
1959
|
-
"vue-eslint-parser",
|
|
1960
|
-
"eslint-processor-vue-blocks"
|
|
1961
|
-
]);
|
|
1962
1946
|
if (a11y) await ensurePackages(["eslint-plugin-vuejs-accessibility"]);
|
|
1963
1947
|
const [pluginVue, parserVue, processorVueBlocks, pluginVueA11y] = await Promise.all([
|
|
1964
1948
|
interopDefault(import("eslint-plugin-vue")),
|
|
@@ -2201,22 +2185,6 @@ async function yaml(options = {}) {
|
|
|
2201
2185
|
}];
|
|
2202
2186
|
}
|
|
2203
2187
|
//#endregion
|
|
2204
|
-
//#region src/configs/e18e.ts
|
|
2205
|
-
async function e18e(options = {}) {
|
|
2206
|
-
const { isInEditor = false, modernization = true, moduleReplacements = isInEditor, overrides = {}, performanceImprovements = true } = options;
|
|
2207
|
-
const configs = pluginE18e.configs;
|
|
2208
|
-
return [{
|
|
2209
|
-
name: "antfu/e18e/rules",
|
|
2210
|
-
plugins: { e18e: pluginE18e },
|
|
2211
|
-
rules: {
|
|
2212
|
-
...modernization ? { ...configs.modernization.rules } : {},
|
|
2213
|
-
...moduleReplacements ? { ...configs.moduleReplacements.rules } : {},
|
|
2214
|
-
...performanceImprovements ? { ...configs.performanceImprovements.rules } : {},
|
|
2215
|
-
...overrides
|
|
2216
|
-
}
|
|
2217
|
-
}];
|
|
2218
|
-
}
|
|
2219
|
-
//#endregion
|
|
2220
2188
|
//#region src/factory.ts
|
|
2221
2189
|
const flatConfigProps = [
|
|
2222
2190
|
"name",
|
|
@@ -2227,15 +2195,17 @@ const flatConfigProps = [
|
|
|
2227
2195
|
"rules",
|
|
2228
2196
|
"settings"
|
|
2229
2197
|
];
|
|
2198
|
+
const VuePackages = [
|
|
2199
|
+
"vue",
|
|
2200
|
+
"nuxt",
|
|
2201
|
+
"vitepress",
|
|
2202
|
+
"@slidev/cli"
|
|
2203
|
+
];
|
|
2230
2204
|
const defaultPluginRenaming = {
|
|
2231
2205
|
"@eslint-react": "react",
|
|
2232
|
-
"@eslint-react/dom": "react-dom",
|
|
2233
|
-
"@eslint-react/hooks-extra": "react-hooks-extra",
|
|
2234
|
-
"@eslint-react/naming-convention": "react-naming-convention",
|
|
2235
2206
|
"@next/next": "next",
|
|
2236
2207
|
"@stylistic": "style",
|
|
2237
2208
|
"@typescript-eslint": "ts",
|
|
2238
|
-
"better-tailwindcss": "tailwindcss",
|
|
2239
2209
|
"import-lite": "import",
|
|
2240
2210
|
"n": "node",
|
|
2241
2211
|
"vitest": "test",
|
|
@@ -2252,7 +2222,7 @@ const defaultPluginRenaming = {
|
|
|
2252
2222
|
* The merged ESLint configurations.
|
|
2253
2223
|
*/
|
|
2254
2224
|
function config(options = {}, ...userConfigs) {
|
|
2255
|
-
const { autoRenamePlugins = true, componentExts = [], e18e: enableE18e = true, gitignore: enableGitignore = true, ignores: userIgnores = [], imports: enableImports = true, jsdoc: enableJsdoc = true, jsx: enableJsx = true, nextjs:
|
|
2225
|
+
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, pnpm: enableCatalogs = !!findUpSync("pnpm-workspace.yaml"), react: enableReact = false, regexp: enableRegexp = true, sonarjs: enableSonarJs = false, tailwindcss: enableTailwindCSS = isPackageExists("tailwindcss"), 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;
|
|
2256
2226
|
let isInEditor = options.isInEditor;
|
|
2257
2227
|
if (isInEditor == null) {
|
|
2258
2228
|
isInEditor = isInEditorEnv();
|
|
@@ -2274,17 +2244,19 @@ function config(options = {}, ...userConfigs) {
|
|
|
2274
2244
|
configs.push(ignores(userIgnores, !enableTypeScript), javascript({
|
|
2275
2245
|
isInEditor,
|
|
2276
2246
|
overrides: getOverrides(options, "javascript")
|
|
2277
|
-
}), comments(), command(), perfectionist());
|
|
2247
|
+
}), comments(), command(), deMorgan(), preferEarlyReturn(), perfectionist());
|
|
2278
2248
|
if (enableNode) configs.push(node());
|
|
2279
2249
|
if (enableJsdoc) configs.push(jsdoc({ stylistic: stylisticOptions }));
|
|
2280
2250
|
if (enableImports) configs.push(imports({
|
|
2281
2251
|
stylistic: stylisticOptions,
|
|
2282
2252
|
...resolveSubOptions(options, "imports")
|
|
2283
2253
|
}));
|
|
2254
|
+
if (enableBaseLineJs) configs.push(baseline({ ...resolveSubOptions(options, "baseline") }));
|
|
2284
2255
|
if (enableE18e) configs.push(e18e({
|
|
2285
2256
|
isInEditor,
|
|
2286
2257
|
...enableE18e === true ? {} : enableE18e
|
|
2287
2258
|
}));
|
|
2259
|
+
if (enableSonarJs) configs.push(sonarjs({ ...resolveSubOptions(options, "sonarjs") }));
|
|
2288
2260
|
if (enableUnicorn) configs.push(unicorn(enableUnicorn === true ? {} : enableUnicorn));
|
|
2289
2261
|
if (enableVue) componentExts.push("vue");
|
|
2290
2262
|
if (enableJsx) configs.push(jsx(enableJsx === true ? {} : enableJsx));
|
|
@@ -2292,10 +2264,11 @@ function config(options = {}, ...userConfigs) {
|
|
|
2292
2264
|
...typescriptOptions,
|
|
2293
2265
|
componentExts,
|
|
2294
2266
|
overrides: getOverrides(options, "typescript"),
|
|
2295
|
-
type:
|
|
2267
|
+
type: appType
|
|
2296
2268
|
}));
|
|
2297
2269
|
if (stylisticOptions) configs.push(stylistic({
|
|
2298
2270
|
...stylisticOptions,
|
|
2271
|
+
lessOpinionated: options.lessOpinionated,
|
|
2299
2272
|
overrides: getOverrides(options, "stylistic")
|
|
2300
2273
|
}));
|
|
2301
2274
|
if (enableRegexp) configs.push(regexp(typeof enableRegexp === "boolean" ? {} : enableRegexp));
|
|
@@ -2315,11 +2288,7 @@ function config(options = {}, ...userConfigs) {
|
|
|
2315
2288
|
overrides: getOverrides(options, "react"),
|
|
2316
2289
|
tsconfigPath
|
|
2317
2290
|
}));
|
|
2318
|
-
if (
|
|
2319
|
-
...resolveSubOptions(options, "reactNative"),
|
|
2320
|
-
overrides: getOverrides(options, "reactNative")
|
|
2321
|
-
}));
|
|
2322
|
-
if (enableNext) configs.push(nextjs({ overrides: getOverrides(options, "nextjs") }));
|
|
2291
|
+
if (enableNextjs) configs.push(nextjs({ overrides: getOverrides(options, "nextjs") }));
|
|
2323
2292
|
if (enableUnoCSS) configs.push(unocss({
|
|
2324
2293
|
...resolveSubOptions(options, "unocss"),
|
|
2325
2294
|
overrides: getOverrides(options, "unocss")
|
|
@@ -2365,6 +2334,7 @@ function config(options = {}, ...userConfigs) {
|
|
|
2365
2334
|
if (Object.keys(fusedConfig).length) configs.push([fusedConfig]);
|
|
2366
2335
|
let composer = new FlatConfigComposer();
|
|
2367
2336
|
composer = composer.append(...configs, ...userConfigs);
|
|
2337
|
+
if (options.markdown ?? true) composer = composer.setDefaultIgnores((prev) => [...prev, GLOB_MARKDOWN]);
|
|
2368
2338
|
if (autoRenamePlugins) composer = composer.renamePlugins(defaultPluginRenaming);
|
|
2369
2339
|
if (isInEditor) composer = composer.disableRulesFix([
|
|
2370
2340
|
"unused-imports/no-unused-imports",
|
|
@@ -2373,9 +2343,17 @@ function config(options = {}, ...userConfigs) {
|
|
|
2373
2343
|
], { builtinRules: () => import(["eslint", "use-at-your-own-risk"].join("/")).then((r) => r.builtinRules) });
|
|
2374
2344
|
return composer;
|
|
2375
2345
|
}
|
|
2346
|
+
function resolveSubOptions(options, key) {
|
|
2347
|
+
return typeof options[key] === "boolean" ? {} : options[key] || {};
|
|
2348
|
+
}
|
|
2349
|
+
function getOverrides(options, key) {
|
|
2350
|
+
const sub = resolveSubOptions(options, key);
|
|
2351
|
+
return { ..."overrides" in sub ? sub.overrides : {} };
|
|
2352
|
+
}
|
|
2376
2353
|
//#endregion
|
|
2377
2354
|
//#region src/config-presets.ts
|
|
2378
2355
|
const CONFIG_PRESET_FULL_ON = {
|
|
2356
|
+
baseline: true,
|
|
2379
2357
|
formatters: true,
|
|
2380
2358
|
gitignore: true,
|
|
2381
2359
|
imports: true,
|
|
@@ -2386,11 +2364,11 @@ const CONFIG_PRESET_FULL_ON = {
|
|
|
2386
2364
|
nextjs: true,
|
|
2387
2365
|
node: true,
|
|
2388
2366
|
pnpm: true,
|
|
2389
|
-
react:
|
|
2390
|
-
reactNative: { expo: true },
|
|
2367
|
+
react: true,
|
|
2391
2368
|
regexp: true,
|
|
2369
|
+
sonarjs: true,
|
|
2392
2370
|
stylistic: { experimental: true },
|
|
2393
|
-
tailwindcss:
|
|
2371
|
+
tailwindcss: { entryPoint: "fixtures/stub.css" },
|
|
2394
2372
|
test: true,
|
|
2395
2373
|
toml: true,
|
|
2396
2374
|
typescript: {
|
|
@@ -2403,6 +2381,7 @@ const CONFIG_PRESET_FULL_ON = {
|
|
|
2403
2381
|
yaml: true
|
|
2404
2382
|
};
|
|
2405
2383
|
const CONFIG_PRESET_FULL_OFF = {
|
|
2384
|
+
baseline: false,
|
|
2406
2385
|
formatters: false,
|
|
2407
2386
|
gitignore: false,
|
|
2408
2387
|
imports: false,
|
|
@@ -2414,8 +2393,8 @@ const CONFIG_PRESET_FULL_OFF = {
|
|
|
2414
2393
|
node: false,
|
|
2415
2394
|
pnpm: false,
|
|
2416
2395
|
react: false,
|
|
2417
|
-
reactNative: false,
|
|
2418
2396
|
regexp: false,
|
|
2397
|
+
sonarjs: false,
|
|
2419
2398
|
stylistic: false,
|
|
2420
2399
|
tailwindcss: false,
|
|
2421
2400
|
test: false,
|
|
@@ -2430,4 +2409,4 @@ const CONFIG_PRESET_FULL_OFF = {
|
|
|
2430
2409
|
//#region src/index.ts
|
|
2431
2410
|
var src_default = config;
|
|
2432
2411
|
//#endregion
|
|
2433
|
-
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,
|
|
2412
|
+
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 };
|