@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.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
- 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;
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
- "antfu/curly": "error",
295
- "antfu/if-newline": "error",
296
- "antfu/top-level-function": "error",
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: 120,
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 = NextJsPackages.some((i) => isPackageExists(i));
878
+ const isUsingNext = isPackageExists("next");
810
879
  const a11yRules = {
811
880
  ...a11yConfig.rules || {},
812
- "jsx-a11y/alt-text": ["error", {
881
+ ...isUsingNext ? { "jsx-a11y/alt-text": ["warn", {
813
882
  elements: ["img"],
814
- img: [...isUsingNext ? ["Image"] : []]
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: "next/setup",
925
- plugins: { next: await interopDefault(import("@next/eslint-plugin-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: "next/rules",
996
+ name: "nextjs/rules",
933
997
  rules: {
934
- "next/google-font-display": "warn",
935
- "next/google-font-preconnect": "warn",
936
- "next/inline-script-id": "error",
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, reactCompiler = ReactCompilerPackages.some((i) => isPackageExists(i)) } = options;
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
- "react/no-leaked-conditional-rendering": "warn",
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-dom": plugins["@eslint-react/dom"],
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
- "react/jsx-key-before-spread": "warn",
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-dom/no-string-style-prop": "off",
1326
- "react-dom/no-unknown-property": "off",
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
- async function tailwindcss(options = {}) {
1659
- const { entryPoint, overrides = {}, stylistic = true, tsconfigPath } = options;
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
- const { indent = 2 } = typeof stylistic === "boolean" ? {} : stylistic;
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: pluginTailwindCSS },
1649
+ plugins: { "better-tailwindcss": _pluginTailwindCSS },
1666
1650
  rules: {
1667
1651
  ...stylistic ? {
1668
- "tailwindcss/enforce-canonical-classes": "warn",
1669
- "tailwindcss/enforce-consistent-class-order": "warn",
1670
- "tailwindcss/enforce-consistent-line-wrapping": ["warn", { indent: typeof indent === "number" ? indent : indent === "tab" ? "tab" : 2 }],
1671
- "tailwindcss/no-deprecated-classes": "warn",
1672
- "tailwindcss/no-duplicate-classes": "warn",
1673
- "tailwindcss/no-unnecessary-whitespace": "warn"
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
- "tailwindcss/no-conflicting-classes": "error",
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: "antfu/typescript/erasable-syntax-only",
1895
- plugins: { "erasable-syntax-only": await interopDefault(import("./lib-C63e_zBF.mjs")) },
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: 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"), typescript: enableTypeScript = isPackageExists("typescript"), unicorn: enableUnicorn = true, unocss: enableUnoCSS = isPackageExists("unocss"), vue: enableVue = VuePackages.some((i) => isPackageExists(i)) } = options;
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: options.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 (enableReactNative) configs.push(reactNative({
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: { reactCompiler: true },
2390
- reactNative: { expo: true },
2367
+ react: true,
2391
2368
  regexp: true,
2369
+ sonarjs: true,
2392
2370
  stylistic: { experimental: true },
2393
- tailwindcss: true,
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, 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 };
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 };