@salesforce/afv-skills 1.28.0 → 1.29.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.
Files changed (31) hide show
  1. package/package.json +1 -1
  2. package/skills/dx-code-analyzer-configure/SKILL.md +31 -13
  3. package/skills/dx-code-analyzer-custom-rule-create/SKILL.md +484 -0
  4. package/skills/dx-code-analyzer-custom-rule-create/assets/pmd-ruleset-template.xml +31 -0
  5. package/skills/dx-code-analyzer-custom-rule-create/examples/metadata-xml-example-fields-api.md +87 -0
  6. package/skills/dx-code-analyzer-custom-rule-create/examples/metadata-xml-example-flows.md +105 -0
  7. package/skills/dx-code-analyzer-custom-rule-create/examples/metadata-xml-example-permissions.md +95 -0
  8. package/skills/dx-code-analyzer-custom-rule-create/examples/metadata-xml-examples.md +84 -0
  9. package/skills/dx-code-analyzer-custom-rule-create/examples/regex-examples.md +127 -0
  10. package/skills/dx-code-analyzer-custom-rule-create/examples/xpath-examples.md +227 -0
  11. package/skills/dx-code-analyzer-custom-rule-create/references/advanced-pmd-patterns.md +288 -0
  12. package/skills/dx-code-analyzer-custom-rule-create/references/apex-ast-reference.md +127 -0
  13. package/skills/dx-code-analyzer-custom-rule-create/references/eslint-custom-plugins.md +247 -0
  14. package/skills/dx-code-analyzer-custom-rule-create/references/eslint-rules-discovery.md +188 -0
  15. package/skills/dx-code-analyzer-custom-rule-create/references/eslint-tier2-configurable.md +114 -0
  16. package/skills/dx-code-analyzer-custom-rule-create/references/eslint-tier3-custom-plugins.md +113 -0
  17. package/skills/dx-code-analyzer-custom-rule-create/references/metadata-xml-rules.md +285 -0
  18. package/skills/dx-code-analyzer-custom-rule-create/references/regex-rule-schema.md +174 -0
  19. package/skills/dx-code-analyzer-custom-rule-create/references/troubleshooting.md +141 -0
  20. package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-governor-limits.md +83 -0
  21. package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-method-calls.md +108 -0
  22. package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-security.md +45 -0
  23. package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-structure.md +127 -0
  24. package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns.md +131 -0
  25. package/skills/dx-code-analyzer-custom-rule-create/scripts/create-pmd-rule.js +209 -0
  26. package/skills/dx-code-analyzer-custom-rule-create/scripts/create-regex-rule.js +220 -0
  27. package/skills/dx-code-analyzer-run/SKILL.md +41 -8
  28. package/skills/mobile-platform-native-capabilities-integrate/SKILL.md +3 -3
  29. package/skills/platform-custom-field-generate/SKILL.md +86 -126
  30. package/skills/platform-custom-field-generate/references/advanced-picklists.md +590 -0
  31. package/skills/platform-value-set-generate/SKILL.md +305 -0
