@lwrjs/lwc-ssr 0.17.2-alpha.2 → 0.17.2-alpha.20

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.
@@ -1,29 +1,16 @@
1
- import type { ClientBootstrapConfig, EnvironmentContext, LwrStringBuilder, NormalizedLwrAppBootstrapConfig, NormalizedLwrErrorRoute, NormalizedLwrRoute, ProviderAppConfig, PublicResourceRegistry, 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
  }
5
5
  export declare const SSR_PROPS_ATTR = "data-lwr-props-id";
6
6
  export declare function getPropsId(): string;
7
7
  export declare function getRenderTimeout(): number;
8
- export declare function createSsrErrorMessage(specifier: string, e: any, fallback?: boolean): string;
9
- export declare function createSsrErrorMarkup(errors: Record<string, string>, basePath: string): string;
10
8
  export declare function getLoaderShim(resourceRegistry: PublicResourceRegistry, runtimeEnvironment: RuntimeEnvironment, bootstrapConfig: NormalizedLwrAppBootstrapConfig): Promise<string>;
11
9
  export declare function getLoaderId(config: ClientBootstrapConfig, bootstrapConfig: NormalizedLwrAppBootstrapConfig): string;
12
10
  export declare function getLoaderConfig(bootstrapModule: string, config: ProviderAppConfig, runtimeParams: RuntimeParams, serverData: ServerData): ClientBootstrapConfig & {
13
11
  env: ServerEnvironment;
14
12
  };
15
13
  export declare function getServerBootstrapServices(route: NormalizedLwrRoute | NormalizedLwrErrorRoute): string[];
16
- /**
17
- * Serialize SsrDataResponse.markup into an HTML string
18
- * @param results An array of responses from getServerData hooks
19
- * @returns A string of HTML generated from markup metadata
20
- */
21
- export declare function createHeadMarkup(results: SsrDataResponse[]): string;
22
- /**
23
- * Serialize SsrDataResponse.markup into HTML, then add it to the <head> of a base doc
24
- * @param results An array of responses from getServerData hooks
25
- * @param stringBuilder The string builder for a base document
26
- */
27
- export declare function addHeadMarkup(results: SsrDataResponse[], stringBuilder: LwrStringBuilder): void;
14
+ export declare function mergeWarnings(metadata: RenderedViewMetadata, warnings?: (Error | string)[]): void;
28
15
  export {};
29
16
  //# sourceMappingURL=utils.d.ts.map
package/build/es/utils.js CHANGED
@@ -1,4 +1,3 @@
1
- import { logger, stringifyError } 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';
@@ -9,18 +8,6 @@ export function getRenderTimeout() {
9
8
  const override = process.env.SSR_TIMEOUT;
10
9
  return override ? Number.parseInt(override) : DEFAULT_SSR_TIMEOUT;
11
10
  }
12
- export function createSsrErrorMessage(specifier, e, fallback = true) {
13
- const fallbackMsg = fallback ? ' Falling back to client-side rendering.' : '';
14
- return `Server-side rendering for "${specifier}" failed.${fallbackMsg} Reason: ${stringifyError(e)}`;
15
- }
16
- export function createSsrErrorMarkup(errors, basePath) {
17
- let markup = '<div style="font-family:sans-serif;margin:50px;font-size:1.2em;"><h1>500: Server-side rendering failed</h1><p>Reasons:</p><ul>';
18
- Object.entries(errors).forEach(([specifier, reason]) => {
19
- markup += `<li><strong>${specifier}</strong>: ${stringifyError(reason)}</li>`;
20
- });
21
- markup += `</ul><p style="padding-top:1em;">See more information in the browser console. Contact your administrator for assistance.</p></div>`;
22
- return markup;
23
- }
24
11
  export async function getLoaderShim(resourceRegistry, runtimeEnvironment, bootstrapConfig) {
25
12
  const { debug } = runtimeEnvironment;
26
13
  // debug resources are not available in deployed lambda env
@@ -101,69 +88,13 @@ export function getServerBootstrapServices(route) {
101
88
  return acc;
102
89
  }, []);
103
90
  }
