Dify 前端静态检查体系:基于 Vite+ 的 vp check、Oxlint、类型感知 Lint 与 ESLint 非代码回退
2026/9/7 5:57:39 网站建设 项目流程

Dify 前端静态检查体系:基于 Vite+ 的 vp check、Oxlint、类型感知 Lint 与 ESLint 非代码回退

【免费下载链接】difyBuild Agentic workflows, RAG pipelines, with rich AI model and tool support on one collaborative workspace. Deploy on cloud, VPC, or self-hosted, so teams move from prototype to production without rebuilding the stack.项目地址: https://gitcode.com/GitHub_Trending/di/dify

Dify 仓库的前端静态检查由 Vite+ 的vp check命令统一驱动,它把 Oxfmt 格式化、Oxlint 代码质量规则和 TypeScript 7 原生编译器诊断合并在同一条流水线中,根命令再追加 ESLint 对非代码文件的兜底检查。本文围绕 静态检查指南 展开,结合 lint.config.ts、vite.config.ts、eslint.config.mjs 等真实配置源码,讲清每条pnpm命令背后的作用域划分、规则基线、自动修复工作流与迁移取舍,帮助你在贡献 Dify 前端代码时建立与 CI 完全一致的本地检查能力。

核心命令:全仓库检查与自动修复

在仓库根目录运行完整静态检查:

pnpm check

在运行同样检查前先应用安全修复:

pnpm check:fix

CI 与本地开发共用同一份根 vite.config.ts 配置,保证两侧检查结果一致。这两个脚本在根 package.json 中的实际定义为:

{ "check": "vp check && pnpm lint:eslint", "check:fix": "pnpm lint:eslint:fix && vp check --fix", "lint:oxlint": "vp lint", "lint:eslint": "eslint --concurrency=auto", "lint:eslint:fix": "eslint --fix --concurrency=auto" }

从源码结构看,pnpm check是一个两段式命令:vp check负责代码文件(JS/JSX/TS/TSX 等)的格式化、Oxlint 规则与类型诊断,随后pnpm lint:eslint对非代码文件做兜底校验。check:fix则先修复 ESLint 可处理的非代码问题,再执行vp check --fix,把可自动修复的问题一次性处理掉。

缩小检查范围:路径参数与类型检查的作用域

格式化和 lint 可以传入具体路径来缩小范围,但类型检查始终是仓库级别的:

vp check web/app/components packages/dify-ui/src/button vp check --fix web/app/components packages/dify-ui/src/button

这意味着即使你只改了一个组件目录,vp check仍会完整跑一遍全仓库的类型检查。这一设计避免"局部修改破坏全局类型契约"的问题,但也要接受全量类型检查带来的耗时。

页面级无障碍诊断:lint:a11y 与 --deps 依赖模式

针对 Web 包的 JSX 无障碍规则,可以用lint:a11y对选定文件或目录单独执行。包含括号等 shell 元字符的路径要加引号:

pnpm --dir web lint:a11y 'app/(commonLayout)/app/(appDetailLayout)/layout.tsx'

使用依赖模式可以解析入口文件的传递性本地导入(包括路径别名、re-export 和动态 import),再对得到的 JSX/TSX 文件做 lint:

pnpm --dir web lint:a11y --deps 'app/(commonLayout)/app/(appDetailLayout)/layout.tsx'

这条命令在 web/package.json 中映射到node ./scripts/lint-a11y.mjs。查看 web/scripts/lint-a11y.mjs 的实现可以确认其工作原理:

  • 脚本从vite-plus中定位捆绑的oxlint包,取其bin/oxlint可执行文件;
  • 参数解析区分--deps标志与目标路径,web前缀的相对路径会解析到仓库根目录,其余相对路径解析到web/目录;
  • --deps模式下调用 TypeScript 的resolveModuleName,基于web/tsconfig.json的编译器选项(含路径别名)递归解析入口文件的导入,跳过外部库导入与.d.ts声明文件,过滤出.jsx/.tsx源文件;
  • 最终以oxlint -c web/scripts/a11y/oxlint.config.ts <files>启动检查,工作目录为web/

