@salesforce/angular-plugin-ui-bundle 12.3.13 → 12.4.1

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/dist/index.d.ts CHANGED
@@ -3,11 +3,11 @@
3
3
  * All rights reserved.
4
4
  * For full license text, see the LICENSE.txt file
5
5
  */
6
- export type { SalesforceOptions, Middleware } from "./types.ts";
6
+ export type { SalesforceOptions, Middleware, SiteOptions } from "./types.ts";
7
7
  export type { ApiVersionResult } from "./plugins/api-version.ts";
8
8
  export { createApiVersionPlugin } from "./plugins/api-version.ts";
9
9
  export { createGraphQLCodegenPlugin } from "./plugins/graphql-codegen.ts";
10
10
  export { DEFAULT_API_VERSION, DEFAULT_PORT, getPort } from "./utils.ts";
11
11
  export { createProxyMiddleware } from "./middleware/proxy.ts";
12
- export { createHtmlMiddleware } from "./middleware/html.ts";
12
+ export { createHtmlMiddleware, createSiteHtmlMiddleware } from "./middleware/html.ts";
13
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAChE,YAAY,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC7E,YAAY,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC"}
package/dist/index.js CHANGED
@@ -7,4 +7,4 @@ export { createApiVersionPlugin } from "./plugins/api-version.js";
7
7
  export { createGraphQLCodegenPlugin } from "./plugins/graphql-codegen.js";
8
8
  export { DEFAULT_API_VERSION, DEFAULT_PORT, getPort } from "./utils.js";
9
9
  export { createProxyMiddleware } from "./middleware/proxy.js";
10
- export { createHtmlMiddleware } from "./middleware/html.js";
10
+ export { createHtmlMiddleware, createSiteHtmlMiddleware } from "./middleware/html.js";
@@ -3,6 +3,58 @@
3
3
  * All rights reserved.
4
4
  * For full license text, see the LICENSE.txt file
5
5
  */
6
- import type { Middleware, SalesforceOptions } from "../types.ts";
7
- export declare function createHtmlMiddleware(options?: SalesforceOptions): Promise<Middleware>;
6
+ /**
7
+ * HTML injection middleware for ng serve.
8
+ * Intercepts SPA navigation responses and injects the Live Preview script,
9
+ * SFDC_ENV global, and base href. Skips static files and /services/ paths.
10
+ * In design mode (SF_DESIGN_MODE) it also injects the runtime <script> tag
11
+ * (proxy.ts serves that script).
12
+ *
13
+ * Runs BEFORE the proxy middleware (wrapping must happen first).
14
+ *
15
+ * Split into a pure createIndexHtmlTransformer (string→string, unit-testable)
16
+ * and createHtmlMiddleware (req/res plumbing). The transformer is not public API.
17
+ *
18
+ * The optional `hooks.resolveRequest` callback enables per-request basePath and
19
+ * language injection — used by the site middleware (`/site` subpath) for
20
+ * language-prefixed URL support.
21
+ */
22
+ import type { IncomingMessage } from "node:http";
23
+ import type { Middleware, SalesforceOptions, SiteOptions } from "../types.ts";
24
+ /**
25
+ * Per-request context returned by a `RequestResolver`. When provided, these
26
+ * values override the static basePath resolved at startup.
27
+ */
28
+ export interface RequestContext {
29
+ /** The basePath to inject into `<base href>` and `SFDC_ENV.basePath`. */
30
+ basePath: string;
31
+ /** The language to inject into `SFDC_ENV.language` (omitted when undefined). */
32
+ language?: string;
33
+ }
34
+ /**
35
+ * A per-request hook that resolves basePath and language from the incoming
36
+ * request. May also mutate `req.url` (e.g., strip a language segment).
37
+ */
38
+ export type RequestResolver = (req: IncomingMessage) => RequestContext;
39
+ export interface HtmlMiddlewareHooks {
40
+ resolveRequest?: RequestResolver;
41
+ }
42
+ export declare function createHtmlMiddleware(options?: SalesforceOptions, hooks?: HtmlMiddlewareHooks): Promise<Middleware>;
43
+ /**
44
+ * Site-aware HTML middleware for Angular apps with language-prefixed URLs.
45
+ *
46
+ * Thin wrapper around `createHtmlMiddleware` that supplies a per-request
47
+ * `resolveRequest` hook. On each request the hook:
48
+ *
49
+ * 1. Resolves the active language from the leading URL path segment.
50
+ * 2. Strips the language segment from `req.url` so Angular's SPA routing
51
+ * sees `/accounts/1` instead of `/fr/accounts/1`.
52
+ * 3. Returns `{ basePath, language }` so the HTML transform injects the
53
+ * correct per-request `SFDC_ENV.language` and `SFDC_ENV.basePath`.
54
+ *
55
+ * When languages are empty, the feature is absent (CodeBuilder), or in
56
+ * production mode, the middleware degrades to the generic `createHtmlMiddleware`
57
+ * (no hook supplied — identity behavior).
58
+ */
59
+ export declare function createSiteHtmlMiddleware(options?: SiteOptions): Promise<Middleware>;
8
60
  //# sourceMappingURL=html.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/middleware/html.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAkBH,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AA4FjE,wBAAsB,oBAAoB,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,UAAU,CAAC,CA+E/F"}
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/middleware/html.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAIjD,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI9E;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,yEAAyE;IACzE,QAAQ,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,eAAe,KAAK,cAAc,CAAC;AAEvE,MAAM,WAAW,mBAAmB;IACnC,cAAc,CAAC,EAAE,eAAe,CAAC;CACjC;AAiGD,wBAAsB,oBAAoB,CACzC,OAAO,GAAE,iBAAsB,EAC/B,KAAK,CAAC,EAAE,mBAAmB,GACzB,OAAO,CAAC,UAAU,CAAC,CAmFrB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,wBAAwB,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,CA4B7F"}
@@ -3,33 +3,23 @@
3
3
  * All rights reserved.
