From 4678a372b1f47afad8d169640eb2c7710b8c83c5 Mon Sep 17 00:00:00 2001 From: robonen Date: Mon, 8 Jun 2026 15:50:59 +0700 Subject: [PATCH] chore(eslint): update shared presets and config exports --- configs/eslint/package.json | 1 + configs/eslint/src/index.ts | 2 +- configs/eslint/src/presets/base.ts | 68 ++++++++++++++++-------- configs/eslint/src/presets/imports.ts | 14 ++--- configs/eslint/src/presets/index.ts | 1 + configs/eslint/src/presets/regexp.ts | 18 +++++++ configs/eslint/src/presets/stylistic.ts | 2 +- configs/eslint/src/presets/typescript.ts | 59 ++++++++++++-------- configs/eslint/src/presets/vitest.ts | 29 ++++++---- configs/eslint/src/presets/vue.ts | 38 ++++++++----- 10 files changed, 156 insertions(+), 76 deletions(-) create mode 100644 configs/eslint/src/presets/regexp.ts diff --git a/configs/eslint/package.json b/configs/eslint/package.json index 93b131c..4a77862 100644 --- a/configs/eslint/package.json +++ b/configs/eslint/package.json @@ -50,6 +50,7 @@ "@vitest/eslint-plugin": "^1.6.19", "eslint-plugin-import-x": "^4.16.2", "eslint-plugin-n": "^18.0.1", + "eslint-plugin-regexp": "^3.1.0", "eslint-plugin-unicorn": "^64.0.0", "eslint-plugin-vue": "^10.9.2", "globals": "^17.6.0", diff --git a/configs/eslint/src/index.ts b/configs/eslint/src/index.ts index 5122390..498a123 100644 --- a/configs/eslint/src/index.ts +++ b/configs/eslint/src/index.ts @@ -2,7 +2,7 @@ export { compose } from './compose'; /* Presets */ -export { base, ignores, typescript, vue, vitest, imports, node, stylistic } from './presets'; +export { base, ignores, typescript, vue, vitest, imports, node, stylistic, regexp } from './presets'; /* Types */ export type { diff --git a/configs/eslint/src/presets/base.ts b/configs/eslint/src/presets/base.ts index b205f2f..b9f018c 100644 --- a/configs/eslint/src/presets/base.ts +++ b/configs/eslint/src/presets/base.ts @@ -2,6 +2,7 @@ import type { FlatConfigArray } from '../types'; import js from '@eslint/js'; import unicorn from 'eslint-plugin-unicorn'; import globals from 'globals'; +import { regexp } from './regexp'; /** * Globally ignored paths — build output, coverage, generated artifacts. @@ -61,41 +62,62 @@ export const base: FlatConfigArray = [ 'no-eval': 'error', 'no-var': 'error', 'prefer-const': 'error', - 'prefer-template': 'warn', - 'no-useless-constructor': 'warn', - 'no-useless-rename': 'warn', + 'prefer-template': 'error', + 'no-useless-constructor': 'error', + 'no-useless-rename': 'error', 'no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }], 'no-self-compare': 'error', - 'no-template-curly-in-string': 'warn', + 'no-template-curly-in-string': 'error', 'no-throw-literal': 'error', - 'no-return-assign': 'warn', - 'no-else-return': 'warn', - 'no-lonely-if': 'warn', - 'no-unneeded-ternary': 'warn', - 'prefer-object-spread': 'warn', - 'prefer-exponentiation-operator': 'warn', - 'no-useless-computed-key': 'warn', - 'no-useless-concat': 'warn', + 'no-return-assign': 'error', + 'no-else-return': 'error', + 'no-lonely-if': 'error', + 'no-unneeded-ternary': 'error', + 'prefer-object-spread': 'error', + 'prefer-exponentiation-operator': 'error', + 'no-useless-computed-key': 'error', + 'no-useless-concat': 'error', + 'no-array-constructor': 'error', + 'no-new-wrappers': 'error', + 'no-useless-return': 'error', + 'object-shorthand': ['error', 'always'], + 'prefer-spread': 'error', + 'prefer-rest-params': 'error', + 'symbol-description': 'error', curly: 'off', /* ── unicorn ──────────────────────────────────────────── */ 'unicorn/prefer-node-protocol': 'error', 'unicorn/no-instanceof-builtins': 'error', 'unicorn/no-new-array': 'error', - 'unicorn/prefer-array-flat-map': 'warn', - 'unicorn/prefer-array-flat': 'warn', - 'unicorn/prefer-includes': 'warn', - 'unicorn/prefer-string-slice': 'warn', - 'unicorn/prefer-string-starts-ends-with': 'warn', + 'unicorn/prefer-array-flat-map': 'error', + 'unicorn/prefer-array-flat': 'error', + 'unicorn/prefer-includes': 'error', + 'unicorn/prefer-string-slice': 'error', + 'unicorn/prefer-string-starts-ends-with': 'error', 'unicorn/throw-new-error': 'error', - 'unicorn/error-message': 'warn', - 'unicorn/no-useless-spread': 'warn', + 'unicorn/error-message': 'error', + 'unicorn/no-useless-spread': 'error', 'unicorn/no-useless-undefined': 'off', - 'unicorn/prefer-optional-catch-binding': 'warn', - 'unicorn/prefer-type-error': 'warn', + 'unicorn/prefer-optional-catch-binding': 'error', + 'unicorn/prefer-type-error': 'error', 'unicorn/no-thenable': 'error', - 'unicorn/prefer-number-properties': 'warn', - 'unicorn/prefer-global-this': 'warn', + 'unicorn/prefer-number-properties': 'error', + 'unicorn/prefer-global-this': 'error', + 'unicorn/prefer-array-some': 'error', + 'unicorn/prefer-array-find': 'error', + 'unicorn/prefer-array-index-of': 'error', + 'unicorn/prefer-date-now': 'error', + 'unicorn/prefer-modern-math-apis': 'error', + 'unicorn/prefer-negative-index': 'error', + 'unicorn/prefer-set-has': 'error', + 'unicorn/prefer-string-trim-start-end': 'error', + 'unicorn/prefer-regexp-test': 'error', + 'unicorn/prefer-string-replace-all': 'error', + 'unicorn/no-typeof-undefined': 'error', + 'unicorn/no-array-push-push': 'error', + 'unicorn/no-useless-promise-resolve-reject': 'error', }, }, + ...regexp, ]; diff --git a/configs/eslint/src/presets/imports.ts b/configs/eslint/src/presets/imports.ts index 22d4770..fa30055 100644 --- a/configs/eslint/src/presets/imports.ts +++ b/configs/eslint/src/presets/imports.ts @@ -25,17 +25,19 @@ export const imports: FlatConfigArray = [ rules: { 'import-x/no-duplicates': 'error', 'import-x/no-self-import': 'error', - 'import-x/no-cycle': 'warn', - 'import-x/first': 'warn', + 'import-x/no-cycle': 'error', + 'import-x/first': 'error', 'import-x/no-mutable-exports': 'error', 'import-x/no-amd': 'error', - 'import-x/no-commonjs': 'warn', - 'import-x/no-empty-named-blocks': 'warn', - 'import-x/consistent-type-specifier-style': ['warn', 'prefer-top-level'], + 'import-x/no-commonjs': 'error', + 'import-x/no-empty-named-blocks': 'error', + 'import-x/no-useless-path-segments': ['error', { noUselessIndex: false }], + 'import-x/consistent-type-specifier-style': ['error', 'prefer-top-level'], /* Only enforce member order within `{ … }`; declaration order is sorted by source path across the codebase, which core `sort-imports` (orders - by first member name) would otherwise fight. */ + by first member name) would otherwise fight. Kept at `warn` — it is not + autofixable and member order is a soft preference. */ 'sort-imports': ['warn', { ignoreDeclarationSort: true }], }, }, diff --git a/configs/eslint/src/presets/index.ts b/configs/eslint/src/presets/index.ts index 3c27e92..dd29f3e 100644 --- a/configs/eslint/src/presets/index.ts +++ b/configs/eslint/src/presets/index.ts @@ -4,4 +4,5 @@ export { vue } from './vue'; export { vitest } from './vitest'; export { imports } from './imports'; export { node } from './node'; +export { regexp } from './regexp'; export { stylistic } from './stylistic'; diff --git a/configs/eslint/src/presets/regexp.ts b/configs/eslint/src/presets/regexp.ts new file mode 100644 index 0000000..5e21887 --- /dev/null +++ b/configs/eslint/src/presets/regexp.ts @@ -0,0 +1,18 @@ +import type { FlatConfigArray } from '../types'; +import regexpPlugin from 'eslint-plugin-regexp'; + +/** + * Regular-expression correctness & optimization rules via + * [`eslint-plugin-regexp`](https://ota-meshi.github.io/eslint-plugin-regexp/). + * + * Applies the plugin's flat `recommended` ruleset — catches buggy/ambiguous + * patterns (control characters, useless quantifiers, ReDoS-prone constructs) + * and pushes toward clearer, faster expressions. Included in {@link base} so it + * applies to every package. + */ +export const regexp: FlatConfigArray = [ + { + ...regexpPlugin.configs['flat/recommended'], + name: 'robonen/regexp', + }, +]; diff --git a/configs/eslint/src/presets/stylistic.ts b/configs/eslint/src/presets/stylistic.ts index 2d602b3..15f9576 100644 --- a/configs/eslint/src/presets/stylistic.ts +++ b/configs/eslint/src/presets/stylistic.ts @@ -12,7 +12,7 @@ import stylisticPlugin from '@stylistic/eslint-plugin'; * - commaDangle: always-multiline * - arrowParens: as-needed * - blockSpacing: true - * - quoteProps: consistent-as-needed + * - quoteProps: as-needed * - jsx: true * * @see https://eslint.style/guide/config-presets diff --git a/configs/eslint/src/presets/typescript.ts b/configs/eslint/src/presets/typescript.ts index 5acadd9..1852856 100644 --- a/configs/eslint/src/presets/typescript.ts +++ b/configs/eslint/src/presets/typescript.ts @@ -4,15 +4,22 @@ import tseslint from 'typescript-eslint'; /** * TypeScript-specific configuration. * - * Pulls in `typescript-eslint`'s recommended (non type-checked) setup — which - * registers the parser/plugin and disables core rules superseded by their - * TypeScript-aware counterparts — then layers opinionated rules on top. + * Adopts `typescript-eslint`'s **strict** (non type-checked) ruleset plus the + * **stylistic** ruleset — registering the parser/plugin, disabling core rules + * superseded by TS-aware counterparts, and enforcing the full strict + stylistic + * sets at `error`. A small overlay re-tunes a few rules for this monorepo. + * + * Two deliberate carve-outs: `no-explicit-any` is kept at `warn` (the low-level + * stdlib/toolkit does a lot of type-boundary work where `any` is idiomatic), and + * `no-non-null-assertion` is `off` (the `!` operator is how the codebase satisfies + * `noUncheckedIndexedAccess` on provably-bounded indexed access). * * `.vue` files are included so the rules apply inside `