@lwrjs/lwc-ssr 0.17.2-alpha.2 → 0.17.2-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cjs/__mocks__/renderer.cjs +1 -5
- package/build/cjs/dataViewTransformer/index.cjs +12 -13
- package/build/cjs/fetchController.cjs +1 -0
- package/build/cjs/renderer.cjs +14 -43
- package/build/cjs/serverBootstrapServices.cjs +3 -3
- package/build/cjs/utils.cjs +0 -14
- package/build/cjs/viewProvider/index.cjs +22 -37
- package/build/cjs/viewTransformer/index.cjs +26 -48
- package/build/es/dataViewTransformer/index.js +15 -11
- package/build/es/fetchController.js +2 -1
- package/build/es/renderer.d.ts +1 -2
- package/build/es/renderer.js +16 -47
- package/build/es/serverBootstrapServices.d.ts +1 -1
- package/build/es/serverBootstrapServices.js +4 -4
- package/build/es/utils.d.ts +0 -2
- package/build/es/utils.js +1 -13
- package/build/es/viewProvider/index.js +27 -44
- package/build/es/viewTransformer/index.js +36 -64
- package/package.json +9 -9
|
@@ -21,7 +21,6 @@ var Renderer = class {
|
|
|
21
21
|
}
|
|
22
22
|
async render(components) {
|
|
23
23
|
const results = {};
|
|
24
|
-
const errors = {};
|
|
25
24
|
for (const [specifier] of Object.entries(components)) {
|
|
26
25
|
const mockOutput = mockComponents[specifier];
|
|
27
26
|
if (!mockOutput) {
|
|
@@ -31,12 +30,9 @@ var Renderer = class {
|
|
|
31
30
|
results[specifier] = mockOutput.result;
|
|
32
31
|
}
|
|
33
32
|
if (mockOutput.error) {
|
|
34
|
-
|
|
33
|
+
throw new Error(mockOutput.error);
|
|
35
34
|
}
|
|
36
35
|
}
|
|
37
|
-
if (Object.keys(errors).length) {
|
|
38
|
-
return {results, errors};
|
|
39
|
-
}
|
|
40
36
|
return {results, bundles: new Set([2, 3, 4])};
|
|
41
37
|
}
|
|
42
38
|
};
|
|
@@ -42,11 +42,7 @@ function preloadDataViewTransformer(_options, {config, moduleBundler, resourceRe
|
|
|
42
42
|
return {};
|
|
43
43
|
}
|
|
44
44
|
import_diagnostics.logger.debug({label: NAME, message: `Preload data for root component "${rootComponent}"`});
|
|
45
|
-
const {
|
|
46
|
-
results = {},
|
|
47
|
-
errors,
|
|
48
|
-
bundles
|
|
49
|
-
} = await (0, import_instrumentation.getTracer)().trace({
|
|
45
|
+
const {results = {}, bundles} = await (0, import_instrumentation.getTracer)().trace({
|
|
50
46
|
name: import_instrumentation.ViewSpan.PreloadData,
|
|
51
47
|
attributes: {rootComponent}
|
|
52
48
|
}, async () => {
|
|
@@ -54,20 +50,23 @@ function preloadDataViewTransformer(_options, {config, moduleBundler, resourceRe
|
|
|
54
50
|
if (!route) {
|
|
55
51
|
throw new Error(`Unable to resolve configuration for view: ${viewContext.view.id}`);
|
|
56
52
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
for (const err of Object.entries(errors)) {
|
|
53
|
+
try {
|
|
54
|
+
return await (0, import_renderer.getRenderer)(config, moduleBundler, resourceRegistry).render({[rootComponent]: {specifier: rootComponent, props: {}}}, route, viewContext.runtimeEnvironment, viewContext.runtimeParams);
|
|
55
|
+
} catch (e) {
|
|
61
56
|
import_diagnostics.logger.warn({
|
|
62
57
|
label: "preloadDataViewTransformer",
|
|
63
|
-
message:
|
|
64
|
-
}
|
|
58
|
+
message: import_diagnostics.descriptions.APPLICATION.PRELOAD_DATA_ERROR(rootComponent, (0, import_diagnostics.stringifyError)(e))
|
|
59
|
+
});
|
|
60
|
+
return {};
|
|
65
61
|
}
|
|
62
|
+
});
|
|
63
|
+
const result = results[rootComponent];
|
|
64
|
+
if (!result) {
|
|
66
65
|
return {};
|
|
67
66
|
}
|
|
68
|
-
const {props, markup, cache: {ttl} = {ttl: void 0}, status} =
|
|
67
|
+
const {props, markup, cache: {ttl} = {ttl: void 0}, status} = result || {};
|
|
69
68
|
import_diagnostics.logger.verbose({label: NAME, message: "response", additionalInfo: props});
|
|
70
|
-
markup && (0, import_utils.addHeadMarkup)([
|
|
69
|
+
markup && (0, import_utils.addHeadMarkup)([result], stringBuilder);
|
|
71
70
|
metadata.serverData = metadata.serverData || {};
|
|
72
71
|
Object.assign(metadata.serverData, props);
|
|
73
72
|
metadata.serverBundles = bundles ? new Set([...allBundles, ...bundles]) : allBundles;
|
|
@@ -166,6 +166,7 @@ var FetchController = class {
|
|
|
166
166
|
}
|
|
167
167
|
async fetchWithAgent(rawUrl, init, forwardedHost, coreProxy, span) {
|
|
168
168
|
let {origin, servername} = coreProxy;
|
|
169
|
+
forwardedHost = (0, import_shared_utils.getHostWithoutPort)(forwardedHost) ?? forwardedHost;
|
|
169
170
|
const host = coreProxy.host ?? forwardedHost.replace(/^https?:\/\//, "");
|
|
170
171
|
const ENHANCED_DOMAIN_TLD = ".site.com";
|
|
171
172
|
const MY_DOMAIN_TLD = ".salesforce.com";
|
package/build/cjs/renderer.cjs
CHANGED
|
@@ -87,47 +87,24 @@ var Renderer = class {
|
|
|
87
87
|
}
|
|
88
88
|
async renderComponents(components, route, runtimeEnvironment, runtimeParams, serverData = {}, isFirstOf2PassSSR = false, abortController) {
|
|
89
89
|
const results = {};
|
|
90
|
-
const errors = {};
|
|
91
90
|
const roots = Object.keys(components);
|
|
92
91
|
const services = (0, import_utils.getServerBootstrapServices)(route);
|
|
93
92
|
const reevaluateModules = (0, import_shared_utils.getFeatureFlags)().REEVALUATE_MODULES === true;
|
|
94
93
|
let loader, bootstrapServiceEvaluationMap;
|
|
95
94
|
try {
|
|
96
95
|
let lwcEngine, serverBootstrapServices;
|
|
97
|
-
({bootstrapServiceEvaluationMap, loader, lwcEngine, serverBootstrapServices} = await this.createLoader(components, route, serverData, isFirstOf2PassSSR, services,
|
|
96
|
+
({bootstrapServiceEvaluationMap, loader, lwcEngine, serverBootstrapServices} = await this.createLoader(components, route, serverData, isFirstOf2PassSSR, services, runtimeEnvironment, runtimeParams, abortController));
|
|
98
97
|
const componentModules = await this.loadAppCode(components, route, roots, loader);
|
|
99
|
-
await this.executeDataServices(components, route, serverBootstrapServices, componentModules, serverData, results,
|
|
98
|
+
await this.executeDataServices(components, route, serverBootstrapServices, componentModules, serverData, results, runtimeEnvironment, runtimeParams);
|
|
100
99
|
if (!route.bootstrap.ssr) {
|
|
101
|
-
if (Object.keys(errors).length) {
|
|
102
|
-
return {results, errors};
|
|
103
|
-
}
|
|
104
100
|
return {results, bundles: loader?.getBundles()};
|
|
105
101
|
}
|
|
106
102
|
loader?.getFetchController().enableNoOpFetch();
|
|
107
103
|
for (const component of componentModules) {
|
|
108
|
-
|
|
109
|
-
continue;
|
|
110
|
-
}
|
|
111
|
-
const {html, error} = await renderToString(lwcEngine.module, component, results[component.specifier].props);
|
|
112
|
-
if (error) {
|
|
113
|
-
errors[component.specifier] = error;
|
|
114
|
-
continue;
|
|
115
|
-
}
|
|
104
|
+
const {html} = await renderToString(lwcEngine.module, component, results[component.specifier].props);
|
|
116
105
|
results[component.specifier].html = html;
|
|
117
106
|
}
|
|
118
|
-
|
|
119
|
-
if (Object.keys(errors).length) {
|
|
120
|
-
return {results, errors, bundles};
|
|
121
|
-
}
|
|
122
|
-
return {results, bundles};
|
|
123
|
-
} catch (e) {
|
|
124
|
-
const error = Object(e);
|
|
125
|
-
return {
|
|
126
|
-
errors: {
|
|
127
|
-
[roots.join(",")]: error.message ?? (0, import_diagnostics.stringifyError)(e)
|
|
128
|
-
},
|
|
129
|
-
bundles: loader?.getBundles()
|
|
130
|
-
};
|
|
107
|
+
return {results, bundles: loader?.getBundles()};
|
|
131
108
|
} finally {
|
|
132
109
|
loader?.getFetchController().disableNoOpFetch();
|
|
133
110
|
loader?.getFetchController().enableFetchKillSwitch();
|
|
@@ -162,7 +139,7 @@ var Renderer = class {
|
|
|
162
139
|
});
|
|
163
140
|
});
|
|
164
141
|
}
|
|
165
|
-
async executeDataServices(components, route, serverBootstrapServices, componentModules, serverData, results,
|
|
142
|
+
async executeDataServices(components, route, serverBootstrapServices, componentModules, serverData, results, runtimeEnvironment, runtimeParams) {
|
|
166
143
|
return (0, import_instrumentation.getTracer)().trace({
|
|
167
144
|
name: import_instrumentation.ViewSpan.ExecuteServices,
|
|
168
145
|
attributes: {
|
|
@@ -189,11 +166,7 @@ var Renderer = class {
|
|
|
189
166
|
basePath: runtimeParams.basePath || runtimeEnvironment.basePath,
|
|
190
167
|
crossRequestCache: this.globalCache
|
|
191
168
|
};
|
|
192
|
-
const {data
|
|
193
|
-
if (error) {
|
|
194
|
-
errors[component.specifier] = error;
|
|
195
|
-
continue;
|
|
196
|
-
}
|
|
169
|
+
const {data} = await getServerData(component, context, serverData);
|
|
197
170
|
if (data) {
|
|
198
171
|
results[component.specifier] = data;
|
|
199
172
|
}
|
|
@@ -203,7 +176,7 @@ var Renderer = class {
|
|
|
203
176
|
}
|
|
204
177
|
});
|
|
205
178
|
}
|
|
206
|
-
async createLoader(components, route, serverData, isFirstOf2PassSSR, services,
|
|
179
|
+
async createLoader(components, route, serverData, isFirstOf2PassSSR, services, runtimeEnvironment, runtimeParams, abortController) {
|
|
207
180
|
return (0, import_instrumentation.getTracer)().trace({
|
|
208
181
|
name: import_instrumentation.ViewSpan.CreateLoader,
|
|
209
182
|
attributes: {
|
|
@@ -232,10 +205,7 @@ var Renderer = class {
|
|
|
232
205
|
const serviceModules = await Promise.all(services.map((specifier) => loader?.load(specifier)));
|
|
233
206
|
serverBootstrapServices = (0, import_serverBootstrapServices.createServerBootstrapServices)(loader);
|
|
234
207
|
for (const service of serviceModules) {
|
|
235
|
-
|
|
236
|
-
if (error) {
|
|
237
|
-
errors[service.specifier] = error;
|
|
238
|
-
}
|
|
208
|
+
await (0, import_serverBootstrapServices.evaluateServerBootstrapModule)(service, serverBootstrapServices.serviceAPI);
|
|
239
209
|
}
|
|
240
210
|
}
|
|
241
211
|
const ret = {
|
|
@@ -301,9 +271,9 @@ function getServerData(component, context, serverData) {
|
|
|
301
271
|
Object.assign(serverData, data.props);
|
|
302
272
|
return {data};
|
|
303
273
|
} catch (e) {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
274
|
+
if (e instanceof import_diagnostics.LwrError)
|
|
275
|
+
throw e;
|
|
276
|
+
throw new import_diagnostics.LwrApplicationError(`Error in "getServerData" for "${component.specifier}": ${(0, import_diagnostics.stringifyError)(e)}`);
|
|
307
277
|
}
|
|
308
278
|
});
|
|
309
279
|
}
|
|
@@ -318,11 +288,12 @@ async function renderToString(engine, component, props) {
|
|
|
318
288
|
const html = (0, import_shared_utils.getFeatureFlags)().SSR_COMPILER_ENABLED ? await engine.renderComponent(elementName, moduleDefault, props, "sync") : engine.renderComponent(elementName, moduleDefault, props);
|
|
319
289
|
return {html};
|
|
320
290
|
} catch (e) {
|
|
291
|
+
if (e instanceof import_diagnostics.LwrError)
|
|
292
|
+
throw e;
|
|
321
293
|
const error = Object(e);
|
|
322
294
|
const message = error.message ?? (0, import_diagnostics.stringifyError)(e);
|
|
323
295
|
const detailedMessage = error.wcStack ? "An error occurred during server-side rendering for component stack: " + error.wcStack + ". Error was: " + message : `An error occurred during server-side rendering "${component.specifier}": ` + message;
|
|
324
|
-
import_diagnostics.
|
|
325
|
-
return {error: detailedMessage};
|
|
296
|
+
throw new import_diagnostics.LwrApplicationError(detailedMessage);
|
|
326
297
|
}
|
|
327
298
|
});
|
|
328
299
|
}
|
|
@@ -76,9 +76,9 @@ function evaluateServerBootstrapModule(serviceModule, serviceApi) {
|
|
|
76
76
|
try {
|
|
77
77
|
await serviceModule.module.default(serviceApi);
|
|
78
78
|
} catch (e) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
if (e instanceof import_diagnostics.LwrError)
|
|
80
|
+
throw e;
|
|
81
|
+
throw new import_diagnostics.LwrApplicationError(`An SSR error occurred in bootstrap service "${serviceModule.specifier}": ${(0, import_diagnostics.stringifyError)(e)}`);
|
|
82
82
|
}
|
|
83
83
|
});
|
|
84
84
|
}
|
package/build/cjs/utils.cjs
CHANGED
|
@@ -27,8 +27,6 @@ __export(exports, {
|
|
|
27
27
|
SSR_PROPS_ATTR: () => SSR_PROPS_ATTR,
|
|
28
28
|
addHeadMarkup: () => addHeadMarkup,
|
|
29
29
|
createHeadMarkup: () => createHeadMarkup,
|
|
30
|
-
createSsrErrorMarkup: () => createSsrErrorMarkup,
|
|
31
|
-
createSsrErrorMessage: () => createSsrErrorMessage,
|
|
32
30
|
getLoaderConfig: () => getLoaderConfig,
|
|
33
31
|
getLoaderId: () => getLoaderId,
|
|
34
32
|
getLoaderShim: () => getLoaderShim,
|
|
@@ -47,18 +45,6 @@ function getRenderTimeout() {
|
|
|
47
45
|
const override = process.env.SSR_TIMEOUT;
|
|
48
46
|
return override ? Number.parseInt(override) : DEFAULT_SSR_TIMEOUT;
|
|
49
47
|
}
|
|
50
|
-
function createSsrErrorMessage(specifier, e, fallback = true) {
|
|
51
|
-
const fallbackMsg = fallback ? " Falling back to client-side rendering." : "";
|
|
52
|
-
return `Server-side rendering for "${specifier}" failed.${fallbackMsg} Reason: ${(0, import_diagnostics.stringifyError)(e)}`;
|
|
53
|
-
}
|
|
54
|
-
function createSsrErrorMarkup(errors, basePath) {
|
|
55
|
-
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>';
|
|
56
|
-
Object.entries(errors).forEach(([specifier, reason]) => {
|
|
57
|
-
markup += `<li><strong>${specifier}</strong>: ${(0, import_diagnostics.stringifyError)(reason)}</li>`;
|
|
58
|
-
});
|
|
59
|
-
markup += `</ul><p style="padding-top:1em;">See more information in the browser console. Contact your administrator for assistance.</p></div>`;
|
|
60
|
-
return markup;
|
|
61
|
-
}
|
|
62
48
|
async function getLoaderShim(resourceRegistry, runtimeEnvironment, bootstrapConfig) {
|
|
63
49
|
const {debug} = runtimeEnvironment;
|
|
64
50
|
const useDebug = debug && !(0, import_shared_utils.isLambdaEnv)();
|
|
@@ -61,54 +61,39 @@ var LwcViewProvider = class extends import_base_view_provider.default {
|
|
|
61
61
|
properties: viewProperties,
|
|
62
62
|
render: async (runtimeParams, runtimeEnvironment) => {
|
|
63
63
|
const {config, moduleBundler, resourceRegistry} = this;
|
|
64
|
-
const {debug} = runtimeEnvironment;
|
|
65
64
|
const element = (0, import_shared_utils.moduleSpecifierToKebabCase)(specifier);
|
|
66
65
|
return (0, import_instrumentation.getTracer)().trace({
|
|
67
66
|
name: import_instrumentation.ViewSpan.RenderPage,
|
|
68
67
|
attributes: {specifier}
|
|
69
68
|
}, async () => {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
results = {},
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (errors) {
|
|
80
|
-
const errorString = Object.values(errors).join(", ");
|
|
81
|
-
if (!debug || (0, import_shared_utils.isLocalDev)() && process.env.SSR_THROW_ERRORS === "true") {
|
|
82
|
-
throw new import_diagnostics.LwrApplicationError(import_diagnostics.descriptions.APPLICATION.SSR_ERROR(specifier, errorString));
|
|
69
|
+
try {
|
|
70
|
+
const route = this.routes.find((r) => r.id === viewId.id);
|
|
71
|
+
if (!route) {
|
|
72
|
+
throw new Error(`Unable to resolve configuration for view: ${viewId.id}`);
|
|
73
|
+
}
|
|
74
|
+
const {results = {}, bundles} = await (0, import_renderer.getRenderer)(config, moduleBundler, resourceRegistry).render({[specifier]: {specifier, props: {}}}, route, runtimeEnvironment, Object(runtimeParams), void 0, true);
|
|
75
|
+
const {html, props, markup, cache, status} = results[specifier] || {};
|
|
76
|
+
if (!html) {
|
|
77
|
+
throw new Error("Failed to render content template component");
|
|
83
78
|
}
|
|
84
|
-
|
|
85
|
-
|
|
79
|
+
if (markup)
|
|
80
|
+
viewProperties.serverHeadMarkup = (0, import_utils.createHeadMarkup)([results[specifier]]);
|
|
86
81
|
return {
|
|
87
|
-
renderedView:
|
|
82
|
+
renderedView: html.replace(`<${element}`, `<${element} lwc:external`),
|
|
88
83
|
metadata: {
|
|
89
|
-
|
|
84
|
+
serverData: props,
|
|
90
85
|
customElements: [],
|
|
91
|
-
assetReferences: []
|
|
92
|
-
|
|
86
|
+
assetReferences: [],
|
|
87
|
+
serverBundles: bundles
|
|
88
|
+
},
|
|
89
|
+
cache,
|
|
90
|
+
status
|
|
93
91
|
};
|
|
92
|
+
} catch (e) {
|
|
93
|
+
if (e instanceof import_diagnostics.LwrError)
|
|
94
|
+
throw e;
|
|
95
|
+
throw new import_diagnostics.LwrApplicationError(import_diagnostics.descriptions.APPLICATION.SSR_ERROR(specifier, (0, import_diagnostics.stringifyError)(e)));
|
|
94
96
|
}
|
|
95
|
-
const {html, props, markup, cache, status} = results[specifier];
|
|
96
|
-
if (!html) {
|
|
97
|
-
throw new Error(`Failed to render content template component '${specifier}'`);
|
|
98
|
-
}
|
|
99
|
-
if (markup)
|
|
100
|
-
viewProperties.serverHeadMarkup = (0, import_utils.createHeadMarkup)([results[specifier]]);
|
|
101
|
-
return {
|
|
102
|
-
renderedView: html.replace(`<${element}`, `<${element} lwc:external`),
|
|
103
|
-
metadata: {
|
|
104
|
-
serverData: props,
|
|
105
|
-
customElements: [],
|
|
106
|
-
assetReferences: [],
|
|
107
|
-
serverBundles: bundles
|
|
108
|
-
},
|
|
109
|
-
cache,
|
|
110
|
-
status
|
|
111
|
-
};
|
|
112
97
|
});
|
|
113
98
|
}
|
|
114
99
|
};
|
|
@@ -48,12 +48,8 @@ function lwcSsrViewTransformer(options, {config, moduleBundler, resourceRegistry
|
|
|
48
48
|
if (!metadata.serverData) {
|
|
49
49
|
metadata.serverData = {};
|
|
50
50
|
}
|
|
51
|
-
if (!metadata.serverDebug) {
|
|
52
|
-
metadata.serverDebug = {};
|
|
53
|
-
}
|
|
54
51
|
const allBundles = new Set([...metadata.serverBundles ?? []]);
|
|
55
|
-
const {customElements, serverData
|
|
56
|
-
const {debug} = viewContext.runtimeEnvironment;
|
|
52
|
+
const {customElements, serverData} = metadata;
|
|
57
53
|
const ssrModules = getComponentsToSSR(customElements, stringBuilder);
|
|
58
54
|
const rootSpecifiers = Object.values(ssrModules).map((m) => m.specifier);
|
|
59
55
|
if (!rootSpecifiers.length) {
|
|
@@ -62,33 +58,34 @@ function lwcSsrViewTransformer(options, {config, moduleBundler, resourceRegistry
|
|
|
62
58
|
const islands = rootSpecifiers.join(",");
|
|
63
59
|
let pageTtl;
|
|
64
60
|
await (0, import_instrumentation.getTracer)().trace({name: import_instrumentation.ViewSpan.RenderIsland, attributes: {specifiers: islands}}, async () => {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
results,
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
61
|
+
try {
|
|
62
|
+
const route = routes.find((r) => r.id === viewContext.view.id);
|
|
63
|
+
if (!route) {
|
|
64
|
+
throw new Error(`Unable to resolve configuration for view: ${viewContext.view.id}`);
|
|
65
|
+
}
|
|
66
|
+
const {results, bundles: islandBundles} = await (0, import_renderer.getRenderer)(config, moduleBundler, resourceRegistry).render(ssrModules, route, viewContext.runtimeEnvironment, viewContext.runtimeParams, metadata.serverData, false);
|
|
67
|
+
for (const root in results) {
|
|
68
|
+
const {html, props, cache} = results[root] || {};
|
|
69
|
+
const {tagName, startOffset, endOffset, hydrate} = ssrModules[root];
|
|
70
|
+
pageTtl = (0, import_shared_utils.shortestTtl)(cache?.ttl, pageTtl);
|
|
71
|
+
if (html) {
|
|
72
|
+
let propsAttr = "";
|
|
73
|
+
if (hydrate) {
|
|
74
|
+
const propsId = (0, import_utils.getPropsId)();
|
|
75
|
+
propsAttr = ` ${import_utils.SSR_PROPS_ATTR}="${propsId}"`;
|
|
76
|
+
serverData[propsId] = props;
|
|
77
|
+
}
|
|
78
|
+
const [, remain] = html.split(`<${tagName}`);
|
|
79
|
+
stringBuilder.overwrite(startOffset, endOffset, [`<${tagName}`, propsAttr, remain].join(""));
|
|
84
80
|
}
|
|
85
|
-
const [, remain] = html.split(`<${tagName}`);
|
|
86
|
-
stringBuilder.overwrite(startOffset, endOffset, [`<${tagName}`, propsAttr, remain].join(""));
|
|
87
81
|
}
|
|
82
|
+
metadata.serverBundles = islandBundles ? new Set([...allBundles, ...islandBundles]) : allBundles;
|
|
83
|
+
results && (0, import_utils.addHeadMarkup)(Object.values(results), stringBuilder);
|
|
84
|
+
} catch (e) {
|
|
85
|
+
if (e instanceof import_diagnostics.LwrError)
|
|
86
|
+
throw e;
|
|
87
|
+
throw new import_diagnostics.LwrApplicationError(import_diagnostics.descriptions.APPLICATION.SSR_ERROR(islands, (0, import_diagnostics.stringifyError)(e)));
|
|
88
88
|
}
|
|
89
|
-
metadata.serverBundles = islandBundles ? new Set([...allBundles, ...islandBundles]) : allBundles;
|
|
90
|
-
results && (0, import_utils.addHeadMarkup)(Object.values(results), stringBuilder);
|
|
91
|
-
errors && handleErrors(errors, customElements, islands, debug, serverDebug);
|
|
92
89
|
});
|
|
93
90
|
import_diagnostics.logger.verbose({
|
|
94
91
|
label: "lwcSsrViewTransformer",
|
|
@@ -118,22 +115,3 @@ function getComponentsToSSR(customElements, stringBuilder) {
|
|
|
118
115
|
}
|
|
119
116
|
return cmpInfo;
|
|
120
117
|
}
|
|
121
|
-
function handleErrors(errors, customElements, specifiers, debug, serverDebug) {
|
|
122
|
-
const allErrors = Object.values(errors).join(", ");
|
|
123
|
-
if (!debug || (0, import_shared_utils.isLocalDev)() && process.env.SSR_THROW_ERRORS === "true") {
|
|
124
|
-
throw new import_diagnostics.LwrApplicationError(import_diagnostics.descriptions.APPLICATION.SSR_ERROR(specifiers, allErrors));
|
|
125
|
-
}
|
|
126
|
-
Object.entries(errors).forEach(([specifier, err]) => {
|
|
127
|
-
const ce = customElements.find(({tagName}) => specifier === (0, import_shared_utils.kebabCaseToModuleSpecifier)(tagName));
|
|
128
|
-
if (ce) {
|
|
129
|
-
ce.props === void 0 ? ce.props = {
|
|
130
|
-
[import_shared_utils.HYDRATE_DIRECTIVE]: import_shared_utils.HYDRATE_CLIENT_VALUE
|
|
131
|
-
} : ce.props[import_shared_utils.HYDRATE_DIRECTIVE] = import_shared_utils.HYDRATE_CLIENT_VALUE;
|
|
132
|
-
}
|
|
133
|
-
const errMessage = (0, import_utils.createSsrErrorMessage)(specifier, err);
|
|
134
|
-
import_diagnostics.logger.warn(errMessage, err);
|
|
135
|
-
});
|
|
136
|
-
if (debug) {
|
|
137
|
-
serverDebug.message = (0, import_utils.createSsrErrorMessage)(specifiers, allErrors);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { logger } from '@lwrjs/diagnostics';
|
|
1
|
+
import { descriptions, logger, stringifyError } from '@lwrjs/diagnostics';
|
|
2
2
|
import { ViewSpan, getTracer } from '@lwrjs/instrumentation';
|
|
3
3
|
import { addHeadMarkup } from '../utils.js';
|
|
4
4
|
import { getRenderer } from '../renderer.js';
|
|
@@ -26,7 +26,7 @@ export default function preloadDataViewTransformer(_options, { config, moduleBun
|
|
|
26
26
|
return {}; // must be a CSRed rootComponent with preloadData ON
|
|
27
27
|
}
|
|
28
28
|
logger.debug({ label: NAME, message: `Preload data for root component "${rootComponent}"` });
|
|
29
|
-
const { results = {},
|
|
29
|
+
const { results = {}, bundles } = await getTracer().trace({
|
|
30
30
|
name: ViewSpan.PreloadData,
|
|
31
31
|
attributes: { rootComponent },
|
|
32
32
|
}, async () => {
|
|
@@ -34,23 +34,27 @@ export default function preloadDataViewTransformer(_options, { config, moduleBun
|
|
|
34
34
|
if (!route) {
|
|
35
35
|
throw new Error(`Unable to resolve configuration for view: ${viewContext.view.id}`);
|
|
36
36
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
try {
|
|
38
|
+
return await getRenderer(config, moduleBundler, resourceRegistry).render({ [rootComponent]: { specifier: rootComponent, props: {} } }, route, viewContext.runtimeEnvironment, viewContext.runtimeParams);
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
// If we find errors just log as warnings since these are SSR errors for a CSR page
|
|
42
42
|
logger.warn({
|
|
43
43
|
label: 'preloadDataViewTransformer',
|
|
44
|
-
message:
|
|
45
|
-
}
|
|
44
|
+
message: descriptions.APPLICATION.PRELOAD_DATA_ERROR(rootComponent, stringifyError(e)),
|
|
45
|
+
});
|
|
46
|
+
return {}; // return 0 results
|
|
46
47
|
}
|
|
48
|
+
});
|
|
49
|
+
const result = results[rootComponent];
|
|
50
|
+
if (!result) {
|
|
47
51
|
// Returns no data since there was an error loading modules
|
|
48
52
|
return {};
|
|
49
53
|
}
|
|
50
54
|
// Log and process the data response
|
|
51
|
-
const { props, markup, cache: { ttl } = { ttl: undefined }, status } =
|
|
55
|
+
const { props, markup, cache: { ttl } = { ttl: undefined }, status } = result || {};
|
|
52
56
|
logger.verbose({ label: NAME, message: 'response', additionalInfo: props });
|
|
53
|
-
markup && addHeadMarkup([
|
|
57
|
+
markup && addHeadMarkup([result], stringBuilder); // add links to the <head> tag
|
|
54
58
|
metadata.serverData = metadata.serverData || {}; // create serverData if necessary
|
|
55
59
|
Object.assign(metadata.serverData, props); // add the preloaded data to serverData for serialization
|
|
56
60
|
metadata.serverBundles = bundles ? new Set([...allBundles, ...bundles]) : allBundles; // add server bundles for preloadData
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Pool as ClientPool } from 'undici';
|
|
2
2
|
import { logger } from '@lwrjs/diagnostics';
|
|
3
3
|
import { getTracer, ViewSpan } from '@lwrjs/instrumentation';
|
|
4
|
-
import { REQUEST_DEPTH_HEADER } from '@lwrjs/shared-utils';
|
|
4
|
+
import { REQUEST_DEPTH_HEADER, getHostWithoutPort } from '@lwrjs/shared-utils';
|
|
5
5
|
const ROUTE_CORE_HEADER = 'X-SFDC-Route-Core';
|
|
6
6
|
// a single Lambda only services 1 org, so this cache will not grow too large
|
|
7
7
|
const CORE_CLIENTS = new Map();
|
|
@@ -186,6 +186,7 @@ export class FetchController {
|
|
|
186
186
|
}
|
|
187
187
|
async fetchWithAgent(rawUrl, init, forwardedHost, coreProxy, span) {
|
|
188
188
|
let { origin, servername } = coreProxy;
|
|
189
|
+
forwardedHost = getHostWithoutPort(forwardedHost) ?? forwardedHost;
|
|
189
190
|
const host = coreProxy.host ?? forwardedHost.replace(/^https?:\/\//, '');
|
|
190
191
|
const ENHANCED_DOMAIN_TLD = '.site.com';
|
|
191
192
|
const MY_DOMAIN_TLD = '.salesforce.com';
|
package/build/es/renderer.d.ts
CHANGED
|
@@ -13,7 +13,6 @@ export interface ServerResults extends SsrDataResponse {
|
|
|
13
13
|
}
|
|
14
14
|
export interface RenderResults {
|
|
15
15
|
results?: Record<string, ServerResults>;
|
|
16
|
-
errors?: Record<string, string>;
|
|
17
16
|
bundles?: Set<BundleDefinition>;
|
|
18
17
|
}
|
|
19
18
|
/**
|
|
@@ -50,7 +49,7 @@ export declare class Renderer {
|
|
|
50
49
|
* @param runtimeEnvironment - environment context
|
|
51
50
|
* @param runtimeParams - request context
|
|
52
51
|
* @param serverData - render data TODO serverData is modified (add test?)
|
|
53
|
-
* @returns render results
|
|
52
|
+
* @returns render results per component
|
|
54
53
|
*/
|
|
55
54
|
render(components: Record<string, Component>, route: NormalizedLwrRoute | NormalizedLwrErrorRoute, runtimeEnvironment: RuntimeEnvironment, runtimeParams: RuntimeParams, serverData?: ServerData, isFirstOf2PassSSR?: boolean): Promise<RenderResults>;
|
|
56
55
|
private renderComponents;
|
package/build/es/renderer.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// TODO: investigate perf impact W-16056356
|
|
2
2
|
import { LRUCache } from 'lru-cache';
|
|
3
|
-
import { LwrApplicationError, descriptions, logger, stringifyError } from '@lwrjs/diagnostics';
|
|
3
|
+
import { LwrApplicationError, LwrError, descriptions, logger, stringifyError } from '@lwrjs/diagnostics';
|
|
4
4
|
import { buildEnvironmentContext, getCacheKeyFromJson, getSpecifier, isLambdaEnv, moduleSpecifierToKebabCase, getFeatureFlags, TaskPool, isLocalDev, cookieStringToObject, } from '@lwrjs/shared-utils';
|
|
5
5
|
import { ViewSpan, cacheCountStore, getTracer } from '@lwrjs/instrumentation';
|
|
6
6
|
import { getServerBootstrapServices, getRenderTimeout } from './utils.js';
|
|
@@ -61,7 +61,7 @@ export class Renderer {
|
|
|
61
61
|
* @param runtimeEnvironment - environment context
|
|
62
62
|
* @param runtimeParams - request context
|
|
63
63
|
* @param serverData - render data TODO serverData is modified (add test?)
|
|
64
|
-
* @returns render results
|
|
64
|
+
* @returns render results per component
|
|
65
65
|
*/
|
|
66
66
|
async render(components, route, runtimeEnvironment, runtimeParams, serverData = {}, isFirstOf2PassSSR) {
|
|
67
67
|
let result;
|
|
@@ -93,7 +93,6 @@ export class Renderer {
|
|
|
93
93
|
}
|
|
94
94
|
async renderComponents(components, route, runtimeEnvironment, runtimeParams, serverData = {}, isFirstOf2PassSSR = false, abortController) {
|
|
95
95
|
const results = {};
|
|
96
|
-
const errors = {};
|
|
97
96
|
const roots = Object.keys(components);
|
|
98
97
|
const services = getServerBootstrapServices(route);
|
|
99
98
|
// For LWR@MRT, module re-evaluation is enabled by default
|
|
@@ -111,48 +110,24 @@ export class Renderer {
|
|
|
111
110
|
// When `REEVALUATE_MODULES` is enabled, we only have a single context/loader, regardless of env
|
|
112
111
|
let lwcEngine, serverBootstrapServices;
|
|
113
112
|
({ bootstrapServiceEvaluationMap, loader, lwcEngine, serverBootstrapServices } =
|
|
114
|
-
await this.createLoader(components, route, serverData, isFirstOf2PassSSR, services,
|
|
113
|
+
await this.createLoader(components, route, serverData, isFirstOf2PassSSR, services, runtimeEnvironment, runtimeParams, abortController));
|
|
115
114
|
// load root component modules (and dependencies)
|
|
116
115
|
const componentModules = await this.loadAppCode(components, route, roots, loader);
|
|
117
116
|
// Execute Services that fetch application data
|
|
118
|
-
await this.executeDataServices(components, route, serverBootstrapServices, componentModules, serverData, results,
|
|
117
|
+
await this.executeDataServices(components, route, serverBootstrapServices, componentModules, serverData, results, runtimeEnvironment, runtimeParams);
|
|
119
118
|
// exit early when preloading data
|
|
120
119
|
if (!route.bootstrap.ssr) {
|
|
121
|
-
if (Object.keys(errors).length) {
|
|
122
|
-
return { results, errors };
|
|
123
|
-
}
|
|
124
120
|
return { results, bundles: loader?.getBundles() };
|
|
125
121
|
}
|
|
126
122
|
// Assumed in SINGLE_RENDER mode to enable no-op during renderCmp phase.
|
|
127
123
|
loader?.getFetchController().enableNoOpFetch();
|
|
128
124
|
// render components
|
|
129
125
|
for (const component of componentModules) {
|
|
130
|
-
// skip rendering if an error has already occurred for the component
|
|
131
|
-
if (errors[component.specifier]) {
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
126
|
// eslint-disable-next-line no-await-in-loop
|
|
135
|
-
const { html
|
|
136
|
-
if (error) {
|
|
137
|
-
errors[component.specifier] = error;
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
127
|
+
const { html } = await renderToString(lwcEngine.module, component, results[component.specifier].props);
|
|
140
128
|
results[component.specifier].html = html;
|
|
141
129
|
}
|
|
142
|
-
|
|
143
|
-
if (Object.keys(errors).length) {
|
|
144
|
-
return { results, errors, bundles };
|
|
145
|
-
}
|
|
146
|
-
return { results, bundles };
|
|
147
|
-
}
|
|
148
|
-
catch (e) {
|
|
149
|
-
const error = Object(e);
|
|
150
|
-
return {
|
|
151
|
-
errors: {
|
|
152
|
-
[roots.join(',')]: error.message ?? stringifyError(e),
|
|
153
|
-
},
|
|
154
|
-
bundles: loader?.getBundles(),
|
|
155
|
-
};
|
|
130
|
+
return { results, bundles: loader?.getBundles() };
|
|
156
131
|
}
|
|
157
132
|
finally {
|
|
158
133
|
// Assumed in SINGLE_RENDER mode
|
|
@@ -205,7 +180,7 @@ export class Renderer {
|
|
|
205
180
|
/**
|
|
206
181
|
* Run bootstrap services and fetch server data
|
|
207
182
|
*/
|
|
208
|
-
async executeDataServices(components, route, serverBootstrapServices, componentModules, serverData, results,
|
|
183
|
+
async executeDataServices(components, route, serverBootstrapServices, componentModules, serverData, results, runtimeEnvironment, runtimeParams) {
|
|
209
184
|
return getTracer().trace({
|
|
210
185
|
name: ViewSpan.ExecuteServices,
|
|
211
186
|
attributes: {
|
|
@@ -237,11 +212,7 @@ export class Renderer {
|
|
|
237
212
|
crossRequestCache: this.globalCache,
|
|
238
213
|
};
|
|
239
214
|
// eslint-disable-next-line
|
|
240
|
-
const { data
|
|
241
|
-
if (error) {
|
|
242
|
-
errors[component.specifier] = error;
|
|
243
|
-
continue;
|
|
244
|
-
}
|
|
215
|
+
const { data } = await getServerData(component, context, serverData);
|
|
245
216
|
if (data) {
|
|
246
217
|
results[component.specifier] = data;
|
|
247
218
|
}
|
|
@@ -255,7 +226,7 @@ export class Renderer {
|
|
|
255
226
|
/**
|
|
256
227
|
* Create and initialize the loader and LWC engine for SSR
|
|
257
228
|
*/
|
|
258
|
-
async createLoader(components, route, serverData, isFirstOf2PassSSR, services,
|
|
229
|
+
async createLoader(components, route, serverData, isFirstOf2PassSSR, services, runtimeEnvironment, runtimeParams, abortController) {
|
|
259
230
|
return getTracer().trace({
|
|
260
231
|
name: ViewSpan.CreateLoader,
|
|
261
232
|
attributes: {
|
|
@@ -301,10 +272,7 @@ export class Renderer {
|
|
|
301
272
|
// this is where the loader and server data hooks are set
|
|
302
273
|
for (const service of serviceModules) {
|
|
303
274
|
// eslint-disable-next-line
|
|
304
|
-
|
|
305
|
-
if (error) {
|
|
306
|
-
errors[service.specifier] = error;
|
|
307
|
-
}
|
|
275
|
+
await evaluateServerBootstrapModule(service, serverBootstrapServices.serviceAPI);
|
|
308
276
|
}
|
|
309
277
|
}
|
|
310
278
|
const ret = {
|
|
@@ -399,9 +367,9 @@ function getServerData(component, context, serverData) {
|
|
|
399
367
|
return { data };
|
|
400
368
|
}
|
|
401
369
|
catch (e) {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
370
|
+
if (e instanceof LwrError)
|
|
371
|
+
throw e;
|
|
372
|
+
throw new LwrApplicationError(`Error in "getServerData" for "${component.specifier}": ${stringifyError(e)}`);
|
|
405
373
|
}
|
|
406
374
|
});
|
|
407
375
|
}
|
|
@@ -419,6 +387,8 @@ async function renderToString(engine, component, props) {
|
|
|
419
387
|
return { html };
|
|
420
388
|
}
|
|
421
389
|
catch (e) {
|
|
390
|
+
if (e instanceof LwrError)
|
|
391
|
+
throw e;
|
|
422
392
|
const error = Object(e);
|
|
423
393
|
// add the LWC rendering stack to the error message
|
|
424
394
|
const message = error.message ?? stringifyError(e);
|
|
@@ -428,8 +398,7 @@ async function renderToString(engine, component, props) {
|
|
|
428
398
|
'. Error was: ' +
|
|
429
399
|
message
|
|
430
400
|
: `An error occurred during server-side rendering "${component.specifier}": ` + message;
|
|
431
|
-
|
|
432
|
-
return { error: detailedMessage };
|
|
401
|
+
throw new LwrApplicationError(detailedMessage);
|
|
433
402
|
}
|
|
434
403
|
});
|
|
435
404
|
}
|
|
@@ -12,5 +12,5 @@ export declare class ServerBootstrapServices {
|
|
|
12
12
|
private registerRequestHooks;
|
|
13
13
|
}
|
|
14
14
|
export declare function createServerBootstrapServices(loader: ModuleLoader): ServerBootstrapServices;
|
|
15
|
-
export declare function evaluateServerBootstrapModule(serviceModule: any, serviceApi: ServerServiceAPI): Promise<
|
|
15
|
+
export declare function evaluateServerBootstrapModule(serviceModule: any, serviceApi: ServerServiceAPI): Promise<void>;
|
|
16
16
|
//# sourceMappingURL=serverBootstrapServices.d.ts.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ViewSpan, getTracer } from '@lwrjs/instrumentation';
|
|
2
|
-
import {
|
|
2
|
+
import { LwrApplicationError, LwrError, stringifyError } from '@lwrjs/diagnostics';
|
|
3
3
|
export class ServerBootstrapServices {
|
|
4
4
|
evaluateServerDataHooks(serverData = {}) {
|
|
5
5
|
// now that we have server data, run the server data hooks
|
|
@@ -48,9 +48,9 @@ export function evaluateServerBootstrapModule(serviceModule, serviceApi) {
|
|
|
48
48
|
await serviceModule.module.default(serviceApi);
|
|
49
49
|
}
|
|
50
50
|
catch (e) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
if (e instanceof LwrError)
|
|
52
|
+
throw e;
|
|
53
|
+
throw new LwrApplicationError(`An SSR error occurred in bootstrap service "${serviceModule.specifier}": ${stringifyError(e)}`);
|
|
54
54
|
}
|
|
55
55
|
});
|
|
56
56
|
}
|
package/build/es/utils.d.ts
CHANGED
|
@@ -5,8 +5,6 @@ interface ServerEnvironment extends EnvironmentContext {
|
|
|
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 & {
|
package/build/es/utils.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { logger
|
|
1
|
+
import { logger } from '@lwrjs/diagnostics';
|
|
2
2
|
import { buildEnvironmentContext, getFeatureFlags, normalizeVersionToUri, isLambdaEnv, getSpecifier, } from '@lwrjs/shared-utils';
|
|
3
3
|
const DEFAULT_SSR_TIMEOUT = 5000; // 5 seconds, override with process.env.SSR_TIMEOUT
|
|
4
4
|
export const SSR_PROPS_ATTR = 'data-lwr-props-id';
|
|
@@ -9,18 +9,6 @@ export function getRenderTimeout() {
|
|
|
9
9
|
const override = process.env.SSR_TIMEOUT;
|
|
10
10
|
return override ? Number.parseInt(override) : DEFAULT_SSR_TIMEOUT;
|
|
11
11
|
}
|
|
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
12
|
export async function getLoaderShim(resourceRegistry, runtimeEnvironment, bootstrapConfig) {
|
|
25
13
|
const { debug } = runtimeEnvironment;
|
|
26
14
|
// debug resources are not available in deployed lambda env
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import BaseViewProvider from '@lwrjs/base-view-provider';
|
|
2
|
-
import { descriptions,
|
|
2
|
+
import { descriptions, LwrApplicationError, LwrError, stringifyError } from '@lwrjs/diagnostics';
|
|
3
3
|
import { ViewSpan, getTracer } from '@lwrjs/instrumentation';
|
|
4
|
-
import { hashContent,
|
|
5
|
-
import { createHeadMarkup
|
|
4
|
+
import { hashContent, isSpecifier, moduleSpecifierToKebabCase, slugify } from '@lwrjs/shared-utils';
|
|
5
|
+
import { createHeadMarkup } from '../utils.js';
|
|
6
6
|
import { getRenderer } from '../renderer.js';
|
|
7
7
|
export default class LwcViewProvider extends BaseViewProvider {
|
|
8
8
|
constructor(_pluginConfig, providerConfig) {
|
|
@@ -35,61 +35,44 @@ export default class LwcViewProvider extends BaseViewProvider {
|
|
|
35
35
|
render: async (runtimeParams, runtimeEnvironment) => {
|
|
36
36
|
// SSR the root component (without passing any public properties)
|
|
37
37
|
const { config, moduleBundler, resourceRegistry } = this;
|
|
38
|
-
const { debug } = runtimeEnvironment;
|
|
39
38
|
const element = moduleSpecifierToKebabCase(specifier);
|
|
40
39
|
return getTracer().trace({
|
|
41
40
|
name: ViewSpan.RenderPage,
|
|
42
41
|
attributes: { specifier },
|
|
43
42
|
}, async () => {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
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));
|
|
43
|
+
try {
|
|
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 = {}, 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
|
+
// Insert the SSRed HTML into the document
|
|
52
|
+
const { html, props, markup, cache, status } = results[specifier] || {};
|
|
53
|
+
if (!html) {
|
|
54
|
+
throw new Error('Failed to render content template component');
|
|
59
55
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
logger.warn(message, errors[specifier]); // TODO specific metadata for errors
|
|
56
|
+
if (markup)
|
|
57
|
+
viewProperties.serverHeadMarkup = createHeadMarkup([results[specifier]]); // generate <head> markup
|
|
63
58
|
return {
|
|
64
|
-
//
|
|
65
|
-
renderedView:
|
|
66
|
-
// send an error message to the client if debug mode is on
|
|
59
|
+
// add "lwc:external" to the contentTemplate component to mark it as already processed
|
|
60
|
+
renderedView: html.replace(`<${element}`, `<${element} lwc:external`),
|
|
67
61
|
metadata: {
|
|
68
|
-
|
|
62
|
+
serverData: props,
|
|
69
63
|
customElements: [],
|
|
70
64
|
assetReferences: [],
|
|
65
|
+
serverBundles: bundles,
|
|
71
66
|
},
|
|
67
|
+
cache,
|
|
68
|
+
status,
|
|
72
69
|
};
|
|
73
70
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
throw new
|
|
71
|
+
catch (e) {
|
|
72
|
+
if (e instanceof LwrError)
|
|
73
|
+
throw e;
|
|
74
|
+
throw new LwrApplicationError(descriptions.APPLICATION.SSR_ERROR(specifier, stringifyError(e)));
|
|
78
75
|
}
|
|
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
76
|
});
|
|
94
77
|
},
|
|
95
78
|
};
|
|
@@ -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 {
|
|
4
|
-
import { SSR_PROPS_ATTR, addHeadMarkup, getPropsId
|
|
3
|
+
import { HYDRATE_DIRECTIVE, isCsrIsland, isHydrateOnLoad, kebabCaseToModuleSpecifier, shortestTtl, } from '@lwrjs/shared-utils';
|
|
4
|
+
import { SSR_PROPS_ATTR, addHeadMarkup, getPropsId } 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
|
|
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,41 @@ 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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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, 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
|
+
metadata.serverBundles = islandBundles
|
|
79
|
+
? new Set([...allBundles, ...islandBundles])
|
|
80
|
+
: allBundles;
|
|
81
|
+
results && addHeadMarkup(Object.values(results), stringBuilder);
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
if (e instanceof LwrError)
|
|
85
|
+
throw e;
|
|
86
|
+
throw new LwrApplicationError(descriptions.APPLICATION.SSR_ERROR(islands, stringifyError(e)));
|
|
80
87
|
}
|
|
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
88
|
});
|
|
87
89
|
logger.verbose({
|
|
88
90
|
label: 'lwcSsrViewTransformer',
|
|
@@ -114,34 +116,4 @@ function getComponentsToSSR(customElements, stringBuilder) {
|
|
|
114
116
|
}
|
|
115
117
|
return cmpInfo;
|
|
116
118
|
}
|
|
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
119
|
//# 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.
|
|
7
|
+
"version": "0.17.2-alpha.4",
|
|
8
8
|
"homepage": "https://developer.salesforce.com/docs/platform/lwr/overview",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
@@ -42,17 +42,17 @@
|
|
|
42
42
|
"build/**/*.d.ts"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@lwrjs/config": "0.17.2-alpha.
|
|
46
|
-
"@lwrjs/diagnostics": "0.17.2-alpha.
|
|
47
|
-
"@lwrjs/instrumentation": "0.17.2-alpha.
|
|
48
|
-
"@lwrjs/loader": "0.17.2-alpha.
|
|
49
|
-
"@lwrjs/shared-utils": "0.17.2-alpha.
|
|
45
|
+
"@lwrjs/config": "0.17.2-alpha.4",
|
|
46
|
+
"@lwrjs/diagnostics": "0.17.2-alpha.4",
|
|
47
|
+
"@lwrjs/instrumentation": "0.17.2-alpha.4",
|
|
48
|
+
"@lwrjs/loader": "0.17.2-alpha.4",
|
|
49
|
+
"@lwrjs/shared-utils": "0.17.2-alpha.4",
|
|
50
50
|
"fs-extra": "^11.2.0",
|
|
51
51
|
"lru-cache": "^10.4.3",
|
|
52
|
-
"undici": "^6.
|
|
52
|
+
"undici": "^6.21.1"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"@lwrjs/types": "0.17.2-alpha.
|
|
55
|
+
"@lwrjs/types": "0.17.2-alpha.4",
|
|
56
56
|
"jest": "^26.6.3",
|
|
57
57
|
"memfs": "^4.13.0",
|
|
58
58
|
"ts-jest": "^26.5.6"
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
"volta": {
|
|
64
64
|
"extends": "../../../package.json"
|
|
65
65
|
},
|
|
66
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "d7fb1605cec0bf9fef18e3daeb17dc28b35a80d3"
|
|
67
67
|
}
|