4
4
  * For full license text, see the LICENSE.txt file
5
5
  */
6
- /**
7
- * HTML injection middleware for ng serve.
8
- * Intercepts SPA navigation responses and injects the Live Preview script,
9
- * SFDC_ENV global, and base href. Skips static files and /services/ paths.
10
- * In design mode (SF_DESIGN_MODE) it also injects the runtime <script> tag
11
- * (proxy.ts serves that script).
12
- *
13
- * Runs BEFORE the proxy middleware (wrapping must happen first).
14
- *
15
- * Split into a pure createIndexHtmlTransformer (string→string, unit-testable)
16
- * and createHtmlMiddleware (req/res plumbing). The transformer is not public API.
17
- */
18
6
  import { getOrgInfo } from "@salesforce/ui-bundle/app";
19
7
  import { injectLivePreviewScript } from "@salesforce/ui-bundle/proxy";
20
8
  import { DESIGN_MODE_HYDRATE_PATH, DESIGN_MODE_SCRIPT_PATH } from "./proxy.js";
21
9
  import { getAppName, getCodeBuilderBasePath, getNamespace, getPort } from "../utils.js";
10
+ import { normalizeLanguages, resolveLanguageFromUrl } from "../utils.js";
22
11
  /**
23
- * Builds a pure HTML transform bound to the current env (basePath resolved once
24
- * from CODE_BUILDER_FRAMEWORK_PROXY_URI or "/"). Only the ng serve middleware
25
- * calls it, so it always injects.
12
+ * Builds a pure HTML transform. When `ctx` is provided (per-request overrides),
13
+ * its `basePath` and `language` are used; otherwise the static basePath resolved
14
+ * at startup is used. The transform injects Live Preview, <base href>,
15
+ * SFDC_ENV global, and (optionally) design-mode scripts.
26
16
  */
