@salesforce/vite-plugin-lwc-ui-bundle 1.131.2 → 1.131.3

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 CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  Vite plugin for compiling and running LWC components off-platform. Bundles the full compilation pipeline — scoped module providers, Lightning npm resolution, missing CSS handling, and the Vite/LWC bridge — behind a single configurable entry point.
4
4
 
5
+ > **Getting started?** See the [Consumer Guide](docs/consumer-guide.md) for a
6
+ > step-by-step walkthrough of adding this plugin to an existing LWC project.
7
+
5
8
  ## Installation
6
9
 
7
10
  ```bash
@@ -220,3 +223,36 @@ globalThis.__sfdc_sdk__ = await createDataSDK({ uiBundle: { basePath: "/" } });
220
223
  | ---------- | --------- | ------------------ | ------------------------------------ |
221
224
  | `orgAlias` | `string` | sf CLI default org | Salesforce org alias |
222
225
  | `debug` | `boolean` | `false` | Log each proxied request to terminal |
226
+
227
+ ## Claude Code Skill
228
+
229
+ This package includes a [Claude Code](https://docs.anthropic.com/en/docs/claude-code) skill that interactively sets up the plugin in your LWC project. It detects your project structure, asks which component to use as the root, inspects the component tree for GraphQL/label/base-component usage, and generates all the config files.
230
+
231
+ ### Installing the skill
232
+
233
+ **Option A — Global (available in all projects):**
234
+
235
+ ```bash
236
+ cp -r node_modules/@salesforce/vite-plugin-lwc-ui-bundle/skills/setup-lwc-vite-plugin \
237
+ ~/.claude/skills/setup-lwc-vite-plugin
238
+ ```
239
+
240
+ **Option B — Project-scoped (available only in this project):**
241
+
242
+ ```bash
243
+ mkdir -p .claude/skills
244
+ cp -r node_modules/@salesforce/vite-plugin-lwc-ui-bundle/skills/setup-lwc-vite-plugin \
245
+ .claude/skills/setup-lwc-vite-plugin
246
+ ```
247
+
248
+ ### Using the skill
249
+
250
+ Once installed, the skill triggers automatically in Claude Code when you ask things like:
251
+
252
+ - "Set up vite-plugin-lwc-ui-bundle in my project"
253
+ - "I want to run my LWC components outside Salesforce"
254
+ - "Build a static LWC bundle"
255
+ - "Add Vite to my SFDX project"
256
+ - "Compile LWC off-platform"
257
+
258
+ You can also invoke it explicitly with `/setup-lwc-vite-plugin`.
@@ -321,7 +321,7 @@ export class graphql {
321
321
  try {
322
322
  // 1. UIBundle / local dev: use globalThis.__sfdc_sdk__.graphql if available
323
323
  if (typeof globalThis.__sfdc_sdk__?.graphql === 'function') {
324
- const result = await globalThis.__sfdc_sdk__.graphql(query, variables);
324
+ const result = await globalThis.__sfdc_sdk__.graphql({ query, variables });
325
325
  this._emit({ data: result?.data, errors: result?.errors });
326
326
  return;
327
327
  }
@@ -389,7 +389,7 @@ export async function executeMutation(config) {
389
389
  try {
390
390
  // 1. UIBundle / local dev
391
391
  if (typeof globalThis.__sfdc_sdk__?.graphql === 'function') {
392
- const result = await globalThis.__sfdc_sdk__.graphql(query, variables ?? {});
392
+ const result = await globalThis.__sfdc_sdk__.graphql({ query, variables: variables ?? {} });
393
393
  return { data: result?.data, errors: result?.errors };
394
394
  }
395
395
  // 2. MCP surface
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/providers/label.ts","../../src/providers/i18n.ts","../../src/providers/gate.ts","../../src/providers/access-check.ts","../../src/providers/client.ts","../../src/providers/primitive-utils.ts","../../src/providers/lightning-graphql.ts"],"sourcesContent":["/**\n * Copyright (c) 2026, Salesforce, Inc.,\n * All rights reserved.\n * For full license text, see the LICENSE.txt file\n */\nimport type { Provider } from \"../types\";\n\nconst LABEL_DEFAULTS: Record<string, string> = {\n\t\"LightningForm.edit\": \"Edit\",\n\t\"LightningForm.save\": \"Save\",\n\t\"LightningForm.cancel\": \"Cancel\",\n\t\"LightningForm.error\": \"Error\",\n\t\"RecordDetailUC.Expand\": \"Expand\",\n\t\"RecordDetailUC.Collapse\": \"Collapse\",\n\t\"Global_Entity.created_by\": \"Created By\",\n\t\"Global_Entity.last_modified_by\": \"Last Modified By\",\n\t\"DetailError.GenericSaveError\": \"An error occurred while saving.\",\n\t\"MobileWebError.NoPreEditAccess\": \"You don't have access to edit this record.\",\n\t\"DuplicateList.ToastMessageBlock\": \"Duplicate records were found.\",\n\t\"DuplicateList.NonBlockingHeaderText\": \"Duplicate records found\",\n\t\"DuplicateList.ToastMessageAlertEdit\": \"Duplicate records were found. You can continue editing.\",\n\t\"Errors.ErrorPopoverHeading\": \"Error\",\n};\n\nexport function label(overrides: Record<string, string> = {}): Provider {\n\tconst defaults = { ...LABEL_DEFAULTS, ...overrides };\n\treturn {\n\t\tprefix: \"@salesforce/label/\",\n\t\tresolve(specifier) {\n\t\t\tif (!specifier.startsWith(\"@salesforce/label/\")) return null;\n\n\t\t\tconst key = specifier.slice(\"@salesforce/label/\".length);\n\t\t\tconst fallbackLabel = key\n\t\t\t\t.split(\".\")\n\t\t\t\t.at(-1)!\n\t\t\t\t.replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n\t\t\t\t.replace(/_/g, \" \")\n\t\t\t\t.trim();\n\t\t\tconst value = defaults[key] ?? fallbackLabel;\n\t\t\treturn `export default ${JSON.stringify(value)};`;\n\t\t},\n\t};\n}\n","/**\n * Copyright (c) 2026, Salesforce, Inc.,\n * All rights reserved.\n * For full license text, see the LICENSE.txt file\n */\nimport type { Provider } from \"../types\";\n\nconst BROWSER_DERIVED = new Set([\n\t\"lang\",\n\t\"dir\",\n\t\"locale\",\n\t\"currency\",\n\t\"timeZone\",\n\t\"firstDayOfWeek\",\n]);\n\nconst STATIC_DEFAULTS: Record<string, string> = {\n\t\"dateTime.shortDateFormat\": \"M/d/yyyy\",\n\t\"dateTime.mediumDateFormat\": \"MMM d, yyyy\",\n\t\"dateTime.longDateFormat\": \"MMMM d, yyyy\",\n\t\"dateTime.shortTimeFormat\": \"h:mm a\",\n\t\"dateTime.mediumTimeFormat\": \"h:mm:ss a\",\n\t\"dateTime.longTimeFormat\": \"h:mm:ss a z\",\n\t\"dateTime.shortDateTimeFormat\": \"M/d/yyyy, h:mm a\",\n\t\"number.numberFormat\": \"#,##0.##\",\n\t\"number.currencyFormat\": \"¤#,##0.00\",\n\t\"number.decimalSeparator\": \".\",\n\t\"number.groupingSeparator\": \",\",\n\t\"number.percentFormat\": \"#,##0%\",\n\t\"number.percentSign\": \"%\",\n\t\"number.plusSign\": \"+\",\n\t\"number.minusSign\": \"-\",\n\t\"number.exponentialSign\": \"E\",\n\t\"number.superscriptingExponentSign\": \"×\",\n\t\"number.perMilleSign\": \"‰\",\n\t\"number.infinity\": \"∞\",\n\t\"number.nan\": \"NaN\",\n\t\"number.currencySymbol\": \"$\",\n\tdefaultCalendar: \"gregory\",\n\tdefaultNumberingSystem: \"latn\",\n};\n\nconst MONTHS_WIDE = [\n\t\"January\",\n\t\"February\",\n\t\"March\",\n\t\"April\",\n\t\"May\",\n\t\"June\",\n\t\"July\",\n\t\"August\",\n\t\"September\",\n\t\"October\",\n\t\"November\",\n\t\"December\",\n];\nconst MONTHS_ABBR = [\n\t\"Jan\",\n\t\"Feb\",\n\t\"Mar\",\n\t\"Apr\",\n\t\"May\",\n\t\"Jun\",\n\t\"Jul\",\n\t\"Aug\",\n\t\"Sep\",\n\t\"Oct\",\n\t\"Nov\",\n\t\"Dec\",\n];\nconst DAYS_WIDE = [\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"];\nconst DAYS_ABBR = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\n\nconst OBJECT_DEFAULTS: Record<string, unknown> = {\n\t\"common.digits\": { latn: \"0123456789\" },\n\t\"common.calendarData\": {\n\t\tgregory: { calendarSystem: \"solar\", eras: {} },\n\t},\n\tcalendarData: {\n\t\tgregory: {\n\t\t\tmonths: {\n\t\t\t\tformat: {\n\t\t\t\t\twide: MONTHS_WIDE,\n\t\t\t\t\tabbreviated: MONTHS_ABBR,\n\t\t\t\t\tnarrow: MONTHS_ABBR,\n\t\t\t\t},\n\t\t\t},\n\t\t\tdays: {\n\t\t\t\tformat: {\n\t\t\t\t\twide: DAYS_WIDE,\n\t\t\t\t\tabbreviated: DAYS_ABBR,\n\t\t\t\t\tnarrow: DAYS_ABBR,\n\t\t\t\t},\n\t\t\t},\n\t\t\tdayPeriods: {\n\t\t\t\tformat: {\n\t\t\t\t\twide: [\"AM\", \"PM\"],\n\t\t\t\t\tabbreviated: [\"am\", \"pm\"],\n\t\t\t\t\tnarrow: [\"a\", \"p\"],\n\t\t\t\t},\n\t\t\t},\n\t\t\teras: {\n\t\t\t\teraAbbr: [\"AD\"],\n\t\t\t\teraNames: [\"Anno Domini\"],\n\t\t\t\teraNarrow: [\"A\"],\n\t\t\t},\n\t\t},\n\t},\n};\n\nconst RUNTIME_SOURCE = `\nconst RTL_LANGS = new Set([\n 'ar', 'arc', 'dv', 'fa', 'ha', 'he', 'khw', 'ks', 'ku', 'ps', 'ur', 'yi',\n]);\n\nfunction getLocale() {\n if (typeof navigator === 'undefined') return 'en-US';\n return navigator.language || 'en-US';\n}\n\nfunction getLang() {\n return getLocale().split('-')[0];\n}\n\nfunction getDir() {\n return RTL_LANGS.has(getLang()) ? 'rtl' : 'ltr';\n}\n\nfunction getCurrency() {\n try {\n const parts = new Intl.NumberFormat(getLocale(), {\n style: 'currency',\n currency: 'USD',\n currencyDisplay: 'code',\n }).resolvedOptions();\n return parts.currency || 'USD';\n } catch {\n return 'USD';\n }\n}\n\nfunction getTimeZone() {\n try {\n return Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';\n } catch {\n return 'UTC';\n }\n}\n\nfunction getFirstDayOfWeek() {\n try {\n const loc = new Intl.Locale(getLocale());\n if (loc.weekInfo) return String(loc.weekInfo.firstDay);\n if (typeof loc.getWeekInfo === 'function') return String(loc.getWeekInfo().firstDay);\n } catch { /* unsupported in this browser */ }\n return '7';\n}\n\nconst values = {\n lang: getLang(),\n dir: getDir(),\n locale: getLocale(),\n currency: getCurrency(),\n timeZone: getTimeZone(),\n firstDayOfWeek: getFirstDayOfWeek(),\n};\n\nexport default values['__KEY__'];\n`;\n\nexport interface I18nOptions {\n\tstaticOverrides?: Record<string, string>;\n\tobjectOverrides?: Record<string, unknown>;\n}\n\nexport function i18n(options: I18nOptions = {}): Provider {\n\tconst { staticOverrides = {}, objectOverrides = {} } = options;\n\tconst statics = { ...STATIC_DEFAULTS, ...staticOverrides };\n\tconst objects = { ...OBJECT_DEFAULTS, ...objectOverrides };\n\n\treturn {\n\t\tprefix: \"@salesforce/i18n/\",\n\t\tresolve(specifier) {\n\t\t\tif (!specifier.startsWith(\"@salesforce/i18n/\")) return null;\n\n\t\t\tconst key = specifier.slice(\"@salesforce/i18n/\".length);\n\n\t\t\tif (BROWSER_DERIVED.has(key)) {\n\t\t\t\treturn RUNTIME_SOURCE.replace(\"__KEY__\", key);\n\t\t\t}\n\n\t\t\tif (key in objects) {\n\t\t\t\treturn `export default ${JSON.stringify(objects[key])};`;\n\t\t\t}\n\n\t\t\tconst value = statics[key] ?? key;\n\t\t\treturn `export default ${JSON.stringify(value)};`;\n\t\t},\n\t};\n}\n","/**\n * Copyright (c) 2026, Salesforce, Inc.,\n * All rights reserved.\n * For full license text, see the LICENSE.txt file\n */\nimport type { Provider } from \"../types\";\n\nexport function gate(overrides: Record<string, boolean> = {}): Provider {\n\treturn {\n\t\tprefix: \"@salesforce/gate/\",\n\t\tresolve(specifier) {\n\t\t\tif (!specifier.startsWith(\"@salesforce/gate/\")) return null;\n\t\t\tconst name = specifier.slice(\"@salesforce/gate/\".length);\n\t\t\tconst isOpen = overrides[name] ?? true;\n\t\t\treturn `export default { isOpen: () => ${isOpen} };`;\n\t\t},\n\t};\n}\n","/**\n * Copyright (c) 2026, Salesforce, Inc.,\n * All rights reserved.\n * For full license text, see the LICENSE.txt file\n */\nimport type { Provider } from \"../types\";\n\nexport function accessCheck(overrides: Record<string, boolean> = {}): Provider {\n\treturn {\n\t\tprefix: \"@salesforce/accessCheck/\",\n\t\tresolve(specifier) {\n\t\t\tif (!specifier.startsWith(\"@salesforce/accessCheck/\")) return null;\n\t\t\tconst name = specifier.slice(\"@salesforce/accessCheck/\".length);\n\t\t\tconst value = overrides[name] ?? false;\n\t\t\treturn `export default ${value};`;\n\t\t},\n\t};\n}\n","/**\n * Copyright (c) 2026, Salesforce, Inc.,\n * All rights reserved.\n * For full license text, see the LICENSE.txt file\n */\nimport type { Provider } from \"../types\";\n\nconst FORM_FACTOR_SOURCE = `\nfunction getFormFactor() {\n if (typeof window === 'undefined') return 'Large';\n if (window.matchMedia('(max-width: 767px)').matches) return 'Small';\n if (window.matchMedia('(max-width: 1023px)').matches) return 'Medium';\n return 'Large';\n}\n\nexport default getFormFactor();\n`;\n\nexport function client(): Provider {\n\treturn {\n\t\tprefix: \"@salesforce/client/\",\n\t\tresolve(specifier) {\n\t\t\tif (specifier === \"@salesforce/client/formFactor\") {\n\t\t\t\treturn FORM_FACTOR_SOURCE;\n\t\t\t}\n\t\t\treturn null;\n\t\t},\n\t};\n}\n","/**\n * Copyright (c) 2026, Salesforce, Inc.,\n * All rights reserved.\n * For full license text, see the LICENSE.txt file\n */\nimport type { Provider } from \"../types\";\n\nexport function primitiveUtils(): Provider {\n\treturn {\n\t\tprefix: \"lightning/primitiveUtils\",\n\t\tmatch(id) {\n\t\t\treturn id === \"lightning/primitiveUtils\";\n\t\t},\n\t\tresolve(specifier) {\n\t\t\tif (specifier !== \"lightning/primitiveUtils\") return null;\n\n\t\t\treturn `export function normalizeBoolean(value) {\n return typeof value === 'string' || !!value;\n}\n\nexport function reflectAttribute(element, attrName, value) {\n if (!element) return;\n if (typeof value === 'string') {\n element.setAttribute(attrName, value);\n } else if (value === true) {\n element.setAttribute(attrName, '');\n } else if (!value) {\n element.removeAttribute(attrName);\n } else {\n console.warn(\\`Invalid attribute value for \"\\${attrName}\": \\${value}\\`);\n }\n}\n`;\n\t\t},\n\t};\n}\n","/**\n * Copyright (c) 2026, Salesforce, Inc.,\n * All rights reserved.\n * For full license text, see the LICENSE.txt file\n */\nimport type { Provider } from \"../types\";\n\nexport interface LightningGraphqlOptions {\n\t/**\n\t * Name of the MCP tool to call for GraphQL query execution.\n\t * Defaults to `'graphqlQuery'`.\n\t *\n\t * The tool must accept `{ query: string; variables?: Record<string, unknown> }`\n\t * and return a GraphQL-shaped response: `{ data: unknown; errors?: unknown[] }`.\n\t *\n\t * The result is normalised across surfaces:\n\t * - OpenAI surface: `sdk.callTool()` resolves with `{ result: \"<JSON string>\" }`\n\t * - MCP Apps surface: `sdk.callTool()` resolves with `{ structuredContent, content }`\n\t */\n\ttoolName?: string;\n}\n\n/**\n * Off-platform implementation of `lightning/graphql`.\n *\n * Provides the `gql` tagged-template helper and a `graphql` wire adapter that\n * executes GraphQL queries by calling an MCP tool via `@salesforce/sdk-chat`.\n *\n * `getChatSDK()` auto-detects the host surface:\n * - `window.openai` present → OpenAI/ChatGPT bridge (`window.openai.callTool`)\n * - Inside an iframe → MCP Apps JSON-RPC session (with OpenAI fallback)\n * - Neither → UIBundle stub (no callTool; adapter emits an error)\n *\n * The tool name is configurable via `options.toolName` (default `'graphqlQuery'`).\n * Requires `@salesforce/sdk-chat` as a peer dependency.\n */\nfunction buildGraphqlSource(toolName: string): string {\n\treturn `\nimport { getChatSDK } from '@salesforce/sdk-chat';\n\nexport function gql(strings, ...values) {\n let result = '';\n strings.forEach((string, i) => {\n result += string;\n if (i < values.length) result += String(values[i]);\n });\n return result;\n}\n\nexport class graphql {\n _dataCallback;\n _config;\n\n constructor(dataCallback) {\n this._dataCallback = dataCallback;\n }\n\n connect() { this._fetch(); }\n disconnect() {}\n\n update(config) {\n this._config = config;\n this._fetch();\n }\n\n refresh() {\n return this._fetch();\n }\n\n async _fetch() {\n const query = this._config?.query;\n const variables = this._config?.variables ?? {};\n\n if (!query) {\n this._emit({ data: undefined, errors: undefined });\n return;\n }\n\n try {\n // 1. UIBundle / local dev: use globalThis.__sfdc_sdk__.graphql if available\n if (typeof globalThis.__sfdc_sdk__?.graphql === 'function') {\n const result = await globalThis.__sfdc_sdk__.graphql(query, variables);\n this._emit({ data: result?.data, errors: result?.errors });\n return;\n }\n\n // 2. MCP surface: use getChatSDK().callTool\n const sdk = await getChatSDK();\n\n if (typeof sdk.callTool !== 'function') {\n throw new Error(\n '[lightning/graphql] No data surface available. ' +\n 'Either initialise globalThis.__sfdc_sdk__ with createDataSDK, ' +\n 'or run inside a ChatGPT / MCP Apps context.'\n );\n }\n\n const raw = await sdk.callTool({\n toolName: ${JSON.stringify(toolName)},\n params: { query, variables },\n });\n\n // Normalise result across surfaces:\n // - OpenAI surface: { result: \"<JSON string of { data, errors }>\" }\n // - MCP Apps surface: { structuredContent: { data, errors }, content: [...] }\n let result;\n if (raw?.structuredContent !== undefined) {\n result = raw.structuredContent;\n } else if (typeof raw?.result === 'string') {\n const parsed = JSON.parse(raw.result);\n if (Array.isArray(parsed)) {\n // MCP content array - extract text from first text block\n const textBlock = parsed.find(\n (b) => b && b.type === 'text' && typeof b.text === 'string',\n );\n const text = textBlock ? textBlock.text : null;\n if (text) {\n try { result = JSON.parse(text); } catch { result = { errors: [{ message: text }] }; }\n } else {\n result = {};\n }\n } else {\n result = parsed;\n }\n } else {\n result = raw ?? {};\n }\n\n this._emit({ data: result?.data, errors: result?.errors });\n } catch (error) {\n this._emit({ data: undefined, errors: [{ message: error.message }] });\n }\n }\n\n _emit({ data, errors }) {\n this._dataCallback({\n data,\n error: errors?.length ? errors : undefined,\n refresh: () => this.refresh(),\n });\n }\n}\n\nexport async function executeMutation(config) {\n const { query, variables } = config;\n if (!query) return { data: undefined, errors: [{ message: 'No query provided' }] };\n try {\n // 1. UIBundle / local dev\n if (typeof globalThis.__sfdc_sdk__?.graphql === 'function') {\n const result = await globalThis.__sfdc_sdk__.graphql(query, variables ?? {});\n return { data: result?.data, errors: result?.errors };\n }\n // 2. MCP surface\n const sdk = await getChatSDK();\n if (typeof sdk.callTool !== 'function') {\n throw new Error('[lightning/graphql] No data surface available.');\n }\n const raw = await sdk.callTool({\n toolName: ${JSON.stringify(toolName)},\n params: { query, variables: variables ?? {} },\n });\n let result;\n if (raw?.structuredContent !== undefined) {\n result = raw.structuredContent;\n } else if (typeof raw?.result === 'string') {\n const parsed = JSON.parse(raw.result);\n if (Array.isArray(parsed)) {\n const textBlock = parsed.find(\n (b) => b && b.type === 'text' && typeof b.text === 'string',\n );\n const text = textBlock ? textBlock.text : null;\n if (text) {\n try { result = JSON.parse(text); } catch { result = { errors: [{ message: text }] }; }\n } else {\n result = {};\n }\n } else {\n result = parsed;\n }\n } else {\n result = raw ?? {};\n }\n return { data: result?.data, errors: result?.errors };\n } catch (error) {\n return { data: undefined, errors: [{ message: error.message }] };\n }\n}\n`;\n}\n\nexport function lightningGraphql(options: LightningGraphqlOptions = {}): Provider {\n\tconst toolName = options.toolName ?? \"graphqlQuery\";\n\treturn {\n\t\tprefix: \"lightning/graphql\",\n\t\tresolve(specifier) {\n\t\t\tif (specifier !== \"lightning/graphql\") return null;\n\t\t\treturn buildGraphqlSource(toolName);\n\t\t},\n\t};\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../src/providers/label.ts","../../src/providers/i18n.ts","../../src/providers/gate.ts","../../src/providers/access-check.ts","../../src/providers/client.ts","../../src/providers/primitive-utils.ts","../../src/providers/lightning-graphql.ts"],"sourcesContent":["/**\n * Copyright (c) 2026, Salesforce, Inc.,\n * All rights reserved.\n * For full license text, see the LICENSE.txt file\n */\nimport type { Provider } from \"../types\";\n\nconst LABEL_DEFAULTS: Record<string, string> = {\n\t\"LightningForm.edit\": \"Edit\",\n\t\"LightningForm.save\": \"Save\",\n\t\"LightningForm.cancel\": \"Cancel\",\n\t\"LightningForm.error\": \"Error\",\n\t\"RecordDetailUC.Expand\": \"Expand\",\n\t\"RecordDetailUC.Collapse\": \"Collapse\",\n\t\"Global_Entity.created_by\": \"Created By\",\n\t\"Global_Entity.last_modified_by\": \"Last Modified By\",\n\t\"DetailError.GenericSaveError\": \"An error occurred while saving.\",\n\t\"MobileWebError.NoPreEditAccess\": \"You don't have access to edit this record.\",\n\t\"DuplicateList.ToastMessageBlock\": \"Duplicate records were found.\",\n\t\"DuplicateList.NonBlockingHeaderText\": \"Duplicate records found\",\n\t\"DuplicateList.ToastMessageAlertEdit\": \"Duplicate records were found. You can continue editing.\",\n\t\"Errors.ErrorPopoverHeading\": \"Error\",\n};\n\nexport function label(overrides: Record<string, string> = {}): Provider {\n\tconst defaults = { ...LABEL_DEFAULTS, ...overrides };\n\treturn {\n\t\tprefix: \"@salesforce/label/\",\n\t\tresolve(specifier) {\n\t\t\tif (!specifier.startsWith(\"@salesforce/label/\")) return null;\n\n\t\t\tconst key = specifier.slice(\"@salesforce/label/\".length);\n\t\t\tconst fallbackLabel = key\n\t\t\t\t.split(\".\")\n\t\t\t\t.at(-1)!\n\t\t\t\t.replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n\t\t\t\t.replace(/_/g, \" \")\n\t\t\t\t.trim();\n\t\t\tconst value = defaults[key] ?? fallbackLabel;\n\t\t\treturn `export default ${JSON.stringify(value)};`;\n\t\t},\n\t};\n}\n","/**\n * Copyright (c) 2026, Salesforce, Inc.,\n * All rights reserved.\n * For full license text, see the LICENSE.txt file\n */\nimport type { Provider } from \"../types\";\n\nconst BROWSER_DERIVED = new Set([\n\t\"lang\",\n\t\"dir\",\n\t\"locale\",\n\t\"currency\",\n\t\"timeZone\",\n\t\"firstDayOfWeek\",\n]);\n\nconst STATIC_DEFAULTS: Record<string, string> = {\n\t\"dateTime.shortDateFormat\": \"M/d/yyyy\",\n\t\"dateTime.mediumDateFormat\": \"MMM d, yyyy\",\n\t\"dateTime.longDateFormat\": \"MMMM d, yyyy\",\n\t\"dateTime.shortTimeFormat\": \"h:mm a\",\n\t\"dateTime.mediumTimeFormat\": \"h:mm:ss a\",\n\t\"dateTime.longTimeFormat\": \"h:mm:ss a z\",\n\t\"dateTime.shortDateTimeFormat\": \"M/d/yyyy, h:mm a\",\n\t\"number.numberFormat\": \"#,##0.##\",\n\t\"number.currencyFormat\": \"¤#,##0.00\",\n\t\"number.decimalSeparator\": \".\",\n\t\"number.groupingSeparator\": \",\",\n\t\"number.percentFormat\": \"#,##0%\",\n\t\"number.percentSign\": \"%\",\n\t\"number.plusSign\": \"+\",\n\t\"number.minusSign\": \"-\",\n\t\"number.exponentialSign\": \"E\",\n\t\"number.superscriptingExponentSign\": \"×\",\n\t\"number.perMilleSign\": \"‰\",\n\t\"number.infinity\": \"∞\",\n\t\"number.nan\": \"NaN\",\n\t\"number.currencySymbol\": \"$\",\n\tdefaultCalendar: \"gregory\",\n\tdefaultNumberingSystem: \"latn\",\n};\n\nconst MONTHS_WIDE = [\n\t\"January\",\n\t\"February\",\n\t\"March\",\n\t\"April\",\n\t\"May\",\n\t\"June\",\n\t\"July\",\n\t\"August\",\n\t\"September\",\n\t\"October\",\n\t\"November\",\n\t\"December\",\n];\nconst MONTHS_ABBR = [\n\t\"Jan\",\n\t\"Feb\",\n\t\"Mar\",\n\t\"Apr\",\n\t\"May\",\n\t\"Jun\",\n\t\"Jul\",\n\t\"Aug\",\n\t\"Sep\",\n\t\"Oct\",\n\t\"Nov\",\n\t\"Dec\",\n];\nconst DAYS_WIDE = [\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"];\nconst DAYS_ABBR = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\n\nconst OBJECT_DEFAULTS: Record<string, unknown> = {\n\t\"common.digits\": { latn: \"0123456789\" },\n\t\"common.calendarData\": {\n\t\tgregory: { calendarSystem: \"solar\", eras: {} },\n\t},\n\tcalendarData: {\n\t\tgregory: {\n\t\t\tmonths: {\n\t\t\t\tformat: {\n\t\t\t\t\twide: MONTHS_WIDE,\n\t\t\t\t\tabbreviated: MONTHS_ABBR,\n\t\t\t\t\tnarrow: MONTHS_ABBR,\n\t\t\t\t},\n\t\t\t},\n\t\t\tdays: {\n\t\t\t\tformat: {\n\t\t\t\t\twide: DAYS_WIDE,\n\t\t\t\t\tabbreviated: DAYS_ABBR,\n\t\t\t\t\tnarrow: DAYS_ABBR,\n\t\t\t\t},\n\t\t\t},\n\t\t\tdayPeriods: {\n\t\t\t\tformat: {\n\t\t\t\t\twide: [\"AM\", \"PM\"],\n\t\t\t\t\tabbreviated: [\"am\", \"pm\"],\n\t\t\t\t\tnarrow: [\"a\", \"p\"],\n\t\t\t\t},\n\t\t\t},\n\t\t\teras: {\n\t\t\t\teraAbbr: [\"AD\"],\n\t\t\t\teraNames: [\"Anno Domini\"],\n\t\t\t\teraNarrow: [\"A\"],\n\t\t\t},\n\t\t},\n\t},\n};\n\nconst RUNTIME_SOURCE = `\nconst RTL_LANGS = new Set([\n 'ar', 'arc', 'dv', 'fa', 'ha', 'he', 'khw', 'ks', 'ku', 'ps', 'ur', 'yi',\n]);\n\nfunction getLocale() {\n if (typeof navigator === 'undefined') return 'en-US';\n return navigator.language || 'en-US';\n}\n\nfunction getLang() {\n return getLocale().split('-')[0];\n}\n\nfunction getDir() {\n return RTL_LANGS.has(getLang()) ? 'rtl' : 'ltr';\n}\n\nfunction getCurrency() {\n try {\n const parts = new Intl.NumberFormat(getLocale(), {\n style: 'currency',\n currency: 'USD',\n currencyDisplay: 'code',\n }).resolvedOptions();\n return parts.currency || 'USD';\n } catch {\n return 'USD';\n }\n}\n\nfunction getTimeZone() {\n try {\n return Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';\n } catch {\n return 'UTC';\n }\n}\n\nfunction getFirstDayOfWeek() {\n try {\n const loc = new Intl.Locale(getLocale());\n if (loc.weekInfo) return String(loc.weekInfo.firstDay);\n if (typeof loc.getWeekInfo === 'function') return String(loc.getWeekInfo().firstDay);\n } catch { /* unsupported in this browser */ }\n return '7';\n}\n\nconst values = {\n lang: getLang(),\n dir: getDir(),\n locale: getLocale(),\n currency: getCurrency(),\n timeZone: getTimeZone(),\n firstDayOfWeek: getFirstDayOfWeek(),\n};\n\nexport default values['__KEY__'];\n`;\n\nexport interface I18nOptions {\n\tstaticOverrides?: Record<string, string>;\n\tobjectOverrides?: Record<string, unknown>;\n}\n\nexport function i18n(options: I18nOptions = {}): Provider {\n\tconst { staticOverrides = {}, objectOverrides = {} } = options;\n\tconst statics = { ...STATIC_DEFAULTS, ...staticOverrides };\n\tconst objects = { ...OBJECT_DEFAULTS, ...objectOverrides };\n\n\treturn {\n\t\tprefix: \"@salesforce/i18n/\",\n\t\tresolve(specifier) {\n\t\t\tif (!specifier.startsWith(\"@salesforce/i18n/\")) return null;\n\n\t\t\tconst key = specifier.slice(\"@salesforce/i18n/\".length);\n\n\t\t\tif (BROWSER_DERIVED.has(key)) {\n\t\t\t\treturn RUNTIME_SOURCE.replace(\"__KEY__\", key);\n\t\t\t}\n\n\t\t\tif (key in objects) {\n\t\t\t\treturn `export default ${JSON.stringify(objects[key])};`;\n\t\t\t}\n\n\t\t\tconst value = statics[key] ?? key;\n\t\t\treturn `export default ${JSON.stringify(value)};`;\n\t\t},\n\t};\n}\n","/**\n * Copyright (c) 2026, Salesforce, Inc.,\n * All rights reserved.\n * For full license text, see the LICENSE.txt file\n */\nimport type { Provider } from \"../types\";\n\nexport function gate(overrides: Record<string, boolean> = {}): Provider {\n\treturn {\n\t\tprefix: \"@salesforce/gate/\",\n\t\tresolve(specifier) {\n\t\t\tif (!specifier.startsWith(\"@salesforce/gate/\")) return null;\n\t\t\tconst name = specifier.slice(\"@salesforce/gate/\".length);\n\t\t\tconst isOpen = overrides[name] ?? true;\n\t\t\treturn `export default { isOpen: () => ${isOpen} };`;\n\t\t},\n\t};\n}\n","/**\n * Copyright (c) 2026, Salesforce, Inc.,\n * All rights reserved.\n * For full license text, see the LICENSE.txt file\n */\nimport type { Provider } from \"../types\";\n\nexport function accessCheck(overrides: Record<string, boolean> = {}): Provider {\n\treturn {\n\t\tprefix: \"@salesforce/accessCheck/\",\n\t\tresolve(specifier) {\n\t\t\tif (!specifier.startsWith(\"@salesforce/accessCheck/\")) return null;\n\t\t\tconst name = specifier.slice(\"@salesforce/accessCheck/\".length);\n\t\t\tconst value = overrides[name] ?? false;\n\t\t\treturn `export default ${value};`;\n\t\t},\n\t};\n}\n","/**\n * Copyright (c) 2026, Salesforce, Inc.,\n * All rights reserved.\n * For full license text, see the LICENSE.txt file\n */\nimport type { Provider } from \"../types\";\n\nconst FORM_FACTOR_SOURCE = `\nfunction getFormFactor() {\n if (typeof window === 'undefined') return 'Large';\n if (window.matchMedia('(max-width: 767px)').matches) return 'Small';\n if (window.matchMedia('(max-width: 1023px)').matches) return 'Medium';\n return 'Large';\n}\n\nexport default getFormFactor();\n`;\n\nexport function client(): Provider {\n\treturn {\n\t\tprefix: \"@salesforce/client/\",\n\t\tresolve(specifier) {\n\t\t\tif (specifier === \"@salesforce/client/formFactor\") {\n\t\t\t\treturn FORM_FACTOR_SOURCE;\n\t\t\t}\n\t\t\treturn null;\n\t\t},\n\t};\n}\n","/**\n * Copyright (c) 2026, Salesforce, Inc.,\n * All rights reserved.\n * For full license text, see the LICENSE.txt file\n */\nimport type { Provider } from \"../types\";\n\nexport function primitiveUtils(): Provider {\n\treturn {\n\t\tprefix: \"lightning/primitiveUtils\",\n\t\tmatch(id) {\n\t\t\treturn id === \"lightning/primitiveUtils\";\n\t\t},\n\t\tresolve(specifier) {\n\t\t\tif (specifier !== \"lightning/primitiveUtils\") return null;\n\n\t\t\treturn `export function normalizeBoolean(value) {\n return typeof value === 'string' || !!value;\n}\n\nexport function reflectAttribute(element, attrName, value) {\n if (!element) return;\n if (typeof value === 'string') {\n element.setAttribute(attrName, value);\n } else if (value === true) {\n element.setAttribute(attrName, '');\n } else if (!value) {\n element.removeAttribute(attrName);\n } else {\n console.warn(\\`Invalid attribute value for \"\\${attrName}\": \\${value}\\`);\n }\n}\n`;\n\t\t},\n\t};\n}\n","/**\n * Copyright (c) 2026, Salesforce, Inc.,\n * All rights reserved.\n * For full license text, see the LICENSE.txt file\n */\nimport type { Provider } from \"../types\";\n\nexport interface LightningGraphqlOptions {\n\t/**\n\t * Name of the MCP tool to call for GraphQL query execution.\n\t * Defaults to `'graphqlQuery'`.\n\t *\n\t * The tool must accept `{ query: string; variables?: Record<string, unknown> }`\n\t * and return a GraphQL-shaped response: `{ data: unknown; errors?: unknown[] }`.\n\t *\n\t * The result is normalised across surfaces:\n\t * - OpenAI surface: `sdk.callTool()` resolves with `{ result: \"<JSON string>\" }`\n\t * - MCP Apps surface: `sdk.callTool()` resolves with `{ structuredContent, content }`\n\t */\n\ttoolName?: string;\n}\n\n/**\n * Off-platform implementation of `lightning/graphql`.\n *\n * Provides the `gql` tagged-template helper and a `graphql` wire adapter that\n * executes GraphQL queries by calling an MCP tool via `@salesforce/sdk-chat`.\n *\n * `getChatSDK()` auto-detects the host surface:\n * - `window.openai` present → OpenAI/ChatGPT bridge (`window.openai.callTool`)\n * - Inside an iframe → MCP Apps JSON-RPC session (with OpenAI fallback)\n * - Neither → UIBundle stub (no callTool; adapter emits an error)\n *\n * The tool name is configurable via `options.toolName` (default `'graphqlQuery'`).\n * Requires `@salesforce/sdk-chat` as a peer dependency.\n */\nfunction buildGraphqlSource(toolName: string): string {\n\treturn `\nimport { getChatSDK } from '@salesforce/sdk-chat';\n\nexport function gql(strings, ...values) {\n let result = '';\n strings.forEach((string, i) => {\n result += string;\n if (i < values.length) result += String(values[i]);\n });\n return result;\n}\n\nexport class graphql {\n _dataCallback;\n _config;\n\n constructor(dataCallback) {\n this._dataCallback = dataCallback;\n }\n\n connect() { this._fetch(); }\n disconnect() {}\n\n update(config) {\n this._config = config;\n this._fetch();\n }\n\n refresh() {\n return this._fetch();\n }\n\n async _fetch() {\n const query = this._config?.query;\n const variables = this._config?.variables ?? {};\n\n if (!query) {\n this._emit({ data: undefined, errors: undefined });\n return;\n }\n\n try {\n // 1. UIBundle / local dev: use globalThis.__sfdc_sdk__.graphql if available\n if (typeof globalThis.__sfdc_sdk__?.graphql === 'function') {\n const result = await globalThis.__sfdc_sdk__.graphql({ query, variables });\n this._emit({ data: result?.data, errors: result?.errors });\n return;\n }\n\n // 2. MCP surface: use getChatSDK().callTool\n const sdk = await getChatSDK();\n\n if (typeof sdk.callTool !== 'function') {\n throw new Error(\n '[lightning/graphql] No data surface available. ' +\n 'Either initialise globalThis.__sfdc_sdk__ with createDataSDK, ' +\n 'or run inside a ChatGPT / MCP Apps context.'\n );\n }\n\n const raw = await sdk.callTool({\n toolName: ${JSON.stringify(toolName)},\n params: { query, variables },\n });\n\n // Normalise result across surfaces:\n // - OpenAI surface: { result: \"<JSON string of { data, errors }>\" }\n // - MCP Apps surface: { structuredContent: { data, errors }, content: [...] }\n let result;\n if (raw?.structuredContent !== undefined) {\n result = raw.structuredContent;\n } else if (typeof raw?.result === 'string') {\n const parsed = JSON.parse(raw.result);\n if (Array.isArray(parsed)) {\n // MCP content array - extract text from first text block\n const textBlock = parsed.find(\n (b) => b && b.type === 'text' && typeof b.text === 'string',\n );\n const text = textBlock ? textBlock.text : null;\n if (text) {\n try { result = JSON.parse(text); } catch { result = { errors: [{ message: text }] }; }\n } else {\n result = {};\n }\n } else {\n result = parsed;\n }\n } else {\n result = raw ?? {};\n }\n\n this._emit({ data: result?.data, errors: result?.errors });\n } catch (error) {\n this._emit({ data: undefined, errors: [{ message: error.message }] });\n }\n }\n\n _emit({ data, errors }) {\n this._dataCallback({\n data,\n error: errors?.length ? errors : undefined,\n refresh: () => this.refresh(),\n });\n }\n}\n\nexport async function executeMutation(config) {\n const { query, variables } = config;\n if (!query) return { data: undefined, errors: [{ message: 'No query provided' }] };\n try {\n // 1. UIBundle / local dev\n if (typeof globalThis.__sfdc_sdk__?.graphql === 'function') {\n const result = await globalThis.__sfdc_sdk__.graphql({ query, variables: variables ?? {} });\n return { data: result?.data, errors: result?.errors };\n }\n // 2. MCP surface\n const sdk = await getChatSDK();\n if (typeof sdk.callTool !== 'function') {\n throw new Error('[lightning/graphql] No data surface available.');\n }\n const raw = await sdk.callTool({\n toolName: ${JSON.stringify(toolName)},\n params: { query, variables: variables ?? {} },\n });\n let result;\n if (raw?.structuredContent !== undefined) {\n result = raw.structuredContent;\n } else if (typeof raw?.result === 'string') {\n const parsed = JSON.parse(raw.result);\n if (Array.isArray(parsed)) {\n const textBlock = parsed.find(\n (b) => b && b.type === 'text' && typeof b.text === 'string',\n );\n const text = textBlock ? textBlock.text : null;\n if (text) {\n try { result = JSON.parse(text); } catch { result = { errors: [{ message: text }] }; }\n } else {\n result = {};\n }\n } else {\n result = parsed;\n }\n } else {\n result = raw ?? {};\n }\n return { data: result?.data, errors: result?.errors };\n } catch (error) {\n return { data: undefined, errors: [{ message: error.message }] };\n }\n}\n`;\n}\n\nexport function lightningGraphql(options: LightningGraphqlOptions = {}): Provider {\n\tconst toolName = options.toolName ?? \"graphqlQuery\";\n\treturn {\n\t\tprefix: \"lightning/graphql\",\n\t\tresolve(specifier) {\n\t\t\tif (specifier !== \"lightning/graphql\") return null;\n\t\t\treturn buildGraphqlSource(toolName);\n\t\t},\n\t};\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,429 @@
1
+ # Compiling LWC Components Off-Platform with @salesforce/vite-plugin-lwc-ui-bundle
2
+
3
+ This guide walks you through adding `@salesforce/vite-plugin-lwc-ui-bundle` to an
4
+ existing LWC project so you can compile your components into a single self-contained
5
+ `dist/index.html` that runs in any browser — no Salesforce org required.
6
+
7
+ ---
8
+
9
+ ## Prerequisites
10
+
11
+ - **Node.js** >= 20.0.0
12
+ - **LWC components** in a project directory (standard SFDX project or custom layout)
13
+ - **Salesforce CLI** (`sf`) — only needed if connecting to a real org (Tier 2)
14
+
15
+ ---
16
+
17
+ ## Overview
18
+
19
+ The plugin supports two tiers of usage:
20
+
21
+ | Tier | What You Get | Org Required? |
22
+ | ------------------------------- | ------------------------------------------ | ------------------ |
23
+ | **Tier 1: Off-Platform Build** | Static bundle with mock data, labels, i18n | No |
24
+ | **Tier 2: Live Org Connection** | Real GraphQL queries, live Salesforce data | Yes (via `sf` CLI) |
25
+
26
+ Start with Tier 1. Add Tier 2 later if your components use `lightning/graphql` or
27
+ need live data.
28
+
29
+ ---
30
+
31
+ ## Tier 1: Off-Platform Build
32
+
33
+ ### Project Structure
34
+
35
+ You need to add **3 files** to your project and update `package.json`:
36
+
37
+ ```
38
+ my-lwc-project/
39
+ ├── force-app/main/default/lwc/ ← your existing LWC components
40
+ │ ├── myApp/
41
+ │ │ ├── myApp.js
42
+ │ │ ├── myApp.html
43
+ │ │ └── myApp.js-meta.xml
44
+ │ └── myComponent/
45
+ │ ├── myComponent.js
46
+ │ ├── myComponent.html
47
+ │ └── myComponent.js-meta.xml
48
+ ├── bootstrap.js ← NEW: mounts your root component
49
+ ├── index.html ← NEW: entry point
50
+ ├── vite.config.js ← NEW: plugin configuration
51
+ └── package.json ← UPDATED: add deps and scripts
52
+ ```
53
+
54
+ ### Step 1: Install Dependencies
55
+
56
+ **Option A — Quick install command:**
57
+
58
+ ```bash
59
+ npm install @salesforce/vite-plugin-lwc-ui-bundle @lwc/rollup-plugin vite vite-plugin-singlefile --save-dev
60
+ npm install lwc @salesforce-ux/design-system lightning-base-components
61
+ ```
62
+
63
+ **Option B — Add to `package.json` manually, then `npm install`:**
64
+
65
+ ```json
66
+ {
67
+ "type": "module",
68
+ "scripts": {
69
+ "dev": "vite",
70
+ "build": "vite build",
71
+ "preview": "vite preview"
72
+ },
73
+ "dependencies": {
74
+ "@salesforce-ux/design-system": "^2.29.1",
75
+ "lightning-base-components": "^1.28.17-alpha",
76
+ "lwc": "^9.0.0"
77
+ },
78
+ "devDependencies": {
79
+ "@lwc/rollup-plugin": "^9.0.0",
80
+ "@salesforce/vite-plugin-lwc-ui-bundle": "^1.0.0",
81
+ "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0",
82
+ "vite-plugin-singlefile": "^2.3.0"
83
+ }
84
+ }
85
+ ```
86
+
87
+ > **Important:** Add `"type": "module"` to your `package.json` if it is not already
88
+ > present. This is required for Vite's ESM-based config.
89
+
90
+ > **Note:** `@lwc/rollup-plugin` is a required peer dependency of the plugin.
91
+ > `lightning-base-components` is only needed if your components use `lightning/*`
92
+ > base components (e.g., `lightning-card`, `lightning-button`).
93
+
94
+ ### Step 2: Create `vite.config.js`
95
+
96
+ ```js
97
+ import { defineConfig } from "vite";
98
+ import lwcVitePlugin, { builtins } from "@salesforce/vite-plugin-lwc-ui-bundle";
99
+ import { viteSingleFile } from "vite-plugin-singlefile";
100
+
101
+ export default defineConfig({
102
+ build: {
103
+ target: "esnext",
104
+ minify: false,
105
+ },
106
+ plugins: [
107
+ lwcVitePlugin({
108
+ modules: {
109
+ // Flat SFDX structure: specify { path, namespace }
110
+ dirs: [{ path: "force-app/main/default/lwc", namespace: "c" }],
111
+
112
+ // Include if your components use lightning/* base components
113
+ npm: ["lightning-base-components"],
114
+ },
115
+ providers: [
116
+ builtins.label(),
117
+ builtins.i18n(),
118
+ builtins.client(),
119
+ builtins.gate(),
120
+ builtins.accessCheck(),
121
+ ],
122
+ }),
123
+ viteSingleFile(),
124
+ ],
125
+ });
126
+ ```
127
+
128
+ #### Component Directory Configuration
129
+
130
+ The plugin supports two directory structures:
131
+
132
+ **Flat SFDX structure** (standard Salesforce DX — most common):
133
+
134
+ ```
135
+ force-app/main/default/lwc/
136
+ myApp/
137
+ myApp.js
138
+ ```
139
+
140
+ Use `{ path, namespace }` to assign a namespace:
141
+
142
+ ```js
143
+ dirs: [{ path: "force-app/main/default/lwc", namespace: "c" }];
144
+ ```
145
+
146
+ Components are importable as `c/myApp` and rendered as `<c-my-app>`.
147
+
148
+ **Namespaced structure** (off-core projects):
149
+
150
+ ```
151
+ src/lwc/
152
+ myNamespace/
153
+ myComponent/
154
+ myComponent.js
155
+ ```
156
+
157
+ Omit `namespace` — the sub-directory name becomes the namespace:
158
+
159
+ ```js
160
+ dirs: ["src/lwc"];
161
+ ```
162
+
163
+ Components are importable as `myNamespace/myComponent`.
164
+
165
+ #### Configuring Labels
166
+
167
+ The `builtins.label()` provider handles `@salesforce/label/*` imports. How you
168
+ configure it depends on your project:
169
+
170
+ **SFDX project with `CustomLabels.labels-meta.xml`:**
171
+
172
+ Copy label values from your XML into the label provider config:
173
+
174
+ ```js
175
+ builtins.label({
176
+ "c.appTitle": "My LWC App",
177
+ "c.saveButton": "Save",
178
+ "c.cancelButton": "Cancel",
179
+ }),
180
+ ```
181
+
182
+ **Off-core project (no labels XML):**
183
+
184
+ Pass label key-value pairs directly. If no override is provided for a key, the
185
+ plugin returns a human-readable fallback derived from the key name (e.g.,
186
+ `c.appTitle` renders as "App Title").
187
+
188
+ ```js
189
+ // Minimal — uses fallback display for all labels
190
+ builtins.label(),
191
+
192
+ // With explicit overrides
193
+ builtins.label({
194
+ "c.appTitle": "My App Title",
195
+ "c.greeting": "Welcome!",
196
+ }),
197
+ ```
198
+
199
+ #### Why `gate()` and `accessCheck()` Are Needed
200
+
201
+ Even if your components don't use `@salesforce/gate/*` or `@salesforce/accessCheck/*`
202
+ directly, **`lightning-base-components` does internally**. Without these providers,
203
+ you'll get runtime errors like:
204
+
205
+ ```
206
+ Uncaught TypeError: Cannot read properties of undefined (reading 'isOpen')
207
+ ```
208
+
209
+ Always include `builtins.gate()` and `builtins.accessCheck()` when using
210
+ `lightning-base-components`.
211
+
212
+ ### Step 3: Create `index.html`
213
+
214
+ ```html
215
+ <!doctype html>
216
+ <html lang="en">
217
+ <head>
218
+ <meta charset="UTF-8" />
219
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
220
+ <title>My LWC App</title>
221
+ </head>
222
+ <body>
223
+ <div id="app"></div>
224
+ <script>
225
+ globalThis.lwcRuntimeFlags = { DISABLE_SYNTHETIC_SHADOW: true };
226
+ </script>
227
+ <script type="module" src="/bootstrap.js"></script>
228
+ </body>
229
+ </html>
230
+ ```
231
+
232
+ ### Step 4: Create `bootstrap.js`
233
+
234
+ ```js
235
+ import "@salesforce-ux/design-system/assets/styles/salesforce-lightning-design-system.css";
236
+ import "@lwc/synthetic-shadow";
237
+ import { createElement } from "lwc";
238
+
239
+ // Import your root component using its LWC specifier: <namespace>/<componentName>
240
+ import App from "c/myApp";
241
+
242
+ // Mount it — tag name is the kebab-case version of the specifier
243
+ const el = createElement("c-my-app", { is: App });
244
+ document.getElementById("app").appendChild(el);
245
+ ```
246
+
247
+ Replace `c/myApp` and `c-my-app` with your actual root component name.
248
+
249
+ ### Step 5: Build and Test
250
+
251
+ ```bash
252
+ # Production build — outputs dist/index.html (single-file bundle)
253
+ npm run build
254
+
255
+ # Open directly in browser
256
+ open dist/index.html
257
+
258
+ # Or use dev server with live reload
259
+ npm run dev
260
+ # Open http://localhost:5173
261
+
262
+ # Or preview the production build
263
+ npm run preview
264
+ # Open http://localhost:4173
265
+ ```
266
+
267
+ ---
268
+
269
+ ## Tier 2: Live Org Connection (lwcProxy)
270
+
271
+ If your components use `lightning/graphql` or need live Salesforce data, add
272
+ `lwcProxy()` to connect to a real org during development.
273
+
274
+ ### Prerequisites
275
+
276
+ - Salesforce CLI installed and an org connected: `sf org display`
277
+ - Additional packages: `@salesforce/sdk-data` and `@salesforce/ui-bundle`
278
+
279
+ ### Step 1: Install Additional Dependencies
280
+
281
+ ```bash
282
+ npm install @salesforce/sdk-data @salesforce/ui-bundle
283
+ ```
284
+
285
+ ### Step 2: Update `vite.config.js`
286
+
287
+ Add `lwcProxy()` before `lwcVitePlugin()` and add `builtins.lightningGraphql()`
288
+ to providers:
289
+
290
+ ```js
291
+ import { defineConfig } from "vite";
292
+ import lwcVitePlugin, { builtins, lwcProxy } from "@salesforce/vite-plugin-lwc-ui-bundle";
293
+ import { viteSingleFile } from "vite-plugin-singlefile";
294
+
295
+ export default defineConfig({
296
+ build: {
297
+ target: "esnext",
298
+ minify: false,
299
+ },
300
+ plugins: [
301
+ lwcProxy({ debug: true }),
302
+ // lwcProxy({ orgAlias: "my-org", debug: true }), // explicit org
303
+ lwcVitePlugin({
304
+ modules: {
305
+ dirs: [{ path: "force-app/main/default/lwc", namespace: "c" }],
306
+ npm: ["lightning-base-components"],
307
+ },
308
+ providers: [
309
+ builtins.label(),
310
+ builtins.i18n(),
311
+ builtins.client(),
312
+ builtins.gate(),
313
+ builtins.accessCheck(),
314
+ builtins.lightningGraphql(),
315
+ ],
316
+ }),
317
+ viteSingleFile(),
318
+ ],
319
+ });
320
+ ```
321
+
322
+ ### Step 3: Update `bootstrap.js`
323
+
324
+ Initialize the Data SDK before mounting your app. This requires top-level `await`
325
+ (hence `target: "esnext"` in the vite config):
326
+
327
+ ```js
328
+ import "@salesforce-ux/design-system/assets/styles/salesforce-lightning-design-system.css";
329
+ import "@lwc/synthetic-shadow";
330
+ import { createElement } from "lwc";
331
+
332
+ // Initialize SDK — lwcProxy() handles /services/* in the Vite dev server
333
+ import { createDataSDK } from "@salesforce/sdk-data";
334
+ try {
335
+ globalThis.__sfdc_sdk__ = await createDataSDK({ uiBundle: { basePath: "/" } });
336
+ } catch (_err) {
337
+ globalThis.__sfdc_sdk__ = {};
338
+ }
339
+
340
+ // Import your root component
341
+ import App from "c/myApp";
342
+
343
+ const el = createElement("c-my-app", { is: App });
344
+ document.getElementById("app").appendChild(el);
345
+ ```
346
+
347
+ ### Step 4: Run Dev Server
348
+
349
+ ```bash
350
+ npm run dev
351
+ # Terminal shows: [lwc-proxy] Connected to https://your-org.my.salesforce.com
352
+ # Open http://localhost:5173
353
+ ```
354
+
355
+ Components using `@wire(graphql, ...)` will now execute real GraphQL queries
356
+ against your connected org. The proxy intercepts `/services/*` calls inside
357
+ the Vite dev server — no separate proxy process needed.
358
+
359
+ > **Note:** `lwcProxy()` works with `npm run dev` only. The production build
360
+ > (`dist/index.html`) does not include the proxy — it's a static file. For
361
+ > production use with live data, you need a separate API proxy or MCP server.
362
+
363
+ ---
364
+
365
+ ## Common Errors
366
+
367
+ ### `Cannot read properties of undefined (reading 'isOpen')`
368
+
369
+ Missing `gate()` provider. `lightning-base-components` use gates internally.
370
+ Add `builtins.gate()` to your providers array.
371
+
372
+ ### `Rollup failed to resolve import "lightning/button"`
373
+
374
+ Your component uses a Lightning Base Component. Add to `vite.config.js`:
375
+
376
+ ```js
377
+ modules: {
378
+ npm: ["lightning-base-components"],
379
+ }
380
+ ```
381
+
382
+ ### `Top-level await is not available in the configured target environment`
383
+
384
+ Your `bootstrap.js` uses top-level `await` (required for Data SDK). Add
385
+ `target: "esnext"` to the build config:
386
+
387
+ ```js
388
+ build: { target: "esnext" },
389
+ ```
390
+
391
+ ### `[scoped-module-providers] Unhandled import: @salesforce/label/...`
392
+
393
+ Expected — the plugin returns the label key as display text by default.
394
+ To provide specific label values:
395
+
396
+ ```js
397
+ builtins.label({ "c.Save": "Save", "c.Cancel": "Cancel" }),
398
+ ```
399
+
400
+ ### `Rollup failed to resolve import "force/someModule"`
401
+
402
+ Core-only module not available off-platform. Add a stub:
403
+
404
+ ```js
405
+ // stubs/someModule.js
406
+ export const someExport = () => {};
407
+ ```
408
+
409
+ ```js
410
+ // vite.config.js
411
+ lwcVitePlugin({ stubs: { "force/someModule": "./stubs/someModule.js" } });
412
+ ```
413
+
414
+ ### Component renders but looks unstyled
415
+
416
+ Add SLDS import to `bootstrap.js`:
417
+
418
+ ```js
419
+ import "@salesforce-ux/design-system/assets/styles/salesforce-lightning-design-system.css";
420
+ ```
421
+
422
+ ---
423
+
424
+ ## Reference
425
+
426
+ - **npm:** https://www.npmjs.com/package/@salesforce/vite-plugin-lwc-ui-bundle
427
+ - **Source:** https://github.com/salesforce-experience-platform-emu/webapps/tree/main/packages/vite-plugin-lwc-ui-bundle
428
+ - **User Guide:** https://github.com/salesforce-experience-platform-emu/webapps/blob/main/packages/vite-plugin-lwc-ui-bundle/docs/user-guide.md
429
+ - **Example project:** https://git.soma.salesforce.com/sisi-zhang/my-lwc-app-test
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/vite-plugin-lwc-ui-bundle",
3
- "version": "1.131.2",
3
+ "version": "1.131.3",
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",
@@ -37,7 +37,8 @@
37
37
  "files": [
38
38
  "dist",
39
39
  "README.md",
40
- "docs"
40
+ "docs",
41
+ "skills"
41
42
  ],
42
43
  "scripts": {
43
44
  "build": "vite build",
@@ -59,7 +60,7 @@
59
60
  }
60
61
  },
61
62
  "devDependencies": {
62
- "@salesforce/sdk-chat": "^1.131.2",
63
+ "@salesforce/sdk-chat": "^1.131.3",
63
64
  "typescript": "^5.9.3",
64
65
  "vite": "^7.0.0",
65
66
  "vite-plugin-dts": "^4.5.4",
@@ -0,0 +1,242 @@
1
+ ---
2
+ name: setup-lwc-vite-plugin
3
+ description: >
4
+ Set up @salesforce/vite-plugin-lwc-ui-bundle in an LWC project so components
5
+ compile and run off-platform in a browser. Use this skill whenever someone wants
6
+ to: run LWC components outside Salesforce, build a static LWC bundle, add Vite
7
+ to an SFDX or LWC project, compile LWC for a browser, create a standalone LWC
8
+ app, set up an LWC dev server, or use vite-plugin-lwc-ui-bundle. Also use it
9
+ when the user mentions "off-platform LWC", "LWC bundle", "LWC without Salesforce",
10
+ "LWC without an org", "Vite + LWC", or "compile LWC off-core".
11
+ ---
12
+
13
+ # Setup @salesforce/vite-plugin-lwc-ui-bundle
14
+
15
+ This skill adds `@salesforce/vite-plugin-lwc-ui-bundle` to an existing LWC project,
16
+ producing a single `dist/index.html` that runs in any browser without a Salesforce org.
17
+
18
+ ## What the plugin does
19
+
20
+ It wraps the full LWC compilation pipeline behind a single Vite plugin: scoped module
21
+ providers (labels, i18n, gates, etc.), Lightning npm resolution, missing CSS handling,
22
+ and the Vite/LWC bridge. The output is a self-contained HTML file with all JS and CSS
23
+ inlined.
24
+
25
+ ## Reference material
26
+
27
+ The `references/consumer-guide.md` file contains exact file templates, dependency
28
+ versions, and config snippets. Read it when you need to generate `vite.config.js`,
29
+ `index.html`, `bootstrap.js`, or `package.json` updates. This skill tells you
30
+ _when and why_ to use each piece; the consumer guide gives you the exact _what_.
31
+
32
+ ## Interactive Setup Flow
33
+
34
+ Walk the user through these steps in order. Ask questions — don't assume.
35
+
36
+ ### Step 1: Detect the project
37
+
38
+ LWC projects come in two layouts, and the directory structure determines how
39
+ `modules.dirs` must be configured. Detecting the layout first avoids misconfigured
40
+ imports that silently fail at build time.
41
+
42
+ Check in order:
43
+
44
+ 1. `sfdx-project.json` in the project root → **SFDX project**. Components live at
45
+ `force-app/main/default/lwc/` in a flat structure where every component shares
46
+ the `c` namespace. Configure as `dirs: [{ path: "force-app/main/default/lwc", namespace: "c" }]`.
47
+ 2. Directories matching `src/lwc/` or `modules/` → **off-core project**. Sub-directories
48
+ are namespaces (e.g., `src/lwc/myNs/myComponent/`). Configure as `dirs: ["src/lwc"]`.
49
+ 3. If neither found, ask the user where their LWC components live.
50
+
51
+ Read `package.json` to understand what's already installed — avoid adding duplicate
52
+ dependencies later.
53
+
54
+ ### Step 2: Ask for the root component
55
+
56
+ The plugin compiles a tree starting from one root component that gets mounted in the
57
+ HTML page. The user needs to tell you which one, since there's no reliable way to
58
+ auto-detect the "main" component.
59
+
60
+ List the discovered components and ask:
61
+
62
+ > "Which component should be the root of your app? This is the one that gets
63
+ > mounted in the HTML page."
64
+
65
+ Present the component names as a numbered list for easy selection.
66
+
67
+ ### Step 3: Inspect the component tree
68
+
69
+ This step drives every downstream decision: which providers to include, whether
70
+ `lightning-base-components` is needed, and whether to offer Tier 2 (live org). Getting
71
+ this right prevents cryptic build errors.
72
+
73
+ Starting from the root component, trace the dependency tree:
74
+
75
+ 1. Read the root component's `.html` file — find all `c-*` tags (or other
76
+ namespace tags) to identify child components
77
+ 2. Recursively read each child component's `.js` and `.html`
78
+ 3. Collect all imports across the tree:
79
+ - `@salesforce/label/*` → label keys (need `builtins.label()`)
80
+ - `lightning/graphql` or `@wire(graphql` → needs GraphQL support
81
+ - `lightning/*` base components → needs `lightning-base-components` npm package,
82
+ plus `gate()`, `accessCheck()`, and `primitiveUtils()` providers because
83
+ base components use these modules internally even if user code doesn't
84
+ - `@salesforce/gate/*`, `@salesforce/accessCheck/*` → handled by providers
85
+ - `@salesforce/i18n/*` → i18n provider
86
+ - `@salesforce/client/*` → client provider (provides `formFactor` based on viewport width)
87
+ - `lightning/primitiveUtils` → primitiveUtils provider
88
+
89
+ Report what you found:
90
+
91
+ > "I traced your component tree from `c/app`. Here's what I found:
92
+ >
93
+ > - 5 components total
94
+ > - 3 label imports: `c.appTitle`, `c.greeting`, `c.save`
95
+ > - Uses `lightning-card` (needs lightning-base-components)
96
+ > - Uses `lightning/graphql` (needs lwcProxy for live data)
97
+ > - No gate/accessCheck imports in your code, but lightning-base-components
98
+ > uses them internally — I'll include those providers automatically."
99
+
100
+ ### Step 4: Decide the tier
101
+
102
+ The plugin supports two tiers. This determines which dependencies and bootstrap
103
+ code to generate.
104
+
105
+ | Tier | What you get | Org required? |
106
+ | ------------------------------- | ------------------------------------------ | ------------------ |
107
+ | **Tier 1: Off-Platform Build** | Static bundle with mock data, labels, i18n | No |
108
+ | **Tier 2: Live Org Connection** | Real GraphQL queries via `lwcProxy()` | Yes (via `sf` CLI) |
109
+
110
+ If GraphQL usage was detected in Step 3, ask:
111
+
112
+ > "Your components use `lightning/graphql`. Do you want to connect to a real
113
+ > Salesforce org for live data during development?
114
+ >
115
+ > A) Yes — I have an org connected via `sf` CLI (adds lwcProxy + Data SDK)
116
+ > B) No — use mock data for now (GraphQL wire adapter returns empty results)"
117
+
118
+ If no GraphQL detected, default to Tier 1 — there's no benefit to Tier 2 without
119
+ GraphQL or live data needs.
120
+
121
+ ### Step 5: Resolve label values
122
+
123
+ Labels need explicit values because the plugin can't read them from an org at build
124
+ time. Getting them right here means the user sees realistic text immediately instead
125
+ of placeholder keys.
126
+
127
+ 1. **SFDX project**: Check for `force-app/main/default/labels/CustomLabels.labels-meta.xml`.
128
+ If found, parse it and extract `<fullName>` → `<value>` pairs for the label
129
+ keys discovered in Step 3.
130
+ 2. **Off-core project or no labels XML**: For each label key found, use a
131
+ placeholder value derived from the key (e.g., `c.appTitle` → `"App Title"`).
132
+
133
+ Tell the user what labels you found and the values you'll use:
134
+
135
+ > "I found 3 label imports. Here are the values I'll configure:
136
+ >
137
+ > - `c.appTitle` → "My LWC App" (from CustomLabels.labels-meta.xml)
138
+ > - `c.greeting` → "Welcome!" (from CustomLabels.labels-meta.xml)
139
+ > - `c.save` → "Save" (fallback — not found in labels XML)
140
+ >
141
+ > You can change these in `vite.config.js` later."
142
+
143
+ If no labels were found at all, still include `builtins.label()` with no overrides.
144
+ The plugin returns a human-readable fallback for unknown keys, and
145
+ `lightning-base-components` may use labels internally.
146
+
147
+ ### Step 6: Generate files
148
+
149
+ Read `references/consumer-guide.md` for the exact file templates. Generate these
150
+ files, adapting the templates based on what you learned in Steps 1-5:
151
+
152
+ #### Files to create or update
153
+
154
+ 1. **`package.json`** — merge in dependencies and scripts (don't overwrite existing).
155
+ See "Step 1: Install Dependencies" in the consumer guide for the full dep list.
156
+ - Add `"type": "module"` if not present
157
+ - Only add `lightning-base-components` and `@salesforce-ux/design-system` if
158
+ the component tree uses `lightning/*` base components
159
+ - For Tier 2, also add `@salesforce/sdk-data` and `@salesforce/ui-bundle`
160
+
161
+ 2. **`vite.config.js`** — see "Step 2" in the consumer guide for the template.
162
+ Adapt based on your findings:
163
+ - Set `dirs` for the detected project structure (SFDX vs namespaced)
164
+ - Populate `builtins.label({...})` with values from Step 5
165
+ - Include `builtins.primitiveUtils()` if using `lightning-base-components`
166
+ - Include `builtins.lightningGraphql()` only if GraphQL was detected
167
+ - Remove `npm: ["lightning-base-components"]` if no base components used
168
+ - For Tier 2, add `lwcProxy()` before `lwcVitePlugin()` in the plugins array
169
+
170
+ 3. **`index.html`** — see "Step 3" in the consumer guide.
171
+
172
+ 4. **`bootstrap.js`** (project root) — see "Step 4" (Tier 1) or "Step 3" (Tier 2)
173
+ in the consumer guide. Replace the component name with the actual root from
174
+ Step 2. Remove the SLDS CSS import if `lightning-base-components` is not used.
175
+
176
+ #### Advanced options (use only when needed)
177
+
178
+ The plugin accepts additional options for complex projects. Only add these if the
179
+ component tree inspection reveals a need:
180
+
181
+ - **`stubs`**: Map bare module specifiers to stub files for core-only modules
182
+ not available off-platform (e.g., `{ "force/someModule": "./stubs/someModule.js" }`).
183
+ Needed when components import platform modules like `aura`, `logger`, or
184
+ `force/*` that don't exist outside Salesforce.
185
+ - **`lwcOptions`**: Pass-through options for `@lwc/rollup-plugin` to configure
186
+ LWC compiler behavior (e.g., `{ enableDynamicComponents: true }`). Only needed
187
+ for projects using advanced LWC features like dynamic component creation.
188
+ - **`passthroughRules`**: Let specific imports bypass the provider system. Each
189
+ rule has a `specifierPrefix` and `importerPattern` — when both match, the import
190
+ resolves normally. Useful when an npm package (like `lightning-base-components`)
191
+ has its own label definitions that shouldn't be intercepted by your label provider.
192
+ - **`ignorePatterns`**: Specifier prefixes that providers should never intercept.
193
+ Defaults include `@salesforce/sdk-*` and `@salesforce/core`. Extend this if
194
+ you have imports that look like provider-handled patterns but should resolve
195
+ through normal Node module resolution.
196
+
197
+ ### Step 7: Install and build
198
+
199
+ Run:
200
+
201
+ ```bash
202
+ npm install
203
+ npm run build
204
+ ```
205
+
206
+ If the build succeeds, report the output file size and open it:
207
+
208
+ ```bash
209
+ open dist/index.html
210
+ ```
211
+
212
+ If the build fails, diagnose and fix. See the "Common Errors" section in
213
+ `references/consumer-guide.md` for the full troubleshooting guide. The most
214
+ frequent issues:
215
+
216
+ - **`Cannot read properties of undefined (reading 'isOpen')`**: Missing `gate()`
217
+ or `accessCheck()` provider — `lightning-base-components` use gates internally.
218
+ - **`Rollup failed to resolve import "lightning/button"`**: Missing
219
+ `npm: ["lightning-base-components"]` in modules config.
220
+ - **`Unhandled import: @salesforce/label/...`**: Expected warning — the plugin
221
+ returns the key as display text. Add overrides to `builtins.label()` to customize.
222
+ - **`Top-level await is not available`**: Missing `target: "esnext"` in build config.
223
+ - **`Rollup failed to resolve import "force/someModule"`**: Core-only module. Add
224
+ a stub via the `stubs` option.
225
+ - **Component renders but looks unstyled**: Missing SLDS CSS import in bootstrap.js.
226
+
227
+ ### Step 8: Test dev server (Tier 2 only)
228
+
229
+ If lwcProxy was configured:
230
+
231
+ ```bash
232
+ npm run dev
233
+ ```
234
+
235
+ Confirm the terminal shows `[lwc-proxy] Connected to https://...`. Open
236
+ `http://localhost:5173` and verify GraphQL queries return real data.
237
+
238
+ ## Reference
239
+
240
+ - npm: https://www.npmjs.com/package/@salesforce/vite-plugin-lwc-ui-bundle
241
+ - README: https://github.com/salesforce-experience-platform-emu/webapps/tree/main/packages/vite-plugin-lwc-ui-bundle
242
+ - Consumer Guide: https://github.com/salesforce-experience-platform-emu/webapps/blob/main/packages/vite-plugin-lwc-ui-bundle/docs/consumer-guide.md
@@ -1,377 +0,0 @@
1
- # User Guide: Compiling LWC Components into a Static Bundle
2
-
3
- This guide walks through using `@salesforce/vite-plugin-lwc-ui-bundle` to compile
4
- your existing LWC components into a single self-contained `dist/index.html` —
5
- no server required, opens directly in a browser.
6
-
7
- ---
8
-
9
- ## Prerequisites
10
-
11
- - Node.js >= 20.0.0
12
- - Your LWC components already exist in a Salesforce DX project
13
-
14
- ---
15
-
16
- ## Project Structure
17
-
18
- The example project `my-lwc-app` uses the standard Salesforce DX flat structure:
19
-
20
- ```
21
- my-lwc-app/
22
- ├── force-app/main/default/lwc/
23
- │ ├── helloApp/ ← component directly, no namespace folder
24
- │ │ ├── helloApp.js
25
- │ │ ├── helloApp.html
26
- │ │ └── helloApp.js-meta.xml
27
- │ └── helloMessage/
28
- │ ├── helloMessage.js
29
- │ ├── helloMessage.html
30
- │ └── helloMessage.js-meta.xml
31
- ├── src/
32
- │ └── bootstrap.js ← mounts root component
33
- ├── index.html ← entry point
34
- ├── vite.config.js ← plugin config
35
- └── package.json
36
- ```
37
-
38
- Use `namespace: 'c'` in `vite.config.js` to tell the plugin to treat this flat
39
- structure as namespace `c` — components are then importable as `'c/helloApp'`.
40
-
41
- ---
42
-
43
- ## Step 1: Add Files
44
-
45
- ### `package.json`
46
-
47
- ```json
48
- {
49
- "name": "my-lwc-app",
50
- "version": "1.0.0",
51
- "type": "module",
52
- "scripts": {
53
- "build": "vite build",
54
- "dev": "vite",
55
- "preview": "vite preview"
56
- },
57
- "devDependencies": {
58
- "@lwc/rollup-plugin": "^9.0.0",
59
- "@salesforce/vite-plugin-lwc-ui-bundle": "^1.0.0",
60
- "vite": "^7.0.0",
61
- "vite-plugin-singlefile": "^2.3.0"
62
- },
63
- "dependencies": {
64
- "@salesforce-ux/design-system": "^2.29.1",
65
- "lightning-base-components": "^1.28.17-alpha",
66
- "lwc": "^9.0.0"
67
- }
68
- }
69
- ```
70
-
71
- ### `vite.config.js`
72
-
73
- ```js
74
- import { defineConfig } from "vite";
75
- import lwcVitePlugin from "@salesforce/vite-plugin-lwc-ui-bundle";
76
- import { viteSingleFile } from "vite-plugin-singlefile";
77
-
78
- export default defineConfig({
79
- build: {
80
- target: "esnext", // required for top-level await in bootstrap.js
81
- },
82
- plugins: [
83
- lwcVitePlugin({
84
- modules: {
85
- // Flat DX structure: specify { path, namespace } per directory
86
- // Use a plain string if your dir already has namespace sub-folders
87
- dirs: [{ path: "force-app/main/default/lwc", namespace: "c" }],
88
-
89
- // Include if your components use lightning/* base components
90
- npm: ["lightning-base-components"],
91
- },
92
- }),
93
- viteSingleFile(), // inlines everything into a single index.html
94
- ],
95
- });
96
- ```
97
-
98
- ### `index.html`
99
-
100
- ```html
101
- <!doctype html>
102
- <html lang="en">
103
- <head>
104
- <meta charset="UTF-8" />
105
- <title>My LWC App</title>
106
- </head>
107
- <body>
108
- <div id="app"></div>
109
- <script>
110
- globalThis.lwcRuntimeFlags = { DISABLE_SYNTHETIC_SHADOW: true };
111
- </script>
112
- <script type="module" src="/src/bootstrap.js"></script>
113
- </body>
114
- </html>
115
- ```
116
-
117
- ### `src/bootstrap.js`
118
-
119
- ```js
120
- import "@salesforce-ux/design-system/assets/styles/salesforce-lightning-design-system.css";
121
- import "@lwc/synthetic-shadow";
122
- import { createElement } from "lwc";
123
-
124
- // Import your root component using its LWC specifier: <namespace>/<componentName>
125
- import App from "c/helloApp";
126
-
127
- // Mount it — tag name is the kebab-case version of the specifier
128
- const el = createElement("c-hello-app", { is: App });
129
- document.getElementById("app").appendChild(el);
130
- ```
131
-
132
- ---
133
-
134
- ## Step 2: Install Dependencies
135
-
136
- ```bash
137
- npm install
138
- ```
139
-
140
- ---
141
-
142
- ## Step 3: Build
143
-
144
- ```bash
145
- npm run build
146
- ```
147
-
148
- Output: `dist/index.html` — a single file with all JS and CSS inlined.
149
-
150
- ---
151
-
152
- ## Step 4: Test
153
-
154
- **Option A — Open directly in browser:**
155
-
156
- ```bash
157
- open dist/index.html
158
- ```
159
-
160
- **Option B — Dev server with live reload:**
161
-
162
- ```bash
163
- npm run dev
164
- # Open http://localhost:5173
165
- ```
166
-
167
- **Option C — Preview the production build:**
168
-
169
- ```bash
170
- npm run preview
171
- # Open http://localhost:4173
172
- ```
173
-
174
- ---
175
-
176
- ## Component Namespace Convention
177
-
178
- The plugin supports two directory structures:
179
-
180
- ### Flat DX structure (standard Salesforce DX)
181
-
182
- ```
183
- force-app/main/default/lwc/
184
- helloApp/
185
- helloApp.js
186
- ```
187
-
188
- Use `{ path, namespace }` per directory to specify the namespace:
189
-
190
- ```js
191
- modules: {
192
- dirs: [{ path: "force-app/main/default/lwc", namespace: "c" }];
193
- }
194
- ```
195
-
196
- | Directory | LWC Specifier | HTML Tag |
197
- | ------------------- | ------------------ | ----------------- |
198
- | `lwc/helloApp/` | `'c/helloApp'` | `c-hello-app` |
199
- | `lwc/helloMessage/` | `'c/helloMessage'` | `c-hello-message` |
200
-
201
- ### Namespaced structure
202
-
203
- ```
204
- modules/
205
- records/
206
- accountOutputName/
207
- accountOutputName.js
208
- ```
209
-
210
- Omit `namespace` — the sub-directory name is used as the namespace:
211
-
212
- ```js
213
- modules: {
214
- dirs: ["modules"];
215
- }
216
- ```
217
-
218
- | Directory | LWC Specifier | HTML Tag |
219
- | ------------------------------------ | ----------------------------- | ----------------------------- |
220
- | `modules/records/accountOutputName/` | `'records/accountOutputName'` | `records-account-output-name` |
221
-
222
- ---
223
-
224
- ## Common Errors
225
-
226
- ### `Rollup failed to resolve import "lightning/button"`
227
-
228
- Your component uses a Lightning Base Component. Add to `vite.config.js`:
229
-
230
- ```js
231
- modules: {
232
- npm: ["lightning-base-components"];
233
- }
234
- ```
235
-
236
- ### `[scoped-module-providers] Unhandled import: @salesforce/label/...`
237
-
238
- Expected — the plugin returns the label key as display text by default.
239
- To provide real label values:
240
-
241
- ```js
242
- import { builtins } from "@salesforce/vite-plugin-lwc-ui-bundle";
243
-
244
- lwcVitePlugin({
245
- providers: [builtins.label({ "c.Save": "Save", "c.Cancel": "Cancel" })],
246
- });
247
- ```
248
-
249
- ### Component renders but looks unstyled
250
-
251
- Add SLDS import to `src/bootstrap.js`:
252
-
253
- ```js
254
- import "@salesforce-ux/design-system/assets/styles/salesforce-lightning-design-system.css";
255
- ```
256
-
257
- ### `[scoped-module-providers] Unhandled import: @salesforce/user/Id`
258
-
259
- Expected warning — `@salesforce/user/*` imports return `undefined` by default.
260
- To provide real values (e.g. current user ID), add the user provider:
261
-
262
- ```js
263
- import { builtins } from "@salesforce/vite-plugin-lwc-ui-bundle";
264
-
265
- lwcVitePlugin({
266
- providers: [
267
- builtins.user({ Id: "005xx000001X8AAAA" }), // optional: override user fields
268
- ],
269
- });
270
- ```
271
-
272
- ### `Top-level await is not available in the configured target environment`
273
-
274
- Your `bootstrap.js` uses top-level `await` (required when initialising the Data
275
- SDK). Add `target: 'esnext'` to the build config:
276
-
277
- ```js
278
- export default defineConfig({
279
- build: { target: "esnext" },
280
- plugins: [...],
281
- });
282
- ```
283
-
284
- ### `Rollup failed to resolve import "force/someModule"`
285
-
286
- Core-only module — add a stub:
287
-
288
- ```js
289
- // stubs/someModule.js
290
- export const someExport = () => {};
291
- ```
292
-
293
- ```js
294
- // vite.config.js
295
- lwcVitePlugin({ stubs: { "force/someModule": "./stubs/someModule.js" } });
296
- ```
297
-
298
- ---
299
-
300
- ## Connecting to a Real Salesforce Org (lwcProxy)
301
-
302
- By default the plugin runs fully off-platform with mock data. To connect your
303
- components to a real Salesforce org in local dev — enabling `lightning/graphql`
304
- to execute real queries — add `lwcProxy()` alongside `lwcVitePlugin()`.
305
-
306
- ### Prerequisites
307
-
308
- - Salesforce CLI installed and an org connected: `sf org display`
309
- - Additional packages: `@salesforce/sdk-data` and `@salesforce/ui-bundle`
310
-
311
- ```bash
312
- npm install @salesforce/sdk-data @salesforce/ui-bundle
313
- ```
314
-
315
- ### 1. Add lwcProxy to vite.config.js
316
-
317
- ```js
318
- import { defineConfig } from "vite";
319
- import lwcVitePlugin, { lwcProxy } from "@salesforce/vite-plugin-lwc-ui-bundle";
320
- import { viteSingleFile } from "vite-plugin-singlefile";
321
-
322
- export default defineConfig({
323
- build: {
324
- target: "esnext", // required for top-level await in bootstrap.js
325
- },
326
- plugins: [
327
- lwcProxy(), // auto-reads sf CLI default org credentials
328
- // lwcProxy({ orgAlias: "my-org", debug: true }) // explicit org + verbose logging
329
- lwcVitePlugin({
330
- modules: {
331
- dirs: [{ path: "force-app/main/default/lwc", namespace: "c" }],
332
- },
333
- }),
334
- viteSingleFile(),
335
- ],
336
- });
337
- ```
338
-
339
- ### 2. Initialise the Data SDK in bootstrap.js
340
-
341
- The SDK must be initialised before any component that uses `lightning/graphql`
342
- mounts. Use a top-level `await` import to guarantee ordering:
343
-
344
- ```js
345
- import "@salesforce-ux/design-system/assets/styles/salesforce-lightning-design-system.css";
346
- import "@lwc/synthetic-shadow";
347
- import { createElement } from "lwc";
348
-
349
- // Initialise the Data SDK — lwcProxy() handles /services/* on the Vite dev server
350
- import { createDataSDK } from "@salesforce/sdk-data";
351
- try {
352
- globalThis.__sfdc_sdk__ = await createDataSDK({
353
- uiBundle: { basePath: "/" },
354
- });
355
- } catch (_err) {
356
- globalThis.__sfdc_sdk__ = {};
357
- }
358
-
359
- import App from "c/helloApp";
360
- const el = createElement("c-hello-app", { is: App });
361
- document.getElementById("app").appendChild(el);
362
- ```
363
-
364
- ### 3. Run the dev server
365
-
366
- ```bash
367
- npm run dev
368
- # Terminal shows: [lwc-proxy] Connected to https://your-org.my.salesforce.com
369
- # Open http://localhost:5173
370
- ```
371
-
372
- Components using `lightning/graphql` will now execute real GraphQL queries
373
- against your org. The proxy intercepts `/services/*` calls inside the Vite dev
374
- server — no separate proxy process needed.
375
-
376
- > **Note:** The `basePath: '/'` setting works for `npm run dev` only. For
377
- > production builds (`dist/index.html`), a standalone proxy server is required.