From 38364370bd9225d3698c979e21b3d18cbf2da364 Mon Sep 17 00:00:00 2001 From: Fran McDade <18710366+frano-m@users.noreply.github.com> Date: Mon, 25 May 2026 17:58:29 +1000 Subject: [PATCH 1/2] chore: migrate findable-ui eslint to flat config (#926) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the legacy .eslintrc.json + .eslintignore setup with eslint.config.mjs using FlatCompat to wrap legacy plugin extends (next, storybook, sonarjs, eslint-comments). Stays on ESLint 8.57 with the `ESLINT_USE_FLAT_CONFIG=true` env var opt-in; the ESLint 9 bump and cascading plugin updates are scoped to #932 (eslint-config-next v16). The .storybook/main.ts sort-keys violation was pre-existing — legacy ESLint silently auto-ignored dot-directories. Flat config doesn't, so it surfaced and was fixed with a one-line key reorder. Co-Authored-By: Claude Opus 4.7 (1M context) --- .eslintignore | 5 --- .eslintrc.json | 79 ----------------------------------------- .storybook/main.ts | 4 +-- eslint.config.mjs | 87 +++++++++++++++++++++++++++++++++++++++++++++ package-lock.json | 88 +++++++++++++++++++++++++++++++++++++++++----- package.json | 4 ++- 6 files changed, 171 insertions(+), 96 deletions(-) delete mode 100644 .eslintignore delete mode 100644 .eslintrc.json create mode 100644 eslint.config.mjs diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 82da52d2..00000000 --- a/.eslintignore +++ /dev/null @@ -1,5 +0,0 @@ -**/node_modules/* -**/out/* -**/.next/* - -lib \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index d882dd89..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint", - "sonarjs", - "jsdoc", - "sort-destructure-keys", - "typescript-sort-keys", - "react-hooks" - ], - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "prettier", - "plugin:prettier/recommended", - "plugin:storybook/recommended", - "next", - "plugin:sonarjs/recommended", - "plugin:eslint-comments/recommended" - ], - "rules": { - "sort-keys": [ - "error", - "asc", - { - "caseSensitive": true, - "minKeys": 2, - "natural": false - } - ], - "sort-destructure-keys/sort-destructure-keys": [ - "error", - { - "caseSensitive": false - } - ], - "typescript-sort-keys/interface": [ - "error", - "asc", - { - "caseSensitive": false - } - ], - "typescript-sort-keys/string-enum": [ - "error", - "asc", - { - "caseSensitive": false - } - ], - "eslint-comments/require-description": "error", - "jsdoc/require-description": "error", - "jsdoc/require-param": "error", - "jsdoc/require-param-name": "error", - "jsdoc/require-param-description": "error", - "jsdoc/require-hyphen-before-param-description": "error", - "jsdoc/require-returns": "error", - "jsdoc/require-returns-description": "error", - "jsdoc/check-alignment": "error", - "jsdoc/check-param-names": "error", - "react-hooks/exhaustive-deps": "error" - }, - "overrides": [ - { - "files": "**", - "excludedFiles": "*.styles.ts?(x)", - "rules": { - "@typescript-eslint/explicit-function-return-type": "error" - } - }, - { - "files": "**/*.test.ts", - "rules": { - "@typescript-eslint/no-explicit-any": "off", - "sonarjs/no-duplicate-string": "off" - } - } - ] -} diff --git a/.storybook/main.ts b/.storybook/main.ts index 9e7b9ac6..84c9e48d 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -1,10 +1,10 @@ import { defineMain } from "@storybook/nextjs-vite/node"; export default defineMain({ - framework: "@storybook/nextjs-vite", - stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"], core: { builder: "@storybook/builder-vite", disableTelemetry: true, }, + framework: "@storybook/nextjs-vite", + stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"], }); diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000..a652398a --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,87 @@ +import { FlatCompat } from "@eslint/eslintrc"; +import js from "@eslint/js"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const compat = new FlatCompat({ + baseDirectory: __dirname, + recommendedConfig: js.configs.recommended, +}); + +const config = [ + { + ignores: ["**/node_modules/**", "**/out/**", "**/.next/**", "lib/**"], + }, + ...compat.config({ + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "prettier", + "plugin:prettier/recommended", + "plugin:storybook/recommended", + "next", + "plugin:sonarjs/recommended", + "plugin:eslint-comments/recommended", + ], + parser: "@typescript-eslint/parser", + plugins: [ + "@typescript-eslint", + "sonarjs", + "jsdoc", + "sort-destructure-keys", + "typescript-sort-keys", + "react-hooks", + ], + rules: { + "eslint-comments/require-description": "error", + "jsdoc/check-alignment": "error", + "jsdoc/check-param-names": "error", + "jsdoc/require-description": "error", + "jsdoc/require-hyphen-before-param-description": "error", + "jsdoc/require-param": "error", + "jsdoc/require-param-description": "error", + "jsdoc/require-param-name": "error", + "jsdoc/require-returns": "error", + "jsdoc/require-returns-description": "error", + "react-hooks/exhaustive-deps": "error", + "sort-destructure-keys/sort-destructure-keys": [ + "error", + { caseSensitive: false }, + ], + "sort-keys": [ + "error", + "asc", + { caseSensitive: true, minKeys: 2, natural: false }, + ], + "typescript-sort-keys/interface": [ + "error", + "asc", + { caseSensitive: false }, + ], + "typescript-sort-keys/string-enum": [ + "error", + "asc", + { caseSensitive: false }, + ], + }, + }), + { + files: ["**/*.{ts,tsx,js,jsx,mjs,cjs}"], + ignores: ["**/*.styles.ts", "**/*.styles.tsx"], + rules: { + "@typescript-eslint/explicit-function-return-type": "error", + }, + }, + { + files: ["**/*.test.ts"], + rules: { + "@typescript-eslint/no-explicit-any": "off", + "sonarjs/no-duplicate-string": "off", + }, + }, +]; + +export default config; diff --git a/package-lock.json b/package-lock.json index 4ddc3550..6c274830 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,8 @@ "@commitlint/cli": "^20.2.0", "@commitlint/config-conventional": "^20.2.0", "@emotion/jest": "^11.14.2", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "^8.57.1", "@next/eslint-plugin-next": "^15.5.13", "@storybook/nextjs-vite": "^10.3.3", "@testing-library/react": "^16.3.1", @@ -1629,24 +1631,24 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "^6.12.4", + "ajv": "^6.14.0", "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -1680,6 +1682,50 @@ "concat-map": "0.0.1" } }, + "node_modules/@eslint/eslintrc/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@eslint/eslintrc/node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -9121,6 +9167,30 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/eslint/node_modules/ajv": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", diff --git a/package.json b/package.json index bc44a952..5ac7b191 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "", "scripts": { "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js", - "lint": "eslint .", + "lint": "ESLINT_USE_FLAT_CONFIG=true eslint .", "check-format": "prettier --check .", "storybook": "storybook dev -p 6006", "test-compile": "tsc --noEmit", @@ -38,6 +38,8 @@ "@commitlint/cli": "^20.2.0", "@commitlint/config-conventional": "^20.2.0", "@emotion/jest": "^11.14.2", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "^8.57.1", "@next/eslint-plugin-next": "^15.5.13", "@storybook/nextjs-vite": "^10.3.3", "@testing-library/react": "^16.3.1", From 46504dec08a4995f5f25a7dabb698ecb3714e6cd Mon Sep 17 00:00:00 2001 From: Fran McDade <18710366+frano-m@users.noreply.github.com> Date: Mon, 25 May 2026 18:18:07 +1000 Subject: [PATCH 2/2] chore: extend test override to .test.tsx files (#926) The legacy override matched only **/*.test.ts, missing the 16 .test.tsx component tests under /tests/. Widen the glob so the no-explicit-any and no-duplicate-string relaxations apply consistently to all test files. Co-Authored-By: Claude Opus 4.7 (1M context) --- eslint.config.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index a652398a..68b4de26 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -76,7 +76,7 @@ const config = [ }, }, { - files: ["**/*.test.ts"], + files: ["**/*.test.{ts,tsx}"], rules: { "@typescript-eslint/no-explicit-any": "off", "sonarjs/no-duplicate-string": "off",