104
- /** SSR HEAD MARKUP UTILS */
105
- function createMetaTags(meta) {
106
- return meta.reduce((metaStr, { name, content, httpEquiv }) => {
107
- if (!name && !content && !httpEquiv)
108
- return metaStr; // do not create empty <meta> tags
109
- const nameStr = name ? ` name="${name}"` : '', httpEquivStr = httpEquiv ? ` http-equiv="${httpEquiv}"` : '', contentStr = content ? ` content="${content}"` : '';
110
- return metaStr + `<meta${nameStr}${httpEquivStr}${contentStr}>\n`;
111
- }, '');
112
- }
113
- function createScriptTags(scripts) {
114
- return scripts.reduce((scriptStr, { body }) => scriptStr + `<script type="application/ld+json">${body}</script>\n`, '');
115
- }
116
- function createLinkTags(links) {
117
- return links.reduce((linkStr, { href, rel, as, fetchpriority }) => {
118
- const relStr = rel ? ` rel="${rel}"` : '', asStr = as ? ` as="${as}"` : '', fetchStr = fetchpriority ? ` fetchpriority="${fetchpriority}"` : '';
119
- return linkStr + `<link href="${href}"${relStr}${asStr}${fetchStr}>\n`;
120
- }, '');
121
- }
122
- function createStyleTags(styles) {
123
- return styles.reduce((styleStr, { body, id }) => {
124
- const idStr = id ? ` id="${id}"` : '';
125
- return styleStr + `<style type="text/css"${idStr}>${body}</style>\n`;
126
- }, '');
127
- }
128
- /**
129
- * Serialize SsrDataResponse.markup into an HTML string
130
- * @param results An array of responses from getServerData hooks
131
- * @returns A string of HTML generated from markup metadata
132
- */
133
- export function createHeadMarkup(results) {
134
- // Loop through the <title>, <script>, <meta>, and <link> tag information
135
- // Create an HTML string for each tag
136
- let hasTitle = false;
137
- return results.reduce((str, { markup: { title, scripts = [], meta = [], links = [], styles = [] } = {} }) => {
138
- if (title && !hasTitle) {
139
- // first <title> wins
140
- hasTitle = true;
141
- str += `<title>${title}</title>\n`;
142
- }
143
- return (str +
144
- createMetaTags(meta) +
145
- createScriptTags(scripts) +
146
- createLinkTags(links) +
147
- createStyleTags(styles));
148
- }, '');
149
- }
150
- /**
151
- * Serialize SsrDataResponse.markup into HTML, then add it to the <head> of a base doc
152
- * @param results An array of responses from getServerData hooks
153
- * @param stringBuilder The string builder for a base document
154
- */
155
- export function addHeadMarkup(results, stringBuilder) {
156
- // Create HTML tags for each item in the SsrDataResponse.markup bag
157
- const headMarkup = createHeadMarkup(Object.values(results));
158
- if (headMarkup) {
159
- // Add all the links to the <head> section of the base document
160
- const headIndex = stringBuilder.original.indexOf('</head>');
161
- if (headIndex >= 0) {
162
- stringBuilder.prependLeft(headIndex, headMarkup);
163
- }
164
- else {
165
- logger.error('Adding markup during server-side rendering failed. Could not find the </head> tag.');
166
- }
91
+ export function mergeWarnings(metadata, warnings = []) {
92
+ if (!warnings.length)
93
+ return;
94
+ if (!metadata.serverDebug) {
95
+ metadata.serverDebug = {};
167
96
  }
97
+ metadata.serverDebug.warnings = metadata.serverDebug.warnings || [];
98
+ metadata.serverDebug.warnings.push(...warnings);
168
99
  }
169
100
  //# sourceMappingURL=utils.js.map
@@ -1,8 +1,7 @@
1
1
  import BaseViewProvider from '@lwrjs/base-view-provider';
2
- import { descriptions, logger, LwrApplicationError } from '@lwrjs/diagnostics';
2
+ import { descriptions, LwrApplicationError, LwrError, stringifyError } from '@lwrjs/diagnostics';
3
3
  import { ViewSpan, getTracer } from '@lwrjs/instrumentation';
4
- import { hashContent, isLocalDev, isSpecifier, moduleSpecifierToKebabCase, slugify, } from '@lwrjs/shared-utils';
5
- import { createHeadMarkup, createSsrErrorMarkup, createSsrErrorMessage } 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 {
8
7
  constructor(_pluginConfig, providerConfig) {
@@ -32,64 +31,47 @@ export default class LwcViewProvider extends BaseViewProvider {
32
31
  filePath: specifier,
33
32
  viewId,
34
33
  properties: viewProperties,
35
- render: async (runtimeParams, runtimeEnvironment) => {
34
+ render: async (context, runtimeEnvironment) => {
36
35
  // SSR the root component (without passing any public properties)
37
36
  const { config, moduleBundler, resourceRegistry } = this;
38
- const { debug } = runtimeEnvironment;
39
37
  const element = moduleSpecifierToKebabCase(specifier);
40
38
  return getTracer().trace({
41
39
  name: ViewSpan.RenderPage,
42
40
  attributes: { specifier },
43
41
  }, async () => {
44
- const route = this.routes.find((r) => r.id === viewId.id);
45
- if (!route) {
46
- throw new Error(`Unable to resolve configuration for view: ${viewId.id}`);
47
- }
48
- const { results = {}, errors, bundles, } = await getRenderer(config, moduleBundler, resourceRegistry).render({ [specifier]: { specifier, props: {} } }, route, runtimeEnvironment, Object(runtimeParams), undefined,
49
- // lets the renderer know this is the first of 2-pass SSR
50
- true);
51
- // Handle errors: throw or return an error page
52
- if (errors) {
53
- const errorString = Object.values(errors).join(', ');
54
- // always throw if debug is off
55
- // throw in local dev mode if SSR_THROW_ERRORS === true
56
- if (!debug || (isLocalDev() && process.env.SSR_THROW_ERRORS === 'true')) {
57
- // 500 error for the page request
58
- throw new LwrApplicationError(descriptions.APPLICATION.SSR_ERROR(specifier, errorString));
42
+ try {
43
+ const route = this.routes.find((r) => r.id === viewId.id);
44
+ if (!route) {
45
+ throw new Error(`Unable to resolve configuration for view: ${viewId.id}`);
46
+ }
47
+ const { results = {}, bundles } = await getRenderer(config, moduleBundler, resourceRegistry).render({ [specifier]: { specifier, props: {} } }, route, runtimeEnvironment, context.runtimeParams, undefined,
48
+ // lets the renderer know this is the first of 2-pass SSR
49
+ true);
50
+ // Insert the SSRed HTML into the document
51
+ const { html, props, markup, cache, warnings } = results[specifier] || {};
52
+ if (!html) {
53
+ throw new Error('Failed to render content template component');
59
54
  }
60
- // error page
61
- const message = createSsrErrorMessage(specifier, errorString, false);
62
- logger.warn(message, errors[specifier]); // TODO specific metadata for errors
55
+ if (markup)
56
+ viewProperties.serverHeadMarkup = createHeadMarkup([markup]); // generate <head> markup
63
57
  return {
64
- // render the errors on the browser page; fallback to CSR is not possible on the 1st pass
65
- renderedView: createSsrErrorMarkup(errors, this.runtimeEnvironment.basePath),
66
- // send an error message to the client if debug mode is on
58
+ // add "lwc:external" to the contentTemplate component to mark it as already processed
59
+ renderedView: html.replace(`<${element}`, `<${element} lwc:external`),
67
60
  metadata: {
68
- serverDebug: { message: debug ? message : undefined },
61
+ serverData: props,
69
62
  customElements: [],
70
63
  assetReferences: [],
64
+ serverBundles: bundles,
65
+ serverDebug: { warnings },
71
66
  },
67
+ cache,
72
68
  };
73
69
  }
74
- // Insert the SSRed HTML into the document
75
- const { html, props, markup, cache, status } = results[specifier];
76
- if (!html) {
77
- throw new Error(`Failed to render content template component '${specifier}'`);
70
+ catch (e) {
71
+ if (e instanceof LwrError)
72
+ throw e;
73
+ throw new LwrApplicationError(descriptions.APPLICATION.SSR_ERROR(specifier, stringifyError(e)));
78
74
  }
79
- if (markup)
80
- viewProperties.serverHeadMarkup = createHeadMarkup([results[specifier]]); // generate <head> markup
81
- return {
82
- // add "lwc:external" to the contentTemplate component to mark it as already processed
83
- renderedView: html.replace(`<${element}`, `<${element} lwc:external`),
84
- metadata: {
85
- serverData: props,
86
- customElements: [],
87
- assetReferences: [],
88
- serverBundles: bundles,
89
- },
90
- cache,
91
- status,
92
- };
93
75
  });
94
76
  },
95
77
  };
@@ -1,7 +1,7 @@
1
- import { descriptions, logger, LwrApplicationError } from '@lwrjs/diagnostics';
1
+ import { descriptions, logger, LwrApplicationError, LwrError, stringifyError } from '@lwrjs/diagnostics';
2
2
  import { ViewSpan, getTracer } from '@lwrjs/instrumentation';
3
- import { HYDRATE_CLIENT_VALUE, HYDRATE_DIRECTIVE, isCsrIsland, isHydrateOnLoad, isLocalDev, kebabCaseToModuleSpecifier, shortestTtl, } from '@lwrjs/shared-utils';
4
- import { SSR_PROPS_ATTR, addHeadMarkup, getPropsId, createSsrErrorMessage } 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]).
@@ -38,12 +38,8 @@ export default function lwcSsrViewTransformer(options, { config, moduleBundler,
38
38
  if (!metadata.serverData) {
39
39
  metadata.serverData = {};
40
40
  }
41
- if (!metadata.serverDebug) {
42
- metadata.serverDebug = {};
43
- }
44
41
  const allBundles = new Set([...(metadata.serverBundles ?? [])]); // make a copy
45
- const { customElements, serverData, serverDebug } = metadata;
46
- const { debug } = viewContext.runtimeEnvironment;
42
+ const { customElements, serverData } = metadata;
47
43
  // Gather all the SSRable custom elements (ie: root components) into 1 list
48
44
  const ssrModules = getComponentsToSSR(customElements, stringBuilder);
49
45
  const rootSpecifiers = Object.values(ssrModules).map((m) => m.specifier);
@@ -54,35 +50,43 @@ export default function lwcSsrViewTransformer(options, { config, moduleBundler,
54
50
  const islands = rootSpecifiers.join(',');
55
51
  let pageTtl;
56
52
  await getTracer().trace({ name: ViewSpan.RenderIsland, attributes: { specifiers: islands } }, async () => {
57
- const route = routes.find((r) => r.id === viewContext.view.id);
58
- if (!route) {
59
- throw new Error(`Unable to resolve configuration for view: ${viewContext.view.id}`);
60
- }
61
- const { results, errors, bundles: islandBundles, } = await getRenderer(config, moduleBundler, resourceRegistry).render(ssrModules, route, viewContext.runtimeEnvironment, viewContext.runtimeParams, metadata.serverData, false);
62
- for (const root in results) {
63
- const { html, props, cache } = results[root];
64
- const { tagName, startOffset, endOffset, hydrate } = ssrModules[root];
65
- pageTtl = shortestTtl(cache?.ttl, pageTtl);
66
- if (html) {
67
- // Add the props id to the HTML for the custom element
68
- // eg: <some-cmp> -> <some-cmp data-lwr-props-id="1234">
69
- // Then overwrite the custom element with the SSRed component string
70
- let propsAttr = '';
71
- if (hydrate) {
72
- // Only serialize props for custom elements that are to be hydrated
73
- const propsId = getPropsId();
74
- propsAttr = ` ${SSR_PROPS_ATTR}="${propsId}"`;
75
- serverData[propsId] = props;
53
+ try {
54
+ const route = routes.find((r) => r.id === viewContext.view.id);
55
+ if (!route) {
56
+ throw new Error(`Unable to resolve configuration for view: ${viewContext.view.id}`);
57
+ }
58
+ const { results, warnings, bundles: islandBundles, } = await getRenderer(config, moduleBundler, resourceRegistry).render(ssrModules, route, viewContext.runtimeEnvironment, viewContext.runtimeParams, metadata.serverData, false);
59
+ for (const root in results) {
60
+ const { html, props, cache } = results[root] || {};
61
+ const { tagName, startOffset, endOffset, hydrate } = ssrModules[root];
62
+ pageTtl = shortestTtl(cache?.ttl, pageTtl);
63
+ if (html) {
64
+ // Add the props id to the HTML for the custom element
65
+ // eg: <some-cmp> -> <some-cmp data-lwr-props-id="1234">
66
+ // Then overwrite the custom element with the SSRed component string
67
+ let propsAttr = '';
68
+ if (hydrate) {
69
+ // Only serialize props for custom elements that are to be hydrated
70
+ const propsId = getPropsId();
71
+ propsAttr = ` ${SSR_PROPS_ATTR}="${propsId}"`;
72
+ serverData[propsId] = props;
73
+ }
74
+ const [, remain] = html.split(`<${tagName}`);
75
+ stringBuilder.overwrite(startOffset, endOffset, [`<${tagName}`, propsAttr, remain].join(''));
76
76
  }
77
- const [, remain] = html.split(`<${tagName}`);
78
- stringBuilder.overwrite(startOffset, endOffset, [`<${tagName}`, propsAttr, remain].join(''));
79
77
  }
78
+ mergeWarnings(metadata, warnings);
79
+ metadata.serverBundles = islandBundles
80
+ ? new Set([...allBundles, ...islandBundles])
81
+ : allBundles;
82
+ results &&
83
+ addHeadMarkup(Object.values(results).map((r) => r.markup), stringBuilder);
84
+ }
85
+ catch (e) {
86
+ if (e instanceof LwrError)
87
+ throw e;
88
+ throw new LwrApplicationError(descriptions.APPLICATION.SSR_ERROR(islands, stringifyError(e)));
80
89
  }
81
- metadata.serverBundles = islandBundles
82
- ? new Set([...allBundles, ...islandBundles])
83
- : allBundles;
84
- results && addHeadMarkup(Object.values(results), stringBuilder);
85
- errors && handleErrors(errors, customElements, islands, debug, serverDebug);
86
90
  });
87
91
  logger.verbose({
88
92
  label: 'lwcSsrViewTransformer',
@@ -114,34 +118,4 @@ function getComponentsToSSR(customElements, stringBuilder) {
114
118
  }
115
119
  return cmpInfo;
116
120
  }
117
- function handleErrors(errors, customElements, specifiers, debug, serverDebug) {
118
- const allErrors = Object.values(errors).join(', ');
119
- // always fallback to CSR if debug === true
120
- // throw in local dev mode if SSR_THROW_ERRORS === true
121
- if (!debug || (isLocalDev() && process.env.SSR_THROW_ERRORS === 'true')) {
122
- // 500 error for the page request
123
- throw new LwrApplicationError(descriptions.APPLICATION.SSR_ERROR(specifiers, allErrors));
124
- }
125
- // Fallback to CSR in debug mode or if enabled
126
- Object.entries(errors).forEach(([specifier, err]) => {
127
- const ce = customElements.find(({ tagName }) => specifier === kebabCaseToModuleSpecifier(tagName));
128
- if (ce) {
129
- // Fallback to CSR by adding lwr:hydrate="client-only" to the custom element
130
- // This ENSURES the component's JavaScript gets sent to the client for CSRing
131
- ce.props === undefined
132
- ? (ce.props = {
133
- [HYDRATE_DIRECTIVE]: HYDRATE_CLIENT_VALUE,
134
- })
135
- : (ce.props[HYDRATE_DIRECTIVE] = HYDRATE_CLIENT_VALUE);
136
- }
137
- // Log error with stack details
138
- const errMessage = createSsrErrorMessage(specifier, err);
139
- logger.warn(errMessage, err); // TODO specific metadata for errors
140
- });
141
- // Inform the client of the failing modules without exposing any additional
142
- // details (such as callstack) for security reasons
143
- if (debug) {
144
- serverDebug.message = createSsrErrorMessage(specifiers, allErrors);
145
- }
146
- }
147
121
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "version": "0.17.2-alpha.2",
7
+ "version": "0.17.2-alpha.20",
8
8
  "homepage": "https://developer.salesforce.com/docs/platform/lwr/overview",
9
9
  "repository": {
10
10
  "type": "git",
@@ -42,26 +42,26 @@
42
42
  "build/**/*.d.ts"
43
43
  ],
44
44
  "dependencies": {
45
- "@lwrjs/config": "0.17.2-alpha.2",
46
- "@lwrjs/diagnostics": "0.17.2-alpha.2",
47
- "@lwrjs/instrumentation": "0.17.2-alpha.2",
48
- "@lwrjs/loader": "0.17.2-alpha.2",
49
- "@lwrjs/shared-utils": "0.17.2-alpha.2",
45
+ "@lwrjs/config": "0.17.2-alpha.20",
46
+ "@lwrjs/diagnostics": "0.17.2-alpha.20",
47
+ "@lwrjs/instrumentation": "0.17.2-alpha.20",
48
+ "@lwrjs/loader": "0.17.2-alpha.20",
49
+ "@lwrjs/shared-utils": "0.17.2-alpha.20",
50
50
  "fs-extra": "^11.2.0",
51
51
  "lru-cache": "^10.4.3",
52
- "undici": "^6.19.8"
52
+ "undici": "^6.21.1"
53
53
  },
54
54
  "devDependencies": {
55
- "@lwrjs/types": "0.17.2-alpha.2",
56
- "jest": "^26.6.3",
55
+ "@lwrjs/types": "0.17.2-alpha.20",
56
+ "jest": "29.7.0",
57
57
  "memfs": "^4.13.0",
58
- "ts-jest": "^26.5.6"
58
+ "ts-jest": "^29.2.5"
59
59
  },
60
60
  "engines": {
61
- "node": ">=18.0.0"
61
+ "node": ">=20.0.0"
62
62
  },
63
63
  "volta": {
64
64
  "extends": "../../../package.json"
65
65
  },
66
- "gitHead": "739b237f5d7f2c1989142cb505a56cc763f21976"
66
+ "gitHead": "871213620dec8ff886be421339f1e90e86dbee96"
67
67
  }