@salesforce/vite-plugin-lwc-ui-bundle 11.13.1 → 11.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/index.d.ts +32 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -5
- package/dist/index.js.map +1 -1
- package/dist/providers/access-check.d.ts.map +1 -1
- package/dist/providers/gate.d.ts.map +1 -1
- package/dist/providers/i18n.d.ts +16 -0
- package/dist/providers/i18n.d.ts.map +1 -1
- package/dist/providers/index.d.ts +2 -0
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +3 -178
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/labels-graphql/index.d.ts.map +1 -1
- package/dist/providers/labels-graphql/index.js +12 -3
- package/dist/providers/labels-graphql/index.js.map +1 -1
- package/dist/providers/labels-graphql/runtime.d.ts +21 -14
- package/dist/providers/labels-graphql/runtime.d.ts.map +1 -1
- package/dist/providers/labels-graphql/runtime.js +39 -26
- package/dist/providers/labels-graphql/runtime.js.map +1 -1
- package/dist/providers/platform-graphql/constants.d.ts +15 -0
- package/dist/providers/platform-graphql/constants.d.ts.map +1 -0
- package/dist/providers/platform-graphql/i18n-static.d.ts +19 -0
- package/dist/providers/platform-graphql/i18n-static.d.ts.map +1 -0
- package/dist/providers/platform-graphql/index.d.ts +58 -0
- package/dist/providers/platform-graphql/index.d.ts.map +1 -0
- package/dist/providers/platform-graphql/index.js +312 -0
- package/dist/providers/platform-graphql/index.js.map +1 -0
- package/dist/providers/platform-graphql/runtime.d.ts +30 -0
- package/dist/providers/platform-graphql/runtime.d.ts.map +1 -0
- package/dist/providers/platform-graphql/runtime.js +189 -0
- package/dist/providers/platform-graphql/runtime.js.map +1 -0
- package/docs/consumer-guide.md +125 -4
- package/docs/limitations.md +365 -0
- package/docs/migration-guide.md +334 -0
- package/package.json +5 -5
- package/skills/setup-lwc-vite-plugin/SKILL.md +44 -1
- package/skills/setup-lwc-vite-plugin/references/known-pitfalls.md +57 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
# Migrating Platform LWC to an LWC UI Bundle
|
|
2
|
+
|
|
3
|
+
This guide walks you through porting a Lightning Web Component that runs **inside a
|
|
4
|
+
Salesforce org** to an **LWC UI Bundle** compiled off-platform with
|
|
5
|
+
`@salesforce/vite-plugin-lwc-ui-bundle`. The build emits a static `dist/` (an
|
|
6
|
+
`index.html` plus hashed `assets/*`, or a single inlined `dist/index.html` if you add
|
|
7
|
+
`vite-plugin-singlefile`) that runs in any browser, an MCP host, or a Salesforce UI
|
|
8
|
+
Bundle served from the `*.salesforce.app` domain.
|
|
9
|
+
|
|
10
|
+
> **New to the plugin?** Read the [Consumer Guide](consumer-guide.md) first for the
|
|
11
|
+
> baseline Vite setup. This guide focuses on what _changes_ when the component was
|
|
12
|
+
> originally written for the platform, and links to the
|
|
13
|
+
> [Limitations reference](limitations.md) for the features that don't come across.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## What actually changes
|
|
18
|
+
|
|
19
|
+
A platform LWC and an LWC UI Bundle share the **same component source** — `.js`,
|
|
20
|
+
`.html`, and `.css` files compile unchanged through `@lwc/rollup-plugin`. What
|
|
21
|
+
changes is the _environment_ your component runs in:
|
|
22
|
+
|
|
23
|
+
| On platform | In an LWC UI Bundle |
|
|
24
|
+
| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
25
|
+
| The Lightning runtime mounts your component | You mount it yourself in `bootstrap.js` via `createElement` |
|
|
26
|
+
| `@salesforce/*` modules resolve to live org services | Scoped **providers** resolve them to generated JS (labels, i18n, gates, …) |
|
|
27
|
+
| LDS wire adapters read from a reactive client-side store | Registered adapters dispatch through the Data SDK's host bridge to MCP tools; no reactive store |
|
|
28
|
+
| Imperative Apex, LMS, and navigation are ambient | They have **no provider** — you supply a stub or a GraphQL/REST replacement |
|
|
29
|
+
| Metadata (`.js-meta.xml`) drives exposure and targets | `.js-meta.xml` is ignored by the build; a `ui-bundle` manifest drives routing |
|
|
30
|
+
|
|
31
|
+
The migration is therefore mostly about **the edges** of your component — how it gets
|
|
32
|
+
mounted, how it gets data, and which platform capabilities it reaches for. The
|
|
33
|
+
component logic in the middle usually ports verbatim.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Migration at a glance
|
|
38
|
+
|
|
39
|
+
1. [Inventory your dependencies](#step-1-inventory-your-dependencies) — find every
|
|
40
|
+
`import` that reaches the platform.
|
|
41
|
+
2. [Set up the Vite project](#step-2-set-up-the-vite-project) — `vite.config.js`,
|
|
42
|
+
`index.html`, `bootstrap.js`.
|
|
43
|
+
3. [Map each dependency](#step-3-map-each-dependency) to a provider, an SDK call, or
|
|
44
|
+
a stub.
|
|
45
|
+
4. [Replace data access](#step-4-replace-data-access) — LDS/Apex → GraphQL or the
|
|
46
|
+
Data SDK.
|
|
47
|
+
5. [Handle the unsupported edges](#step-5-handle-the-unsupported-edges) — LMS,
|
|
48
|
+
navigation, and other org-only APIs.
|
|
49
|
+
6. [Build, run, and verify](#step-6-build-run-and-verify).
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Step 1: Inventory your dependencies
|
|
54
|
+
|
|
55
|
+
Before touching config, list every non-relative import in your component tree. These
|
|
56
|
+
are the imports that reach beyond your own `.js`/`.html` files and into the platform:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# From your LWC source root — list the platform-facing specifiers your bundle uses
|
|
60
|
+
grep -rhoE "from \"(@salesforce/[^\"]+|lightning/[^\"]+|force/[^\"]+|aura)\"" \
|
|
61
|
+
force-app/main/default/lwc | sort -u
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Sort what you find into four buckets — this table _is_ your migration plan:
|
|
65
|
+
|
|
66
|
+
| Import pattern | Bucket | Action |
|
|
67
|
+
| ----------------------------------------------------------------------------------------------------- | ------------------ | ------------------------------------------------------------------------------------ |
|
|
68
|
+
| `lwc`, `lightning/*` base components | **Compiles as-is** | Nothing — bundled via `@lwc/rollup-plugin` + npm |
|
|
69
|
+
| `@salesforce/label/*`, `@salesforce/i18n/*`, `client`, `gate`, `accessCheck` | **Has a provider** | Add the matching `builtins.*` provider |
|
|
70
|
+
| `lightning/graphql`, registered LDS adapters | **Data access** | See [Step 4](#step-4-replace-data-access) |
|
|
71
|
+
| `@salesforce/apex/*`, `lightning/messageService`, `lightning/navigation`, `force/*`, `aura`, `logger` | **Unsupported** | Replace with GraphQL/REST or a stub — [Step 5](#step-5-handle-the-unsupported-edges) |
|
|
72
|
+
|
|
73
|
+
Anything in the last two buckets needs a decision. The [Limitations
|
|
74
|
+
reference](limitations.md) covers each unsupported module and its recommended
|
|
75
|
+
workaround in detail.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Step 2: Set up the Vite project
|
|
80
|
+
|
|
81
|
+
Follow [Consumer Guide → Off-Platform Build](consumer-guide.md#off-platform-build) to
|
|
82
|
+
add `vite.config.js`, `index.html`, and `bootstrap.js`. The one part worth calling out
|
|
83
|
+
for migrations is that **you now own mounting**. On platform, the framework
|
|
84
|
+
instantiated your top-level component; off-platform, `bootstrap.js` does:
|
|
85
|
+
|
|
86
|
+
```js
|
|
87
|
+
// bootstrap.js
|
|
88
|
+
import "@salesforce-ux/design-system/assets/styles/salesforce-lightning-design-system.css";
|
|
89
|
+
import "@lwc/synthetic-shadow";
|
|
90
|
+
import { createElement } from "lwc";
|
|
91
|
+
import App from "c/myApp"; // your existing root component, unchanged
|
|
92
|
+
|
|
93
|
+
const el = createElement("c-my-app", { is: App });
|
|
94
|
+
|
|
95
|
+
// Public @api properties the org used to set — set them here instead
|
|
96
|
+
el.recordId = "001xx000003DGb2AAG";
|
|
97
|
+
|
|
98
|
+
document.getElementById("app").appendChild(el);
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Any `@api` property the org used to inject (`recordId`, `objectApiName`, flexipage
|
|
102
|
+
attributes, etc.) now has no injector — set it explicitly when you mount, or read it
|
|
103
|
+
from the URL/host. This is the single most common migration surprise.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Step 3: Map each dependency
|
|
108
|
+
|
|
109
|
+
For every import in the **"has a provider"** bucket, add the matching provider to
|
|
110
|
+
your `vite.config.js`. The defaults cover the common set:
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
import lwcVitePlugin, { builtins } from "@salesforce/vite-plugin-lwc-ui-bundle";
|
|
114
|
+
|
|
115
|
+
lwcVitePlugin({
|
|
116
|
+
modules: {
|
|
117
|
+
dirs: [{ path: "force-app/main/default/lwc", namespace: "c" }],
|
|
118
|
+
npm: ["lightning-base-components"],
|
|
119
|
+
},
|
|
120
|
+
providers: [
|
|
121
|
+
builtins.label(), // @salesforce/label/*
|
|
122
|
+
builtins.i18n(), // @salesforce/i18n/*
|
|
123
|
+
builtins.accessCheck(), // @salesforce/accessCheck/* (default false)
|
|
124
|
+
builtins.client(), // @salesforce/client/*
|
|
125
|
+
builtins.gate(), // @salesforce/gate/* (default open)
|
|
126
|
+
builtins.primitiveUtils(), // lightning/primitiveUtils
|
|
127
|
+
builtins.lds(), // lightning/uiRecordApi, lightning/graphql, … (MCP-backed)
|
|
128
|
+
],
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
If you pass a `providers` array, it **replaces** the defaults — list every provider
|
|
133
|
+
you need. Omit `providers` entirely to get the full default set: `label`, `i18n`,
|
|
134
|
+
`accessCheck`, `client`, `gate`, `primitiveUtils`, and `lds`. Note there is no separate
|
|
135
|
+
GraphQL provider — `lightning/graphql` is handled by `lds()`, whose default registry
|
|
136
|
+
routes `graphql`/`executeMutation` to an MCP tool.
|
|
137
|
+
|
|
138
|
+
A few provider-specific notes for migrated code:
|
|
139
|
+
|
|
140
|
+
- **Labels** default to a human-readable fallback derived from the key
|
|
141
|
+
(`c.appTitle` → "App Title"). Port real values with
|
|
142
|
+
`builtins.label({ "c.appTitle": "My App" })`, or copy them from your
|
|
143
|
+
`CustomLabels.labels-meta.xml`. If you have a live data path and want translated
|
|
144
|
+
values at the user's locale, swap in `builtins.labelsGraphql()` instead — see
|
|
145
|
+
[Limitations → Custom Labels](limitations.md#custom-labels--static-default-or-live-via-graphql).
|
|
146
|
+
- **Gates** default **open** and **access checks** default **false** off-platform.
|
|
147
|
+
If your component branches on either, pass overrides so its behavior matches the
|
|
148
|
+
org: `builtins.gate({ myGate: false })`, `builtins.accessCheck({ MyPerm: true })`.
|
|
149
|
+
- **i18n** derives locale/currency from the browser via the `Intl` API; CLDR format
|
|
150
|
+
patterns use en-US defaults. Locale-identity is live, format specifics are static.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Step 4: Replace data access
|
|
155
|
+
|
|
156
|
+
This is the heart of most migrations. Platform LWC reaches org data three ways:
|
|
157
|
+
**LDS wire adapters**, **imperative Apex**, and **`lightning/graphql`**. Off-platform,
|
|
158
|
+
GraphQL is the through-line — it works in both a component wire and imperatively via
|
|
159
|
+
the Data SDK.
|
|
160
|
+
|
|
161
|
+
### LDS wire adapters (`getRecord`, `createRecord`, …)
|
|
162
|
+
|
|
163
|
+
The `lds()` provider (on by default) routes a **registered** set of adapters to MCP
|
|
164
|
+
tools. Out of the box that covers `getRecord`, `createRecord`, `updateRecord`
|
|
165
|
+
(`lightning/uiRecordApi`) and `getObjectInfo_imperative` (`lightning/uiObjectInfoApi`). Code
|
|
166
|
+
that uses only these ports **unchanged**:
|
|
167
|
+
|
|
168
|
+
```js
|
|
169
|
+
import { getRecord } from "lightning/uiRecordApi";
|
|
170
|
+
|
|
171
|
+
// Same @wire on platform and off — the adapter resolves to an MCP tool off-platform
|
|
172
|
+
@wire(getRecord, { recordId: "$recordId", fields: FIELDS })
|
|
173
|
+
wiredRecord({ data, error }) { /* ... */ }
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Two behavioral differences to plan for:
|
|
177
|
+
|
|
178
|
+
- **No reactive store.** `subscribe()` on the imperative read shapes is a deliberate
|
|
179
|
+
no-op — the callback never fires. Data does not auto-refresh when it changes
|
|
180
|
+
elsewhere; call `refresh()` (on the `subscribable-refreshable` shape) or re-query.
|
|
181
|
+
- **Only registered exports resolve.** Any LDS export not in the registry (e.g.
|
|
182
|
+
`getRelatedListRecords`, `getListUi`) passes through to normal `lightning/*`
|
|
183
|
+
resolution — which has no off-platform implementation, so the build fails to resolve
|
|
184
|
+
it. Register it with your own MCP tool via the `lds({ ... })` config, or replace the
|
|
185
|
+
call with GraphQL. See [Limitations → LDS](limitations.md#lds--partial-adapter-coverage-no-store).
|
|
186
|
+
|
|
187
|
+
### Imperative Apex (`@salesforce/apex/MyClass.myMethod`)
|
|
188
|
+
|
|
189
|
+
There is **no Apex provider**, and `@AuraEnabled` methods are **not** reachable
|
|
190
|
+
off-platform. Migrate each imperative Apex call to one of:
|
|
191
|
+
|
|
192
|
+
1. **GraphQL** — if the method just reads records, replace it with a UI API GraphQL
|
|
193
|
+
query (preferred; see below).
|
|
194
|
+
2. **Apex REST** — expose the logic as an `@RestResource` and call it through the Data
|
|
195
|
+
SDK's `fetch`. Budget time to build this surface for any business logic you can't
|
|
196
|
+
express as GraphQL.
|
|
197
|
+
|
|
198
|
+
See [Limitations → Imperative Apex](limitations.md#imperative-apex-salesforceapexmyclassmymethod)
|
|
199
|
+
for the `sdk.fetch?.()` snippet and the `@AuraEnabled`-vs-`@RestResource` distinction.
|
|
200
|
+
|
|
201
|
+
### `lightning/graphql` and the Data SDK
|
|
202
|
+
|
|
203
|
+
`lightning/graphql` keeps working via the default `builtins.lds()` registry — no
|
|
204
|
+
separate provider, and `@wire(graphql, …)` components port unchanged. The wire adapter
|
|
205
|
+
dispatches through the MCP `graphqlQuery` tool. For imperative reads, use
|
|
206
|
+
`@salesforce/platform-sdk` directly:
|
|
207
|
+
|
|
208
|
+
```js
|
|
209
|
+
import { createDataSDK, gql } from "@salesforce/platform-sdk";
|
|
210
|
+
|
|
211
|
+
const sdk = await createDataSDK();
|
|
212
|
+
const result = await sdk.graphql?.query({
|
|
213
|
+
query: gql`
|
|
214
|
+
query GetAccounts {
|
|
215
|
+
uiapi {
|
|
216
|
+
query {
|
|
217
|
+
Account(first: 10) {
|
|
218
|
+
edges {
|
|
219
|
+
node {
|
|
220
|
+
Id
|
|
221
|
+
Name {
|
|
222
|
+
value
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
`,
|
|
231
|
+
});
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
`sdk.graphql?.query(...)` resolves against whatever surface the bundle runs on — a real
|
|
235
|
+
MCP host's bridge, or the authenticated same-origin session on `*.salesforce.app`. Where
|
|
236
|
+
each data path gets live data (and why a bundle on any other origin `401`s) is covered in
|
|
237
|
+
[Limitations → Data access & auth](limitations.md#data-access-and-authentication).
|
|
238
|
+
|
|
239
|
+
For **local development without a real host**, install a guarded host-bridge mock in
|
|
240
|
+
your entry script (on ChatGPT that's a `window.openai.callTool` shim) so
|
|
241
|
+
`@wire(graphql)` / `@wire(getRecord)` return data — see
|
|
242
|
+
[Consumer Guide → Step 4: `bootstrap.js`](consumer-guide.md#step-4-create-bootstrapjs).
|
|
243
|
+
(`lwcProxy()` is a separate, optional companion plugin — it forwards `/services/*` for
|
|
244
|
+
_legacy_ `lightning/*` modules that call REST directly, not for the MCP-backed LDS and
|
|
245
|
+
GraphQL adapters.)
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Step 5: Handle the unsupported edges
|
|
250
|
+
|
|
251
|
+
Some platform capabilities have no off-platform equivalent. The repo's convention is
|
|
252
|
+
a **hand-written stub** wired through the plugin's `stubs` option — the same pattern
|
|
253
|
+
the [`lwc-records` example](../../../examples/lwc-axl/lwc-records) uses for
|
|
254
|
+
`force/navigation`, `aura`, and `logger`.
|
|
255
|
+
|
|
256
|
+
Each unsupported capability has a recommended stub or replacement documented in the
|
|
257
|
+
Limitations reference — this step is about wiring them through the plugin's `stubs`
|
|
258
|
+
option. The pattern is the same for each: point the specifier at a hand-written stub
|
|
259
|
+
file that exports the shapes your code imports.
|
|
260
|
+
|
|
261
|
+
```js
|
|
262
|
+
// vite.config.js — one entry per unsupported specifier
|
|
263
|
+
lwcVitePlugin({
|
|
264
|
+
stubs: {
|
|
265
|
+
"lightning/navigation": "src/stubs/navigation.js",
|
|
266
|
+
"lightning/messageService": "src/stubs/message-service.js",
|
|
267
|
+
aura: "src/stubs/aura.js",
|
|
268
|
+
"force/someModule": "src/stubs/some-module.js",
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
For the actual stub contents and per-module guidance:
|
|
274
|
+
|
|
275
|
+
- **Navigation** (`lightning/navigation`, `force/navigation`) — no-op `NavigationMixin`,
|
|
276
|
+
then decide per call site: [Limitations → Navigation](limitations.md#navigation-lightningnavigation-forcenavigation).
|
|
277
|
+
- **Lightning Message Service** (`lightning/messageService`) — `EventTarget`-backed bus
|
|
278
|
+
for same-bundle messaging, host bridge across surfaces:
|
|
279
|
+
[Limitations → LMS](limitations.md#lightning-message-service-lightningmessageservice).
|
|
280
|
+
- **`aura`, `logger`, `force/*` and other core-only modules** — export just the bindings
|
|
281
|
+
your code imports (often no-ops): [Limitations → Aura and other `force/*`](limitations.md#aura-and-other-force--core-only-modules).
|
|
282
|
+
|
|
283
|
+
If the build fails with `Rollup failed to resolve import "force/someModule"` (or
|
|
284
|
+
`aura`), that specifier needs a stub. The `lwc-records` example ships real stubs for
|
|
285
|
+
`force/navigation`, `aura`, and `logger`.
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Step 6: Build, run, and verify
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
npm run build # → dist/ (index.html + assets/*, or a single dist/index.html
|
|
293
|
+
# if you added vite-plugin-singlefile)
|
|
294
|
+
open dist/index.html
|
|
295
|
+
|
|
296
|
+
npm run dev # dev server with live reload (+ lwcProxy for live data)
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
A migration is done when:
|
|
300
|
+
|
|
301
|
+
- The bundle **builds** with no unresolved imports (unresolved = a dependency you
|
|
302
|
+
haven't yet mapped to a provider, SDK call, or stub — go back to Step 3).
|
|
303
|
+
- Every `@api` the org used to set is set at mount time or read from the host/URL.
|
|
304
|
+
- Data reads succeed against your target surface (mock, `lwcProxy` dev, or a
|
|
305
|
+
`*.salesforce.app`-served deployed bundle).
|
|
306
|
+
- Behavior that branched on gates/access checks matches the org, given your provider
|
|
307
|
+
overrides.
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## Common migration errors
|
|
312
|
+
|
|
313
|
+
| Symptom | Cause | Fix |
|
|
314
|
+
| ------------------------------------------------------------ | -------------------------------------------------- | ----------------------------------------------------------------------- |
|
|
315
|
+
| `Rollup failed to resolve import "@salesforce/apex/…"` | No Apex provider | Migrate to GraphQL or Apex REST ([Step 4](#step-4-replace-data-access)) |
|
|
316
|
+
| `Rollup failed to resolve import "lightning/navigation"` | No navigation provider | Add a stub ([Step 5](#step-5-handle-the-unsupported-edges)) |
|
|
317
|
+
| `Rollup failed to resolve import "force/…"` | Core-only module | Add a stub via `stubs` |
|
|
318
|
+
| `@wire(getRelatedListRecords)` builds but returns nothing | Adapter not in the `lds()` registry | Register an MCP tool or replace with GraphQL |
|
|
319
|
+
| Wire data never refreshes | `subscribe()` is a no-op off-platform | Call `refresh()` or re-query explicitly |
|
|
320
|
+
| `Cannot read properties of undefined (reading 'isOpen')` | Missing `gate()` provider (base components use it) | Add `builtins.gate()` |
|
|
321
|
+
| Component mounts but is missing data it used to get for free | `@api` no longer injected by the platform | Set the property in `bootstrap.js` |
|
|
322
|
+
| Deployed bundle `401`s on `/services/data/*` | Served from a non-`salesforce.app` origin | Serve from `*.salesforce.app`, or use `lwcProxy` in dev |
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## Reference
|
|
327
|
+
|
|
328
|
+
- [Consumer Guide](consumer-guide.md) — baseline Vite setup and local-dev options
|
|
329
|
+
- [Limitations & Unsupported Features](limitations.md) — the full constraint reference
|
|
330
|
+
- [`lwc-records` example](../../../examples/lwc-axl/lwc-records) — real stubs for
|
|
331
|
+
`force/navigation`, `aura`, `logger`
|
|
332
|
+
- [`@salesforce/platform-sdk`](../../sdk/platform-sdk/README.md) — GraphQL + REST data access
|
|
333
|
+
</content>
|
|
334
|
+
</invoke>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/vite-plugin-lwc-ui-bundle",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.14.0",
|
|
4
4
|
"description": "Vite plugin for compiling LWC components into static bundles for off-platform and MCP use",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"author": "Salesforce",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"skills"
|
|
50
50
|
],
|
|
51
51
|
"scripts": {
|
|
52
|
-
"build": "vite build && vite build --mode runtime-lds && vite build --mode runtime-labels-graphql",
|
|
52
|
+
"build": "vite build && vite build --mode runtime-lds && vite build --mode runtime-labels-graphql && vite build --mode runtime-platform-graphql",
|
|
53
53
|
"clean": "rm -rf dist",
|
|
54
54
|
"dev": "vite build --watch",
|
|
55
55
|
"test": "vitest run",
|
|
@@ -74,9 +74,9 @@
|
|
|
74
74
|
},
|
|
75
75
|
"peerDependencies": {
|
|
76
76
|
"@lwc/rollup-plugin": "^9.0.0",
|
|
77
|
-
"@salesforce/platform-sdk": "^11.
|
|
77
|
+
"@salesforce/platform-sdk": "^11.14.0",
|
|
78
78
|
"@salesforce/state-managers-uiapi": "^0.31.0",
|
|
79
|
-
"@salesforce/ui-bundle": "^11.
|
|
79
|
+
"@salesforce/ui-bundle": "^11.14.0",
|
|
80
80
|
"lwc": "^9.0.0",
|
|
81
81
|
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0",
|
|
82
82
|
"zod": "^3.23.8"
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
"devDependencies": {
|
|
101
101
|
"@conduit-client/bindings-utils": "3.19.6",
|
|
102
102
|
"@conduit-client/command-base": "3.19.6",
|
|
103
|
-
"@salesforce/platform-sdk": "^11.
|
|
103
|
+
"@salesforce/platform-sdk": "^11.14.0",
|
|
104
104
|
"@types/ws": "^8.5.12",
|
|
105
105
|
"typescript": "^5.9.3",
|
|
106
106
|
"vite": "^7.0.0",
|
|
@@ -157,7 +157,15 @@ Starting from the root component, trace the dependency tree:
|
|
|
157
157
|
providers because base components use these modules internally even
|
|
158
158
|
if user code doesn't
|
|
159
159
|
- `@salesforce/gate/*`, `@salesforce/accessCheck/*` → handled by
|
|
160
|
-
those providers
|
|
160
|
+
those providers (accessCheck deny-defaults to `false`, no per-name
|
|
161
|
+
config required)
|
|
162
|
+
- `@salesforce/userPermission/<Name>`, `@salesforce/customPermission/<Name>`
|
|
163
|
+
→ **collect every `<Name>`.** These are named per-user permissions:
|
|
164
|
+
the default `platformGraphql()` provider **fails the build** unless
|
|
165
|
+
each imported name has a first-paint value declared in
|
|
166
|
+
`userPermissionDefaults` / `customPermissionDefaults`. Record the
|
|
167
|
+
exact names so Step 7 can generate them (default them to `false`
|
|
168
|
+
unless the user says otherwise). See `known-pitfalls.md#17`.
|
|
161
169
|
- `@salesforce/i18n/*` → `i18n` provider
|
|
162
170
|
- `@salesforce/client/*` → `client` provider (provides `formFactor`
|
|
163
171
|
based on viewport width)
|
|
@@ -227,6 +235,13 @@ Check in this order:
|
|
|
227
235
|
present (the default registry covers all three specifiers)
|
|
228
236
|
- Step 4 found any `@salesforce/i18n/*` → `builtins.i18n()` must be
|
|
229
237
|
present
|
|
238
|
+
- Step 4 found any `@salesforce/userPermission/<Name>` or
|
|
239
|
+
`@salesforce/customPermission/<Name>` → the default
|
|
240
|
+
`platformGraphql()` provider must declare a first-paint value for
|
|
241
|
+
**each** name in `userPermissionDefaults` / `customPermissionDefaults`,
|
|
242
|
+
or the build throws. Use the `defaultProviders({ platformGraphql: {…} })`
|
|
243
|
+
helper so the rest of the registry stays default. See
|
|
244
|
+
`references/known-pitfalls.md#17`.
|
|
230
245
|
- Step 4 found any `@salesforce/client/*` → `builtins.client()` must
|
|
231
246
|
be present
|
|
232
247
|
- `modules.npm` includes `lightning-base-components` (or
|
|
@@ -465,6 +480,34 @@ Adapt based on earlier findings:
|
|
|
465
480
|
|
|
466
481
|
- Set `dirs` for the detected project structure (SFDX vs namespaced).
|
|
467
482
|
- Populate `builtins.label({...})` with values from Step 6.
|
|
483
|
+
- **Declare a first-paint value for every named permission Step 4 found.**
|
|
484
|
+
Each `@salesforce/userPermission/<Name>` / `@salesforce/customPermission/<Name>`
|
|
485
|
+
import needs an entry, or the build throws (`known-pitfalls.md#17`).
|
|
486
|
+
Prefer the `defaultProviders()` helper so you configure only this while
|
|
487
|
+
keeping the rest of the default registry:
|
|
488
|
+
|
|
489
|
+
```js
|
|
490
|
+
import lwcVitePlugin, { defaultProviders } from "@salesforce/vite-plugin-lwc-ui-bundle";
|
|
491
|
+
|
|
492
|
+
lwcVitePlugin({
|
|
493
|
+
modules: {
|
|
494
|
+
/* ... */
|
|
495
|
+
},
|
|
496
|
+
providers: defaultProviders({
|
|
497
|
+
platformGraphql: {
|
|
498
|
+
userPermissionDefaults: { ApiEnabled: false, CustomizeApplication: false },
|
|
499
|
+
customPermissionDefaults: { My_Custom_Perm: false },
|
|
500
|
+
},
|
|
501
|
+
}),
|
|
502
|
+
});
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
Default each to `false` unless the user wants a `true` first paint; note
|
|
506
|
+
the resolved org value only reaches the DOM if the component imports the
|
|
507
|
+
module's `subscribe()` export (keep a plain default import if the same
|
|
508
|
+
source also deploys on-platform). `@salesforce/accessCheck/*` needs no
|
|
509
|
+
such config — it deny-defaults to `false`.
|
|
510
|
+
|
|
468
511
|
- Include `builtins.primitiveUtils()` if using `lightning-base-components`.
|
|
469
512
|
- Include `builtins.lds()` if any component uses `lightning/uiRecordApi`,
|
|
470
513
|
`lightning/uiObjectInfoApi`, `lightning/graphql`, or any of the
|
|
@@ -440,3 +440,60 @@ the plugin with `livePreview({ debug: true })` (or run with plugin debug
|
|
|
440
440
|
logging) — it logs the underlying import error. Note the bridge is a
|
|
441
441
|
local-dev convenience only; its absence never affects the compiled
|
|
442
442
|
bundle.
|
|
443
|
+
|
|
444
|
+
## 17. Build fails: named permission imported with no first-paint value
|
|
445
|
+
|
|
446
|
+
**Symptom:** `vite build` (or `npm run dev`) fails with:
|
|
447
|
+
|
|
448
|
+
```
|
|
449
|
+
[platform-graphql] @salesforce/userPermission/ApiEnabled was imported but
|
|
450
|
+
has no first-paint value configured. A permission is per-user runtime
|
|
451
|
+
state, so the plugin refuses to silently render a fabricated `false`. …
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
**Cause:** A component imports `@salesforce/userPermission/<Name>` or
|
|
455
|
+
`@salesforce/customPermission/<Name>`, but no value was declared for that
|
|
456
|
+
name. A named permission is an assertion about **per-user runtime state**;
|
|
457
|
+
a plain default import binds the value **once** at module-eval time and
|
|
458
|
+
never updates on its own (only the module's `subscribe()` export does).
|
|
459
|
+
So a silently-defaulted `false` would render a permanent, wrong answer —
|
|
460
|
+
even when the GraphQL fetch **succeeds**. Rather than guess, the plugin
|
|
461
|
+
requires you to state the value shown before the org responds. (This is
|
|
462
|
+
**not** the same as `@salesforce/accessCheck/*`, which is a feature gate
|
|
463
|
+
and safely deny-defaults to `false` — see pitfall #8.)
|
|
464
|
+
|
|
465
|
+
**Fix:** Declare each imported permission's first-paint value via the
|
|
466
|
+
`defaultProviders()` helper (keeps every other default in place):
|
|
467
|
+
|
|
468
|
+
```js
|
|
469
|
+
import lwcVitePlugin, { defaultProviders } from "@salesforce/vite-plugin-lwc-ui-bundle";
|
|
470
|
+
|
|
471
|
+
lwcVitePlugin({
|
|
472
|
+
modules: {
|
|
473
|
+
/* ... */
|
|
474
|
+
},
|
|
475
|
+
providers: defaultProviders({
|
|
476
|
+
platformGraphql: {
|
|
477
|
+
userPermissionDefaults: { ApiEnabled: false, CustomizeApplication: false },
|
|
478
|
+
customPermissionDefaults: { My_Custom_Perm: false },
|
|
479
|
+
},
|
|
480
|
+
}),
|
|
481
|
+
});
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
Use `false` unless you have a specific reason to render `true` before the
|
|
485
|
+
fetch resolves. To reflect the **resolved** org value in the DOM, import
|
|
486
|
+
the module's `subscribe(callback)` export and assign to a reactive field:
|
|
487
|
+
|
|
488
|
+
```js
|
|
489
|
+
import apiEnabled, { subscribe } from "@salesforce/userPermission/ApiEnabled";
|
|
490
|
+
// this.apiEnabled = apiEnabled; // first paint (configured)
|
|
491
|
+
// connectedCallback() { subscribe(v => this.apiEnabled = v); } // corrects in DOM
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
If the same component must also deploy on-platform (where the scoped
|
|
495
|
+
module has no `subscribe` export), keep the plain default import.
|
|
496
|
+
|
|
497
|
+
**Prevention:** SKILL.md Step 4 flags every `@salesforce/userPermission/*`
|
|
498
|
+
and `@salesforce/customPermission/*` import so the generated config
|
|
499
|
+
declares its first-paint value up front.
|