@vinicunca/eslint-config 4.10.0 → 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
@@ -145,13 +145,17 @@ const GLOB_EXCLUDE = [
145
145
  "**/.output",
146
146
  "**/.vite-inspect",
147
147
  "**/.yarn",
148
- "**/vite.config.*.timestamp-*",
149
148
  "**/CHANGELOG*.md",
150
- "**/*.min.*",
151
149
  "**/LICENSE*",
150
+ "**/*.min.*",
152
151
  "**/__snapshots__",
152
+ "**/vite.config.*.timestamp-*",
153
153
  "**/auto-import?(s).d.ts",
154
- "**/components.d.ts"
154
+ "**/components.d.ts",
155
+ "**/.context",
156
+ "**/.claude",
157
+ "**/.agents",
158
+ "**/.*/skills"
155
159
  ];
156
160
  //#endregion
157
161
  //#region src/utils.ts
@@ -390,6 +394,9 @@ async function e18e(options = {}) {
390
394
  ...modernization ? { ...configs.modernization.rules } : {},
391
395
  ...moduleReplacements ? { ...configs.moduleReplacements.rules } : {},
392
396
  ...performanceImprovements ? { ...configs.performanceImprovements.rules } : {},
397
+ ...type === "lib" ? {} : { "e18e/prefer-static-regex": "off" },
398
+ "e18e/prefer-array-at": "off",
399
+ "e18e/prefer-array-from-map": "off",
393
400
  "e18e/prefer-array-to-reversed": "off",
394
401
  "e18e/prefer-array-to-sorted": "off",
395
402
  "e18e/prefer-array-to-spliced": "off",
@@ -401,18 +408,20 @@ async function e18e(options = {}) {
401
408
  //#endregion
402
409
  //#region src/configs/stylistic.ts
403
410
  const STYLISTIC_CONFIG_DEFAULTS = {
411
+ braceStyle: "stroustrup",
404
412
  indent: 2,
405
413
  jsx: true,
406
414
  quotes: "single",
407
415
  semi: true
408
416
  };
409
417
  async function stylistic(options = {}) {
410
- const { experimental, indent, jsx, overrides = {}, quotes, semi } = {
418
+ const { braceStyle, experimental, indent, jsx, overrides = {}, quotes, semi } = {
411
419
  ...STYLISTIC_CONFIG_DEFAULTS,
412
420
  ...options
413
421
  };
414
422
  const pluginStylistic = await interopDefault(import("@stylistic/eslint-plugin"));
415
423
  const config = pluginStylistic.configs.customize({
424
+ braceStyle,
416
425
  experimental,
417
426
  indent,
418
427
  jsx,
@@ -637,8 +646,9 @@ async function formatters(options = {}, stylistic = {}) {
637
646
  }
638
647
  //#endregion
639
648
  //#region src/configs/ignores.ts
640
- async function ignores(userIgnores = []) {
649
+ async function ignores(userIgnores = [], ignoreTypeScript = false) {
641
650
  let ignores = [...GLOB_EXCLUDE];
651
+ if (ignoreTypeScript) ignores.push(GLOB_TS, GLOB_TSX);
642
652
  if (e$1(userIgnores)) ignores = userIgnores(ignores);
643
653
  else ignores = [...ignores, ...userIgnores];
644
654
  return [{
@@ -1064,6 +1074,11 @@ async function markdown(options = {}) {
1064
1074
  files,
1065
1075
  ignores: [GLOB_MARKDOWN_IN_MARKDOWN],
1066
1076
  name: "vinicunca/markdown/processor",
1077
+ /**
1078
+ * `eslint-plugin-markdown` only creates virtual files for code blocks,
1079
+ * but not the markdown file itself. We use `eslint-merge-processors` to
1080
+ * add a pass-through processor for the markdown file itself.
1081
+ */
1067
1082
  processor: mergeProcessors([markdown.processors.markdown, processorPassThrough])
1068
1083
  },
1069
1084
  {
@@ -1182,7 +1197,8 @@ async function node() {
1182
1197
  *
1183
1198
  * @see https://github.com/azat-io/eslint-plugin-perfectionist
1184
1199
  */
1185
- async function perfectionist() {
1200
+ async function perfectionist(options) {
1201
+ const { overrides = {} } = options;
1186
1202
  return [{
1187
1203
  name: "vinicunca/perfectionist/rules",
1188
1204
  plugins: { perfectionist: pluginPerfectionist },
@@ -1223,7 +1239,8 @@ async function perfectionist() {
1223
1239
  "perfectionist/sort-named-imports": [ERROR, {
1224
1240
  order: "asc",
1225
1241
  type: "natural"
1226
- }]
1242
+ }],
1243
+ ...overrides
1227
1244
  }
1228
1245
  }];
1229
1246
  }
@@ -1369,38 +1386,22 @@ const ReactRouterPackages = [
1369
1386
  "@react-router/dev"
1370
1387
  ];
1371
1388
  const NextJsPackages = ["next"];
1372
- const ReactCompilerPackages = ["babel-plugin-react-compiler"];
1373
1389
  async function react(options = {}) {
1374
- const { files = [GLOB_SRC], filesTypeAware = [GLOB_TS, GLOB_TSX], ignoresTypeAware = [`${GLOB_MARKDOWN}/**`, GLOB_ASTRO_TS], overrides = {}, reactCompiler = ReactCompilerPackages.some((i) => isPackageExists(i)), tsconfigPath } = options;
1375
- await ensurePackages([
1376
- "@eslint-react/eslint-plugin",
1377
- "eslint-plugin-react-hooks",
1378
- "eslint-plugin-react-refresh"
1379
- ]);
1390
+ const { files = [GLOB_SRC], filesTypeAware = [GLOB_TS, GLOB_TSX], ignoresTypeAware = [`${GLOB_MARKDOWN}/**`, GLOB_ASTRO_TS], overrides = {}, tsconfigPath } = options;
1391
+ await ensurePackages(["@eslint-react/eslint-plugin", "eslint-plugin-react-refresh"]);
1380
1392
  const isTypeAware = !!tsconfigPath;
1381
- const typeAwareRules = { "react/no-leaked-conditional-rendering": WARN };
1382
- const [pluginReact, pluginReactHooks, pluginReactRefresh] = await Promise.all([
1383
- interopDefault(import("@eslint-react/eslint-plugin")),
1384
- interopDefault(import("eslint-plugin-react-hooks")),
1385
- interopDefault(import("eslint-plugin-react-refresh"))
1386
- ]);
1393
+ const typeAwareRules = { "react/no-leaked-conditional-rendering": ERROR };
1394
+ const [pluginReact, pluginReactRefresh] = await Promise.all([interopDefault(import("@eslint-react/eslint-plugin")), interopDefault(import("eslint-plugin-react-refresh"))]);
1387
1395
  const isAllowConstantExport = ReactRefreshAllowConstantExportPackages.some((i) => isPackageExists(i));
1388
1396
  const isUsingRemix = RemixPackages.some((i) => isPackageExists(i));
1389
1397
  const isUsingReactRouter = ReactRouterPackages.some((i) => isPackageExists(i));
1390
1398
  const isUsingNext = NextJsPackages.some((i) => isPackageExists(i));
1391
- const plugins = pluginReact.configs.all.plugins;
1392
1399
  return [
1393
1400
  {
1394
1401
  name: "vinicunca/react/setup",
1395
1402
  plugins: {
1396
- "react": plugins["@eslint-react"],
1397
- "react-dom": plugins["@eslint-react/dom"],
1398
- "react-hooks": pluginReactHooks,
1399
- "react-hooks-extra": plugins["@eslint-react/hooks-extra"],
1400
- "react-naming-convention": plugins["@eslint-react/naming-convention"],
1401
- "react-refresh": pluginReactRefresh,
1402
- "react-rsc": plugins["@eslint-react/rsc"],
1403
- "react-web-api": plugins["@eslint-react/web-api"]
1403
+ "react": pluginReact.configs.all.plugins["@eslint-react"],
1404
+ "react-refresh": pluginReactRefresh
1404
1405
  }
1405
1406
  },
1406
1407
  {
@@ -1411,89 +1412,8 @@ async function react(options = {}) {
1411
1412
  },
1412
1413
  name: "vinicunca/react/rules",
1413
1414
  rules: {
1414
- "react-dom/no-dangerously-set-innerhtml": WARN,
1415
- "react-dom/no-dangerously-set-innerhtml-with-children": ERROR,
1416
- "react-dom/no-find-dom-node": ERROR,
1417
- "react-dom/no-flush-sync": ERROR,
1418
- "react-dom/no-hydrate": ERROR,
1419
- "react-dom/no-missing-button-type": WARN,
1420
- "react-dom/no-missing-iframe-sandbox": WARN,
1421
- "react-dom/no-namespace": ERROR,
1422
- "react-dom/no-render": ERROR,
1423
- "react-dom/no-render-return-value": ERROR,
1424
- "react-dom/no-script-url": WARN,
1425
- "react-dom/no-unsafe-iframe-sandbox": WARN,
1426
- "react-dom/no-unsafe-target-blank": WARN,
1427
- "react-dom/no-use-form-state": ERROR,
1428
- "react-dom/no-void-elements-with-children": ERROR,
1429
- "react-hooks-extra/no-direct-set-state-in-use-effect": WARN,
1430
- "react-hooks/exhaustive-deps": WARN,
1431
- "react-hooks/rules-of-hooks": ERROR,
1432
- "react-naming-convention/context-name": WARN,
1433
- "react-naming-convention/ref-name": WARN,
1434
- "react-naming-convention/use-state": WARN,
1435
- "react-rsc/function-definition": ERROR,
1436
- "react-web-api/no-leaked-event-listener": WARN,
1437
- "react-web-api/no-leaked-interval": WARN,
1438
- "react-web-api/no-leaked-resize-observer": WARN,
1439
- "react-web-api/no-leaked-timeout": WARN,
1440
- "react/jsx-no-comment-textnodes": WARN,
1441
- "react/jsx-no-duplicate-props": WARN,
1442
- "react/jsx-uses-vars": WARN,
1443
- "react/no-access-state-in-setstate": ERROR,
1444
- "react/no-array-index-key": WARN,
1445
- "react/no-children-count": WARN,
1446
- "react/no-children-for-each": WARN,
1447
- "react/no-children-map": WARN,
1448
- "react/no-children-only": WARN,
1449
- "react/no-children-to-array": WARN,
1450
- "react/no-clone-element": WARN,
1451
- "react/no-component-will-mount": ERROR,
1452
- "react/no-component-will-receive-props": ERROR,
1453
- "react/no-component-will-update": ERROR,
1454
- "react/no-context-provider": WARN,
1455
- "react/no-create-ref": ERROR,
1456
- "react/no-default-props": ERROR,
1457
- "react/no-direct-mutation-state": ERROR,
1458
- "react/no-forward-ref": WARN,
1459
- "react/no-missing-key": ERROR,
1460
- "react/no-nested-component-definitions": ERROR,
1461
- "react/no-prop-types": ERROR,
1462
- "react/no-redundant-should-component-update": ERROR,
1463
- "react/no-set-state-in-component-did-mount": WARN,
1464
- "react/no-set-state-in-component-did-update": WARN,
1465
- "react/no-set-state-in-component-will-update": WARN,
1466
- "react/no-string-refs": ERROR,
1467
- "react/no-unnecessary-use-prefix": WARN,
1468
- "react/no-unsafe-component-will-mount": WARN,
1469
- "react/no-unsafe-component-will-receive-props": WARN,
1470
- "react/no-unsafe-component-will-update": WARN,
1471
- "react/no-unstable-context-value": WARN,
1472
- "react/no-unstable-default-props": WARN,
1473
- "react/no-unused-class-component-members": WARN,
1474
- "react/no-unused-state": WARN,
1475
- "react/no-use-context": WARN,
1476
- "react/no-useless-forward-ref": WARN,
1477
- "react/prefer-namespace-import": ERROR,
1478
- "react/prefer-use-state-lazy-initialization": WARN,
1479
- ...reactCompiler ? {
1480
- "react-hooks/component-hook-factories": ERROR,
1481
- "react-hooks/config": ERROR,
1482
- "react-hooks/error-boundaries": ERROR,
1483
- "react-hooks/gating": ERROR,
1484
- "react-hooks/globals": ERROR,
1485
- "react-hooks/immutability": ERROR,
1486
- "react-hooks/incompatible-library": WARN,
1487
- "react-hooks/preserve-manual-memoization": ERROR,
1488
- "react-hooks/purity": ERROR,
1489
- "react-hooks/refs": ERROR,
1490
- "react-hooks/set-state-in-effect": ERROR,
1491
- "react-hooks/set-state-in-render": ERROR,
1492
- "react-hooks/static-components": ERROR,
1493
- "react-hooks/unsupported-syntax": WARN,
1494
- "react-hooks/use-memo": ERROR
1495
- } : {},
1496
- "react-refresh/only-export-components": [WARN, {
1415
+ ...pluginReact.configs.recommended.rules,
1416
+ "react-refresh/only-export-components": [ERROR, {
1497
1417
  allowConstantExport: isAllowConstantExport,
1498
1418
  allowExportNames: [...isUsingNext ? [
1499
1419
  "dynamic",
@@ -1503,12 +1423,13 @@ async function react(options = {}) {
1503
1423
  "runtime",
1504
1424
  "preferredRegion",
1505
1425
  "maxDuration",
1506
- "config",
1507
1426
  "generateStaticParams",
1508
1427
  "metadata",
1509
1428
  "generateMetadata",
1510
1429
  "viewport",
1511
- "generateViewport"
1430
+ "generateViewport",
1431
+ "generateImageMetadata",
1432
+ "generateSitemaps"
1512
1433
  ] : [], ...isUsingRemix || isUsingReactRouter ? [
1513
1434
  "meta",
1514
1435
  "links",
@@ -1528,12 +1449,8 @@ async function react(options = {}) {
1528
1449
  files: filesTypeAware,
1529
1450
  name: "vinicunca/react/typescript",
1530
1451
  rules: {
1531
- "react-dom/no-string-style-prop": "off",
1532
- "react-dom/no-unknown-property": "off",
1533
- "react/jsx-no-duplicate-props": "off",
1534
- "react/jsx-no-undef": "off",
1535
- "react/jsx-uses-react": "off",
1536
- "react/jsx-uses-vars": "off"
1452
+ "react/dom-no-string-style-prop": "off",
1453
+ "react/dom-no-unknown-property": "off"
1537
1454
  }
1538
1455
  },
1539
1456
  ...isTypeAware ? [{
@@ -1615,11 +1532,13 @@ async function solid(options = {}) {
1615
1532
  //#region src/configs/sonar.ts
1616
1533
  async function sonar() {
1617
1534
  return [{
1535
+ files: [GLOB_SRC],
1618
1536
  name: "vinicunca/sonar/rules",
1619
1537
  plugins: { sonar: pluginSonar },
1620
1538
  rules: {
1621
- ...pluginSonar.configs.recommended.rules,
1539
+ ...(pluginSonar.configs?.recommended).rules,
1622
1540
  "sonar/cognitive-complexity": "off",
1541
+ "sonar/no-unused-import": "off",
1623
1542
  "sonar/no-unused-vars": "off",
1624
1543
  "sonar/pseudo-random": "off",
1625
1544
  "sonar/slow-regex": "off",
@@ -2165,7 +2084,7 @@ async function typescript(options = {}) {
2165
2084
  }] : [],
2166
2085
  ...erasableOnly ? [{
2167
2086
  name: "antfu/typescript/erasable-syntax-only",
2168
- plugins: { "erasable-syntax-only": await interopDefault(import("./lib-D3Kr7UIJ.mjs")) },
2087
+ plugins: { "erasable-syntax-only": await interopDefault(import("./lib-BXrNxJq9.mjs")) },
2169
2088
  rules: {
2170
2089
  "erasable-syntax-only/enums": ERROR,
2171
2090
  "erasable-syntax-only/import-aliases": ERROR,
@@ -2221,7 +2140,7 @@ async function unocss(options = {}) {
2221
2140
  async function vue(options = {}) {
2222
2141
  const { a11y = false, files = [GLOB_VUE], overrides = {}, stylistic = true } = options;
2223
2142
  const sfcBlocks = options.sfcBlocks === true ? {} : options.sfcBlocks ?? {};
2224
- const { indent = 2 } = e$2(stylistic) ? {} : stylistic;
2143
+ const { braceStyle = "stroustrup", indent = 2 } = e$2(stylistic) ? {} : stylistic;
2225
2144
  if (a11y) await ensurePackages(["eslint-plugin-vuejs-accessibility"]);
2226
2145
  const [pluginVue, parserVue, processorVueBlocks, pluginVueA11y] = await Promise.all([
2227
2146
  interopDefault(import("eslint-plugin-vue")),
@@ -2363,7 +2282,7 @@ async function vue(options = {}) {
2363
2282
  }],
2364
2283
  "vue/brace-style": [
2365
2284
  ERROR,
2366
- "stroustrup",
2285
+ braceStyle,
2367
2286
  { allowSingleLine: true }
2368
2287
  ],
2369
2288
  "vue/comma-dangle": [ERROR, "always-multiline"],
@@ -2486,9 +2405,6 @@ const VuePackages = [
2486
2405
  ];
2487
2406
  const defaultPluginRenaming = {
2488
2407
  "@eslint-react": "react",
2489
- "@eslint-react/dom": "react-dom",
2490
- "@eslint-react/hooks-extra": "react-hooks-extra",
2491
- "@eslint-react/naming-convention": "react-naming-convention",
2492
2408
  "@next/next": "next",
2493
2409
  "@stylistic": "style",
2494
2410
  "@typescript-eslint": "ts",
@@ -2509,7 +2425,7 @@ const defaultPluginRenaming = {
2509
2425
  * The merged ESLint configurations.
2510
2426
  */
2511
2427
  function vinicuncaESLint(options = {}, ...userConfigs) {
2512
- const { astro: enableAstro = false, autoRenamePlugins = true, componentExts = [], e18e: enableE18e = true, gitignore: enableGitignore = true, ignores: userIgnores = [], imports: enableImports = true, jsdoc: enableJsdoc = true, jsx: enableJsx = true, nextjs: enableNextjs = false, node: enableNode = true, pnpm: enableCatalogs = !!findUpSync("pnpm-workspace.yaml"), react: enableReact = false, regexp: enableRegexp = true, solid: enableSolid = false, svelte: enableSvelte = false, type: appType = "app", typescript: enableTypeScript = isPackageExists("typescript"), unicorn: enableUnicorn = true, unocss: enableUnoCSS = false, vue: enableVue = VuePackages.some((i) => isPackageExists(i)) } = options;
2428
+ const { astro: enableAstro = false, autoRenamePlugins = true, componentExts = [], e18e: enableE18e = true, gitignore: enableGitignore = true, ignores: userIgnores = [], imports: enableImports = true, jsdoc: enableJsdoc = true, jsx: enableJsx = true, nextjs: enableNextjs = false, node: enableNode = true, perfectionist: enablePerfectionist = true, pnpm: enableCatalogs = !!findUpSync("pnpm-workspace.yaml"), react: enableReact = false, regexp: enableRegexp = true, solid: enableSolid = false, svelte: enableSvelte = 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;
2513
2429
  let isInEditor = options.isInEditor;
2514
2430
  if (isInEditor == null) {
2515
2431
  isInEditor = isInEditorEnv();
@@ -2528,10 +2444,11 @@ function vinicuncaESLint(options = {}, ...userConfigs) {
2528
2444
  })]));
2529
2445
  const typescriptOptions = resolveSubOptions(options, "typescript");
2530
2446
  const tsconfigPath = "tsconfigPath" in typescriptOptions ? typescriptOptions.tsconfigPath : void 0;
2531
- configs.push(ignores(userIgnores), javascript({
2447
+ configs.push(ignores(userIgnores, !enableTypeScript), javascript({
2532
2448
  isInEditor,
2533
2449
  overrides: getOverrides(options, "javascript")
2534
- }), comments(), imports({ stylistic: stylisticOptions }), command(), perfectionist(), sonar());
2450
+ }), comments(), command(), sonar());
2451
+ if (enablePerfectionist) configs.push(perfectionist({ overrides: getOverrides(options, "perfectionist") }));
2535
2452
  if (enableNode) configs.push(node());
2536
2453
  if (enableJsdoc) configs.push(jsdoc({ stylistic: stylisticOptions }));
2537
2454
  if (enableImports) configs.push(imports({