而 web/scripts/a11y/oxlint.config.ts 直接复用主基线导出的webJsxA11yRules

import { webJsxA11yRules } from '../../../lint.config.ts' export default { categories: { correctness: 'off' }, plugins: ['jsx-a11y'], rules: webJsxA11yRules, }

这正是文档强调"页面级诊断是本地工具,仓库级无障碍规则基线仍由lint.config.ts持有,并被常规vp check强制执行"的原因:页面脚本只是把 lint.config.ts 中导出的同一份webJsxA11yRules(覆盖alt-textaria-*no-autofocusno-static-element-interactions等 30 余条jsx-a11y规则)单独拿出来按页运行,两份配置天然不会漂移。

非代码文件的 ESLint 回退:JSON、YAML、TOML、Markdown

Oxlint 插件无法提供自定义 parser 和文件语言,因此 JSON、JSONC、JSON5、YAML、TOML、Markdown 交由 ESLint 处理。单独运行回退检查:

pnpm lint:eslint package.json pnpm-workspace.yaml web/docs pnpm lint:eslint:fix package.json pnpm-workspace.yaml web/docs

eslint.config.mjs 展示了这套回退配置的完整形态:

  • 项目作用域globalIgnores先排除一切文件,再重新放行cli/e2e/packages/sdks/nodejs-client/web/及根目录package.jsonpnpm-workspace.yamleslint.config.mjslint.config.tsvite.config.ts
  • 代码文件全局忽略**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}被整体 ignore,注释写明"代码文件仅由 Oxlint 处理",这是迁移取舍的核心边界;
  • 按文件类型挂载语言插件:JSON/JSON5/JSONC 用jsonc/x语言与eslint-plugin-jsonc,YAML 用yml/yaml,TOML 用toml/toml,Markdown 用markdown/gfm配合markdown-preferencesmd插件;
  • 有业务语义的规则tsconfig文件的compilerOptions键序强制按 eslint.config.mjs 中 120 余项固定顺序排序;pnpm-workspace.yamleslint-plugin-pnpm强制shellEmulator: truetrustPolicy: 'no-downgrade'并校验 catalog;web/i18n/**/*.json挂载本地dify插件,校验 i18n 占位符一致性、扁平 key 与多余 key。

规则基线:lint.config.ts 与 vite.config.ts 的 lint 块

主要规则基线位于 lint.config.ts,通过根 vite.config.ts 的lint字段接入:

