@salesforce/afv-skills 1.34.0 → 1.35.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 (67) hide show
  1. package/package.json +1 -1
  2. package/skills/automation-sandbox-post-copy-config-generate/SKILL.md +239 -0
  3. package/skills/automation-sandbox-post-copy-config-generate/assets/config_template.json +21 -0
  4. package/skills/automation-sandbox-post-copy-config-generate/assets/json_schema.json +90 -0
  5. package/skills/automation-sandbox-post-copy-config-generate/examples/sample_sop_excerpt.md +31 -0
  6. package/skills/automation-sandbox-post-copy-config-generate/examples/sample_sop_to_config.json +50 -0
  7. package/skills/automation-sandbox-post-copy-config-generate/references/configuration_catalog.md +76 -0
  8. package/skills/automation-sandbox-post-copy-config-generate/references/sop_parsing_patterns.md +157 -0
  9. package/skills/automation-sandbox-post-copy-config-generate/references/source_format_handling.md +230 -0
  10. package/skills/dx-apexguru-scan/SKILL.md +403 -0
  11. package/skills/dx-apexguru-scan/examples/README.md +54 -0
  12. package/skills/dx-apexguru-scan/examples/sample-decoded-summary.json +176 -0
  13. package/skills/dx-apexguru-scan/examples/sample-full-no-runtime-response.json +26 -0
  14. package/skills/dx-apexguru-scan/examples/sample-succeeded-response.json +15 -0
  15. package/skills/dx-apexguru-scan/references/api-reference.md +81 -0
  16. package/skills/dx-apexguru-scan/references/authentication.md +134 -0
  17. package/skills/dx-apexguru-scan/references/error-handling.md +56 -0
  18. package/skills/dx-apexguru-scan/references/violation-catalog.md +28 -0
  19. package/skills/dx-apexguru-scan/scripts/build-zip.sh +87 -0
  20. package/skills/dx-apexguru-scan/scripts/decode-report.js +389 -0
  21. package/skills/dx-apexguru-scan/scripts/resolve-token.sh +151 -0
  22. package/skills/dx-apexguru-scan/scripts/run-scan.sh +153 -0
  23. package/skills/dx-apexguru-scan/scripts/scan.sh +96 -0
  24. package/skills/dx-apexguru-scan/scripts/validate-token.js +121 -0
  25. package/skills/dx-devops-pipeline-manage/SKILL.md +263 -0
  26. package/skills/dx-devops-pipeline-manage/examples/common-workflows.md +177 -0
  27. package/skills/dx-devops-pipeline-manage/references/cli-commands.md +298 -0
  28. package/skills/dx-devops-pipeline-manage/references/parsing-patterns.md +134 -0
  29. package/skills/dx-devops-pipeline-manage/scripts/check-activation-ready.sh +34 -0
  30. package/skills/dx-devops-pipeline-manage/scripts/validate-org-type.sh +17 -0
  31. package/skills/dx-devops-pipeline-manage/scripts/verify-operation.sh +82 -0
  32. package/skills/dx-devops-promote/SKILL.md +214 -0
  33. package/skills/dx-devops-promote/examples/promotion-workflows.md +212 -0
  34. package/skills/dx-devops-promote/references/cli-commands.md +303 -0
  35. package/skills/experience-lwc-base-components-integrate/SKILL.md +176 -0
  36. package/skills/experience-lwc-base-components-integrate/references/lbc-expert-guidance.md +127 -0
  37. package/skills/experience-lwc-base-components-integrate/references/lightning-component-index.md +179 -0
  38. package/skills/experience-lwc-base-components-integrate/references/lightning-components.md +5429 -0
  39. package/skills/experience-lwc-base-components-integrate/scripts/extract-component-docs.sh +61 -0
  40. package/skills/experience-lwc-rtl-validate/SKILL.md +149 -0
  41. package/skills/experience-lwc-rtl-validate/references/rtl-expert.md +892 -0
  42. package/skills/experience-lwc-rtl-validate/scripts/scan-rtl-css.sh +206 -0
  43. package/skills/experience-lwc-typescript-migrate/SKILL.md +207 -0
  44. package/skills/experience-lwc-typescript-migrate/assets/dts-template.ts +15 -0
  45. package/skills/experience-lwc-typescript-migrate/assets/type-patterns.ts +44 -0
  46. package/skills/experience-lwc-typescript-migrate/scripts/find-consumers.sh +128 -0
  47. package/skills/experience-ui-bundle-localize/SKILL.md +323 -0
  48. package/skills/experience-ui-bundle-localize/references/gotchas.md +249 -0
  49. package/skills/experience-ui-bundle-localize/references/i18n-setup.md +169 -0
  50. package/skills/experience-ui-bundle-localize/references/interpolation.md +311 -0
  51. package/skills/experience-ui-bundle-localize/references/label-xml.md +282 -0
  52. package/skills/experience-ui-bundle-localize/references/verifying.md +219 -0
  53. package/skills/experience-ui-bundle-localize/scripts/check-i18n-wired.sh +195 -0
  54. package/skills/experience-ui-bundle-localize/scripts/check-manifest-registered.sh +100 -0
  55. package/skills/experience-ui-bundle-localize/scripts/check-org-api-version.sh +40 -0
  56. package/skills/experience-ui-bundle-localize/scripts/detect-bundle-type.sh +57 -0
  57. package/skills/platform-custom-lightning-type-generate/SKILL.md +3 -0
  58. package/skills/platform-custom-lightning-type-generate/assets/primitive-types-and-constraints.md +1 -1
  59. package/skills/platform-mcp-tool-widget-coordinate/SKILL.md +250 -0
  60. package/skills/platform-mcp-tool-widget-coordinate/examples/action-name-source-prompt.md +74 -0
  61. package/skills/platform-mcp-tool-widget-coordinate/examples/apex-invocable-source-prompt.md +90 -0
  62. package/skills/platform-mcp-tool-widget-coordinate/examples/nested-object-source-prompt.md +191 -0
  63. package/skills/platform-mcp-tool-widget-coordinate/examples/pasted-tool-output-prompt.md +85 -0
  64. package/skills/platform-mcp-tool-widget-coordinate/references/build-plan-format.md +74 -0
  65. package/skills/platform-mcp-tool-widget-coordinate/references/mcp-tool-output-discovery.md +184 -0
  66. package/skills/platform-mcp-tool-widget-coordinate/references/two-clt-modeling.md +128 -0
  67. package/skills/platform-mcp-tool-widget-coordinate/references/validation-gates.md +181 -0
