typescript-eslint 如何用 TSESTREE_SINGLE_RUN 环境变量控制单次运行推断以优化 CI 速度?
【免费下载链接】typescript-eslint:sparkles: Monorepo for all the tooling which enables ESLint to support TypeScript项目地址: https://gitcode.com/GitHub_Trending/ty/typescript-eslint
在 CI 中跑类型感知 linting(parserOptions.project或projectService)时,lint 耗时往往接近一次完整类型检查。typescript-eslint 为此内置了"单次运行"(single run)模式:当它判断本次 ESLint 调用是一次性的 CLI 运行(而不是--fix循环或编辑器这类长驻会话)时,会跳过为长驻场景准备的 TypeScript Watch Program 管理,改为一次性创建不可变 Program,并让缓存永久驻留。TSESTREE_SINGLE_RUN环境变量就是用来显式开启或关闭这一推断的手柄,官方文档说明自动推断被允许时"CI 中的 lint 速度最多可提升 10-20%"(见 Parser 文档)。
单次运行模式做了什么
按 Parser 文档 的描述,这一区分对性能的意义在于:管理长驻场景所需的 Watch Program 开销显著,而假定单次运行场景后,typescript-eslint 可以改用更快的不可变 Program。具体到行为:
- 使用
project时,single run 模式下 Program 会按项目一次性创建并缓存复用,而不是走 Watch Program(参见 parser 实现 中Detected single-run/CLI usage, creating Program once ahead of time for project的日志分支,以及 single run 测试 断言"每个 tsconfig 只创建一次 Program")。 - 使用
projectService时,single run 模式下不再执行"文件未被任何项目收录时 reload 整个 project service 并重试"的兜底逻辑(该逻辑只服务于编辑器中文件信息过期的场景,见 useProgramFromProjectService 实现)。 - 缓存策略随之变化:默认情况下缓存条目 30 秒后过期;如果
disallowAutomaticSingleRunInference未开启且解析器推断为单次运行,则缓存永久保留(见 Parser 文档cacheLifetime一节)。
前提:环境变量只作用于类型感知 linting
TSESTREE_SINGLE_RUN能否生效取决于parserOptions。从 inferSingleRun 实现 的判定顺序可以看出以下边界:
- 必须配置了
project或projectService(即使用了需要类型信息的规则)。两者都没有时,single run 恒为false,环境变量不起作用——因为 single run 本身就意味着类型感知 linting。 - 通过
programs直接传入 Program 时,Program 由用户自己管理,single run 恒为false。 - 使用
project(传统模式)且extraFileExtensions非空时,single run 被强制关闭,且该判断发生在环境变量读取之前——此时即使设置TSESTREE_SINGLE_RUN=true也不会进入 single run。原因是文档与测试都指明:single-run 宿主不支持extraFileExtensions,只有 watch program 宿主和 project service 支持(见 inferSingleRun 测试 中extraFileExtensions相关用例)。若确有.vue等扩展名需求,改用projectService即可保留 single run 推断能力。
如何设置 TSESTREE_SINGLE_RUN
先了解默认推断逻辑,再决定要不要显式设置。未设置环境变量时,除非disallowAutomaticSingleRunInference为true,typescript-eslint 会自动推断:
CI=true(大多数 CI 提供商默认设置)或命令以node_modules/.bin/eslint、node_modules/eslint/bin/eslint.js(即npx eslint ...)启动时,推断为单次运行;- 命令行带
--fix时推断为非单次运行; - 其余情况(如 IDE 插件调用)默认推断为非单次运行,按长驻会话处理。
也就是说,在典型的npx eslint .CI 流水线里 single run 通常已经被自动开启。显式设置环境变量用于两种情形:环境不满足自动推断条件时强制开启,或者推断结论不符合预期时强制关闭。
强制开启(适用于 CI 中未设置CI=true、或通过非node_modules路径调用 ESLint 的场景):
TSESTREE_SINGLE_RUN=true npx eslint .强制关闭(官方文档给出的示例命令),例如在长驻进程或需要 watch 行为的环境里排除自动推断的误判:
TSESTREE_SINGLE_RUN=false npx eslint .两点说明:
- 环境变量只识别字符串
"true"和"false",其他取值视同未设置(见 inferSingleRun 实现)。 - 环境变量的优先级高于自动推断:测试用例证实
CI=true之外再设TSESTREE_SINGLE_RUN=false时结果为false,而显式true时结果为true,即显式意图覆盖启发式(见 inferSingleRun 测试)。 - 除环境变量外,还可以用
parserOptions.disallowAutomaticSingleRunInference在配置层面关闭全部自动推断,其默认值正是process.env.TSESTREE_SINGLE_RUN或false(见 Parser 文档)。文档建议"尽可能保持该选项关闭",因为它关闭的是一项自动性能优化。
验证是否生效
typescript-eslint 提供调试日志开关。按 TypeScript ESTree 文档 的 Debugging 一节和 性能排查文档,设置DEBUG=typescript-eslint:*即可打开完整调试输出。用它对比两次运行:
# 强制开启并输出调试日志 TSESTREE_SINGLE_RUN=true DEBUG=typescript-eslint:* npx eslint .single run 模式下,解析器在处理project中的每个 tsconfig 时会输出类似(日志文案来自 parser 源码):
Detected single-run/CLI usage, creating Program once ahead of time for project: ./tsconfig.json把环境变量改为false重复运行,这条日志应不再出现,可据此确认开关确实改变了 Program 的创建路径。若要量化收益,可对照 性能排查文档 的建议:用 ESLint 的TIMING=1选项观察规则耗时,并留意"项目的第一条类型感知规则因内部缓存几乎总是显得最慢",比较不同配置下的总耗时。文档给出的经验数据是自动单次运行推断在 CI 中最多带来 10-20% 的提速,实际幅度以你自己的前后对比为准。
限制与已知边界
--fix与显式TSESTREE_SINGLE_RUN=true同时出现时,--fix循环可能对同一文件解析多次。对传统project模式,typescript-eslint 会在第二次解析同一文件后回退为每次从最新源码创建隔离 Program(保证 fix 结果正确,但失去 AOT Program 的收益);使用projectService时语言服务始终提供最新 Program,不需要该回退(见 parser 源码注释)。- 如前所述,
project+ 非空extraFileExtensions组合下环境变量无法开启 single run;projectService组合则可以正常推断。 - 该环境变量只影响 Program 管理与缓存时长,不改变类型信息本身的正确性;类型检查本身慢的项目("Slow TypeScript Types")应参照 性能排查文档 中的类型复杂度排查路径处理,而不是依赖本开关。
【免费下载链接】typescript-eslint:sparkles: Monorepo for all the tooling which enables ESLint to support TypeScript项目地址: https://gitcode.com/GitHub_Trending/ty/typescript-eslint
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考