export default defineConfig({ lint: lintConfig, // ... })

从 lint.config.ts 的主配置可以看到几个关键设计点:

  1. 显式规则快照:配置注释明确说明这是"迁移前生效的 ESLint 配置的 Oxlint 等价物",规则逐条显式声明,避免上游 preset 的变更静默改变 lint 基线。策略是优先使用 Oxlint 原生规则,兼容的 ESLint 规则通过 Oxlint 的jsPlugins机制运行。文件头部注释强调:不要整体导入上游 preset,新规则要有意启用并先审查存量违规。
  2. 插件分层plugins声明原生 Oxlint 插件(importjsdocjsx-a11ynodereacttypescriptunicornvitest);jsPlugins按规则命名空间排序加载 JS 插件,包括@tanstack/eslint-plugin-queryeslint-plugin-antfueslint-plugin-perfectionisteslint-plugin-regexp等,并注册了本地插件dify(指向./web/plugins/eslint/index.js,实现dify/prefer-tailwind-icons这类项目专属规则)。
  3. 关键 options(lint.config.ts):
options: { reportUnusedDisableDirectives: 'warn', respectEslintDisableDirectives: false, typeAware: true, typeCheck: true, },

其中respectEslintDisableDirectives: false是文档特别强调的一点:ESLint 注释无法掩盖 Oxlint 的诊断。因此抑制注释必须各归各主——lint.config.ts中的代码规则用oxlint-disableeslint.config.mjs中的非代码规则才用eslint-disable。 4.忽略范围ignorePatterns排除了api/**docs/**dify-agent/**docker/**等非前端目录、**/*.d.{ts,cts,mts}声明文件,以及packages/contracts/**生成的契约包(该包目前只有 Oxfmt 这一道质量关卡)。 5.按目录的 overrides:web 目录下额外启用no-barrel-files、TanStack Query 系列规则与 React 规则;web/**/*.tsx应用webJsxA11yRules;Storybook 文件应用 storybook 插件规则;web 源码还通过no-restricted-imports禁止直接引入next/imagenext/font@base-ui/react@floating-ui/*等模块,强制走@/next封装与@langgenius/dify-ui组件原语。

可选的 Tailwind 规范类检查

Tailwind 规范类清理是可选的——加载该 JS 插件会带来明显的 lint 启动开销,默认的pnpm check不加载它。需要时用:

pnpm lint:tailwind # 检查 web/ 与 packages/dify-ui/ pnpm lint:tailwind:fix # 应用安全替换

对照根 package.json,这两个脚本实际是:

TAILWIND_CANONICAL_CLASSES=true vp lint web packages/dify-ui TAILWIND_CANONICAL_CLASSES=true vp lint --fix web packages/dify-ui

lint.config.ts 通过读取环境变量TAILWIND_CANONICAL_CLASSES条件注入eslint-plugin-better-tailwindcss插件和better-tailwindcss/enforce-canonical-classes规则(warn 级别,collapse: falselogical: false),并在 settings 中为其指定cwd: web/、入口样式表web/app/styles/globals.css、16px 根字号——这与 vite.config.ts 中 Oxfmt 的sortTailwindcss配置使用同一份样式表与 16px 根字号,保证"格式化排序"与"规范类检查"对类名合法性的判断一致。

自动修复工作流:编辑器与提交钩子

自动修复分三层,全部复用同一套 Vite+ 组合检查:

  1. 编辑器:配置 Oxc 与 ESLint 编辑器扩展,在保存时分别应用各自的修复。
  2. 提交钩子:commit hook 运行vp staged。从根 vite.config.ts 的staged配置可以看到委托关系:
staged: { [lintFiles]: checkFix, // *.{js,cjs,mjs,jsx,ts,cts,mts,tsx} → vp check --fix [eslintFiles]: [eslintFix, formatFix], // *.{json,jsonc,json5,md,yml,yaml,toml} → eslint --fix + vp fmt [formatOnlyFiles]: formatFix, // *.{mdx,css,scss,less,html,vue,svelte,gql,graphql,hbs,handlebars} → vp fmt '.vite-hooks/*': 'sh -n', },

即暂存的代码文件交给vp check --fix(含 ESLint 非代码回退),暂存的 Markdown/CSS 等纯格式文件只跑vp fmt。 3.注意:提交前务必审查自动修复结果。JS 插件被允许提供 fix,其修复行为不一定与 Oxlint 原生规则完全一致。

类型感知 Lint 与独立类型检查

根配置同时开启了typeAwaretypeCheck,因此vp check既运行类型感知规则,也通过TypeScript 7 原生编译器(仓库的@typescript/native依赖,见根 package.json 与 web/package.json)跑完整类型诊断。

Web 包仍单独运行既有的 TSSLint 规则集:

pnpm --dir web lint:tss

该命令在 web/package.json 中定义为tsslint --project tsconfig.json,其 devDependencies 依赖@tsslint/cli@tsslint/compat-eslint@tsslint/config。类型检查建议也覆盖编辑器体验:所有已打开文件应能看到编辑器的 TypeScript 提示。

提交或推送前运行完整静态检查:

pnpm check

存量错误基线:oxlint-suppressions.json 与批量抑制

存量的 Oxlint error 诊断记录在根目录的oxlint-suppressions.json基线中(该文件同时出现在 vite.config.ts 的generatedIgnores里,不会被格式化/检查误伤)。Oxlint 只会报告超出该按文件规则基线的新增 error;ESLint 没有这类批量抑制基线;warning 保持可见且不会让常规 lint 命令失败。

批量抑制标志在当前捆绑的 Oxlint 版本中可用,但从vp lint --help中被隐藏。要在仓库根目录运行,使所有包共用同一份基线:

pnpm lint:oxlint --suppress-all # 记录当前所有 error 为基线 pnpm lint:oxlint --prune-suppressions # 清理已不存在的基线条目

一个已知局限:Oxc 编辑器扩展尚未应用批量抑制基线,因此编辑器可能仍显示 CLI 已抑制的问题——以 CLI 结果为准。

已知迁移缺口与取舍

ESLint 被有意限制在非代码文件上,其余限制与已接受的迁移取舍如下(摘自 静态检查指南):

领域当前状态
仅代码的兜底规则ESLint 全局忽略所有代码文件。六条核心兜底规则、JS 的dot-notation及其他仅代码 ESLint 检查只以注释形式列出,而非可执行配置(见 eslint.config.mjs 的迁移取舍注释)。
声明文件Oxlint 排除声明文件且 ESLint 不再处理代码,原先的 223 规则声明文件快照和 CLI 声明导入限制均不再强制执行。
生成的契约包两个 linter 与 Vite+ 类型检查都忽略packages/contracts/**;Oxfmt 是该包唯一暂存质量步骤。
非 JavaScript 格式Oxlint 插件无法提供自定义 parser 或文件语言;ESLint 覆盖 JSON、JSONC、YAML、TOML、Markdown 的语义规则,Oxfmt 负责其格式化。
Markdown 代码块ESLint 校验 Markdown 文档本身,但围栏内的 JavaScript/TypeScript 块不再经过原重叠 preset,该事项被搁置而非复制 Oxlint 规则集。
override 作用域设置三条 Dify UI Tailwind 规则随 ESLint 代码路径一并禁用;Oxlint 仍全局应用 web 的react-x.additionalStateHooks设置(见 lint.config.ts 的正则/^use\w*State(?:s)?|useAtom$/u),因为它无法把设置限定到某个 override。
Oxlint disable 严重度Oxlint 只接受根级reportUnusedDisableDirectives,当前保持warn;原先 Dify UI 专属的 ESLinterror严重度不再应用于代码文件。

配套约定再强调一次:抑制注释只能属于一个 linter。代码规则(lint.config.ts)用oxlint-disable;非代码规则(eslint.config.mjs)用eslint-disable。由于respectEslintDisableDirectives被显式设为false,ESLint 注释对 Oxlint 诊断无效。

引入新插件或新规则的流程

文档给出的引入顺序值得固化为团队习惯:

  1. 优先 Oxlint 原生规则。若没有等价物,先在代表性文件上验证该规则能否通过 Oxlint JS 插件正常运行。
  2. 记录而非回填:Oxlint 无法覆盖的代码规则应记录为迁移缺口,而不是加进 ESLint——ESLint 配置保留给 Oxlint 无法解析的非代码语言。
  3. 不引入 Antfu ESLint 配置作为依赖,也不启用 Oxlint 已覆盖的规则;新增规则要有意识地启用,并先审查存量违规。

运行环境前提

  • 根 package.json 声明packageManagerpnpm@11.25.0,Node 要求^24.20.0devEngines指定 24.20.0 且onFail: "download"),安装依赖时 pnpm 会按声明准备运行时;
  • prepare脚本会执行vp config,即 Vite+ 的初始化钩子,pnpm install后自动运行;
  • 前端检查只针对前端包(web/packages/cli/e2e/sdks/nodejs-client/等),Python 侧的api/dify-agent/均被 lint.config.ts 的ignorePatterns排除,两套代码库互不干扰。

掌握以上链路后,你可以在提交前用pnpm check得到与 CI 完全一致的判定,用vp check <paths>做快速局部验证,用lint:a11y --deps对新页面做范围化的无障碍体检,并通过oxlint-suppressions.json基线机制让存量问题不阻塞增量改进。

【免费下载链接】difyBuild Agentic workflows, RAG pipelines, with rich AI model and tool support on one collaborative workspace. Deploy on cloud, VPC, or self-hosted, so teams move from prototype to production without rebuilding the stack.项目地址: https://gitcode.com/GitHub_Trending/di/dify

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询