@@ -0,0 +1,206 @@
1
+ #!/usr/bin/env bash
2
+ # Deterministic RTL CSS scanner.
3
+ #
4
+ # Emits one line per physical-CSS declaration in the given files, in the form:
5
+ # <file>:<line>: <property>: <value> -> <logical-property>: <logical-value>
6
+ #
7
+ # Each output line is a raw finding the caller translates into a
8
+ # Step 6 report bullet (see SKILL.md).
9
+ #
10
+ # Usage:
11
+ # scripts/scan-rtl-css.sh <file1> [<file2> ...]
12
+ #
13
+ # Recognised patterns (CSS declarations only — HTML `class="…"` is NEVER scanned):
14
+ # left / right -> inset-inline-start / inset-inline-end
15
+ # margin-left / margin-right -> margin-inline-start / margin-inline-end
16
+ # padding-left / padding-right -> padding-inline-start / padding-inline-end
17
+ # text-align: left / right -> text-align: start / end
18
+ # border-left-* / border-right-* -> border-inline-start-* / border-inline-end-*
19
+ # float: left / float: right -> float: inline-start / float: inline-end
20
+ # transform: translateX(...) -> flag; needs sign-flip or logical alternative
21
+ #
22
+ # The scanner tokenizes declarations (split on `;` within braces) so a single
23
+ # minified line with multiple declarations produces one finding per physical
24
+ # declaration. It never rewrites selectors — only the `property: value` half
25
+ # of each declaration is transformed.
26
+ #
27
+ # Exit codes:
28
+ # 0 — scan completed (findings on stdout may be empty; that IS a valid result)
29
+ # 2 — usage error
30
+
31
+ set -euo pipefail
32
+
33
+ if [ "$#" -lt 1 ]; then
34
+ echo "usage: $0 <file1> [<file2> ...]" >&2
35
+ exit 2
36
+ fi
37
+
38
+ python3 - "$@" <<'PY'
39
+ import re
40
+ import sys
41
+
42
+ # Direct property renames (physical -> logical). Value is preserved verbatim.
43
+ PROP_RENAMES = {
44
+ "margin-left": "margin-inline-start",
45
+ "margin-right": "margin-inline-end",
46
+ "padding-left": "padding-inline-start",
47
+ "padding-right": "padding-inline-end",
48
+ "left": "inset-inline-start",
49
+ "right": "inset-inline-end",
50
+ }
51
+
52
+ # `border-left*` / `border-right*` prefix rewrites (keeps the suffix).
53
+ BORDER_PREFIXES = {
54
+ "border-left": "border-inline-start",
55
+ "border-right": "border-inline-end",
56
+ }
57
+
58
+ # Property/value pairs where the value is the physical keyword to swap.
59
+ VALUE_KEYWORDS = {
60
+ "text-align": {"left": "start", "right": "end"},
61
+ "float": {"left": "inline-start", "right": "inline-end"},
62
+ }
63
+
64
+ DECL_RE = re.compile(r"^\s*([a-zA-Z-]+)\s*:\s*(.*?)\s*$")
65
+
66
+ def emit(path, lineno, msg):
67
+ print(f"{path}:{lineno}: {msg}")
68
+
69
+ def scan_declaration(path, lineno, decl):
70
+ """Analyse a single `property: value` declaration."""
71
+ m = DECL_RE.match(decl)
72
+ if not m:
73
+ return
74
+ prop = m.group(1).lower()
75
+ value = m.group(2)
76
+ if not value:
77
+ return
78
+
79
+ # Direct property renames (value preserved verbatim).
80
+ if prop in PROP_RENAMES:
81
+ emit(path, lineno, f"{prop}: {value} -> {PROP_RENAMES[prop]}: {value}")
82
+ return
83
+
84
+ # border-left* / border-right* (with optional -color / -width / -style / etc).
85
+ for prefix, replacement in BORDER_PREFIXES.items():
86
+ if prop == prefix or prop.startswith(prefix + "-"):
87
+ new_prop = replacement + prop[len(prefix):]
88
+ emit(path, lineno, f"{prop}: {value} -> {new_prop}: {value}")
89
+ return
90
+
91
+ # text-align / float — swap the physical keyword in the value.
92
+ if prop in VALUE_KEYWORDS:
93
+ for phys, logical in VALUE_KEYWORDS[prop].items():
94
+ # Match the keyword as a whole token, case-insensitive.
95
+ if re.search(rf"\b{phys}\b", value, re.IGNORECASE):
96
+ new_value = re.sub(
97
+ rf"\b{phys}\b", logical, value, count=1, flags=re.IGNORECASE
98
+ )
99
+ emit(path, lineno, f"{prop}: {value} -> {prop}: {new_value}")
100
+ return
101
+
102
+ # transform: translateX(...) — flag for review.
103
+ if prop == "transform" and re.search(r"\btranslateX\s*\(", value):
104
+ emit(
105
+ path,
106
+ lineno,
107
+ f"{prop}: {value} # translateX flagged: sign-flip or use a logical alternative",
108
+ )
109
+
110
+ def strip_css_comments(source):
111
+ """Replace `/* ... */` runs with spaces (preserving \n so line numbers are
112
+ unchanged). Respects `"..."` / `'...'` strings so a `/*` inside a CSS
113
+ string is not treated as a comment opener."""
114
+ out = []
115
+ i = 0
116
+ n = len(source)
117
+ quote = None
118
+ while i < n:
119
+ ch = source[i]
120
+ if quote is not None:
121
+ out.append(ch)
122
+ if ch == "\\" and i + 1 < n:
123
+ out.append(source[i + 1])
124
+ i += 2
125
+ continue
126
+ if ch == quote:
127
+ quote = None
128
+ i += 1
129
+ continue
130
+ if ch == "/" and i + 1 < n and source[i + 1] == "*":
131
+ j = source.find("*/", i + 2)
132
+ if j == -1:
133
+ j = n
134
+ else:
135
+ j += 2
136
+ # Replace the comment with spaces, but keep newlines to preserve
137
+ # line numbers for the tokenizer below.
138
+ for k in range(i, j):
139
+ out.append("\n" if source[k] == "\n" else " ")
140
+ i = j
141
+ continue
142
+ if ch == '"' or ch == "'":
143
+ quote = ch
144
+ out.append(ch)
145
+ i += 1
146
+ return "".join(out)
147
+
148
+ def scan_file(path):
149
+ try:
150
+ with open(path, "r", encoding="utf-8", errors="replace") as fh:
151
+ source = fh.read()
152
+ except OSError as exc:
153
+ print(f"scan-rtl-css.sh: {exc}", file=sys.stderr)
154
+ return
155
+
156
+ lines = strip_css_comments(source).splitlines(keepends=True)
157
+
158
+ # Split by declaration boundaries (`;`) while tracking source line numbers.
159
+ # Each declaration starts on the line where its property begins.
160
+ in_selector = True # before the first `{`
161
+ depth = 0
162
+ buf = []
163
+ buf_lineno = 0
164
+
165
+ for lineno, raw in enumerate(lines, start=1):
166
+ i = 0
167
+ while i < len(raw):
168
+ ch = raw[i]
169
+ if ch == "{":
170
+ # We are entering a rule block — the buffer so far was a selector.
171
+ depth += 1
172
+ buf = []
173
+ buf_lineno = 0
174
+ in_selector = False
175
+ i += 1
176
+ continue
177
+ if ch == "}":
178
+ if buf and not in_selector:
179
+ scan_declaration(path, buf_lineno or lineno, "".join(buf))
180
+ buf = []
181
+ buf_lineno = 0
182
+ depth -= 1
183
+ if depth == 0:
184
+ in_selector = True
185
+ i += 1
186
+ continue
187
+ if ch == ";":
188
+ if not in_selector and buf:
189
+ scan_declaration(path, buf_lineno or lineno, "".join(buf))
190
+ buf = []
191
+ buf_lineno = 0
192
+ i += 1
193
+ continue
194
+ if not in_selector:
195
+ if not buf and not ch.isspace():
196
+ buf_lineno = lineno
197
+ buf.append(ch)
198
+ i += 1
199
+
200
+ # Trailing declaration without a terminating `;` (allowed in CSS).
201
+ if buf and not in_selector:
202
+ scan_declaration(path, buf_lineno or len(lines), "".join(buf))
203
+
204
+ for arg in sys.argv[1:]:
205
+ scan_file(arg)
206
+ PY
@@ -0,0 +1,207 @@
1
+ ---
2
+ name: experience-lwc-typescript-migrate
3
+ description: "Use when converting an existing JavaScript Lightning Web Component (.js, .html, .css) to TypeScript with full type annotations and a matching `.d.ts` file that exposes only the component's `@api` surface. TRIGGER when the user says \"convert LWC to TypeScript\", \"migrate LWC to TS\", \"rename .js to .ts for this component\", \"add types to my LWC\", \"generate .d.ts for this LWC\", \"type-annotate @api properties\", or \"produce declare module 'c/componentName' definitions\". DO NOT TRIGGER when the user is authoring a brand-new LWC from scratch (use experience-lwc-generate), generating Jest tests for an existing LWC (use experience-lwc-generate), or migrating an Aura component to LWC."
4
+ metadata:
5
+ version: "1.0"
6
+ relatedSkills:
7
+ - "experience-lwc-generate"
8
+ cliTools:
9
+ - tool: ["git"]
10
+ semver: ">=2.0.0"
11
+ - tool: ["jq"]
12
+ semver: ">=1.6"
13
+ - tool: ["tsc"]
14
+ semver: ">=4.0.0"
15
+ ---
16
+ <!-- adk-managed-skill -->
17
+ # Converting LWC to TypeScript
18
+
19
+ Convert a Lightning Web Component bundle from JavaScript to TypeScript. The
20
+ deliverable is a fully-typed `.ts` implementation **plus** a `.d.ts` file
21
+ that only exposes `@api` members (the public surface other LWCs consume).
22
+
23
+ ## When to Use This Skill
24
+
25
+ - User wants to migrate a single component or a folder of components from
26
+ `.js` to `.ts`.
27
+ - User needs a `.d.ts` for an existing LWC so other components (or an
28
+ external TypeScript host) can import it safely.
29
+ - User is adding type annotations to an already-renamed `.ts` LWC that
30
+ hasn't been properly typed yet.
31
+ - User wants JSDoc-style type hints upgraded to real TypeScript types.
32
+
33
+ ## Prerequisites
34
+
35
+ - The component builds and runs correctly in JavaScript today.
36
+ - `git` is available (the rename must preserve history via `git mv`).
37
+ - A TypeScript compiler is wired into the build (either the SFDX TS
38
+ pipeline or a standalone `tsc` step).
39
+
40
+ ---
41
+
42
+ ## Workflow
43
+
44
+ ### Step 1 — Read the component
45
+
46
+ Open every file in the bundle:
47
+
48
+ ```text
49
+ componentName/
50
+ ├── componentName.js
51
+ ├── componentName.html
52
+ ├── componentName.css
53
+ └── (possibly) __tests__/, __utam__/, existing .d.ts
54
+ ```
55
+
56
+ Understand:
57
+
58
+ - What extends `LightningElement`? What is the class name?
59
+ - Which fields and methods carry the `@api` decorator?
60
+ - Which properties/methods have existing JSDoc (use as a type hint
61
+ starting point, but validate against actual usage — JSDoc lies).
62
+ - Which parameters / return types can you infer from how the code is
63
+ called internally?
64
+
65
+ ### Step 2 — Rename `.js` → `.ts` using `git mv`
66
+
67
+ ```bash
68
+ git mv componentName/componentName.js componentName/componentName.ts
69
+ ```
70
+
71
+ Repeat for any helper `.js` files in the bundle (unless they're already
72
+ `.ts`). **Never** plain `mv` — that loses the history link TypeScript
73
+ reviewers rely on.
74
+
75
+ ### Step 3 — Add type annotations in the `.ts`
76
+
77
+ Apply types in this priority order so you stop as soon as the public
78
+ contract is solid:
79
+
80
+ 1. **`@api` properties and methods first.** Generate JSDoc if it's
81
+ missing, then translate JSDoc types to TS syntax (`string`, `number`,
82
+ `boolean`, `Promise<T>`). Validate each JSDoc claim against the code
83
+ before trusting it.
84
+ 2. **Complex shapes become `interface` or `type` aliases** — not inline
85
+ shapes repeated everywhere.
86
+ 3. **Optional members use `?`** only when the value is genuinely allowed
87
+ to be `undefined`. Do not sprinkle `?` defensively.
88
+ 4. **Private/internal state** — still type it, but don't export the
89
+ types. Use `private` for members that must never be touched by
90
+ consumers.
91
+ 5. **Event handlers** — prefer precise DOM event types:
92
+ - `MouseEvent` for `onclick` (and other click-like handlers). `click`
93
+ is dispatched as a `MouseEvent` — including keyboard-activated
94
+ clicks — so typing it as `PointerEvent` would let handlers rely on
95
+ pointer-only fields (`pointerType`, `pressure`, etc.) that are
96
+ undefined in those cases.
97
+ - `PointerEvent` for `onpointerdown` / `onpointerup` / `onpointermove`
98
+ and other `pointer*` handlers where pointer-specific fields are
99
+ actually meaningful.
100
+ - `CustomEvent<{ detail: ... }>` for LWC custom events.
101
+ - `Event` is the last resort; document why when using it.
102
+ 6. **Async methods** always return `Promise<T>` — never bare `T`.
103
+ 7. **Avoid `any`.** If you genuinely can't type something, use `unknown`
104
+ and narrow with a type guard.
105
+
106
+ #### Reference patterns
107
+
108
+ Load [[assets/type-patterns.ts|assets/type-patterns.ts]] as an inline example
109
+ covering property types, method types, and event handler types.
110
+
111
+ ### Step 4 — Generate the `.d.ts`
112
+
113
+ Create `componentName.d.ts` next to the `.ts`. It must:
114
+
115
+ - Contain **only `@api` members** — no private state, no internal
116
+ methods, no lifecycle hooks unless they are themselves `@api`.
117
+ - Preserve `@api` JSDoc verbatim (including `@type`, `@required`,
118
+ `@default`, `@param`, `@returns` tags) directly above each declaration.
119
+ - Declare the LWC module namespace `c/componentName` (or the org's
120
+ namespace if different).
121
+
122
+ Template: load [[assets/dts-template.ts|assets/dts-template.ts]] as the
123
+ starting `.d.ts` shape.
124
+
125
+ If the component has **no** `@api` members, still produce the module
126
+ declaration with a comment explaining there's no public surface — don't
127
+ skip the file.
128
+
129
+ ### Step 5 — Compile and test
130
+
131
+ - Run the TypeScript compiler (`tsc --noEmit` or the build's equivalent).
132
+ Resolve every error before calling it done; no `@ts-ignore` patches.
133
+ - Run the component's existing Jest tests. The behavior should be
134
+ identical.
135
+ - Run the bundled consumer-finder unconditionally — empty output is a
136
+ valid result, not a reason to skip. The script resolves the search
137
+ paths from `sfdx-project.json`'s `packageDirectories` (or falls back
138
+ to `<project-root>`), rejects any entry that escapes the project root,
139
+ and performs the LWC-import search internally so the invocation is
140
+ fully deterministic:
141
+
142
+ ```bash
143
+ "<skill_dir>/scripts/find-consumers.sh" "<project-root>" "<componentName>"
144
+ ```
145
+
146
+ For each match, confirm the consumer's expected types still align with
147
+ the new `.d.ts` public surface.
148
+
149
+ ### Step 6 — Expected final bundle shape
150
+
151
+ ```text
152
+ componentName/
153
+ ├── componentName.ts # Main TypeScript implementation
154
+ ├── componentName.html # Template (unchanged)
155
+ ├── componentName.css # Styles (unchanged)
156
+ └── componentName.d.ts # Type definitions (new)
157
+ ```
158
+
159
+ ---
160
+
161
+ ## Verification Checklist
162
+
163
+ **Before conversion:**
164
+
165
+ - [ ] Component is valid JS and all tests pass.
166
+ - [ ] You've identified every `@api` member and its intended type.
167
+
168
+ **After conversion:**
169
+
170
+ - [ ] `git mv` was used so history is preserved.
171
+ - [ ] Every variable and parameter in the `.ts` has a concrete type
172
+ (no implicit `any`).
173
+ - [ ] Complex object shapes live in `interface` / `type` aliases, not
174
+ inline repeats.
175
+ - [ ] Optional `?` is only on genuinely optional fields.
176
+ - [ ] `.d.ts` exists, declares `c/componentName`, extends
177
+ `LightningElement`, includes **only** `@api` members.
178
+ - [ ] Every `@api` JSDoc is preserved verbatim in the `.d.ts`.
179
+ - [ ] `tsc` passes with zero errors; no `@ts-ignore` or `any` used as a
180
+ workaround.
181
+ - [ ] Jest tests still pass.
182
+
183
+ ---
184
+
185
+ ## Common Pitfalls
186
+
187
+ - **Using `any` to silence errors.** Solve the actual type instead.
188
+ If the value is truly unknown, use `unknown` + a type guard.
189
+ - **Including private members in the `.d.ts`.** The `.d.ts` is the
190
+ public contract. Internal lifecycle and helpers must not leak.
191
+ - **Losing JSDoc during the rename.** Scan before and after — JSDoc
192
+ comments on `@api` members must appear in both the `.ts` and `.d.ts`.
193
+ - **Skipping `git mv`.** Makes review miserable and confuses blame.
194
+ - **Forgetting async return types.** `foo()` with an `async` keyword
195
+ always returns a `Promise`. Declare it.
196
+ - **Typing `onclick` as `PointerEvent`.** `click` is a `MouseEvent`
197
+ (keyboard-triggered clicks included), so `PointerEvent` fields like
198
+ `pointerType` are undefined for those events. Type `onclick` as
199
+ `MouseEvent`; reserve `PointerEvent` for `onpointer*` handlers. Use
200
+ `MouseEvent | TouchEvent` only when the code branches on `TouchEvent`
201
+ distinctly.
202
+
203
+ ## Support Resources
204
+
205
+ - [LWC TypeScript docs](https://developer.salesforce.com/docs/platform/lwc/guide/ts.html)
206
+ - [TypeScript Handbook](https://www.typescriptlang.org/docs/)
207
+ - [LWC Developer Guide](https://developer.salesforce.com/docs/component-library/documentation/en/lwc)
@@ -0,0 +1,15 @@
1
+ declare module 'c/componentName' {
2
+ import { LightningElement } from 'lwc';
3
+
4
+ export default class ComponentName extends LightningElement {
5
+ /**
6
+ * [Preserved JSDoc comment here]
7
+ */
8
+ propertyName: type;
9
+
10
+ /**
11
+ * [Preserved JSDoc comment here]
12
+ */
13
+ methodName(param: type): returnType;
14
+ }
15
+ }
@@ -0,0 +1,44 @@
1
+ // Reference patterns for typing an LWC. Members are illustrative — copy the
2
+ // shapes you need into your own class.
3
+
4
+ import { LightningElement, api } from 'lwc';
5
+
6
+ interface DataItem {
7
+ id: string;
8
+ name: string;
9
+ }
10
+
11
+ export default class TypePatternsExample extends LightningElement {
12
+ // Property types
13
+ @api title: string = '';
14
+ @api count: number = 0;
15
+ @api isVisible: boolean = false;
16
+ @api items: string[] = [];
17
+ @api config: { enabled: boolean; name: string } = { enabled: false, name: '' };
18
+ @api optionalProp?: string;
19
+
20
+ // Method types
21
+ @api
22
+ handleClick(): void {
23
+ /* ... */
24
+ }
25
+
26
+ @api
27
+ updateItem(id: string, data: { name: string; value: number }): Promise<void> {
28
+ return Promise.resolve();
29
+ }
30
+
31
+ @api
32
+ async loadData(): Promise<DataItem[]> {
33
+ return [];
34
+ }
35
+
36
+ // Event handlers
37
+ handleButtonClick(event: MouseEvent): void {
38
+ /* ... */
39
+ }
40
+
41
+ handleCustomEvent(event: CustomEvent<{ detail: string }>): void {
42
+ /* ... */
43
+ }
44
+ }
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env bash
2
+ # find-consumers.sh — deterministically find LWC consumers of a component.
3
+ #
4
+ # Usage: find-consumers.sh <project-root> <componentName>
5
+ #
6
+ # Behavior:
7
+ # 1. Canonicalize <project-root>. Fail if it is not a directory.
8
+ # 2. If <project-root>/sfdx-project.json exists, read every
9
+ # packageDirectories[].path and canonicalize each one relative to the
10
+ # project root. Reject absolute paths and any path that escapes the
11
+ # canonical project root (S2 safety). Skip paths that do not resolve
12
+ # to a real directory. If no usable paths remain, fail.
13
+ # Otherwise the search list is just [project-root].
14
+ # 3. Grep every resolved directory for LWC import statements matching
15
+ # "import ... from 'c/<componentName>'" (single OR double quotes),
16
+ # limited to .js and .ts files.
17
+ # 4. Print matches (grep -H format: <path>:<match>) to stdout, one per
18
+ # line. Empty output is a valid result — the caller should treat it
19
+ # as "no consumers".
20
+ # 5. Exits 0 on success (matches or no matches). Nonzero only on invalid
21
+ # arguments, unreadable project, or a malformed sfdx-project.json.
22
+ #
23
+ # The consumer-import search algorithm and directory-safety canonicalization
24
+ # both live here so SKILL.md contains no algorithmic logic (A9), and the
25
+ # search list is built into an array so paths with whitespace survive (no
26
+ # xargs word-splitting).
27
+ set -euo pipefail
28
+
29
+ if [ "$#" -lt 2 ] || [ -z "${1:-}" ] || [ -z "${2:-}" ]; then
30
+ echo "find-consumers: missing arguments" >&2
31
+ echo "usage: find-consumers.sh <project-root> <componentName>" >&2
32
+ exit 64
33
+ fi
34
+
35
+ root_in="$1"
36
+ component="$2"
37
+
38
+ if [ ! -d "$root_in" ]; then
39
+ echo "find-consumers: <project-root> is not a directory: $root_in" >&2
40
+ exit 66
41
+ fi
42
+
43
+ # Canonicalize the project root so subsequent containment checks are exact.
44
+ root="$(cd "$root_in" && pwd -P)"
45
+
46
+ # Validate componentName — camelCase-ish alphanumeric, no separators that
47
+ # could inject shell/regex metacharacters into the grep pattern below.
48
+ if ! printf '%s' "$component" | grep -Eq '^[A-Za-z][A-Za-z0-9]*$'; then
49
+ echo "find-consumers: componentName must match [A-Za-z][A-Za-z0-9]*: $component" >&2
50
+ exit 64
51
+ fi
52
+
53
+ cfg="$root/sfdx-project.json"
54
+ search_dirs=()
55
+
56
+ if [ -f "$cfg" ]; then
57
+ # jq -r prints one path per line; empty and null entries are elided.
58
+ raw_paths="$(jq -r '.packageDirectories[]?.path // empty' "$cfg" 2>/dev/null)" || {
59
+ echo "find-consumers: $cfg is not valid JSON" >&2
60
+ exit 65
61
+ }
62
+
63
+ while IFS= read -r p; do
64
+ [ -z "$p" ] && continue
65
+
66
+ # S2: reject absolute paths outright — a project cfg has no business
67
+ # pointing at anything outside its own root.
68
+ case "$p" in
69
+ /*)
70
+ echo "find-consumers: rejecting absolute packageDirectory path: $p" >&2
71
+ continue
72
+ ;;
73
+ esac
74
+
75
+ # Resolve relative to the (canonical) project root.
76
+ candidate="$root/${p%/}"
77
+
78
+ if [ ! -d "$candidate" ]; then
79
+ echo "find-consumers: packageDirectory not found on disk: $candidate" >&2
80
+ continue
81
+ fi
82
+
83
+ # Canonicalize the candidate and ensure it stays inside the project
84
+ # root. This catches `..` escapes and symlinks that point outward.
85
+ canon="$(cd "$candidate" && pwd -P)"
86
+ case "$canon" in
87
+ "$root"|"$root"/*) ;;
88
+ *)
89
+ echo "find-consumers: rejecting packageDirectory that escapes project root: $canon" >&2
90
+ continue
91
+ ;;
92
+ esac
93
+
94
+ search_dirs+=("$canon")
95
+ done <<< "$raw_paths"
96
+
97
+ if [ "${#search_dirs[@]}" -eq 0 ]; then
98
+ echo "find-consumers: no usable packageDirectory paths in $cfg" >&2
99
+ exit 65
100
+ fi
101
+ else
102
+ search_dirs=("$root")
103
+ fi
104
+
105
+ # Build the grep pattern. We anchor on the module-specifier clause —
106
+ # `from <ws> ['"]c/<component>['"]` — which uniquely identifies a consumer
107
+ # import regardless of whether the binding is single-line, multi-line
108
+ # destructured, a `type` import, or a re-export. The `import ... from`
109
+ # prefix is intentionally dropped because in a multi-line import the
110
+ # closing `} from 'c/x'` line does not contain the word `import`; a
111
+ # per-line grep that requires both tokens on the same line would miss
112
+ # it. `[[:space:]]+` handles tabs and repeated spaces. Component name
113
+ # was already validated so it is safe to interpolate into the regex.
114
+ pattern="from[[:space:]]+['\"]c/${component}['\"]"
115
+
116
+ # Array-quoted expansion preserves paths that contain whitespace. grep
117
+ # returns 1 when there are no matches — treat that as a successful empty
118
+ # result, not an error.
119
+ grep -rHE "$pattern" \
120
+ --include='*.js' --include='*.ts' \
121
+ "${search_dirs[@]}" || rc=$?
122
+
123
+ # grep exit codes: 0=matched, 1=no matches, 2+=error. Only surface real errors.
124
+ rc="${rc:-0}"
125
+ if [ "$rc" -ge 2 ]; then
126
+ exit "$rc"
127
+ fi
128
+ exit 0