From 9bcae32c5bf28ff5021d99992f7aabdeafaa92ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Costa?= Date: Mon, 9 Nov 2026 09:45:10 +0100 Subject: [PATCH 1/7] feat(oxlint): warn on code-quality rules Adds complexity (10), max-depth (3), max-params (3), no-else-return, no-nested-ternary, no-param-reassign (props), import/no-cycle and typescript/switch-exhaustiveness-check as warnings. The size and style rules are off in tests and stories. --- .changeset/calm-rules-warn.md | 6 ++++++ packages/oxlint-config-basic/index.mjs | 28 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 .changeset/calm-rules-warn.md diff --git a/.changeset/calm-rules-warn.md b/.changeset/calm-rules-warn.md new file mode 100644 index 0000000..3c8403b --- /dev/null +++ b/.changeset/calm-rules-warn.md @@ -0,0 +1,6 @@ +--- +'@frontify/oxlint-config-basic': minor +'@frontify/oxlint-config-react': minor +--- + +feat: warn on code-quality rules (`complexity`, `max-depth`, `max-params`, `no-else-return`, `no-nested-ternary`, `no-param-reassign`, `import/no-cycle`, `typescript/switch-exhaustiveness-check`), off in tests and stories diff --git a/packages/oxlint-config-basic/index.mjs b/packages/oxlint-config-basic/index.mjs index 807acee..1305aaa 100644 --- a/packages/oxlint-config-basic/index.mjs +++ b/packages/oxlint-config-basic/index.mjs @@ -89,6 +89,14 @@ export default defineConfig({ 'typescript/restrict-template-expressions': 'error', 'typescript/triple-slash-reference': 'error', 'typescript/unbound-method': 'error', + // Code-quality rules start as warnings; raise to error once consumers are clean. + complexity: ['warn', { max: 10 }], + 'max-depth': ['warn', { max: 3 }], + 'max-params': ['warn', { max: 3 }], + 'no-else-return': 'warn', + 'no-nested-ternary': 'warn', + 'no-param-reassign': ['warn', { props: true }], + 'import/no-cycle': 'warn', }, overrides: [ { @@ -537,6 +545,10 @@ export default defineConfig({ 'typescript/restrict-plus-operands': 'error', 'typescript/restrict-template-expressions': 'error', 'typescript/unbound-method': 'off', + 'typescript/switch-exhaustiveness-check': [ + 'warn', + { considerDefaultExhaustiveForUnions: true }, + ], 'typescript/consistent-type-imports': [ 'error', { prefer: 'type-imports', fixStyle: 'inline-type-imports' }, @@ -670,6 +682,22 @@ export default defineConfig({ }, jsPlugins: ['eslint-plugin-no-only-tests'], }, + { + files: [ + '**/*.test.{ts,tsx,js,jsx}', + '**/*.spec.{ts,tsx,js,jsx}', + '**/*.spec.{cy,ct}.{ts,tsx,js,jsx}', + '**/*.stories.{ts,tsx,js,jsx}', + ], + rules: { + complexity: 'off', + 'max-depth': 'off', + 'max-params': 'off', + 'no-else-return': 'off', + 'no-nested-ternary': 'off', + 'no-param-reassign': 'off', + }, + }, { files: ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'], rules: { From eee61509f7673d7006e1104c95985e265602aced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Costa?= Date: Mon, 9 Nov 2026 09:48:51 +0100 Subject: [PATCH 2/7] feat(oxlint): warn on JSDoc shape in existing doc blocks Adds native jsdoc rules (check-tag-names, no-defaults, require-param for blocks that already list params, param and returns descriptions) and eslint-plugin-jsdoc as the jsdoc-js JS plugin for no-types, check-param-names and require-hyphen-before-param-description. No rule requires a doc block. --- .changeset/calm-rules-warn.md | 2 +- packages/oxlint-config-basic/index.mjs | 17 +- packages/oxlint-config-basic/package.json | 1 + pnpm-lock.yaml | 285 +++++++++++++++++++++- 4 files changed, 296 insertions(+), 9 deletions(-) diff --git a/.changeset/calm-rules-warn.md b/.changeset/calm-rules-warn.md index 3c8403b..df5f4bb 100644 --- a/.changeset/calm-rules-warn.md +++ b/.changeset/calm-rules-warn.md @@ -3,4 +3,4 @@ '@frontify/oxlint-config-react': minor --- -feat: warn on code-quality rules (`complexity`, `max-depth`, `max-params`, `no-else-return`, `no-nested-ternary`, `no-param-reassign`, `import/no-cycle`, `typescript/switch-exhaustiveness-check`), off in tests and stories +feat: warn on code-quality rules (`complexity`, `max-depth`, `max-params`, `no-else-return`, `no-nested-ternary`, `no-param-reassign`, `import/no-cycle`, `typescript/switch-exhaustiveness-check`), off in tests and stories, and on JSDoc shape rules for existing doc blocks diff --git a/packages/oxlint-config-basic/index.mjs b/packages/oxlint-config-basic/index.mjs index 1305aaa..08a508a 100644 --- a/packages/oxlint-config-basic/index.mjs +++ b/packages/oxlint-config-basic/index.mjs @@ -3,8 +3,12 @@ import { defineConfig } from 'oxlint'; export default defineConfig({ - plugins: ['typescript', 'eslint', 'promise', 'unicorn', 'import'], - jsPlugins: ['@eslint-community/eslint-plugin-eslint-comments'], + plugins: ['typescript', 'eslint', 'promise', 'unicorn', 'import', 'jsdoc'], + jsPlugins: [ + '@eslint-community/eslint-plugin-eslint-comments', + // Aliased: the native jsdoc plugin lacks these rules and owns the `jsdoc` name. + { name: 'jsdoc-js', specifier: 'eslint-plugin-jsdoc' }, + ], categories: { correctness: 'off', }, @@ -97,6 +101,15 @@ export default defineConfig({ 'no-nested-ternary': 'warn', 'no-param-reassign': ['warn', { props: true }], 'import/no-cycle': 'warn', + // JSDoc rules check existing blocks only: no block is required, and a summary-only block needs no @param. + 'jsdoc/check-tag-names': 'warn', + 'jsdoc/no-defaults': 'warn', + 'jsdoc/require-param': ['warn', { ignoreWhenAllParamsMissing: true }], + 'jsdoc/require-param-description': 'warn', + 'jsdoc/require-returns-description': 'warn', + 'jsdoc-js/check-param-names': 'warn', + 'jsdoc-js/no-types': 'warn', + 'jsdoc-js/require-hyphen-before-param-description': 'warn', }, overrides: [ { diff --git a/packages/oxlint-config-basic/package.json b/packages/oxlint-config-basic/package.json index e976517..284816c 100644 --- a/packages/oxlint-config-basic/package.json +++ b/packages/oxlint-config-basic/package.json @@ -23,6 +23,7 @@ "dependencies": { "@eslint-community/eslint-plugin-eslint-comments": "^4.7.2", "@stylistic/eslint-plugin": "^5.10.0", + "eslint-plugin-jsdoc": "^65.4.5", "eslint-plugin-jsonc": "^3.2.0", "eslint-plugin-no-only-tests": "^3.4.0", "eslint-plugin-yml": "^3.4.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0efa338..e3090e9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -55,10 +55,10 @@ importers: version: 10.1.8(eslint@10.4.1(jiti@2.7.0)) eslint-import-resolver-typescript: specifier: ^4.4.5 - version: 4.4.5(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.1(jiti@2.7.0)))(eslint@10.4.1(jiti@2.7.0)) + version: 4.4.5(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.72.1(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.1(jiti@2.7.0)))(eslint@10.4.1(jiti@2.7.0)) eslint-plugin-import-x: specifier: ^4.16.2 - version: 4.16.2(@typescript-eslint/utils@8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.1(jiti@2.7.0)) + version: 4.16.2(@typescript-eslint/utils@8.72.1(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.1(jiti@2.7.0)) eslint-plugin-jsonc: specifier: ^3.2.0 version: 3.2.0(eslint@10.4.1(jiti@2.7.0)) @@ -148,6 +148,9 @@ importers: '@stylistic/eslint-plugin': specifier: ^5.10.0 version: 5.10.0(eslint@10.4.1(jiti@2.7.0)) + eslint-plugin-jsdoc: + specifier: ^65.4.5 + version: 65.4.5(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3) eslint-plugin-jsonc: specifier: ^3.2.0 version: 3.2.0(eslint@10.4.1(jiti@2.7.0)) @@ -261,6 +264,27 @@ packages: '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + '@es-joy/escodegen@5.0.1': + resolution: {integrity: sha512-0sOnDSVMt7Zg5qmXz4ab1yOXrUi0QmSPqpm5d547puNdJxT0J+Xdip2F/5j07nuE1pW131AX5jFUvOIfZPYNRg==} + engines: {node: ^22.16.0 || >=24.0.0} + hasBin: true + + '@es-joy/estraverse@7.1.1': + resolution: {integrity: sha512-h2d737iS0LFAqypZOmXizu54qAbHofI/RpD30Zom03O6BCXKHuFevWKPcGarZbGq82h7qSBb5G6kLdhTMO+caw==} + engines: {node: '>=4.0'} + + '@es-joy/esutils@3.0.0': + resolution: {integrity: sha512-K8M2b2ixqOiF5B+qYJIWIznFtvbD8biX2mj31pXmXAaTRUUd2x6anAFZk4GVGXz7sX9tOkx+z6TaQc/paeVCeg==} + engines: {node: ^22.16.0 || >=24.0.0} + + '@es-joy/jsdoccomment@1.1.0': + resolution: {integrity: sha512-DpIIjjhvlQqpQrsDC8x911I/8mx6athyl/Tk9+lIq5UtQrNmh3EtLaDYKeeu58wh5HIzUfRhjy84S4szWZ9q+A==} + engines: {node: ^22.22.2 || >=24.15.0} + + '@es-joy/resolve.exports@1.2.0': + resolution: {integrity: sha512-Q9hjxWI5xBM+qW2enxfe8wDKdFWMfd0Z29k5ZJnuBqD/CasY5Zryj09aCA6owbGATWz+39p5uIdaHXpopOcG8g==} + engines: {node: '>=10'} + '@eslint-community/eslint-plugin-eslint-comments@4.7.2': resolution: {integrity: sha512-LF03qURSwEWm2dz5wtdDCzNk+7Opl0X7q6I3undsaIuNsEiNvRV3BCtqu14Q/6Pzg1tBj44LcxpW2EpSLZStZw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -680,6 +704,10 @@ packages: '@quansync/fs@1.0.0': resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==} + '@sindresorhus/base62@1.0.0': + resolution: {integrity: sha512-TeheYy0ILzBEI/CO55CP6zJCSdSWeRtGnHy8U8dWSUH4I68iqTsy7HkMktR4xakThc9jotkPQUXT4ITdbV7cHA==} + engines: {node: '>=18'} + '@stylistic/eslint-plugin@5.10.0': resolution: {integrity: sha512-nPK52ZHvot8Ju/0A4ucSX1dcPV2/1clx0kLcH5wDmrE4naKso7TUC/voUyU1O9OTKTrR6MYip6LP0ogEMQ9jPQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -695,6 +723,9 @@ packages: '@types/esrecurse@4.3.1': resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + '@types/estree@1.0.10': + resolution: {integrity: sha512-lTyOMnOcmRBzdCv6/4zUZCrEhV0xrqQiUZBZtVcbmLHBXKLnN8q89v/wKRF/Q0HbwVsqbIG3o5HDoFl9sduM+g==} + '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} @@ -740,16 +771,32 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/project-service@8.72.1': + resolution: {integrity: sha512-fAuVMkwdO/huI8nyo/WKnNKf6Gag93S56Rh3Ts5mGcavwSdaN8tRg1gCeIGX8PF+nzp1BjGfO4gHhGzW1J6jTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/scope-manager@8.60.0': resolution: {integrity: sha512-pFzqhllJMs+jghLQWzV00ds39xLzuyqPSev5pd8f4Ir0rtKR3ZLUB4/4dhjOFighWb9larvtfJvqL+4yKDI3Xw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/scope-manager@8.72.1': + resolution: {integrity: sha512-lC/t6eMCZZQAAKZEYGL6RDVSuFehpdmMT6h+nJIjIa6oylN/PwDgDVtEIbB7O9xFfZYtZAYLoLlDNWKJhtF+uQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/tsconfig-utils@8.60.0': resolution: {integrity: sha512-BZPR3RGYlAXnly6ymAxfkVn5rCbZzQNou0rxv3GfWZ8cTQp+hhVd73khbGLAd8k1TlAPLISH337M+tAgAnaJDQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/tsconfig-utils@8.72.1': + resolution: {integrity: sha512-e4D5R5vpeAEPBxT2BAzsFQL3sbwyeeeo5+aczgNSRK1V734jE7pTqaGFU+fXapWEQwnZbpFaTn32+Z8DwS6OTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/type-utils@8.60.0': resolution: {integrity: sha512-SX46wEUtitCpq7AN38HkUU/+zvUpdKf7ephtWAFgckH8O7PQIyL5gvrhQgBLuEYgLfuKWOVvWVskMbuFHAz5xg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -761,12 +808,22 @@ packages: resolution: {integrity: sha512-AsE7x2XaAK+CVbeih0Fvbn+r1qHxtpLDJ3XUuFcIinT318T90yHMJC+Zgv+jUuDjQQd06HKwxnDu6sz1IcTilA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.72.1': + resolution: {integrity: sha512-LmE5IhQet1BWOWEXqBrVtBPsMAOQ6Nzk35d+WLh3mk4EUF33sqlfxjio1MdYeSvKSG5skactKKuixBIKXhL4Rw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@8.60.0': resolution: {integrity: sha512-3AcZNBGMClm6CXDyo8kYvVGT/sx29sS0oBsIb9oZI2gunA4Vm2M3YHzRLPvsUBBsl+yB5FPtltq7gGH0iTlp9g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/typescript-estree@8.72.1': + resolution: {integrity: sha512-DAlUoUV93hIvrtIQLwCbmZQYVZwOMhiHJrOVUUIWicFZaIYjTRqlY3I/NZevfkLmSqkNHRu2qYDcq9lrn16oOQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/utils@8.60.0': resolution: {integrity: sha512-HtXuPfrHTyBDkameWpl+vJb1Uevu2tznAyahM1Oc4AENidCLTPiZDWIo4GfcxNdC/RcfGcadzzkqbRG87dUrQA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -774,10 +831,21 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/utils@8.72.1': + resolution: {integrity: sha512-5v5liypurJBFHCBNqh/RjsymAD+VWcE0djuGY/BUqp5fC3Qn+luWDEksjuTtWjPn6i5zQlUtkZCRG/CYG0yCkA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/visitor-keys@8.60.0': resolution: {integrity: sha512-9WI52t8ZGLVGrPMBet25yAftqY/n95+zmoUUtJBBQTKDSKUu7OsPTroT2op7U9JatkoRccL0YkWDNMFfC4Sjxg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/visitor-keys@8.72.1': + resolution: {integrity: sha512-SB8xqipSzZHUePCCLNeYxqnxq83X61/qzNhjQGbTfjxGZIeOEP7tBgWYX05Tcd6CgE02WdTJnh5b8kMZJ/+bnQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@unrs/resolver-binding-android-arm-eabi@1.12.2': resolution: {integrity: sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==} cpu: [arm] @@ -919,6 +987,10 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + are-docs-informative@0.1.1: + resolution: {integrity: sha512-sqRsNQBwbKLRX0jV5Cu5uzmtflf892n4Vukz7T659ebL4pz3mpOqCMU7lxMoBTFwnp10E3YB5ZcyHM41W5bcDA==} + engines: {node: '>=18'} + argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -1009,6 +1081,10 @@ packages: resolution: {integrity: sha512-0h+uSNtQGW3D98eQt3jJ8L06Fves8hncB4V/PKdw/Qb8Hnk19VaKuTr55UNRYiSoVa7WwrFls+rh3ux9agmkeQ==} engines: {node: '>= 12.0.0'} + comment-parser@1.4.9: + resolution: {integrity: sha512-+2AvZKjJaNq9GQljm3tr0utwrig5BL+jgJGvz5rDpGKNIp+ojcRXWUwBbh/sGIi1QCprR6PsKFakub+w1v+4hQ==} + engines: {node: '>= 12.0.0'} + compare-versions@6.1.1: resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} @@ -1144,6 +1220,16 @@ packages: eslint-import-resolver-node: optional: true + eslint-plugin-jsdoc@65.4.5: + resolution: {integrity: sha512-qQiYrQDTi4b5jO6oy1oeKokbBZC8nRbjZBgjhR9tHE//naMCjjRzprqhd/4jU+Pj2exPeCDdWogg22gxSJSCEg==} + engines: {node: ^22.22.2 || >=24.15.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + eslint-plugin-jsonc@3.2.0: resolution: {integrity: sha512-eQSxJypkpNycQAFE/ph/j+bDD2MiCcojxNb+7nugYzuQZvELYg4YO1Cv1y/8MbjPIEw5u3Lx0VPOTlqJJIhPPw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} @@ -1404,6 +1490,9 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + html-entities@2.6.0: + resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} + human-id@4.1.3: resolution: {integrity: sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==} hasBin: true @@ -1470,6 +1559,10 @@ packages: resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==} hasBin: true + jsdoc-type-pratt-parser@9.5.0: + resolution: {integrity: sha512-Qu/AuBJocEUhxq187P4Ud7xGBHB+NhtrclSrS7Utkr6uNeeh8BrZ5+Ax/AGe8VXQLGY+4oYgbUp04hY8iGsIWA==} + engines: {node: ^22.22.2 || >=24.15.0} + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -1712,6 +1805,9 @@ packages: resolution: {integrity: sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==} engines: {node: '>=18'} + object-deep-merge@2.0.1: + resolution: {integrity: sha512-aKttDKcU3pyZqKcCkDhsMn70WmZFG2JGDQLP9EcLyTSIFQRCPWLAmBZRLJnrVUrhPG1jETEEbfdgbNtJf1LyMg==} + ofetch@1.5.1: resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} @@ -1786,6 +1882,12 @@ packages: package-manager-detector@1.6.0: resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} + parse-imports-exports@0.2.4: + resolution: {integrity: sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==} + + parse-statements@1.0.11: + resolution: {integrity: sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -1870,6 +1972,10 @@ packages: resolution: {integrity: sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==} hasBin: true + reserved-identifiers@1.2.0: + resolution: {integrity: sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==} + engines: {node: '>=18'} + resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} @@ -1896,6 +2002,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.8.6: + resolution: {integrity: sha512-9lpSSU4Mly3mrtruwLs1eLK6+gzbzqeBFwRNjIjE+9LuDpWdYctrQlQ3ej1Ilgi7gknpsTRnPoLP8DpCHqHIJw==} + engines: {node: '>=10'} + hasBin: true + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1912,9 +2023,22 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + source-map@0.8.0: + resolution: {integrity: sha512-d8EqvL+k/SOXCreS/SUzg2ciyHqBBLcN/yuRjFsbvVhHTE2pgei7oAhmPM7kWFbkX6OSMQfUq4KbkF3au9lhYQ==} + engines: {node: '>= 12'} + spawndamnit@3.0.1: resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@5.0.0: + resolution: {integrity: sha512-vngmw3Rgn+o2arXNbnZaj5UtOEBuWBfvaI+Wc8GFfykIhA5/vdK9/Sp/XkLv63dykz2rxKDvKEHupF5P0FORcQ==} + + spdx-license-ids@3.0.24: + resolution: {integrity: sha512-cLS9TtWkIQFyLkJ3/5aFQAOHOSKTlOs/7WDut/XPSdjom1fJhUdkepeJAKXa9Y05+FabHd0sti+Et559vDtkpQ==} + sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} @@ -1965,6 +2089,10 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + to-valid-identifier@1.0.0: + resolution: {integrity: sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw==} + engines: {node: '>=20'} + tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} @@ -2257,6 +2385,33 @@ snapshots: tslib: 2.8.1 optional: true + '@es-joy/escodegen@5.0.1': + dependencies: + '@es-joy/estraverse': 7.1.1 + '@es-joy/esutils': 3.0.0 + '@es-joy/jsdoccomment': 1.1.0 + '@types/estree': 1.0.10 + esprima: 4.0.1 + optionator: 0.9.4 + optionalDependencies: + source-map: 0.8.0 + + '@es-joy/estraverse@7.1.1': {} + + '@es-joy/esutils@3.0.0': + dependencies: + '@types/estree': 1.0.9 + + '@es-joy/jsdoccomment@1.1.0': + dependencies: + '@types/estree': 1.0.10 + '@typescript-eslint/types': 8.72.1 + comment-parser: 1.4.9 + esquery: 1.7.0 + jsdoc-type-pratt-parser: 9.5.0 + + '@es-joy/resolve.exports@1.2.0': {} + '@eslint-community/eslint-plugin-eslint-comments@4.7.2(eslint@10.4.1(jiti@2.7.0))': dependencies: escape-string-regexp: 4.0.0 @@ -2595,6 +2750,8 @@ snapshots: dependencies: quansync: 1.0.0 + '@sindresorhus/base62@1.0.0': {} + '@stylistic/eslint-plugin@5.10.0(eslint@10.4.1(jiti@2.7.0))': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.1(jiti@2.7.0)) @@ -2616,6 +2773,8 @@ snapshots: '@types/esrecurse@4.3.1': {} + '@types/estree@1.0.10': {} + '@types/estree@1.0.9': {} '@types/hast@3.0.4': @@ -2673,15 +2832,33 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/project-service@8.72.1(typescript@6.0.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.72.1(typescript@6.0.3) + '@typescript-eslint/types': 8.72.1 + debug: 4.4.3 + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/scope-manager@8.60.0': dependencies: '@typescript-eslint/types': 8.60.0 '@typescript-eslint/visitor-keys': 8.60.0 + '@typescript-eslint/scope-manager@8.72.1': + dependencies: + '@typescript-eslint/types': 8.72.1 + '@typescript-eslint/visitor-keys': 8.72.1 + '@typescript-eslint/tsconfig-utils@8.60.0(typescript@6.0.3)': dependencies: typescript: 6.0.3 + '@typescript-eslint/tsconfig-utils@8.72.1(typescript@6.0.3)': + dependencies: + typescript: 6.0.3 + '@typescript-eslint/type-utils@8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3)': dependencies: '@typescript-eslint/types': 8.60.0 @@ -2696,6 +2873,8 @@ snapshots: '@typescript-eslint/types@8.60.0': {} + '@typescript-eslint/types@8.72.1': {} + '@typescript-eslint/typescript-estree@8.60.0(typescript@6.0.3)': dependencies: '@typescript-eslint/project-service': 8.60.0(typescript@6.0.3) @@ -2711,6 +2890,21 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/typescript-estree@8.72.1(typescript@6.0.3)': + dependencies: + '@typescript-eslint/project-service': 8.72.1(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.72.1(typescript@6.0.3) + '@typescript-eslint/types': 8.72.1 + '@typescript-eslint/visitor-keys': 8.72.1 + debug: 4.4.3 + minimatch: 10.2.5 + semver: 7.8.1 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/utils@8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.1(jiti@2.7.0)) @@ -2722,11 +2916,27 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/utils@8.72.1(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.1(jiti@2.7.0)) + '@typescript-eslint/scope-manager': 8.72.1 + '@typescript-eslint/types': 8.72.1 + '@typescript-eslint/typescript-estree': 8.72.1(typescript@6.0.3) + eslint: 10.4.1(jiti@2.7.0) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/visitor-keys@8.60.0': dependencies: '@typescript-eslint/types': 8.60.0 eslint-visitor-keys: 5.0.1 + '@typescript-eslint/visitor-keys@8.72.1': + dependencies: + '@typescript-eslint/types': 8.72.1 + eslint-visitor-keys: 5.0.1 + '@unrs/resolver-binding-android-arm-eabi@1.12.2': optional: true @@ -2814,6 +3024,8 @@ snapshots: ansi-regex@5.0.1: {} + are-docs-informative@0.1.1: {} + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 @@ -2878,6 +3090,8 @@ snapshots: comment-parser@1.4.7: {} + comment-parser@1.4.9: {} + compare-versions@6.1.1: {} core-js-compat@3.49.0: @@ -2950,7 +3164,7 @@ snapshots: optionalDependencies: unrs-resolver: 1.12.2 - eslint-import-resolver-typescript@4.4.5(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.1(jiti@2.7.0)))(eslint@10.4.1(jiti@2.7.0)): + eslint-import-resolver-typescript@4.4.5(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.72.1(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.1(jiti@2.7.0)))(eslint@10.4.1(jiti@2.7.0)): dependencies: debug: 4.4.3 eslint: 10.4.1(jiti@2.7.0) @@ -2961,7 +3175,7 @@ snapshots: tinyglobby: 0.2.17 unrs-resolver: 1.12.2 optionalDependencies: - eslint-plugin-import-x: 4.16.2(@typescript-eslint/utils@8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.1(jiti@2.7.0)) + eslint-plugin-import-x: 4.16.2(@typescript-eslint/utils@8.72.1(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.1(jiti@2.7.0)) transitivePeerDependencies: - supports-color @@ -2971,7 +3185,7 @@ snapshots: esquery: 1.7.0 jsonc-eslint-parser: 3.1.0 - eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.1(jiti@2.7.0)): + eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.72.1(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.1(jiti@2.7.0)): dependencies: '@package-json/types': 0.0.12 '@typescript-eslint/types': 8.60.0 @@ -2985,7 +3199,31 @@ snapshots: stable-hash-x: 0.2.0 unrs-resolver: 1.12.2 optionalDependencies: - '@typescript-eslint/utils': 8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/utils': 8.72.1(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3) + transitivePeerDependencies: + - supports-color + + eslint-plugin-jsdoc@65.4.5(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3): + dependencies: + '@es-joy/escodegen': 5.0.1 + '@es-joy/jsdoccomment': 1.1.0 + '@es-joy/resolve.exports': 1.2.0 + '@typescript-eslint/utils': 8.72.1(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3) + are-docs-informative: 0.1.1 + comment-parser: 1.4.9 + debug: 4.4.3 + escape-string-regexp: 5.0.0 + eslint: 10.4.1(jiti@2.7.0) + espree: 11.2.0 + esquery: 1.7.0 + html-entities: 2.6.0 + object-deep-merge: 2.0.1 + parse-imports-exports: 0.2.4 + semver: 7.8.6 + spdx-expression-parse: 5.0.0 + to-valid-identifier: 1.0.0 + optionalDependencies: + typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -3346,6 +3584,8 @@ snapshots: graceful-fs@4.2.11: {} + html-entities@2.6.0: {} + human-id@4.1.3: {} iconv-lite@0.7.2: @@ -3395,6 +3635,10 @@ snapshots: dependencies: argparse: 2.0.1 + jsdoc-type-pratt-parser@9.5.0: + dependencies: + '@types/estree': 1.0.9 + jsesc@3.1.0: {} json-buffer@3.0.1: {} @@ -3814,6 +4058,8 @@ snapshots: node-releases@2.0.46: {} + object-deep-merge@2.0.1: {} + ofetch@1.5.1: dependencies: destr: 2.0.5 @@ -3911,6 +4157,12 @@ snapshots: package-manager-detector@1.6.0: {} + parse-imports-exports@0.2.4: + dependencies: + parse-statements: 1.0.11 + + parse-statements@1.0.11: {} + path-exists@4.0.0: {} path-key@3.1.1: {} @@ -3966,6 +4218,8 @@ snapshots: dependencies: jsesc: 3.1.0 + reserved-identifiers@1.2.0: {} + resolve-from@5.0.0: {} resolve-pkg-maps@1.0.0: {} @@ -3985,6 +4239,8 @@ snapshots: semver@7.8.1: {} + semver@7.8.6: {} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -3995,11 +4251,23 @@ snapshots: slash@3.0.0: {} + source-map@0.8.0: + optional: true + spawndamnit@3.0.1: dependencies: cross-spawn: 7.0.6 signal-exit: 4.1.0 + spdx-exceptions@2.5.0: {} + + spdx-expression-parse@5.0.0: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.24 + + spdx-license-ids@3.0.24: {} + sprintf-js@1.0.3: {} stable-hash-x@0.2.0: {} @@ -4048,6 +4316,11 @@ snapshots: dependencies: is-number: 7.0.0 + to-valid-identifier@1.0.0: + dependencies: + '@sindresorhus/base62': 1.0.0 + reserved-identifiers: 1.2.0 + tr46@0.0.3: {} ts-api-utils@2.5.0(typescript@6.0.3): From 4d2849b18f453f8d1768e7623c296403cb4ef5d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Costa?= Date: Mon, 9 Nov 2026 10:51:07 +0100 Subject: [PATCH 3/7] fix(oxlint): limit jsdoc no-types to TypeScript files In plain JS, JSDoc braces are the only type information. --- packages/oxlint-config-basic/index.mjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/oxlint-config-basic/index.mjs b/packages/oxlint-config-basic/index.mjs index 08a508a..69a68bc 100644 --- a/packages/oxlint-config-basic/index.mjs +++ b/packages/oxlint-config-basic/index.mjs @@ -108,7 +108,6 @@ export default defineConfig({ 'jsdoc/require-param-description': 'warn', 'jsdoc/require-returns-description': 'warn', 'jsdoc-js/check-param-names': 'warn', - 'jsdoc-js/no-types': 'warn', 'jsdoc-js/require-hyphen-before-param-description': 'warn', }, overrides: [ @@ -558,6 +557,8 @@ export default defineConfig({ 'typescript/restrict-plus-operands': 'error', 'typescript/restrict-template-expressions': 'error', 'typescript/unbound-method': 'off', + // TypeScript carries the types; in plain JS, JSDoc braces are the only type information. + 'jsdoc-js/no-types': 'warn', 'typescript/switch-exhaustiveness-check': [ 'warn', { considerDefaultExhaustiveForUnions: true }, From 44e4aad8ddeb4b9cb9367b78134a0c28df16bd8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Costa?= Date: Mon, 9 Nov 2026 12:05:29 +0100 Subject: [PATCH 4/7] fix(oxlint): drop import/no-cycle, dependency-cruiser covers it --- .changeset/calm-rules-warn.md | 2 +- packages/oxlint-config-basic/index.mjs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.changeset/calm-rules-warn.md b/.changeset/calm-rules-warn.md index df5f4bb..f4d584a 100644 --- a/.changeset/calm-rules-warn.md +++ b/.changeset/calm-rules-warn.md @@ -3,4 +3,4 @@ '@frontify/oxlint-config-react': minor --- -feat: warn on code-quality rules (`complexity`, `max-depth`, `max-params`, `no-else-return`, `no-nested-ternary`, `no-param-reassign`, `import/no-cycle`, `typescript/switch-exhaustiveness-check`), off in tests and stories, and on JSDoc shape rules for existing doc blocks +feat: warn on code-quality rules (`complexity`, `max-depth`, `max-params`, `no-else-return`, `no-nested-ternary`, `no-param-reassign`, `typescript/switch-exhaustiveness-check`), off in tests and stories, and on JSDoc shape rules for existing doc blocks diff --git a/packages/oxlint-config-basic/index.mjs b/packages/oxlint-config-basic/index.mjs index 69a68bc..687c56a 100644 --- a/packages/oxlint-config-basic/index.mjs +++ b/packages/oxlint-config-basic/index.mjs @@ -100,7 +100,6 @@ export default defineConfig({ 'no-else-return': 'warn', 'no-nested-ternary': 'warn', 'no-param-reassign': ['warn', { props: true }], - 'import/no-cycle': 'warn', // JSDoc rules check existing blocks only: no block is required, and a summary-only block needs no @param. 'jsdoc/check-tag-names': 'warn', 'jsdoc/no-defaults': 'warn', From 1a7ea326e0c905cad3193cdacde5ad381cc85fc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Costa?= Date: Mon, 9 Nov 2026 15:30:45 +0100 Subject: [PATCH 5/7] feat(oxlint): error on cheap-to-fix rules, keep size rules as warnings --- .changeset/calm-rules-warn.md | 2 +- packages/oxlint-config-basic/index.mjs | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.changeset/calm-rules-warn.md b/.changeset/calm-rules-warn.md index f4d584a..f132b9c 100644 --- a/.changeset/calm-rules-warn.md +++ b/.changeset/calm-rules-warn.md @@ -3,4 +3,4 @@ '@frontify/oxlint-config-react': minor --- -feat: warn on code-quality rules (`complexity`, `max-depth`, `max-params`, `no-else-return`, `no-nested-ternary`, `no-param-reassign`, `typescript/switch-exhaustiveness-check`), off in tests and stories, and on JSDoc shape rules for existing doc blocks +feat: error on `no-else-return`, `no-nested-ternary`, `typescript/switch-exhaustiveness-check` and JSDoc shape rules for existing doc blocks; warn on `complexity`, `max-depth`, `max-params` and `no-param-reassign`. The size and style rules are off in tests and stories. diff --git a/packages/oxlint-config-basic/index.mjs b/packages/oxlint-config-basic/index.mjs index 687c56a..a264772 100644 --- a/packages/oxlint-config-basic/index.mjs +++ b/packages/oxlint-config-basic/index.mjs @@ -97,17 +97,17 @@ export default defineConfig({ complexity: ['warn', { max: 10 }], 'max-depth': ['warn', { max: 3 }], 'max-params': ['warn', { max: 3 }], - 'no-else-return': 'warn', - 'no-nested-ternary': 'warn', + 'no-else-return': 'error', + 'no-nested-ternary': 'error', 'no-param-reassign': ['warn', { props: true }], // JSDoc rules check existing blocks only: no block is required, and a summary-only block needs no @param. - 'jsdoc/check-tag-names': 'warn', - 'jsdoc/no-defaults': 'warn', - 'jsdoc/require-param': ['warn', { ignoreWhenAllParamsMissing: true }], - 'jsdoc/require-param-description': 'warn', - 'jsdoc/require-returns-description': 'warn', - 'jsdoc-js/check-param-names': 'warn', - 'jsdoc-js/require-hyphen-before-param-description': 'warn', + 'jsdoc/check-tag-names': 'error', + 'jsdoc/no-defaults': 'error', + 'jsdoc/require-param': ['error', { ignoreWhenAllParamsMissing: true }], + 'jsdoc/require-param-description': 'error', + 'jsdoc/require-returns-description': 'error', + 'jsdoc-js/check-param-names': 'error', + 'jsdoc-js/require-hyphen-before-param-description': 'error', }, overrides: [ { @@ -557,9 +557,9 @@ export default defineConfig({ 'typescript/restrict-template-expressions': 'error', 'typescript/unbound-method': 'off', // TypeScript carries the types; in plain JS, JSDoc braces are the only type information. - 'jsdoc-js/no-types': 'warn', + 'jsdoc-js/no-types': 'error', 'typescript/switch-exhaustiveness-check': [ - 'warn', + 'error', { considerDefaultExhaustiveForUnions: true }, ], 'typescript/consistent-type-imports': [ From 33589a1a85405c4ab34350227d1fabfe8dd10f64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Costa?= Date: Tue, 10 Nov 2026 15:19:16 +0100 Subject: [PATCH 6/7] feat(oxlint): close review gaps in the code-quality rules Require unions to list every case, share one test glob list, apply no-empty and no-useless-catch to TypeScript, and add unicorn/no-array-reduce and eslint-comments/require-description. --- .changeset/calm-rules-warn.md | 2 +- packages/oxlint-config-basic/index.mjs | 42 +++++++------------------- 2 files changed, 12 insertions(+), 32 deletions(-) diff --git a/.changeset/calm-rules-warn.md b/.changeset/calm-rules-warn.md index f132b9c..3e329a4 100644 --- a/.changeset/calm-rules-warn.md +++ b/.changeset/calm-rules-warn.md @@ -3,4 +3,4 @@ '@frontify/oxlint-config-react': minor --- -feat: error on `no-else-return`, `no-nested-ternary`, `typescript/switch-exhaustiveness-check` and JSDoc shape rules for existing doc blocks; warn on `complexity`, `max-depth`, `max-params` and `no-param-reassign`. The size and style rules are off in tests and stories. +feat: error on `no-else-return`, `no-nested-ternary`, `unicorn/no-array-reduce`, `eslint-comments/require-description` and JSDoc shape rules for existing doc blocks, and in TypeScript files on `typescript/switch-exhaustiveness-check` (a `default` no longer counts as covering a union) and `jsdoc-js/no-types`; `no-empty` and `no-useless-catch` now apply to TypeScript files too. Warn on `complexity`, `max-depth`, `max-params` and `no-param-reassign`. `complexity`, `max-depth`, `max-params`, `no-else-return`, `no-nested-ternary` and `no-param-reassign` are off in tests and stories. diff --git a/packages/oxlint-config-basic/index.mjs b/packages/oxlint-config-basic/index.mjs index a264772..0d6816d 100644 --- a/packages/oxlint-config-basic/index.mjs +++ b/packages/oxlint-config-basic/index.mjs @@ -2,6 +2,8 @@ import { defineConfig } from 'oxlint'; +const TEST_FILES = ['**/*.test.{ts,tsx,js,jsx}', '**/*.spec.{ts,tsx,js,jsx}', '**/*.spec.{cy,ct}.{ts,tsx,js,jsx}']; + export default defineConfig({ plugins: ['typescript', 'eslint', 'promise', 'unicorn', 'import', 'jsdoc'], jsPlugins: [ @@ -53,6 +55,7 @@ export default defineConfig({ '@eslint-community/eslint-comments/no-duplicate-disable': 'error', '@eslint-community/eslint-comments/no-unlimited-disable': 'error', '@eslint-community/eslint-comments/no-unused-enable': 'error', + '@eslint-community/eslint-comments/require-description': 'error', 'typescript/await-thenable': 'error', 'typescript/ban-ts-comment': 'error', 'typescript/no-array-delete': 'error', @@ -93,7 +96,7 @@ export default defineConfig({ 'typescript/restrict-template-expressions': 'error', 'typescript/triple-slash-reference': 'error', 'typescript/unbound-method': 'error', - // Code-quality rules start as warnings; raise to error once consumers are clean. + // Size and mutation rules warn until consumers are clean; the rest are errors. complexity: ['warn', { max: 10 }], 'max-depth': ['warn', { max: 3 }], 'max-params': ['warn', { max: 3 }], @@ -131,7 +134,6 @@ export default defineConfig({ 'no-dupe-else-if': 'error', 'no-dupe-keys': 'error', 'no-duplicate-case': 'error', - 'no-empty': 'error', 'no-empty-character-class': 'error', 'no-empty-pattern': 'error', 'no-empty-static-block': 'error', @@ -164,7 +166,6 @@ export default defineConfig({ 'no-unused-labels': 'error', 'no-unused-private-class-members': 'error', 'no-useless-backreference': 'error', - 'no-useless-catch': 'error', 'no-useless-escape': 'error', 'no-with': 'error', 'preserve-caught-error': 'error', @@ -459,6 +460,8 @@ export default defineConfig({ node: true, }, rules: { + 'no-empty': 'error', + 'no-useless-catch': 'error', 'prefer-template': 'error', 'no-var': 'error', eqeqeq: 'error', @@ -511,6 +514,7 @@ export default defineConfig({ 'unicorn/throw-new-error': 'error', 'unicorn/prefer-node-protocol': 'error', 'unicorn/no-array-for-each': 'error', + 'unicorn/no-array-reduce': 'error', 'unicorn/custom-error-definition': 'error', 'unicorn/explicit-length-check': 'error', 'unicorn/no-await-expression-member': 'error', @@ -558,10 +562,7 @@ export default defineConfig({ 'typescript/unbound-method': 'off', // TypeScript carries the types; in plain JS, JSDoc braces are the only type information. 'jsdoc-js/no-types': 'error', - 'typescript/switch-exhaustiveness-check': [ - 'error', - { considerDefaultExhaustiveForUnions: true }, - ], + 'typescript/switch-exhaustiveness-check': 'error', 'typescript/consistent-type-imports': [ 'error', { prefer: 'type-imports', fixStyle: 'inline-type-imports' }, @@ -670,24 +671,7 @@ export default defineConfig({ }, }, { - files: [ - '**/*.test.ts', - '**/*.test.tsx', - '**/*.test.js', - '**/*.test.jsx', - '**/*.spec.ts', - '**/*.spec.tsx', - '**/*.spec.js', - '**/*.spec.jsx', - '**/*.spec.cy.ts', - '**/*.spec.cy.tsx', - '**/*.spec.cy.js', - '**/*.spec.cy.jsx', - '**/*.spec.ct.ts', - '**/*.spec.ct.tsx', - '**/*.spec.ct.js', - '**/*.spec.ct.jsx', - ], + files: TEST_FILES, rules: { 'no-unused-expressions': 'off', 'no-only-tests/no-only-tests': 'error', @@ -696,12 +680,8 @@ export default defineConfig({ jsPlugins: ['eslint-plugin-no-only-tests'], }, { - files: [ - '**/*.test.{ts,tsx,js,jsx}', - '**/*.spec.{ts,tsx,js,jsx}', - '**/*.spec.{cy,ct}.{ts,tsx,js,jsx}', - '**/*.stories.{ts,tsx,js,jsx}', - ], + // Tests and stories favour readable, inline fixtures over small, flat functions. + files: [...TEST_FILES, '**/*.stories.{ts,tsx,js,jsx}'], rules: { complexity: 'off', 'max-depth': 'off', From 485ef68ac8519e84fc9f8f2cea1408a9bf3fb93e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Costa?= Date: Tue, 10 Nov 2026 15:51:57 +0100 Subject: [PATCH 7/7] docs(oxlint): give a reason for each rule turned off in tests and stories --- packages/oxlint-config-basic/index.mjs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/oxlint-config-basic/index.mjs b/packages/oxlint-config-basic/index.mjs index 0d6816d..11c2519 100644 --- a/packages/oxlint-config-basic/index.mjs +++ b/packages/oxlint-config-basic/index.mjs @@ -683,11 +683,17 @@ export default defineConfig({ // Tests and stories favour readable, inline fixtures over small, flat functions. files: [...TEST_FILES, '**/*.stories.{ts,tsx,js,jsx}'], rules: { + // A test or story callback holds a whole scenario, so it branches more than production code. complexity: 'off', + // describe, it and render callbacks nest by design. 'max-depth': 'off', + // Test helpers and story renderers mirror the signature of the API they exercise. 'max-params': 'off', + // Mock implementations read as a list of cases. 'no-else-return': 'off', + // Story args pick a variant inline in one expression. 'no-nested-ternary': 'off', + // Mocks and fixtures set up the objects they are handed. 'no-param-reassign': 'off', }, },