mirror of
https://github.com/langgenius/dify.git
synced 2026-04-05 10:12:43 +08:00
docs(web): update dev guide (#33815)
This commit is contained in:
@@ -12,25 +12,44 @@ We use ESLint and Typescript to maintain code quality and consistency across the
|
||||
pnpm eslint [options] file.js [file.js] [dir]
|
||||
```
|
||||
|
||||
**`--cache`**: Caches lint results for faster subsequent runs. Keep this enabled by default; only disable when you encounter unexpected lint results.
|
||||
**`--cache`**: Caches lint results for faster subsequent runs.
|
||||
Keep this enabled by default; only disable when you encounter unexpected lint results.
|
||||
|
||||
**`--concurrency`**: Enables multi-threaded linting. Use `--concurrency=auto` or experiment with specific numbers to find the optimal setting for your machine. Keep this enabled when linting multiple files.
|
||||
**`--concurrency`**: Enables multi-threaded linting.
|
||||
Use `--concurrency=auto` or experiment with specific numbers to find the optimal setting for your machine.
|
||||
Keep this enabled when linting multiple files.
|
||||
|
||||
- [ESLint multi-thread linting blog post](https://eslint.org/blog/2025/08/multithread-linting/)
|
||||
- [ESLint multi-thread linting blog post]
|
||||
|
||||
**`--fix`**: Automatically fixes auto-fixable rule violations. Always review the diff before committing to ensure no unintended changes.
|
||||
**`--fix`**: Automatically fixes auto-fixable rule violations.
|
||||
Keep this enabled so that you do not have to care about auto-fixable errors (e.g., formatting issues) and can focus on more important errors.
|
||||
Always review the diff before committing to ensure no unintended changes.
|
||||
|
||||
**`--quiet`**: Suppresses warnings and only shows errors. Useful when you want to reduce noise from existing issues.
|
||||
**`--quiet`**: Suppresses warnings and only shows errors.
|
||||
Useful when you want to reduce noise from existing warnings.
|
||||
|
||||
**`--suppress-all`**: Temporarily suppresses error-level violations and records them, allowing CI to pass. Treat this as an escape hatch—fix these errors when time permits.
|
||||
**`--suppress-all`**: Temporarily suppresses error-level violations and records them, allowing CI to pass.
|
||||
Treat this as an escape hatch—fix these errors when time permits.
|
||||
|
||||
**`--prune-suppressions`**: Removes outdated suppressions after you've fixed the underlying errors.
|
||||
|
||||
- [ESLint bulk suppressions blog post](https://eslint.org/blog/2025/04/introducing-bulk-suppressions/)
|
||||
- [ESLint bulk suppressions blog post]
|
||||
|
||||
### The Auto-Fix Workflow and Suppression Strategy
|
||||
|
||||
To streamline your development process, we recommend configuring your editor to automatically fix lint errors on save.
|
||||
As a fallback, any remaining autofixable errors will be corrected automatically when you commit.
|
||||
To prevent workflow disruptions, these commit hooks are intentionally bypassed when you are merging branches, rebasing, or cherry-picking.
|
||||
|
||||
Additionally, we currently track many existing legacy errors in eslint-suppressions.json.
|
||||
You do not need to spend time manually pruning these suppressions (we already append `--pass-on-unpruned-suppressions` in the commit hook);
|
||||
once you open a Pull Request, the CI pipeline will automatically handle the cleanup for you.
|
||||
|
||||
### Type-Aware Linting
|
||||
|
||||
Some ESLint rules require type information, such as [no-leaked-conditional-rendering](https://www.eslint-react.xyz/docs/rules/no-leaked-conditional-rendering). However, [typed linting via typescript-eslint](https://typescript-eslint.io/getting-started/typed-linting) is too slow for practical use, so we use [TSSLint](https://github.com/johnsoncodehk/tsslint) instead.
|
||||
Some ESLint rules require type information, such as [no-leaked-conditional-rendering].
|
||||
However, [typed linting via typescript-eslint] is too slow for practical use.
|
||||
So we use [TSSLint] instead.
|
||||
|
||||
```sh
|
||||
pnpm lint:tss
|
||||
@@ -43,7 +62,7 @@ This command lints the entire project and is intended for final verification bef
|
||||
If a new rule causes many existing code errors or automatic fixes generate too many diffs, do not use the `--fix` option for automatic fixes.
|
||||
You can introduce the rule first, then use the `--suppress-all` option to temporarily suppress these errors, and gradually fix them in subsequent changes.
|
||||
|
||||
For overlay migration policy and cleanup phases, see [Overlay Migration Guide](./overlay-migration.md).
|
||||
For overlay migration policy and cleanup phases, see [Overlay Migration Guide].
|
||||
|
||||
## Type Check
|
||||
|
||||
@@ -55,4 +74,12 @@ However, it can be useful to run the TypeScript 7 command-line (tsgo) to type ch
|
||||
pnpm type-check:tsgo
|
||||
```
|
||||
|
||||
Prefer using `tsgo` for type checking as it is significantly faster than the standard TypeScript compiler. Only fall back to `pnpm type-check` (which uses `tsc`) if you encounter unexpected results.
|
||||
Prefer using `tsgo` for type checking as it is significantly faster than the standard TypeScript compiler.
|
||||
Only fall back to `pnpm type-check` (which uses `tsc`) if you encounter unexpected results.
|
||||
|
||||
[ESLint bulk suppressions blog post]: https://eslint.org/blog/2025/04/introducing-bulk-suppressions
|
||||
[ESLint multi-thread linting blog post]: https://eslint.org/blog/2025/08/multithread-linting
|
||||
[Overlay Migration Guide]: ./overlay-migration.md
|
||||
[TSSLint]: https://github.com/johnsoncodehk/tsslint
|
||||
[no-leaked-conditional-rendering]: https://www.eslint-react.xyz/docs/rules/no-leaked-conditional-rendering
|
||||
[typed linting via typescript-eslint]: https://typescript-eslint.io/getting-started/typed-linting
|
||||
|
||||
@@ -23,7 +23,7 @@ This document tracks the migration away from legacy overlay APIs.
|
||||
- `@/app/components/base/ui/alert-dialog`
|
||||
- `@/app/components/base/ui/select`
|
||||
- `@/app/components/base/ui/toast`
|
||||
- Tracking issue: https://github.com/langgenius/dify/issues/32767
|
||||
- Tracking issue: <https://github.com/langgenius/dify/issues/32767>
|
||||
|
||||
## ESLint policy
|
||||
|
||||
@@ -72,14 +72,14 @@ All new overlay primitives in `base/ui/` share a single z-index value:
|
||||
During the migration period, legacy and new overlays coexist. Legacy overlays
|
||||
portal to `document.body` with explicit z-index values:
|
||||
|
||||
| Layer | z-index | Components |
|
||||
|-------|---------|------------|
|
||||
| Legacy Drawer | `z-[30]` | `base/drawer` |
|
||||
| Legacy Modal | `z-[60]` | `base/modal` (default) |
|
||||
| Legacy PortalToFollowElem callers | up to `z-[1001]` | various business components |
|
||||
| **New UI primitives** | **`z-[1002]`** | `base/ui/*` (Popover, Dialog, Tooltip, etc.) |
|
||||
| Legacy Modal (highPriority) | `z-[1100]` | `base/modal` (`highPriority={true}`) |
|
||||
| Toast (legacy + new) | `z-[1101]` | `base/toast`, `base/ui/toast` |
|
||||
| Layer | z-index | Components |
|
||||
| --------------------------------- | ---------------- | -------------------------------------------- |
|
||||
| Legacy Drawer | `z-[30]` | `base/drawer` |
|
||||
| Legacy Modal | `z-[60]` | `base/modal` (default) |
|
||||
| Legacy PortalToFollowElem callers | up to `z-[1001]` | various business components |
|
||||
| **New UI primitives** | **`z-[1002]`** | `base/ui/*` (Popover, Dialog, Tooltip, etc.) |
|
||||
| Legacy Modal (highPriority) | `z-[1100]` | `base/modal` (`highPriority={true}`) |
|
||||
| Toast (legacy + new) | `z-[1101]` | `base/toast`, `base/ui/toast` |
|
||||
|
||||
`z-[1002]` sits above all common legacy overlays, so new primitives always
|
||||
render on top without needing per-call-site z-index hacks. Among themselves,
|
||||
|
||||
@@ -119,13 +119,11 @@ When assigned to test a **directory/path** (not just a single file), follow thes
|
||||
Choose based on directory complexity:
|
||||
|
||||
1. **Single spec file (Integration approach)** - Preferred for related components
|
||||
|
||||
- Minimize mocking - use real project components
|
||||
- Test actual integration between components
|
||||
- Only mock: API calls, complex context providers, third-party libs
|
||||
|
||||
1. **Multiple spec files (Unit approach)** - For complex directories
|
||||
|
||||
- One spec file per component/hook/utility
|
||||
- More isolated testing
|
||||
- Useful when components are independent
|
||||
@@ -139,7 +137,7 @@ When using a single spec file:
|
||||
- ❌ **DO NOT mock** base components (`@/app/components/base/*`)
|
||||
- ❌ **DO NOT mock** sibling/child components in the same directory
|
||||
|
||||
> See [Example Structure](#example-structure) for correct import/mock patterns.
|
||||
> See [Example Structure] for correct import/mock patterns.
|
||||
|
||||
## Testing Components with Dedicated Dependencies
|
||||
|
||||
@@ -185,8 +183,8 @@ Treat component state as part of the public behavior: confirm the initial render
|
||||
- ✅ When creating lightweight provider stubs, mirror the real default values and surface helper builders (for example `createMockWorkflowContext`).
|
||||
- ✅ Reset shared stores (React context, Zustand, TanStack Query cache) between tests to avoid leaking state. Prefer helper factory functions over module-level singletons in specs.
|
||||
- ✅ For hooks that read from context, use `renderHook` with a custom wrapper that supplies required providers.
|
||||
- ✅ **Use factory functions for mock data**: Import actual types and create factory functions with complete defaults (see [Test Data Builders](#9-test-data-builders-anti-hardcoding) section).
|
||||
- ✅ If it's need to mock some common context provider used across many components (for example, `ProviderContext`), put it in __mocks__/context(for example, `__mocks__/context/provider-context`). To dynamically control the mock behavior (for example, toggling plan type), use module-level variables to track state and change them(for example, `context/provider-context-mock.spec.tsx`).
|
||||
- ✅ **Use factory functions for mock data**: Import actual types and create factory functions with complete defaults (see [Test Data Builders] section).
|
||||
- ✅ If it's need to mock some common context provider used across many components (for example, `ProviderContext`), put it in **mocks**/context(for example, `__mocks__/context/provider-context`). To dynamically control the mock behavior (for example, toggling plan type), use module-level variables to track state and change them(for example, `context/provider-context-mock.spec.tsx`).
|
||||
- ✅ Use factory functions to create mock data with TypeScript types. This ensures type safety and makes tests more maintainable.
|
||||
|
||||
**Rules**:
|
||||
@@ -363,7 +361,6 @@ describe('ComponentName', () => {
|
||||
1. **i18n**: Uses global mock in `web/vitest.setup.ts` (auto-loaded by Vitest setup)
|
||||
|
||||
The global mock provides:
|
||||
|
||||
- `useTranslation` - returns translation keys with namespace prefix
|
||||
- `Trans` component - renders i18nKey and components
|
||||
- `useMixedTranslation` (from `@/app/components/plugins/marketplace/hooks`)
|
||||
@@ -533,16 +530,25 @@ const element = await screen.findByText('Async Content')
|
||||
|
||||
Test examples in the project:
|
||||
|
||||
- [classnames.spec.ts](../utils/classnames.spec.ts) - Utility function tests
|
||||
- [index.spec.tsx](../app/components/base/button/index.spec.tsx) - Component tests
|
||||
- [classnames.spec.ts] - Utility function tests
|
||||
- [index.spec.tsx] - Component tests
|
||||
|
||||
## Resources
|
||||
|
||||
- [Vitest Documentation](https://vitest.dev/guide/)
|
||||
- [React Testing Library Documentation](https://testing-library.com/docs/react-testing-library/intro/)
|
||||
- [Testing Library Best Practices](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library)
|
||||
- [Vitest Mocking Guide](https://vitest.dev/guide/mocking.html)
|
||||
- [Vitest Documentation]
|
||||
- [React Testing Library Documentation]
|
||||
- [Testing Library Best Practices]
|
||||
- [Vitest Mocking Guide]
|
||||
|
||||
______________________________________________________________________
|
||||
---
|
||||
|
||||
**Remember**: Writing tests is not just about coverage, but ensuring code quality and maintainability. Good tests should be clear, concise, and meaningful.
|
||||
|
||||
[Example Structure]: #example-structure
|
||||
[React Testing Library Documentation]: https://testing-library.com/docs/react-testing-library/intro
|
||||
[Test Data Builders]: #9-test-data-builders-anti-hardcoding
|
||||
[Testing Library Best Practices]: https://kentcdodds.com/blog/common-mistakes-with-react-testing-library
|
||||
[Vitest Documentation]: https://vitest.dev/guide
|
||||
[Vitest Mocking Guide]: https://vitest.dev/guide/mocking.html
|
||||
[classnames.spec.ts]: ../utils/classnames.spec.ts
|
||||
[index.spec.tsx]: ../app/components/base/button/index.spec.tsx
|
||||
|
||||
Reference in New Issue
Block a user