@salesforce/vite-plugin-lwc-ui-bundle 11.13.2 → 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.
Files changed (35) hide show
  1. package/dist/index.d.ts +32 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +30 -5
  4. package/dist/index.js.map +1 -1
  5. package/dist/providers/access-check.d.ts.map +1 -1
  6. package/dist/providers/gate.d.ts.map +1 -1
  7. package/dist/providers/i18n.d.ts +16 -0
  8. package/dist/providers/i18n.d.ts.map +1 -1
  9. package/dist/providers/index.d.ts +2 -0
  10. package/dist/providers/index.d.ts.map +1 -1
  11. package/dist/providers/index.js +3 -178
  12. package/dist/providers/index.js.map +1 -1
  13. package/dist/providers/labels-graphql/index.d.ts.map +1 -1
  14. package/dist/providers/labels-graphql/index.js +12 -3
  15. package/dist/providers/labels-graphql/index.js.map +1 -1
  16. package/dist/providers/labels-graphql/runtime.d.ts +21 -14
  17. package/dist/providers/labels-graphql/runtime.d.ts.map +1 -1
  18. package/dist/providers/labels-graphql/runtime.js +39 -26
  19. package/dist/providers/labels-graphql/runtime.js.map +1 -1
  20. package/dist/providers/platform-graphql/constants.d.ts +15 -0
  21. package/dist/providers/platform-graphql/constants.d.ts.map +1 -0
  22. package/dist/providers/platform-graphql/i18n-static.d.ts +19 -0
  23. package/dist/providers/platform-graphql/i18n-static.d.ts.map +1 -0
  24. package/dist/providers/platform-graphql/index.d.ts +58 -0
  25. package/dist/providers/platform-graphql/index.d.ts.map +1 -0
  26. package/dist/providers/platform-graphql/index.js +312 -0
  27. package/dist/providers/platform-graphql/index.js.map +1 -0
  28. package/dist/providers/platform-graphql/runtime.d.ts +30 -0
  29. package/dist/providers/platform-graphql/runtime.d.ts.map +1 -0
  30. package/dist/providers/platform-graphql/runtime.js +189 -0
  31. package/dist/providers/platform-graphql/runtime.js.map +1 -0
  32. package/docs/consumer-guide.md +120 -4
  33. package/package.json +5 -5
  34. package/skills/setup-lwc-vite-plugin/SKILL.md +44 -1
  35. package/skills/setup-lwc-vite-plugin/references/known-pitfalls.md +57 -0
@@ -259,6 +259,21 @@ export default defineConfig({
259
259
  });
