@salesforce/afv-skills 1.28.0 → 1.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/package.json +1 -1
- package/skills/agentforce-architecture-analyze/README.md +2 -8
- package/skills/agentforce-architecture-analyze/SKILL.md +2 -2
- package/skills/agentforce-architecture-analyze/references/contract.json +4 -4
- package/skills/agentforce-architecture-analyze/references/soql_fields.md +1 -1
- package/skills/data360-activate/README.md +0 -1
- package/skills/data360-activate/SKILL.md +0 -2
- package/skills/data360-connect/README.md +0 -2
- package/skills/data360-connect/SKILL.md +0 -2
- package/skills/data360-harmonize/SKILL.md +0 -1
- package/skills/data360-orchestrate/README.md +0 -7
- package/skills/data360-orchestrate/SKILL.md +0 -2
- package/skills/data360-prepare/README.md +0 -1
- package/skills/data360-prepare/SKILL.md +0 -1
- package/skills/data360-query/README.md +0 -1
- package/skills/data360-query/SKILL.md +0 -1
- package/skills/data360-segment/README.md +0 -1
- package/skills/data360-segment/SKILL.md +0 -2
- package/skills/dx-code-analyzer-configure/SKILL.md +31 -13
- package/skills/dx-code-analyzer-custom-rule-create/SKILL.md +484 -0
- package/skills/dx-code-analyzer-custom-rule-create/assets/pmd-ruleset-template.xml +31 -0
- package/skills/dx-code-analyzer-custom-rule-create/examples/metadata-xml-example-fields-api.md +87 -0
- package/skills/dx-code-analyzer-custom-rule-create/examples/metadata-xml-example-flows.md +105 -0
- package/skills/dx-code-analyzer-custom-rule-create/examples/metadata-xml-example-permissions.md +95 -0
- package/skills/dx-code-analyzer-custom-rule-create/examples/metadata-xml-examples.md +84 -0
- package/skills/dx-code-analyzer-custom-rule-create/examples/regex-examples.md +127 -0
- package/skills/dx-code-analyzer-custom-rule-create/examples/xpath-examples.md +227 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/advanced-pmd-patterns.md +288 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/apex-ast-reference.md +127 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/eslint-custom-plugins.md +247 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/eslint-rules-discovery.md +188 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/eslint-tier2-configurable.md +114 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/eslint-tier3-custom-plugins.md +113 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/metadata-xml-rules.md +285 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/regex-rule-schema.md +174 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/troubleshooting.md +141 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-governor-limits.md +83 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-method-calls.md +108 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-security.md +45 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-structure.md +127 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns.md +131 -0
- package/skills/dx-code-analyzer-custom-rule-create/scripts/create-pmd-rule.js +209 -0
- package/skills/dx-code-analyzer-custom-rule-create/scripts/create-regex-rule.js +220 -0
- package/skills/dx-code-analyzer-run/SKILL.md +41 -8
- package/skills/experience-cms-brand-apply/SKILL.md +47 -16
- package/skills/experience-lwc-generate/README.md +3 -2
- package/skills/experience-lwc-generate/references/async-notification-patterns.md +1 -1
- package/skills/experience-lwc-generate/references/component-patterns.md +3 -3
- package/skills/experience-lwc-generate/references/flow-integration-guide.md +1 -1
- package/skills/experience-lwc-generate/references/lwc-best-practices.md +4 -4
- package/skills/experience-lwc-generate/references/scoring-and-testing.md +1 -1
- package/skills/experience-lwc-generate/references/state-management.md +2 -2
- package/skills/experience-lwc-generate/references/triangle-pattern.md +3 -3
- package/skills/experience-lwc-generate/scripts/post-tool-validate.py +2 -2
- package/skills/experience-lwc-generate/scripts/template_validator.py +1 -1
- package/skills/external-diagram-mermaid-generate/README.md +2 -2
- package/skills/external-diagram-mermaid-generate/SKILL.md +1 -3
- package/skills/external-diagram-mermaid-generate/assets/agentforce/agent-flow.md +4 -4
- package/skills/external-diagram-mermaid-generate/assets/architecture/system-landscape.md +1 -1
- package/skills/external-diagram-mermaid-generate/assets/datamodel/b2b-commerce-erd.md +3 -3
- package/skills/external-diagram-mermaid-generate/assets/datamodel/campaigns-erd.md +1 -1
- package/skills/external-diagram-mermaid-generate/assets/datamodel/consent-erd.md +2 -2
- package/skills/external-diagram-mermaid-generate/assets/datamodel/files-erd.md +2 -2
- package/skills/external-diagram-mermaid-generate/assets/datamodel/forecasting-erd.md +2 -2
- package/skills/external-diagram-mermaid-generate/assets/datamodel/fsl-erd.md +2 -2
- package/skills/external-diagram-mermaid-generate/assets/datamodel/party-model-erd.md +1 -1
- package/skills/external-diagram-mermaid-generate/assets/datamodel/quote-order-erd.md +3 -3
- package/skills/external-diagram-mermaid-generate/assets/datamodel/revenue-cloud-erd.md +2 -2
- package/skills/external-diagram-mermaid-generate/assets/datamodel/sales-cloud-erd.md +1 -1
- package/skills/external-diagram-mermaid-generate/assets/datamodel/salesforce-erd.md +1 -1
- package/skills/external-diagram-mermaid-generate/assets/datamodel/scheduler-erd.md +2 -2
- package/skills/external-diagram-mermaid-generate/assets/datamodel/service-cloud-erd.md +3 -3
- package/skills/external-diagram-mermaid-generate/assets/datamodel/territory-management-erd.md +1 -1
- package/skills/external-diagram-mermaid-generate/assets/integration/api-sequence.md +1 -1
- package/skills/external-diagram-mermaid-generate/assets/oauth/authorization-code-pkce.md +1 -1
- package/skills/external-diagram-mermaid-generate/assets/oauth/authorization-code.md +1 -1
- package/skills/external-diagram-mermaid-generate/assets/oauth/client-credentials.md +1 -1
- package/skills/external-diagram-mermaid-generate/assets/oauth/device-authorization.md +2 -2
- package/skills/external-diagram-mermaid-generate/assets/oauth/jwt-bearer.md +2 -2
- package/skills/external-diagram-mermaid-generate/assets/oauth/refresh-token.md +1 -1
- package/skills/external-diagram-mermaid-generate/assets/oauth/user-agent-social-sign-on.md +2 -2
- package/skills/external-diagram-mermaid-generate/assets/role-hierarchy/user-hierarchy.md +1 -1
- package/skills/external-diagram-mermaid-generate/references/color-palette.md +13 -13
- package/skills/external-diagram-mermaid-generate/references/diagram-conventions.md +5 -5
- package/skills/external-diagram-mermaid-generate/references/erd-conventions.md +1 -1
- package/skills/external-diagram-mermaid-generate/references/mermaid-styling.md +2 -2
- package/skills/external-diagram-mermaid-generate/references/preview-guide.md +1 -1
- package/skills/external-diagram-mermaid-generate/references/usage-examples.md +9 -9
- package/skills/external-diagram-mermaid-generate/scripts/README.md +1 -1
- package/skills/integration-connectivity-connected-app-configure/README.md +4 -5
- package/skills/integration-connectivity-connected-app-configure/SKILL.md +2 -2
- package/skills/integration-connectivity-connected-app-configure/references/example-usage.md +7 -7
- package/skills/integration-connectivity-connected-app-configure/references/migration-guide.md +1 -1
- package/skills/integration-connectivity-connected-app-configure/references/oauth-flows-reference.md +1 -1
- package/skills/integration-connectivity-connected-app-configure/references/testing-validation-guide.md +3 -3
- package/skills/integration-connectivity-generate/README.md +1 -1
- package/skills/integration-connectivity-generate/SKILL.md +6 -0
- package/skills/integration-connectivity-generate/assets/callouts/rest-queueable-callout.cls +1 -1
- package/skills/integration-connectivity-generate/assets/external-services/external-service-operations.md +1 -1
- package/skills/integration-connectivity-generate/assets/soap/wsdl2apex-guide.md +1 -1
- package/skills/integration-connectivity-generate/references/callout-patterns.md +4 -0
- package/skills/integration-connectivity-generate/references/cdc-guide.md +2 -2
- package/skills/integration-connectivity-generate/references/event-driven-architecture-guide.md +5 -5
- package/skills/integration-connectivity-generate/references/event-patterns.md +3 -2
- package/skills/integration-connectivity-generate/references/external-services-guide.md +4 -1
- package/skills/integration-connectivity-generate/references/messaging-api-v2.md +2 -2
- package/skills/integration-connectivity-generate/references/named-credentials-automation.md +1 -1
- package/skills/integration-connectivity-generate/references/named-credentials-guide.md +12 -2
- package/skills/integration-connectivity-generate/references/platform-events-guide.md +3 -3
- package/skills/integration-connectivity-generate/references/rest-callout-patterns.md +1 -1
- package/skills/integration-connectivity-generate/references/scoring-rubric.md +1 -1
- package/skills/integration-connectivity-generate/references/security-best-practices.md +5 -2
- package/skills/integration-connectivity-generate/scripts/configure-named-credential.sh +16 -5
- package/skills/integration-connectivity-generate/scripts/set-api-credential.sh +11 -2
- package/skills/integration-connectivity-generate/scripts/suggest_credential_setup.py +4 -3
- package/skills/integration-connectivity-generate/scripts/validate_integration.py +1 -1
- package/skills/mobile-platform-native-capabilities-integrate/SKILL.md +3 -3
- package/skills/omnistudio-callable-apex-generate/README.md +1 -5
- package/skills/omnistudio-callable-apex-generate/SKILL.md +14 -4
- package/skills/omnistudio-callable-apex-generate/assets/pattern_callable_vanilla.cls +8 -1
- package/skills/omnistudio-callable-apex-generate/examples/Test_VlocityOpenInterface2Conversion/TRANSCRIPT.md +1 -1
- package/skills/omnistudio-callable-apex-generate/examples/Test_VlocityOpenInterfaceConversion/TRANSCRIPT.md +1 -1
- package/skills/omnistudio-datamapper-generate/SKILL.md +1 -1
- package/skills/omnistudio-datamapper-generate/assets/completion-summary-template.md +2 -2
- package/skills/omnistudio-datamapper-generate/references/best-practices.md +9 -9
- package/skills/omnistudio-datamapper-generate/references/naming-conventions.md +6 -6
- package/skills/omnistudio-datapacks-deploy/README.md +1 -1
- package/skills/omnistudio-datapacks-deploy/examples/business-internet-plus-bundle/deploy-business-internet-plus-bundle.yaml +1 -1
- package/skills/omnistudio-dependencies-analyze/SKILL.md +12 -5
- package/skills/omnistudio-dependencies-analyze/references/dependency-patterns.md +9 -9
- package/skills/omnistudio-dependencies-analyze/references/namespace-guide.md +1 -1
- package/skills/omnistudio-epc-catalog-generate/README.md +1 -1
- package/skills/omnistudio-flexcard-generate/SKILL.md +11 -4
- package/skills/omnistudio-flexcard-generate/references/best-practices.md +6 -6
- package/skills/omnistudio-flexcard-generate/references/data-binding-guide.md +9 -9
- package/skills/omnistudio-integration-procedure-generate/SKILL.md +9 -1
- package/skills/omnistudio-integration-procedure-generate/references/element-types.md +3 -3
- package/skills/omnistudio-omniscript-generate/SKILL.md +2 -2
- package/skills/omnistudio-omniscript-generate/references/best-practices.md +1 -1
- package/skills/omnistudio-omniscript-generate/scripts/check-duplicate-omniscript.sh +8 -0
- package/skills/omnistudio-omniscript-generate/scripts/deploy-omniscript.sh +8 -0
- package/skills/platform-apex-logs-debug/README.md +2 -2
- package/skills/platform-apex-logs-debug/references/cli-commands.md +2 -2
- package/skills/platform-apex-logs-debug/references/debug-log-reference.md +16 -16
- package/skills/platform-apex-logs-debug/references/log-analysis-tools.md +2 -2
- package/skills/platform-apex-test-run/README.md +1 -1
- package/skills/platform-apex-test-run/references/test-fix-loop.md +2 -2
- package/skills/platform-apex-test-run/references/testing-best-practices.md +3 -3
- package/skills/platform-custom-field-generate/SKILL.md +86 -126
- package/skills/platform-custom-field-generate/references/advanced-picklists.md +590 -0
- package/skills/platform-data-manage/README.md +4 -4
- package/skills/platform-data-manage/references/bulk-testing-example.md +1 -1
- package/skills/platform-data-manage/references/crud-workflow-example.md +2 -2
- package/skills/platform-data-manage/references/orchestration.md +6 -6
- package/skills/platform-flexipage-generate/SKILL.md +281 -346
- package/skills/platform-flexipage-generate/references/cli_commands.md +39 -0
- package/skills/platform-flexipage-generate/references/flexipage_fieldSection.md +162 -0
- package/skills/platform-flexipage-generate/references/flexipage_richText.md +84 -0
- package/skills/platform-flexipage-generate/references/identifiers_and_regions.md +167 -0
- package/skills/platform-flexipage-generate/references/mcp_action_examples.md +104 -0
- package/skills/platform-flexipage-generate/references/record_flexipage_dynamicHighlights.md +119 -0
- package/skills/platform-flexipage-generate/references/xml_rules.md +335 -0
- package/skills/platform-flexipage-generate/scripts/resolve-component-instructions.sh +42 -0
- package/skills/platform-flexipage-generate/scripts/scan-lwc-components.sh +113 -0
- package/skills/platform-metadata-deploy/README.md +2 -2
- package/skills/platform-metadata-deploy/references/agent-deployment-guide.md +2 -2
- package/skills/platform-metadata-deploy/references/deployment-workflows.md +16 -16
- package/skills/platform-metadata-deploy/references/orchestration.md +3 -3
- package/skills/platform-metadata-deploy/references/trigger-deployment-safety.md +2 -2
- package/skills/platform-models-api-configure/SKILL.md +140 -0
- package/skills/platform-models-api-configure/scripts/get-orgjwt.sh +48 -0
- package/skills/platform-policy-rule-generate/SKILL.md +260 -0
- package/skills/platform-policy-rule-generate/references/deploy-errors.md +54 -0
- package/skills/platform-policy-rule-generate/references/fixtures-index.md +29 -0
- package/skills/platform-policy-rule-generate/references/policy-schema-full.md +250 -0
- package/skills/platform-policy-rule-generate/references/templates-access.md +132 -0
- package/skills/platform-policy-rule-generate/references/templates-advanced.md +58 -0
- package/skills/platform-policy-rule-generate/references/templates-record.md +75 -0
- package/skills/platform-policy-rule-generate/references/templates-transform.md +70 -0
- package/skills/platform-policy-rule-generate/references/templates.md +10 -0
- package/skills/platform-policy-rule-generate/references/ui-compatibility.md +142 -0
- package/skills/platform-soql-query/README.md +1 -1
- package/skills/platform-soql-query/SKILL.md +1 -1
- package/skills/platform-soql-query/references/anti-patterns.md +1 -1
- package/skills/platform-soql-query/references/field-coverage-rules.md +1 -1
- package/skills/platform-soql-query/references/query-optimization.md +1 -1
- package/skills/platform-soql-query/scripts/post-tool-validate.py +3 -3
- package/skills/platform-value-set-generate/SKILL.md +305 -0
- package/skills/data360-activate/CREDITS.md +0 -5
- package/skills/data360-connect/CREDITS.md +0 -5
- package/skills/data360-harmonize/CREDITS.md +0 -3
- package/skills/data360-orchestrate/CREDITS.md +0 -15
- package/skills/data360-orchestrate/UPSTREAM.md +0 -45
- package/skills/data360-prepare/CREDITS.md +0 -7
- package/skills/data360-query/CREDITS.md +0 -7
- package/skills/data360-segment/CREDITS.md +0 -3
- package/skills/experience-lwc-generate/CREDITS.md +0 -5
- package/skills/external-diagram-mermaid-generate/CREDITS.md +0 -46
- package/skills/external-diagram-visual-generate/CREDITS.md +0 -80
- package/skills/external-diagram-visual-generate/README.md +0 -83
- package/skills/external-diagram-visual-generate/SKILL.md +0 -207
- package/skills/external-diagram-visual-generate/assets/architecture/integration-flow.md +0 -55
- package/skills/external-diagram-visual-generate/assets/erd/core-objects.md +0 -131
- package/skills/external-diagram-visual-generate/assets/erd/custom-objects.md +0 -60
- package/skills/external-diagram-visual-generate/assets/lwc/dashboard-card.md +0 -45
- package/skills/external-diagram-visual-generate/assets/lwc/data-table.md +0 -57
- package/skills/external-diagram-visual-generate/assets/lwc/record-form.md +0 -60
- package/skills/external-diagram-visual-generate/assets/review/apex-review.md +0 -57
- package/skills/external-diagram-visual-generate/assets/review/lwc-review.md +0 -48
- package/skills/external-diagram-visual-generate/references/architect-aesthetic-guide.md +0 -257
- package/skills/external-diagram-visual-generate/references/examples-index.md +0 -35
- package/skills/external-diagram-visual-generate/references/gemini-cli-setup.md +0 -65
- package/skills/external-diagram-visual-generate/references/interview-questions.md +0 -529
- package/skills/external-diagram-visual-generate/references/iteration-workflow.md +0 -173
- package/skills/external-diagram-visual-generate/scripts/check-prerequisites.sh +0 -101
- package/skills/external-diagram-visual-generate/scripts/generate_image.py +0 -243
- package/skills/integration-connectivity-connected-app-configure/CREDITS.md +0 -3
- package/skills/integration-connectivity-generate/CREDITS.md +0 -5
- package/skills/omnistudio-callable-apex-generate/CREDITS.md +0 -9
- package/skills/omnistudio-datamapper-generate/CREDITS.md +0 -5
- package/skills/omnistudio-datapacks-deploy/CREDITS.md +0 -5
- package/skills/omnistudio-dependencies-analyze/CREDITS.md +0 -5
- package/skills/omnistudio-epc-catalog-generate/CREDITS.md +0 -14
- package/skills/omnistudio-flexcard-generate/CREDITS.md +0 -5
- package/skills/omnistudio-integration-procedure-generate/CREDITS.md +0 -5
- package/skills/omnistudio-omniscript-generate/CREDITS.md +0 -5
- package/skills/platform-apex-generate/CREDITS.md +0 -5
- package/skills/platform-apex-logs-debug/CREDITS.md +0 -22
- package/skills/platform-apex-test-generate/CREDITS.md +0 -5
- package/skills/platform-apex-test-run/CREDITS.md +0 -22
- package/skills/platform-data-manage/CREDITS.md +0 -5
- package/skills/platform-metadata-deploy/CREDITS.md +0 -25
- package/skills/platform-soql-query/CREDITS.md +0 -21
- /package/skills/omnistudio-callable-apex-generate/examples/Test_VlocityOpenInterfaceConversion/{MyCustomVlocityOpenInterface2.cls → MyCustomClass.cls} +0 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# ESLint Rules Discovery & Configuration
|
|
2
|
+
|
|
3
|
+
**READ THIS FIRST** for ANY ESLint request. 90% of ESLint rules already exist — this guide shows you how to find and configure them instead of creating custom plugins.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Three Tiers of ESLint Rules
|
|
8
|
+
|
|
9
|
+
| Tier | What It Is | When to Use | Effort | Coverage |
|
|
10
|
+
|---|---|---|---|---|
|
|
11
|
+
| **Tier 1: Built-In Rules** | 200+ core ESLint rules | First choice — check here FIRST | LOW (enable in config) | 70% of requests |
|
|
12
|
+
| **Tier 2: Configurable Rules** | no-restricted-syntax, no-restricted-globals, no-restricted-properties | When no built-in rule exists but pattern is generic | MEDIUM (AST selector) | 20% of requests |
|
|
13
|
+
| **Tier 3: Custom Plugins** | Write your own ESLint plugin | LAST RESORT — domain-specific multi-node patterns only | HIGH (Node code, testing) | 10% of requests |
|
|
14
|
+
|
|
15
|
+
**Always start at Tier 1 and work down.**
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Tier 1: Discover Built-In Rules
|
|
20
|
+
|
|
21
|
+
### Discovery Workflow (MANDATORY)
|
|
22
|
+
|
|
23
|
+
Before creating ANY custom ESLint rule:
|
|
24
|
+
|
|
25
|
+
1. **Run:** `sf code-analyzer rules --rule-selector eslint`
|
|
26
|
+
2. **Search output** for keywords from the user's request:
|
|
27
|
+
- User says "ban console.log" → search for "console"
|
|
28
|
+
- User says "enforce ===" → search for "equal" or "strict"
|
|
29
|
+
- User says "no unused variables" → search for "unused"
|
|
30
|
+
3. **Check naming patterns:**
|
|
31
|
+
- `no-*` — Disallow something (no-console, no-debugger, no-eval)
|
|
32
|
+
- `prefer-*` — Prefer one style (prefer-const, prefer-arrow-callback)
|
|
33
|
+
- `require-*` — Require something (require-await, require-yield)
|
|
34
|
+
- `@lwc/lwc/*` — LWC-specific rules (no-inner-html, no-document-query)
|
|
35
|
+
4. **If found:** Configure it (see "Configuration Workflow" below). **STOP — do NOT create a custom plugin.**
|
|
36
|
+
5. **If NOT found:** Proceed to Tier 2.
|
|
37
|
+
|
|
38
|
+
### Common Built-In Rules by Category
|
|
39
|
+
|
|
40
|
+
#### Code Quality
|
|
41
|
+
| User Request | Rule Name | Config Example |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| "No unused variables" | `no-unused-vars` | `"no-unused-vars": "error"` |
|
|
44
|
+
| "No unused imports" | `no-unused-vars` | Same rule covers imports |
|
|
45
|
+
| "Require await in async functions" | `require-await` | `"require-await": "error"` |
|
|
46
|
+
| "No empty blocks" | `no-empty` | `"no-empty": "error"` |
|
|
47
|
+
| "No unreachable code" | `no-unreachable` | `"no-unreachable": "error"` |
|
|
48
|
+
|
|
49
|
+
#### Security
|
|
50
|
+
| User Request | Rule Name | Config Example |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| "Ban eval()" | `no-eval` | `"no-eval": "error"` |
|
|
53
|
+
| "Ban debugger" | `no-debugger` | `"no-debugger": "error"` |
|
|
54
|
+
| "No implied eval" | `no-implied-eval` | `"no-implied-eval": "error"` |
|
|
55
|
+
| "Ban alert()" | `no-alert` | `"no-alert": "error"` |
|
|
56
|
+
|
|
57
|
+
#### Best Practices
|
|
58
|
+
| User Request | Rule Name | Config Example |
|
|
59
|
+
|---|---|---|
|
|
60
|
+
| "Enforce ===" | `eqeqeq` | `"eqeqeq": ["error", "always"]` |
|
|
61
|
+
| "Prefer const" | `prefer-const` | `"prefer-const": "error"` |
|
|
62
|
+
| "No var" | `no-var` | `"no-var": "error"` |
|
|
63
|
+
| "Prefer arrow functions" | `prefer-arrow-callback` | `"prefer-arrow-callback": "error"` |
|
|
64
|
+
| "Require default in switch" | `default-case` | `"default-case": "error"` |
|
|
65
|
+
|
|
66
|
+
#### Logging/Debugging
|
|
67
|
+
| User Request | Rule Name | Config Example |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| "Ban console.log" | `no-console` | `"no-console": "error"` |
|
|
70
|
+
| "Ban console.* except error" | `no-console` | `"no-console": ["error", { "allow": ["error", "warn"] }]` |
|
|
71
|
+
|
|
72
|
+
### LWC Plugin Rules (Check if Available)
|
|
73
|
+
|
|
74
|
+
If Code Analyzer has `@lwc/eslint-plugin-lwc` enabled:
|
|
75
|
+
|
|
76
|
+
| User Request | Rule Name | Notes |
|
|
77
|
+
|---|---|---|
|
|
78
|
+
| "Ban innerHTML" | `@lwc/lwc/no-inner-html` | XSS prevention |
|
|
79
|
+
| "No document.querySelector" | `@lwc/lwc/no-document-query` | Use template queries |
|
|
80
|
+
| "Validate @api usage" | `@lwc/lwc/no-api-reassignments` | Prevents reassigning @api properties |
|
|
81
|
+
| "Validate @wire syntax" | `@lwc/lwc/valid-wire` | Built-in |
|
|
82
|
+
| "No async in getters" | `@lwc/lwc/no-async-operation` | Lifecycle hook validation |
|
|
83
|
+
|
|
84
|
+
**Check availability:**
|
|
85
|
+
```bash
|
|
86
|
+
sf code-analyzer rules --rule-selector eslint | grep -i "@lwc"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### External Documentation
|
|
90
|
+
|
|
91
|
+
- **All ESLint built-in rules:** https://eslint.org/docs/latest/rules/
|
|
92
|
+
- **LWC ESLint plugin:** https://github.com/salesforce/eslint-plugin-lwc
|
|
93
|
+
- **Salesforce Lightning plugin:** https://github.com/forcedotcom/eslint-plugin-lightning
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Configuration Workflow (Tier 1)
|
|
98
|
+
|
|
99
|
+
When a built-in rule exists:
|
|
100
|
+
|
|
101
|
+
1. **Create `eslint.config.js`** if it doesn't exist:
|
|
102
|
+
```javascript
|
|
103
|
+
module.exports = [
|
|
104
|
+
{
|
|
105
|
+
files: ["**/lwc/**/*.js"], // or ["**/*.js"] for all JS files
|
|
106
|
+
rules: {
|
|
107
|
+
// Rules go here
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
];
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
2. **Add the rule** with desired severity:
|
|
114
|
+
```javascript
|
|
115
|
+
rules: {
|
|
116
|
+
"no-console": "error", // Ban completely
|
|
117
|
+
"eqeqeq": ["error", "always"], // With options
|
|
118
|
+
"no-unused-vars": ["warn"] // Warning instead of error
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
3. **Update `code-analyzer.yml`** (AFTER file exists):
|
|
123
|
+
```yaml
|
|
124
|
+
engines:
|
|
125
|
+
eslint:
|
|
126
|
+
eslint_config_file: "eslint.config.js"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
4. **Validate:**
|
|
130
|
+
```bash
|
|
131
|
+
sf code-analyzer rules --rule-selector eslint:no-console
|
|
132
|
+
```
|
|
133
|
+
If the rule does NOT appear in the output, the config is wrong. Do NOT proceed to testing.
|
|
134
|
+
|
|
135
|
+
5. **Test positive:**
|
|
136
|
+
```bash
|
|
137
|
+
sf code-analyzer run --rule-selector eslint:no-console --target lwc/
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
6. **Test negative:** Run against clean code, confirm 0 violations.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Tier 2 and Tier 3: Configurable Rules and Custom Plugins
|
|
145
|
+
|
|
146
|
+
For detailed information on these tiers:
|
|
147
|
+
|
|
148
|
+
| Tier | File | When to Use |
|
|
149
|
+
|------|------|-------------|
|
|
150
|
+
| **Tier 2: Configurable Rules** | [eslint-tier2-configurable.md](eslint-tier2-configurable.md) | no-restricted-globals, no-restricted-syntax, no-restricted-properties — ban specific patterns without writing plugins |
|
|
151
|
+
| **Tier 3: Custom Plugins** | [eslint-tier3-custom-plugins.md](eslint-tier3-custom-plugins.md) | Complete examples for all tiers + when to create custom plugins (LAST RESORT) |
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Decision Tree
|
|
156
|
+
|
|
157
|
+
```text
|
|
158
|
+
User asks for ESLint rule
|
|
159
|
+
↓
|
|
160
|
+
Run: sf code-analyzer rules --rule-selector eslint
|
|
161
|
+
↓
|
|
162
|
+
Search output for keywords
|
|
163
|
+
↓
|
|
164
|
+
├─ Built-in rule found? → Configure it (Tier 1) → DONE ✅
|
|
165
|
+
│
|
|
166
|
+
├─ Pattern is "ban function X"? → Use no-restricted-globals (Tier 2) → DONE ✅
|
|
167
|
+
│
|
|
168
|
+
├─ Pattern is "ban syntax Y"? → Use no-restricted-syntax (Tier 2) → DONE ✅
|
|
169
|
+
│
|
|
170
|
+
└─ Complex multi-node pattern? → Create custom plugin (Tier 3) → See eslint-custom-plugins.md
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Key Rules
|
|
176
|
+
|
|
177
|
+
1. **ALWAYS run discovery FIRST** — 90% of requests are Tier 1 or Tier 2
|
|
178
|
+
2. **NEVER create a custom plugin without checking built-in rules** — this is a skill failure
|
|
179
|
+
3. **VALIDATE after configuration** — `sf code-analyzer rules --rule-selector eslint:<name>` must show the rule
|
|
180
|
+
4. **TEST both positive and negative samples** — confirm violations are caught AND clean code passes
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## When to Read Other References
|
|
185
|
+
|
|
186
|
+
- **All ESLint requests start here** (discovery)
|
|
187
|
+
- **If Tier 3 custom plugin needed:** Read `references/eslint-custom-plugins.md`
|
|
188
|
+
- **If troubleshooting config issues:** Read `references/troubleshooting.md` (ESLint section)
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# ESLint Tier 2: Configurable Rules
|
|
2
|
+
|
|
3
|
+
[← Back to ESLint Rules Discovery](eslint-rules-discovery.md)
|
|
4
|
+
|
|
5
|
+
## Tier 2: Configurable Rules
|
|
6
|
+
|
|
7
|
+
When no built-in rule exists but the pattern is generic (ban a function, ban a property, ban a syntax construct):
|
|
8
|
+
|
|
9
|
+
### Option A: `no-restricted-globals`
|
|
10
|
+
|
|
11
|
+
Ban specific global functions or variables.
|
|
12
|
+
|
|
13
|
+
**Example: Ban setTimeout/setInterval**
|
|
14
|
+
```javascript
|
|
15
|
+
// eslint.config.js
|
|
16
|
+
module.exports = [
|
|
17
|
+
{
|
|
18
|
+
files: ["**/lwc/**/*.js"],
|
|
19
|
+
rules: {
|
|
20
|
+
"no-restricted-globals": ["error",
|
|
21
|
+
{
|
|
22
|
+
name: "setTimeout",
|
|
23
|
+
message: "Use LWC lifecycle hooks instead of setTimeout"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: "setInterval",
|
|
27
|
+
message: "Use LWC lifecycle hooks instead of setInterval"
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
];
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Example: Ban window.localStorage**
|
|
36
|
+
```javascript
|
|
37
|
+
rules: {
|
|
38
|
+
"no-restricted-globals": ["error",
|
|
39
|
+
{
|
|
40
|
+
name: "localStorage",
|
|
41
|
+
message: "Use Salesforce state management instead of localStorage"
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Option B: `no-restricted-syntax`
|
|
48
|
+
|
|
49
|
+
Ban specific AST patterns using ESLint selectors.
|
|
50
|
+
|
|
51
|
+
**Example: Ban eval() calls**
|
|
52
|
+
```javascript
|
|
53
|
+
rules: {
|
|
54
|
+
"no-restricted-syntax": ["error",
|
|
55
|
+
{
|
|
56
|
+
selector: "CallExpression[callee.name='eval']",
|
|
57
|
+
message: "eval() is forbidden for security reasons"
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Example: Ban innerHTML property access**
|
|
64
|
+
```javascript
|
|
65
|
+
rules: {
|
|
66
|
+
"no-restricted-syntax": ["error",
|
|
67
|
+
{
|
|
68
|
+
selector: "MemberExpression[property.name='innerHTML']",
|
|
69
|
+
message: "innerHTML is forbidden - use textContent or render() for XSS prevention"
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Example: Ban for-in loops**
|
|
76
|
+
```javascript
|
|
77
|
+
rules: {
|
|
78
|
+
"no-restricted-syntax": ["error",
|
|
79
|
+
{
|
|
80
|
+
selector: "ForInStatement",
|
|
81
|
+
message: "for-in loops are forbidden - use for-of or Object.keys()"
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
#### ESLint Selector Syntax Cheat Sheet
|
|
88
|
+
|
|
89
|
+
| Pattern | Selector | Example |
|
|
90
|
+
|---|---|---|
|
|
91
|
+
| Function call | `CallExpression[callee.name='functionName']` | `eval()`, `alert()` |
|
|
92
|
+
| Property access | `MemberExpression[property.name='propName']` | `obj.innerHTML` |
|
|
93
|
+
| Statement type | `ForInStatement`, `WithStatement` | for-in, with statements |
|
|
94
|
+
| Operator | `BinaryExpression[operator='==']` | `==` instead of `===` |
|
|
95
|
+
| Literal value | `Literal[value='string']` | Specific string literals |
|
|
96
|
+
|
|
97
|
+
**Full selector docs:** https://eslint.org/docs/latest/extend/selectors
|
|
98
|
+
|
|
99
|
+
### Option C: `no-restricted-properties`
|
|
100
|
+
|
|
101
|
+
Ban specific object properties.
|
|
102
|
+
|
|
103
|
+
**Example: Ban Object.prototype methods**
|
|
104
|
+
```javascript
|
|
105
|
+
rules: {
|
|
106
|
+
"no-restricted-properties": ["error",
|
|
107
|
+
{
|
|
108
|
+
object: "Object",
|
|
109
|
+
property: "setPrototypeOf",
|
|
110
|
+
message: "setPrototypeOf is forbidden for performance reasons"
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
```
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# ESLint Tier 3: Custom Plugins
|
|
2
|
+
|
|
3
|
+
[← Back to ESLint Rules Discovery](eslint-rules-discovery.md)
|
|
4
|
+
|
|
5
|
+
## Tier 3: Custom Plugins (LAST RESORT)
|
|
6
|
+
|
|
7
|
+
Only create a custom ESLint plugin when:
|
|
8
|
+
- ❌ No built-in rule exists (checked Tier 1)
|
|
9
|
+
- ❌ No configurable rule can express the pattern (checked Tier 2 — see [eslint-tier2-configurable.md](eslint-tier2-configurable.md))
|
|
10
|
+
- ✅ The pattern requires checking multiple AST nodes with complex logic
|
|
11
|
+
- ✅ The pattern is domain-specific to your codebase
|
|
12
|
+
|
|
13
|
+
**Examples that justify custom plugins:**
|
|
14
|
+
- "Flag LWC components with @api properties but no input validation in connectedCallback"
|
|
15
|
+
- "Detect imperative Apex calls without error handling (import + .then() without .catch())"
|
|
16
|
+
- "Enforce naming conventions on specific decorator patterns"
|
|
17
|
+
|
|
18
|
+
**See:** [eslint-custom-plugins.md](eslint-custom-plugins.md) for the plugin creation guide.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Tier-by-Tier Worked Examples
|
|
23
|
+
|
|
24
|
+
Tier 2 worked examples (banning globals, restricting syntax, restricting properties) live in [eslint-tier2-configurable.md](eslint-tier2-configurable.md). The examples below cover Tier 1 (built-in rules and the LWC plugin) so the discovery → configure → validate flow is documented end-to-end.
|
|
25
|
+
|
|
26
|
+
### Example 1: "Ban console.log" (Tier 1 — built-in)
|
|
27
|
+
|
|
28
|
+
**User Request:** "Create a rule to ban console.log in LWC"
|
|
29
|
+
|
|
30
|
+
**Discovery:**
|
|
31
|
+
```bash
|
|
32
|
+
$ sf code-analyzer rules --rule-selector eslint | grep -i console
|
|
33
|
+
eslint:no-console disallow the use of console
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Result:** Built-in rule exists. ✅
|
|
37
|
+
|
|
38
|
+
**Configuration:**
|
|
39
|
+
```javascript
|
|
40
|
+
// eslint.config.js
|
|
41
|
+
module.exports = [
|
|
42
|
+
{
|
|
43
|
+
files: ["**/lwc/**/*.js"],
|
|
44
|
+
rules: {
|
|
45
|
+
"no-console": "error"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
];
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Validation:**
|
|
52
|
+
```bash
|
|
53
|
+
sf code-analyzer rules --rule-selector eslint:no-console
|
|
54
|
+
# Should show: eslint:no-console | error | ...
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Test:**
|
|
58
|
+
```bash
|
|
59
|
+
sf code-analyzer run --rule-selector eslint:no-console --target lwc/
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
### Example 2: "Ban innerHTML" (Tier 1 — LWC plugin)
|
|
65
|
+
|
|
66
|
+
**User Request:** "Ban innerHTML in LWC for XSS prevention"
|
|
67
|
+
|
|
68
|
+
**Discovery:**
|
|
69
|
+
```bash
|
|
70
|
+
$ sf code-analyzer rules --rule-selector eslint | grep -i inner
|
|
71
|
+
@lwc/lwc/no-inner-html disallow use of innerHTML
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Result:** LWC plugin rule exists. ✅
|
|
75
|
+
|
|
76
|
+
**Configuration:**
|
|
77
|
+
```javascript
|
|
78
|
+
// eslint.config.js
|
|
79
|
+
module.exports = [
|
|
80
|
+
{
|
|
81
|
+
files: ["**/lwc/**/*.js"],
|
|
82
|
+
rules: {
|
|
83
|
+
"@lwc/lwc/no-inner-html": "error"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
];
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Validation:**
|
|
90
|
+
```bash
|
|
91
|
+
sf code-analyzer rules --rule-selector eslint:@lwc/lwc/no-inner-html
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
### Example 3: "Enforce === over ==" (Tier 1 — built-in)
|
|
97
|
+
|
|
98
|
+
**User Request:** "Enforce strict equality checks"
|
|
99
|
+
|
|
100
|
+
**Discovery:**
|
|
101
|
+
```bash
|
|
102
|
+
$ sf code-analyzer rules --rule-selector eslint | grep -i equal
|
|
103
|
+
eslint:eqeqeq require the use of === and !==
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Result:** Built-in rule `eqeqeq` exists. ✅
|
|
107
|
+
|
|
108
|
+
**Configuration:**
|
|
109
|
+
```javascript
|
|
110
|
+
rules: {
|
|
111
|
+
"eqeqeq": ["error", "always"]
|
|
112
|
+
}
|
|
113
|
+
```
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# Metadata XML Rules (PMD with language="xml")
|
|
2
|
+
|
|
3
|
+
Write PMD XPath rules that target Salesforce metadata XML files (`.field-meta.xml`, `.permissionset-meta.xml`, `.profile-meta.xml`, `.flow-meta.xml`, etc.) to enforce org governance.
|
|
4
|
+
|
|
5
|
+
## How It Works
|
|
6
|
+
|
|
7
|
+
PMD's `language="xml"` mode parses any XML file into a DOM-like AST. You write XPath expressions against element/attribute names in the XML structure — same mechanism as Apex rules, but the AST is the XML DOM.
|
|
8
|
+
|
|
9
|
+
## Critical: PMD 7 XML XPath Rules
|
|
10
|
+
|
|
11
|
+
### Rule 1: Namespace — use `local-name()`
|
|
12
|
+
|
|
13
|
+
Salesforce metadata files declare a namespace:
|
|
14
|
+
```xml
|
|
15
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
16
|
+
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
17
|
+
<fullName>My_Field__c</fullName>
|
|
18
|
+
...
|
|
19
|
+
</CustomField>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
This namespace **breaks standard XPath** because `//fullName` won't match — PMD sees it as `//metadata:fullName` internally.
|
|
23
|
+
|
|
24
|
+
**Solution:** Use `local-name()` to match element names regardless of namespace:
|
|
25
|
+
|
|
26
|
+
```xpath
|
|
27
|
+
//*[local-name()='CustomField']/*[local-name()='fullName']
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**This applies to ALL Salesforce metadata XPath rules.** Never use bare element names without `local-name()`.
|
|
31
|
+
|
|
32
|
+
### Rule 2: Text content — use `@Text` (NOT `text()`)
|
|
33
|
+
|
|
34
|
+
⚠️ **PMD 7's XML language does NOT support `text()` for value comparison.** The `text()` function returns 0 matches regardless of content. This is a PMD 7 change from PMD 6.
|
|
35
|
+
|
|
36
|
+
**Use the `@Text` attribute instead:**
|
|
37
|
+
|
|
38
|
+
```xpath
|
|
39
|
+
// ❌ WRONG — does not work in PMD 7:
|
|
40
|
+
//*[local-name()='name'][text()='ModifyAllData']
|
|
41
|
+
|
|
42
|
+
// ✅ CORRECT — use @Text attribute:
|
|
43
|
+
//*[@Text='ModifyAllData']
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Rule 3: Navigation — text nodes require `../..`
|
|
47
|
+
|
|
48
|
+
In PMD 7 XML, `@Text` matches TEXT NODES (child nodes of elements), not the elements themselves. To navigate from a matched text node to its parent element hierarchy:
|
|
49
|
+
|
|
50
|
+
```xpath
|
|
51
|
+
// @Text='ModifyAllData' matches the TEXT NODE inside <name>ModifyAllData</name>
|
|
52
|
+
// To get the <name> element: go up one level
|
|
53
|
+
//*[@Text='ModifyAllData']/..
|
|
54
|
+
|
|
55
|
+
// To get the <userPermissions> parent: go up two levels
|
|
56
|
+
//*[@Text='ModifyAllData']/../..
|
|
57
|
+
|
|
58
|
+
// To check a sibling's text value from the parent:
|
|
59
|
+
//*[@Text='ModifyAllData']/../..[.//*[@Text='true']]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Complete Pattern for Checking Parent + Sibling Text Values
|
|
63
|
+
|
|
64
|
+
The canonical PMD 7 pattern for "find element X that contains child with text A AND child with text B":
|
|
65
|
+
|
|
66
|
+
```xpath
|
|
67
|
+
//*[@Text='TargetValue']/../..
|
|
68
|
+
[local-name()='ParentElement']
|
|
69
|
+
[.//*[@Text='ConditionValue']]
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Example — find `userPermissions` where name=ModifyAllData AND enabled=true:
|
|
73
|
+
```xpath
|
|
74
|
+
//*[@Text='ModifyAllData']/../..
|
|
75
|
+
[local-name()='userPermissions']
|
|
76
|
+
[.//*[@Text='true']]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## AST Dump for Metadata Files
|
|
80
|
+
|
|
81
|
+
Dump the AST to see the exact XML structure:
|
|
82
|
+
```bash
|
|
83
|
+
sf code-analyzer ast-dump --file force-app/main/default/objects/Account/fields/MyField__c.field-meta.xml --language xml
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The output shows the DOM tree — element nodes, text nodes, and attributes. Use this to confirm element names before writing XPath.
|
|
87
|
+
|
|
88
|
+
## Ruleset XML Template for Metadata Rules
|
|
89
|
+
|
|
90
|
+
```xml
|
|
91
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
92
|
+
<ruleset name="MetadataGovernanceRules"
|
|
93
|
+
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
|
|
94
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
95
|
+
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
|
|
96
|
+
|
|
97
|
+
<description>Custom rules for Salesforce metadata governance</description>
|
|
98
|
+
|
|
99
|
+
<rule name="MyMetadataRule"
|
|
100
|
+
language="xml"
|
|
101
|
+
message="Violation message here"
|
|
102
|
+
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule">
|
|
103
|
+
|
|
104
|
+
<description>What this rule enforces</description>
|
|
105
|
+
<priority>3</priority>
|
|
106
|
+
|
|
107
|
+
<properties>
|
|
108
|
+
<property name="xpath">
|
|
109
|
+
<value><![CDATA[
|
|
110
|
+
//*[local-name()='YourElement'][your-condition]
|
|
111
|
+
]]></value>
|
|
112
|
+
</property>
|
|
113
|
+
</properties>
|
|
114
|
+
</rule>
|
|
115
|
+
</ruleset>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Configuration
|
|
119
|
+
|
|
120
|
+
```yaml
|
|
121
|
+
# code-analyzer.yml
|
|
122
|
+
engines:
|
|
123
|
+
pmd:
|
|
124
|
+
custom_rulesets:
|
|
125
|
+
- "custom-rules/metadata-governance.xml"
|
|
126
|
+
file_extensions:
|
|
127
|
+
xml: [".xml"]
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
⚠️ **IMPORTANT:** PMD's XML language only scans `.xml` files by default. You MUST add `file_extensions: { xml: [".xml"] }` under `engines.pmd` in your config. This single entry covers ALL Salesforce metadata compound extensions (`.permissionset-meta.xml`, `.field-meta.xml`, `.flow-meta.xml`, etc.) because the system reads the final `.xml` portion of the filename.
|
|
131
|
+
|
|
132
|
+
⚠️ **Do NOT add compound extensions** like `.permissionset-meta.xml` to the list — Code Analyzer's validator only accepts single-segment extensions matching `/^[.][a-zA-Z0-9]+$/` and will reject them with a config error.
|
|
133
|
+
|
|
134
|
+
## Common Metadata File Structures
|
|
135
|
+
|
|
136
|
+
### Custom Field (`.field-meta.xml`)
|
|
137
|
+
```xml
|
|
138
|
+
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
139
|
+
<fullName>Revenue__c</fullName>
|
|
140
|
+
<label>Revenue</label>
|
|
141
|
+
<type>Currency</type>
|
|
142
|
+
<required>false</required>
|
|
143
|
+
<description>Annual revenue</description>
|
|
144
|
+
</CustomField>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Permission Set (`.permissionset-meta.xml`)
|
|
148
|
+
```xml
|
|
149
|
+
<PermissionSet xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
150
|
+
<label>Admin Access</label>
|
|
151
|
+
<userPermissions>
|
|
152
|
+
<enabled>true</enabled>
|
|
153
|
+
<name>ModifyAllData</name>
|
|
154
|
+
</userPermissions>
|
|
155
|
+
<fieldPermissions>
|
|
156
|
+
<editable>true</editable>
|
|
157
|
+
<field>Account.Revenue__c</field>
|
|
158
|
+
<readable>true</readable>
|
|
159
|
+
</fieldPermissions>
|
|
160
|
+
</PermissionSet>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Profile (`.profile-meta.xml`)
|
|
164
|
+
```xml
|
|
165
|
+
<Profile xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
166
|
+
<userPermissions>
|
|
167
|
+
<enabled>true</enabled>
|
|
168
|
+
<name>ViewSetup</name>
|
|
169
|
+
</userPermissions>
|
|
170
|
+
<fieldPermissions>
|
|
171
|
+
<editable>true</editable>
|
|
172
|
+
<field>Account.SSN__c</field>
|
|
173
|
+
</fieldPermissions>
|
|
174
|
+
</Profile>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Flow (`.flow-meta.xml`)
|
|
178
|
+
```xml
|
|
179
|
+
<Flow xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
180
|
+
<apiVersion>58.0</apiVersion>
|
|
181
|
+
<label>My Flow</label>
|
|
182
|
+
<processType>AutoLaunchedFlow</processType>
|
|
183
|
+
<status>Active</status>
|
|
184
|
+
<processMetadataValues>
|
|
185
|
+
<name>CanvasMode</name>
|
|
186
|
+
<value><stringValue>AUTO_LAYOUT_CANVAS</stringValue></value>
|
|
187
|
+
</processMetadataValues>
|
|
188
|
+
<recordCreates>
|
|
189
|
+
<name>Create_Account</name>
|
|
190
|
+
<object>Account</object>
|
|
191
|
+
</recordCreates>
|
|
192
|
+
<loops>
|
|
193
|
+
<name>Loop_Records</name>
|
|
194
|
+
<collectionReference>Get_Records</collectionReference>
|
|
195
|
+
</loops>
|
|
196
|
+
</Flow>
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## XPath Patterns for Metadata (PMD 7)
|
|
200
|
+
|
|
201
|
+
All patterns below use `@Text` for text content matching (NOT `text()` which is broken in PMD 7).
|
|
202
|
+
|
|
203
|
+
### Require description on Custom Fields
|
|
204
|
+
|
|
205
|
+
**Missing description entirely:**
|
|
206
|
+
```xpath
|
|
207
|
+
//*[local-name()='CustomField'][not(*[local-name()='description'])]
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**Missing OR empty description (catches `<description></description>` too):**
|
|
211
|
+
```xpath
|
|
212
|
+
//*[local-name()='CustomField'][not(*[local-name()='description']) or *[local-name()='description'][not(.//*[@Text])]]
|
|
213
|
+
```
|
|
214
|
+
Note: `.//*[@Text]` checks for any descendant text node with content. Do NOT put `[not(@Text)]` directly on the `<description>` element — `@Text` only exists on child text nodes, not on elements themselves. Putting it on the element always evaluates to true (false positive).
|
|
215
|
+
|
|
216
|
+
### Flag ModifyAllData / ViewAllData in Permission Sets
|
|
217
|
+
```xpath
|
|
218
|
+
//*[@Text='ModifyAllData' or @Text='ViewAllData']/../..
|
|
219
|
+
[local-name()='userPermissions']
|
|
220
|
+
[.//*[@Text='true']]
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Enforce minimum API version (60.0+)
|
|
224
|
+
```xpath
|
|
225
|
+
//*[local-name()='apiVersion']/*[number(@Text) < 60]
|
|
226
|
+
```
|
|
227
|
+
Note: `/*` navigates to the **child text node** where `@Text` lives. Do NOT put `@Text` directly on the element — it won't match because `@Text` only exists on text nodes (children), not on elements themselves.
|
|
228
|
+
|
|
229
|
+
### Flag field permissions in Profiles (should be in Perm Sets)
|
|
230
|
+
```xpath
|
|
231
|
+
//*[local-name()='Profile']/*[local-name()='fieldPermissions']
|
|
232
|
+
```
|
|
233
|
+
(No text matching needed — structural only.)
|
|
234
|
+
|
|
235
|
+
### Flag dangerous permissions (ModifyAllData only, enabled)
|
|
236
|
+
```xpath
|
|
237
|
+
//*[@Text='ModifyAllData']/../..
|
|
238
|
+
[local-name()='userPermissions']
|
|
239
|
+
[.//*[@Text='true']]
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Flag DML (recordCreates/recordUpdates/recordDeletes) inside Flow loops
|
|
243
|
+
```xpath
|
|
244
|
+
//*[local-name()='loops'][
|
|
245
|
+
following-sibling::*[local-name()='recordCreates' or local-name()='recordUpdates' or local-name()='recordDeletes']
|
|
246
|
+
]
|
|
247
|
+
```
|
|
248
|
+
(No text matching needed — structural only.)
|
|
249
|
+
|
|
250
|
+
## Targeting Specific File Types
|
|
251
|
+
|
|
252
|
+
PMD only scans `.xml` files by default. You must add `.xml` to the config. This single extension covers ALL Salesforce compound metadata extensions (`.permissionset-meta.xml`, `.field-meta.xml`, etc.) because `path.extname()` returns `.xml` from these filenames.
|
|
253
|
+
|
|
254
|
+
```yaml
|
|
255
|
+
# code-analyzer.yml
|
|
256
|
+
engines:
|
|
257
|
+
pmd:
|
|
258
|
+
file_extensions:
|
|
259
|
+
xml: [".xml"]
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
⚠️ Do NOT add compound extensions — the validator rejects anything that doesn't match `/^[.][a-zA-Z0-9]+$/`.
|
|
263
|
+
|
|
264
|
+
Additionally, scope your XPath to the correct root element to avoid false positives across file types:
|
|
265
|
+
|
|
266
|
+
```xpath
|
|
267
|
+
//*[@Text='ModifyAllData']/../..[local-name()='userPermissions']
|
|
268
|
+
[ancestor::*[local-name()='PermissionSet']]
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
This ensures the rule only fires on permission set files, not on profile or other metadata files.
|
|
272
|
+
|
|
273
|
+
## Gotchas
|
|
274
|
+
|
|
275
|
+
| Issue | Resolution |
|
|
276
|
+
|-------|------------|
|
|
277
|
+
| XPath matches nothing | Three causes: (1) Forgot `local-name()`. (2) Used `text()` — broken in PMD 7, use `@Text`. (3) File extension not in `file_extensions.xml` config. |
|
|
278
|
+
| Rule fires on wrong metadata type | Add root element scope: `[ancestor::*[local-name()='PermissionSet']]` |
|
|
279
|
+
| Can't match text content | **Use `@Text` attribute** (NOT `text()`). Example: `//*[@Text='ModifyAllData']` |
|
|
280
|
+
| `@Text` matches but parent navigation fails | `@Text` matches TEXT NODES. Go up with `../..`: text→element→parent. |
|
|
281
|
+
| Number comparison fails | `@Text` lives on the **child text node**, NOT on the element. Use: `//*[local-name()='element']/*[number(@Text) < N]` — the `/*` navigates to the text node. Do NOT put `[@Text]` predicate directly on the element (returns 0 matches). |
|
|
282
|
+
| Too many violations on large orgs | Scope with `file_extensions` in PMD config or add `ignores.files` patterns |
|
|
283
|
+
| Rule doesn't fire on metadata files | Add `file_extensions: { xml: [".xml"] }` under `engines.pmd` — this covers all compound extensions. Do NOT add `.permissionset-meta.xml` etc. (validator rejects them). |
|
|
284
|
+
| ast-dump fails with "XmlEncoding" error | Known issue. Read the raw XML file — the file content IS the DOM structure |
|
|
285
|
+
| ast-dump shows different structure | XML AST is the literal DOM — what you see in the file is what you get |
|