@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.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
- if (await (await import("@clack/prompts")).confirm({ message: `${nonExistingPackages.length === 1 ? "Package is" : "Packages are"} required for this config: ${nonExistingPackages.join(", ")}. Do you want to install them?` })) await import("@antfu/install-pkg").then((i) => i.installPackage(nonExistingPackages, { dev: true }));
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
- "antfu/curly": "error",
295
- "antfu/if-newline": "error",
296
- "antfu/top-level-function": "error",
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: 120,
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 = NextJsPackages.some((i) => isPackageExists(i));
880
+ const isUsingNext = isPackageExists("next");
810
881
  const a11yRules = {
811
882
  ...a11yConfig.rules || {},
812
- "jsx-a11y/alt-text": ["error", {
883
+ ...isUsingNext ? { "jsx-a11y/alt-text": ["warn", {
813
884
  elements: ["img"],
814
- img: [...isUsingNext ? ["Image"] : []]
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: "next/setup",
926
- plugins: { next: await interopDefault(import("@next/eslint-plugin-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: "next/rules",
998
+ name: "nextjs/rules",
934
999
  rules: {
935
- "next/google-font-display": "warn",
936
- "next/google-font-preconnect": "warn",
937
- "next/inline-script-id": "error",
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, reactCompiler = ReactCompilerPackages.some((i) => isPackageExists(i)) } = options;
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
- "react/no-leaked-conditional-rendering": "warn",
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-dom": plugins["@eslint-react/dom"],
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
- "react/jsx-key-before-spread": "warn",
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-dom/no-string-style-prop": "off",
1327
- "react-dom/no-unknown-property": "off",
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
- async function tailwindcss(options = {}) {
1660
- const { entryPoint, overrides = {}, stylistic = true, tsconfigPath } = options;
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
- const { indent = 2 } = typeof stylistic === "boolean" ? {} : stylistic;
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: pluginTailwindCSS },
1653
+ plugins: { "better-tailwindcss": _pluginTailwindCSS },
1667
1654
  rules: {
1668
1655
  ...stylistic ? {
1669
- "tailwindcss/enforce-canonical-classes": "warn",
1670
- "tailwindcss/enforce-consistent-class-order": "warn",
1671
- "tailwindcss/enforce-consistent-line-wrapping": ["warn", { indent: typeof indent === "number" ? indent : indent === "tab" ? "tab" : 2 }],
1672
- "tailwindcss/no-deprecated-classes": "warn",
1673
- "tailwindcss/no-duplicate-classes": "warn",
1674
- "tailwindcss/no-unnecessary-whitespace": "warn"
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
- "tailwindcss/no-conflicting-classes": "error",
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: "antfu/typescript/erasable-syntax-only",
1897
- plugins: { "erasable-syntax-only": await interopDefault(import("./lib-C63e_zBF.mjs")) },
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
- "stroustrup",
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: enableNext = NextJsPackages.some((i) => isPackageExists(i)), node: enableNode = true, pnpm: enableCatalogs = !!findUpSync("pnpm-workspace.yaml"), react: enableReact = ReactPackages.some((i) => isPackageExists(i)), reactNative: enableReactNative = ReactNativePackages.some((i) => isPackageExists(i)), regexp: enableRegexp = true, tailwindcss: enableTailwindCSS = isPackageExists("tailwindcss"), type: appType = "app", typescript: enableTypeScript = isPackageExists("typescript") || isPackageExists("@typescript/native-preview"), unicorn: enableUnicorn = true, unocss: enableUnoCSS = isPackageExists("unocss"), vue: enableVue = VuePackages.some((i) => isPackageExists(i)) } = options;
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(), perfectionist());
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 (enableReactNative) configs.push(reactNative({
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: { reactCompiler: true },
2392
- reactNative: { expo: true },
2373
+ react: true,
2393
2374
  regexp: true,
2375
+ sonarjs: true,
2394
2376
  stylistic: { experimental: true },
2395
- tailwindcss: true,
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, combine, command, comments, config, src_default as default, defaultPluginRenaming, disables, ensurePackages, formatters, getOverrides, ignores, imports, interopDefault, isInEditorEnv, isInGitHooksOrLintStaged, isPackageInScope, javascript, jsdoc, jsonc, jsx, markdown, nextjs, node, parserPlain, perfectionist, pnpm, react, reactNative, regexp, renameRules, resolveSubOptions, sortPackageJson, sortTsconfig, stylistic, tailwindcss, test, toml, typescript, unicorn, unocss, vue, yaml };
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 };