@lwrjs/lwc-ssr 0.11.0-alpha.9 → 0.11.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.
@@ -40,14 +40,14 @@ import ${importName} from '${service}';`;
40
40
  serviceCalls += `
41
41
 
42
42
  globalThis.trace({
43
- name: '${import_instrumentation.ViewSpan.SsrBootstrapService}',
43
+ name: '${import_instrumentation.ViewSpan.BootstrapService}',
44
44
  attributes: {
45
45
  specifier: '${rootSpecifier}',
46
46
  serviceSpecifier: '${service}',
47
47
  }
48
48
  }, () => {
49
49
  try {
50
- ${importName}();
50
+ ${importName}({ serverData });
51
51
  } catch(e) {
52
52
  const message = e.message || e;
53
53
  // we need to re-throw with the service specifier in the error message
@@ -60,7 +60,15 @@ import { renderComponent } from '@lwc/engine-server';
60
60
  import Ctor, * as rootComponent from '${rootSpecifier}';${serviceImports}
61
61
 
62
62
  (async () => {
63
- let result, props, markup, existingTaskCount;
63
+ let result, props, markup, serverData = globalThis.LWR?.serverData || {}, existingTaskCount;
64
+ // remove the LWR global properties before any customer code is run
65
+ if (globalThis.LWR?.define) {
66
+ // AMD: support dynamic imports in getServerData
67
+ globalThis.LWR = Object.freeze({ define: globalThis.LWR.define });
68
+ } else {
69
+ delete globalThis.LWR; // ESM
70
+ }
71
+
64
72
  try {
65
73
  // 1. setup page data
66
74
  const context = globalThis.getContext();
@@ -80,10 +88,8 @@ import Ctor, * as rootComponent from '${rootSpecifier}';${serviceImports}
80
88
  throw new Error('Error in "getServerData" for "${rootSpecifier}": ' + message);
81
89
  }
82
90
  });
83
-
84
91
  props = data.props; // overwrite public props
85
- globalThis.LWR = globalThis.LWR || { serverData: {} };
86
- Object.assign(globalThis.LWR.serverData, data.props); // add props to server data
92
+ Object.assign(serverData, data.props); // add props to server data
87
93
  markup = data.markup;
88
94
  }${serviceCalls}
89
95
 
@@ -101,8 +107,8 @@ import Ctor, * as rootComponent from '${rootSpecifier}';${serviceImports}
101
107
 
102
108
  } catch(e) {
103
109
  const message = e.message || e;
104
- // Relay the LWC rendering stack
105
-
110
+
111
+ // add the LWC rendering stack
106
112
  const error = e.wcStack ?
107
113
  'An error occurred during server-side rendering for component stack: ' + e.wcStack + '. Error was: ' + message :
108
114
  'An error occured during server-side rendering: ' + message;
@@ -114,7 +120,6 @@ import Ctor, * as rootComponent from '${rootSpecifier}';${serviceImports}
114
120
  const currentTaskCount = process.getActiveResourcesInfo
115
121
  ? process.getActiveResourcesInfo().length
116
122
  : 0;
117
-
118
123
  if (currentTaskCount - existingTaskCount > 0) {
119
124
  console.warn('[warn] async tasks encountered while server rendering "${rootSpecifier}"');
120
125
  }
@@ -92,7 +92,7 @@ async function getCode(runtimeEnvironment, serverData, lwrVersion, bundleSpecifi
92
92
  }
93
93
  async function getBundle(specifier, moduleBundler, routes, runtimeEnvironment, runtimeParams) {
94
94
  if ((0, import_shared_utils.getFeatureFlags)().SSR_STATIC_BUNDLES) {
95
- return buildBundle(specifier, moduleBundler, routes, runtimeEnvironment, runtimeParams);
95
+ return buildBundle(specifier, moduleBundler, routes, {...runtimeEnvironment, debug: false}, runtimeParams);
96
96
  }
97
97
  return bundle(specifier, moduleBundler, runtimeEnvironment, runtimeParams, {
98
98
  appendExcludes: false,
@@ -102,27 +102,38 @@ async function getBundle(specifier, moduleBundler, routes, runtimeEnvironment, r
102
102
  async function bundle(specifier, moduleBundler, runtimeEnvironment, runtimeParams, bundleConfigOverrides) {
103
103
  return await moduleBundler.getModuleBundle({specifier}, runtimeEnvironment, runtimeParams, bundleConfigOverrides);
104
104
  }
105
- async function bundleImports(bundleCode, imports = [], visited, moduleBundler, runtimeEnvironment, runtimeParams) {
105
+ async function bundleImports(bundleCode, dynamicImports = [], imports = [], visited, moduleBundler, runtimeEnvironment, runtimeParams) {
106
106
  for (const {specifier} of imports) {
107
107
  if (!visited.has(specifier)) {
108
108
  visited.add(specifier);
109
109
  const {code, bundleRecord, version} = await bundle(specifier, moduleBundler, runtimeEnvironment, runtimeParams);
110
- let bundledCode;
111
- if (runtimeEnvironment.featureFlags?.EXPERIMENTAL_UNVERSIONED_ALIASES) {
112
- const versionedSpecifier = (0, import_shared_utils.getSpecifier)({specifier, version});
113
- const aliasCode = (0, import_shared_utils.createAmdAlias)(versionedSpecifier, specifier);
114
- bundledCode = [code, aliasCode].filter(Boolean).join("");
115
- } else {
116
- bundledCode = code;
117
- }
118
- bundleCode = await bundleImports(bundledCode, bundleRecord.imports, visited, moduleBundler, runtimeEnvironment, runtimeParams) + bundleCode;
110
+ const bundledCode = getBundledCode(runtimeEnvironment, specifier, version, code);
111
+ bundleCode = await bundleImports(bundledCode, bundleRecord.dynamicImports, bundleRecord.imports, visited, moduleBundler, runtimeEnvironment, runtimeParams) + bundleCode;
112
+ }
113
+ }
114
+ for (const {specifier} of dynamicImports) {
115
+ if (!visited.has(specifier)) {
116
+ visited.add(specifier);
117
+ const {code, version} = await bundle(specifier, moduleBundler, runtimeEnvironment, runtimeParams);
118
+ bundleCode += getBundledCode(runtimeEnvironment, specifier, version, code);
119
119
  }
120
120
  }
121
121
  return bundleCode;
122
122
  }
123
+ function getBundledCode(runtimeEnvironment, specifier, version, code) {
124
+ let bundledCode;
125
+ if (runtimeEnvironment.featureFlags?.EXPERIMENTAL_UNVERSIONED_ALIASES) {
126
+ const versionedSpecifier = (0, import_shared_utils.getSpecifier)({specifier, version});
127
+ const aliasCode = (0, import_shared_utils.createAmdAlias)(versionedSpecifier, specifier);
128
+ bundledCode = [code, aliasCode].filter(Boolean).join("");
129
+ } else {
130
+ bundledCode = code;
131
+ }
132
+ return bundledCode;
133
+ }
123
134
  async function getBundleWithImports(specifier, moduleBundler, runtimeEnvironment, runtimeParams) {
124
135
  const {code, bundleRecord} = await bundle(specifier, moduleBundler, runtimeEnvironment, runtimeParams);
125
- return await bundleImports(code, bundleRecord.imports, new Set(["lwc", specifier]), moduleBundler, runtimeEnvironment, runtimeParams);
136
+ return await bundleImports(code, bundleRecord.dynamicImports, bundleRecord.imports, new Set(["lwc", specifier]), moduleBundler, runtimeEnvironment, runtimeParams);
126
137
  }
127
138
  async function buildBundle(ssrSpecifier, moduleBundler, routes, runtimeEnvironment, runtimeParams) {
128
139
  const {rootSpecifier, route} = (0, import_identity.parseSpecifier)(ssrSpecifier, routes);
@@ -28,11 +28,10 @@ __export(exports, {
28
28
  });
29
29
  var import_instrumentation = __toModule(require("@lwrjs/instrumentation"));
30
30
  var import_near_membrane_node = __toModule(require("@locker/near-membrane-node"));
31
- var import_node_fetch = __toModule(require("node-fetch"));
32
31
  var import_crypto = __toModule(require("crypto"));
33
32
  var import_url = __toModule(require("url"));
34
33
  var import_utils = __toModule(require("./utils.cjs"));
35
- function runCode(codes, context) {
34
+ function runCode(codes, context, host) {
36
35
  return new Promise((resolve, reject) => {
37
36
  let resolver;
38
37
  const p = new Promise((r) => resolver = r);
@@ -46,9 +45,10 @@ function runCode(codes, context) {
46
45
  crypto: import_crypto.webcrypto,
47
46
  getContext,
48
47
  trace,
49
- fetch: import_node_fetch.default,
48
+ fetch: (0, import_utils.createFetchEndowment)(host),
50
49
  resolver,
51
50
  URL: import_url.URL,
51
+ URLSearchParams: import_url.URLSearchParams,
52
52
  process,
53
53
  setTimeout,
54
54
  clearTimeout,
@@ -31,17 +31,18 @@ var import_utils = __toModule(require("./utils.cjs"));
31
31
  var HEADER = "/* This module is generated and meant to be used in a Server context */";
32
32
  var WORKER_CODE_SANDBOX_APIS = [
33
33
  `const { parentPort, workerData } = require('worker_threads');`,
34
+ `globalThis.fetch = require('@lwrjs/lwc-ssr/viewTransformer').createFetchEndowment(workerData.host);`,
35
+ `delete workerData.host`,
34
36
  `globalThis.getContext = () => workerData;`,
35
- `globalThis.fetch = require('node-fetch');`,
36
37
  `globalThis.crypto = require('crypto').webcrypto;`,
37
38
  `globalThis.resolver = (...args) => parentPort.postMessage(...args);`,
38
39
  `globalThis.trace = (id, fn) => fn()`
39
40
  ];
40
- function runCode(codes, workerData) {
41
+ function runCode(codes, workerData, host) {
41
42
  const time = (0, import_utils.getWatchdogTime)();
42
43
  const workerCode = [HEADER, ...WORKER_CODE_SANDBOX_APIS, ...codes].join("\n");
43
44
  return new Promise((resolve, reject) => {
44
- const worker = new import_worker_threads.Worker(workerCode, {eval: true, workerData});
45
+ const worker = new import_worker_threads.Worker(workerCode, {eval: true, workerData: {...workerData, host}});
45
46
  const timerId = (0, import_utils.startWatchdogTimer)(() => {
46
47
  worker.terminate();
47
48
  reject(new Error(`SSR timed out after ${time}ms`));
@@ -29,9 +29,9 @@ __export(exports, {
29
29
  var import_sandbox_worker = __toModule(require("./sandbox-worker.cjs"));
30
30
  var import_sandbox_locker = __toModule(require("./sandbox-locker.cjs"));
31
31
  var import_shared_utils = __toModule(require("@lwrjs/shared-utils"));
32
- function runCode(codes, context) {
32
+ function runCode(codes, context, host) {
33
33
  if ((0, import_shared_utils.getFeatureFlags)().SSR_SANDBOX_WORKER) {
34
- return (0, import_sandbox_worker.default)(codes, context);
34
+ return (0, import_sandbox_worker.default)(codes, context, host);
35
35
  }
36
- return (0, import_sandbox_locker.default)(codes, context);
36
+ return (0, import_sandbox_locker.default)(codes, context, host);
37
37
  }
@@ -29,9 +29,9 @@ __export(exports, {
29
29
  var import_amd_utils = __toModule(require("./amd-utils.cjs"));
30
30
  var import_sandbox = __toModule(require("./sandbox.cjs"));
31
31
  var import_perf_hooks = __toModule(require("perf_hooks"));
32
- var import_shared_utils = __toModule(require("@lwrjs/shared-utils"));
32
+ var import_diagnostics = __toModule(require("@lwrjs/diagnostics"));
33
33
  var import_instrumentation = __toModule(require("@lwrjs/instrumentation"));
34
- var SSR_FLAG = "process.env.SSR = true;";
34
+ var import_shared_utils = __toModule(require("@lwrjs/shared-utils"));
35
35
  var EMIT_WIRES = `globalThis.lwcRuntimeFlags = { ENABLE_WIRE_SYNC_EMIT: true };`;
36
36
  async function ssrElement({
37
37
  specifier,
@@ -55,9 +55,15 @@ async function ssrElement({
55
55
  url: runtimeParams.url,
56
56
  params: runtimeParams.params || {},
57
57
  query: runtimeParams.query || {},
58
- locale: runtimeParams.locale || runtimeEnvironment.defaultLocale,
58
+ locale: runtimeParams.locale || runtimeEnvironment.i18n.defaultLocale,
59
59
  basePath: runtimeParams.basePath || runtimeEnvironment.basePath
60
60
  };
61
+ const environment = {
62
+ SSR: true,
63
+ ...(0, import_shared_utils.buildEnvironmentContext)(runtimeParams)
64
+ };
65
+ const environmentCode = `process.env = ${JSON.stringify(environment)}`;
66
+ const host = runtimeParams.host;
61
67
  const startTime = import_perf_hooks.performance.now();
62
68
  return (0, import_instrumentation.getTracer)().trace({
63
69
  name: import_instrumentation.ViewSpan.ServerSideRender,
@@ -66,14 +72,14 @@ async function ssrElement({
66
72
  }
67
73
  }, async () => {
68
74
  const {result, props, markup, cache, error} = format === "amd" ? await (0, import_sandbox.default)([
69
- SSR_FLAG,
70
75
  EMIT_WIRES,
76
+ environmentCode,
71
77
  ...await (0, import_amd_utils.getCode)(runtimeEnvironment, serverData, version.replace(/\./g, "_"), bundleSpecifier, bundleRecord.includedModules, resourceRegistry),
72
78
  code
73
- ], context) : await (0, import_sandbox.default)([SSR_FLAG, EMIT_WIRES, code], context);
79
+ ], context, host) : await (0, import_sandbox.default)([EMIT_WIRES, environmentCode, code], context, host);
74
80
  const endTime = import_perf_hooks.performance.now();
75
81
  const timeDiff = endTime - startTime;
76
- import_shared_utils.logger.info(`[Server-side Rendering] ${specifier} in ${Math.round(timeDiff)} ms`);
82
+ import_diagnostics.logger.info(`[Server-side Rendering] ${specifier} in ${Math.round(timeDiff)} ms`);
77
83
  if (error)
78
84
  throw new Error(error);
79
85
  return {html: result, props, markup, cache};
@@ -1,17 +1,36 @@
1
+ var __create = Object.create;
1
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;
2
7
  var __markAsModule = (target) => __defProp(target, "__esModule", {value: true});
3
8
  var __export = (target, all) => {
4
9
  for (var name in all)
5
10
  __defProp(target, name, {get: all[name], enumerable: true});
6
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
+ };
7
23
 
8
24
  // packages/@lwrjs/lwc-ssr/src/utils/utils.ts
9
25
  __markAsModule(exports);
10
26
  __export(exports, {
27
+ createFetchEndowment: () => createFetchEndowment,
28
+ createSsrErrorMessage: () => createSsrErrorMessage,
11
29
  getWatchdogTime: () => getWatchdogTime,
12
30
  startWatchdogTimer: () => startWatchdogTimer,
13
31
  stopWatchdogTimer: () => stopWatchdogTimer
14
32
  });
33
+ var import_node_fetch = __toModule(require("node-fetch"));
15
34
  var DEFAULT_SSR_TIMEOUT = 5e3;
16
35
  function getWatchdogTime() {
17
36
  const override = process.env.SSR_TIMEOUT;
@@ -23,3 +42,18 @@ function startWatchdogTimer(callback, time) {
23
42
  function stopWatchdogTimer(timerId) {
24
43
  clearTimeout(timerId);
25
44
  }
45
+ function createFetchEndowment(host) {
46
+ if (!host) {
47
+ return import_node_fetch.default;
48
+ }
49
+ return (url, init) => {
50
+ const urlStr = typeof url === "string" ? url : url.toString();
51
+ if (urlStr.startsWith("/")) {
52
+ return (0, import_node_fetch.default)(host + url, init);
53
+ }
54
+ return (0, import_node_fetch.default)(url, init);
55
+ };
56
+ }
57
+ function createSsrErrorMessage(specifier, e) {
58
+ return `Server-side rendering for "${specifier}" failed. Falling back to client-side rendering. Reason: ${e.message || e}`;
59
+ }
@@ -27,9 +27,12 @@ __export(exports, {
27
27
  default: () => viewProvider_default
28
28
  });
29
29
  var import_base_view_provider = __toModule(require("@lwrjs/base-view-provider"));
30
+ var import_diagnostics = __toModule(require("@lwrjs/diagnostics"));
31
+ var import_instrumentation = __toModule(require("@lwrjs/instrumentation"));
30
32
  var import_shared_utils = __toModule(require("@lwrjs/shared-utils"));
31
33
  var import_identity = __toModule(require("../identity.cjs"));
32
34
  var import_ssr_element = __toModule(require("../utils/ssr-element.cjs"));
35
+ var import_utils = __toModule(require("../utils/utils.cjs"));
33
36
  var LwcViewProvider = class extends import_base_view_provider.default {
34
37
  constructor(pluginConfig, providerConfig) {
35
38
  super();
@@ -56,11 +59,38 @@ var LwcViewProvider = class extends import_base_view_provider.default {
56
59
  viewId,
57
60
  render: async (runtimeParams) => {
58
61
  const {moduleBundler, resourceRegistry, runtimeEnvironment} = this;
59
- const {html: renderedView, props} = await (0, import_ssr_element.ssrElement)({specifier: `${import_identity.LWC_SSR_PREFIX}${viewId.id}/${specifier}`, props: {}}, moduleBundler, resourceRegistry, this.routes, {runtimeEnvironment, runtimeParams});
60
- return {
61
- renderedView,
62
- metadata: {serverData: props, customElements: [], assetReferences: []}
63
- };
62
+ const debug = runtimeParams.query?.debug !== void 0;
63
+ const element = (0, import_shared_utils.moduleSpecifierToKebabCase)(specifier);
64
+ return (0, import_instrumentation.getTracer)().trace({
65
+ name: import_instrumentation.ViewSpan.RenderPage,
66
+ attributes: {specifier}
67
+ }, async () => {
68
+ try {
69
+ const {html, props} = await (0, import_ssr_element.ssrElement)({specifier: `${import_identity.LWC_SSR_PREFIX}${viewId.id}/${specifier}`, props: {}}, moduleBundler, resourceRegistry, this.routes, {
70
+ runtimeEnvironment: {...runtimeEnvironment, debug},
71
+ runtimeParams
72
+ });
73
+ return {
74
+ renderedView: html.replace(`<${element}`, `<${element} lwc:external`),
75
+ metadata: {serverData: props, customElements: [], assetReferences: []}
76
+ };
77
+ } catch (e) {
78
+ if (debug || (0, import_shared_utils.getFeatureFlags)().SSR_WITH_CSR_FALLBACK) {
79
+ const message = (0, import_utils.createSsrErrorMessage)(specifier, e);
80
+ import_diagnostics.logger.warn(message, e.stack);
81
+ return {
82
+ renderedView: `<${element} ${import_shared_utils.HYDRATE_DIRECTIVE}="${import_shared_utils.HYDRATE_CLIENT_VALUE}"></${element}>`,
83
+ metadata: {
84
+ serverDebug: {message: debug ? message : void 0},
85
+ customElements: [],
86
+ assetReferences: []
87
+ }
88
+ };
89
+ } else {
90
+ throw e;
91
+ }
92
+ }
93
+ });
64
94
  }
65
95
  };
66
96
  }
@@ -24,11 +24,16 @@ var __toModule = (module2) => {
24
24
  // packages/@lwrjs/lwc-ssr/src/viewTransformer/index.ts
25
25
  __markAsModule(exports);
26
26
  __export(exports, {
27
+ createFetchEndowment: () => import_utils2.createFetchEndowment,
27
28
  default: () => lwcSsrViewTransformer
28
29
  });
30
+ var import_diagnostics = __toModule(require("@lwrjs/diagnostics"));
31
+ var import_instrumentation = __toModule(require("@lwrjs/instrumentation"));
29
32
  var import_shared_utils = __toModule(require("@lwrjs/shared-utils"));
30
33
  var import_identity = __toModule(require("../identity.cjs"));
31
34
  var import_ssr_element = __toModule(require("../utils/ssr-element.cjs"));
35
+ var import_utils = __toModule(require("../utils/utils.cjs"));
36
+ var import_utils2 = __toModule(require("../utils/utils.cjs"));
32
37
  function lwcSsrViewTransformer(options, {config, moduleBundler, resourceRegistry}) {
33
38
  const routes = [...config.routes, ...config.errorRoutes];
34
39
  return {
@@ -38,8 +43,8 @@ function lwcSsrViewTransformer(options, {config, moduleBundler, resourceRegistry
38
43
  if (!viewContext.view.bootstrap?.ssr) {
39
44
  return {};
40
45
  }
41
- import_shared_utils.logger.debug("[lwcSsrViewTransformer] link");
42
- import_shared_utils.logger.verbose("[lwcSsrViewTransformer] link input", stringBuilder);
46
+ import_diagnostics.logger.debug("[lwcSsrViewTransformer] link");
47
+ import_diagnostics.logger.verbose("[lwcSsrViewTransformer] link input", stringBuilder);
43
48
  if (!metadata.serverData) {
44
49
  metadata.serverData = {};
45
50
  }
@@ -54,7 +59,7 @@ function lwcSsrViewTransformer(options, {config, moduleBundler, resourceRegistry
54
59
  const {startOffset, endOffset} = location;
55
60
  stringBuilder.overwrite(startOffset, endOffset, `<${tagName}></${tagName}>`);
56
61
  }
57
- if (!isCsr && location) {
62
+ if (!isCsr && !props?.["lwc:external"] && location) {
58
63
  const {startOffset, endOffset} = location;
59
64
  const moduleSpecifier = (0, import_shared_utils.kebabCaseToModuleSpecifier)(tagName);
60
65
  ssrModules.push({
@@ -81,41 +86,46 @@ function lwcSsrViewTransformer(options, {config, moduleBundler, resourceRegistry
81
86
  const hydrate = (0, import_shared_utils.isHydrateOnLoad)(rawProps);
82
87
  const passProps = {...rawProps};
83
88
  delete passProps[import_shared_utils.HYDRATE_DIRECTIVE];
84
- return (0, import_ssr_element.ssrElement)({specifier: bootstrapSpecifier, props: passProps, serverData}, moduleBundler, resourceRegistry, routes, viewContext).then(({
85
- html,
86
- props = {},
87
- markup: {links = []} = {links: []},
88
- cache: {ttl} = {}
89
- }) => {
90
- pageTtl = (0, import_shared_utils.shortestTtl)(ttl, pageTtl);
91
- let propsAttr = "";
92
- if (hydrate) {
93
- const propsId = (0, import_identity.getPropsId)();
94
- propsAttr = ` ${import_identity.SSR_PROPS_ATTR}="${propsId}"`;
95
- serverData[propsId] = props;
96
- }
97
- const [, remain] = html.split(`<${tagName}`);
98
- html = [`<${tagName}`, propsAttr, remain].join("");
99
- links.forEach(({href, rel, as, fetchpriority}) => {
100
- const relStr = rel ? ` rel="${rel}"` : "", asStr = as ? ` as="${as}"` : "", fetchStr = fetchpriority ? ` fetchpriority="${fetchpriority}"` : "";
101
- ssrLinks += `<link href="${href}"${relStr}${asStr}${fetchStr}>
89
+ return (0, import_instrumentation.getTracer)().trace({
90
+ name: import_instrumentation.ViewSpan.RenderIsland,
91
+ attributes: {specifier}
92
+ }, async () => {
93
+ return (0, import_ssr_element.ssrElement)({specifier: bootstrapSpecifier, props: passProps, serverData}, moduleBundler, resourceRegistry, routes, viewContext).then(({
94
+ html,
95
+ props = {},
96
+ markup: {links = []} = {links: []},
97
+ cache: {ttl} = {}
98
+ }) => {
99
+ pageTtl = (0, import_shared_utils.shortestTtl)(ttl, pageTtl);
100
+ let propsAttr = "";
101
+ if (hydrate) {
102
+ const propsId = (0, import_identity.getPropsId)();
103
+ propsAttr = ` ${import_identity.SSR_PROPS_ATTR}="${propsId}"`;
104
+ serverData[propsId] = props;
105
+ }
106
+ const [, remain] = html.split(`<${tagName}`);
107
+ html = [`<${tagName}`, propsAttr, remain].join("");
108
+ links.forEach(({href, rel, as, fetchpriority}) => {
109
+ const relStr = rel ? ` rel="${rel}"` : "", asStr = as ? ` as="${as}"` : "", fetchStr = fetchpriority ? ` fetchpriority="${fetchpriority}"` : "";
110
+ ssrLinks += `<link href="${href}"${relStr}${asStr}${fetchStr}>
102
111
  `;
103
- });
104
- stringBuilder.overwrite(startOffset, endOffset, html);
105
- }).catch((err) => {
106
- if (debug || (0, import_shared_utils.getFeatureFlags)().SSR_WITH_CSR_FALLBACK) {
107
- customElements[index].props === void 0 ? customElements[index].props = {
108
- [import_shared_utils.HYDRATE_DIRECTIVE]: import_shared_utils.HYDRATE_CLIENT_VALUE
109
- } : customElements[index].props[import_shared_utils.HYDRATE_DIRECTIVE] = import_shared_utils.HYDRATE_CLIENT_VALUE;
110
- const errMessage = `Server-side rendering for "${specifier}" failed. Falling back to client-side rendering. Reason: `;
111
- import_shared_utils.logger.warn(errMessage, err.stack);
112
- if (debug) {
113
- debugMessage = `${errMessage}${err.message || err}".`;
114
- serverDebug.message = debugMessage;
112
+ });
113
+ stringBuilder.overwrite(startOffset, endOffset, html);
114
+ }).catch((err) => {
115
+ if (debug || (0, import_shared_utils.getFeatureFlags)().SSR_WITH_CSR_FALLBACK) {
116
+ customElements[index].props === void 0 ? customElements[index].props = {
117
+ [import_shared_utils.HYDRATE_DIRECTIVE]: import_shared_utils.HYDRATE_CLIENT_VALUE
118
+ } : customElements[index].props[import_shared_utils.HYDRATE_DIRECTIVE] = import_shared_utils.HYDRATE_CLIENT_VALUE;
119
+ const errMessage = (0, import_utils.createSsrErrorMessage)(specifier, err);
120
+ import_diagnostics.logger.warn(errMessage, err.stack);
121
+ if (debug) {
122
+ debugMessage = errMessage;
123
+ serverDebug.message = debugMessage;
124
+ }
125
+ } else {
126
+ throw err;
115
127
  }
116
- } else {
117
- throw err;
118
- }
128
+ });
119
129
  });
120
130
  }));
121
131
  if (ssrLinks) {
@@ -123,10 +133,10 @@ function lwcSsrViewTransformer(options, {config, moduleBundler, resourceRegistry
123
133
  if (headIndex >= 0) {
124
134
  stringBuilder.prependLeft(headIndex, ssrLinks);
125
135
  } else {
126
- import_shared_utils.logger.error("Adding links during server-side rendering failed. Could not find the </head> tag.");
136
+ import_diagnostics.logger.error("Adding links during server-side rendering failed. Could not find the </head> tag.");
127
137
  }
128
138
  }
129
- import_shared_utils.logger.verbose("lwcSsrViewTransformer response", stringBuilder);
139
+ import_diagnostics.logger.verbose("lwcSsrViewTransformer response", stringBuilder);
130
140
  return {cache: {ttl: pageTtl}};
131
141
  }
132
142
  };
@@ -23,14 +23,14 @@ export function createSsrBootstrapModule(rootSpecifier, services) {
23
23
  serviceImports += `\nimport ${importName} from '${service}';`;
24
24
  serviceCalls += `\n
25
25
  globalThis.trace({
26
- name: '${ViewSpan.SsrBootstrapService}',
26
+ name: '${ViewSpan.BootstrapService}',
27
27
  attributes: {
28
28
  specifier: '${rootSpecifier}',
29
29
  serviceSpecifier: '${service}',
30
30
  }
31
31
  }, () => {
32
32
  try {
33
- ${importName}();
33
+ ${importName}({ serverData });
34
34
  } catch(e) {
35
35
  const message = e.message || e;
36
36
  // we need to re-throw with the service specifier in the error message
@@ -43,7 +43,15 @@ import { renderComponent } from '@lwc/engine-server';
43
43
  import Ctor, * as rootComponent from '${rootSpecifier}';${serviceImports}
44
44
 
45
45
  (async () => {
46
- let result, props, markup, existingTaskCount;
46
+ let result, props, markup, serverData = globalThis.LWR?.serverData || {}, existingTaskCount;
47
+ // remove the LWR global properties before any customer code is run
48
+ if (globalThis.LWR?.define) {
49
+ // AMD: support dynamic imports in getServerData
50
+ globalThis.LWR = Object.freeze({ define: globalThis.LWR.define });
51
+ } else {
52
+ delete globalThis.LWR; // ESM
53
+ }
54
+
47
55
  try {
48
56
  // 1. setup page data
49
57
  const context = globalThis.getContext();
@@ -63,10 +71,8 @@ import Ctor, * as rootComponent from '${rootSpecifier}';${serviceImports}
63
71
  throw new Error('Error in "getServerData" for "${rootSpecifier}": ' + message);
64
72
  }
65
73
  });
66
-
67
74
  props = data.props; // overwrite public props
68
- globalThis.LWR = globalThis.LWR || { serverData: {} };
69
- Object.assign(globalThis.LWR.serverData, data.props); // add props to server data
75
+ Object.assign(serverData, data.props); // add props to server data
70
76
  markup = data.markup;
71
77
  }${serviceCalls}
72
78
 
@@ -84,8 +90,8 @@ import Ctor, * as rootComponent from '${rootSpecifier}';${serviceImports}
84
90
 
85
91
  } catch(e) {
86
92
  const message = e.message || e;
87
- // Relay the LWC rendering stack
88
-
93
+
94
+ // add the LWC rendering stack
89
95
  const error = e.wcStack ?
90
96
  'An error occurred during server-side rendering for component stack: ' + e.wcStack + '. Error was: ' + message :
91
97
  'An error occured during server-side rendering: ' + message;
@@ -97,7 +103,6 @@ import Ctor, * as rootComponent from '${rootSpecifier}';${serviceImports}
97
103
  const currentTaskCount = process.getActiveResourcesInfo
98
104
  ? process.getActiveResourcesInfo().length
99
105
  : 0;
100
-
101
106
  if (currentTaskCount - existingTaskCount > 0) {
102
107
  console.warn('[warn] async tasks encountered while server rendering "${rootSpecifier}"');
103
108
  }
@@ -1,6 +1,6 @@
1
- import type { BundleDefinition, Json, PublicModuleBundler, PublicResourceRegistry, RuntimeEnvironment, RuntimeParams } from '@lwrjs/types';
1
+ import type { BundleDefinition, PublicModuleBundler, PublicResourceRegistry, RuntimeEnvironment, RuntimeParams, ServerData } from '@lwrjs/types';
2
2
  import type { Route } from '../identity.js';
3
- export declare function getCode(runtimeEnvironment: RuntimeEnvironment, serverData: Json, lwrVersion: string, bundleSpecifier: string, includedModules: string[], resourceRegistry: PublicResourceRegistry): Promise<string[]>;
3
+ export declare function getCode(runtimeEnvironment: RuntimeEnvironment, serverData: ServerData, lwrVersion: string, bundleSpecifier: string, includedModules: string[], resourceRegistry: PublicResourceRegistry): Promise<string[]>;
4
4
  type PartialBundleDefinition = Pick<BundleDefinition, 'bundleRecord' | 'code' | 'specifier' | 'version'>;
5
5
  export declare function getBundle(specifier: string, // e.g. "@lwrjs/lwc-ssr/home/root/component"
6
6
  moduleBundler: PublicModuleBundler, routes: Route[], runtimeEnvironment: RuntimeEnvironment, runtimeParams: RuntimeParams): Promise<PartialBundleDefinition>;
@@ -77,7 +77,9 @@ export async function getBundle(specifier, // e.g. "@lwrjs/lwc-ssr/home/root/com
77
77
  moduleBundler, routes, runtimeEnvironment, runtimeParams) {
78
78
  if (getFeatureFlags().SSR_STATIC_BUNDLES) {
79
79
  // concatenate existing bundles SSGed at build time
80
- return buildBundle(specifier, moduleBundler, routes, runtimeEnvironment, runtimeParams);
80
+ return buildBundle(specifier, moduleBundler, routes,
81
+ // debug: false needed to force SSR to not use debug bundles
82
+ { ...runtimeEnvironment, debug: false }, runtimeParams);
81
83
  }
82
84
  // create a new SSR bundle
83
85
  // "lwc" is aliased as "@lwc/engine-server" in the AMD worker code
@@ -90,31 +92,45 @@ async function bundle(specifier, moduleBundler, runtimeEnvironment, runtimeParam
90
92
  return await moduleBundler.getModuleBundle({ specifier }, runtimeEnvironment, runtimeParams, bundleConfigOverrides);
91
93
  }
92
94
  // Recursively bundle the static imports of a root bundle into a single bundle
93
- async function bundleImports(bundleCode, imports = [], visited, moduleBundler, runtimeEnvironment, runtimeParams) {
95
+ async function bundleImports(bundleCode, dynamicImports = [], imports = [], visited, moduleBundler, runtimeEnvironment, runtimeParams) {
94
96
  for (const { specifier } of imports) {
95
97
  if (!visited.has(specifier)) {
96
98
  visited.add(specifier);
97
99
  // eslint-disable-next-line no-await-in-loop
98
100
  const { code, bundleRecord, version } = await bundle(specifier, moduleBundler, runtimeEnvironment, runtimeParams);
99
- let bundledCode;
100
- if (runtimeEnvironment.featureFlags?.EXPERIMENTAL_UNVERSIONED_ALIASES) {
101
- const versionedSpecifier = getSpecifier({ specifier, version });
102
- const aliasCode = createAmdAlias(versionedSpecifier, specifier);
103
- bundledCode = [code, aliasCode].filter(Boolean).join('');
104
- }
105
- else {
106
- bundledCode = code;
107
- }
101
+ const bundledCode = getBundledCode(runtimeEnvironment, specifier, version, code);
108
102
  bundleCode =
109
103
  // eslint-disable-next-line no-await-in-loop
110
- (await bundleImports(bundledCode, bundleRecord.imports, visited, moduleBundler, runtimeEnvironment, runtimeParams)) + bundleCode;
104
+ (await bundleImports(bundledCode, bundleRecord.dynamicImports, bundleRecord.imports, visited, moduleBundler, runtimeEnvironment, runtimeParams)) + bundleCode;
105
+ }
106
+ }
107
+ // Pull in one level of dynamic imports
108
+ for (const { specifier } of dynamicImports) {
109
+ if (!visited.has(specifier)) {
110
+ visited.add(specifier);
111
+ // eslint-disable-next-line no-await-in-loop
112
+ const { code, version } = await bundle(specifier, moduleBundler, runtimeEnvironment, runtimeParams);
113
+ // Add Dynamic Code
114
+ bundleCode += getBundledCode(runtimeEnvironment, specifier, version, code);
111
115
  }
112
116
  }
113
117
  return bundleCode;
114
118
  }
119
+ function getBundledCode(runtimeEnvironment, specifier, version, code) {
120
+ let bundledCode;
121
+ if (runtimeEnvironment.featureFlags?.EXPERIMENTAL_UNVERSIONED_ALIASES) {
122
+ const versionedSpecifier = getSpecifier({ specifier, version });
123
+ const aliasCode = createAmdAlias(versionedSpecifier, specifier);
124
+ bundledCode = [code, aliasCode].filter(Boolean).join('');
125
+ }
126
+ else {
127
+ bundledCode = code;
128
+ }
129
+ return bundledCode;
130
+ }
115
131
  async function getBundleWithImports(specifier, moduleBundler, runtimeEnvironment, runtimeParams) {
116
132
  const { code, bundleRecord } = await bundle(specifier, moduleBundler, runtimeEnvironment, runtimeParams);
117
- return await bundleImports(code, bundleRecord.imports, new Set(['lwc', specifier]), // visited (lwc is excluded)
133
+ return await bundleImports(code, bundleRecord.dynamicImports, bundleRecord.imports, new Set(['lwc', specifier]), // visited (lwc is excluded)
118
134
  moduleBundler, runtimeEnvironment, runtimeParams);
119
135
  }
120
136
  // Build a SSR bundle for a root component by concatenating:
@@ -1,4 +1,4 @@
1
1
  import type { SsrRequestContext } from '@lwrjs/types';
2
2
  import type { SandboxResults } from './sandbox.js';
3
- export default function runCode(codes: string[], context: SsrRequestContext): Promise<SandboxResults>;
3
+ export default function runCode(codes: string[], context: SsrRequestContext, host?: string): Promise<SandboxResults>;
4
4
  //# sourceMappingURL=sandbox-locker.d.ts.map
@@ -1,10 +1,9 @@
1
1
  import { getTracer } from '@lwrjs/instrumentation';
2
2
  import createVirtualEnvironment from '@locker/near-membrane-node';
3
- import fetch from 'node-fetch';
4
3
  import { webcrypto as crypto } from 'crypto';
5
- import { URL } from 'url';
6
- import { getWatchdogTime, startWatchdogTimer, stopWatchdogTimer } from './utils.js';
7
- export default function runCode(codes, context) {
4
+ import { URL, URLSearchParams } from 'url';
5
+ import { createFetchEndowment, getWatchdogTime, startWatchdogTimer, stopWatchdogTimer } from './utils.js';
6
+ export default function runCode(codes, context, host) {
8
7
  return new Promise((resolve, reject) => {
9
8
  let resolver;
10
9
  const p = new Promise((r) => (resolver = r));
@@ -18,9 +17,10 @@ export default function runCode(codes, context) {
18
17
  crypto,
19
18
  getContext,
20
19
  trace,
21
- fetch,
20
+ fetch: createFetchEndowment(host),
22
21
  resolver,
23
22
  URL,
23
+ URLSearchParams,
24
24
  // for AMD loader ModuleRegistry
25
25
  process,
26
26
  setTimeout,
@@ -5,5 +5,5 @@ import type { SandboxResults } from './sandbox.js';
5
5
  * @param codes - Code strings which SSR a root component
6
6
  * @returns a promise to the SSRed code string, or an error message
7
7
  */
8
- export default function runCode(codes: string[], workerData: SsrRequestContext): Promise<SandboxResults>;
8
+ export default function runCode(codes: string[], workerData: SsrRequestContext, host?: string): Promise<SandboxResults>;
9
9
  //# sourceMappingURL=sandbox-worker.d.ts.map
@@ -14,8 +14,9 @@ const HEADER = '/* This module is generated and meant to be used in a Server con
14
14
  */
15
15
  const WORKER_CODE_SANDBOX_APIS = [
16
16
  `const { parentPort, workerData } = require('worker_threads');`,
17
+ `globalThis.fetch = require('@lwrjs/lwc-ssr/viewTransformer').createFetchEndowment(workerData.host);`,
18
+ `delete workerData.host`,
17
19
  `globalThis.getContext = () => workerData;`,
18
- `globalThis.fetch = require('node-fetch');`,
19
20
  `globalThis.crypto = require('crypto').webcrypto;`,
20
21
  `globalThis.resolver = (...args) => parentPort.postMessage(...args);`,
21
22
  // TODO: implement tracing with worker thread
@@ -26,11 +27,11 @@ const WORKER_CODE_SANDBOX_APIS = [
26
27
  * @param codes - Code strings which SSR a root component
27
28
  * @returns a promise to the SSRed code string, or an error message
28
29
  */
29
- export default function runCode(codes, workerData) {
30
+ export default function runCode(codes, workerData, host) {
30
31
  const time = getWatchdogTime();
31
32
  const workerCode = [HEADER, ...WORKER_CODE_SANDBOX_APIS, ...codes].join('\n');
32
33
  return new Promise((resolve, reject) => {
33
- const worker = new Worker(workerCode, { eval: true, workerData });
34
+ const worker = new Worker(workerCode, { eval: true, workerData: { ...workerData, host } });
34
35
  const timerId = startWatchdogTimer(() => {
35
36
  worker.terminate();
36
37
  reject(new Error(`SSR timed out after ${time}ms`));
@@ -3,5 +3,5 @@ export interface SandboxResults extends SsrDataResponse {
3
3
  result?: string;
4
4
  error?: string;
5
5
  }
6
- export default function runCode(codes: string[], context: SsrRequestContext): Promise<SandboxResults>;
6
+ export default function runCode(codes: string[], context: SsrRequestContext, host?: string): Promise<SandboxResults>;
7
7
  //# sourceMappingURL=sandbox.d.ts.map
@@ -1,11 +1,11 @@
1
1
  import runCodeOnWorker from './sandbox-worker.js';
2
2
  import runCodeOnLocker from './sandbox-locker.js';
3
3
  import { getFeatureFlags } from '@lwrjs/shared-utils';
4
- export default function runCode(codes, context) {
4
+ export default function runCode(codes, context, host) {
5
5
  if (getFeatureFlags().SSR_SANDBOX_WORKER) {
6
- return runCodeOnWorker(codes, context);
6
+ return runCodeOnWorker(codes, context, host);
7
7
  }
8
8
  // Default to use locker because it works rolled up in MRT
9
- return runCodeOnLocker(codes, context);
9
+ return runCodeOnLocker(codes, context, host);
10
10
  }
11
11
  //# sourceMappingURL=sandbox.js.map
@@ -1,4 +1,4 @@
1
- import type { Json, PublicModuleBundler, PublicResourceRegistry, SsrDataResponse, ViewTransformPluginContext } from '@lwrjs/types';
1
+ import type { Json, PublicModuleBundler, PublicResourceRegistry, ServerData, SsrDataResponse, ViewTransformPluginContext } from '@lwrjs/types';
2
2
  import type { Route } from '../identity.js';
3
3
  interface SsrResults extends SsrDataResponse {
4
4
  html: string;
@@ -16,9 +16,7 @@ export declare function ssrElement({ specifier, props: templateProps, serverData
16
16
  props: {
17
17
  [prop: string]: Json;
18
18
  };
19
- serverData?: {
20
- [prop: string]: Json;
21
- };
19
+ serverData?: ServerData;
22
20
  }, moduleBundler: PublicModuleBundler, resourceRegistry: PublicResourceRegistry, routes: Route[], { runtimeEnvironment, runtimeParams }: ViewTransformPluginContext): Promise<SsrResults>;
23
21
  export {};
24
22
  //# sourceMappingURL=ssr-element.d.ts.map
@@ -1,9 +1,9 @@
1
1
  import { getBundle, getCode } from './amd-utils.js';
2
2
  import runCode from './sandbox.js';
3
3
  import { performance } from 'perf_hooks';
4
- import { logger } from '@lwrjs/shared-utils';
4
+ import { logger } from '@lwrjs/diagnostics';
5
5
  import { getTracer, ViewSpan } from '@lwrjs/instrumentation';
6
- const SSR_FLAG = 'process.env.SSR = true;'; // Used for import.meta.env.SSR
6
+ import { buildEnvironmentContext } from '@lwrjs/shared-utils';
7
7
  const EMIT_WIRES = `globalThis.lwcRuntimeFlags = { ENABLE_WIRE_SYNC_EMIT: true };`; // Invoke wire adapters immediately after a component is connected
8
8
  /**
9
9
  * Create a bundle for the given SSR module and run the code in a sandbox.
@@ -34,9 +34,18 @@ export async function ssrElement({ specifier, props: templateProps, serverData =
34
34
  url: runtimeParams.url,
35
35
  params: runtimeParams.params || {},
36
36
  query: runtimeParams.query || {},
37
- locale: runtimeParams.locale || runtimeEnvironment.defaultLocale,
37
+ locale: runtimeParams.locale || runtimeEnvironment.i18n.defaultLocale,
38
38
  basePath: runtimeParams.basePath || runtimeEnvironment.basePath,
39
39
  };
40
+ const environment = {
41
+ // Used for import.meta.env.SSR
42
+ // Note: SSR is always true because this script is executed on the server
43
+ SSR: true,
44
+ // Used by `lwr/environment`
45
+ ...buildEnvironmentContext(runtimeParams),
46
+ };
47
+ const environmentCode = `process.env = ${JSON.stringify(environment)}`;
48
+ const host = runtimeParams.host;
40
49
  // Get the SSR string and properties bag
41
50
  const startTime = performance.now();
42
51
  return getTracer().trace({
@@ -47,12 +56,12 @@ export async function ssrElement({ specifier, props: templateProps, serverData =
47
56
  }, async () => {
48
57
  const { result, props, markup, cache, error } = format === 'amd'
49
58
  ? await runCode([
50
- SSR_FLAG,
51
59
  EMIT_WIRES,
60
+ environmentCode,
52
61
  ...(await getCode(runtimeEnvironment, serverData, version.replace(/\./g, '_'), bundleSpecifier, bundleRecord.includedModules, resourceRegistry)),
53
62
  code,
54
- ], context)
55
- : await runCode([SSR_FLAG, EMIT_WIRES, code], context);
63
+ ], context, host)
64
+ : await runCode([EMIT_WIRES, environmentCode, code], context, host);
56
65
  const endTime = performance.now();
57
66
  const timeDiff = endTime - startTime;
58
67
  logger.info(`[Server-side Rendering] ${specifier} in ${Math.round(timeDiff)} ms`);
@@ -1,4 +1,7 @@
1
+ import type { Response, RequestInfo, RequestInit } from 'node-fetch';
1
2
  export declare function getWatchdogTime(): number;
2
3
  export declare function startWatchdogTimer(callback: Function, time: number): number;
3
4
  export declare function stopWatchdogTimer(timerId: number): void;
5
+ export declare function createFetchEndowment(host?: string): (url: RequestInfo, init?: RequestInit) => Promise<Response>;
6
+ export declare function createSsrErrorMessage(specifier: string, e: Error): string;
4
7
  //# sourceMappingURL=utils.d.ts.map
@@ -1,3 +1,4 @@
1
+ import fetch from 'node-fetch';
1
2
  const DEFAULT_SSR_TIMEOUT = 5000; // 5 seconds, override with process.env.SSR_TIMEOUT
2
3
  export function getWatchdogTime() {
3
4
  const override = process.env.SSR_TIMEOUT;
@@ -9,4 +10,23 @@ export function startWatchdogTimer(callback, time) {
9
10
  export function stopWatchdogTimer(timerId) {
10
11
  clearTimeout(timerId);
11
12
  }
13
+ export function createFetchEndowment(host) {
14
+ if (!host) {
15
+ // never proxy relative URLs
16
+ return fetch;
17
+ }
18
+ return (url, init) => {
19
+ // handle all stringable types, eg: URL
20
+ const urlStr = typeof url === 'string' ? url : url.toString();
21
+ if (urlStr.startsWith('/')) {
22
+ // proxy relative URLs through the given host
23
+ return fetch(host + url, init);
24
+ }
25
+ // carry on
26
+ return fetch(url, init);
27
+ };
28
+ }
29
+ export function createSsrErrorMessage(specifier, e) {
30
+ return `Server-side rendering for "${specifier}" failed. Falling back to client-side rendering. Reason: ${e.message || e}`;
31
+ }
12
32
  //# sourceMappingURL=utils.js.map
@@ -1,7 +1,10 @@
1
1
  import BaseViewProvider from '@lwrjs/base-view-provider';
2
- import { hashContent, isSpecifier, slugify } from '@lwrjs/shared-utils';
2
+ import { logger } from '@lwrjs/diagnostics';
3
+ import { ViewSpan, getTracer } from '@lwrjs/instrumentation';
4
+ import { HYDRATE_CLIENT_VALUE, HYDRATE_DIRECTIVE, getFeatureFlags, hashContent, isSpecifier, moduleSpecifierToKebabCase, slugify, } from '@lwrjs/shared-utils';
3
5
  import { LWC_SSR_PREFIX } from '../identity.js';
4
6
  import { ssrElement } from '../utils/ssr-element.js';
7
+ import { createSsrErrorMessage } from '../utils/utils.js';
5
8
  export default class LwcViewProvider extends BaseViewProvider {
6
9
  constructor(pluginConfig, providerConfig) {
7
10
  super();
@@ -30,11 +33,43 @@ export default class LwcViewProvider extends BaseViewProvider {
30
33
  render: async (runtimeParams) => {
31
34
  // SSR the root component (without passing any public properties)
32
35
  const { moduleBundler, resourceRegistry, runtimeEnvironment } = this;
33
- const { html: renderedView, props } = await ssrElement({ specifier: `${LWC_SSR_PREFIX}${viewId.id}/${specifier}`, props: {} }, moduleBundler, resourceRegistry, this.routes, { runtimeEnvironment, runtimeParams });
34
- return {
35
- renderedView,
36
- metadata: { serverData: props, customElements: [], assetReferences: [] },
37
- };
36
+ const debug = runtimeParams.query?.debug !== undefined;
37
+ const element = moduleSpecifierToKebabCase(specifier);
38
+ return getTracer().trace({
39
+ name: ViewSpan.RenderPage,
40
+ attributes: { specifier },
41
+ }, async () => {
42
+ try {
43
+ const { html, props } = await ssrElement({ specifier: `${LWC_SSR_PREFIX}${viewId.id}/${specifier}`, props: {} }, moduleBundler, resourceRegistry, this.routes, {
44
+ runtimeEnvironment: { ...runtimeEnvironment, debug },
45
+ runtimeParams,
46
+ });
47
+ return {
48
+ // add "lwc:external" to the contentTemplate component to mark it as already processed
49
+ renderedView: html.replace(`<${element}`, `<${element} lwc:external`),
50
+ metadata: { serverData: props, customElements: [], assetReferences: [] },
51
+ };
52
+ }
53
+ catch (e) {
54
+ if (debug || getFeatureFlags().SSR_WITH_CSR_FALLBACK) {
55
+ // fallback to CSR
56
+ const message = createSsrErrorMessage(specifier, e);
57
+ logger.warn(message, e.stack);
58
+ return {
59
+ renderedView: `<${element} ${HYDRATE_DIRECTIVE}="${HYDRATE_CLIENT_VALUE}"></${element}>`,
60
+ // send an error message to the client if debug mode is on
61
+ metadata: {
62
+ serverDebug: { message: debug ? message : undefined },
63
+ customElements: [],
64
+ assetReferences: [],
65
+ },
66
+ };
67
+ }
68
+ else {
69
+ throw e;
70
+ }
71
+ }
72
+ });
38
73
  },
39
74
  };
40
75
  }
@@ -1,4 +1,5 @@
1
1
  import type { ProviderContext, ViewTransformPlugin } from '@lwrjs/types';
2
+ export { createFetchEndowment } from '../utils/utils.js';
2
3
  interface SsrPluginOptions {
3
4
  shareWorker?: boolean;
4
5
  }
@@ -21,5 +22,4 @@ interface SsrPluginOptions {
21
22
  * 5. During bootstrap on the client, the "lwr/init" module will hydrate ALL the custom elements on the page
22
23
  */
23
24
  export default function lwcSsrViewTransformer(options: SsrPluginOptions, { config, moduleBundler, resourceRegistry }: ProviderContext): ViewTransformPlugin;
24
- export {};
25
25
  //# sourceMappingURL=index.d.ts.map
@@ -1,6 +1,10 @@
1
- import { HYDRATE_CLIENT_VALUE, HYDRATE_DIRECTIVE, getFeatureFlags, isCsrIsland, isHydrateOnLoad, kebabCaseToModuleSpecifier, logger, shortestTtl, } from '@lwrjs/shared-utils';
1
+ import { logger } from '@lwrjs/diagnostics';
2
+ import { ViewSpan, getTracer } from '@lwrjs/instrumentation';
3
+ import { HYDRATE_CLIENT_VALUE, HYDRATE_DIRECTIVE, getFeatureFlags, isCsrIsland, isHydrateOnLoad, kebabCaseToModuleSpecifier, shortestTtl, } from '@lwrjs/shared-utils';
2
4
  import { LWC_SSR_PREFIX, SSR_PROPS_ATTR, getPropsId } from '../identity.js';
3
5
  import { ssrElement } from '../utils/ssr-element.js';
6
+ import { createSsrErrorMessage } from '../utils/utils.js';
7
+ export { createFetchEndowment } from '../utils/utils.js';
4
8
  /**
5
9
  * This is a view transformer run by the view registry during linking of a page document/route (configured in lwr.config.json[routes]).
6
10
  * If the "ssr" bootstrap flag is on for the route, it will server-side render (SSR) each custom element found in the page HTML.
@@ -46,7 +50,8 @@ export default function lwcSsrViewTransformer(options, { config, moduleBundler,
46
50
  const { startOffset, endOffset } = location;
47
51
  stringBuilder.overwrite(startOffset, endOffset, `<${tagName}></${tagName}>`);
48
52
  }
49
- if (!isCsr && location) {
53
+ if (!isCsr && !props?.['lwc:external'] && location) {
54
+ // only SSR the custom elements which are NOT CSR islands or lwc:external
50
55
  const { startOffset, endOffset } = location;
51
56
  const moduleSpecifier = kebabCaseToModuleSpecifier(tagName);
52
57
  ssrModules.push({
@@ -66,57 +71,62 @@ export default function lwcSsrViewTransformer(options, { config, moduleBundler,
66
71
  const hydrate = isHydrateOnLoad(rawProps);
67
72
  const passProps = { ...rawProps };
68
73
  delete passProps[HYDRATE_DIRECTIVE];
69
- return ssrElement({ specifier: bootstrapSpecifier, props: passProps, serverData }, moduleBundler, resourceRegistry, routes, viewContext)
70
- .then(({ html, props = {}, markup: { links = [] } = { links: [] }, cache: { ttl } = {}, }) => {
71
- // Keep track of the shortest TTL from all getServerData hooks
72
- pageTtl = shortestTtl(ttl, pageTtl);
73
- // Add the props id to the HTML for the custom element
74
- // eg: <some-cmp> -> <some-cmp data-lwr-props-id="1234">
75
- let propsAttr = '';
76
- if (hydrate) {
77
- // Only serialize props for custom elements that are to be hydrated
78
- const propsId = getPropsId();
79
- propsAttr = ` ${SSR_PROPS_ATTR}="${propsId}"`;
80
- serverData[propsId] = props;
81
- }
82
- const [, remain] = html.split(`<${tagName}`);
83
- html = [`<${tagName}`, propsAttr, remain].join('');
84
- // Create HTML <link> strings for each item in the links array
85
- links.forEach(({ href, rel, as, fetchpriority }) => {
86
- const relStr = rel ? ` rel="${rel}"` : '', asStr = as ? ` as="${as}"` : '', fetchStr = fetchpriority
87
- ? ` fetchpriority="${fetchpriority}"`
88
- : '';
89
- ssrLinks += `<link href="${href}"${relStr}${asStr}${fetchStr}>\n`;
90
- });
91
- // Overwrite the custom element with the SSRed component string
92
- stringBuilder.overwrite(startOffset, endOffset, html);
93
- })
94
- .catch((err) => {
95
- // fallback to CSR in debug mode or if enabled
96
- if (debug || getFeatureFlags().SSR_WITH_CSR_FALLBACK) {
97
- // Fallback to CSR by adding lwr:hydrate="client-only" to the custom element
98
- // This ENSURES the component's JavaScript gets sent to the client for CSRing
99
- customElements[index].props === undefined
100
- ? (customElements[index].props = {
101
- [HYDRATE_DIRECTIVE]: HYDRATE_CLIENT_VALUE,
102
- })
103
- : // eslint-disable-next-line @typescript-eslint/ban-ts-comment
104
- // @ts-ignore - TS thinks that props may still be undefined
105
- (customElements[index].props[HYDRATE_DIRECTIVE] =
106
- HYDRATE_CLIENT_VALUE);
107
- const errMessage = `Server-side rendering for "${specifier}" failed. Falling back to client-side rendering. Reason: `;
108
- // log message w/ stack details
109
- logger.warn(errMessage, err.stack);
110
- // Inform the client of the failing modules w/o exposing any additional
111
- // details (such as callstack) for security reasons
112
- if (debug) {
113
- debugMessage = `${errMessage}${err.message || err}".`;
114
- serverDebug.message = debugMessage;
74
+ return getTracer().trace({
75
+ name: ViewSpan.RenderIsland,
76
+ attributes: { specifier },
77
+ }, async () => {
78
+ return ssrElement({ specifier: bootstrapSpecifier, props: passProps, serverData }, moduleBundler, resourceRegistry, routes, viewContext)
79
+ .then(({ html, props = {}, markup: { links = [] } = { links: [] }, cache: { ttl } = {}, }) => {
80
+ // Keep track of the shortest TTL from all getServerData hooks
81
+ pageTtl = shortestTtl(ttl, pageTtl);
82
+ // Add the props id to the HTML for the custom element
83
+ // eg: <some-cmp> -> <some-cmp data-lwr-props-id="1234">
84
+ let propsAttr = '';
85
+ if (hydrate) {
86
+ // Only serialize props for custom elements that are to be hydrated
87
+ const propsId = getPropsId();
88
+ propsAttr = ` ${SSR_PROPS_ATTR}="${propsId}"`;
89
+ serverData[propsId] = props;
90
+ }
91
+ const [, remain] = html.split(`<${tagName}`);
92
+ html = [`<${tagName}`, propsAttr, remain].join('');
93
+ // Create HTML <link> strings for each item in the links array
94
+ links.forEach(({ href, rel, as, fetchpriority }) => {
95
+ const relStr = rel ? ` rel="${rel}"` : '', asStr = as ? ` as="${as}"` : '', fetchStr = fetchpriority
96
+ ? ` fetchpriority="${fetchpriority}"`
97
+ : '';
98
+ ssrLinks += `<link href="${href}"${relStr}${asStr}${fetchStr}>\n`;
99
+ });
100
+ // Overwrite the custom element with the SSRed component string
101
+ stringBuilder.overwrite(startOffset, endOffset, html);
102
+ })
103
+ .catch((err) => {
104
+ // fallback to CSR in debug mode or if enabled
105
+ if (debug || getFeatureFlags().SSR_WITH_CSR_FALLBACK) {
106
+ // Fallback to CSR by adding lwr:hydrate="client-only" to the custom element
107
+ // This ENSURES the component's JavaScript gets sent to the client for CSRing
108
+ customElements[index].props === undefined
109
+ ? (customElements[index].props = {
110
+ [HYDRATE_DIRECTIVE]: HYDRATE_CLIENT_VALUE,
111
+ })
112
+ : // eslint-disable-next-line @typescript-eslint/ban-ts-comment
113
+ // @ts-ignore - TS thinks that props may still be undefined
114
+ (customElements[index].props[HYDRATE_DIRECTIVE] =
115
+ HYDRATE_CLIENT_VALUE);
116
+ const errMessage = createSsrErrorMessage(specifier, err);
117
+ // log message w/ stack details
118
+ logger.warn(errMessage, err.stack);
119
+ // Inform the client of the failing modules w/o exposing any additional
120
+ // details (such as callstack) for security reasons
121
+ if (debug) {
122
+ debugMessage = errMessage;
123
+ serverDebug.message = debugMessage;
124
+ }
115
125
  }
116
- }
117
- else {
118
- throw err;
119
- }
126
+ else {
127
+ throw err;
128
+ }
129
+ });
120
130
  });
121
131
  }));
122
132
  if (ssrLinks) {
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "version": "0.11.0-alpha.9",
7
+ "version": "0.11.0",
8
8
  "homepage": "https://developer.salesforce.com/docs/platform/lwr/overview",
9
9
  "repository": {
10
10
  "type": "git",
@@ -43,13 +43,13 @@
43
43
  ],
44
44
  "dependencies": {
45
45
  "@locker/near-membrane-node": "^0.13.3",
46
- "@lwrjs/diagnostics": "0.11.0-alpha.9",
47
- "@lwrjs/instrumentation": "0.11.0-alpha.9",
48
- "@lwrjs/shared-utils": "0.11.0-alpha.9",
46
+ "@lwrjs/diagnostics": "0.11.0",
47
+ "@lwrjs/instrumentation": "0.11.0",
48
+ "@lwrjs/shared-utils": "0.11.0",
49
49
  "node-fetch": "^2.6.8"
50
50
  },
51
51
  "devDependencies": {
52
- "@lwrjs/types": "0.11.0-alpha.9",
52
+ "@lwrjs/types": "0.11.0",
53
53
  "jest": "^26.6.3",
54
54
  "ts-jest": "^26.5.6"
55
55
  },
@@ -59,5 +59,5 @@
59
59
  "volta": {
60
60
  "extends": "../../../package.json"
61
61
  },
62
- "gitHead": "bc50e3f828be56ebdd2f8206cafe8158ab722470"
62
+ "gitHead": "fbc883ea90a12672ce6f1adc2201144fda8762bd"
63
63
  }