@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.
- package/package.json +1 -1
- 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/mobile-platform-native-capabilities-integrate/SKILL.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-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
|
+
```
|