ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

typescript-eslint 浏览器端构建:解读 website-eslint 如何在浏览器中运行 ESLint 与 TypeScript Lint

typescript-eslint 浏览器端构建:解读 website-eslint 如何在浏览器中运行 ESLint 与 TypeScript Lint typescript-eslint 浏览器端构建解读 website-eslint 如何在浏览器中运行 ESLint 与 TypeScript Lint【免费下载链接】typescript-eslint:sparkles: Monorepo for all the tooling which enables ESLint to support TypeScript项目地址: https://gitcode.com/GitHub_Trending/ty/typescript-eslintwebsite-eslint是 typescript-eslint 仓库中一个不起眼却关键的基础包它把 ESLint 的Linter与内置规则、typescript-eslint 全家桶以及 TypeScript 编译器能力捆绑成一个可在浏览器中运行的 CommonJS 产物专门服务于website包中的在线 Playground。本文将以packages/website-eslint/README.md为核心骨架结合build.mts、src/index.js等源码完整解析它的构建流程、打包内容、浏览器适配技巧与对外 API帮你理解ESLint 上浏览器这条工程链路的真实实现。一、这个包是什么为 Playground 而生的浏览器版 ESLintwebsite-eslint的定位非常纯粹——它是一个bundled version of ESLint plustypescript-eslint, made to work in a browser即能在浏览器里工作的 ESLint typescript-eslint 打包版。它只被用于website包中的在线 Playground不参与任何 Node.js 侧的 lint 流程也不面向最终用户单独发布package.json中private: true明确标记为私有包。也就是说当你在 typescript-eslint 官网上打开 Playground 输入一段 TypeScript 代码并得到即时 lint 反馈时背后执行 lint 的并不是常见的 Node 进程而是被 esbuild 打包进浏览器 bundle 的这个website-eslint产物。从源码结构看整个包非常精简packages/website-eslint/build.mts构建脚本README 中写作build.ts实际仓库中的文件名是build.mts由package.json中 nx 配置command: tsx build.mts驱动packages/website-eslint/src/index.js运行时入口负责组装 Linter、配置与规则packages/website-eslint/src/mock/一组 Node 专属模块的浏览器桩实现path、util、assert、lru-cache等。二、构建流程一条命令产出浏览器 bundle构建命令非常简单pnpm run buildpackage.json中该脚本为pnpm exec nx build而 nx target 最终执行tsx build.mts即用tsx直接运行build.mts。脚本内部调用esbuild版本约~0.28.0完成打包核心配置如下格式与目标format: cjs、platform: browser、target: es2020输出到./dist/同时开启minify: true压缩与treeShaking: true摇树入口./src/index.js产物名index对应dist/index.js并生成 linked sourcemap外部依赖external: []即所有依赖全部打进 bundle不保留任何运行时 require输出约定package.json的exports字段将.指向./dist/index.js类型声明指向./dist/index.d.ts。构建过程在build.mts的buildPackage函数中完成函数末尾通过console.time(building eslint for web)记录整体耗时便于在 CI 中观测打包性能。三、打包内容三件套Linter、TypeScript、typescript-eslintREADME 明确指出这个 bundle 包含三大块内容下面逐一结合源码展开。1. ESLint 的 Linter 类与内置规则bundle 包含 ESLint 的Linter类通过eslint/lib/linter/linter.js直接引入以及其全部内置规则eslint/lib/rules/index.js。在src/index.js中对外暴露了工厂方法exports.createLinter function () { return new Linter(); };同时内置规则通过eslint/use-at-your-own-risk的builtinRules原样导出exports.builtinRules builtinRules;。Playground 可以据此拿到与 Node 端一致的全量内置规则集而不必为浏览器单独裁剪 ESLint 核心功能。2. TypeScript按需在线加载的 Monaco web 兼容 bundle这是最巧妙的工程点。由于typescript包体积庞大不能直接打包进页面website-eslint采用了一个 wrapper让typescript与typescript/lib/tsserverlibrary两个模块入口被按需下载downloaded on the fly。具体机制藏在build.mts的 esbuildalias配置中typescript与typescript/lib/tsserverlibrary都会被 alias 到./src/mock/typescript.js而后者只有一行代码module.exports window.ts;即运行时直接读取浏览器全局对象window.ts。这个window.ts由网站侧在需要时加载与 TypeScript Playground 使用同一套源码方案从而得到 README 所说的Monaco web compatible bundle——即与 Monaco Editor 的 TypeScript 语言服务兼容的浏览器版 TypeScript。这让 Playground 既能完整使用 TypeScript 的类型系统能力解析、分析、报错又不把数 MB 的编译器塞进首屏 bundle。3. typescript-eslint 全家桶bundle 同时打包进 typescript-eslint 的核心包包括typescript-eslint/eslint-plugin全部 configs 与规则typescript-eslint/parser与typescript-eslint/typescript-estree负责把 TypeScript 源码解析为 ESLint 可处理的 AST。这背后的打包细节非常讲究。构建脚本通过 esbuild 插件把相关包全部解析到源码的index.ts见build.mts的onResolve逻辑形如typescript-eslint/pkg/sub的导入被重定向到pkg/src/sub/index.ts形如typescript-eslint/pkg的导入被重定向到pkg/src/index.tstypescript-eslint/typescript-estree及其/use-at-your-own-risk深链接则被重定向到use-at-your-own-risk.ts。也就是说打包的是各包未经编译的 TypeScript 源码而非已构建的 dist 产物这一步正是实现精细摇树的关键前提。四、深链接 /use-at-your-own-risk 与 tree-shaking 的工程哲学README 专门强调构建文件刻意使用各包内部的深层/use-at-your-own-risk导入deep imports。这是有明确收益的设计This is so that esbuild can properly tree-shake and only include the necessary code. This saves us having to mock unnecessary things and reduces our website bundle size.即通过深链接直接指向源码内部模块esbuild 才能做精细的 tree-shaking只把真正被用到的代码打进 bundle从而避免为无关代码编写 mock并显著压缩网站 bundle 体积。在src/index.js中可以直观看到这种导入风格import rawPlugin from typescript-eslint/eslint-plugin/use-at-your-own-risk/raw-plugin; import { analyze } from typescript-eslint/scope-manager; import { astConverter, getScriptKind, } from typescript-eslint/typescript-estree/use-at-your-own-risk; import { visitorKeys } from typescript-eslint/visitor-keys; import { Linter } from eslint; import { builtinRules } from eslint/use-at-your-own-risk;入口只挑选 Playground 运行时真正需要的能力如 AST 转换器astConverter、脚本类型判断getScriptKind、作用域分析analyze、访问者键表visitorKeys与查询库esquery其余未被引用到的主包代码如getParsedConfigFile、ESLint、RuleTester、CLIEngine等 Node 专用入口都会被 esbuild 摇树剔除。五、浏览器环境的去 Node 化define 与 mock 模块ESLint 及其依赖链包含大量 Node 专属模块直接打包必然在浏览器里爆炸。website-eslint用两层手段解决1. 全局 define 替换在build.mts中esbuild 的define配置把 Node 环境变量在编译期全部替换为浏览器值define: { define.amd: false, global: window, process.emitWarning: console.warn, process.env.DEBUG: false, process.env.IGNORE_TEST_WIN32: true, process.env.NODE_DEBUG: false, process.env.NODE_ENV: production, process.env.TIMING: undefined, process.platform: browser, }比如process.platform被固定为browser避免代码里出现基于平台的require(child_process)等路径。2. mock 模块alias 桩对于无法用 define 解决的模块build.mts通过alias把它们全部指向packages/website-eslint/src/mock/下的桩实现Node 内置的util、assert、path每个都同时注册xx与node:xx两种写法npm 包lru-cache——桩实现是一个基于Map的极简LruCachesrc/mock/lru-cache.jstypescript与typescript/lib/tsserverlibrary——桩为window.tssrc/mock/typescript.js。此外esbuild 插件还通过onLoad对若干关键文件做了内容级替换build.mtsgetParsedConfigFile.ts、ts-eslint/ESLint.ts、ts-eslint/RuleTester.ts、ts-eslint/CLIEngine.ts全部替换为空模块src/mock/empty.jsESLint 的lib/api.js被替换为src/mock/eslint.js其中RuleTester、SourceCode是空壳类Linter则通过vt:eslint/linter虚拟标识符重定向到真实的eslint/lib/linter/linter.jsESLint 的unsupported-api.js被替换为src/mock/eslint-rules.jsbuiltinRules通过vt:eslint/rules指向真实的eslint/lib/rules/index.jstypescript-eslint/parser/src/parser.ts被替换为src/mock/parser.js其parseForESLint为空操作——因为在 Playground 场景下AST 转换实际由typescript-estree的astConverter直接完成不需要 parser 重新走一遍解析流程。这套组合拳保证了 bundle 内不会残留任何fs、child_process、os等浏览器不存在的模块引用。六、运行时 API 与配置的浏览器化处理src/index.js最终对外暴露的完整 API 如下exports.getScriptKind // 来自 typescript-estree判断脚本类型 exports.analyze // 来自 typescript-eslint/scope-manager作用域分析 exports.visitorKeys // 来自 typescript-eslint/visitor-keys exports.astConverter // 来自 typescript-estreeTS AST → ESTree exports.esquery // esquery供 Playground 做 AST 查询 exports.createLinter // () new Linter() exports.configs // js/* 与 typescript-eslint/* 全套配置 exports.plugin // rawPlugin.plugintypescript-eslint 插件本体 exports.builtinRules // ESLint 内置规则其中configs的组装有一个非常实用的细节src/index.jsconst stripParser config { if (!config.languageOptions?.parser) { return config; } const { parser: _parser, ...languageOptions } config.languageOptions; return { ...config, languageOptions }; };源码注释解释了原因部分配置自带languageOptions.parser默认的 Node 版 parser这会把 Playground 使用的浏览器兼容 parser 覆盖掉并导致报错因此在导出配置前统一剥离parser字段。随后for (const [name, value] of Object.entries(js.configs)) { configs[js/${name}] stripParserFromConfig(value); } for (const [name, value] of Object.entries(rawPlugin.flatConfigs)) { configs[typescript-eslint/${name}] stripParserFromConfig(value); }最终得到以js/name与typescript-eslint/name命名的全套扁平配置flat config供 Playground 按预设选择 lint 规则组合。另外值得一提的是 esbuild 的banner/footer配置build.mtsbanner: { js: define([exports, vs/language/typescript/tsWorker], function (exports) { }, footer: { js: }); },这是针对 esbuild#819 的已知 workaround将产物包进一个 AMD 风格的define调用中使其能无缝对接 Monaco/vs/language/typescript/tsWorker的加载环境这正是Monaco web 兼容在产物形态上的具体体现。七、构建产物如何被 Playground 使用构建完成后dist/index.js由website包网站/Playground 应用在浏览器侧加载。package.json中exports[.]的default指向./dist/index.js配合tsx build.mts产出的类型声明dist/index.d.ts使得网站侧可以像使用普通 npm 包一样获得类型提示。整个依赖关系是单向的website-eslint依赖typescript-eslint/eslint-plugin、typescript-eslint/parser、typescript-eslint/scope-manager、typescript-eslint/typescript-estree、typescript-eslint/visitor-keys以及eslint、eslint/js、esquery等见package.json的devDependencies全部为workspace:*或catalog:版本它自身则只被website引用不参与任何发布给用户的产物链路。八、小结一条可复用的ESLint 上浏览器工程范式纵观README.md与其实现website-eslint给出了一个非常典型的浏览器化 Node 工具链方案其要点可归纳为构建期裁剪esbuildaliasonLoad替换 define把 Node 专属模块在编译期替换为桩或常量源码级深链接通过/use-at-your-own-risk直接引用包内源码让 tree-shaking 精准到函数粒度最小化 bundle按需加载重型依赖TypeScript 编译器不打包而是暴露window.ts由宿主按需注入与 Monaco/TS Playground 方案共用同一套加载机制运行时收口入口文件只暴露 Playground 真正需要的少量 APIcreateLinter、configs、plugin、builtinRules等并针对浏览器环境对配置做必要的净化剥离 Node parser。如果你需要在自己的项目中让 ESLint typescript-eslint 跑在浏览器里例如在线 IDE、文档站 Playground、代码沙箱website-eslint的这套build.mtssrc/mocksrc/index.js组合是可直接参照的完整参考实现——从构建脚本、桩模块到运行时 API每一个环节都能在本文引用的仓库文件里找到一一对应的代码依据。【免费下载链接】typescript-eslint:sparkles: Monorepo for all the tooling which enables ESLint to support TypeScript项目地址: https://gitcode.com/GitHub_Trending/ty/typescript-eslint创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表