
lit-labs/analyzer 能力演进全解析Lit 静态分析器的架构设计与模板解析实现【免费下载链接】litLit is a simple library for building fast, lightweight web components.项目地址: https://gitcode.com/GitHub_Trending/li/lit导读本文以 lit-labs/analyzer 的 CHANGELOG 为主体脉络系统梳理这个面向 Lit 生态的静态分析库从 0.1.0 到 0.14.0 的完整演进过程。你将了解它如何通过 TypeScript AST 与类型系统识别 LitElement、ReactiveElement、原生自定义元素乃至 lit-html 模板如何对外暴露Analyzer/createPackageAnalyzer编程接口以及它如何支撑 linter、IDE 插件tsserver-plugin和代码生成器React/Angular wrapper 生成器等下游工具。读完后你既能掌握该库的 API 用法与分析能力边界也能从源码层面理解模板解析器、声明模型与引用解析的底层原理。一、这个包是什么面向 Lit 的静态分析基础设施lit-labs/analyzer是 Lit 仓库packages/labs/analyzer目录下的一个实验性Lit Labs包其 README 给出的定位是包含用于分析包含 Lit 模板和元素的源代码的静态分析工具可用于 linter、IDE 插件、代码生成器等下游程序。从仓库结构看该包被设计为纯 TypeScript/JavaScript 静态分析库而非运行时库它读取源码文件、解析出结构化模型供其他工具消费。它与仓库中其他 Labs 包的关系可以从生成器侧反推——例如 gen-wrapper-react、gen-wrapper-angular、gen-wrapper-vue 等框架 wrapper 生成器以及 custom-elements-manifest 类工具 都以它产出的模型为基础。[!IMPORTANT] 依据 README 中的警告该包属于 Lit Labs 系列发布目的是收集设计反馈可能包含破坏性变更或停止维护生产环境使用前请先阅读 Labs 文档。二、快速上手两种编程入口2.1 Node 环境createPackageAnalyzer最常见的用法是基于文件系统路径创建包级分析器源码实现位于 package-analyzer.tsimport {createPackageAnalyzer} from lit-labs/analyzer/package-analyzer.js; import * as path from path; const packagePath path.resolve(./my-package); const analyzer createPackageAnalyzer(packagePath); const module analyzer.getModule( path.resolve(packagePath, src/my-element.ts) );createPackageAnalyzer的入参解析逻辑源码 L36-L88值得注意传入路径可以是包根目录也可以是某个具体的 tsconfig 文件若传入目录且目录下存在tsconfig.json则按 TypeScript 工程分析读取配置并通过ts.parseJsonConfigFileContent构造ParsedCommandLine若传入目录但没有tsconfig.json控制台会打印No tsconfig.json found; assuming package is JavaScript.随后以硬编码的 JS 编译器选项module: es2021、allowJs: true、typeRoots: []等将工程当作 JavaScript 分析——这正是 CHANGELOG 0.3.0 Added support for analyzing JavaScript files 的落地实现传入的既不是目录也不是 tsconfig 文件时会抛出The specified path ... was not a folder or a tsconfig file.错误。它还接收一个AnalyzerOptions源码 L12-L20目前只有exclude?: string[]一个选项用于排除工程中不应参与分析的源文件const analyzer createPackageAnalyzer(packagePath, { exclude: [**/test/**, **/*_test.ts], });这正是 CHANGELOG 0.6.0 中 --exclude options对排除测试文件以生成 manifest 或 wrapper 至关重要的实现所在——排除 glob 会被合并进 tsconfig 的exclude数组L43-L45。内部流程L90-L114还会用ts.createCompilerHost(options, /* setParentNodes */ true)创建带父节点指针的编译器宿主因为getText()等 API 需要向上回溯 AST随后基于解析出的文件名与编译选项创建ts.Program并把program.getSyntacticDiagnostics()收集进 analyzer 的诊断队列。2.2 浏览器环境底层AnalyzerAnalyzer类analyzer.ts不依赖 Node 文件系统而是通过构造参数注入依赖因此可以在浏览器中与 bundler 配合使用。它的构造参数AnalyzerInit源码 L19-L25包括export interface AnalyzerInit { typescript: TypeScript; getProgram: () ts.Program; fs: AnalyzerInterface[fs]; path: AnalyzerInterface[path]; basePath?: AbsolutePath; }由于要传入一个ts.Program在浏览器中运行必须先用 bundler 打包 TypeScriptREADME 建议使用 Rollup 配合 CommonJS 插件。在 Rollup 配置中需要忽略os、fs、inspector等 Node 内建库// rollup.config.js import commonjs from rollup/plugin-commonjs; // ... plugins: [ commonjs({ ignore: (id) [fs, os, inspector].includes(id), }), ], // ...并且可能需要安装path包npm i path。随后按 README 所示引入import {Analyzer} from lit-labs/analyzer/lib/analyzer.js; import {AbsolutePath} from lit-labs/analyzer/lib/paths.js; import ts from typescript; import * as path from path; // TODO: show constructing an Analyzer in browser contexts依据 package.json 的exports字段该包公开了.、./package-analyzer.js与./lib/*.js三个入口其中package-analyzer.js入口即createPackageAnalyzer依赖 Node API是 0.9.0 中Add separate entrypoint for createPackageAnalyzer() which requires Node APIs的结果。2.3 公共导出与包级模型src/index.ts 导出Analyzer及一系列模型类型Package、Module、Reference、Type、Event、Declaration、VariableDeclaration、ClassDeclaration、ClassField、ClassMethod、Parameter、Return、LitElementDeclaration、MixinDeclaration、CustomElementDeclaration、FunctionDeclaration等以及getImportsStringForReferences工具函数。其中Package模型model.ts提供getLitElementModules()返回包含 LitElement 声明的模块 过滤后的声明列表这是下游代码生成器最常用的入口之一。三、能力演进时间线从最小骨架到模板级分析以下是依据 CHANGELOG 整理的完整能力演进脉络。该包遵循 changesets 语义化版本Minor 为新增能力Patch 为缺陷修复。0.1.x2022 年初初代骨架与 LitElement 发现0.1.0新增初始Analyzer类PR #2676这是整个库的地基。0.1.1三个关键补丁——Initial support for finding LitElement declarations在源码中定位 LitElement 声明PR #2796、Refactor LitElement-specific utilities into separate module将 LitElement 相关工具拆成独立模块PR #2798、Add minimal class declaration gathering最基础的类声明收集PR #2789。从源码看LitElement 专用工具最终沉淀为 src/lib/lit/lit-element.ts其中isLitElementSubclass()L112-L130通过类型检查器取基类型并逐层判定是否最终指向规范的 LitElement 声明而_isLitElementModule()L86-L96则通过文件路径特征node_modules/lit-element/lit-element.d.ts、monorepo 下的packages/lit-element/lit-element.d.ts等识别 LitElement 本源文件。0.2.x属性选项、事件与 React wrapper 雏形0.2.0CLI 增加 React wrapper 的基本生成能力PR #2822——这是lit-labs/gen-wrapper-react方向的最早萌芽。0.2.1模型新增Type、Reference、VariableDeclarationPR #2976。0.2.2TypeScript 升级至 ~4.7.4PR #3116。补丁Read property options from decorated propertiesPR #2804从property()装饰器中读取选项、Read events from class JSDoc fires tagsPR #2812从类 JSDoc 的fires标签读取事件、Add utilities for getting LitElement declarationsPR #2896。属性选项的读取在 src/lib/lit/properties.ts 中有完整实现getProperties()遍历类成员区分带property装饰器的属性从装饰器参数对象字面量解析attribute/type/reflect/converter等选项对应源码 L58-L72 与 decorators.ts 的getPropertyOptions、静态properties块L73-L78JS 用户常用写法与无装饰器普通字段L79-L85用于类型推断。0.3.xJavaScript 支持与 Analyzer 重构0.3.0三件事——Added support for analyzing JavaScript filesPR #3304Refactored Analyzer into better fit for use in pluginsPR #3288Analyzer类改为接收ts.Program新增PackageAnalyzer接收包路径并在文件系统上创建 program修复 CLI 全局安装导致 analyzer 跨包不兼容的 bugPR #3254。Analyzer 接收 Program、PackageAnalyzer 接收路径这一分层一直保持到今天正是我们在第二节看到的两个入口的由来。JS 分析能力则体现在createPackageAnalyzer的 tsconfig 缺失回退路径见 2.1 节。0.4.x缓存与 custom elements manifest 生成器0.4.0Cache Module models based on dependenciesPR #3333——按依赖关系缓存 Module 模型避免重复解析。补丁Added initial implementation of custom elements manifest generator (WIP)PR #2990——自定义元素清单生成器的初始实现进行中状态。模块缓存在 analyzer.ts 中体现为readonly moduleCache new MapAbsolutePath, Module()注释明确说明当源文件或其任一依赖变化时失效。而 custom elements manifest 生成逻辑的入口在 src/lib/custom-elements/custom-elements.ts除了customElement装饰器它还能从 JSDoc 的customelement标签以及customElements.define(x-foo, XFoo)命令式调用中提取 tag 名L70-L100 之后。0.5.x超类分析、导出查询与引用解引用0.5.0PR #3507export、slot、cssPart、cssProperty进入 analyzer 与 manifest 生成器同时改善 JS 工程分析性能ClassDeclaration新增 superclass 分析Module新增getExport()/getResolvedExport()Reference新增dereference()。引用解析在 model.ts 中有清晰呈现getExport(name)L193-L205返回本模块定义的Declaration或指向其他模块的Reference重导出场景getResolvedExport(name)L226-L232沿着重导出链循环dereference()直到拿到具体声明getExportReference(name)L213-L220统一返回Reference形式。而超类继承链的查询方式在 CHANGELOG 中有明确示例classDeclaration.heritage.superClass.dereference()——heritage.superClass返回Reference解引用后得到超类的ClassDeclaration模型。ClassDeclaration.heritage的实现在 model.ts继承信息由getHeritage工厂函数惰性计算。0.6.x分析覆盖面大幅扩展0.6.0的 Minor 变更最为密集Added analysis of vanilla custom elements that extend HTMLElementPR #3621——原生非 Lit自定义元素分析对应 custom-elements.ts 中的isCustomElementSubclass()通过基类型链查找HTMLElement接口声明判定支持const变量初始化为类表达式/函数表达式时按ClassDeclaration/FunctionDeclaration分析PR #3662JSDoc 类型在 TS 文件中对输出无影响与 TS 自身行为一致PR #3658函数重载支持PR #3702以字符串 key 查询的方法将返回重载函数的实现签名声明该声明新增overloads字段内含每个重载签名的FunctionOverloadDeclaration——对应 model.ts 中FunctionDeclaration.overloads与FunctionOverloadDeclaration的设计静态类成员支持按名称分别存储到独立的 mapPR #3648即 model.ts 中的staticFieldMap/staticMethodMap与普通fieldMap/methodMap分离函数声明分析PR #3655CLI 增加--exclude选项分析器与 manifest 输出新增TS 枚举类型变量、所有模型的description/summary/deprecated、模块级 description summary、ClassField和ClassMethodPR #3529。至此分析器的模型宇宙基本成型Declaration抽象基类model.ts提供isVariableDeclaration()、isClassDeclaration()、isLitElementDeclaration()、isFunctionDeclaration()、isMixinDeclaration()、isClassField()、isClassMethod()、isCustomElementDeclaration()等类型守卫。0.7.xCSS 自定义属性回退值0.7.0PR #3812manifest 中新增 CSS 自定义属性回退默认值。这为设计系统文档生成等场景补充了--my-color: red中默认值red的语义信息。0.8.x从崩溃走向尽力而为0.8.0PR #3866在遇到意外语法或尚未处理的场景时analyzer 不再大面积崩溃custom elements manifest 生成器会记录分析过程中收集到的 diagnostics能生成 manifest 就尽量生成。这一容错优先的哲学对下游代码生成工具的健壮性至关重要——单个不支持的语法不再阻塞整个包的清单输出。诊断的收集与读取实现在 analyzer.tsaddDiagnostic()入队、getDiagnostics()经sortAndDeduplicateDiagnostics去重排序后产出。0.9.xTypeScript 5.0 与 API 收敛0.9.0TypeScript 升级至 ~5.0PR #4030createPackageAnalyzer()拆出独立入口PR #3980即今天的lit-labs/analyzer/package-analyzer.js构造Analyzer必须传入 TypeScript 对象PR #4029。补丁PR #4006当tsconfig.json通过extends继承其他配置时也能正确检测源文件。必须传入 TypeScript 对象是重要的设计决策——它保证 analyzer 使用与分析者相同的 TypeScript 版本避免多版本 TypeScript 并存导致的类型不兼容。这一点在 0.10.0 的补丁中被进一步强化见下节。0.10.xTypeScript 5.2 与消费者优先0.10.0TypeScript 升级至 ~5.2.0PR #4141。补丁Always use consumers typescript rather than analyzers dependency to avoid version mismatchesPR #4252感谢 43081j——始终使用消费者侧安装的 TypeScript避免版本失配与 0.9.0 的 API 变更一脉相承TypeScript v5.0 更新PR #3814移除对 Node 专有库的依赖absoluteToPackage()需显式传入路径分隔符0.11.0PR #4322分析器模型对象上新增 TypeScript 节点引用0.11.0PR #4260。0.12.x ~ 0.13.xMixin 与模块解析修复0.12.0新增lib/lit-html/template.js模块提供初始模板工具PR #4261——这是模板分析能力的先声。0.12.1支持将mixin 类/函数作为被分析类的超类PR #4147感谢 43081j。0.13.1正确忽略分析 LitElement 响应式属性时的类私有字段#field语法PR #4746修复 TypeScriptNodeNext模块解析下的类型解析与 Lit 模块检测 bugPR #4744。0.13.2README 添加 Lit Labs 提示PR #4903——即我们在开头引用的那段实验性警告。从 properties.ts 可以看到私有字段处理的相关代码仅当属性名为普通标识符或私有标识符ts.isPrivateIdentifier时才继续分析否则产生UNSUPPORTED警告诊断并跳过。0.14.0当前版本模板解析器与类型检查Minor Changestsserver-plugin 中支持对 lit-html 属性绑定做类型检查PR #5056——分析能力开始反哺 IDE 体验对应仓库中的 tsserver-plugin。新增模板解析器PR #4267 与 PR #4805——能够把html\...模板解析成结构化的LitTemplateparse5DocumentFragment 的扩展模板中的子节点绑定、属性绑定、事件绑定、属性绑定、布尔属性绑定都被识别为可查询的 part。声明的联合类型union types不再被拓宽为基类型PR #5177感谢 ClaudioHoffmann——修复了 Angular wrapper 生成器生成的属性访问器中的意外类型错误。Patch Changes调整模板解析器中属性的源码位置PR #5057TypeScript 依赖升级至5.8并同步处理ARIAMixin相关变更ariaColIndexText、ariaRelevant、ariaRowIndexTextPR #4984感谢 kyubisation。四、源码深潜模板解析器是如何工作的0.14.0 引入的模板解析器位于 src/lib/lit/template.ts是整个分析器目前最有技术含量也最有代表性的模块值得单独展开。4.1 识别真正的 lit-html 模板isLitHtmlTaggedTemplateExpression()L58-L77负责判定一个 AST 节点是否为 lit-html 模板。它递归调用isResolvedIdentifierLitHtmlTemplate()L101-L140把 tag 标识符解析回符号声明校验它确实是来自lit或lit-html的html命名导入。注释中的例子很直观import {html as h} from lit; h; // ✅ 是 lit-html 模板支持别名导入import {html} from lit-html/static.js; htmlfalse; // ❌ 不是static 模板不算可编译模板4.2 Part 类型体系解析结果中的绑定part按PartType枚举分类L142-L149export const PartType { ATTRIBUTE: 1, // 属性绑定 div foo${x} CHILD: 2, // 子节点绑定 div${x}/div PROPERTY: 3, // 属性绑定. 前缀div .foo${x} BOOLEAN_ATTRIBUTE: 4, // 布尔属性绑定? 前缀div ?hidden${x} EVENT: 5, // 事件绑定 前缀button click${onClick} ELEMENT: 6, // 元素绑定 div ${directive} } as const;SinglePartInfo覆盖 CHILD / ELEMENT 两类携带单个ts.ExpressionAttributePartInfo覆盖 ATTRIBUTE / PROPERTY / BOOLEAN_ATTRIBUTE / EVENT携带prefix、绑定名与表达式数组。4.3 解析流程与源码位置映射parseLitTemplate()L306-L574的流程是从ts.TaggedTemplateExpression提取模板字符串数组与插值表达式getTemplateStringsL582-L612调用 lit-html 内部的_$LH.getTemplateHtml(strings, 1)生成已准备的 HTML含 marker再交给 parse5 的parseFragment(source, {sourceCodeLocationInfo: true})解析成 DOM 树深度优先遍历 parse5 树遇到 marker 注释节点子绑定、以 marker 开头的属性元素绑定或带boundAttributeSuffix后缀的属性各类属性绑定时把对应ts.Expression挂到节点的litPart上并记录valueIndex由于${表达式}在准备 HTML 中被替换为等长的 marker解析器通过lineAdjust/colAdjust/offsetAdjust三组游标把 parse5 的行列偏移精确映射回 TypeScript 源码位置——这正是 0.14.0 Patch 中Adjust attribute source locations的基础结果以WeakMapts.TaggedTemplateExpression, LitTemplate缓存L277同一模板节点重复解析直接命中缓存。最终产物LitTemplateL236-L253同时携带tsNode原始 TS 节点、strings模板字符串数组和parts全部绑定信息。getLitTemplateExpressions()L282-L298则负责遍历整个源文件收集所有 lit-html 模板表达式供 linter 规则或 tsserver-plugin 逐模板分析。五、源码深潜LitElement 声明的完整解剖以customElement(my-element)装饰的 LitElement 类为例getLitElementDeclaration()lit-element.ts会产出包含以下信息的LitElementDeclarationtagnamegetTagName()L137-L157优先读customElement(x-foo)装饰器的字符串参数否则回退到原生自定义元素的 tag 检测JSDoccustomelement标签或customElements.define(...)调用reactiveProperties由 properties.ts 的getProperties()产出——装饰器属性从property({...})对象字面量中解析attribute、type、reflect、converter等选项静态properties块与构造函数赋值路径则用于 JS 工程中的类型推断heritagegetHeritage()惰性计算超类与 mixin 引用类成员getClassMembers()收集字段与方法含static/privacy/readonly等元数据JSDoc 数据description、summary、deprecated。判定一个类是 LitElement 子类的逻辑isLitElementSubclassL112-L130走类型系统取类的基类型链逐个判断是否最终命中规范的LitElement声明遇到 mixin 产生的交叉类型则递归检查交叉成员。0.12.1 支持 mixin 类/函数作为超类正是这一逻辑的受益者。六、JSDoc 驱动的声明元数据面向文档生成器的细节0.5.0 与 0.9.2 等版本密集补充了 JSDoc 解析能力这些细节对 API 文档生成器、设计系统 catalog 工具价值极高类/方法/字段级别的description、summary、deprecatedfires {EventType} name事件声明0.9.2 起支持类型在前的写法cssprop/cssproperty/csspart小写标签0.9.2cssProperty {color} --my-color带语法元数据的 CSS 自定义属性0.9.2readonly标签与 TypeScriptreadonly关键字0.9.2针对非响应式类字段类访问器成对 / 只读 / 仅 setter0.9.2非响应式、构造函数赋值的类字段0.9.2ECMAScript 私有方法的privacy字段正确设置为 private0.9.2CSS 自定义属性的回退默认值进入 manifest0.7.0。对应解析实现在 src/lib/javascript/jsdoc.ts命名/类型化 JSDoc 信息解析、src/lib/custom-elements/events.ts事件收集以及 src/lib/custom-elements/custom-elements.ts。七、工程视角测试、构建与依赖测试组织源码树 src/test 分为servernode 原生 test runner覆盖 JavaScript 分析、Lit 属性/事件/模板、原生元素、类型解析等与browser经 web-test-runner 在浏览器中运行验证脱离 Node 环境的可行性依据 package.json 的 wireit 配置浏览器测试前会用 Rollup 把 TypeScript 打包成test/browser/typescript.js供浏览器加载。依赖typescript ~5.9.0、parse5 ^7.3.0、parse5/tools、lit-html ^3.1.2复用其_$LH私有模板准备逻辑、package-json-type。构建脚本npm run buildtsc --build依赖../../lit:build即需要先构建核心lit包。八、总结从 changelog 读出的设计哲学回看 CHANGELOG 的完整脉络可以提炼出lit-labs/analyzer的几条关键设计主线分层解耦Analyzer接收ts.Program可嵌入任意宿主与PackageAnalyzer接收路径面向文件系统的分层让同一套分析逻辑既能跑在 CLI / Node 工具链里也能跑在浏览器或编辑器插件进程里。版本对齐优先从必须传入 TypeScript 对象0.9.0到始终使用消费者的 TypeScript0.10.0再到 NodeNext 模块解析修复0.13.1与 TypeScript 5.8 跟进0.14.0版本兼容性始终是最高优先级。容错优于崩溃0.8.0 起能生成就生成的策略使下游代码生成器面对不完整源码时依然可用。覆盖面持续外扩分析对象从 LitElement 类 → JavaScript 工程 → 原生自定义元素 → 函数/重载/静态成员 → mixin 超类 →lit-html 模板本身最终形成从元素级到模板级的完整静态分析栈为 linter、IDE 插件tsserver-plugin和 React/Angular/Vue wrapper 生成器提供了统一的信息底座。如果你正在构建面向 Lit 生态的代码生成器、lint 规则或 IDE 增强建议从 README 的两个入口示例起步对照 model.ts 的模型定义理解输出结构再以 src/test/server 下的测试用例作为行为参考。注意该包仍处于 Labs 阶段接口可能随版本演进发生破坏性变更锁定版本使用并在升级时查阅 changelog 是更稳妥的做法。【免费下载链接】litLit is a simple library for building fast, lightweight web components.项目地址: https://gitcode.com/GitHub_Trending/li/lit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考