260
260
  ```
261
261
 
262
+ **What is the `providers` array?** It is the list of resolvers for `@salesforce/*`
263
+ scoped-module imports (`@salesforce/label/*`, `@salesforce/i18n/*`,
264
+ `@salesforce/userPermission/*`, `@salesforce/gate/*`, …) that don't exist as
265
+ real npm packages. Each `builtins.*()` returns a Vite plugin that intercepts one
266
+ family of specifiers and generates the module the platform LWC compiler would
267
+ otherwise provide.
268
+
269
+ **You usually don't need it.** When you **omit** `providers` entirely, the plugin
270
+ installs a default set that resolves labels, i18n, permissions, and access checks
271
+ at runtime via GraphQL (see [GraphQL-backed scoped modules](#graphql-backed-scoped-modules-default)
272
+ below). Pass `providers` only to opt a family **out** of the runtime fetch (e.g.
273
+ `builtins.i18n()` for browser/`Intl`-derived locale identity, `builtins.label()`
274
+ for static build-time labels) or to add extra config — the explicit list above
275
+ shows the opt-out shape, not a required one.
276
+
262
277
  #### Component Directory Configuration
263
278
 
264
279
  The plugin supports two directory structures:
@@ -298,8 +313,22 @@ Components are importable as `myNamespace/myComponent`.
298
313
 
299
314
  #### Configuring Labels
300
315
 
301
- The `builtins.label()` provider handles `@salesforce/label/*` imports. How you
302
- configure it depends on your project:
316
+ There are two providers for `@salesforce/label/*`:
317
+
318
+ - **`builtins.labelsGraphql()` — the default.** Resolves labels at runtime via UI
319
+ API GraphQL through the Platform Data SDK (`createDataSDK()`), so labels reflect
320
+ the current user's translation. The SDK picks the transport per surface: a
321
+ direct session-authenticated GraphQL request in a full-page web app, or the
322
+ `window.openai` bridge in an MCP/ChatGPT host. Build-time values (or the
323
+ key-derived fallback) render until the runtime fetch resolves. Included
324
+ automatically when you omit the `providers` array.
325
+ - **`builtins.label()` — static.** Resolves to a fixed build-time string. Use it
326
+ to opt out of runtime fetching (e.g. a pure off-core demo with no org).
327
+
328
+ Both accept the same overrides object; `labelsGraphql` uses the overrides as the
329
+ static fallback shown before/instead of a successful GraphQL fetch.
330
+
331
+ How you configure labels depends on your project:
303
332
 
304
333
  **SFDX project with `CustomLabels.labels-meta.xml`:**
305
334
 
@@ -340,8 +369,95 @@ you'll get runtime errors like:
340
369
  Uncaught TypeError: Cannot read properties of undefined (reading 'isOpen')
341
370
  ```
342
371
 
343
- Always include `builtins.gate()` and `builtins.accessCheck()` when using
344
- `lightning-base-components`.
372
+ Include `builtins.gate()` when using `lightning-base-components`. (When you omit
373
+ the `providers` array entirely, the default set already covers these.)
374
+
375
+ #### GraphQL-backed scoped modules (default)
376
+
377
+ When you **omit** the `providers` array, these scoped modules resolve at runtime
378
+ via UI API GraphQL through the Platform Data SDK (with build-time first-paint
379
+ values):
380
+
381
+ | Scoped module | GraphQL source (`uiapi.platform.*`) | Provider | First-paint value |
382
+ | -------------------------------------- | ----------------------------------- | ------------------- | ------------------------------------ |
383
+ | `@salesforce/label/*` | `labels` | `labelsGraphql()` | override or key-derived text |
384
+ | `@salesforce/i18n/*` (locale identity) | `i18n` | `platformGraphql()` | CLDR placeholder |
385
+ | `@salesforce/userPermission/*` | `userPermissions` | `platformGraphql()` | **required — you must configure it** |
386
+ | `@salesforce/accessCheck/*` | `userPermissions` | `platformGraphql()` | `false` (deny-by-default) |
387
+ | `@salesforce/customPermission/*` | `customPermissions` | `platformGraphql()` | **required — you must configure it** |
388
+
389
+ The generated modules export the first-paint value as `default` and an opt-in
390
+ `subscribe(callback)` that fires with the resolved org value.
391
+
392
+ ##### Named permissions must be configured (no silent guess)
393
+
394
+ A `@salesforce/userPermission/<Name>` or `@salesforce/customPermission/<Name>`
395
+ import is an assertion about **per-user runtime state**. A plain default import
396
+ binds the module's value **once** at module-eval time and never updates on its
397
+ own — so if the plugin silently defaulted an unconfigured permission to `false`,
398
+ your UI would render a permanent, wrong answer about what the current user can do
399
+ (and it would be wrong even on the GraphQL **success** path, because a default
400
+ import never re-reads the resolved value).
401
+
402
+ Rather than guess, **the build fails** if you import a named permission without
403
+ declaring the value it should show before the org responds:
404
+
405
+ ```
406
+ [platform-graphql] @salesforce/userPermission/ApiEnabled was imported but has no
407
+ first-paint value configured. …
408
+ ```
409
+
410
+ Declare it with the `defaultProviders()` helper — which lets you configure this
411
+ one provider while keeping every other default in place:
412
+
413
+ ```js
414
+ import lwcVitePlugin, { defaultProviders } from "@salesforce/vite-plugin-lwc-ui-bundle";
415
+
416
+ lwcVitePlugin({
417
+ modules: { dirs: [{ path: "force-app/main/default/lwc", namespace: "c" }] },
418
+ providers: defaultProviders({
419
+ platformGraphql: {
420
+ userPermissionDefaults: { ApiEnabled: false, CustomizeApplication: false },
421
+ customPermissionDefaults: { My_Custom_Perm: false },
422
+ },
423
+ }),
424
+ });
425
+ ```
426
+
427
+ Use `false` unless you have a specific reason to render `true` before the fetch
428
+ resolves. `@salesforce/accessCheck/*` is exempt: it's a feature **gate**, so an
429
+ unconfigured check safely deny-defaults to `false` (base components rely on this),
430
+ though you can still override it.
431
+
432
+ ##### Reflecting the resolved value reactively (`subscribe`)
433
+
434
+ The configured value is only the **first paint**. To show the real org value once
435
+ the GraphQL fetch lands, import the module's `subscribe(callback)` export and
436
+ assign to a reactive field — the callback fires immediately with the current value
437
+ and again when the resolved value differs:
438
+
439
+ ```js
440
+ import apiEnabled, { subscribe } from "@salesforce/userPermission/ApiEnabled";
441
+
442
+ export default class extends LightningElement {
443
+ apiEnabled = apiEnabled; // first paint (your configured value)
444
+ connectedCallback() {
445
+ subscribe((v) => (this.apiEnabled = v)); // corrects in the DOM when the org responds
446
+ }
447
+ }
448
+ ```
449
+
450
+ > **Dual-deploy note:** the `subscribe` export exists only when the module is
451
+ > served by this plugin. If the **same** component source must also deploy as
452
+ > on-platform LWC metadata (where `@salesforce/userPermission/*` has no `subscribe`
453
+ > export), keep a plain default import — it will render the configured first-paint
454
+ > value off-core and the platform-resolved value on-core.
455
+
456
+ i18n number/date **format** patterns and calendar data stay static (CLDR reference
457
+ data, no org source). `@salesforce/gate/*` also stays static — its GraphQL field is
458
+ UiTier-context-only and not reachable off-core. The static `builtins.i18n()` /
459
+ `builtins.accessCheck()` / `builtins.label()` providers remain available for
460
+ explicit opt-out.
345
461
 
346
462
  ### Step 3: Create `index.html`
347
463
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/vite-plugin-lwc-ui-bundle",
3
- "version": "11.13.2",
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.13.2",
77
+ "@salesforce/platform-sdk": "^11.14.0",
78
78
  "@salesforce/state-managers-uiapi": "^0.31.0",
79
- "@salesforce/ui-bundle": "^11.13.2",
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.13.2",
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.