From 4efab8d7a35d02d0256d6fa4166a97bb895163bb Mon Sep 17 00:00:00 2001 From: yyh <92089059+lyzno1@users.noreply.github.com> Date: Sun, 12 Jul 2026 22:23:39 +0800 Subject: [PATCH] chore(dify-ui): enforce strict ESLint rules (#38793) --- .github/workflows/style.yml | 4 +- packages/dify-ui/README.md | 1 + packages/dify-ui/eslint.config.mjs | 109 ++ packages/dify-ui/package.json | 8 + .../src/autocomplete/index.stories.tsx | 21 +- packages/dify-ui/src/button/index.tsx | 2 +- packages/dify-ui/src/checkbox/index.tsx | 2 +- .../dify-ui/src/collapsible/index.stories.tsx | 40 +- .../dify-ui/src/combobox/index.stories.tsx | 12 +- packages/dify-ui/src/combobox/index.tsx | 2 +- packages/dify-ui/src/dialog/index.stories.tsx | 8 +- packages/dify-ui/src/dialog/index.tsx | 2 +- packages/dify-ui/src/drawer/index.stories.tsx | 50 +- packages/dify-ui/src/drawer/index.tsx | 20 +- packages/dify-ui/src/field/index.tsx | 4 +- packages/dify-ui/src/fieldset/index.tsx | 2 +- packages/dify-ui/src/file-tree/index.tsx | 6 +- packages/dify-ui/src/input/index.stories.tsx | 16 +- packages/dify-ui/src/kbd/index.stories.tsx | 1 + packages/dify-ui/src/kbd/index.tsx | 2 +- .../src/number-field/index.stories.tsx | 4 +- .../src/pagination/__tests__/index.spec.tsx | 12 + packages/dify-ui/src/pagination/index.tsx | 23 +- .../dify-ui/src/popover/index.stories.tsx | 2 +- packages/dify-ui/src/radio/index.tsx | 4 +- .../dify-ui/src/scroll-area/index.stories.tsx | 12 +- packages/dify-ui/src/scroll-area/index.tsx | 2 +- .../dify-ui/src/segmented-control/index.tsx | 2 +- packages/dify-ui/src/switch/index.tsx | 4 +- packages/dify-ui/src/tabs/index.tsx | 2 +- .../dify-ui/src/textarea/index.stories.tsx | 12 +- packages/dify-ui/src/toast/index.tsx | 2 +- pnpm-lock.yaml | 948 ++++++++++++++++-- 33 files changed, 1155 insertions(+), 186 deletions(-) create mode 100644 packages/dify-ui/eslint.config.mjs diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index 1b3334dd274..3854d4ecd4d 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -176,9 +176,9 @@ jobs: uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: .eslintcache - key: ${{ runner.os }}-eslint-${{ hashFiles('pnpm-lock.yaml', 'eslint.config.mjs', 'web/eslint.config.mjs', 'web/eslint.constants.mjs', 'web/plugins/eslint/**') }}-${{ github.sha }} + key: ${{ runner.os }}-eslint-${{ hashFiles('pnpm-lock.yaml', 'eslint.config.mjs', 'packages/dify-ui/eslint.config.mjs', 'web/eslint.config.mjs', 'web/eslint.constants.mjs', 'web/plugins/eslint/**') }}-${{ github.sha }} restore-keys: | - ${{ runner.os }}-eslint-${{ hashFiles('pnpm-lock.yaml', 'eslint.config.mjs', 'web/eslint.config.mjs', 'web/eslint.constants.mjs', 'web/plugins/eslint/**') }}- + ${{ runner.os }}-eslint-${{ hashFiles('pnpm-lock.yaml', 'eslint.config.mjs', 'packages/dify-ui/eslint.config.mjs', 'web/eslint.config.mjs', 'web/eslint.constants.mjs', 'web/plugins/eslint/**') }}- - name: Style check if: steps.changed-files.outputs.any_changed == 'true' diff --git a/packages/dify-ui/README.md b/packages/dify-ui/README.md index 1740157c7aa..eccdb888d48 100644 --- a/packages/dify-ui/README.md +++ b/packages/dify-ui/README.md @@ -188,6 +188,7 @@ See `[web/docs/overlay.md](../../web/docs/overlay.md)` for the web app overlay b ## Development +- `vp run @langgenius/dify-ui#lint` (from the repository root) — strict ESLint checks for component source, stories, tests, and package configuration. - `pnpm -C packages/dify-ui test` — Vitest unit tests for primitives. - `pnpm -C packages/dify-ui storybook` — Storybook on the default port. Each primitive has `index.stories.tsx`. - `pnpm -C packages/dify-ui test:storybook` — Storybook component tests in Vitest browser mode. Stories without `play` are render and a11y smoke tests; stories with `play` should cover public UI contracts such as opening overlays, keyboard navigation, disabled/loading guards, form submission, and controlled state updates. diff --git a/packages/dify-ui/eslint.config.mjs b/packages/dify-ui/eslint.config.mjs new file mode 100644 index 00000000000..1a33c409edd --- /dev/null +++ b/packages/dify-ui/eslint.config.mjs @@ -0,0 +1,109 @@ +// @ts-check + +import path from 'node:path' +import antfu, { GLOB_MARKDOWN_CODE, GLOB_TS, GLOB_TSX } from '@antfu/eslint-config' +import tailwindcss from 'eslint-plugin-better-tailwindcss' +import jsxA11y from 'eslint-plugin-jsx-a11y' +import storybook from 'eslint-plugin-storybook' + +const SOURCE_FILES = [GLOB_TS, GLOB_TSX] +const TEST_FILES = ['**/__tests__/**/*.{ts,tsx}', '**/*.spec.{ts,tsx}'] + +export default antfu( + { + ignores: [ + 'coverage/', + 'dist/', + 'storybook-static/', + ], + react: { + overrides: { + 'react/exhaustive-deps': ['error', { additionalHooks: 'useIsoLayoutEffect' }], + 'react/no-context-provider': 'off', + 'react/no-unnecessary-use-prefix': 'error', + 'react/no-use-context': 'off', + 'react/rules-of-hooks': 'error', + 'react/set-state-in-effect': 'error', + 'react/set-state-in-render': 'error', + 'react/static-components': 'error', + 'react-refresh/only-export-components': 'off', + }, + }, + typescript: { + overrides: { + 'ts/consistent-type-definitions': ['error', 'type'], + 'ts/no-explicit-any': 'error', + 'ts/no-redeclare': 'off', + }, + erasableOnly: true, + }, + test: { + overrides: { + 'test/prefer-lowercase-title': 'off', + }, + }, + stylistic: { + overrides: { + 'antfu/top-level-function': 'off', + }, + }, + e18e: false, + pnpm: false, + }, + { + files: [GLOB_TSX], + ...jsxA11y.flatConfigs.recommended, + rules: { + ...jsxA11y.flatConfigs.recommended.rules, + 'jsx-a11y/anchor-has-content': 'off', + 'jsx-a11y/label-has-associated-control': 'off', + }, + }, + ...storybook.configs['flat/recommended'], + { + name: 'dify-ui/storybook', + files: ['**/.storybook/main.{js,cjs,mjs,ts}'], + rules: { + 'storybook/no-uninstalled-addons': ['error', { + packageJsonLocation: path.resolve(import.meta.dirname, 'package.json'), + }], + }, + }, + { + name: 'dify-ui/tailwind', + files: SOURCE_FILES, + ignores: [GLOB_MARKDOWN_CODE, ...TEST_FILES], + plugins: { + tailwindcss, + }, + rules: { + 'tailwindcss/enforce-consistent-class-order': 'error', + 'tailwindcss/no-duplicate-classes': 'error', + 'tailwindcss/no-deprecated-classes': 'error', + 'tailwindcss/no-unknown-classes': 'error', + 'tailwindcss/no-unnecessary-whitespace': 'error', + }, + settings: { + 'better-tailwindcss': { + cwd: import.meta.dirname, + entryPoint: path.resolve(import.meta.dirname, './.storybook/storybook.css'), + }, + }, + }, + { + files: TEST_FILES, + rules: { + 'react/purity': 'off', + }, + }, + { + rules: { + 'node/prefer-global/process': 'off', + }, + }, + { + linterOptions: { + reportUnusedDisableDirectives: 'error', + }, + }, +) diff --git a/packages/dify-ui/package.json b/packages/dify-ui/package.json index a133ab31e0b..e49c4f932e0 100644 --- a/packages/dify-ui/package.json +++ b/packages/dify-ui/package.json @@ -151,6 +151,7 @@ } }, "scripts": { + "lint": "eslint --cache --max-warnings=0 .", "storybook": "storybook dev -p 6006", "storybook:build": "storybook build", "test": "vp test --project unit", @@ -170,10 +171,12 @@ "tailwind-merge": "catalog:" }, "devDependencies": { + "@antfu/eslint-config": "catalog:", "@base-ui/react": "catalog:", "@chromatic-com/storybook": "catalog:", "@dify/tsconfig": "workspace:*", "@egoist/tailwindcss-icons": "catalog:", + "@eslint-react/eslint-plugin": "catalog:", "@iconify-json/ri": "catalog:", "@storybook/addon-a11y": "catalog:", "@storybook/addon-docs": "catalog:", @@ -192,6 +195,11 @@ "@vitest/browser-playwright": "catalog:", "@vitest/coverage-v8": "catalog:", "class-variance-authority": "catalog:", + "eslint": "catalog:", + "eslint-plugin-better-tailwindcss": "catalog:", + "eslint-plugin-jsx-a11y": "catalog:", + "eslint-plugin-react-refresh": "catalog:", + "eslint-plugin-storybook": "catalog:", "playwright": "catalog:", "react": "catalog:", "react-dom": "catalog:", diff --git a/packages/dify-ui/src/autocomplete/index.stories.tsx b/packages/dify-ui/src/autocomplete/index.stories.tsx index bf96cd1ee17..953b90de0af 100644 --- a/packages/dify-ui/src/autocomplete/index.stories.tsx +++ b/packages/dify-ui/src/autocomplete/index.stories.tsx @@ -254,7 +254,7 @@ const BasicTagAutocomplete = ({ openOnInputClick > -