关于 dx-code-analyzer-custom-rule-create
用于通过 Regex、PMD/XPath 或 ESLint 创建 Salesforce Code Analyzer 自定义规则的 Skill。它指导选择引擎,并要求检查 AST、使用脚本创建规则、验证及进行正反样例测试。
主要功能
- 01根据模式选择规则引擎
- 02创建 Regex、PMD/XPath 和 ESLint 规则
- 03使用 ast-dump 检查 Apex 结构
- 04验证规则并测试正反样例
- 05按引擎处理测试文件排除
适用场景
- 01落实团队专属编码规范
- 02标记禁止的代码或元数据模式
- 03为 Apex、XML 或 JavaScript 创建规则
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ forcedotcom/ sf-skills --skill dx-code-analyzer-custom-rule-create安装后在 Claude Code 中输入/dx-code-analyzer-custom-rule-create确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/pmd-ruleset-template.xmlexamples/metadata-xml-example-fields-api.mdmetadata-xml-example-flows.mdmetadata-xml-example-permissions.mdmetadata-xml-examples.mdregex-examples.mdxpath-examples.mdreferences/advanced-pmd-patterns.mdapex-ast-reference.mdeslint-custom-plugins.mdeslint-rules-discovery.mdeslint-tier2-configurable.mdeslint-tier3-custom-plugins.mdmetadata-xml-rules.mdregex-rule-schema.mdtroubleshooting.mdxpath-patterns-governor-limits.mdxpath-patterns-method-calls.mdxpath-patterns-security.mdxpath-patterns-structure.mdxpath-patterns.mdscripts/create-pmd-rule.jscreate-regex-rule.js详细介绍
dx-code-analyzer-custom-rule-create:自定义代码分析器规则编写
生态系统: 此技能属于包含 3 项技能的 Code Analyzer 套件:
dx-code-analyzer-run(扫描和结果)·dx-code-analyzer-configure(设置、配置、CI/CD)·dx-code-analyzer-custom-rule-create(自定义规则编写)。
当用户需要创建自定义规则来强制执行 Code Analyzer 内置规则未涵盖的模式时,使用此技能。支持 Regex 引擎(文本模式匹配)和 PMD 引擎(针对 AST 的结构化 XPath 查询)。
此技能适用的任务
在工作涉及以下内容时,使用 dx-code-analyzer-custom-rule-create:
- 为 Code Analyzer 创建新的自定义规则(适用于任意引擎)
- 通过静态分析强制执行团队特定的编码规范
- 禁止特定模式(System.debug、硬编码 ID、TODO)
- 为 PMD 规则编写 XPath 表达式(Apex 或元数据 XML)
- 为 Regex 引擎编写正则表达式模式
- 为 LWC/JavaScript 设置自定义 ESLint 规则/插件
- 强制执行元数据治理(API 版本、字段描述、危险权限)
- 覆盖内置规则阈值(CyclomaticComplexity、ExcessiveParameterList 等)
- 将多条规则整理到共享规则集中
- 调整未能正确匹配的自定义规则
在以下情况下,将任务委派给其他技能:
- 使用现有规则运行扫描 →
dx-code-analyzer-run技能 - 配置引擎、先决条件、CI/CD →
dx-code-analyzer-configure技能 - 解释现有内置规则的含义 →
dx-code-analyzer-run技能 - 编写 Apex 代码或测试 →
generating-apex/running-apex-tests技能
首先需要收集的上下文
询问或推断以下信息:
- 要捕获的模式——哪些代码应被标记?(如果用户在 IDE 中选中了代码,选中内容就是答案,无需再次询问。)
- 允许的情况——是否有例外?(测试类、特定上下文)
- 文件范围——适用于哪些文件类型?(.cls、.trigger、.js,还是全部?)
- 严重程度——有多重要?(默认值:3/中等)
如果用户选中了代码(存在 IDE 选区上下文),则将选中内容视为模式定义。除非无法确定要针对选中内容的哪个方面,否则跳过澄清。
如果请求含糊且没有选区(“为最佳实践添加一条规则”),请提出一个澄清问题:
“这条规则应标记哪种具体模式?”
硬性约束
以下规则不可妥协。无论输出是否碰巧能正常工作,违反任何一条都意味着技能执行失败。
-
编写 XPath 前,必须始终先运行
ast-dump。 不得例外。不得凭记忆、参考资料或之前的对话使用节点名称。AST 是唯一依据——运行sf code-analyzer ast-dump,阅读输出,然后编写与所见内容匹配的 XPath。即使是“众所周知”的模式(例如循环中的 SOQL),也必须先运行ast-dump。如果跳过此步骤,但规则能够正常工作,仍然属于流程失败。 -
创建规则时必须始终使用脚本。 对于 regex 规则,必须始终使用
create-regex-rule.js。对于 PMD 规则,必须始终使用create-pmd-rule.js。不得手动编辑code-analyzer.yml来添加规则定义——YAML 中的 regex 模式会导致转义失败(引号中的引号、反斜杠被吞掉)。脚本每次都能正确处理 YAML 序列化。 -
脚本写入
code-analyzer.yml后,绝不能手动编辑该文件——即使只是修正错误值也不行。 脚本会生成转义正确的 YAML。之后如果重写或重新组织文件,就会破坏转义。如果用户添加了顶层配置(例如ignores.files),也不要改动——只处理你自己写入的内容。如果脚本的输出看起来有误(规则无法通过验证、YAML 解析错误、regex 中有多余字符):
- 不要手动修补 YAML。这正是此约束要避免的失败方式。
- 务必删除出问题规则的整个 YAML 块,然后使用修正后的参数重新调用脚本。删除你刚写入的块不违反此规则;重写块内字段则违反。
- 如果脚本接受了错误的输入并生成了错误的输出,那么错误在于输入(例如,
--regex "/.../ g"中多了空格——标志必须严格为/g,不得有空格)。使用修正后的参数重新调用脚本。 - 如果你确实认为脚本有缺陷,请停止操作并告知用户。不得手动编辑来绕过问题。
-
--regex必须采用/pattern/flags格式,且不得包含空格。 脚本会严格去除并验证标志——只允许g、i、m、s、u、y。/pat/ g(含空格)会被拒绝,/pat/x(无效标志)也会被拒绝,/pat/(没有标志)同样会被拒绝。必须包含全局标志g。如果验证失败,请修正参数——不得绕过验证直接编写 YAML。 -
创建后必须始终进行验证。 运行
sf code-analyzer rules --rule-selector <engine>:<name>,然后再测试。如果显示Found 0 rules,说明 YAML 解析失败——删除该块、修正参数,并重新调用脚本。 -
必须始终使用示例文件进行测试。 至少确认一个真正例和一个真反例。
对于 regex 规则,反例样本中不得在任何位置包含该模式文本,包括注释和字符串字面量。Regex 引擎会扫描原始文本;
// no System.debug here会匹配/System\.debug/g。运行前,请先在脑中检查该模式对反例文件的匹配情况。 -
按顺序一次创建一条规则。 用户请求多条规则时,每条规则都要分别完成完整流程(创建 → 验证 → 正例测试 → 反例测试),再开始下一条。不得批量创建规则——如果其中一条失败,会导致后续所有规则的配置都出问题。完成每条规则的端到端流程并确认其有效后,再继续下一条。
-
对于 regex 规则,使用
ignores.files排除测试类——regex_ignore无法做到这一点。regex_ignore是逐行筛选器(该行必须同时匹配规则和忽略模式);它无法排除整个测试类。如果用户希望“跳过测试类”,请在所有规则创建完成后,添加包含"**/*Test.cls"等 glob 的顶层ignores.files块——不要在脚本调用之间穿插配置编辑。
引擎选择
| 模式类型 | 引擎 | 原因 |
|---|---|---|
| 文本/字符串模式(TODO、硬编码 ID、关键字) | Regex | 简单、快速,无需 Java |
| Apex 代码结构(方法调用、嵌套、循环中的 SOQL) | PMD/XPath(language=apex) | 能理解 AST,不会被注释/字符串误导 |
| 元数据 XML 治理(API 版本、权限、描述) | PMD/XPath(language=xml) | 支持结构化 XML 匹配并处理命名空间 |
| LWC/JavaScript/TypeScript 模式 | ESLint* | 标准 JS 工具链和插件生态 |
| 两者都可用(仅限 Apex/元数据) | 优先使用 Regex | 更易创建和维护 |
* **对于 ESLint:**创建自定义插件前,务必先检查 Tier 1(内置规则)和 Tier 2(可配置规则)。参见 references/eslint-rules-discovery.md。
“两者都可用 → 优先使用 Regex”绝不适用于 JavaScript/LWC/TypeScript 文件。 JS/LWC/TS 模式必须使用 ESLint——Regex 无法区分 JS 中的代码与注释/字符串,会产生误报。不要以“简单”或“无需 npm 依赖”为由,在 JS 文件中使用 Regex。
告知用户你选择了哪种引擎以及原因。如果用户不同意,请尊重其偏好。
排除测试类 — 按引擎采用的策略
规则不应应用于测试类时,不同引擎的处理方式有所不同:
| 引擎 | 如何排除测试类 | 说明 |
|---|---|---|
| PMD(Apex) | 在 XPath 中添加 [not(ancestor::UserClass[ModifierNode[@Test = true()]])] | 结构化排除——效果可靠,无需更改配置 |
| Regex | 在 code-analyzer.yml 中使用 ignores.files,并设置 **/*Test.cls 之类的 glob | regex_ignore 按行生效,而不是按文件生效——它无法排除整个测试类。仅将 regex_ignore 用于按行匹配的模式,例如 // NOPMD |
| ESLint | 在 eslint.config.js 中使用 ignores 数组 | 标准的 ESLint 文件级忽略 |
**regex_ignore 不是文件级排除机制。**它只会跳过同时匹配忽略模式的行。示例:regex_ignore: "/@isTest/i" 只会抑制包含 @isTest 的行上的违规项——测试类第 50 行的 SOQL 查询仍会被标记,因为第 50 行不包含 @isTest。若要通过 regex 规则完全排除测试文件,请使用:
ignores:
files:
- "**/*Test.cls"
- "**/*_Test.cls"
ignores.files 是全局设置——会影响所有引擎和所有规则。如果部分规则需要排除测试类,而其他规则不需要(例如,从 SOQL 规则中排除测试类,但仍扫描测试类中的 @AuraEnabled),请对需要选择性排除的规则使用 PMD 和 XPath。PMD 的 XPath 可以按方法或类结构检查 @Test = true()——Regex 无法做到这一点。
Apex 规则跳过测试类的决策指南:
- 如果模式具有结构性(方法调用、注解、嵌套)→ 使用 PMD。XPath 原生支持排除测试类。
- 如果模式纯属文本 AND 所有 regex 规则都应跳过测试类 → 使用 Regex +
ignores.files。 - 如果规则要求不一(部分规则跳过测试类,其他规则不跳过)→ 对需要考虑测试类的规则使用 PMD,其他规则使用 Regex。
工作流
用户选择代码时(IDE 中的选区)
当用户在编辑器中选中一段代码,并要求“捕获这个”“标记这种模式”“为此创建一条规则”或提出类似要求时:
- 选区就是你的正例。 不要问“这条规则应该标记什么模式?”——用户已经展示给你了。不要从头编写新的示例。
- 判断选区中哪些是结构性的,哪些是偶然的:
- 结构性(值得编写规则):方法调用、循环模式、缺失的关键字、嵌套结构
- 偶然的(忽略):具体变量名、字符串值、参数数量
- 如果有歧义,只问一个问题:“这条规则应该捕获所有
System.debug调用,还是只捕获没有 LoggingLevel 参数的调用?”
- 对用户打开的实际文件执行 Ast-dump(不要创建新示例文件):
sf code-analyzer ast-dump --file <the-open-file.cls> --output-file <ast.xml> - 在 AST 输出中查找选区——定位与高亮行对应的节点。这些就是目标节点。
- 泛化 XPath——编写与结构模式匹配的 XPath,而不是与具体实例匹配。将具体变量名替换为通配符,保留结构节点和有区分作用的属性。
- 继续执行标准工作流(负例、创建规则、验证、测试正例和负例)。
示例流程:
- 用户选中:
Database.query('SELECT Id FROM ' + objectName) - 结构模式:
Database.query调用(动态 SOQL) - 偶然因素:其中具体的字符串拼接
- 引擎:PMD(结构化调用检测)
- XPath:
//MethodCallExpression[@FullMethodName='Database.query'] - 不要使用:匹配
Database.query的正则表达式(会漏掉多行调用,也会匹配注释)
示例流程(选中代码块):
- 用户选中一个包含 for-each 循环内 SOQL 语句的 5 行代码块
- 结构模式:循环体的后代节点中的 SOQL 查询
- 偶然因素:具体查询字段、变量名
- 引擎:PMD
- 对打开的文件执行 Ast-dump → 在循环体中查找 ForEachStatement + SoqlExpression
- XPath:
//ForEachStatement/BlockStatement//SoqlExpression
正则表达式规则
- 编写正例(5-10 行)来展示违规情况。将示例文件放在项目工作区内(例如项目根目录下临时创建
samples/目录),以便 Code Analyzer 能够扫描这些文件。 - 单独编写一个负例文件——代码看起来相似,但绝不能被标记。创建规则前,先在脑中用正则表达式检查此文件。
- 构建并创建规则——阅读
references/regex-rule-schema.md了解完整的架构,然后运行脚本:
始终使用脚本。 不要手动将正则表达式模式写入node "<skill_dir>/scripts/create-regex-rule.js" \ --name "<RuleName>" --regex "<pattern>" --description "<desc>" \ --severity <1-5> --file-extensions ".cls,.trigger"code-analyzer.yml——YAML 中的正则表达式字符(引号、反斜杠、大括号)会导致解析失败。脚本会正确处理序列化。 - 验证——
sf code-analyzer rules --rule-selector regex:<RuleName> - 测试正例——
sf code-analyzer run --rule-selector regex:<RuleName> --target <violation-sample>——必须找到违规。 - 测试负例——
sf code-analyzer run --rule-selector regex:<RuleName> --target <clean-sample>——必须找到 0 个违规。如果干净代码被标记,说明正则表达式范围太宽——返回并收紧模式。 - 如果测试失败,则迭代调整(调整正则表达式、添加
regex_ignore、缩小扩展名范围) - 清理——删除你创建的所有示例文件。不要在用户的项目中留下临时测试文件。
适用于 PMD/XPath 规则(Apex)
- 编写最小示例(5–10 行),展示违规情况。请在项目工作区内编写示例文件(例如,在项目根目录下创建临时
samples/目录),以便 Code Analyzer 能对其进行检查。正向和负向测试都通过后,请删除你创建的所有示例文件——包括原始示例和测试期间创建的所有副本。不要在用户的项目中留下临时测试样例。对于基于循环的规则,正向示例必须包含 Apex 的全部 3 种循环类型(for-each、传统for和while)——遗漏任何一种循环类型,就意味着 XPath 未针对该类型进行验证,可能会悄悄漏掉违规情况。 - 必须:导出 AST——
sf code-analyzer ast-dump --file <sample.cls> --output-file <ast.xml>——此步骤不可省略。即使你“已经知道”节点名称,也不得跳过。请运行命令、阅读输出,并确认确切的节点名称和属性。 - 阅读 AST 输出——根据实际的 ast-dump 输出(而非记忆)识别目标节点及其属性。仅将
references/apex-ast-reference.md和references/xpath-patterns.md用作补充参考。 - 编写 XPath——使用具有区分性属性的最小稳定节点作为目标。XPath 中的每个节点名称都必须原样出现在你刚刚阅读的 ast-dump 输出中。要有意区分使用
/Child(直接子节点)和//Descendant(后代节点)——请检查 ast-dump,了解哪些节点是同级节点,哪些是嵌套节点。 - 创建规则之前:编写单独的负向示例文件(5–10 行),展示正确且不应被标记的代码。该文件必须与正向示例分开——不得将正向和负向案例合并到同一个文件中。对于基于循环的规则,请包含
for (x : [SELECT...])这种写法。也要对此负向示例文件运行sf code-analyzer ast-dump。阅读输出并对照检查 XPath,确认它不会匹配负向 AST 中的任何节点。如果会匹配,请返回第 4 步,在继续之前收紧 XPath。不得跳过此步骤,也不得推迟到创建规则之后再做。第 9 步还会再次使用该负向文件,明确确认违规数为零。 - 创建规则——运行脚本:
node "<skill_dir>/scripts/create-pmd-rule.js" \ --name "<RuleName>" --xpath "<expression>" --message "<msg>" \ --language apex --priority <1-5> - 验证——
sf code-analyzer rules --rule-selector pmd:<RuleName> - 测试正向案例——
sf code-analyzer run --rule-selector pmd:<RuleName> --target <violation-sample.cls>——必须检测到违规情况 - 测试负向案例——
sf code-analyzer run --rule-selector pmd:<RuleName> --target <clean-sample.cls>——必须检测到 0 个违规。如果标记了正确代码,说明 XPath 范围过宽——请返回第 4 步。 - 如果测试失败,请迭代调整(重新检查 AST、调整 XPath、检查节点名称)
- 清理——删除你创建的所有示例文件(正向和负向示例,以及所有 ast-dump 输出文件)。不要在用户的项目中留下临时测试样例。
对于 PMD/XPath 规则(元数据 XML)
- 识别元数据文件类型(field、permissionset、profile、flow 等)
- 导出 XML AST —
sf code-analyzer ast-dump --file <file>-meta.xml --language xml --output-file <ast.xml>。如果 ast-dump 报错(例如"XmlEncoding is not a valid XML name"),则改为直接读取原始 XML 文件 — 对于元数据文件,XML DOM 结构就是 AST(文件中看到的内容就是 PMD 看到的内容)。读取文件并记录元素名称、嵌套关系和文本内容。 - 读取 DOM 结构 — 根据 ast-dump 输出或原始文件确认元素名称、嵌套关系和文本内容。仅将
references/metadata-xml-rules.md用作补充参考。 - 为 PMD 7 编写 XPath — 元数据 XML XPath 的关键规则:
- 匹配所有元素时必须使用
local-name()='ElementName'(命名空间会屏蔷裸名称) - 匹配文本内容时必须使用
@Text属性(不能用text()—text()函数在 PMD 7 的 XML 语言中不起作用)。示例://*[@Text='ModifyAllData'] - 使用
../..从文本节点向上导航到父元素(文本节点 → 元素 → 父元素) - 使用父元素上的
.//*[@Text='value']检查同级条件 - 完整的 PMD 7 XPath 模式请参见
references/metadata-xml-rules.md
- 匹配所有元素时必须使用
- 配置文件扩展名 — PMD 的 XML 语言默认只处理
.xml。Salesforce 元数据文件使用复合扩展名(.permissionset-meta.xml、.field-meta.xml等),但path.extname()会从这些文件中返回.xml,因此.xml已足够:
不要添加engines: pmd: file_extensions: xml: [".xml"].permissionset-meta.xml等复合扩展名 — Code Analyzer 的验证器会拒绝它们(/^[.][a-zA-Z0-9]+$/模式)。只需配置.xml,即可自动涵盖所有 Salesforce 元数据文件。 - 单独编写负向样本文件 — 与 Apex 规则相同:创建一个正确且不应被标记的元数据文件。在创建规则前,验证 XPath 不会匹配该文件。正向和负向样本都应在工作区中使用
.xml扩展名,以便 PMD 直接扫描。 - 创建规则 — 运行脚本:
node "<skill_dir>/scripts/create-pmd-rule.js" \ --name "<RuleName>" --xpath "<expression>" --message "<msg>" \ --language xml --priority <1-5> - 验证 —
sf code-analyzer rules --rule-selector pmd:<RuleName> - 测试正向样本 —
sf code-analyzer run --rule-selector pmd:<RuleName> --target <violation-sample>.xml— 必须找到违规项 - 测试负向样本 —
sf code-analyzer run --rule-selector pmd:<RuleName> --target <clean-sample>.xml— 必须找到 0 个违规项 - 如果测试失败,则迭代调整(检查
@Text与text()的使用是否正确,确认使用了local-name(),检查文件扩展名配置) - 清理 — 删除你创建的所有样本文件(正向和负向样本,以及为测试制作的任何
.xml副本)。不要在用户的项目中留下临时测试文件。
对于 ESLint 规则(LWC/JavaScript/TypeScript)
ESLint 发现工作流(请先阅读)
ESLint 有 200 多条内置规则,以及数千条来自插件的规则。在检查内置规则是否存在之前,切勿尝试创建自定义插件。发现工作流如下:
-
第 1 层(内置规则) — 占请求的 70%
- 运行:
sf code-analyzer rules --rule-selector eslint - 搜索关键词(例如“console”“unused”“equal”)
- 检查 LWC 插件规则(
@lwc/lwc/*) - 如果找到匹配项:配置后停止
- 运行:
-
第 2 层(可配置规则) — 占请求的 20%
- 如果模式是“禁止函数 X”?→
no-restricted-globals - 如果模式是“禁止语法 Y”?→
no-restricted-syntax - 如果模式是“禁止属性 Z”?→
no-restricted-properties - 如果适用:配置后停止
- 如果模式是“禁止函数 X”?→
-
第 3 层(自定义插件) — 占请求的 10%
- 仅用于特定领域的多节点模式
- 需要 Node.js 代码、测试和 meta.docs 元数据
- 参见:
references/eslint-custom-plugins.md
参见: references/eslint-rules-discovery.md,其中提供了完整的发现指南和示例。
-
必须:先运行 ESLint 发现工作流。 90% 的 ESLint 请求都可以通过内置规则(第 1 层)或可配置规则(如
no-restricted-syntax,第 2 层)解决。创建自定义插件(第 3 层)是最后的手段。继续操作前:a) 运行:
sf code-analyzer rules --rule-selector eslintb) 在输出中搜索用户请求中的关键词(例如“console”“equal”“unused”) c) **检查 LWC 插件规则:**在输出中搜索@lwc/lwc/d) **如果找到:**配置该规则(参见references/eslint-rules-discovery.md)并停止。不要创建自定义插件。 e) **如果未找到:**检查no-restricted-globals、no-restricted-syntax或no-restricted-properties是否能够表达该模式(第 2 层)。 f) **只有在第 1 层和第 2 层均无可行方案时:**才创建自定义插件。**验证:**配置后,运行
sf code-analyzer rules --rule-selector eslint:<ruleName>,确认规则已显示。如果没有显示,说明配置有误。跳过此发现工作流,在存在内置规则时创建自定义插件,属于技能缺陷,无论自定义插件是否有效。
**ESLint 内置规则与可配置规则:**Code Analyzer 在其基础配置中包含一部分 ESLint 规则。no-restricted-globals、no-restricted-syntax 和 no-restricted-properties 等规则是 ESLint 核心规则,但在 eslint.config.js 中启用之前不会生效。只有在配置后,它们才会显示在 sf code-analyzer rules 的输出中。务必验证(第 4 步),确认规则确实已加载——不要仅因某条规则是 ESLint 核心规则就认定它已存在。
- 安装 ESLint 插件(使用 ESLint 内置/核心规则时跳过)——
npm install --save-dev eslint-plugin-<name> - 创建/更新
eslint.config.js——添加插件和规则配置(或仅启用内置规则并将严重级别设为"error")。在code-analyzer.yml中配置engines.eslint.eslint_config_file之前,必须先创建此文件——Code Analyzer 会验证该路径,若文件缺失则会失败。 - 配置 Code Analyzer——在
code-analyzer.yml中设置engines.eslint.eslint_config_file。此操作必须在文件创建后进行。 - 验证——运行
sf code-analyzer rules --rule-selector eslint:<ruleName>。如果规则没有显示,说明配置有误——不要继续测试。请检查:(a)eslint.config.js文件是否位于正确位置?(b) 插件是否包含meta.docs.description和meta.docs.url?(c) 规则是否已弃用?Code Analyzer 会静默排除已弃用的规则。 - 在工作区中编写一个测试示例文件(例如
lwc/testSample/testSample.js),展示违规情况 - 正向测试——运行
sf code-analyzer run --rule-selector eslint:<ruleName> --target <test-sample>——必须能发现违规 - 反向测试——对现有的干净 LWC 文件运行——必须发现 0 个违规
- 清理——删除所有示例文件及其父目录(LWC 要求每个组件都有独立文件夹)。使用
rm -rf <directory>,不要只使用rm <file>。不要在用户的项目中留下空目录或临时测试夹具。 - 完整指南参见
references/eslint-custom-plugins.md
不使用自定义插件的常见 ESLint 模式
no-restricted-globals、no-restricted-syntax 和 no-restricted-properties 的配置示例,请参阅 <skill_dir>/references/eslint-custom-plugins.md 中的“禁止 API 而不使用自定义插件”部分。
ESLint 发现示例
示例 1:内置规则(Tier 1)
- 用户:“在 LWC 中禁止使用 console.log”
- 发现:
sf code-analyzer rules --rule-selector eslint | grep console→ 找到no-console - 操作:在 eslint.config.js 中启用
no-console - 结果:2 分钟内完成(无需自定义插件)
示例 2:可配置规则(Tier 2)
- 用户:“在 LWC 中禁止使用 setTimeout”
- 发现:没有内置的
no-setTimeout规则 - 操作:使用
no-restricted-globals并设置自定义消息 - 结果:5 分钟内完成(无需自定义插件)
示例 3:LWC 插件规则(Tier 1)
- 用户:“禁止使用 innerHTML 以防止 XSS”
- 发现:已有
@lwc/lwc/no-inner-html - 操作:在配置中启用
@lwc/lwc/no-inner-html - 结果:2 分钟内完成(无需自定义插件)
示例 4:适合使用自定义插件(Tier 3)
- 用户:“标记未进行错误处理的命令式 Apex 调用”
- 发现:没有针对这种模式的内置规则
- 分析:该模式需要检查没有
.catch()的import+.then()——需要遍历多个节点 - 操作:使用 visitor 模式创建自定义插件
- 结果:适合使用自定义插件(耗时 20 分钟以上)
完整的发现工作流请参阅 references/eslint-rules-discovery.md。
覆盖内置规则的阈值
无需编写新规则,即可自定义现有规则的严重性或属性:
- 创建自定义规则集,引用带有覆盖项的内置规则
- 添加到配置中——
code-analyzer.yml中的engines.pmd.custom_rulesets - 覆盖语法和常见示例请参阅
references/advanced-pmd-patterns.md
多规则请求
当用户一次请求多条规则(例如,“为 AppExchange 审查创建 5 条规则”)时,请遵循以下流程:
第 1 步:先规划引擎分配
创建任何规则之前,先列出所有请求的规则及其引擎分配,并向用户展示此计划:
Rule plan:
1. RequireUserMode → PMD/XPath (needs test-class exclusion via AST)
2. NoHardcodedIds → Regex (text pattern, no structural context needed)
3. AuraEnabledCacheable → PMD/XPath (needs annotation + DML structural check)
4. CustomFieldDescription → PMD/XML (metadata governance)
5. NoSetTimeout → ESLint (JavaScript pattern)
引擎分配的关键判断因素:
- 规则是否需要有选择地排除测试类?→ PMD(XPath 可以检查
@Test = true()) - 是否为纯文本模式,不涉及结构上下文?→ Regex
- 是否需要理解代码结构(注解、嵌套、DML)?→ PMD
- 是否为 JavaScript/LWC?→ ESLint(绝不使用 Regex)
- 是否为元数据 XML?→ 使用
language="xml"的 PMD
第 2 步:逐条按顺序创建规则
为每条规则完成完整工作流(创建 → 验证 → 测试正向用例 → 测试负向用例 → 确认正常工作)后,再开始下一条。不要批量创建规则——如果其中一条失败,会导致后续所有规则的配置损坏。
创建顺序:
- 先创建 Regex 规则(最快、依赖最少)
- PMD Apex 规则(每条规则都需要 ast-dump)
- PMD XML 规则(可以共用一个规则集文件)
- 最后创建 ESLint 规则(需要 npm/配置设置)
第 3 步:多条 PMD 规则可以共用一个规则集文件
创建多条 PMD 规则时,第一条规则使用 create-pmd-rule.js 脚本(该脚本会创建规则集 XML 文件)。对于后续要添加到同一个规则集文件中的 PMD 规则,可以手动添加到现有 XML 文件中——这是“绝不手动编辑”规则的唯一例外。该脚本始终会创建新文件;将规则添加到现有文件需要直接编辑 XML。
不要这样做
- 不要重写整个
code-analyzer.yml来重新组织配置 - 不要并行创建所有规则,然后一次性验证
- 不要使用未经验证、来自参考文档的 XPath 创建 PMD 规则——应对每种模式运行 ast-dump
- 不要在一个创建步骤中混用引擎类型(例如,同时创建 Regex 和 PMD 规则)
- 在文件尚不存在时,不要将
engines.eslint.eslint_config_file添加到code-analyzer.yml
高信号规则
| 规则 | 原因 |
|---|---|
规则名称必须匹配 /^[A-Za-z@][A-Za-z_0-9@\-/]*$/ | Code Analyzer 验证会拒绝其他名称 |
正则表达式必须采用 /pattern/flags 格式 | 必须使用 JavaScript 正则字面量表示法 |
文件扩展名必须以 . 开头 | 验证强制要求符合 /^([.][a-zA-Z0-9-_]+)+$/ |
| 创建后务必验证 | sf code-analyzer rules --rule-selector <engine>:<name> 可捕获配置错误 |
| 务必使用示例代码进行测试 | 在完整扫描之前捕获 XPath/正则表达式不匹配问题 |
在 XPath 中匹配方法调用时使用 @FullMethodName | 比单独使用 @Image 或 @MethodName 更可靠 |
绝不跳过 ast-dump | 运行 ast-dump,阅读输出,然后再编写 XPath。无一例外——即使模式看起来“显而易见”也不例外。不经 ast-dump 验证就凭记忆使用节点名称,是技能上的失误。 |
PMD 7 布尔属性:使用 = false() XPath 函数 | 在 PMD 7 中,@WithSharing、@Abstract、@Final 等布尔属性始终存在于节点上(不会缺失)。使用 = false()(XPath 布尔函数)进行比较,不要使用字符串 'false'。@WithSharing = false() 可行。@WithSharing='false'(字符串)不可行。not(@WithSharing) 不可行(该属性始终存在)。 |
项目根目录中的 code-analyzer.yml | CLI 会自动发现该文件——将其放在其他位置会导致规则作者遇到无提示的失败 |
XML 规则必须使用 local-name() | Salesforce 元数据命名空间会导致裸元素名称无法匹配 |
XML 文本匹配必须使用 @Text 属性 | text() 在 PMD 7 的 XML 语言中不起作用。使用 //*[@Text='value'] 匹配文本内容,然后通过 ../.. 向上导航到父元素。 |
@Text 位于子文本节点上,而非元素上 | //*[local-name()='apiVersion'][@Text < 60] → 错误(0 个匹配项)。//*[local-name()='apiVersion']/*[number(@Text) < 60] → 正确。/* 会导航到子文本节点。对于精确匹配模式,//*[@Text='value'] 可用,因为它会搜索所有节点,包括文本节点。但当你先用 local-name() 定位特定元素时,必须使用 /*[@Text...] 来访问其文本子节点。 |
XML 规则需要 file_extensions 配置 | PMD 默认只扫描 .xml 文件——在 engines.pmd 下添加 file_extensions: { xml: [".xml"] }。不要添加复合扩展名(.permissionset-meta.xml)——验证器会拒绝它们,而仅使用 .xml 已足以涵盖所有 Salesforce 元数据文件。 |
正则表达式中的命名捕获组 (?<target>...) | 将违规高亮范围缩小到仅捕获的部分 |
Salesforce ID 以 0 开头,长度恰好为 15 或 18 个字符 | 使用 /['"](?<target>0[a-zA-Z0-9]{14}(?:[a-zA-Z0-9]{3})?)['\"]/g——不要使用 {15,18},因为它也会匹配长度为 16 或 17 个字符的字符串,并对 'BusinessAccount' 等普通词语产生误报 |
regex_ignore 针对每一行,而非每个文件 | 它只会跳过匹配忽略模式的行。它不会排除整个文件或类。测试类第 10 行中的硬编码 ID 仍会被标记,除非该行本身包含忽略模式(例如 @isTest)。 |
| 始终使用脚本创建规则 | 不要手动编辑用于正则表达式规则的 code-analyzer.yml——YAML 中正则表达式里的引号和反斜杠会导致解析失败。create-regex-rule.js 脚本会正确处理序列化。 |
使用 <rule ref="..."> 覆盖内置规则 | 无需编写新规则即可更改阈值 |
| 绝不要对 JS/LWC/TS 文件使用 Regex | Regex 无法区分 JS 中的代码、注释和字符串——JavaScript 模式始终使用 ESLint |
| 编写自定义规则前先检查内置 ESLint 规则 | no-console、no-debugger、no-alert、eqeqeq、no-eval 等规则已存在——只需在配置中启用即可 |
易错点
| 问题 | 解决方法 |
|---|---|
| 未运行 ast-dump 就编写 XPath | 务必先运行 ast-dump。 即使规则有效,跳过 ast-dump 也属于流程失败。不同 PMD 版本中的节点名称会变化,无法靠猜测得出。 |
SOQL-in-loop 规则标记了 for (x : [SELECT...]) | 使用 //ForEachStatement/BlockStatement//SoqlExpression(限定在循环体范围内)。可迭代 SOQL 是 ForEachStatement 的直接子节点,与 BlockStatement 同级;//ForEachStatement//SoqlExpression 会匹配到它,造成误报。 |
| 用 Regex 检查 JS/LWC/TS 模式 | 绝不要对 JavaScript 文件使用 Regex。 Regex 无法区分 JS 中的代码、注释和字符串。始终使用 ESLint——先检查是否已有内置规则(例如 no-console)。 |
| XPath 返回 0 个匹配项(XML 元数据) | 常见原因有三种:(1) 忘记使用 local-name()——命名空间会导致裸元素名称无法匹配。(2) 使用了 text()='value'——在 PMD 7 中不起作用。改用 @Text='value'。(3) 将 [@Text] 谓词放在元素上——@Text 位于子文本节点上。使用 /*[@Text...] 来访问这些节点。 |
将 Regex 规则内联写入 code-analyzer.yml——导致 YAML 解析错误 | 务必使用 create-regex-rule.js——YAML 中的引号和反斜杠会导致转义失败。绝不要手动将 Regex 写入配置文件。 |
regex_ignore 未排除测试类 | regex_ignore 仅对单行有效。测试类第 50 行的 SOQL 查询会被标记,因为第 50 行不包含 @isTest。若要排除整个文件:使用 ignores.files(全局),或使用 PMD XPath [not(ancestor::UserClass[ModifierNode[@Test = true()]])](针对单条规则)。 |
XPath @WithSharing='false' 或 not(@WithSharing) 不起作用 | PMD 7 的布尔属性(@WithSharing、@Abstract、@Final)始终存在。字符串 ='false' 无法匹配(它是布尔值)。not(@attr) 不起作用(属性始终存在)。请使用 XPath 布尔函数:@WithSharing = false()。 |
基于循环的规则只覆盖 ForEachStatement | Apex 有 3 种循环类型:ForEachStatement、ForLoopStatement 和 WhileLoopStatement。XPath 中必须包含这 3 种类型——漏掉任何一种都会留下隐蔽的覆盖缺口。 |
脚本写入后又重写了 code-analyzer.yml——导致 YAML 解析错误 | 脚本运行后绝不要手动重写文件。 只需新增顶层配置块(例如 ignores.files)——不要改动脚本生成的 engines.regex.custom_rules 部分。 |
| 一次创建多条规则——一条失败会导致后续规则全部失败 | 逐条依次创建规则。 每条规则都完成完整工作流(创建 → 验证 → 测试)后,再开始下一条。 |
如需了解更多诊断信息(严重级别错误、找不到 Java、ESLint 配置路径、元数据文件类型范围等),请参阅 <skill_dir>/references/troubleshooting.md。
跨技能集成
| 需求 | 委派给 | 原因 |
|---|---|---|
| 创建规则后运行完整扫描 | dx-code-analyzer-run | 扫描执行和结果呈现 |
| 安装 Code Analyzer/修复先决条件 | dx-code-analyzer-configure | 设置和故障排除 |
| 说明现有内置规则 | dx-code-analyzer-run | 规则说明和文档查找 |
编辑 code-analyzer.yml 中的引擎设置 | dx-code-analyzer-configure | 配置管理 |
脚本执行
<skill_dir> 是包含此 SKILL.md 文件的目录的绝对路径。
所有脚本都放在与此 SKILL.md 文件同一目录下的 scripts/ 子目录中。请使用该目录的绝对路径——不要使用 ./scripts/,因为它相对于当前工作目录解析,而非技能目录。
node "<skill_dir>/scripts/create-regex-rule.js" \
--name "RuleName" --regex "/pattern/flags" ...
请勿:
- 自行编造或生成脚本代码
- 使用裸相对路径,例如
node scripts/create-regex-rule.js(无法从用户的当前工作目录解析) - 使用 heredoc 或内联脚本内容
- 跳过
<skill_dir>的解析——先找到其绝对路径
参考文件索引
| 文件 | 何时阅读 |
|---|---|
references/regex-rule-schema.md | 编写正则表达式规则时——完整字段参考、验证规则、多规则示例 |
references/xpath-patterns.md | 为 Apex 编写 XPath 时——模式类别索引,包含语法参考和 AST 节点词汇表 |
references/xpath-patterns-governor-limits.md | 用于循环中 SOQL/DML、循环中 Database 方法的 XPath 模式 |
references/xpath-patterns-method-calls.md | 用于禁止方法和注解模式(@AuraEnabled、@future、@IsTest)的 XPath 模式 |
references/xpath-patterns-security.md | 用于共享声明、SOQL 安全性、硬编码 ID 的 XPath 模式 |
references/xpath-patterns-structure.md | 用于代码结构、测试质量、命名约定的 XPath 模式 |
references/apex-ast-reference.md | 阅读 Apex AST 转储时——节点层级、修饰符属性、关键节点类型 |
references/metadata-xml-rules.md | 为元数据 XML 编写规则时——命名空间变通方案、常见结构、XPath 模式 |
references/advanced-pmd-patterns.md | 多规则规则集、覆盖内置规则、排除模式、Java 规则、跨项目共享 |
references/eslint-rules-discovery.md | 任何 ESLint 请求都请先阅读——发现工作流、内置规则、Tier 1-3 索引 |
references/eslint-tier2-configurable.md | ESLint Tier 2:no-restricted-globals、no-restricted-syntax、no-restricted-properties 模式 |
references/eslint-tier3-custom-plugins.md | ESLint Tier 3:何时创建自定义插件,以及涵盖所有 Tier 的完整示例 |
references/eslint-custom-plugins.md | 创建自定义 ESLint 插件——仅在发现工作流确认不存在内置或可配置规则后使用(Tier 3) |
references/troubleshooting.md | 验证或测试失败时——按引擎类型诊断错误 |
assets/pmd-ruleset-template.xml | PMD XML 骨架,包含供 create-pmd-rule 脚本使用的占位符 |
examples/regex-examples.md | 解决社区反馈问题的 6 个真实正则表达式规则 |
examples/xpath-examples.md | 6 个用于 Apex 的真实 XPath 规则,包含 AST 上下文和逐步创建说明 |
examples/metadata-xml-examples.md | 6 个真实元数据 XML 规则索引(权限、描述、API 版本、流) |
examples/metadata-xml-example-permissions.md | 元数据示例:ModifyAllData/ViewAllData、配置文件中的字段权限 |
examples/metadata-xml-example-fields-api.md | 元数据示例:自定义字段描述、最低 API 版本 |
examples/metadata-xml-example-flows.md | 元数据示例:流自动布局、流故障处理程序 |