agent-docs 1.2.0 → 1.3.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/PLAN.md CHANGED
@@ -607,7 +607,7 @@ Standard MIT license with copyright holder: starch-uk
607
607
 
608
608
  - Name: `agent-docs`
609
609
  - Type: module
610
- - Version: `1.1.0` (current)
610
+ - Version: `1.0.0`
611
611
  - Scripts: `format`, `format:fix`, `format:check`, `postinstall`
612
612
  - Dev dependencies: `prettier`
613
613
  - Engines: Node.js >= 20.0.0
@@ -646,11 +646,11 @@ The plan should:
646
646
  APEXDOC.md, CML.md, CODEANALYZER.md, CONTEXTDEFINITIONS.md, ESLINT.md,
647
647
  ESLINTJSDOC.md, FIELDSERVICE.md, GRAPHBINARY.md, GRAPHENGINE.md, GRAPHML.md,
648
648
  GRAPHSON.md, GREMLIN.md, GRYO.md, HUSKY.md, JEST.md, JORJE.md, JSDOC.md,
649
- PMD.md, PNPM.md, PRETTIER.md, PRETTIERAPEX.md, REVENUETRANSACTIONMANAGEMENT.md,
650
- TINKERPOP.md, VITEST.md, XPATH31.md) need to be initialized with version
651
- `1.0.0` (or appropriate version based on their current state) when the
652
- versioning system is first implemented. These existing docs will be tracked
653
- going forward using the same semver system.
649
+ LIGHTNINGBASECOMPONENTS.md, LWCHTMLTEMPLATES.md, PMD.md, PNPM.md, PRETTIER.md,
650
+ PRETTIERAPEX.md, REVENUETRANSACTIONMANAGEMENT.md, TINKERPOP.md, VITEST.md,
651
+ XPATH31.md) need to be initialized with version `1.0.0` (or appropriate version
652
+ based on their current state) when the versioning system is first implemented.
653
+ These existing docs will be tracked going forward using the same semver system.
654
654
  - Describe how scripts can help with versioning by:
655
655
  - Reading markdown files and detecting headers/sections
656
656
  - Comparing current state with the latest commit in `main` branch
package/README.md CHANGED
@@ -101,6 +101,9 @@ structured format optimized for AI agent consumption:
101
101
  - **[JEST.md](docs/JEST.md)** - Jest testing framework reference
102
102
  - **[JORJE.md](docs/JORJE.md)** - Jorje Apex parser reference
103
103
  - **[JSDOC.md](docs/JSDOC.md)** - JSDoc documentation generator reference
104
+ - **[LIGHTNINGBASECOMPONENTS.md](docs/LIGHTNINGBASECOMPONENTS.md)** - Lightning Base
105
+ Components reference
106
+ - **[LWCHTMLTEMPLATES.md](docs/LWCHTMLTEMPLATES.md)** - LWC HTML Templates reference
104
107
  - **[PMD.md](docs/PMD.md)** - PMD static analysis tool reference (includes Apex
105
108
  AST reference and suppressing warnings)
106
109
  - **[PNPM.md](docs/PNPM.md)** - pnpm package manager reference
