dx-code-analyzer-custom-rule-create
Create custom Code Analyzer rules for Regex (pattern matching), PMD (XPath/AST for Apex and metadata XML), and ESLint (LWC/JavaScript/TypeScript). Use when users want to enforce coding standards, ban patterns, detect hardcoded values, govern metadata, or add rules not in the built-in set. TRIGGER wh
By forcedotcom · 4,588 installs
npx skills add forcedotcom/sf-skills --skill dx-code-analyzer-custom-rule-create
Source repository · Upstream listing
dx code analyzer custom rule create: Custom Code Analyzer Rule Authoring
Ecosystem: This skill is part of a 3 skill Code Analyzer suite — dx code analyzer run (scans & results) · dx code analyzer configure (setup, config, CI/CD) · dx code analyzer custom rule create (custom rule authoring).
Use this skill when the user needs to create a custom rule that enforces a pattern not covered by Code Analyzer's built in rules. Supports Regex engine (text pattern matching) and PMD engine (structural XPath queries against the AST).
When This Skill Owns the Task
Use dx code analyzer custom rule create when the work involves:
Creating a new custom rule for Code Analyzer (any engine)
Enforcing team specific coding standards via static analysis
Banning specific patterns (System.debug, hardcoded IDs, TODOs)
Writing XPath expressions for PMD rules (Apex or metadata XML)
Writing regex patterns for the Regex engine
Setting up custom ESLint rules/plugins for LWC/JavaScript
Enforcing metadata governance (API versions, field descriptions, dangerous permissions)
Overriding built in rule thresholds (CyclomaticComplexity, ExcessiveParameterList, etc.)
Organizing multiple rules into shared rulesets
Iterating on a custom rule that isn't matching correctly
Delegate elsewhere when the user is:
Running a scan against existing rules → dx code analyzer run skill
Configuring engines, prerequisites, CI/CD → dx code analyzer configure skill
Explaining what an existing built in rule means → dx code analyzer run skill
Writing Apex code or tests → generating apex / running apex tests skills
Required Context to Gather First
Ask for or infer:
What pattern to catch — what code should be flagged? (If user selected code in their IDE, the selection IS the answer — do not re ask.)
What to allow — any exceptions? (test classes, specific contexts)
File scope — which file types? (.cls, .trigger, .js, all?)
Severity — how critical? (default: 3/Moderate)
If the user selected code (IDE selection context present), treat it as the pattern definition. Skip clarification unless genuinely ambiguous about what aspect of the selection to target.
If the request is vague with NO selection ("add a rule for best practices"), ask ONE clarifying question:
"What specific pattern should this rule flag?"
Hard Constraints
These are non negotiable rules. Violating any of them is a skill failure regardless of whether the output happens to work.
1. ALWAYS run ast dump before writing XPath. No exceptions. Do not use node names from memory, references, or prior conversations. The AST is the source of truth — run sf code analyzer ast dump , read the output, then write XPath that matches what you see. Even for "well known" patterns like SOQL in loop, run ast dump first. If you skip this step and the rule works, it is still a process failure.
2. ALWAYS use the scripts to create rules. For regex rules, ALWAYS use create regex rule.js . For PMD rules, ALWAYS use create pmd rule.js . Do NOT manually edit code analyzer.yml to add rule definitions — regex patterns in YAML cause escaping failures (quotes inside quotes, backslashes getting eaten). The scripts handle YAML serialization correctly every time.
3. NEVER manually edit code analyzer.yml after a script writes to it — even to fix a bad value. The scripts produce correctly escaped YAML. If you then rewrite or restructure the file, you WILL break the escaping. If the user added top level config (like ignores.files ), leave it alone too — only touch what you wrote.
If a script's output looks wrong (rule fails to validate, YAML parse error, stray characters in the regex):
DO NOT patch the YAML by hand. That is exactly the failure mode this constraint exists to prevent.
Always delete the broken rule's entire YAML block, then re invoke the script with corrected arguments. Removing a block you just wrote does not violate this rule; rewriting fields inside it does.
If the script accepted bad input and produced bad output, the input was wrong (e.g., regex "/.../ g" with a stray space — the flags must be /g exactly, no whitespace). Re invoke with the corrected argument.
If you genuinely believe the script has a bug, STOP and surface it to the user. Do not hand edit as a workaround.
4. regex must be /pattern/flags with NO whitespace. The script trims and validates flags strictly — only g , i , m , s , u , y . /pat/ g (with a space) is rejected; so is /pat/x (invalid flag) and /pat/ (no flags). The global flag g is mandatory. If validation fails, fix the argument — do NOT bypass by writing YAML directly.
5. ALWAYS validate after creation. Run sf code analyzer rules rule selector <engine :<name before testing. If Found 0 rules , the YAML didn't parse — delete the block, fix the argument, re invoke the script.
6. ALWAYS test against a sample file. Confirm at least one true positive and one true negative.
For regex rules, the negative sample MUST NOT contain the pattern text anywhere — including inside comments and string literals. Regex engines scan raw text; // no System.debug here IS a match for /System\.debug/g . Trace your pattern against the negative file mentally before running it.
7. Create rules ONE AT A TIME, sequentially. When the user requests multiple rules, create each rule individually through the full workflow (create → validate → test positive → test negative) before starting the next one. Do NOT batch create rules — if one fails, it corrupts the config for all subsequent rules. Complete each rule end to end, confirm it works, then move to the next.
8. For regex rules, exclude test classes via ignores.files — regex ignore does NOT do this. regex ignore is a per LINE filter (the line must match BOTH the rule and the ignore pattern); it cannot exclude an entire test class. If the user's intent is "skip test classes," add a top level ignores.files block with globs like " / Test.cls" AFTER all rules are created — do not interleave config edits with script invocations.
Engine Selection
Pattern Type Engine Why
Text/string pattern (TODO, hardcoded ID, keyword) Regex Simple, fast, no Java needed
Apex code structure (method calls, nesting, SOQL in loops) PMD/XPath (language=apex) Understands AST, not fooled by comments/strings
Metadata XML governance (API version, permissions, descriptions) PMD/XPath (language=xml) Structural XML matching with namespace handling
LWC/JavaScript/TypeScript patterns ESLint Standard JS tooling, plugin ecosystem
Both could work (Apex/metadata only) Regex first Simpler to create and maintain
\ For ESLint: ALWAYS check Tier 1 (built in rules) and Tier 2 (configurable rules) BEFORE creating a custom plugin. See references/eslint rules discovery.md .
"Both could work → Regex first" NEVER applies to JavaScript/LWC/TypeScript files. JS/LWC/TS patterns MUST use ESLint — Regex cannot distinguish code from comments/strings in JS and produces false positives. Do NOT rationalize Regex for JS files based on "simplicity" or "no npm dependencies."
Tell the user which engine you chose and why. Respect their preference if they disagree.
Excluding Test Classes — Strategy by Engine
When a rule should NOT apply to test classes, the approach differs by engine:
Engine How to exclude test classes Notes
PMD (Apex) Add [not(ancestor::UserClass[ModifierNode[@Test = true()]])] to the XPath Structural exclusion — works perfectly, no config changes needed
Regex Use ignores.files in code analyzer.yml with globs like / Test.cls regex ignore is per LINE, not per FILE — it CANNOT exclude entire test classes. Only use regex ignore for per line patterns like // NOPMD
ESLint Use ignores array in eslint.config.js Standard ESLint file level ignores
regex ignore is NOT file level exclusion. It only skips matches on lines that ALSO match the ignore pattern. Example: regex ignore: "/@isTest/i" only suppresses violations on lines containing @isTest — a SOQL query on line 50 of a test class still flags because line 50 doesn't contain @isTest . To exclude test files from regex rules entirely, use:
ignores.files is GLOBAL — it affects ALL engines and ALL rules. If you need test class exclusion for some rules but not others (e.g., exclude tests from SOQL rules but still scan tests for @AuraEnabled), use PMD with XPath for the rules that need selective exclusion. PMD's XPath can structurally check @Test = true() per method or per class — Regex cannot.
Decision guide for Apex rules that should skip test classes:
If the pattern is structural (method calls, annotations, nesting) → use PMD. XPath handles test class exclusion natively.
If the pattern is purely textual AND all regex rules should skip tests → use Regex + ignores.files .
If you have a mix (some rules skip tests, others don't) → use PMD for the test sensitive rules, Regex for the others.
Workflow
When User Selects Code (IDE Selection)
When the user highlights a code block in their editor and asks to "catch this", "flag this pattern", "create a rule for this", or similar:
1. The selection IS your positive sample. Do NOT ask "what pattern should this rule flag?" — the user already showed you. Do NOT write a new sample from scratch.
2. Identify what's structural vs. incidental in the selection:
Structural (rule worthy): the method call, the loop pattern, the missing keyword, the nesting
Incidental (ignore): specific variable names, string values, parameter counts
Ask ONE question if ambiguous: "Should the rule catch all System.debug calls, or only those without a LoggingLevel parameter?"
3. Ast dump the ACTUAL file the user has open (not a new sample file):
4. Find the selection in the AST output — locate the nodes corresponding to the highlighted lines. These are your target nodes.
5. Generalize the XPath — write XPath that matches the structural pattern, NOT the specific instance. Replace specific variable names with wildcards, keep structural nodes and discriminating attributes.
6. Continue with standard workflow (negative sample, create rule, validate, test positive + negative).
Example flow:
User selects: Database.query('SELECT Id FROM ' + objectName)
Structural pattern: Database.query call (dynamic SOQL)
Incidental: the specific string concatenation inside
Engine: PMD (structural call detection)
XPath: //MethodCallExpression[@FullMethodName='Database.query']
NOT: regex matching Database.query (would miss multiline, match comments)
Example flow (block selection):
User selects a 5 line block with SOQL inside a for each loop
Structural: SOQL query as descendant of loop body
Incidental: specific query fields, variable names
Engine: PMD
Ast dump the open file → find ForEachStatement + SoqlExpression in body
XPath: //ForEachStatement/BlockStatement//SoqlExpression
For Regex Rules
1. Write a positive sample (5 10 lines) demonstrating the violation. Write sample files inside the project workspace (e.g., a temporary samples/ directory at the project root) so Code Analyzer can target them.
2. Write a SEPARATE negative sample file — code that looks similar but must NOT be flagged. Test your regex mentally against this file BEFORE creating the rule.
3. Build and create the rule — read references/regex rule schema.md for the complete schema, then run the script:
ALWAYS use the script. Do NOT manually write regex patterns into code analyzer.yml — regex characters (quotes, backslashes, braces) inside YAML cause parsing failures. The script handles serialization correctly.
4. Validate — sf code analyzer rules rule selector regex:<RuleName
5. Test positive — sf code analyzer run rule sel