@salesforce/vite-plugin-lwc-ui-bundle 11.13.0 → 11.13.2
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/docs/consumer-guide.md +5 -0
- package/docs/limitations.md +365 -0
- package/docs/migration-guide.md +334 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -4,6 +4,9 @@ Vite plugin for compiling and running LWC components off-platform. Bundles the f
|
|
|
4
4
|
|
|
5
5
|
> **Getting started?** See the [Consumer Guide](docs/consumer-guide.md) for a
|
|
6
6
|
> step-by-step walkthrough of adding this plugin to an existing LWC project.
|
|
7
|
+
>
|
|
8
|
+
> - **Porting a platform component?** See the [Migration Guide](docs/migration-guide.md).
|
|
9
|
+
> - **What's not supported off-platform?** See [Limitations & Unsupported Features](docs/limitations.md).
|
|
7
10
|
|
|
8
11
|
## Installation
|
|
9
12
|
|
package/docs/consumer-guide.md
CHANGED
|
@@ -515,6 +515,9 @@ export const someExport = () => {};
|
|
|
515
515
|
lwcVitePlugin({ stubs: { "force/someModule": "./stubs/someModule.js" } });
|
|
516
516
|
```
|
|
517
517
|
|
|
518
|
+
See [Limitations → Not supported](limitations.md#not-supported--and-how-to-work-around-it)
|
|
519
|
+
for stubbing navigation, LMS, Apex, and other core-only modules.
|
|
520
|
+
|
|
518
521
|
### Component renders but looks unstyled
|
|
519
522
|
|
|
520
523
|
Add SLDS import to `bootstrap.js`:
|
|
@@ -527,6 +530,8 @@ import "@salesforce-ux/design-system/assets/styles/salesforce-lightning-design-s
|
|
|
527
530
|
|
|
528
531
|
## Reference
|
|
529
532
|
|
|
533
|
+
- **Migration Guide:** [migration-guide.md](migration-guide.md) — porting a platform LWC to a bundle
|
|
534
|
+
- **Limitations & Unsupported Features:** [limitations.md](limitations.md) — what's not supported off-platform, and the workaround for each gap
|
|
530
535
|
- **Live Preview & HMR verification:** [live-preview-hmr-verification.md](live-preview-hmr-verification.md) — how local dev (Live Preview, HMR, dev-gateway) works and is verified
|
|
531
536
|
- **npm:** https://www.npmjs.com/package/@salesforce/vite-plugin-lwc-ui-bundle
|
|
532
537
|
- **Source:** https://github.com/salesforce-experience-platform-emu/webapps/tree/main/packages/vite-plugin-lwc-ui-bundle
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
# LWC UI Bundle Limitations & Unsupported Features
|
|
2
|
+
|
|
3
|
+
An LWC UI Bundle compiles your Lightning Web Components off-platform into a static
|
|
4
|
+
`dist/` — an `index.html` plus hashed `assets/*.js`/`*.css` by default, or a single
|
|
5
|
+
inlined `dist/index.html` if you add [`vite-plugin-singlefile`](https://www.npmjs.com/package/vite-plugin-singlefile)
|
|
6
|
+
(see [Consumer Guide → Off-Platform Build](consumer-guide.md#off-platform-build)). The
|
|
7
|
+
output runs anywhere with a DOM — an agentic MCP host (ChatGPT, MCP Apps), a plain
|
|
8
|
+
website, or a `*.salesforce.app`-served page. Because there is **no Lightning runtime**
|
|
9
|
+
around your component in any of those surfaces, some platform capabilities that are
|
|
10
|
+
ambient on-platform have no equivalent off-platform. This document is the authoritative
|
|
11
|
+
list of what is and isn't supported, _why_, and the recommended workaround for each gap.
|
|
12
|
+
|
|
13
|
+
> Porting an existing platform component? Pair this reference with the
|
|
14
|
+
> [Migration Guide](migration-guide.md), which walks through applying these
|
|
15
|
+
> workarounds step by step.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## The core architectural constraint
|
|
20
|
+
|
|
21
|
+
A platform LWC runs **inside** a Salesforce org: the Lightning runtime mounts it,
|
|
22
|
+
`@salesforce/*` and `lightning/*` modules resolve to live org services, and Lightning
|
|
23
|
+
Data Service (LDS) backs wire adapters with a **reactive client-side store** that
|
|
24
|
+
pushes updates as records change.
|
|
25
|
+
|
|
26
|
+
An LWC UI Bundle runs **anywhere with a DOM** — a browser tab, an MCP host, a
|
|
27
|
+
`*.salesforce.app`-served page. There is no org runtime in the page. The plugin
|
|
28
|
+
recreates the pieces it can as **scoped module providers**: small generated JS modules
|
|
29
|
+
that stand in for `@salesforce/label/*`, `@salesforce/i18n/*`, and friends. Everything
|
|
30
|
+
else — anything that needs a live authenticated session, a reactive store, a shared
|
|
31
|
+
message bus, or platform navigation — either routes through an explicit data path
|
|
32
|
+
(GraphQL / the Data SDK / an MCP tool) or is simply **not available** and must be
|
|
33
|
+
stubbed.
|
|
34
|
+
|
|
35
|
+
Two consequences flow from this and explain almost every limitation below:
|
|
36
|
+
|
|
37
|
+
1. **No reactive store.** Wire adapters can fetch once, but nothing observes org state
|
|
38
|
+
to push updates. `subscribe()` is a no-op.
|
|
39
|
+
2. **No ambient authenticated session** unless the bundle is served from a
|
|
40
|
+
`*.salesforce.app` app domain (deployed) or proxied to an org (`lwcProxy`, dev
|
|
41
|
+
only). A raw static file has no org to call.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Support matrix
|
|
46
|
+
|
|
47
|
+
| Capability | Status | Off-platform mechanism |
|
|
48
|
+
| ------------------------------------------------------- | -------------- | ------------------------------------------------------------- |
|
|
49
|
+
| LWC component compilation (`.js/.html/.css`) | ✅ Full | `@lwc/rollup-plugin` |
|
|
50
|
+
| `lightning/*` base components | ✅ Full | `lightning-base-components` (npm) |
|
|
51
|
+
| Custom Labels (`@salesforce/label/*`) | ✅ Provider | `builtins.label()` static · `builtins.labelsGraphql()` live |
|
|
52
|
+
| i18n (`@salesforce/i18n/*`) | ⚠️ Partial | `builtins.i18n()` — live locale identity, static CLDR formats |
|
|
53
|
+
| Client / form factor (`@salesforce/client/*`) | ✅ Provider | `builtins.client()` |
|
|
54
|
+
| Feature gates (`@salesforce/gate/*`) | ⚠️ Static | `builtins.gate()` — **defaults open**, no live gate state |
|
|
55
|
+
| Access checks (`@salesforce/accessCheck/*`) | ⚠️ Static | `builtins.accessCheck()` — **defaults false**, no live perms |
|
|
56
|
+
| `lightning/primitiveUtils` | ✅ Provider | `builtins.primitiveUtils()` |
|
|
57
|
+
| GraphQL (`lightning/graphql`) | ✅ Supported | `builtins.lds()` default registry → MCP `graphqlQuery` tool |
|
|
58
|
+
| LDS wire/imperative adapters | ⚠️ Partial | `builtins.lds()` — **only registered exports**; MCP-backed |
|
|
59
|
+
| REST / Apex REST | ✅ Supported | `createDataSDK().fetch?.()` → `/services/apexrest/*` |
|
|
60
|
+
| Imperative Apex (`@salesforce/apex/*`) | ❌ Unsupported | No provider — use GraphQL or Apex REST |
|
|
61
|
+
| `@AuraEnabled` Apex methods | ❌ Unsupported | Expose as `@RestResource` instead |
|
|
62
|
+
| Lightning Message Service (`lightning/messageService`) | ❌ Unsupported | No provider — stub with `EventTarget` / host bridge |
|
|
63
|
+
| Navigation (`lightning/navigation`, `force/navigation`) | ❌ Unsupported | No provider — stub `NavigationMixin` |
|
|
64
|
+
| LDS store subscriptions / auto-refresh | ❌ Unsupported | `subscribe()` is a no-op; use `refresh()` or re-query |
|
|
65
|
+
| Aura (`aura`) and other `force/*` modules | ❌ Unsupported | No provider — supply a stub |
|
|
66
|
+
| Static Resources (`@salesforce/resourceUrl/*`) | ❌ Unsupported | Bundle assets directly / import as Vite assets |
|
|
67
|
+
| Wire to `$CurrentPageReference`, User, Org context | ❌ Unsupported | No provider — pass context in explicitly |
|
|
68
|
+
|
|
69
|
+
Legend: ✅ works like platform · ⚠️ works with documented differences · ❌ no
|
|
70
|
+
off-platform equivalent, workaround required.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Supported with differences
|
|
75
|
+
|
|
76
|
+
These work, but not identically to the platform. Know the difference before you rely
|
|
77
|
+
on them.
|
|
78
|
+
|
|
79
|
+
### i18n — live identity, static formats
|
|
80
|
+
|
|
81
|
+
`builtins.i18n()` derives locale, language, currency, and time zone from the browser's
|
|
82
|
+
`Intl` API at runtime, so `@salesforce/i18n/lang` and similar identity values are
|
|
83
|
+
live. **Format patterns** (number/date/currency CLDR patterns), however, use en-US
|
|
84
|
+
defaults rather than the org's locale data. If your component depends on exact
|
|
85
|
+
locale-specific formatting matching the org, format through `Intl.NumberFormat` /
|
|
86
|
+
`Intl.DateTimeFormat` explicitly rather than trusting the static patterns.
|
|
87
|
+
|
|
88
|
+
### Feature gates — default open, not live
|
|
89
|
+
|
|
90
|
+
`builtins.gate()` resolves every `@salesforce/gate/*` import to **open** by default.
|
|
91
|
+
There is no connection to the org's live gate state. Pass overrides to model closed
|
|
92
|
+
gates: `builtins.gate({ myFeature: false })`. Note that `lightning-base-components`
|
|
93
|
+
use gates internally — you must include `builtins.gate()` or base components throw
|
|
94
|
+
`Cannot read properties of undefined (reading 'isOpen')`.
|
|
95
|
+
|
|
96
|
+
### Access checks — default false, not live
|
|
97
|
+
|
|
98
|
+
`builtins.accessCheck()` resolves every `@salesforce/accessCheck/*` import to `false`
|
|
99
|
+
by default — no live permission evaluation. Pass overrides for checks your UI depends
|
|
100
|
+
on: `builtins.accessCheck({ MyCustomPerm: true })`. Because the default is `false`,
|
|
101
|
+
permission-gated UI is hidden unless you opt it in; verify your overrides match the
|
|
102
|
+
org behavior you're emulating.
|
|
103
|
+
|
|
104
|
+
> **Coming soon — live permission evaluation.** A GraphQL-backed provider that resolves
|
|
105
|
+
> `@salesforce/accessCheck/*` (plus `@salesforce/userPermission/*` and
|
|
106
|
+
> `@salesforce/customPermission/*`) against the live user's permissions via UI API
|
|
107
|
+
> GraphQL is in flight ([#670](https://github.com/salesforce-experience-platform-emu/webapps/pull/670)).
|
|
108
|
+
> Until it merges, access checks are static-only as described above.
|
|
109
|
+
|
|
110
|
+
### Custom Labels — static default, or live via GraphQL
|
|
111
|
+
|
|
112
|
+
There are two label providers; pick per your data path:
|
|
113
|
+
|
|
114
|
+
- **`builtins.label()` (static, the default).** Returns label strings from a defaults
|
|
115
|
+
map. Unknown keys get a human-readable fallback derived from the key
|
|
116
|
+
(`c.appTitle` → "App Title"). It does **not** fetch live translations. Provide real
|
|
117
|
+
values via `builtins.label({ "c.appTitle": "My App" })` or copy them from your
|
|
118
|
+
`CustomLabels.labels-meta.xml`. Translations for non-default languages are not
|
|
119
|
+
resolved.
|
|
120
|
+
- **`builtins.labelsGraphql()` (live, opt-in).** Each `@salesforce/label/*` import
|
|
121
|
+
resolves to a runtime module that batches its key and fetches the translated value
|
|
122
|
+
via UI API GraphQL, so labels reflect the **current user's locale**. Static defaults
|
|
123
|
+
(and any `staticOverrides` you pass) act as the pre-fetch fallback. This needs a live
|
|
124
|
+
data path (a real MCP host, a mocked `callTool`, or a `*.salesforce.app`-served
|
|
125
|
+
bundle) — see [Data access and authentication](#data-access-and-authentication). To
|
|
126
|
+
use it, swap `label()` for `labelsGraphql()` in your `providers` array.
|
|
127
|
+
|
|
128
|
+
### LDS — partial adapter coverage, no store
|
|
129
|
+
|
|
130
|
+
The `lds()` provider (on by default) routes a **registered** set of adapters to MCP
|
|
131
|
+
tools. The default registry covers:
|
|
132
|
+
|
|
133
|
+
| Specifier | Export | Shape |
|
|
134
|
+
| --------------------------- | -------------------------- | ------------------------ |
|
|
135
|
+
| `lightning/uiRecordApi` | `getRecord` | wire |
|
|
136
|
+
| `lightning/uiRecordApi` | `createRecord` | imperative-mutation |
|
|
137
|
+
| `lightning/uiRecordApi` | `updateRecord` | imperative-mutation |
|
|
138
|
+
| `lightning/uiObjectInfoApi` | `getObjectInfo_imperative` | imperative-read (legacy) |
|
|
139
|
+
|
|
140
|
+
Code using only these exports ports unchanged, and each shape delegates to the same
|
|
141
|
+
OneStore invoker used on-platform, so success payloads are deep-frozen and validation
|
|
142
|
+
errors throw the same typed classes. Two hard limits:
|
|
143
|
+
|
|
144
|
+
- **Unregistered exports don't resolve.** `getListUi`, `getRelatedListRecords`,
|
|
145
|
+
`deleteRecord`, and any other LDS export not in the registry pass through unresolved
|
|
146
|
+
and fail the build. Register your own MCP-backed adapter via the `lds({ … })` config
|
|
147
|
+
(see the [plugin README](../README.md#ldsadapters)), or replace the call with
|
|
148
|
+
GraphQL.
|
|
149
|
+
- **`subscribe()` is a deliberate no-op.** Off-platform there is no reactive store to
|
|
150
|
+
observe, so the subscribe callback never fires and the returned unsubscribe is
|
|
151
|
+
idempotent. Data does not auto-update. Use `refresh()` (on the
|
|
152
|
+
`subscribable-refreshable` shape) or re-query when you need fresh data.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Not supported — and how to work around it
|
|
157
|
+
|
|
158
|
+
### Imperative Apex (`@salesforce/apex/MyClass.myMethod`)
|
|
159
|
+
|
|
160
|
+
**No provider.** There is no way to invoke an `@AuraEnabled` Apex method off-platform.
|
|
161
|
+
|
|
162
|
+
**Workarounds:**
|
|
163
|
+
|
|
164
|
+
1. **GraphQL** — if the method only reads records, replace it with a UI API GraphQL
|
|
165
|
+
query. This is the preferred path and works both in a `@wire` and imperatively via
|
|
166
|
+
the Data SDK.
|
|
167
|
+
2. **Apex REST** — for business logic that can't be expressed as GraphQL, expose it as
|
|
168
|
+
an `@RestResource` and call it through the Data SDK:
|
|
169
|
+
|
|
170
|
+
```js
|
|
171
|
+
import { createDataSDK } from "@salesforce/platform-sdk";
|
|
172
|
+
const sdk = await createDataSDK();
|
|
173
|
+
const res = await sdk.fetch?.("/services/apexrest/property/listings");
|
|
174
|
+
const listings = await res?.json();
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Note the distinction: **`@AuraEnabled` methods are not reachable; only `@RestResource`
|
|
178
|
+
endpoints are.** Plan to expose an Apex REST surface for any imperative Apex you can't
|
|
179
|
+
convert to GraphQL.
|
|
180
|
+
|
|
181
|
+
### Lightning Message Service (`lightning/messageService`)
|
|
182
|
+
|
|
183
|
+
**No provider.** LMS assumes a shared platform message bus (backed by
|
|
184
|
+
`@salesforce/messageChannel/*` metadata) that doesn't exist off-platform.
|
|
185
|
+
|
|
186
|
+
**Workaround — stub it, backed by a local bus:** for cross-component messaging _within
|
|
187
|
+
the same bundle_, a module-scoped `EventTarget` reproduces publish/subscribe
|
|
188
|
+
faithfully. For cross-surface messaging (bundle ↔ host), route through the MCP/host
|
|
189
|
+
bridge instead.
|
|
190
|
+
|
|
191
|
+
```js
|
|
192
|
+
// src/stubs/message-service.js
|
|
193
|
+
const bus = new EventTarget();
|
|
194
|
+
|
|
195
|
+
export function createMessageContext() {
|
|
196
|
+
return {};
|
|
197
|
+
}
|
|
198
|
+
export function releaseMessageContext() {}
|
|
199
|
+
export function publish(_ctx, channel, message) {
|
|
200
|
+
bus.dispatchEvent(new CustomEvent(channelKey(channel), { detail: message }));
|
|
201
|
+
}
|
|
202
|
+
export function subscribe(_ctx, channel, listener) {
|
|
203
|
+
const handler = (e) => listener(e.detail);
|
|
204
|
+
const key = channelKey(channel);
|
|
205
|
+
bus.addEventListener(key, handler);
|
|
206
|
+
return { key, handler };
|
|
207
|
+
}
|
|
208
|
+
export function unsubscribe(sub) {
|
|
209
|
+
if (sub) bus.removeEventListener(sub.key, sub.handler);
|
|
210
|
+
}
|
|
211
|
+
export const APPLICATION_SCOPE = Symbol("APPLICATION_SCOPE");
|
|
212
|
+
export const MessageContext = Symbol("MessageContext");
|
|
213
|
+
|
|
214
|
+
function channelKey(channel) {
|
|
215
|
+
return typeof channel === "string" ? channel : String(channel?.name ?? channel);
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
```js
|
|
220
|
+
// vite.config.js
|
|
221
|
+
lwcVitePlugin({ stubs: { "lightning/messageService": "src/stubs/message-service.js" } });
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
`@salesforce/messageChannel/*` imports (the channel references) also have no provider —
|
|
225
|
+
stub each to a plain identifier string, or import the channel name as a constant.
|
|
226
|
+
|
|
227
|
+
### Navigation (`lightning/navigation`, `force/navigation`)
|
|
228
|
+
|
|
229
|
+
**No provider.** There is no page reference resolver or router off-platform.
|
|
230
|
+
|
|
231
|
+
**Workaround — a no-op `NavigationMixin` stub**, then decide per call site what
|
|
232
|
+
navigation should mean (a real `window.location` change, a host callback, or nothing).
|
|
233
|
+
This is exactly what the [`lwc-records` example](../../../examples/lwc-axl/lwc-records/sf/lwc/force/navigation/navigation.js)
|
|
234
|
+
ships:
|
|
235
|
+
|
|
236
|
+
```js
|
|
237
|
+
// src/stubs/navigation.js
|
|
238
|
+
export const CurrentPageReference = { adapter: Symbol("CurrentPageReference") };
|
|
239
|
+
|
|
240
|
+
export const NavigationMixin = (Base) =>
|
|
241
|
+
class extends Base {
|
|
242
|
+
[NavigationMixin.Navigate]() {} // no-op, or window.location / host callback
|
|
243
|
+
[NavigationMixin.GenerateUrl]() {
|
|
244
|
+
return Promise.resolve("");
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
NavigationMixin.Navigate = Symbol("Navigate");
|
|
248
|
+
NavigationMixin.GenerateUrl = Symbol("GenerateUrl");
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
```js
|
|
252
|
+
// vite.config.js
|
|
253
|
+
lwcVitePlugin({ stubs: { "lightning/navigation": "src/stubs/navigation.js" } });
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Aura and other `force/*` / core-only modules
|
|
257
|
+
|
|
258
|
+
**No provider.** `aura`, `logger`, and `force/*` / `runtime_*` modules are core-only.
|
|
259
|
+
If the build fails with `Rollup failed to resolve import "force/someModule"`, add a
|
|
260
|
+
stub exporting the shapes your code imports. The `lwc-records` example stubs several:
|
|
261
|
+
|
|
262
|
+
```js
|
|
263
|
+
// vite.config.js
|
|
264
|
+
lwcVitePlugin({
|
|
265
|
+
stubs: {
|
|
266
|
+
aura: "src/stubs/aura-off-platform.js",
|
|
267
|
+
logger: "src/stubs/logger-stub.js",
|
|
268
|
+
"force/someModule": "src/stubs/some-module.js",
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
A stub only needs to export the named bindings your components import — often no-ops or
|
|
274
|
+
empty objects are enough to satisfy the bundler and let the rest of the app run.
|
|
275
|
+
|
|
276
|
+
### Static Resources (`@salesforce/resourceUrl/*`) & content assets
|
|
277
|
+
|
|
278
|
+
**No provider.** Off-platform there is no static resource CDN. Import assets through
|
|
279
|
+
Vite instead (they get emitted into `dist/assets/`, or inlined into `index.html` if you
|
|
280
|
+
build single-file), or reference them by absolute URL if hosted separately.
|
|
281
|
+
|
|
282
|
+
### Org / user / page context wires
|
|
283
|
+
|
|
284
|
+
Wire adapters that inject ambient context — `$CurrentPageReference`, current user,
|
|
285
|
+
org info — have **no provider**. Pass the values your component needs in explicitly at
|
|
286
|
+
mount time (`bootstrap.js`) or read them from the URL/host, rather than wiring them.
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Data access and authentication
|
|
291
|
+
|
|
292
|
+
Even when your data code is correct, _where the bundle is served from_ determines
|
|
293
|
+
whether it can reach the org. Off-platform there are **two distinct data paths**, and
|
|
294
|
+
they resolve differently:
|
|
295
|
+
|
|
296
|
+
1. **MCP-backed adapters** — `lightning/graphql` (`@wire(graphql)`) and the registered
|
|
297
|
+
`lightning/uiRecordApi` exports. These dispatch through the **host bridge** exposed
|
|
298
|
+
by `@salesforce/platform-sdk` — `getChatSDK().callTool(name, params)` with tool names
|
|
299
|
+
like `graphqlQuery`, `getRecordMcpTool`, …. The SDK detects the runtime surface and
|
|
300
|
+
picks the transport: on ChatGPT it calls `window.openai.callTool`; on MCP Apps it
|
|
301
|
+
sends a JSON-RPC `tools/call` request over the app's message channel. Either way the
|
|
302
|
+
adapter code is transport-agnostic. These do **not** hit `/services/*` and do **not**
|
|
303
|
+
use `lwcProxy`.
|
|
304
|
+
2. **Same-origin REST** — the imperative Data SDK (`createDataSDK().graphql.query()` /
|
|
305
|
+
`.fetch()`) and any legacy `lightning/*` module that calls `/services/*` directly.
|
|
306
|
+
These make same-origin HTTP requests and need a real org session.
|
|
307
|
+
|
|
308
|
+
How each path gets live data, by surface:
|
|
309
|
+
|
|
310
|
+
| Surface | MCP-backed adapters | Same-origin REST (Data SDK / legacy) |
|
|
311
|
+
| ------------------------------------- | -------------------------------- | --------------------------------------------------- |
|
|
312
|
+
| **Local dev (`npm run dev`)** | mock the host bridge (see below) | `lwcProxy()` forwards `/services/*` to `sf` CLI org |
|
|
313
|
+
| **Real MCP host (ChatGPT, MCP Apps)** | host provides `callTool` | n/a on this surface |
|
|
314
|
+
| **Deployed to `*.salesforce.app`** | host bridge, if present | authenticated same-origin session |
|
|
315
|
+
| **Raw static file / other origin** | no host bridge → no data | no org session — `/services/data/*` returns `401` |
|
|
316
|
+
|
|
317
|
+
Key facts:
|
|
318
|
+
|
|
319
|
+
- **`lwcProxy()` is development-only and REST-only.** It runs inside the Vite dev
|
|
320
|
+
server (not in the production `dist/index.html`) and exists for the same-origin REST
|
|
321
|
+
path — the Data SDK's imperative calls and legacy `lightning/*` modules that hit
|
|
322
|
+
`/services/*`. The MCP-backed `lightning/graphql` and `lightning/uiRecordApi`
|
|
323
|
+
adapters bypass it entirely; drive those with a real host or, in dev, a mocked host
|
|
324
|
+
bridge. On the ChatGPT surface that mock is a guarded `window.openai.callTool` shim in
|
|
325
|
+
your entry script; other surfaces (MCP Apps) resolve the bridge through their own
|
|
326
|
+
transport, so the SDK's `getChatSDK().callTool` picks the right one automatically.
|
|
327
|
+
- **A deployed bundle needs the `*.salesforce.app` app domain** for the same-origin
|
|
328
|
+
REST path. Served from that domain, the Data SDK's cookie+CSRF flow against
|
|
329
|
+
`/services/data/v{version}/graphql` succeeds. Served from a different origin (a plain
|
|
330
|
+
static host, or the `my.salesforce.com` org domain), the same calls `401` — this is
|
|
331
|
+
by design, not a bug in your code.
|
|
332
|
+
- **The Data SDK methods are optional.** Both `graphql?.query(...)` and `fetch?.()` may
|
|
333
|
+
be unavailable depending on the runtime surface — always call them with optional
|
|
334
|
+
chaining, and handle the `undefined` case.
|
|
335
|
+
|
|
336
|
+
For the full data-access API, see [`@salesforce/platform-sdk`](../../sdk/platform-sdk/README.md).
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## Quick reference: "which workaround?"
|
|
341
|
+
|
|
342
|
+
| You were using… | Off-platform replacement |
|
|
343
|
+
| --------------------------------------------- | ---------------------------------------------------------------- |
|
|
344
|
+
| Imperative `@AuraEnabled` Apex | GraphQL query, or `@RestResource` + `sdk.fetch?.()` |
|
|
345
|
+
| `getRecord` / `createRecord` / `updateRecord` | Works via `lds()` (registered) — no change |
|
|
346
|
+
| Other LDS adapter (`getListUi`, …) | Register an MCP tool via `lds({ … })`, or use GraphQL |
|
|
347
|
+
| `lightning/messageService` | `EventTarget` stub (same-bundle) or host bridge (cross-surface) |
|
|
348
|
+
| `lightning/navigation` | No-op `NavigationMixin` stub + explicit routing |
|
|
349
|
+
| `@salesforce/label/*` | `builtins.label()` (static) or `builtins.labelsGraphql()` (live) |
|
|
350
|
+
| `@salesforce/gate/*` | `builtins.gate()` with overrides (defaults open) |
|
|
351
|
+
| Static resource URL | Import the asset through Vite |
|
|
352
|
+
| Wire that auto-refreshes on store change | `refresh()` or manual re-query — no reactive store off-platform |
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## Reference
|
|
357
|
+
|
|
358
|
+
- [Migration Guide](migration-guide.md) — applying these workarounds step by step
|
|
359
|
+
- [Consumer Guide](consumer-guide.md) — baseline setup and local-dev options
|
|
360
|
+
- [Plugin README → Built-in Providers](../README.md#built-in-providers) — provider API
|
|
361
|
+
- [Plugin README → `lds(adapters?)`](../README.md#ldsadapters) — registering adapters
|
|
362
|
+
- [`lwc-records` example](../../../examples/lwc-axl/lwc-records) — real stubs for
|
|
363
|
+
`force/navigation`, `aura`, `logger`
|
|
364
|
+
- [`@salesforce/platform-sdk`](../../sdk/platform-sdk/README.md) — GraphQL + REST data access
|
|
365
|
+
</content>
|
|
@@ -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.13.
|
|
3
|
+
"version": "11.13.2",
|
|
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",
|
|
@@ -74,9 +74,9 @@
|
|
|
74
74
|
},
|
|
75
75
|
"peerDependencies": {
|
|
76
76
|
"@lwc/rollup-plugin": "^9.0.0",
|
|
77
|
-
"@salesforce/platform-sdk": "^11.13.
|
|
77
|
+
"@salesforce/platform-sdk": "^11.13.2",
|
|
78
78
|
"@salesforce/state-managers-uiapi": "^0.31.0",
|
|
79
|
-
"@salesforce/ui-bundle": "^11.13.
|
|
79
|
+
"@salesforce/ui-bundle": "^11.13.2",
|
|
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.13.
|
|
103
|
+
"@salesforce/platform-sdk": "^11.13.2",
|
|
104
104
|
"@types/ws": "^8.5.12",
|
|
105
105
|
"typescript": "^5.9.3",
|
|
106
106
|
"vite": "^7.0.0",
|