27
17
  async function createIndexHtmlTransformer(options = {}) {
28
18
  const port = getPort();
29
19
  const codeBuilderProxyUrl = process.env.CODE_BUILDER_FRAMEWORK_PROXY_URI;
30
20
  const isCodeBuilder = !!codeBuilderProxyUrl;
31
- const basePath = isCodeBuilder ? getCodeBuilderBasePath(codeBuilderProxyUrl, port) : "/";
32
- const apiPath = basePath;
21
+ const staticBasePath = isCodeBuilder ? getCodeBuilderBasePath(codeBuilderProxyUrl, port) : "/";
22
+ const staticApiPath = staticBasePath;
33
23
  // App identity for SFDC_ENV (namespace + appName). Resolved from the app root
34
24
  // (cwd — where ng serve runs), matching the server injector so local dev sees
35
25
  // the same values a deployed bundle would.
@@ -47,9 +37,12 @@ async function createIndexHtmlTransformer(options = {}) {
47
37
  orgUrl = undefined;
48
38
  }
49
39
  if (options.debug) {
50
- console.log(`[angular-plugin-ui-bundle] HTML transformer: basePath="${basePath}", isCodeBuilder=${isCodeBuilder}, port=${port}, orgUrl=${orgUrl}`);
40
+ console.log(`[angular-plugin-ui-bundle] HTML transformer: basePath="${staticBasePath}", isCodeBuilder=${isCodeBuilder}, port=${port}, orgUrl=${orgUrl}`);
51
41
  }
52
- return (html) => {
42
+ return (html, ctx) => {
43
+ // Resolve per-request basePath or fall back to static
44
+ const basePath = ctx?.basePath ?? staticBasePath;
45
+ const apiPath = staticApiPath;
53
46
  // 1. Live Preview script (enables VS Code extension communication)
54
47
  html = injectLivePreviewScript(html);
55
48
  // 2. <base href>: replace an existing tag, else inject after <head>.
@@ -61,13 +54,15 @@ async function createIndexHtmlTransformer(options = {}) {
61
54
  else if (/<head(\s[^>]*)?>/i.test(html)) {
62
55
  html = html.replace(/<head(\s[^>]*)?>/i, (match) => `${match}\n <base href="${baseHref}">`);
63
56
  }
64
- // 3. SFDC_ENV global (basePath + apiPath + orgUrl + app identity).
57
+ // 3. SFDC_ENV global (basePath + apiPath + orgUrl + app identity + language).
65
58
  // JSON.stringify escapes quotes/backslashes in every value (e.g. an
66
59
  // appName derived from a dir name), so no single field can break the
67
60
  // emitted SFDC_ENV object. It does NOT escape `<`, so we also escape it
68
61
  // to `<` to prevent a `</script>` in a value from closing this
69
62
  // inline <script> element early.
70
63
  const sfdcEnv = { basePath, apiPath };
64
+ if (ctx?.language)
65
+ sfdcEnv.language = ctx.language;
71
66
  if (orgUrl)
72
67
  sfdcEnv.orgUrl = orgUrl;
73
68
  if (namespace)
@@ -96,9 +91,12 @@ async function createIndexHtmlTransformer(options = {}) {
96
91
  return html;
97
92
  };
98
93
  }
99
- export async function createHtmlMiddleware(options = {}) {
94
+ export async function createHtmlMiddleware(options = {}, hooks) {
100
95
  const htmlTransformer = await createIndexHtmlTransformer(options);
101
96
  return (req, res, next) => {
97
+ // Call the resolver hook BEFORE checking for static files — the hook may
98
+ // mutate req.url (e.g., strip a language segment).
99
+ const ctx = hooks?.resolveRequest?.(req);
102
100
  // Skip static files (.js, .css, .ico) and internal paths
103
101
  const url = req.url?.split("?")[0] ?? "/";
104
102
  const hasExtension = url.lastIndexOf(".") > url.lastIndexOf("/");
@@ -132,7 +130,7 @@ export async function createHtmlMiddleware(options = {}) {
132
130
  }
133
131
  const html = body.toString("utf8");
134
132
  try {
135
- const transformedResult = htmlTransformer(html);
133
+ const transformedResult = htmlTransformer(html, ctx);
136
134
  Promise.resolve(transformedResult)
137
135
  .then((finalHtml) => {
138
136
  if (options.debug) {
@@ -168,3 +166,43 @@ export async function createHtmlMiddleware(options = {}) {
168
166
  next();
169
167
  };
170
168
  }
169
+ /**
170
+ * Site-aware HTML middleware for Angular apps with language-prefixed URLs.
171
+ *
172
+ * Thin wrapper around `createHtmlMiddleware` that supplies a per-request
173
+ * `resolveRequest` hook. On each request the hook:
174
+ *
175
+ * 1. Resolves the active language from the leading URL path segment.
176
+ * 2. Strips the language segment from `req.url` so Angular's SPA routing
177
+ * sees `/accounts/1` instead of `/fr/accounts/1`.
178
+ * 3. Returns `{ basePath, language }` so the HTML transform injects the
179
+ * correct per-request `SFDC_ENV.language` and `SFDC_ENV.basePath`.
180
+ *
181
+ * When languages are empty, the feature is absent (CodeBuilder), or in
182
+ * production mode, the middleware degrades to the generic `createHtmlMiddleware`
183
+ * (no hook supplied — identity behavior).
184
+ */
185
+ export async function createSiteHtmlMiddleware(options = {}) {
186
+ const supported = normalizeLanguages(options.languages ?? []);
187
+ const defaultLang = options.defaultLanguage
188
+ ? normalizeLanguages([options.defaultLanguage])[0]
189
+ : supported[0];
190
+ const isCodeBuilder = !!process.env.CODE_BUILDER_FRAMEWORK_PROXY_URI;
191
+ const siteEnabled = supported.length > 0 && !isCodeBuilder && !!defaultLang;
192
+ const resolveRequest = siteEnabled
193
+ ? (req) => {
194
+ const rawUrl = req.url ?? "/";
195
+ const resolved = resolveLanguageFromUrl(rawUrl, supported, defaultLang);
196
+ if (resolved?.segment) {
197
+ // Non-default language: strip the segment from req.url so
198
+ // Angular's SPA routing sees the path without it.
199
+ const rest = rawUrl.slice(`/${resolved.segment}`.length);
200
+ req.url = rest.startsWith("/") ? rest : `/${rest}`;
201
+ return { basePath: `/${resolved.segment}`, language: resolved.language };
202
+ }
203
+ // Default language or no match: un-prefixed
204
+ return { basePath: "/", language: resolved?.language };
205
+ }
206
+ : undefined;
207
+ return createHtmlMiddleware(options, resolveRequest ? { resolveRequest } : undefined);
208
+ }
package/dist/types.d.ts CHANGED
@@ -9,4 +9,17 @@ export interface SalesforceOptions {
9
9
  debug?: boolean;
10
10
  }
11
11
  export type Middleware = (req: IncomingMessage, res: ServerResponse, next: (err?: unknown) => void) => void;
12
+ export interface SiteOptions extends SalesforceOptions {
13
+ /**
14
+ * Supported language codes (e.g. `["en_US", "fr", "es"]`).
15
+ * Codes are normalized from underscores to hyphens for URL use.
16
+ * When empty or omitted, site features are disabled.
17
+ */
18
+ languages?: string[];
19
+ /**
20
+ * The default language code (served un-prefixed at `/`).
21
+ * When omitted, the first entry in `languages` is used.
22
+ */
23
+ defaultLanguage?: string;
24
+ }
12
25
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjE,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,MAAM,UAAU,GAAG,CACxB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,KAAK,IAAI,KACzB,IAAI,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjE,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,MAAM,UAAU,GAAG,CACxB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,KAAK,IAAI,KACzB,IAAI,CAAC;AAEV,MAAM,WAAW,WAAY,SAAQ,iBAAiB;IACrD;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB"}
package/dist/utils.d.ts CHANGED
@@ -30,4 +30,34 @@ export declare function getAppName(rootPath: string): string;
30
30
  * @returns The registered namespace, or "c" when none is found
31
31
  */
32
32
  export declare function getNamespace(startDir: string): string;
33
+ /**
34
+ * Resolve the active language for a request, in hyphenated URL form.
35
+ *
36
+ * The default language (the FIRST supported entry) is served un-prefixed, so:
37
+ * - a leading URL segment matching a NON-DEFAULT supported language -> that
38
+ * language (it carries a "/<lang>" segment);
39
+ * - no segment, or a segment equal to the default -> the default language.
40
+ *
41
+ * Returns `undefined` only when there are no supported languages (feature
42
+ * absent), so `SFDC_ENV.language` is otherwise ALWAYS set — matching production,
43
+ * which sets it even for the default at the root.
44
+ *
45
+ * Detection is anchored to the exact supported set, so a real route segment
46
+ * (e.g. a page named like a locale) is never mistaken for a language.
47
+ *
48
+ * @param url The request URL (may include a query string) or `undefined`.
49
+ * @param supported Supported languages in hyphenated form.
50
+ * @param defaultLanguage The default language code (served un-prefixed at `/`).
51
+ * @returns The resolved `language` and, when present, the leading `segment`.
52
+ */
53
+ export declare function resolveLanguageFromUrl(url: string | undefined, supported: string[], defaultLanguage: string): {
54
+ language: string;
55
+ segment?: string;
56
+ } | undefined;
57
+ /**
58
+ * Normalize the feature's `LANGUAGES` codes to hyphenated URL form
59
+ * (`en_US` -> `en-US`), dropping blanks. This mirrors how the switcher's client
60
+ * code (`buildLanguageUrl`) and the platform both use hyphens in URL segments.
61
+ */
62
+ export declare function normalizeLanguages(languages: string[]): string[];
33
63
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,eAAO,MAAM,mBAAmB,SAAS,CAAC;AAE1C,eAAO,MAAM,YAAY,OAAO,CAAC;AAEjC,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAErC,wBAAgB,OAAO,IAAI,MAAM,CAEhC;AAED,qFAAqF;AACrF,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAQ7E;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAYnD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAwBrD"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,eAAO,MAAM,mBAAmB,SAAS,CAAC;AAE1C,eAAO,MAAM,YAAY,OAAO,CAAC;AAEjC,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAErC,wBAAgB,OAAO,IAAI,MAAM,CAEhC;AAED,qFAAqF;AACrF,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAQ7E;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAYnD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAwBrD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,sBAAsB,CACrC,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,SAAS,EAAE,MAAM,EAAE,EACnB,eAAe,EAAE,MAAM,GACrB;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CASpD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAEhE"}
package/dist/utils.js CHANGED
@@ -81,3 +81,42 @@ export function getNamespace(startDir) {
81
81
  }
82
82
  return DEFAULT_NAMESPACE;
83
83
  }
84
+ /**
85
+ * Resolve the active language for a request, in hyphenated URL form.
86
+ *
87
+ * The default language (the FIRST supported entry) is served un-prefixed, so:
88
+ * - a leading URL segment matching a NON-DEFAULT supported language -> that
89
+ * language (it carries a "/<lang>" segment);
90
+ * - no segment, or a segment equal to the default -> the default language.
91
+ *
92
+ * Returns `undefined` only when there are no supported languages (feature
93
+ * absent), so `SFDC_ENV.language` is otherwise ALWAYS set — matching production,
94
+ * which sets it even for the default at the root.
95
+ *
96
+ * Detection is anchored to the exact supported set, so a real route segment
97
+ * (e.g. a page named like a locale) is never mistaken for a language.
98
+ *
99
+ * @param url The request URL (may include a query string) or `undefined`.
100
+ * @param supported Supported languages in hyphenated form.
101
+ * @param defaultLanguage The default language code (served un-prefixed at `/`).
102
+ * @returns The resolved `language` and, when present, the leading `segment`.
103
+ */
104
+ export function resolveLanguageFromUrl(url, supported, defaultLanguage) {
105
+ if (supported.length === 0)
106
+ return undefined;
107
+ const nonDefault = supported.filter((lang) => lang !== defaultLanguage);
108
+ const path = (url ?? "").split("?")[0] ?? "";
109
+ const [first] = path.replace(/^\/+/, "").split("/");
110
+ if (first && nonDefault.includes(first)) {
111
+ return { language: first, segment: first };
112
+ }
113
+ return { language: defaultLanguage }; // no segment (or the default): un-prefixed
114
+ }
115
+ /**
116
+ * Normalize the feature's `LANGUAGES` codes to hyphenated URL form
117
+ * (`en_US` -> `en-US`), dropping blanks. This mirrors how the switcher's client
118
+ * code (`buildLanguageUrl`) and the platform both use hyphens in URL segments.
119
+ */
120
+ export function normalizeLanguages(languages) {
121
+ return languages.map((s) => s.trim().replace(/_/g, "-")).filter(Boolean);
122
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@salesforce/angular-plugin-ui-bundle",
3
3
  "description": "Angular CLI plugin for Salesforce UI Bundles",
4
- "version": "12.3.13",
4
+ "version": "12.4.1",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
@@ -29,8 +29,8 @@
29
29
  "test:coverage": "vitest run --coverage"
30
30
  },
31
31
  "dependencies": {
32
- "@salesforce/ui-bundle": "^12.3.13",
33
- "@salesforce/ui-design-mode": "^12.3.13",
32
+ "@salesforce/ui-bundle": "^12.4.1",
33
+ "@salesforce/ui-design-mode": "^12.4.1",
34
34
  "chokidar": "^4.0.0"
35
35
  },
36
36
  "devDependencies": {