diff --git a/.agents/skills/frontend-testing/references/checklist.md b/.agents/skills/frontend-testing/references/checklist.md index 519c3f166fa..530a4e43ad5 100644 --- a/.agents/skills/frontend-testing/references/checklist.md +++ b/.agents/skills/frontend-testing/references/checklist.md @@ -126,8 +126,8 @@ For the current file being tested: - [ ] Run full directory test: `pnpm test path/to/directory/` - [ ] Check coverage report: `pnpm test:coverage` -- [ ] Run `pnpm lint:fix` on all test files -- [ ] Run `pnpm type-check` +- [ ] Run `pnpm check:fix` +- [ ] Run `pnpm check` ## Common Issues to Watch diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml index 8afb89ad432..e1f6fbe7c46 100644 --- a/.github/workflows/autofix.yml +++ b/.github/workflows/autofix.yml @@ -169,19 +169,13 @@ jobs: if: github.event_name != 'merge_group' && steps.frontend-contract-changes.outputs.any_changed == 'true' run: pnpm --dir packages/contracts gen-api-contract - - name: Oxlint autofix - if: github.event_name != 'merge_group' && steps.web-changes.outputs.any_changed == 'true' - run: | - pnpm lint:oxlint:fix --prune-suppressions --quiet || true - - name: ESLint fallback autofix if: github.event_name != 'merge_group' && steps.web-changes.outputs.any_changed == 'true' - run: | - pnpm lint:eslint:fix --quiet || true + run: pnpm lint:eslint:fix --quiet || true - - name: Format frontend files + - name: Vite+ static autofix if: github.event_name != 'merge_group' && (steps.web-changes.outputs.any_changed == 'true' || steps.frontend-contract-changes.outputs.any_changed == 'true') - run: vp fmt + run: vp check --fix || true - if: github.event_name != 'merge_group' uses: autofix-ci/action@c5b2d67aa2274e7b5a18224e8171550871fc7e4a # v1.3.4 diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index 2e495235785..ec7f5779083 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -137,7 +137,7 @@ jobs: ts-common-style: name: TS Common - runs-on: depot-ubuntu-24.04 + runs-on: depot-ubuntu-24.04-4 permissions: checks: write pull-requests: read @@ -176,17 +176,9 @@ jobs: if: steps.changed-files.outputs.any_changed == 'true' uses: ./.github/actions/setup-web - - name: Format check + - name: Static check if: steps.changed-files.outputs.any_changed == 'true' - run: vp fmt --check - - - name: Style check - if: steps.changed-files.outputs.any_changed == 'true' - run: pnpm -w lint:ci - - - name: Type check - if: steps.changed-files.outputs.any_changed == 'true' - run: pnpm -w type-check + run: pnpm -w check superlinter: name: SuperLinter diff --git a/AGENTS.md b/AGENTS.md index 5815b7cf76c..0da1d9e9eb6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -31,7 +31,7 @@ The codebase is split into: ## Language Style - **Python**: Keep type hints on functions and attributes, and implement relevant special methods (e.g., `__repr__`, `__str__`). Prefer `TypedDict` over `dict` or `Mapping` for type safety and better code documentation. -- **TypeScript**: Use the strict config, format with `vp fmt`, run `pnpm lint` for Oxlint code checks plus ESLint non-code checks, run `pnpm type-check`, and avoid `any` types. +- **TypeScript**: Use the strict config, run `pnpm check` for formatting, Oxlint, ESLint non-code checks, and type checking, and avoid `any` types. ## General Practices diff --git a/cli/AGENTS.md b/cli/AGENTS.md index 99f4f24922e..56a59128c03 100644 --- a/cli/AGENTS.md +++ b/cli/AGENTS.md @@ -57,10 +57,8 @@ pnpm install # one-time pnpm dev [args...] # run CLI from source (no -- separator) pnpm test # vitest pnpm test:coverage # with coverage -pnpm -w type-check # repository-wide type check -pnpm -w lint # repository-wide lint -pnpm -w lint:fix # repository-wide lint fixes -vp fmt # format with Oxfmt +pnpm -w check # repository-wide static check +pnpm -w check:fix # repository-wide static fixes pnpm build # production bundle (vp pack) pnpm tree:gen # regenerate src/commands/tree.ts (registry) pnpm tree:check # verify tree.ts is up-to-date with the fs @@ -72,7 +70,7 @@ Release binaries (5 platform targets, Bun-compiled) are produced by `pnpm build: - Behavior tests run against real Hono mock at `test/fixtures/dify-mock/`. No `nock`, `msw`, or `fetchMock` — every test exercises real HTTP. - Test files co-located: `foo.test.ts` next to `foo.ts`. -- Repository-wide type-check and lint, plus the full test suite, must be green before any commit. +- The repository-wide static check and full test suite must be green before any commit. ## Spec docs (`docs/specs/`) diff --git a/cli/ARD.md b/cli/ARD.md index 9c4d1cc17fc..d90c574d8e1 100644 --- a/cli/ARD.md +++ b/cli/ARD.md @@ -306,10 +306,8 @@ expect(JSON.parse(out).workspaces).toHaveLength(2) | `pnpm dev [args]` | Run CLI from source during dev | | `pnpm test` | Full vitest suite — run before every commit | | `pnpm test:coverage` | Coverage report | -| `pnpm -w type-check` | Repository-wide TypeScript type check | -| `pnpm -w lint` | Repository-wide Oxlint and ESLint checks | -| `pnpm -w lint:fix` | Repository-wide Oxlint and ESLint fixes | -| `vp fmt` | Oxfmt formatting and import sorting | +| `pnpm -w check` | Repository-wide static check | +| `pnpm -w check:fix` | Repository-wide static fixes | | `pnpm build` | Production bundle (`vp pack`) | | `pnpm tree:gen` | Regenerate `src/commands/tree.ts` (registry) | | `pnpm tree:check` | Verify `tree.ts` matches the filesystem | @@ -333,7 +331,7 @@ The repository runs Vite+ Oxlint as the primary code-quality linter, an explicit | `unicorn/no-new-array` | Use `Array.from({ length: n })` not `new Array(n)` | | `noUncheckedIndexedAccess` (tsc) | `arr[i]` is `T \| undefined`; guard before use | -Run `pnpm -w lint:fix` for Oxlint and ESLint fixes, then `vp fmt` so Oxfmt produces the final layout. +Run `pnpm -w check:fix` for Oxlint, ESLint, TypeScript, and Oxfmt fixes and diagnostics. --- diff --git a/eslint.config.mjs b/eslint.config.mjs index 76a3f5c015c..0bd17450e00 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -12,6 +12,7 @@ import { defineConfig, globalIgnores } from 'eslint/config' import dify from './web/plugins/eslint/index.js' const codeFiles = '**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}' +const mdPlugin = /** @type {import('eslint').ESLint.Plugin} */ (/** @type {unknown} */ (md)) /** * Migration tradeoff: ESLint is intentionally restricted to non-code files. @@ -385,7 +386,7 @@ export default defineConfig([ plugins: { markdown, 'markdown-preferences': markdownPreferences, - md, + md: mdPlugin, }, rules: { 'markdown/fenced-code-language': 'off', diff --git a/lint.config.ts b/lint.config.ts index 74f3c100a83..bd70c6f5156 100644 --- a/lint.config.ts +++ b/lint.config.ts @@ -91,7 +91,7 @@ export const lintConfig = { reportUnusedDisableDirectives: 'warn', respectEslintDisableDirectives: false, typeAware: true, - typeCheck: false, + typeCheck: true, }, settings: { 'better-tailwindcss': { diff --git a/package.json b/package.json index 3c99225318c..f3a537aaaef 100644 --- a/package.json +++ b/package.json @@ -3,15 +3,10 @@ "private": true, "type": "module", "scripts": { + "check": "vp check && pnpm lint:eslint", + "check:fix": "pnpm lint:eslint:fix && vp check --fix", "dev": "concurrently -k -n vinext,proxy \"vp run dify-web#dev:vinext\" \"vp run dify-web#dev:proxy\"", - "fmt": "vp fmt", - "fmt:check": "vp fmt --check", "prepare": "vp config", - "type-check": "vp run -r type-check", - "lint": "pnpm lint:oxlint && pnpm lint:eslint", - "lint:ci": "pnpm lint:oxlint && pnpm lint:eslint", - "lint:fix": "pnpm lint:oxlint:fix && pnpm lint:eslint:fix", - "lint:quiet": "pnpm lint:oxlint:quiet && pnpm lint:eslint:quiet", "lint:oxlint": "vp lint", "lint:oxlint:fix": "vp lint --fix", "lint:oxlint:quiet": "vp lint --quiet", diff --git a/packages/dify-ui/src/styles.d.ts b/packages/dify-ui/src/styles.d.ts new file mode 100644 index 00000000000..5894ae0b6e9 --- /dev/null +++ b/packages/dify-ui/src/styles.d.ts @@ -0,0 +1 @@ +declare module '*.css' diff --git a/packages/dify-ui/tsconfig.json b/packages/dify-ui/tsconfig.json index 55080c646c8..f6a4a896843 100644 --- a/packages/dify-ui/tsconfig.json +++ b/packages/dify-ui/tsconfig.json @@ -3,6 +3,13 @@ "compilerOptions": { "types": ["vite-plus/test/globals", "vite-plus/test/matchers"] }, - "include": ["src/**/*.ts", "src/**/*.tsx", "vite.config.ts"], + "include": [ + ".storybook/**/*.ts", + ".storybook/**/*.tsx", + "src/**/*.ts", + "src/**/*.tsx", + "vite.config.ts", + "vitest.config.ts" + ], "exclude": ["node_modules", "dist", "storybook-static", "coverage"] } diff --git a/vite.config.ts b/vite.config.ts index 5a0e2a8a373..6baf45aa69e 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -4,10 +4,9 @@ import { lintConfig } from './lint.config' const lintFiles = '*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}' const eslintFiles = '*.{json,jsonc,json5,md,yml,yaml,toml}' const formatOnlyFiles = '*.{mdx,css,scss,less,html,vue,svelte,gql,graphql,hbs,handlebars}' -const lintFix = 'vp lint --fix --no-error-on-unmatched-pattern' +const checkFix = 'vp check --fix --no-error-on-unmatched-pattern' const eslintFix = 'eslint --fix --pass-on-unpruned-suppressions --no-error-on-unmatched-pattern --no-warn-ignored' -const format = 'vp fmt --no-error-on-unmatched-pattern' const nonFrontendIgnores = [ '.agents/**', @@ -47,9 +46,9 @@ const formatterUnstableInputs = ['web/app/components/develop/template/*.mdx'] export default defineConfig({ lint: lintConfig, staged: { - [lintFiles]: [lintFix, format], - [eslintFiles]: [eslintFix, format], - [formatOnlyFiles]: format, + [lintFiles]: checkFix, + [eslintFiles]: [eslintFix, checkFix], + [formatOnlyFiles]: checkFix, }, fmt: { ignorePatterns: [...nonFrontendIgnores, ...generatedIgnores, ...formatterUnstableInputs], diff --git a/web/.storybook/preview.tsx b/web/.storybook/preview.tsx index e1216869115..b9da6f7c6af 100644 --- a/web/.storybook/preview.tsx +++ b/web/.storybook/preview.tsx @@ -1,4 +1,4 @@ -import type { Preview } from '@storybook/react' +import type { Decorator, Preview } from '@storybook/react' import type { Resource } from 'i18next' import { ToastHost } from '@langgenius/dify-ui/toast' import { withThemeByDataAttribute } from '@storybook/addon-themes' @@ -24,7 +24,7 @@ const storyResources: Resource = { }, } -export const decorators = [ +export const decorators: Decorator[] = [ withThemeByDataAttribute({ themes: { light: 'light', diff --git a/web/app/components/datasets/documents/components/document-list/hooks/__tests__/use-document-actions.spec.tsx b/web/app/components/datasets/documents/components/document-list/hooks/__tests__/use-document-actions-with-query-client.spec.tsx similarity index 99% rename from web/app/components/datasets/documents/components/document-list/hooks/__tests__/use-document-actions.spec.tsx rename to web/app/components/datasets/documents/components/document-list/hooks/__tests__/use-document-actions-with-query-client.spec.tsx index 641ed4f8722..9ae1115ad74 100644 --- a/web/app/components/datasets/documents/components/document-list/hooks/__tests__/use-document-actions.spec.tsx +++ b/web/app/components/datasets/documents/components/document-list/hooks/__tests__/use-document-actions-with-query-client.spec.tsx @@ -37,7 +37,6 @@ describe('useDocumentActions', () => { beforeEach(() => { vi.clearAllMocks() - // Setup all mocks with default values const createMockMutation = () => ({ mutateAsync: mockMutateAsync, isPending: false, diff --git a/web/docs/lint.md b/web/docs/lint.md index 57097b4302c..ae0c1ce3ebd 100644 --- a/web/docs/lint.md +++ b/web/docs/lint.md @@ -1,50 +1,38 @@ -# Format and Lint Guide +# Static Check Guide -We use Vite+ Oxfmt for formatting, Vite+ Oxlint for code-quality linting, ESLint for non-code file types that Oxlint cannot parse, and TypeScript for type safety. +Vite+ provides the primary static check through `vp check`, which combines Oxfmt formatting, Oxlint code-quality rules, and TypeScript diagnostics. The root command also runs ESLint for non-code file types that Oxlint cannot parse. -## Format +## Check -Format the supported frontend and TypeScript workspace files from the repository root: +Run the complete repository check from the root: ```sh -vp fmt +pnpm check ``` -Check formatting without writing changes: +Apply safe fixes before running the same checks: ```sh -vp fmt --check +pnpm check:fix ``` -The shared formatter options and ignore boundaries live in the root `vite.config.ts`. -Editor format-on-save must point Oxc at that file so local edits match the commit hook and CI. +CI and local development use the same root `vite.config.ts` configuration. -## Lint - -Run the repository lint from the root: +For a smaller code scope, pass paths directly to Vite+: ```sh -pnpm lint +vp check web/app/components packages/dify-ui/src/button +vp check --fix web/app/components packages/dify-ui/src/button ``` -Run the two lint paths separately when targeting a smaller scope: +Run the ESLint fallback separately when targeting JSON, JSONC, JSON5, YAML, TOML, or Markdown: ```sh -pnpm lint:oxlint web/app/components packages/dify-ui/src/button pnpm lint:eslint package.json pnpm-workspace.yaml web/docs -``` - -Apply safe fixes, then format the result: - -```sh -pnpm lint:oxlint:fix web/app/components pnpm lint:eslint:fix package.json pnpm-workspace.yaml web/docs -vp fmt web/app/components ``` -Use `pnpm lint:quiet` to hide warnings from both linters. Oxlint runs in parallel by default, and the root ESLint scripts enable automatic concurrency. - -The default and CI lint commands do not pass file lists on the command line. Oxlint's repository scope is defined by `lint.config.ts` `ignorePatterns`, and ESLint's scope is defined by `eslint.config.mjs` global ignores. The individual linter commands above accept explicit paths for ad hoc local subsets. +Oxlint and Vite+ type-check scope is defined by `lint.config.ts` `ignorePatterns`, and ESLint's scope is defined by `eslint.config.mjs` global ignores. The primary rule baseline lives in `lint.config.ts` and is connected through the root `vite.config.ts` `lint` block. Oxlint-native rules are preferred, and compatible ESLint rules can run through Oxlint's `jsPlugins` support. The rules are explicit snapshots of the ESLint configurations that were active at migration time. Do not import an upstream preset wholesale: enable a new rule intentionally and review its existing violations first. @@ -52,13 +40,13 @@ The non-code baseline and its repository-wide file scope live in `eslint.config. ### Auto-fix Workflow -Configure the Oxc and ESLint editor extensions to apply their respective fixes on save. The commit hook runs `vp staged`: JavaScript and TypeScript pass through Oxlint and Oxfmt, while data and Markdown files pass through ESLint and Oxfmt. The autofix workflow also prunes resolved bulk suppressions. +Configure the Oxc and ESLint editor extensions to apply their respective fixes on save. The commit hook runs `vp staged`, which delegates staged files to `vp check --fix` and adds the ESLint fallback for non-code files. The autofix workflow uses the same combined Vite+ check. Always review automatic fixes before committing. JS plugins are allowed to provide fixes, and their behavior is not necessarily identical to a native Oxlint rule. ### Type-aware Linting -The root configuration enables `typeAware` without enabling Oxlint's full `typeCheck` diagnostics. This preserves the Node SDK's existing type-aware lint rules without making lint duplicate the repository type-check task. +The root configuration enables both `typeAware` and `typeCheck`, so `vp check` runs type-aware rules and full TypeScript diagnostics through the TypeScript Go toolchain. The web package still runs its existing TSSLint rule separately: @@ -66,10 +54,10 @@ The web package still runs its existing TSSLint rule separately: pnpm --dir web lint:tss ``` -Run the regular type check before committing or pushing: +Run the complete static check before committing or pushing: ```sh -pnpm type-check +pnpm check ``` ### Bulk Suppressions @@ -99,8 +87,8 @@ ESLint is intentionally limited to non-code files. The remaining limitations and | Area | Current status | | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Code-only fallback rules | ESLint globally ignores all code files. Six core fallback rules, JS `dot-notation`, and other code-only ESLint checks are listed only in comments rather than executable configuration. | -| Declaration files | Oxlint excludes declaration files and ESLint no longer processes code. The former 223-rule declaration snapshot and CLI declaration import restriction are not enforced; TypeScript still checks these files. | -| Generated contracts | Both linters ignore `packages/contracts/**`; Oxfmt remains the only staged quality step for the contracts package. | +| Declaration files | Oxlint excludes declaration files and ESLint no longer processes code. The former 223-rule declaration snapshot and CLI declaration import restriction are not enforced. | +| Generated contracts | Both linters and Vite+ type checking ignore `packages/contracts/**`; Oxfmt remains the only staged quality step for the contracts package. | | Non-JavaScript formats | Oxlint plugins cannot provide custom parsers or file languages. ESLint covers JSON, JSONC, YAML, TOML, and Markdown semantic rules, while Oxfmt remains responsible for their formatting. | | Markdown code blocks | ESLint validates the Markdown document, but fenced JavaScript and TypeScript blocks are not passed through the former overlapping preset. This remains deferred rather than duplicating the Oxlint rule set. | | Override-scoped settings | The three Dify UI Tailwind rules are disabled with the rest of ESLint's code path. Oxlint still applies the web `react-x.additionalStateHooks` setting globally because it cannot scope settings to an override. | @@ -114,14 +102,14 @@ Prefer a native Oxlint rule. If none exists, verify that the rule works through For overlay import policy and composition rules, see [Overlay Guide]. -## Type Check +## Type Checking You should be able to see suggestions from TypeScript in your editor for all open files. -Run the repository type check from the root: +Type checking is part of the repository check: ```sh -pnpm type-check +pnpm check ``` Type checking is powered by [`tsgo`] (the native TypeScript 7 compiler), which is significantly faster than `tsc`. diff --git a/web/package.json b/web/package.json index ec2048469b7..cc984181603 100644 --- a/web/package.json +++ b/web/package.json @@ -18,7 +18,7 @@ "dev:proxy": "dev-proxy --config ./dev-proxy.config.ts --env-file ./.env.local", "dev:vinext": "vinext dev", "gen-doc-paths": "tsx ./scripts/gen-doc-paths.ts && vp fmt types/doc-paths.ts", - "gen-icons": "pnpm --filter @dify/iconify-collections generate && node ./scripts/gen-icons.mjs && pnpm -w lint:oxlint:fix -- web/app/components/base/icons/src/ && vp fmt app/components/base/icons/src/", + "gen-icons": "pnpm --filter @dify/iconify-collections generate && node ./scripts/gen-icons.mjs && pnpm -w exec vp check --fix web/app/components/base/icons/src/", "i18n:check": "tsx ./scripts/check-i18n.js", "i18n:migrate-selectors": "tsx ./scripts/migrate-i18n-selectors.ts", "i18n:prune-unused": "tsx ./scripts/prune-unused-i18n.ts", diff --git a/web/tsconfig.json b/web/tsconfig.json index ecf46952970..9b525484b11 100644 --- a/web/tsconfig.json +++ b/web/tsconfig.json @@ -13,6 +13,8 @@ }, "include": [ "next-env.d.ts", + ".storybook/**/*.ts", + ".storybook/**/*.tsx", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", diff --git a/web/types/assets.d.ts b/web/types/assets.d.ts index d7711f7eb45..6afed58b48d 100644 --- a/web/types/assets.d.ts +++ b/web/types/assets.d.ts @@ -1,3 +1,5 @@ +declare module '*.css' + declare module '*.svg' { const value: any export default value