@lwrjs/lwc-ssr 0.17.2-alpha.9 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,6 +28,7 @@ __export(exports, {
28
28
  });
29
29
  var import_diagnostics = __toModule(require("@lwrjs/diagnostics"));
30
30
  var import_instrumentation = __toModule(require("@lwrjs/instrumentation"));
31
+ var import_shared_utils = __toModule(require("@lwrjs/shared-utils"));
31
32
  var import_utils = __toModule(require("../utils.cjs"));
32
33
  var import_renderer = __toModule(require("../renderer.cjs"));
33
34
  var NAME = "preload-data-transformer";
@@ -71,7 +72,7 @@ function preloadDataViewTransformer(_options, {config, moduleBundler, resourceRe
71
72
  }
72
73
  const {props, markup, cache: {ttl} = {ttl: void 0}} = result || {};
73
74
  import_diagnostics.logger.verbose({label: NAME, message: "response", additionalInfo: props});
74
- markup && (0, import_utils.addHeadMarkup)([result], stringBuilder);
75
+ markup && (0, import_shared_utils.addHeadMarkup)([result.markup], stringBuilder);
75
76
  metadata.serverData = metadata.serverData || {};
76
77
  Object.assign(metadata.serverData, props);
77
78
  metadata.serverBundles = bundles ? new Set([...allBundles, ...bundles]) : allBundles;
@@ -30,13 +30,12 @@ var import_undici = __toModule(require("undici"));
30
30
  var import_diagnostics = __toModule(require("@lwrjs/diagnostics"));
31
31
  var import_instrumentation = __toModule(require("@lwrjs/instrumentation"));
32
32
  var import_shared_utils = __toModule(require("@lwrjs/shared-utils"));
33
- var ROUTE_CORE_HEADER = "X-SFDC-Route-Core";
34
33
  var CORE_CLIENTS = new Map();
35
34
  var FetchController = class {
36
35
  constructor(context) {
37
36
  this.controlledFetch = (request, init) => {
38
37
  if (this.killSwitchActivated) {
39
- return this.handleAbortError(request, void 0);
38
+ return this.handleAbortError(request, "Kill switch was already enabled");
40
39
  }
41
40
  if (!this.abortController) {
42
41
  this.abortController = new AbortController();
@@ -49,7 +48,8 @@ var FetchController = class {
49
48
  };
50
49
  const fetchFunction = this.noOpActivated ? this.fetchNoOp(request, updatedInit) : this.fetchEndowment(request, updatedInit);
51
50
  const fetchPromise = fetchFunction.catch((error) => {
52
- if (error && error?.stack.startsWith("AbortError")) {
51
+ const errorMsg = error.message || error;
52
+ if (error && (errorMsg.startsWith("AbortError") || error?.stack.startsWith("AbortError"))) {
53
53
  return this.handleAbortError(request, error);
54
54
  } else {
55
55
  throw error;
@@ -59,7 +59,7 @@ var FetchController = class {
59
59
  };
60
60
  this.activateKillSwitch = () => {
61
61
  this.killSwitchActivated = true;
62
- this.abortController?.abort();
62
+ this.abortController?.abort("AbortError: Kill switch enabled");
63
63
  this.abortController = void 0;
64
64
  };
65
65
  this.deactivateKillSwitch = () => {
@@ -72,10 +72,9 @@ var FetchController = class {
72
72
  this.noOpActivated = false;
73
73
  };
74
74
  this.setFetchRequestContext = (context) => {
75
- const {abortController, host, headers, requestDepth, coreProxy} = context;
75
+ const {abortController, host, headers, coreProxy} = context;
76
76
  this.host = host;
77
77
  this.headers = headers;
78
- this.requestDepth = requestDepth;
79
78
  this.coreProxy = coreProxy;
80
79
  this.abortController = abortController;
81
80
  };
@@ -85,7 +84,7 @@ var FetchController = class {
85
84
  this.fetchEndowment = this.createFetchEndowment();
86
85
  }
87
86
  handleAbortError(request, error) {
88
- const message = `Orphaned ${String(request)} request was killed. Either the request timed out or it was dispatched during SSR. Async processes are not supported during SSR. For more information, see: https://developer.salesforce.com/docs/platform/lwr/guide/lwr-configure-component-ssr.html.`;
87
+ const message = `${String(request)} request was killed. Either the request timed out or it was dispatched during SSR. Async processes are not supported during SSR. For more information, see: https://developer.salesforce.com/docs/platform/lwr/guide/lwr-configure-component-ssr.html.`;
89
88
  import_diagnostics.logger.warn({label: `Server-side Rendering`, message}, error);
90
89
  return Promise.resolve(new Response(message, {status: 500}));
91
90
  }
@@ -94,30 +93,23 @@ var FetchController = class {
94
93
  if (!init?.signal) {
95
94
  resolve(this.handleAbortError(request, new Error("RequestInit was not setup as expected")));
96
95
  } else if (init.signal.aborted) {
97
- resolve(this.handleAbortError(request, new Error("Request was aborted")));
96
+ resolve(this.handleAbortError(request, new Error("Request was already aborted")));
98
97
  } else {
99
98
  const abortHandler = (err) => {
100
99
  init?.signal?.removeEventListener("abort", abortHandler);
101
- resolve(this.handleAbortError(request, err));
100
+ resolve(this.handleAbortError(request, new Error("Not allowed: Request was dispatched during SSR", {cause: err})));
102
101
  };
103
102
  init.signal.addEventListener("abort", abortHandler);
104
103
  }
105
104
  });
106
105
  }
107
106
  createFetchEndowment() {
108
- return (request, init) => {
109
- const {host: forwardedOrigin = "", requestDepth = 1, coreProxy} = this;
107
+ return (request, init = {}) => {
108
+ const {host: forwardedOrigin = "", coreProxy} = this;
110
109
  const origin = coreProxy?.origin && coreProxy.origin.startsWith("http") ? coreProxy.origin : forwardedOrigin;
111
110
  const {finalRequest, finalUrl} = this.getFinalRequest(request, origin);
112
- const finalInit = {
113
- ...init,
114
- headers: {
115
- ...init?.headers,
116
- [import_shared_utils.REQUEST_DEPTH_HEADER]: String(requestDepth)
117
- }
118
- };
119
111
  if (coreProxy || forwardedOrigin && finalUrl.startsWith(forwardedOrigin)) {
120
- finalInit.headers[ROUTE_CORE_HEADER] = "true";
112
+ init.headers[import_shared_utils.ROUTE_CORE_HEADER] = "true";
121
113
  }
122
114
  const proxyStr = coreProxy ? JSON.stringify(coreProxy) : "none";
123
115
  const hasCookies = this.headers && this.headers.Cookie ? "yes" : "no";
@@ -140,15 +132,25 @@ var FetchController = class {
140
132
  span.setAttributes({statusCode: res.status});
141
133
  return res;
142
134
  };
135
+ const addErrorToSpan = (err) => {
136
+ span.setAttributes({error: (0, import_diagnostics.stringifyError)(err)});
137
+ };
138
+ init.headers = {...init.headers, ...(0, import_shared_utils.getTraceHeaders)({}, span)};
143
139
  if (coreProxy) {
144
- return this.fetchWithAgent(finalUrl, finalInit, forwardedOrigin, coreProxy, span).then((res) => addInfoToSpan(res)).catch((err) => {
140
+ return this.fetchWithAgent(finalUrl, init, forwardedOrigin, coreProxy, span).then((res) => addInfoToSpan(res)).catch((err) => {
145
141
  const {finalRequest: cdnRequest, finalUrl: cdnUrl} = this.getFinalRequest(request, forwardedOrigin);
146
142
  import_diagnostics.logger.warn(`Fetching data directly from Core failed, retrying through CDN: ${cdnUrl} Error is: ${err.message || err}`);
147
143
  span.setAttributes({fetchType: "cdnFallback"});
148
- return fetch(cdnRequest, finalInit).then((res) => addInfoToSpan(res));
144
+ return fetch(cdnRequest, init).then((res) => addInfoToSpan(res)).catch((e) => {
145
+ addErrorToSpan(e);
146
+ throw e;
147
+ });
149
148
  });
150
149
  }
151
- return fetch(finalRequest, finalInit).then((res) => addInfoToSpan(res));
150
+ return fetch(finalRequest, init).then((res) => addInfoToSpan(res)).catch((e) => {
151
+ addErrorToSpan(e);
152
+ throw e;
153
+ });
152
154
  });
153
155
  };
154
156
  }
@@ -41,7 +41,7 @@ function createModuleLoader(config, resourceRegistry, bundleRegistry, runtimeEnv
41
41
  }
42
42
  async function createAMDModuleLoader(config, resourceRegistry, bundleRegistry, runtimeEnvironment, runtimeParams, serverData, bootstrapConfig, abortController) {
43
43
  const loaderConfig = (0, import_utils.getLoaderConfig)(BOOTSTRAP_SPECIFIER, config, runtimeParams, serverData);
44
- const {context, controller} = createContext(loaderConfig, runtimeParams, runtimeEnvironment, abortController);
44
+ const {context, controller} = createContext(loaderConfig, runtimeParams, abortController);
45
45
  const contextKeyMap = new Map(Object.keys(context).map((key) => [key, true]));
46
46
  let run;
47
47
  context.LWR.customInit = async (lwr) => {
@@ -218,10 +218,9 @@ function isValidResolveResponse(res) {
218
218
  async function createESMModuleLoader() {
219
219
  throw new Error("ESM support coming soon.");
220
220
  }
221
- function createContext(LWR, runtimeParams, runtimeEnvironment, abortController) {
221
+ function createContext(LWR, runtimeParams, abortController) {
222
222
  const fetchController = new import_fetchController.FetchController({
223
223
  host: runtimeParams.host,
224
- requestDepth: runtimeParams.requestDepth,
225
224
  coreProxy: runtimeParams.coreProxy,
226
225
  abortController
227
226
  });
@@ -67,8 +67,9 @@ var Renderer = class {
67
67
  const abortController = new AbortController();
68
68
  const timeout = new Promise((_, reject) => {
69
69
  timerId = setTimeout(() => {
70
- abortController.abort();
71
- reject(new import_diagnostics.LwrApplicationError(import_diagnostics.descriptions.APPLICATION.SSR_TIMEOUT(route.id, (0, import_utils.getRenderTimeout)())));
70
+ const message = import_diagnostics.descriptions.APPLICATION.SSR_TIMEOUT(route.id, (0, import_utils.getRenderTimeout)());
71
+ abortController.abort(`AbortError: ${message}`);
72
+ reject(new import_diagnostics.LwrApplicationError(message));
72
73
  }, (0, import_utils.getRenderTimeout)());
73
74
  });
74
75
  result = await this.pendingRenders.execute(async () => {
@@ -229,13 +230,13 @@ var Renderer = class {
229
230
  }
230
231
  resetFetchController(fetchController, runtimeParams, route, abortController) {
231
232
  fetchController.disableFetchKillSwitch();
233
+ const baseHeaders = {
234
+ [import_shared_utils.REQUEST_DEPTH_HEADER]: runtimeParams.requestDepth ?? "0",
235
+ ...(0, import_shared_utils.getTraceHeaders)(runtimeParams)
236
+ };
232
237
  fetchController.setFetchRequestContext({
233
238
  host: runtimeParams.host,
234
- requestDepth: runtimeParams.requestDepth,
235
- headers: route.bootstrap.includeCookiesForSSR ? {
236
- Cookie: runtimeParams.cookie,
237
- "True-Client-IP": runtimeParams.trueClientIP
238
- } : {"True-Client-IP": runtimeParams.trueClientIP},
239
+ headers: route.bootstrap.includeCookiesForSSR ? {Cookie: runtimeParams.cookie, ...baseHeaders} : baseHeaders,
239
240
  coreProxy: runtimeParams.coreProxy,
240
241
  abortController
241
242
  });
@@ -267,14 +268,13 @@ function getServerData(component, context, serverData) {
267
268
  return (0, import_instrumentation.getTracer)().trace({
268
269
  name: import_instrumentation.ViewSpan.GetServerData,
269
270
  attributes: {specifier: component.specifier}
270
- }, async () => {
271
+ }, async (span) => {
271
272
  try {
272
273
  const data = await component.module.getServerData(context);
273
274
  Object.assign(serverData, data.props);
274
- const dataRes = data;
275
- if (dataRes.status) {
276
- const {code, location} = dataRes.status;
277
- throw new import_diagnostics.LwrStatusError(void 0, code, location ? {location} : void 0);
275
+ if (data.warnings) {
276
+ const warnings = data.warnings.map((w) => (0, import_diagnostics.stringifyError)(w)).join(", ");
277
+ span.setAttributes({warnings});
278
278
  }
279
279
  return data;
280
280
  } catch (e) {
@@ -289,14 +289,15 @@ function getServerData(component, context, serverData) {
289
289
  });
290
290
  }
291
291
  async function renderToString(engine, component, props) {
292
+ const isSSRv2 = (0, import_shared_utils.getFeatureFlags)().SSR_COMPILER_ENABLED || false;
292
293
  return (0, import_instrumentation.getTracer)().trace({
293
294
  name: import_instrumentation.ViewSpan.RenderComponent,
294
- attributes: {specifier: component.specifier}
295
+ attributes: {specifier: component.specifier, isSSRv2}
295
296
  }, async () => {
296
297
  try {
297
298
  const elementName = (0, import_shared_utils.moduleSpecifierToKebabCase)(component.specifier);
298
299
  const moduleDefault = component.module.default;
299
- const html = (0, import_shared_utils.getFeatureFlags)().SSR_COMPILER_ENABLED ? await engine.renderComponent(elementName, moduleDefault, props, "sync") : engine.renderComponent(elementName, moduleDefault, props);
300
+ const html = isSSRv2 ? await engine.renderComponent(elementName, moduleDefault, props, elementName, false, "sync") : engine.renderComponent(elementName, moduleDefault, props);
300
301
  return {html};
301
302
  } catch (e) {
302
303
  if (e instanceof import_diagnostics.LwrError)
@@ -0,0 +1,94 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __markAsModule = (target) => __defProp(target, "__esModule", {value: true});
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, {get: all[name], enumerable: true});
11
+ };
12
+ var __exportStar = (target, module2, desc) => {
13
+ if (module2 && typeof module2 === "object" || typeof module2 === "function") {
14
+ for (let key of __getOwnPropNames(module2))
15
+ if (!__hasOwnProp.call(target, key) && key !== "default")
16
+ __defProp(target, key, {get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable});
17
+ }
18
+ return target;
19
+ };
20
+ var __toModule = (module2) => {
21
+ return __exportStar(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? {get: () => module2.default, enumerable: true} : {value: module2, enumerable: true})), module2);
22
+ };
23
+
24
+ // packages/@lwrjs/lwc-ssr/src/resourceProvider.ts
25
+ __markAsModule(exports);
26
+ __export(exports, {
27
+ default: () => resourceProvider_default
28
+ });
29
+ var import_path = __toModule(require("path"));
30
+ var import_rollup = __toModule(require("rollup"));
31
+ var import_plugin_node_resolve = __toModule(require("@rollup/plugin-node-resolve"));
32
+ var import_plugin_terser = __toModule(require("@rollup/plugin-terser"));
33
+ var import_config = __toModule(require("@lwrjs/config"));
34
+ var import_instrumentation = __toModule(require("@lwrjs/instrumentation"));
35
+ var import_package = __toModule(require("@lwrjs/lwc-ssr/package"));
36
+ var import_shared_utils = __toModule(require("@lwrjs/shared-utils"));
37
+ var banner = `/**
38
+ * Copyright (c) 2025, salesforce.com, inc.
39
+ * All rights reserved.
40
+ * SPDX-License-Identifier: MIT
41
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
42
+ */
43
+ /* LWC SSR Client Utils v${import_config.LWC_VERSION} */`;
44
+ var SsrResourceProvider = class {
45
+ constructor(_config, context) {
46
+ this.name = "lwc-ssr-client-utils";
47
+ this.ssrUtilsName = "register-lwc-style.js";
48
+ this.versionCache = new Map();
49
+ this.context = context;
50
+ }
51
+ async getResource(resource, environment) {
52
+ const {specifier, version = import_config.LWC_VERSION} = resource;
53
+ if (specifier !== this.ssrUtilsName || version !== import_config.LWC_VERSION) {
54
+ return;
55
+ }
56
+ const cacheKey = `${specifier}_${version}`;
57
+ if (this.versionCache.has(cacheKey)) {
58
+ return this.versionCache.get(cacheKey);
59
+ }
60
+ const {output} = await (0, import_instrumentation.getTracer)().trace({
61
+ name: import_instrumentation.BundleSpan.Rollup,
62
+ attributes: {specifier}
63
+ }, async () => {
64
+ const bundler = await (0, import_rollup.rollup)({
65
+ input: import_path.default.join(import_package.rootPath, "build", "scripts", specifier),
66
+ plugins: [
67
+ (0, import_plugin_node_resolve.nodeResolve)({
68
+ extensions: [".js"],
69
+ modulesOnly: true,
70
+ modulePaths: ["node_modules/@lwc"]
71
+ }),
72
+ (0, import_plugin_terser.default)({
73
+ output: {
74
+ comments: (_, comment) => comment.value.includes("LWC SSR Client Utils")
75
+ }
76
+ })
77
+ ]
78
+ });
79
+ return bundler.generate({format: "iife", banner});
80
+ });
81
+ const resourceDef = {
82
+ specifier,
83
+ version,
84
+ type: "application/javascript",
85
+ inline: true,
86
+ src: await this.context.resourceRegistry.resolveResourceUri({specifier, version}, environment),
87
+ content: output[0].code,
88
+ integrity: (0, import_shared_utils.createIntegrityHash)(output[0].code)
89
+ };
90
+ this.versionCache.set(cacheKey, resourceDef);
91
+ return resourceDef;
92
+ }
93
+ };
94
+ var resourceProvider_default = SsrResourceProvider;
@@ -25,8 +25,6 @@ var __toModule = (module2) => {
25
25
  __markAsModule(exports);
26
26
  __export(exports, {
27
27
  SSR_PROPS_ATTR: () => SSR_PROPS_ATTR,
28
- addHeadMarkup: () => addHeadMarkup,
29
- createHeadMarkup: () => createHeadMarkup,
30
28
  getLoaderConfig: () => getLoaderConfig,
31
29
  getLoaderId: () => getLoaderId,
32
30
  getLoaderShim: () => getLoaderShim,
@@ -35,7 +33,6 @@ __export(exports, {
35
33
  getServerBootstrapServices: () => getServerBootstrapServices,
36
34
  mergeWarnings: () => mergeWarnings
37
35
  });
38
- var import_diagnostics = __toModule(require("@lwrjs/diagnostics"));
39
36
  var import_shared_utils = __toModule(require("@lwrjs/shared-utils"));
40
37
  var DEFAULT_SSR_TIMEOUT = 5e3;
41
38
  var SSR_PROPS_ATTR = "data-lwr-props-id";
@@ -120,52 +117,3 @@ function mergeWarnings(metadata, warnings = []) {
120
117
  metadata.serverDebug.warnings = metadata.serverDebug.warnings || [];
121
118
  metadata.serverDebug.warnings.push(...warnings);
122
119
  }
123
- function createMetaTags(meta) {
124
- return meta.reduce((metaStr, {name, content, httpEquiv}) => {
125
- if (!name && !content && !httpEquiv)
126
- return metaStr;
127
- const nameStr = name ? ` name="${name}"` : "", httpEquivStr = httpEquiv ? ` http-equiv="${httpEquiv}"` : "", contentStr = content ? ` content="${content}"` : "";
128
- return metaStr + `<meta${nameStr}${httpEquivStr}${contentStr}>
129
- `;
130
- }, "");
131
- }
132
- function createScriptTags(scripts) {
133
- return scripts.reduce((scriptStr, {body}) => scriptStr + `<script type="application/ld+json">${body}</script>
134
- `, "");
135
- }
136
- function createLinkTags(links) {
137
- return links.reduce((linkStr, {href, rel, as, fetchpriority}) => {
138
- const relStr = rel ? ` rel="${rel}"` : "", asStr = as ? ` as="${as}"` : "", fetchStr = fetchpriority ? ` fetchpriority="${fetchpriority}"` : "";
139
- return linkStr + `<link href="${href}"${relStr}${asStr}${fetchStr}>
140
- `;
141
- }, "");
142
- }
143
- function createStyleTags(styles) {
144
- return styles.reduce((styleStr, {body, id}) => {
145
- const idStr = id ? ` id="${id}"` : "";
146
- return styleStr + `<style type="text/css"${idStr}>${body}</style>
147
- `;
148
- }, "");
149
- }
150
- function createHeadMarkup(results) {
151
- let hasTitle = false;
152
- return results.reduce((str, {markup: {title, scripts = [], meta = [], links = [], styles = []} = {}}) => {
153
- if (title && !hasTitle) {
154
- hasTitle = true;
155
- str += `<title>${title}</title>
156
- `;
157
- }
158
- return str + createMetaTags(meta) + createScriptTags(scripts) + createLinkTags(links) + createStyleTags(styles);
159
- }, "");
160
- }
161
- function addHeadMarkup(results, stringBuilder) {
162
- const headMarkup = createHeadMarkup(Object.values(results));
163
- if (headMarkup) {
164
- const headIndex = stringBuilder.original.indexOf("</head>");
165
- if (headIndex >= 0) {
166
- stringBuilder.prependLeft(headIndex, headMarkup);
167
- } else {
168
- import_diagnostics.logger.error("Adding markup during server-side rendering failed. Could not find the </head> tag.");
169
- }
170
- }
171
- }
@@ -30,7 +30,6 @@ var import_base_view_provider = __toModule(require("@lwrjs/base-view-provider"))
30
30
  var import_diagnostics = __toModule(require("@lwrjs/diagnostics"));
31
31
  var import_instrumentation = __toModule(require("@lwrjs/instrumentation"));
32
32
  var import_shared_utils = __toModule(require("@lwrjs/shared-utils"));
33
- var import_utils = __toModule(require("../utils.cjs"));
34
33
  var import_renderer = __toModule(require("../renderer.cjs"));
35
34
  var LwcViewProvider = class extends import_base_view_provider.default {
36
35
  constructor(_pluginConfig, providerConfig) {
@@ -77,7 +76,7 @@ var LwcViewProvider = class extends import_base_view_provider.default {
77
76
  throw new Error("Failed to render content template component");
78
77
  }
79
78
  if (markup)
80
- viewProperties.serverHeadMarkup = (0, import_utils.createHeadMarkup)([results[specifier]]);
79
+ viewProperties.serverHeadMarkup = (0, import_shared_utils.createHeadMarkup)([markup]);
81
80
  return {
82
81
  renderedView: html.replace(`<${element}`, `<${element} lwc:external`),
83
82
  metadata: {
@@ -85,7 +85,7 @@ function lwcSsrViewTransformer(options, {config, moduleBundler, resourceRegistry
85
85
  }
86
86
  (0, import_utils.mergeWarnings)(metadata, warnings);
87
87
  metadata.serverBundles = islandBundles ? new Set([...allBundles, ...islandBundles]) : allBundles;
88
- results && (0, import_utils.addHeadMarkup)(Object.values(results), stringBuilder);
88
+ results && (0, import_shared_utils.addHeadMarkup)(Object.values(results).map((r) => r.markup), stringBuilder);
89
89
  } catch (e) {
90
90
  if (e instanceof import_diagnostics.LwrError)
91
91
  throw e;
@@ -1,6 +1,7 @@
1
1
  import { descriptions, logger, LwrStatusError, stringifyError } from '@lwrjs/diagnostics';
2
2
  import { ViewSpan, getTracer } from '@lwrjs/instrumentation';
3
- import { addHeadMarkup, mergeWarnings } from '../utils.js';
3
+ import { addHeadMarkup } from '@lwrjs/shared-utils';
4
+ import { mergeWarnings } from '../utils.js';
4
5
  import { getRenderer } from '../renderer.js';
5
6
  const NAME = 'preload-data-transformer';
6
7
  /**
@@ -56,7 +57,7 @@ export default function preloadDataViewTransformer(_options, { config, moduleBun
56
57
  // Log and process the data response
57
58
  const { props, markup, cache: { ttl } = { ttl: undefined } } = result || {};
58
59
  logger.verbose({ label: NAME, message: 'response', additionalInfo: props });
59
- markup && addHeadMarkup([result], stringBuilder); // add links to the <head> tag
60
+ markup && addHeadMarkup([result.markup], stringBuilder); // add links to the <head> tag
60
61
  metadata.serverData = metadata.serverData || {}; // create serverData if necessary
61
62
  Object.assign(metadata.serverData, props); // add the preloaded data to serverData for serialization
62
63
  metadata.serverBundles = bundles ? new Set([...allBundles, ...bundles]) : allBundles; // add server bundles for preloadData
@@ -2,7 +2,6 @@ import type { DirectToCoreProxy } from '@lwrjs/types';
2
2
  export type FetchFunction = (request: string | URL | globalThis.Request, init?: RequestInit) => Promise<Response>;
3
3
  export type FetchRequestContext = {
4
4
  host?: string;
5
- requestDepth?: number;
6
5
  headers?: HeadersInit | undefined;
7
6
  coreProxy?: DirectToCoreProxy;
8
7
  abortController?: AbortController;
@@ -22,7 +21,6 @@ export declare class FetchController {
22
21
  private abortController;
23
22
  private headers;
24
23
  private host;
25
- private requestDepth;
26
24
  private coreProxy;
27
25
  fetchEndowment: FetchFunction;
28
26
  constructor(context: FetchRequestContext);
@@ -1,8 +1,7 @@
1
1
  import { Pool as ClientPool } from 'undici';
2
- import { logger } from '@lwrjs/diagnostics';
2
+ import { logger, stringifyError } from '@lwrjs/diagnostics';
3
3
  import { getTracer, ViewSpan } from '@lwrjs/instrumentation';
4
- import { REQUEST_DEPTH_HEADER, toHostname } from '@lwrjs/shared-utils';
5
- const ROUTE_CORE_HEADER = 'X-SFDC-Route-Core';
4
+ import { ROUTE_CORE_HEADER, getTraceHeaders, toHostname } from '@lwrjs/shared-utils';
6
5
  // a single Lambda only services 1 org, so this cache will not grow too large
7
6
  const CORE_CLIENTS = new Map();
8
7
  /**
@@ -15,72 +14,79 @@ const CORE_CLIENTS = new Map();
15
14
  * Any new fetch calls (i.e. from other async function calls) would be immediately aborted.
16
15
  */
17
16
  export class FetchController {
17
+ killSwitchActivated;
18
+ noOpActivated;
19
+ abortController;
20
+ headers;
21
+ host;
22
+ coreProxy;
23
+ fetchEndowment;
18
24
  constructor(context) {
19
- this.controlledFetch = (request, init) => {
20
- if (this.killSwitchActivated) {
21
- return this.handleAbortError(request, undefined);
22
- }
23
- if (!this.abortController) {
24
- this.abortController = new AbortController();
25
- }
26
- const signal = this.abortController?.signal;
27
- // Ensure the init object exists and then add the signal to it.
28
- const updatedInit = {
29
- ...init,
30
- headers: { ...init?.headers, ...this.headers },
31
- signal,
32
- };
33
- const fetchFunction = this.noOpActivated
34
- ? this.fetchNoOp(request, updatedInit)
35
- : this.fetchEndowment(request, updatedInit);
36
- const fetchPromise = fetchFunction.catch((error) => {
37
- // Check if the error is an AbortError
38
- if (error && error?.stack.startsWith('AbortError')) {
39
- return this.handleAbortError(request, error);
40
- }
41
- else {
42
- // Re-throw the error if it's not an AbortError
43
- throw error;
44
- }
45
- });
46
- return fetchPromise;
47
- };
48
- /**
49
- * After SSR is complete the kill switch will abort any pending fetch requests.
50
- */
51
- this.activateKillSwitch = () => {
52
- this.killSwitchActivated = true;
53
- this.abortController?.abort();
54
- this.abortController = undefined;
55
- };
56
- this.deactivateKillSwitch = () => {
57
- this.killSwitchActivated = false;
58
- };
59
- /**
60
- * During SSR renderComponent (which is synchronous) Do not even call any fetch requests
61
- * since they would not complete before SSR is done.
62
- */
63
- this.activateNoOp = () => {
64
- this.noOpActivated = true;
65
- };
66
- this.deactivateNoOp = () => {
67
- this.noOpActivated = false;
68
- };
69
- this.setFetchRequestContext = (context) => {
70
- const { abortController, host, headers, requestDepth, coreProxy } = context;
71
- this.host = host;
72
- this.headers = headers;
73
- this.requestDepth = requestDepth;
74
- this.coreProxy = coreProxy;
75
- this.abortController = abortController;
76
- };
77
25
  this.killSwitchActivated = false;
78
26
  this.noOpActivated = false;
79
27
  this.setFetchRequestContext(context);
80
28
  this.fetchEndowment = this.createFetchEndowment();
81
29
  }
30
+ controlledFetch = (request, init) => {
31
+ if (this.killSwitchActivated) {
32
+ return this.handleAbortError(request, 'Kill switch was already enabled');
33
+ }
34
+ if (!this.abortController) {
35
+ this.abortController = new AbortController();
36
+ }
37
+ const signal = this.abortController?.signal;
38
+ // Ensure the init object exists and then add the signal to it.
39
+ const updatedInit = {
40
+ ...init,
41
+ headers: { ...init?.headers, ...this.headers },
42
+ signal,
43
+ };
44
+ const fetchFunction = this.noOpActivated
45
+ ? this.fetchNoOp(request, updatedInit)
46
+ : this.fetchEndowment(request, updatedInit);
47
+ const fetchPromise = fetchFunction.catch((error) => {
48
+ // Check if the error is an AbortError
49
+ const errorMsg = error.message || error;
50
+ if (error && (errorMsg.startsWith('AbortError') || error?.stack.startsWith('AbortError'))) {
51
+ return this.handleAbortError(request, error);
52
+ }
53
+ else {
54
+ // Re-throw the error if it's not an AbortError
55
+ throw error;
56
+ }
57
+ });
58
+ return fetchPromise;
59
+ };
60
+ /**
61
+ * After SSR is complete the kill switch will abort any pending fetch requests.
62
+ */
63
+ activateKillSwitch = () => {
64
+ this.killSwitchActivated = true;
65
+ this.abortController?.abort('AbortError: Kill switch enabled');
66
+ this.abortController = undefined;
67
+ };
68
+ deactivateKillSwitch = () => {
69
+ this.killSwitchActivated = false;
70
+ };
71
+ /**
72
+ * During SSR renderComponent (which is synchronous) Do not even call any fetch requests
73
+ * since they would not complete before SSR is done.
74
+ */
75
+ activateNoOp = () => {
76
+ this.noOpActivated = true;
77
+ };
78
+ deactivateNoOp = () => {
79
+ this.noOpActivated = false;
80
+ };
81
+ setFetchRequestContext = (context) => {
82
+ const { abortController, host, headers, coreProxy } = context;
83
+ this.host = host;
84
+ this.headers = headers;
85
+ this.coreProxy = coreProxy;
86
+ this.abortController = abortController;
87
+ };
82
88
  handleAbortError(request, error) {
83
- const message = `Orphaned ${String(request)} request was killed. Either the request timed out or it was dispatched during SSR. Async processes are not supported during SSR. For more information, see: https://developer.salesforce.com/docs/platform/lwr/guide/lwr-configure-component-ssr.html.`;
89
+ const message = `${String(request)} request was killed. Either the request timed out or it was dispatched during SSR. Async processes are not supported during SSR. For more information, see: https://developer.salesforce.com/docs/platform/lwr/guide/lwr-configure-component-ssr.html.`;
84
90
  logger.warn({ label: `Server-side Rendering`, message }, error);
85
91
  // Return a response to indicate kill switch
86
92
  return Promise.resolve(new Response(message, { status: 500 }));
@@ -97,35 +103,28 @@ export class FetchController {
97
103
  }
98
104
  else if (init.signal.aborted) {
99
105
  // The request was already aborted go ahead and return the abort error
100
- resolve(this.handleAbortError(request, new Error('Request was aborted')));
106
+ resolve(this.handleAbortError(request, new Error('Request was already aborted')));
101
107
  }
102
108
  else {
103
109
  // Wait until fetches are aborted to resolve with an abort error
104
110
  const abortHandler = (err) => {
105
111
  init?.signal?.removeEventListener('abort', abortHandler);
106
112
  // Resolve the fetch
107
- resolve(this.handleAbortError(request, err));
113
+ resolve(this.handleAbortError(request, new Error('Not allowed: Request was dispatched during SSR', { cause: err })));
108
114
  };
109
115
  init.signal.addEventListener('abort', abortHandler);
110
116
  }
111
117
  });
112
118
  }
113
119
  createFetchEndowment() {
114
- return (request, init) => {
115
- const { host: forwardedOrigin = '', requestDepth = 1, coreProxy } = this;
120
+ return (request, init = {}) => {
121
+ const { host: forwardedOrigin = '', coreProxy } = this;
116
122
  // The Direct-to-Core proxy origin takes precedence over the Forwarded host
117
123
  const origin = coreProxy?.origin && coreProxy.origin.startsWith('http') ? coreProxy.origin : forwardedOrigin;
118
124
  const { finalRequest, finalUrl } = this.getFinalRequest(request, origin);
119
- const finalInit = {
120
- ...init,
121
- headers: {
122
- ...init?.headers,
123
- [REQUEST_DEPTH_HEADER]: String(requestDepth),
124
- },
125
- };
126
125
  if (coreProxy || (forwardedOrigin && finalUrl.startsWith(forwardedOrigin))) {
127
126
  // hint for the CDN that the request is targeted to Core
128
- finalInit.headers[ROUTE_CORE_HEADER] = 'true';
127
+ init.headers[ROUTE_CORE_HEADER] = 'true';
129
128
  }
130
129
  const proxyStr = coreProxy ? JSON.stringify(coreProxy) : 'none';
131
130
  const hasCookies = this.headers && this.headers.Cookie ? 'yes' : 'no';
@@ -156,17 +155,33 @@ export class FetchController {
156
155
  span.setAttributes({ statusCode: res.status });
157
156
  return res;
158
157
  };
158
+ const addErrorToSpan = (err) => {
159
+ // add fetch errors to the trace
160
+ span.setAttributes({ error: stringifyError(err) });
161
+ };
162
+ // add tracing headers based on span values
163
+ init.headers = { ...init.headers, ...getTraceHeaders({}, span) };
159
164
  if (coreProxy) {
160
- return this.fetchWithAgent(finalUrl, finalInit, forwardedOrigin, coreProxy, span)
165
+ return this.fetchWithAgent(finalUrl, init, forwardedOrigin, coreProxy, span)
161
166
  .then((res) => addInfoToSpan(res))
162
167
  .catch((err) => {
163
168
  const { finalRequest: cdnRequest, finalUrl: cdnUrl } = this.getFinalRequest(request, forwardedOrigin);
164
169
  logger.warn(`Fetching data directly from Core failed, retrying through CDN: ${cdnUrl} Error is: ${err.message || err}`);
165
170
  span.setAttributes({ fetchType: 'cdnFallback' });
166
- return fetch(cdnRequest, finalInit).then((res) => addInfoToSpan(res));
171
+ return fetch(cdnRequest, init)
172
+ .then((res) => addInfoToSpan(res))
173
+ .catch((e) => {
174
+ addErrorToSpan(e);
175
+ throw e;
176
+ });
167
177
  });
168
178
  }
169
- return fetch(finalRequest, finalInit).then((res) => addInfoToSpan(res));
179
+ return fetch(finalRequest, init)
180
+ .then((res) => addInfoToSpan(res))
181
+ .catch((e) => {
182
+ addErrorToSpan(e);
183
+ throw e;
184
+ });
170
185
  });
171
186
  };
172
187
  }
@@ -259,19 +274,6 @@ export class FetchController {
259
274
  values.forEach((v) => headers.append(key, v));
260
275
  }
261
276
  return new Response(body, { status: res.statusCode, headers });
262
- // If we want to fallback to CDN for certain status codes
263
- // if (!fetchRes.ok) {
264
- // throw new Error('Failed with status code: ' + fetchRes.status);
265
- // }
266
- // return {
267
- // ...res,
268
- // url: origin + path,
269
- // ok: res.statusCode < 400,
270
- // status: res.statusCode,
271
- // redirected: res.statusCode >= 300 && res.statusCode < 400,
272
- // text: res.body.text.bind(res.body),
273
- // json: res.body.json.bind(res.body),
274
- // } as unknown as Response;
275
277
  });
276
278
  }
277
279
  }
@@ -13,7 +13,7 @@ export function createModuleLoader(config, resourceRegistry, bundleRegistry, run
13
13
  async function createAMDModuleLoader(config, resourceRegistry, bundleRegistry, runtimeEnvironment, runtimeParams, serverData, bootstrapConfig, abortController) {
14
14
  // creating a render context to avoid polluting globals
15
15
  const loaderConfig = getLoaderConfig(BOOTSTRAP_SPECIFIER, config, runtimeParams, serverData);
16
- const { context, controller } = createContext(loaderConfig, runtimeParams, runtimeEnvironment, abortController);
16
+ const { context, controller } = createContext(loaderConfig, runtimeParams, abortController);
17
17
  const contextKeyMap = new Map(Object.keys(context).map((key) => [key, true]));
18
18
  // attaching custom init to delay bootstrap module evaluation
19
19
  let run;
@@ -238,10 +238,9 @@ async function createESMModuleLoader() {
238
238
  * @param runtimeParams - request parameters
239
239
  * @returns a `globalThis` object
240
240
  */
241
- function createContext(LWR, runtimeParams, runtimeEnvironment, abortController) {
241
+ function createContext(LWR, runtimeParams, abortController) {
242
242
  const fetchController = new FetchController({
243
243
  host: runtimeParams.host,
244
- requestDepth: runtimeParams.requestDepth,
245
244
  coreProxy: runtimeParams.coreProxy,
246
245
  abortController,
247
246
  });
@@ -1,7 +1,7 @@
1
1
  // TODO: investigate perf impact W-16056356
2
2
  import { LRUCache } from 'lru-cache';
3
3
  import { LwrApplicationError, LwrError, LwrStatusError, descriptions, logger, stringifyError, } from '@lwrjs/diagnostics';
4
- import { buildEnvironmentContext, getCacheKeyFromJson, getSpecifier, isLambdaEnv, moduleSpecifierToKebabCase, getFeatureFlags, TaskPool, isLocalDev, cookieStringToObject, } from '@lwrjs/shared-utils';
4
+ import { REQUEST_DEPTH_HEADER, TaskPool, buildEnvironmentContext, cookieStringToObject, getCacheKeyFromJson, getFeatureFlags, getSpecifier, getTraceHeaders, isLambdaEnv, isLocalDev, moduleSpecifierToKebabCase, } from '@lwrjs/shared-utils';
5
5
  import { ViewSpan, cacheCountStore, getTracer } from '@lwrjs/instrumentation';
6
6
  import { getServerBootstrapServices, getRenderTimeout } from './utils.js';
7
7
  import { createModuleLoader } from './moduleLoader.js';
@@ -22,8 +22,13 @@ export function getRenderer(config, bundleRegistry, resourceRegistry) {
22
22
  return singleton;
23
23
  }
24
24
  export class Renderer {
25
+ config;
26
+ bundleRegistry;
27
+ resourceRegistry;
28
+ contextPerEnv;
29
+ pendingRenders;
30
+ globalCache = {};
25
31
  constructor(config, bundleRegistry, resourceRegistry) {
26
- this.globalCache = {};
27
32
  this.config = config;
28
33
  this.bundleRegistry = bundleRegistry;
29
34
  this.resourceRegistry = resourceRegistry;
@@ -71,8 +76,9 @@ export class Renderer {
71
76
  const abortController = new AbortController();
72
77
  const timeout = new Promise((_, reject) => {
73
78
  timerId = setTimeout(() => {
74
- abortController.abort();
75
- reject(new LwrApplicationError(descriptions.APPLICATION.SSR_TIMEOUT(route.id, getRenderTimeout())));
79
+ const message = descriptions.APPLICATION.SSR_TIMEOUT(route.id, getRenderTimeout());
80
+ abortController.abort(`AbortError: ${message}`);
81
+ reject(new LwrApplicationError(message));
76
82
  }, getRenderTimeout());
77
83
  });
78
84
  result = (await this.pendingRenders.execute(async () => {
@@ -301,15 +307,15 @@ export class Renderer {
301
307
  resetFetchController(fetchController, runtimeParams, route, abortController) {
302
308
  // Re-enable fetch that was disabled at the end of the previous page render via `enableFetchKillSwitch`
303
309
  fetchController.disableFetchKillSwitch();
310
+ const baseHeaders = {
311
+ [REQUEST_DEPTH_HEADER]: runtimeParams.requestDepth ?? '0',
312
+ ...getTraceHeaders(runtimeParams),
313
+ };
304
314
  fetchController.setFetchRequestContext({
305
315
  host: runtimeParams.host,
306
- requestDepth: runtimeParams.requestDepth,
307
316
  headers: route.bootstrap.includeCookiesForSSR
308
- ? {
309
- Cookie: runtimeParams.cookie,
310
- 'True-Client-IP': runtimeParams.trueClientIP,
311
- }
312
- : { 'True-Client-IP': runtimeParams.trueClientIP },
317
+ ? { Cookie: runtimeParams.cookie, ...baseHeaders }
318
+ : baseHeaders,
313
319
  coreProxy: runtimeParams.coreProxy,
314
320
  abortController,
315
321
  });
@@ -362,15 +368,13 @@ function getServerData(component, context, serverData) {
362
368
  return getTracer().trace({
363
369
  name: ViewSpan.GetServerData,
364
370
  attributes: { specifier: component.specifier },
365
- }, async () => {
371
+ }, async (span) => {
366
372
  try {
367
373
  const data = await component.module.getServerData(context);
368
374
  Object.assign(serverData, data.props);
369
- // TODO: delete with W-17406173 after consumers have migrated away from SsrDataResponse.status
370
- const dataRes = data;
371
- if (dataRes.status) {
372
- const { code, location } = dataRes.status;
373
- throw new LwrStatusError(undefined, code, location ? { location } : undefined);
375
+ if (data.warnings) {
376
+ const warnings = data.warnings.map((w) => stringifyError(w)).join(', ');
377
+ span.setAttributes({ warnings });
374
378
  }
375
379
  return data;
376
380
  }
@@ -388,15 +392,18 @@ function getServerData(component, context, serverData) {
388
392
  });
389
393
  }
390
394
  async function renderToString(engine, component, props) {
395
+ const isSSRv2 = getFeatureFlags().SSR_COMPILER_ENABLED || false;
391
396
  return getTracer().trace({
392
397
  name: ViewSpan.RenderComponent,
393
- attributes: { specifier: component.specifier },
398
+ attributes: { specifier: component.specifier, isSSRv2 },
394
399
  }, async () => {
395
400
  try {
396
401
  const elementName = moduleSpecifierToKebabCase(component.specifier);
397
402
  const moduleDefault = component.module.default;
398
- const html = getFeatureFlags().SSR_COMPILER_ENABLED
399
- ? await engine.renderComponent(elementName, moduleDefault, props, 'sync')
403
+ const html = isSSRv2
404
+ ? // serverSideRenderComponent(tagName: string, Component: ComponentWithGenerateMarkup, props: Properties = {}, styleDedupePrefix = '', styleDedupeIsEnabled = false, mode: CompilationMode = DEFAULT_SSR_MODE / 'sync'
405
+ await engine.renderComponent(elementName, moduleDefault, props, elementName, false, // true, re-enable with W-18004123
406
+ 'sync')
400
407
  : engine.renderComponent(elementName, moduleDefault, props);
401
408
  return { html };
402
409
  }
@@ -0,0 +1,10 @@
1
+ import type { ProviderContext, ResourceDefinition, ResourceIdentifier, ResourceProvider, RuntimeEnvironment } from '@lwrjs/types';
2
+ export default class SsrResourceProvider implements ResourceProvider {
3
+ name: string;
4
+ private ssrUtilsName;
5
+ private versionCache;
6
+ private context;
7
+ constructor(_config: any, context: ProviderContext);
8
+ getResource<T extends ResourceIdentifier, R extends RuntimeEnvironment>(resource: T, environment: R): Promise<ResourceDefinition | undefined>;
9
+ }
10
+ //# sourceMappingURL=resourceProvider.d.ts.map
@@ -0,0 +1,80 @@
1
+ import path from 'path';
2
+ import { rollup } from 'rollup';
3
+ import { nodeResolve } from '@rollup/plugin-node-resolve';
4
+ import terser from '@rollup/plugin-terser';
5
+ import { LWC_VERSION } from '@lwrjs/config';
6
+ import { BundleSpan, getTracer } from '@lwrjs/instrumentation';
7
+ import { rootPath } from '@lwrjs/lwc-ssr/package';
8
+ import { createIntegrityHash } from '@lwrjs/shared-utils';
9
+ /**
10
+ * Provide an IIFE script resource containing @lwc/ssr-client-utils#register-lwc-style.js
11
+ * This script performs style de-duping for components which are SSR compiled.
12
+ * It must be included in the <head> section of every page which uses the SSR compiler.
13
+ * If the script is not included, only the first instance of each SSRed component will be styled!
14
+ * The @lwc/ssr-client-utils package is a peer dependency so LWR is not tied to an LWC version.
15
+ */
16
+ const banner = `/**
17
+ * Copyright (c) 2025, salesforce.com, inc.
18
+ * All rights reserved.
19
+ * SPDX-License-Identifier: MIT
20
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
21
+ */
22
+ /* LWC SSR Client Utils v${LWC_VERSION} */`;
23
+ export default class SsrResourceProvider {
24
+ name = 'lwc-ssr-client-utils';
25
+ ssrUtilsName = 'register-lwc-style.js';
26
+ versionCache = new Map();
27
+ context;
28
+ constructor(_config, context) {
29
+ this.context = context;
30
+ }
31
+ async getResource(resource, environment) {
32
+ // validate identifier
33
+ const { specifier, version = LWC_VERSION } = resource;
34
+ if (specifier !== this.ssrUtilsName || version !== LWC_VERSION) {
35
+ return;
36
+ }
37
+ // check cache
38
+ const cacheKey = `${specifier}_${version}`;
39
+ if (this.versionCache.has(cacheKey)) {
40
+ return this.versionCache.get(cacheKey);
41
+ }
42
+ // generate script resource
43
+ const { output } = await getTracer().trace({
44
+ name: BundleSpan.Rollup,
45
+ attributes: { specifier },
46
+ }, async () => {
47
+ const bundler = await rollup({
48
+ input: path.join(rootPath, 'build', 'scripts', specifier),
49
+ plugins: [
50
+ nodeResolve({
51
+ // import from @lwc/ssr-client-utils
52
+ extensions: ['.js'],
53
+ modulesOnly: true,
54
+ modulePaths: ['node_modules/@lwc'],
55
+ }),
56
+ terser({
57
+ output: {
58
+ comments: (_, comment) => comment.value.includes('LWC SSR Client Utils'),
59
+ },
60
+ }),
61
+ ],
62
+ });
63
+ return bundler.generate({ format: 'iife', banner });
64
+ });
65
+ // assemble resource definition
66
+ const resourceDef = {
67
+ specifier,
68
+ version,
69
+ type: 'application/javascript',
70
+ inline: true,
71
+ src: await this.context.resourceRegistry.resolveResourceUri({ specifier, version }, environment),
72
+ content: output[0].code,
73
+ integrity: createIntegrityHash(output[0].code),
74
+ };
75
+ // cache and return
76
+ this.versionCache.set(cacheKey, resourceDef);
77
+ return resourceDef;
78
+ }
79
+ }
80
+ //# sourceMappingURL=resourceProvider.js.map
@@ -1,6 +1,9 @@
1
1
  import { ViewSpan, getTracer } from '@lwrjs/instrumentation';
2
2
  import { LwrApplicationError, LwrError, stringifyError } from '@lwrjs/diagnostics';
3
3
  export class ServerBootstrapServices {
4
+ serverDataCallbacks;
5
+ requestHooks;
6
+ _serviceAPI;
4
7
  evaluateServerDataHooks(serverData = {}) {
5
8
  // now that we have server data, run the server data hooks
6
9
  for (const serverDataHook of this.serverDataCallbacks) {
@@ -1,4 +1,4 @@
1
- import type { ClientBootstrapConfig, EnvironmentContext, LwrStringBuilder, NormalizedLwrAppBootstrapConfig, NormalizedLwrErrorRoute, NormalizedLwrRoute, ProviderAppConfig, PublicResourceRegistry, RenderedViewMetadata, RuntimeEnvironment, RuntimeParams, ServerData, SsrDataResponse } from '@lwrjs/types';
1
+ import type { ClientBootstrapConfig, EnvironmentContext, NormalizedLwrAppBootstrapConfig, NormalizedLwrErrorRoute, NormalizedLwrRoute, ProviderAppConfig, PublicResourceRegistry, RenderedViewMetadata, RuntimeEnvironment, RuntimeParams, ServerData } from '@lwrjs/types';
2
2
  interface ServerEnvironment extends EnvironmentContext {
3
3
  SSR: boolean;
4
4
  }
@@ -12,17 +12,5 @@ export declare function getLoaderConfig(bootstrapModule: string, config: Provide
12
12
  };
13
13
  export declare function getServerBootstrapServices(route: NormalizedLwrRoute | NormalizedLwrErrorRoute): string[];
14
14
  export declare function mergeWarnings(metadata: RenderedViewMetadata, warnings?: (Error | string)[]): void;
15
- /**
16
- * Serialize SsrDataResponse.markup into an HTML string
17
- * @param results An array of responses from getServerData hooks
18
- * @returns A string of HTML generated from markup metadata
19
- */
20
- export declare function createHeadMarkup(results: SsrDataResponse[]): string;
21
- /**
22
- * Serialize SsrDataResponse.markup into HTML, then add it to the <head> of a base doc
23
- * @param results An array of responses from getServerData hooks
24
- * @param stringBuilder The string builder for a base document
25
- */
26
- export declare function addHeadMarkup(results: SsrDataResponse[], stringBuilder: LwrStringBuilder): void;
27
15
  export {};
28
16
  //# sourceMappingURL=utils.d.ts.map
package/build/es/utils.js CHANGED
@@ -1,4 +1,3 @@
1
- import { logger } from '@lwrjs/diagnostics';
2
1
  import { buildEnvironmentContext, getFeatureFlags, normalizeVersionToUri, isLambdaEnv, getSpecifier, } from '@lwrjs/shared-utils';
3
2
  const DEFAULT_SSR_TIMEOUT = 5000; // 5 seconds, override with process.env.SSR_TIMEOUT
4
3
  export const SSR_PROPS_ATTR = 'data-lwr-props-id';
@@ -98,69 +97,4 @@ export function mergeWarnings(metadata, warnings = []) {
98
97
  metadata.serverDebug.warnings = metadata.serverDebug.warnings || [];
99
98
  metadata.serverDebug.warnings.push(...warnings);
100
99
  }
101
- /** SSR HEAD MARKUP UTILS */
102
- function createMetaTags(meta) {
103
- return meta.reduce((metaStr, { name, content, httpEquiv }) => {
104
- if (!name && !content && !httpEquiv)
105
- return metaStr; // do not create empty <meta> tags
106
- const nameStr = name ? ` name="${name}"` : '', httpEquivStr = httpEquiv ? ` http-equiv="${httpEquiv}"` : '', contentStr = content ? ` content="${content}"` : '';
107
- return metaStr + `<meta${nameStr}${httpEquivStr}${contentStr}>\n`;
108
- }, '');
109
- }
110
- function createScriptTags(scripts) {
111
- return scripts.reduce((scriptStr, { body }) => scriptStr + `<script type="application/ld+json">${body}</script>\n`, '');
112
- }
113
- function createLinkTags(links) {
114
- return links.reduce((linkStr, { href, rel, as, fetchpriority }) => {
115
- const relStr = rel ? ` rel="${rel}"` : '', asStr = as ? ` as="${as}"` : '', fetchStr = fetchpriority ? ` fetchpriority="${fetchpriority}"` : '';
116
- return linkStr + `<link href="${href}"${relStr}${asStr}${fetchStr}>\n`;
117
- }, '');
118
- }
119
- function createStyleTags(styles) {
120
- return styles.reduce((styleStr, { body, id }) => {
121
- const idStr = id ? ` id="${id}"` : '';
122
- return styleStr + `<style type="text/css"${idStr}>${body}</style>\n`;
123
- }, '');
124
- }
125
- /**
126
- * Serialize SsrDataResponse.markup into an HTML string
127
- * @param results An array of responses from getServerData hooks
128
- * @returns A string of HTML generated from markup metadata
129
- */
130
- export function createHeadMarkup(results) {
131
- // Loop through the <title>, <script>, <meta>, and <link> tag information
132
- // Create an HTML string for each tag
133
- let hasTitle = false;
134
- return results.reduce((str, { markup: { title, scripts = [], meta = [], links = [], styles = [] } = {} }) => {
135
- if (title && !hasTitle) {
136
- // first <title> wins
137
- hasTitle = true;
138
- str += `<title>${title}</title>\n`;
139
- }
140
- return (str +
141
- createMetaTags(meta) +
142
- createScriptTags(scripts) +
143
- createLinkTags(links) +
144
- createStyleTags(styles));
145
- }, '');
146
- }
147
- /**
148
- * Serialize SsrDataResponse.markup into HTML, then add it to the <head> of a base doc
149
- * @param results An array of responses from getServerData hooks
150
- * @param stringBuilder The string builder for a base document
151
- */
152
- export function addHeadMarkup(results, stringBuilder) {
153
- // Create HTML tags for each item in the SsrDataResponse.markup bag
154
- const headMarkup = createHeadMarkup(Object.values(results));
155
- if (headMarkup) {
156
- // Add all the links to the <head> section of the base document
157
- const headIndex = stringBuilder.original.indexOf('</head>');
158
- if (headIndex >= 0) {
159
- stringBuilder.prependLeft(headIndex, headMarkup);
160
- }
161
- else {
162
- logger.error('Adding markup during server-side rendering failed. Could not find the </head> tag.');
163
- }
164
- }
165
- }
166
100
  //# sourceMappingURL=utils.js.map
@@ -1,13 +1,17 @@
1
1
  import BaseViewProvider from '@lwrjs/base-view-provider';
2
2
  import { descriptions, LwrApplicationError, LwrError, stringifyError } from '@lwrjs/diagnostics';
3
3
  import { ViewSpan, getTracer } from '@lwrjs/instrumentation';
4
- import { hashContent, isSpecifier, moduleSpecifierToKebabCase, slugify } from '@lwrjs/shared-utils';
5
- import { createHeadMarkup } from '../utils.js';
4
+ import { createHeadMarkup, hashContent, isSpecifier, moduleSpecifierToKebabCase, slugify, } from '@lwrjs/shared-utils';
6
5
  import { getRenderer } from '../renderer.js';
7
6
  export default class LwcViewProvider extends BaseViewProvider {
7
+ name = 'ssr-view-provider';
8
+ moduleBundler;
9
+ resourceRegistry;
10
+ routes;
11
+ runtimeEnvironment;
12
+ config;
8
13
  constructor(_pluginConfig, providerConfig) {
9
14
  super();
10
- this.name = 'ssr-view-provider';
11
15
  this.moduleBundler = providerConfig.moduleBundler;
12
16
  this.resourceRegistry = providerConfig.resourceRegistry;
13
17
  this.routes = [...providerConfig.config.routes, ...providerConfig.config.errorRoutes];
@@ -54,7 +58,7 @@ export default class LwcViewProvider extends BaseViewProvider {
54
58
  throw new Error('Failed to render content template component');
55
59
  }
56
60
  if (markup)
57
- viewProperties.serverHeadMarkup = createHeadMarkup([results[specifier]]); // generate <head> markup
61
+ viewProperties.serverHeadMarkup = createHeadMarkup([markup]); // generate <head> markup
58
62
  return {
59
63
  // add "lwc:external" to the contentTemplate component to mark it as already processed
60
64
  renderedView: html.replace(`<${element}`, `<${element} lwc:external`),
@@ -1,7 +1,7 @@
1
1
  import { descriptions, logger, LwrApplicationError, LwrError, stringifyError } from '@lwrjs/diagnostics';
2
2
  import { ViewSpan, getTracer } from '@lwrjs/instrumentation';
3
- import { HYDRATE_DIRECTIVE, isCsrIsland, isHydrateOnLoad, kebabCaseToModuleSpecifier, shortestTtl, } from '@lwrjs/shared-utils';
4
- import { SSR_PROPS_ATTR, addHeadMarkup, getPropsId, mergeWarnings } from '../utils.js';
3
+ import { HYDRATE_DIRECTIVE, addHeadMarkup, isCsrIsland, isHydrateOnLoad, kebabCaseToModuleSpecifier, shortestTtl, } from '@lwrjs/shared-utils';
4
+ import { SSR_PROPS_ATTR, getPropsId, mergeWarnings } from '../utils.js';
5
5
  import { getRenderer } from '../renderer.js';
6
6
  /**
7
7
  * This is a view transformer run by the view registry during linking of a page document/route (configured in lwr.config.json[routes]).
@@ -79,7 +79,8 @@ export default function lwcSsrViewTransformer(options, { config, moduleBundler,
79
79
  metadata.serverBundles = islandBundles
80
80
  ? new Set([...allBundles, ...islandBundles])
81
81
  : allBundles;
82
- results && addHeadMarkup(Object.values(results), stringBuilder);
82
+ results &&
83
+ addHeadMarkup(Object.values(results).map((r) => r.markup), stringBuilder);
83
84
  }
84
85
  catch (e) {
85
86
  if (e instanceof LwrError)
@@ -0,0 +1,5 @@
1
+ // see: https://github.com/salesforce/lwc/blob/master/packages/%40lwc/ssr-client-utils/register-lwc-style.js
2
+
3
+ import { registerLwcStyleComponent } from '@lwc/ssr-client-utils';
4
+
5
+ registerLwcStyleComponent();
package/package.cjs ADDED
@@ -0,0 +1,9 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const rootPath = path.join(__dirname, './');
4
+ const version = JSON.parse(fs.readFileSync(path.join(rootPath, 'package.json'), 'utf-8')).version;
5
+
6
+ module.exports = {
7
+ rootPath,
8
+ version
9
+ };
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "version": "0.17.2-alpha.9",
7
+ "version": "0.18.0",
8
8
  "homepage": "https://developer.salesforce.com/docs/platform/lwr/overview",
9
9
  "repository": {
10
10
  "type": "git",
@@ -29,39 +29,51 @@
29
29
  "./dataViewTransformer": {
30
30
  "import": "./build/es/dataViewTransformer/index.js",
31
31
  "require": "./build/cjs/dataViewTransformer/index.cjs"
32
- }
32
+ },
33
+ "./resourceProvider": {
34
+ "import": "./build/es/resourceProvider.js",
35
+ "require": "./build/cjs/resourceProvider.cjs"
36
+ },
37
+ "./package": "./package.cjs"
33
38
  },
34
39
  "scripts": {
35
- "build": "tsc -b",
40
+ "build": "tsc -b && shx cp -R ./src/scripts ./build",
36
41
  "clean": "rimraf build node_modules",
37
42
  "test": "jest"
38
43
  },
39
44
  "files": [
40
45
  "build/**/*.js",
41
46
  "build/**/*.cjs",
42
- "build/**/*.d.ts"
47
+ "build/**/*.d.ts",
48
+ "package.cjs"
43
49
  ],
44
50
  "dependencies": {
45
- "@lwrjs/config": "0.17.2-alpha.9",
46
- "@lwrjs/diagnostics": "0.17.2-alpha.9",
47
- "@lwrjs/instrumentation": "0.17.2-alpha.9",
48
- "@lwrjs/loader": "0.17.2-alpha.9",
49
- "@lwrjs/shared-utils": "0.17.2-alpha.9",
51
+ "@lwrjs/config": "0.18.0",
52
+ "@lwrjs/diagnostics": "0.18.0",
53
+ "@lwrjs/instrumentation": "0.18.0",
54
+ "@lwrjs/loader": "0.18.0",
55
+ "@lwrjs/shared-utils": "0.18.0",
56
+ "@rollup/plugin-node-resolve": "^15.2.3",
57
+ "@rollup/plugin-terser": "^0.4.4",
50
58
  "fs-extra": "^11.2.0",
51
59
  "lru-cache": "^10.4.3",
60
+ "rollup": "^2.79.2",
52
61
  "undici": "^6.21.1"
53
62
  },
54
63
  "devDependencies": {
55
- "@lwrjs/types": "0.17.2-alpha.9",
56
- "jest": "^26.6.3",
64
+ "@lwrjs/types": "0.18.0",
65
+ "jest": "29.7.0",
57
66
  "memfs": "^4.13.0",
58
- "ts-jest": "^26.5.6"
67
+ "ts-jest": "^29.2.6"
68
+ },
69
+ "peerDependencies": {
70
+ "@lwc/ssr-client-utils": ">= 8.16"
59
71
  },
60
72
  "engines": {
61
- "node": ">=18.0.0"
73
+ "node": ">=20.0.0"
62
74
  },
63
75
  "volta": {
64
76
  "extends": "../../../package.json"
65
77
  },
66
- "gitHead": "7ede5d04a97513b9869b13e66155bc4f3920bb99"
78
+ "gitHead": "763c7c558ea75fe34a91162c9793de63e55f1d2f"
67
79
  }