@@ -0,0 +1,247 @@
1
+ # ESLint Custom Rules for LWC/JavaScript
2
+
3
+ Create and integrate custom ESLint rules for Lightning Web Components, JavaScript, and TypeScript through Code Analyzer.
4
+
5
+ ## How It Works
6
+
7
+ Code Analyzer's ESLint engine loads your project's ESLint configuration alongside its built-in rules. You can:
8
+ - Use your own `eslint.config.js` with custom plugins
9
+ - Extend or override built-in base configs (LWC, TypeScript, SLDS, React)
10
+ - Install any npm ESLint plugin and have Code Analyzer pick it up
11
+
12
+ ## Configuration
13
+
14
+ ```yaml
15
+ # code-analyzer.yml
16
+ engines:
17
+ eslint:
18
+ # Point to your project's ESLint config
19
+ eslint_config_file: "eslint.config.js"
20
+
21
+ # Or auto-discover from workspace (searches for eslint.config.js/mjs/cjs)
22
+ auto_discover_eslint_config: true
23
+
24
+ # Disable base configs you don't need
25
+ disable_javascript_base_config: false
26
+ disable_lwc_base_config: false
27
+ disable_typescript_base_config: false
28
+ disable_slds_base_config: false
29
+ disable_react_base_config: false
30
+
31
+ # Custom file extension mapping
32
+ file_extensions:
33
+ javascript: [".js", ".cjs", ".mjs", ".jsx"]
34
+ typescript: [".ts", ".tsx"]
35
+ html: [".html", ".htm", ".cmp"]
36
+ css: [".css", ".scss"]
37
+ ```
38
+
39
+ ## Adding Custom ESLint Plugins
40
+
41
+ ### Step 1: Install the plugin
42
+
43
+ ```bash
44
+ npm install --save-dev eslint-plugin-my-custom
45
+ ```
46
+
47
+ ### Step 2: Create or update `eslint.config.js` (flat config)
48
+
49
+ ```javascript
50
+ const myPlugin = require('eslint-plugin-my-custom');
51
+
52
+ module.exports = [
53
+ {
54
+ plugins: {
55
+ 'my-custom': myPlugin
56
+ },
57
+ rules: {
58
+ 'my-custom/no-dangerous-pattern': 'error',
59
+ 'my-custom/enforce-naming': ['warn', { pattern: '^[a-z]' }]
60
+ }
61
+ }
62
+ ];
63
+ ```
64
+
65
+ ### Step 3: Reference in `code-analyzer.yml`
66
+
67
+ ```yaml
68
+ engines:
69
+ eslint:
70
+ eslint_config_file: "eslint.config.js"
71
+ ```
72
+
73
+ ### Step 4: Validate and run
74
+
75
+ ```bash
76
+ # Check that custom rules appear
77
+ sf code-analyzer rules --rule-selector eslint
78
+
79
+ # Run against targets
80
+ sf code-analyzer run --rule-selector eslint --target force-app/main/default/lwc/
81
+ ```
82
+
83
+ ## Common LWC Custom Rule Patterns
84
+
85
+ ### Enforce component naming convention
86
+
87
+ ```javascript
88
+ // eslint.config.js
89
+ module.exports = [
90
+ {
91
+ rules: {
92
+ '@lwc/lwc/no-unknown-wire-adapters': 'error',
93
+ '@lwc/lwc/no-api-reassignments': 'error',
94
+ '@lwc/lwc/no-leaky-event-listeners': 'error'
95
+ }
96
+ }
97
+ ];
98
+ ```
99
+
100
+ ### Add SSR compatibility checks
101
+
102
+ ```javascript
103
+ module.exports = [
104
+ {
105
+ rules: {
106
+ '@lwc/lwc/no-restricted-browser-globals-during-ssr': 'error',
107
+ '@lwc/lwc/no-unsupported-ssr-properties': 'error'
108
+ }
109
+ }
110
+ ];
111
+ ```
112
+
113
+ ### TypeScript strict rules
114
+
115
+ ```javascript
116
+ module.exports = [
117
+ {
118
+ rules: {
119
+ '@typescript-eslint/no-explicit-any': 'error',
120
+ '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
121
+ '@typescript-eslint/strict-boolean-expressions': 'warn'
122
+ }
123
+ }
124
+ ];
125
+ ```
126
+
127
+ ## Disabling Base Configs
128
+
129
+ When you want full control over which rules run, disable the built-in base configs:
130
+
131
+ ```yaml
132
+ engines:
133
+ eslint:
134
+ eslint_config_file: "eslint.config.js"
135
+ disable_javascript_base_config: true
136
+ disable_lwc_base_config: true
137
+ disable_typescript_base_config: true
138
+ ```
139
+
140
+ This prevents Code Analyzer's bundled rules from running — only your custom config's rules apply. Parsers are still configured (so files parse correctly), but no built-in rules fire.
141
+
142
+ ## Overriding Rule Severity via Config
143
+
144
+ After ESLint rules are loaded, override severity in `code-analyzer.yml`:
145
+
146
+ ```yaml
147
+ rules:
148
+ eslint:
149
+ no-unused-vars:
150
+ severity: "Low"
151
+ my-custom/dangerous-pattern:
152
+ severity: "Critical"
153
+ no-console:
154
+ disabled: true
155
+ ```
156
+
157
+ ## Legacy Config Support
158
+
159
+ If your project uses `.eslintrc.js` (ESLint v8 format), it still works:
160
+
161
+ ```yaml
162
+ engines:
163
+ eslint:
164
+ eslint_config_file: ".eslintrc.js"
165
+ eslint_ignore_file: ".eslintignore" # Only needed with legacy config
166
+ ```
167
+
168
+ Flat config (`eslint.config.js`) is recommended for new projects.
169
+
170
+ ## Plugin Requirements
171
+
172
+ For Code Analyzer to pick up custom plugin rules, each rule must have:
173
+ - `meta.docs.description` — rule description
174
+ - `meta.docs.url` — documentation URL
175
+
176
+ Rules without these metadata fields are silently excluded. Deprecated rules are also excluded.
177
+
178
+ ## Banning APIs Without a Custom Plugin
179
+
180
+ Core ESLint includes powerful "restrictor" rules that can ban specific globals, syntax patterns, or properties — no plugin installation needed. These rules are NOT active by default — you must enable them in `eslint.config.js`:
181
+
182
+ ### `no-restricted-globals` — ban global functions/variables
183
+
184
+ ```javascript
185
+ module.exports = [
186
+ {
187
+ files: ["**/lwc/**/*.js"],
188
+ rules: {
189
+ "no-restricted-globals": ["error",
190
+ { "name": "setTimeout", "message": "Use lifecycle hooks instead of setTimeout." },
191
+ { "name": "setInterval", "message": "Use lifecycle hooks instead of setInterval." },
192
+ { "name": "eval", "message": "eval is forbidden for security." }
193
+ ]
194
+ }
195
+ }
196
+ ];
197
+ ```
198
+
199
+ ### `no-restricted-syntax` — ban arbitrary AST patterns
200
+
201
+ ```javascript
202
+ module.exports = [
203
+ {
204
+ files: ["**/lwc/**/*.js"],
205
+ rules: {
206
+ "no-restricted-syntax": ["error",
207
+ { "selector": "CallExpression[callee.name='fetch']", "message": "Use Lightning Data Service instead of fetch." },
208
+ { "selector": "NewExpression[callee.name='XMLHttpRequest']", "message": "Use fetch or LDS." }
209
+ ]
210
+ }
211
+ }
212
+ ];
213
+ ```
214
+
215
+ ### `no-restricted-properties` — ban specific object methods
216
+
217
+ ```javascript
218
+ module.exports = [
219
+ {
220
+ files: ["**/lwc/**/*.js"],
221
+ rules: {
222
+ "no-restricted-properties": ["error",
223
+ { "object": "window", "property": "location", "message": "Use NavigationMixin." },
224
+ { "object": "document", "property": "cookie", "message": "Cookies are not available in LWC." }
225
+ ]
226
+ }
227
+ }
228
+ ];
229
+ ```
230
+
231
+ ⚠️ **These rules will NOT appear in `sf code-analyzer rules` output until you:**
232
+ 1. Create the `eslint.config.js` file with the rule enabled
233
+ 2. Set `engines.eslint.eslint_config_file: "eslint.config.js"` in `code-analyzer.yml`
234
+ 3. Run `sf code-analyzer rules --rule-selector eslint:no-restricted-globals` to verify
235
+
236
+ They are core ESLint rules, not Code Analyzer built-ins — they require your config to activate.
237
+
238
+ ## When to Use ESLint vs Regex vs PMD
239
+
240
+ | Need | Engine |
241
+ |------|--------|
242
+ | JavaScript/TypeScript code patterns | **ESLint** |
243
+ | LWC component best practices | **ESLint** (with @lwc plugin) |
244
+ | HTML template issues | **ESLint** (with SLDS or custom HTML plugin) |
245
+ | Simple string patterns in JS/TS | **ESLint** (Regex cannot distinguish code from comments/strings in JS) |
246
+ | Apex code structure | **PMD** (ESLint doesn't parse Apex) |
247
+ | Metadata XML governance | **PMD** with `language="xml"` |
@@ -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
+ ```