@@ -0,0 +1,328 @@
1
+ # Lightning Base Components
2
+
3
+ > **Version**: 1.0.0
4
+
5
+ ## Guidelines
6
+ - Prefer base components over custom HTML - built-in SLDS, a11y, less maintenance
7
+ - A11y: Always provide accessible names via `label`, `aria-label`, `alternative-text`
8
+ - Events: `onclick` (buttons), `onchange`/`onselect` (inputs/menus), `onstatuschange` (flows), `onuploadfinished` (files), `onscan`/`onerror` (barcode)
9
+ - Nav: Use `lightning/navigation` not `href`
10
+ - Style: SLDS utilities + `--slds-c-*` CSS vars. Avoid internal overrides
11
+ - Files: Guest uploads via org prefs. `file-field-name`/`value` for guests, `record-id` for auth users
12
+ - Limits: combobox no multi-select/autocomplete; barcode/click-to-dial no iFrames; carousel max 6 images; flow restrictions on LWR+custom components
13
+
14
+ ## ValidityState & Validation
15
+ **ValidityState props** (all boolean): `valid`, `valueMissing`, `typeMismatch`, `patternMismatch`, `tooLong`, `tooShort`, `rangeOverflow`, `rangeUnderflow`, `stepMismatch`, `badInput`, `customError`
16
+
17
+ **Methods**: `setCustomValidity(msg)`, `reportValidity()`, `checkValidity()`
18
+
19
+ **Custom messages**: `message-when-value-missing`, `message-when-type-mismatch`, `message-when-pattern-mismatch`, `message-when-too-long`, `message-when-too-short`, `message-when-range-overflow`, `message-when-range-underflow`, `message-when-step-mismatch`, `message-when-bad-input`
20
+
21
+ Start w/ LDS components for built-in validation. Client+server validation together.
22
+
23
+ ## Components
24
+
25
+ **Legend**: `*` = required, `|` = options, `→` = returns
26
+
27
+ ### lightning-accordion-section
28
+ Nest in `lightning-accordion`
29
+ - `class`, `heading-level` (1-6), `label`, `name`*
30
+ - Slots: default, `actions`
31
+ - ARIA: `role="listitem"`, `aria-expanded`, `aria-controls`
32
+ - Event: `onactive`
33
+
34
+ ### lightning-alert (lightning/alert)
35
+ `LightningAlert.open(config)` → Promise
36
+ - `label`, `message`*, `theme` (default|shade|inverse|alt-inverse|success|info|warning|error|offline), `variant` (header|headerless)
37
+ - ARIA: `role="alertdialog"`, focus trap
38
+
39
+ ### lightning-avatar
40
+ - `alternative-text`* (if informational), `class`, `fallback-icon-name` (standard/custom only), `initials`, `src`, `variant` (circle|square)
41
+ - Initials+fallback: initials show w/ icon bg color
42
+
43
+ ### lightning-badge
44
+ - `class`, `icon-name`, `icon-position` (end|start), `label`
45
+ - No links/nested elements. Use `slds-theme_*` for colors
46
+
47
+ ### lightning-barcode-scanner
48
+ Mobile only, no iFrames
49
+ - `disabled`, `disabled-alternative-text`, `disabled-icon-src`, `enable-continuous-scan`, `enabled-alternative-text`, `enabled-icon-src`, `icon-size`
50
+ - Events: `onerror`, `onscan` → `event.detail.scannedBarcodes`
51
+
52
+ ### lightning-breadcrumb
53
+ Nest in `lightning-breadcrumbs`
54
+ - `class`, `href` (defaults "#"), `label`*, `name`
55
+ - Event: `onclick` (use w/ lightning/navigation)
56
+
57
+ ### lightning-breadcrumbs
58
+ - `class`
59
+ - Slots: `lightning-breadcrumb`
60
+ - ARIA: `role="navigation"`, last item `aria-current="page"`
61
+
62
+ ### lightning-button
63
+ - `accesskey`, `aria-atomic`, `aria-controls`, `aria-describedby`, `aria-expanded`, `aria-haspopup`, `aria-label`, `aria-labelledby`, `aria-live`, `class`, `disabled`, `disable-animation`, `icon-name` (utility), `icon-position` (end|start), `label`*, `stretch`, `tabindex`, `title`, `type` (button|reset|submit), `variant` (base|brand|brand-outline|destructive|destructive-text|inverse|neutral|success)
64
+ - Event: `onclick`
65
+ - Use button-icon for icon-only. Min 44x44px mobile
66
+
67
+ ### lightning-button-group
68
+ - `class`
69
+ - Slots: `lightning-button*`
70
+
71
+ ### lightning-button-icon-stateful
72
+ - `accesskey`, `alternative-text`, `aria-atomic`, `aria-controls`, `aria-describedby`, `aria-expanded`, `aria-haspopup`, `aria-label`*, `aria-live`, `class`, `disabled`, `icon-name`* (utility), `selected`, `size`, `variant` (border|border-filled|border-inverse)
73
+ - ARIA: `aria-pressed`
74
+ - Event: `onclick`
75
+
76
+ ### lightning-button-menu
77
+ - `alternative-text`, `class`, `disabled`, `icon-name`, `icon-size`, `is-draft`, `draft-alternative-text`, `is-loading`, `loading-state-alternative-text`, `label`, `menu-alignment`, `size`, `tooltip`, `title`, `variant` (bare|bare-inverse|border|border-filled|border-inverse|container)
78
+ - Slots: `lightning-menu-item`, `lightning-menu-divider`, `lightning-menu-subheader`
79
+ - ARIA: `aria-haspopup="true"`, `aria-expanded`
80
+ - Events: `onclose`, `onopen`, `onselect`
81
+
82
+ ### lightning-button-stateful
83
+ - `accesskey`, `aria-*` (same as button), `class`, `disabled`, `icon-name` (utility), `label-when-hover`, `label-when-off`, `label-when-on`, `selected`, `tabindex`, `variant` (brand|destructive|inverse|neutral|success|text)
84
+ - ARIA: `aria-pressed`, `aria-live="polite"`
85
+ - Event: `onclick` (toggle selected)
86
+
87
+ ### lightning-card
88
+ - `class`, `icon-name`, `title`
89
+ - Slots: `actions`, `footer`, `title`, default
90
+
91
+ ### lightning-carousel
92
+ Max 6 images
93
+ - `class`, `disable-auto-refresh`, `disable-auto-scroll`, `scroll-duration`
94
+ - Slots: `lightning-carousel-image`
95
+ - ARIA: indicators `role="tablist"`
96
+
97
+ ### lightning-carousel-image
98
+ - `alternative-text`, `description`, `header`, `href`, `src`*
99
+
100
+ ### lightning-click-to-dial
101
+ Lightning Experience only, no iFrames. Requires Open CTI `enableClickToDial`
102
+ - `record-id`, `value` (phone)
103
+ - Event: `onclicktodial`
104
+
105
+ ### lightning-combobox
106
+ No multi-select/autocomplete. Mobile issues - consider HTML `<select>`
107
+ - `autocomplete`, `class`, `label`*, `message-when-value-missing`, `options`* [{value,label,description?}], `required`, `validity`, `value`, `variant` (label-hidden)
108
+ - Events: `onchange` → `event.detail.selectedValue`, `onopen`
109
+
110
+ ### lightning-dynamic-icon
111
+ - `alternative-text` (provide on small screens), `type`* (ellie|eq|score|strength|trend|waffle)
112
+
113
+ ### lightning-file-upload
114
+ Max 25 files, 2GB each. Guest: create `*fileupload__c` field
115
+ - `accept`, `class`, `file-field-name`, `file-field-value`, `label`, `record-id`
116
+ - Event: `onuploadfinished` → `event.detail.files` [{name,documentId}]
117
+
118
+ ### lightning-flow
119
+ Custom components unsupported on LWR
120
+ - `flow-api-name`*, `flow-input-variables`, `flow-finish-behavior` (NONE|RESTART)
121
+ - Event: `onstatuschange` → status, outputVariables
122
+
123
+ ### lightning-formatted-address
124
+ Format by user locale. Links Google Maps
125
+ - `city`, `country`, `disabled`, `latitude`, `longitude`, `postal-code`, `province`, `show-static-map`, `street`, `variant` (plain)
126
+
127
+ ### lightning-formatted-email
128
+ - `bcc`, `body`, `cc`, `disable-linkify`, `hide-icon`, `label`, `subject`, `value`
129
+
130
+ ### lightning-formatted-location
131
+ - `latitude`*, `longitude`*
132
+
133
+ ### lightning-formatted-number
134
+ User locale
135
+ - `currency-code`, `currency-display-as` (code|name|symbol), `format-style` (currency|decimal|percent|percent-fixed), `maximum-fraction-digits`, `maximum-significant-digits`, `minimum-fraction-digits`, `minimum-significant-digits`, `value`*
136
+
137
+ ### lightning-formatted-rich-text
138
+ - `disable-linkify`, `value`
139
+ - Supported: a, div, p, h1-h6, strong, em, ul, ol, li, table, img, etc.
140
+
141
+ ### lightning-formatted-time
142
+ - `value`* (ISO8601)
143
+
144
+ ### lightning-input
145
+ - `accept`, `aria-describedby`, `aria-labelledby`, `autocomplete`, `checked`, `class`, `date-aria-describedby`, `date-aria-labelledby`, `disabled`, `field-level-help`, `files`, `label`*, `max`, `maxlength`, `message-when-*` (all validation msgs), `min`, `minlength`, `multiple`, `name`, `pattern`, `placeholder`, `readonly`, `required`, `step`, `time-aria-describedby`, `time-aria-labelledby`, `type` (checkbox|checkbox-button|color|date|datetime|datetime-local|email|file|number|password|search|tel|text|time|toggle|url), `validity`, `value`, `variant` (label-hidden|label-inline|label-stacked|standard)
146
+ - Events: `onblur`, `onchange`, `onfocus`, `oninput`
147
+
148
+ ### lightning-input-field
149
+ - `field-name`*, `required`, `value`, `variant`
150
+ - Event: `onchange`
151
+
152
+ ### lightning-input-location
153
+ Validates lat -90/90, lon -180/180
154
+ - `city`, `country`, `latitude`, `longitude`, `postal-code`, `province`, `street`
155
+ - Event: `onchange` → lat, lon
156
+
157
+ ### lightning-layout-item
158
+ - `flexibility`, `large-device-size` (1-12), `medium-device-size`, `padding`, `size`, `small-device-size`
159
+
160
+ ### lightning-map
161
+ - `center`, `list-view`, `map-markers`*, `markers-title`, `show-footer-address`, `zoom-level`
162
+
163
+ ### lightning-menu-divider
164
+ - `variant` (compact|default)
165
+
166
+ ### lightning-menu-item
167
+ - `accesskey`, `checked`, `class`, `draft-alternative-text`, `href`, `icon-name`, `icon-type` (color|standard), `is-draft`, `label`, `prefix-icon-name`, `tabindex`, `target`, `value`
168
+ - Selection via parent `onselect`
169
+
170
+ ### lightning-menu-subheader
171
+ - `label`*
172
+
173
+ ### lightning-modal (lightning/modal)
174
+ Extend `LightningModal`
175
+ - `description`, `disableClose`, `label`*, `size` (full|large|medium|small)
176
+ - ARIA: focus trap
177
+ - Events bubble to opener
178
+
179
+ ### lightning-modal-body/footer
180
+ Slots: default
181
+
182
+ ### lightning-modal-header
183
+ - `icon-assistive-text`, `icon-name`, `label`*
184
+
185
+ ### lightning-omnistudio-flexcard
186
+ - `flexcard-name`*, `input`, `record-id`
187
+
188
+ ### lightning-omnistudio-omniscript
189
+ - `input`, `record-id`, `script-name`*
190
+
191
+ ### lightning-output-field
192
+ - `field-name`*, `record-id`, `variant` (label-hidden|standard)
193
+
194
+ ### lightning-pill
195
+ 3 clickable areas: icon/avatar, label, remove btn
196
+ - `class`, `has-error`, `href`, `label`, `name`, `variant` (link|plain)
197
+ - Slots: default (icon/avatar)
198
+ - Events: `onclick`, `onremove`
199
+
200
+ ### lightning-pill-container
201
+ - `class`, `is-collapsible`, `is-expanded`, `items`*, `single-line`, `variant` (bare|standard)
202
+ - Event: `onitemremove` → item, index
203
+
204
+ ### lightning-progress-bar
205
+ - `size` (large|medium|small|x-large), `value`* (0-100), `variant` (circular|circular-with-label|light)
206
+
207
+ ### lightning-progress-indicator
208
+ - `class`, `current-step`, `type` (horizontal|vertical), `variant` (base|path)
209
+ - Slots: `lightning-progress-step`
210
+
211
+ ### lightning-progress-ring
212
+ - `direction` (clockwise|counterclockwise), `value`* (0-100), `variant` (active-step|base|base-autocomplete|expired|warning)
213
+
214
+ ### lightning-prompt (lightning/prompt)
215
+ `LightningPrompt.open()` → Promise
216
+ - `defaultValue`, `label`*, `message`, `options`, `variant` (header|headerless)
217
+
218
+ ### lightning-quick-action-panel
219
+ - `object-api-name`*, `quick-action-api-name`, `record-id`*
220
+ - Event: `onquickactionmenu`
221
+
222
+ ### lightning-record-edit-form
223
+ Uses LDS. Shows first validation error
224
+ - `class`, `layout-type` (Compact|Full), `mode` (edit|readonly), `object-api-name`, `record-id`
225
+ - Events: `onerror`, `onload`, `onsubmit`, `onsuccess`
226
+
227
+ ### lightning-relative-date-time
228
+ - `class`, `options`, `value`*
229
+
230
+ ### lightning-rich-text-toolbar-button
231
+ - `class`, `disabled`, `icon-name`, `pressed`, `value`
232
+
233
+ ### lightning-rich-text-toolbar-button-group
234
+ Slots: `lightning-rich-text-toolbar-button`
235
+
236
+ ### lightning-select
237
+ - `class`, `disabled`, `field-level-help`, `label`*, `message-when-value-missing`, `multiple`, `options` [{value,label,disabled?}], `required`, `size`, `validity`, `value`, `variant`
238
+ - Event: `onchange` → `event.detail.value`
239
+
240
+ ### lightning-slider
241
+ Use onchange not onblur (Safari). Values clamped to min/max
242
+ - `class`, `disabled`, `label`, `max` (100), `min` (0), `step`, `type` (horizontal|vertical), `value`
243
+ - Event: `onchange`
244
+
245
+ ### lightning-spinner
246
+ - `variant` (brand|inverse|default)
247
+ - Use w/ `if:true`
248
+
249
+ ### lightning-tab
250
+ Lazy loaded. Only query active/prev-active content
251
+ - `class`, `end-icon-alternative-text`, `end-icon-name`, `icon-assistive-text`, `icon-name`, `label`*, `show-error-indicator`
252
+ - Event: `onactive`
253
+
254
+ ### lightning-textarea
255
+ - `aria-describedby`, `aria-labelledby`, `autocomplete`, `class`, `disabled`, `label`*, `maxlength`, `message-when-*`, `minlength`, `placeholder`, `read-only`, `required`, `validity`, `value`, `variant`
256
+ - Events: `onblur`, `onchange`, `onfocus`
257
+
258
+ ### lightning-tile
259
+ - `class`, `label`*
260
+ - Slots: `media`, default
261
+ - Event: `onactiontriggered`
262
+
263
+ ### lightning-toast (lightning/toast)
264
+ `Toast.show()` → Promise
265
+ - `label`*, `labelLinks`, `message`, `messageLinks`, `mode` (dismissible|sticky), `variant` (error|info|success|warning)
266
+
267
+ ### lightning-toast-container (lightning/toastContainer)
268
+ One per page. Ctrl+F6/Cmd+F6 nav
269
+ - `containerPosition` (absolute|fixed), `maxToasts` (3), `toastPosition` (bottom-center|bottom-left|bottom-right|top-center|top-left|top-right)
270
+
271
+ ### lightning-tree
272
+ - `class`, `header`, `items`*
273
+ - Event: `onselect` → name
274
+
275
+ ### lightning-tree-grid
276
+ Set `sortable:true` on columns for sorting
277
+ - `class`, `columns`*, `data`, `expanded-rows`, `hide-checkbox-column`, `key-field`*, `max-column-width`, `min-column-width`, `resize-column-disabled`, `selected-rows`, `show-row-number-column`, `variant`
278
+ - Events: `onrowaction`, `onrowselection`, `onsort`
279
+
280
+ ### lightning-vertical-navigation
281
+ - `class`, `selected-item`
282
+ - Slots: `lightning-vertical-navigation-item*`
283
+ - Events: `onbeforeselect`, `onselect`
284
+
285
+ ### lightning-vertical-navigation-item
286
+ - `badge`, `class`, `href`, `icon-name`, `label`*, `name`
287
+ - Event: `onselect`
288
+
289
+ ### lightning-vertical-navigation-item-badge
290
+ - `class`, `label`, `variant` (default|inverse|lightest)
291
+
292
+ ### lightning-vertical-navigation-item-icon
293
+ - `alternative-text`, `class`, `icon-name`, `position` (end|start)
294
+
295
+ ## Patterns
296
+
297
+ ### Validation
298
+ ```js
299
+ // Check validity
300
+ if (!this.template.querySelector('lightning-input').checkValidity()) {
301
+ this.template.querySelector('lightning-input').reportValidity();
302
+ }
303
+ ```
304
+
305
+ ### Styling
306
+ ```html
307
+ <lightning-button class="slds-m-left_small">
308
+ ```
309
+ ```css
310
+ --slds-c-button-color-background
311
+ --slds-c-button-text-color
312
+ ```
313
+
314
+ ### Navigation
315
+ ```js
316
+ import { NavigationMixin } from 'lightning/navigation';
317
+ this[NavigationMixin.Navigate]({
318
+ type: 'standard__recordPage',
319
+ attributes: { recordId, actionName: 'view' }
320
+ });
321
+ ```
322
+
323
+ ### Events
324
+ ```js
325
+ handleChange(event) {
326
+ const value = event.detail.value; // or .selectedValue
327
+ }
328
+ ```
@@ -0,0 +1,314 @@
1
+ # LWC HTML Templates
2
+
3
+ > **Version**: 1.0.0
4
+
5
+ ## File Structure
6
+
7
+ - Location: `lwc/<ComponentName>/<ComponentName>.html`
8
+ - Additional HTML files allowed for multiple template rendering (advanced)
9
+ - Leverage virtual DOM; avoid manual DOM manipulation in JavaScript
10
+
11
+ ## Template Structure
12
+
13
+ - **Root element:** `<template>` (renders as `<namespace-component-name>`)
14
+ - **Nested `<template>` tags:** required for directives; only allowed attributes:
15
+ - `for:each`, `iterator:iteratorname`, `lwc:if`, `lwc:else`, `lwc:elseif`, `if:true|false`
16
+ - No other directives or HTML attributes (e.g., no `class` on nested `<template>`)
17
+ - **No inline `<style>` or `<script>` tags** — use separate CSS/JS files
18
+ - Follow Lightning Design System patterns and accessibility guidelines
19
+
20
+ ## Data Binding & Expressions
21
+
22
+ | Syntax | Usage |
23
+ |--------|-------|
24
+ | `{property}` | Bind data (no spaces around property) |
25
+ | `{data.name}` | Dot notation supported |
26
+ | `{!property}` | Two-way binding (form inputs) |
27
+ | `{handlerMethod}` | Event handler binding |
28
+
29
+ ### Allowed Expression Syntax
30
+
31
+ The `{expression}` binding syntax **only** supports:
32
+
33
+ - **Simple property references:** `{myProperty}`
34
+ - **Dot notation paths:** `{contact.name}`, `{account.owner.email}`
35
+ - **Method references for events:** `onclick={handleClick}`
36
+
37
+ **That's it.** No operators, no function calls, no array indexing, no ternaries.
38
+
39
+ ### ❌ What's NOT Allowed (Common AI Agent Mistakes)
40
+
41
+ ```html
42
+ <!-- ❌ Logical operators -->
43
+ {isReadOnly || isLoading}
44
+ {isActive && isVisible}
45
+
46
+ <!-- ❌ Negation operator -->
47
+ {!isDisabled}
48
+
49
+ <!-- ❌ Ternary expressions -->
50
+ {isActive ? 'active' : 'inactive'}
51
+
52
+ <!-- ❌ Arithmetic -->
53
+ {count + 1}
54
+ {price * quantity}
55
+
56
+ <!-- ❌ Array indexing -->
57
+ {items[0].name}
58
+ {person[2].name['John']}
59
+
60
+ <!-- ❌ Function calls in templates -->
61
+ {formatDate(startDate)}
62
+ {calculateTotal()}
63
+
64
+ <!-- ❌ String concatenation -->
65
+ {'Hello ' + name}
66
+ {`Item: ${itemName}`}
67
+
68
+ <!-- ❌ Comparisons -->
69
+ {count > 0}
70
+ {status === 'active'}
71
+
72
+ <!-- ❌ Nullish coalescing / optional chaining in binding -->
73
+ {user?.name}
74
+ {value ?? 'default'}
75
+ ```
76
+
77
+ ### ✅ The Solution: Use JavaScript Getters
78
+
79
+ All computed logic **must** be moved to getters in the JavaScript class:
80
+
81
+ **JavaScript (myComponent.js):**
82
+ ```javascript
83
+ import { LightningElement, api, track } from 'lwc';
84
+
85
+ export default class MyComponent extends LightningElement {
86
+ @api isReadOnly = false;
87
+ @api isLoading = false;
88
+ @track items = [];
89
+
90
+ // ✅ Logical operators → getter
91
+ get isDisabled() {
92
+ return this.isReadOnly || this.isLoading;
93
+ }
94
+
95
+ // ✅ Negation → getter
96
+ get isEnabled() {
97
+ return !this.isDisabled;
98
+ }
99
+
100
+ // ✅ Ternary → getter
101
+ get buttonLabel() {
102
+ return this.isLoading ? 'Loading...' : 'Submit';
103
+ }
104
+
105
+ // ✅ Arithmetic → getter
106
+ get itemCountPlusOne() {
107
+ return this.count + 1;
108
+ }
109
+
110
+ // ✅ Array indexing → getter
111
+ get firstItemName() {
112
+ return this.items[0]?.name || '';
113
+ }
114
+
115
+ // ✅ String formatting → getter
116
+ get formattedCount() {
117
+ return `${this.count} items`;
118
+ }
119
+
120
+ // ✅ Comparisons for conditional rendering → getter
121
+ get hasItems() {
122
+ return this.items.length > 0;
123
+ }
124
+
125
+ // ✅ Complex class computation → getter
126
+ get containerClass() {
127
+ return {
128
+ 'slds-box': true,
129
+ 'slds-theme_shade': this.isActive,
130
+ 'slds-hide': !this.isVisible
131
+ };
132
+ }
133
+ }
134
+ ```
135
+
136
+ **HTML (myComponent.html):**
137
+ ```html
138
+ <template>
139
+ <template lwc:if={hasItems}>
140
+ <div class={containerClass}>
141
+ <p>{formattedCount}</p>
142
+ <p>First item: {firstItemName}</p>
143
+ </div>
144
+ </template>
145
+
146
+ <lightning-button
147
+ label={buttonLabel}
148
+ disabled={isDisabled}
149
+ onclick={handleClick}>
150
+ </lightning-button>
151
+ </template>
152
+ ```
153
+
154
+ ### Why This Design?
155
+
156
+ Salesforce made this intentional choice for:
157
+
158
+ | Reason | Explanation |
159
+ |--------|-------------|
160
+ | **Performance** | Simple property lookups are fast; no runtime expression parsing/evaluation |
161
+ | **Security** | No risk of template injection or expression-based exploits |
162
+ | **Predictability** | Reactivity is straightforward—change a property, component re-renders |
163
+ | **Testability** | All logic lives in unit-testable JavaScript, not scattered in templates |
164
+ | **Debugging** | Stack traces point to JS files, not opaque template expressions |
165
+
166
+ ### AI Agent Rule Summary
167
+
168
+ > **LWC Template Expression Rule:** The `{...}` binding syntax accepts ONLY property names and dot-notation paths. Any logic requiring operators (`||`, `&&`, `?:`, `+`, `>`, `===`, etc.), function calls, array indexing, or string interpolation MUST be implemented as a getter in the JavaScript class.
169
+
170
+ ```
171
+ Template binding: {propertyName} or {object.nested.property}
172
+ Computed logic: get computedProperty() { return /* any JS expression */; }
173
+ ```
174
+
175
+ ## Conditional Rendering
176
+
177
+ Use `lwc:if`, `lwc:elseif`, `lwc:else` directives (prefer over multiple templates for simple conditions):
178
+
179
+ ```html
180
+ <template lwc:if={condition}>...</template>
181
+ <template lwc:elseif={otherCondition}>...</template>
182
+ <template lwc:else>...</template>
183
+ ```
184
+
185
+ **Note:** The condition must be a simple property reference or getter—not an expression:
186
+
187
+ ```html
188
+ <!-- ❌ Invalid -->
189
+ <template lwc:if={items.length > 0}>
190
+
191
+ <!-- ✅ Valid — use a getter -->
192
+ <template lwc:if={hasItems}>
193
+ ```
194
+
195
+ ## List Rendering
196
+
197
+ ### for:each
198
+ ```html
199
+ <template for:each={array} for:item="item">
200
+ <div key={item.uniqueId}>{item.property}</div>
201
+ </template>
202
+ ```
203
+ - `key` required: string/number, **not** index or object
204
+ - Access current item via `for:item="itemName"` → `{itemName.property}`
205
+
206
+ ### iterator
207
+ ```html
208
+ <template iterator:it={array}>
209
+ <div key={it.value.uniqueId}>{it.value.property}</div>
210
+ </template>
211
+ ```
212
+ - Iterator name must be lowercase
213
+ - Properties: `{it.value.property}`, `{it.index}`, `{it.first}`, `{it.last}`
214
+ - Combine with `lwc:if` for first/last styling: `lwc:if={it.first}`
215
+
216
+ ## Multiple Template Rendering (Advanced)
217
+
218
+ - Import templates: `import templateOne from "./templateOne.html"`
219
+ - Override `render()` to return template based on state
220
+ - CSS must match template filename: `templateTwo.html` → `templateTwo.css`
221
+ - Default: `componentName.html` when no `render()` override
222
+ - **Prefer `lwc:if` for simple variations; use multiple templates for significantly different layouts**
223
+
224
+ ## Class Object Binding (LWC API v62.0+)
225
+
226
+ Bind dynamic classes using arrays or objects instead of string concatenation:
227
+
228
+ | Input | Output |
229
+ |-------|--------|
230
+ | `["highlight", "yellow"]` | `class="highlight yellow"` |
231
+ | `{highlight: true, hidden: false}` | `class="highlight"` |
232
+
233
+ - Booleans, numbers, functions render as empty string (use `String(value)` if needed)
234
+ - Use getters to compute complex class combinations
235
+
236
+ **Example with getter:**
237
+ ```javascript
238
+ get cardClasses() {
239
+ return {
240
+ 'slds-card': true,
241
+ 'slds-card_boundary': this.hasBorder,
242
+ 'custom-highlight': this.isHighlighted
243
+ };
244
+ }
245
+ ```
246
+
247
+ ```html
248
+ <div class={cardClasses}>...</div>
249
+ ```
250
+
251
+ ## Inline Style Binding
252
+
253
+ - Syntax: `style={computedStyles}`
254
+ - Getter returns semi-colon separated properties: `width: 50%; font-size: 20px`
255
+ - Use kebab-case CSS properties (`font-size`, not `fontSize`)
256
+ - **Prefer CSS classes over inline styles**; use inline for dynamic/computed values only
257
+
258
+ **Example:**
259
+ ```javascript
260
+ get progressBarStyle() {
261
+ return `width: ${this.progressPercent}%; background-color: ${this.barColor}`;
262
+ }
263
+ ```
264
+
265
+ ```html
266
+ <div class="progress-bar" style={progressBarStyle}></div>
267
+ ```
268
+
269
+ ## Event Handling
270
+
271
+ - Bind handlers via method reference: `onclick={handleClick}`
272
+ - No inline functions in templates
273
+ - Access values via `event.target.value`
274
+ - Custom events: extend `LightningElement`, use proper event naming
275
+
276
+ ```html
277
+ <!-- ❌ Invalid — no inline functions -->
278
+ <button onclick={() => this.handleClick()}>
279
+
280
+ <!-- ✅ Valid — method reference -->
281
+ <button onclick={handleClick}>
282
+ ```
283
+
284
+ ## Accessibility
285
+
286
+ - Include proper ARIA attributes
287
+ - Use semantic HTML elements
288
+ - Ensure keyboard navigation for interactive elements
289
+
290
+ ## Performance
291
+
292
+ - Minimize DOM manipulation
293
+ - Use lazy loading / pagination for large lists
294
+ - Avoid unnecessary rerenders by optimizing data binding
295
+ - Getters are re-evaluated on every render—keep them lightweight or cache expensive computations
296
+
297
+ ## Validation Checklist
298
+
299
+ - [ ] File path: `lwc/<ComponentName>/<ComponentName>.html`
300
+ - [ ] Root element is `<template>`
301
+ - [ ] Nested `<template>` tags include only allowed directives
302
+ - [ ] Nested `<template>` tags have no other attributes (e.g., no `class`)
303
+ - [ ] No inline `<style>` or `<script>` tags
304
+ - [ ] No manual DOM manipulation in JS
305
+ - [ ] Data binding: `{property}` syntax, no spaces
306
+ - [ ] **No computed expressions in templates** (no operators, function calls, array indexing)
307
+ - [ ] **All computed logic implemented as getters in JS class**
308
+ - [ ] `for:each`: has `for:item` and unique `key` (string/number)
309
+ - [ ] `iterator`: lowercase name, unique `key={it.value.uniqueId}`
310
+ - [ ] Multiple templates: proper imports, `render()` override, matching CSS filenames
311
+ - [ ] Class binding follows v62.0+ semantics (arrays/objects via getters)
312
+ - [ ] Inline styles: `style={getter}`, kebab-case properties
313
+ - [ ] Valid HTML syntax (use Salesforce Extensions Pack for validation)
314
+ - [ ] Follows accessibility and performance best practices