@lwrjs/lwc-ssr 0.13.0-alpha.8 → 0.13.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.
- package/README.md +1 -1
- package/build/cjs/moduleLoader.cjs +44 -12
- package/build/cjs/renderer.cjs +105 -27
- package/build/cjs/serverBootstrapServices.cjs +7 -2
- package/build/cjs/utils.cjs +22 -10
- package/build/cjs/viewProvider/index.cjs +4 -4
- package/build/cjs/viewTransformer/index.cjs +1 -1
- package/build/es/moduleLoader.d.ts +11 -5
- package/build/es/moduleLoader.js +58 -16
- package/build/es/renderer.d.ts +14 -1
- package/build/es/renderer.js +145 -38
- package/build/es/serverBootstrapServices.d.ts +2 -2
- package/build/es/serverBootstrapServices.js +7 -3
- package/build/es/utils.d.ts +5 -3
- package/build/es/utils.js +34 -12
- package/build/es/viewProvider/index.js +6 -4
- package/build/es/viewTransformer/index.js +2 -4
- package/package.json +10 -9
package/README.md
CHANGED
|
@@ -115,7 +115,7 @@ declare global {
|
|
|
115
115
|
|
|
116
116
|
### Loading data during SSR
|
|
117
117
|
|
|
118
|
-
Many components depend on external data and resources. LWR provides a `getServerData()` hook for developers to fetch data on the server. During SSR, LWR calls the `getServerData()` hook for each **root component**, then serializes the resulting data into the page document as either [JSON](#json) or [markup](#markup).
|
|
118
|
+
Many components depend on external data and resources. LWR provides a [`getServerData()` hook](https://developer.salesforce.com/docs/platform/lwr/guide/lwr-enable-ssr-site.html#load-data-during-ssr) for developers to fetch data on the server. During SSR, LWR calls the `getServerData()` hook for each **root component**, then serializes the resulting data into the page document as either [JSON](#json) or [markup](#markup).
|
|
119
119
|
|
|
120
120
|
If an error is thrown from `getServerData()`, rendering will fail and the server will return an HTTP 500 error for the page request. If this is undesirable, catch any errors thrown while fetching data and return default values from `getServerData()`.
|
|
121
121
|
|
|
@@ -24,6 +24,7 @@ var __toModule = (module2) => {
|
|
|
24
24
|
// packages/@lwrjs/lwc-ssr/src/moduleLoader.ts
|
|
25
25
|
__markAsModule(exports);
|
|
26
26
|
__export(exports, {
|
|
27
|
+
FETCH_ABORT_KEY: () => FETCH_ABORT_KEY,
|
|
27
28
|
createModuleLoader: () => createModuleLoader
|
|
28
29
|
});
|
|
29
30
|
var import_path = __toModule(require("path"));
|
|
@@ -31,34 +32,44 @@ var import_crypto = __toModule(require("crypto"));
|
|
|
31
32
|
var import_instrumentation = __toModule(require("@lwrjs/instrumentation"));
|
|
32
33
|
var import_shared_utils = __toModule(require("@lwrjs/shared-utils"));
|
|
33
34
|
var import_utils = __toModule(require("./utils.cjs"));
|
|
35
|
+
var FETCH_ABORT_KEY = "__fetchAbortId__";
|
|
34
36
|
var BOOTSTRAP_SPECIFIER = "@lwrjs/ssr-bootstrap";
|
|
35
|
-
function createModuleLoader(config, resourceRegistry, bundleRegistry, runtimeEnvironment, runtimeParams, serverData) {
|
|
37
|
+
function createModuleLoader(config, resourceRegistry, bundleRegistry, runtimeEnvironment, runtimeParams, serverData, bootstrapConfig) {
|
|
36
38
|
const createLoader = runtimeEnvironment.format === "amd" ? createAMDModuleLoader : createESMModuleLoader;
|
|
37
|
-
return createLoader(config, resourceRegistry, bundleRegistry, runtimeEnvironment, runtimeParams, serverData);
|
|
39
|
+
return createLoader(config, resourceRegistry, bundleRegistry, runtimeEnvironment, runtimeParams, serverData, bootstrapConfig);
|
|
38
40
|
}
|
|
39
|
-
async function createAMDModuleLoader(config, resourceRegistry, bundleRegistry, runtimeEnvironment, runtimeParams, serverData) {
|
|
41
|
+
async function createAMDModuleLoader(config, resourceRegistry, bundleRegistry, runtimeEnvironment, runtimeParams, serverData, bootstrapConfig) {
|
|
40
42
|
const loaderConfig = (0, import_utils.getLoaderConfig)(BOOTSTRAP_SPECIFIER, config, runtimeParams, serverData);
|
|
41
43
|
const {context, controller} = createContext(loaderConfig, runtimeParams);
|
|
44
|
+
const contextKeyMap = new Map(Object.keys(context).map((key) => [key, true]));
|
|
42
45
|
let run;
|
|
43
46
|
context.LWR.customInit = async (lwr) => {
|
|
44
47
|
run = lwr.initializeApp;
|
|
45
48
|
};
|
|
49
|
+
const useEval = process.env.SSR_DEBUG === "true";
|
|
46
50
|
const init = (source) => {
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
if (!useEval) {
|
|
52
|
+
const fn = new Function("globalThis", ...Object.keys(context), source);
|
|
53
|
+
fn(context, ...Object.values(context));
|
|
54
|
+
} else {
|
|
55
|
+
((context) => {
|
|
56
|
+
eval(`${Object.keys(context).reduce((c, k) => c + `const ${k} = context['${k}'];`, "globalThis=context;")} ${source}`);
|
|
57
|
+
})(context);
|
|
58
|
+
}
|
|
49
59
|
};
|
|
50
|
-
init(await (0, import_utils.getLoaderShim)(resourceRegistry, runtimeEnvironment));
|
|
60
|
+
init(await (0, import_utils.getLoaderShim)(resourceRegistry, runtimeEnvironment, bootstrapConfig));
|
|
51
61
|
if (!run) {
|
|
52
62
|
throw new Error("Failed to init loader shim: custom init not called");
|
|
53
63
|
}
|
|
54
64
|
const p = new Promise((resolve) => {
|
|
55
|
-
context.LWR.define(BOOTSTRAP_SPECIFIER, [(0, import_utils.getLoaderId)(context.LWR)], (l) => resolve(l));
|
|
65
|
+
context.LWR.define(BOOTSTRAP_SPECIFIER, [(0, import_utils.getLoaderId)(context.LWR, bootstrapConfig)], (l) => resolve(l));
|
|
56
66
|
});
|
|
57
67
|
run();
|
|
58
|
-
const {load, services} = await p;
|
|
68
|
+
const {load, services, clearRegistry} = await p;
|
|
59
69
|
const visited = new Set(["lwc"]);
|
|
60
70
|
return {
|
|
61
71
|
services,
|
|
72
|
+
clearRegistry,
|
|
62
73
|
load: async (specifier, aliases) => {
|
|
63
74
|
const injectBundle = async (bundle, aliases2) => {
|
|
64
75
|
if (visited.has(bundle) && specifier !== bundle) {
|
|
@@ -67,9 +78,10 @@ async function createAMDModuleLoader(config, resourceRegistry, bundleRegistry, r
|
|
|
67
78
|
visited.add(bundle);
|
|
68
79
|
const moduleId = (0, import_shared_utils.explodeSpecifier)(bundle);
|
|
69
80
|
const def = await bundleRegistry.getModuleBundle(moduleId, runtimeEnvironment, runtimeParams);
|
|
70
|
-
if (typeof def.src === "string") {
|
|
81
|
+
if (typeof def.src === "string" && !def.code.includes("//# sourceURL=")) {
|
|
82
|
+
const srcUrl = (0, import_shared_utils.isLocalDev)() ? (0, import_shared_utils.getLocalDevOverrideUrl)(config.cacheDir, moduleId.specifier, def.src) : def.src;
|
|
71
83
|
def.code += `
|
|
72
|
-
//# sourceURL=${import_path.default.resolve(
|
|
84
|
+
//# sourceURL=${import_path.default.resolve(srcUrl)}`;
|
|
73
85
|
}
|
|
74
86
|
const staticImports = def.bundleRecord.imports?.map((dep) => (0, import_shared_utils.getSpecifier)(dep)) ?? [];
|
|
75
87
|
const dynamicImports = def.bundleRecord.dynamicImports?.map((dep) => (0, import_shared_utils.getSpecifier)(dep)) ?? [];
|
|
@@ -101,7 +113,21 @@ async function createAMDModuleLoader(config, resourceRegistry, bundleRegistry, r
|
|
|
101
113
|
module: mod
|
|
102
114
|
};
|
|
103
115
|
},
|
|
104
|
-
|
|
116
|
+
getFetchController: () => controller,
|
|
117
|
+
resetGlobalContext: () => {
|
|
118
|
+
for (const key in context) {
|
|
119
|
+
if (Object.prototype.hasOwnProperty.call(context, key) && !contextKeyMap.has(key)) {
|
|
120
|
+
delete context[key];
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
context.LWR = {
|
|
124
|
+
...context.LWR,
|
|
125
|
+
serverData: void 0
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
getContext: () => {
|
|
129
|
+
return context;
|
|
130
|
+
}
|
|
105
131
|
};
|
|
106
132
|
}
|
|
107
133
|
async function createESMModuleLoader() {
|
|
@@ -125,8 +151,14 @@ function createContext(LWR, runtimeParams) {
|
|
|
125
151
|
enableNoOpFetch: () => {
|
|
126
152
|
fetchController.activateNoOp();
|
|
127
153
|
},
|
|
128
|
-
|
|
154
|
+
disableNoOpFetch: () => {
|
|
155
|
+
fetchController.deactivateNoOp();
|
|
156
|
+
},
|
|
157
|
+
enableFetchKillSwitch: () => {
|
|
129
158
|
fetchController.activateKillSwitch();
|
|
159
|
+
},
|
|
160
|
+
disableFetchKillSwitch: () => {
|
|
161
|
+
fetchController.deactivateKillSwitch();
|
|
130
162
|
}
|
|
131
163
|
}
|
|
132
164
|
};
|
package/build/cjs/renderer.cjs
CHANGED
|
@@ -27,6 +27,7 @@ __export(exports, {
|
|
|
27
27
|
Renderer: () => Renderer,
|
|
28
28
|
getRenderer: () => getRenderer
|
|
29
29
|
});
|
|
30
|
+
var import_lru_cache = __toModule(require("lru-cache"));
|
|
30
31
|
var import_diagnostics = __toModule(require("@lwrjs/diagnostics"));
|
|
31
32
|
var import_shared_utils = __toModule(require("@lwrjs/shared-utils"));
|
|
32
33
|
var import_instrumentation = __toModule(require("@lwrjs/instrumentation"));
|
|
@@ -45,38 +46,70 @@ var Renderer = class {
|
|
|
45
46
|
this.config = config;
|
|
46
47
|
this.bundleRegistry = bundleRegistry;
|
|
47
48
|
this.resourceRegistry = resourceRegistry;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
reject((0, import_diagnostics.createSingleDiagnosticError)({
|
|
54
|
-
description: import_diagnostics.descriptions.UNRESOLVABLE.SSR_TIMEOUT(route.id, (0, import_utils.getRenderTimeout)())
|
|
55
|
-
}, import_diagnostics.LwrUnresolvableError));
|
|
56
|
-
}, (0, import_utils.getRenderTimeout)());
|
|
49
|
+
this.contextPerEnv = new import_lru_cache.LRUCache({
|
|
50
|
+
max: 50,
|
|
51
|
+
dispose: () => {
|
|
52
|
+
import_diagnostics.logger.info("evicted bootstrap context from renderer cache");
|
|
53
|
+
}
|
|
57
54
|
});
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
const taskPoolSize = (0, import_shared_utils.getFeatureFlags)().SINGLE_RENDER_MODE ? 1 : 15;
|
|
56
|
+
this.pendingRenders = new import_shared_utils.TaskPool(taskPoolSize);
|
|
57
|
+
}
|
|
58
|
+
async render(components, route, runtimeEnvironment, runtimeParams, serverData = {}, isFirstOf2PassSSR) {
|
|
59
|
+
let result;
|
|
60
|
+
if ((0, import_shared_utils.isLambdaEnv)() || process.env.SSR_TIMEOUT !== void 0) {
|
|
61
|
+
let timerId;
|
|
62
|
+
const timeout = new Promise((_, reject) => {
|
|
63
|
+
timerId = setTimeout(() => {
|
|
64
|
+
reject((0, import_diagnostics.createSingleDiagnosticError)({
|
|
65
|
+
description: import_diagnostics.descriptions.UNRESOLVABLE.SSR_TIMEOUT(route.id, (0, import_utils.getRenderTimeout)())
|
|
66
|
+
}, import_diagnostics.LwrUnresolvableError));
|
|
67
|
+
}, (0, import_utils.getRenderTimeout)());
|
|
68
|
+
});
|
|
69
|
+
result = await this.pendingRenders.execute(async () => {
|
|
70
|
+
return Promise.race([
|
|
71
|
+
timeout,
|
|
72
|
+
this.renderComponents(components, route, runtimeEnvironment, runtimeParams, serverData, isFirstOf2PassSSR)
|
|
73
|
+
]);
|
|
74
|
+
});
|
|
75
|
+
clearTimeout(timerId);
|
|
76
|
+
} else {
|
|
77
|
+
result = await this.pendingRenders.execute(async () => {
|
|
78
|
+
return this.renderComponents(components, route, runtimeEnvironment, runtimeParams, serverData, isFirstOf2PassSSR);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
63
81
|
return result;
|
|
64
82
|
}
|
|
65
|
-
async renderComponents(components, route, runtimeEnvironment, runtimeParams, serverData = {}) {
|
|
83
|
+
async renderComponents(components, route, runtimeEnvironment, runtimeParams, serverData = {}, isFirstOf2PassSSR = false) {
|
|
66
84
|
const results = {};
|
|
67
85
|
const errors = {};
|
|
68
86
|
const roots = Object.keys(components);
|
|
69
87
|
const services = (0, import_utils.getServerBootstrapServices)(route);
|
|
70
|
-
|
|
88
|
+
const reevaluateModules = (0, import_shared_utils.getFeatureFlags)().REEVALUATE_MODULES === true;
|
|
89
|
+
const singleRenderMode = (0, import_shared_utils.getFeatureFlags)().SINGLE_RENDER_MODE;
|
|
90
|
+
let loader, loaderPromise, bootstrapServiceEvaluationMap;
|
|
71
91
|
try {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
92
|
+
const bootstrapContext = this.getCachedBootstrapContext(route, runtimeEnvironment, runtimeParams, serverData, reevaluateModules);
|
|
93
|
+
loaderPromise = bootstrapContext.loader;
|
|
94
|
+
bootstrapServiceEvaluationMap = bootstrapContext.bootstrapServiceEvaluationMap;
|
|
95
|
+
loader = await loaderPromise;
|
|
96
|
+
loader.getFetchController().disableFetchKillSwitch();
|
|
97
|
+
const engineServerSpecifier = (0, import_shared_utils.getSpecifier)({
|
|
98
|
+
specifier: "@lwc/engine-server",
|
|
99
|
+
version: route.bootstrap.lwcVersion
|
|
100
|
+
});
|
|
101
|
+
const engine = await loader.load(engineServerSpecifier, ["lwc"]);
|
|
102
|
+
const shouldRunBootstrapServices = !bootstrapServiceEvaluationMap?.has(route.id);
|
|
103
|
+
let serverBootstrapServices;
|
|
104
|
+
if (shouldRunBootstrapServices) {
|
|
105
|
+
bootstrapServiceEvaluationMap?.set(route.id, true);
|
|
106
|
+
const serviceModules = await Promise.all(services.map((specifier) => loader?.load(specifier)));
|
|
107
|
+
serverBootstrapServices = (0, import_serverBootstrapServices.createServerBootstrapServices)(loader);
|
|
108
|
+
for (const service of serviceModules) {
|
|
109
|
+
const error = await (0, import_serverBootstrapServices.evaluateServerBootstrapModule)(service, serverBootstrapServices.serviceAPI);
|
|
110
|
+
if (error) {
|
|
111
|
+
errors[service.specifier] = error;
|
|
112
|
+
}
|
|
80
113
|
}
|
|
81
114
|
}
|
|
82
115
|
const componentModules = await Promise.all(roots.map((specifier) => loader?.load(specifier)));
|
|
@@ -103,14 +136,18 @@ var Renderer = class {
|
|
|
103
136
|
results[component.specifier] = data;
|
|
104
137
|
}
|
|
105
138
|
}
|
|
106
|
-
serverBootstrapServices
|
|
139
|
+
if (serverBootstrapServices) {
|
|
140
|
+
serverBootstrapServices.evaluateServerDataHooks(serverData);
|
|
141
|
+
}
|
|
107
142
|
if (!route.bootstrap.ssr) {
|
|
108
143
|
if (Object.keys(errors).length) {
|
|
109
144
|
return {results, errors};
|
|
110
145
|
}
|
|
111
146
|
return {results};
|
|
112
147
|
}
|
|
113
|
-
|
|
148
|
+
if ((0, import_shared_utils.getFeatureFlags)().SINGLE_RENDER_MODE) {
|
|
149
|
+
loader.getFetchController().enableNoOpFetch();
|
|
150
|
+
}
|
|
114
151
|
for (const component of componentModules) {
|
|
115
152
|
if (errors[component.specifier]) {
|
|
116
153
|
continue;
|
|
@@ -134,8 +171,49 @@ var Renderer = class {
|
|
|
134
171
|
}
|
|
135
172
|
};
|
|
136
173
|
} finally {
|
|
137
|
-
|
|
174
|
+
if (singleRenderMode) {
|
|
175
|
+
loader?.getFetchController().disableNoOpFetch();
|
|
176
|
+
loader?.getFetchController().enableFetchKillSwitch();
|
|
177
|
+
}
|
|
178
|
+
if (!isFirstOf2PassSSR) {
|
|
179
|
+
loader?.resetGlobalContext();
|
|
180
|
+
if (reevaluateModules) {
|
|
181
|
+
bootstrapServiceEvaluationMap?.delete(route.id);
|
|
182
|
+
loader?.clearRegistry();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
getCachedBootstrapContext(route, runtimeEnvironment, runtimeParams, serverData, reevaluateModules) {
|
|
188
|
+
let loader, bootstrapServiceEvaluationMap;
|
|
189
|
+
if (reevaluateModules) {
|
|
190
|
+
if (!this.cachedBootstrapContext) {
|
|
191
|
+
this.cachedBootstrapContext = {
|
|
192
|
+
loader: (0, import_moduleLoader.createModuleLoader)(this.config, this.resourceRegistry, this.bundleRegistry, runtimeEnvironment, runtimeParams, serverData, route.bootstrap),
|
|
193
|
+
bootstrapServiceEvaluationMap: new Map()
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return this.cachedBootstrapContext;
|
|
197
|
+
}
|
|
198
|
+
const envContext = (0, import_shared_utils.buildEnvironmentContext)(runtimeParams);
|
|
199
|
+
const {host, requestDepth} = runtimeParams;
|
|
200
|
+
const contextCacheKey = (0, import_shared_utils.getCacheKeyFromJson)({envContext, host, requestDepth});
|
|
201
|
+
const bootstrapContext = this.contextPerEnv.get(contextCacheKey);
|
|
202
|
+
if (!bootstrapContext) {
|
|
203
|
+
loader = (0, import_moduleLoader.createModuleLoader)(this.config, this.resourceRegistry, this.bundleRegistry, runtimeEnvironment, runtimeParams, serverData, route.bootstrap);
|
|
204
|
+
bootstrapServiceEvaluationMap = new Map();
|
|
205
|
+
this.contextPerEnv.set(contextCacheKey, {
|
|
206
|
+
loader,
|
|
207
|
+
bootstrapServiceEvaluationMap
|
|
208
|
+
});
|
|
209
|
+
} else {
|
|
210
|
+
loader = bootstrapContext.loader;
|
|
211
|
+
bootstrapServiceEvaluationMap = bootstrapContext.bootstrapServiceEvaluationMap;
|
|
138
212
|
}
|
|
213
|
+
return {
|
|
214
|
+
loader,
|
|
215
|
+
bootstrapServiceEvaluationMap
|
|
216
|
+
};
|
|
139
217
|
}
|
|
140
218
|
};
|
|
141
219
|
function getServerData(component, context, serverData) {
|
|
@@ -31,7 +31,7 @@ __export(exports, {
|
|
|
31
31
|
var import_instrumentation = __toModule(require("@lwrjs/instrumentation"));
|
|
32
32
|
var import_diagnostics = __toModule(require("@lwrjs/diagnostics"));
|
|
33
33
|
var ServerBootstrapServices = class {
|
|
34
|
-
evaluateServerDataHooks(serverData) {
|
|
34
|
+
evaluateServerDataHooks(serverData = {}) {
|
|
35
35
|
for (const serverDataHook of this.serverDataCallbacks) {
|
|
36
36
|
serverDataHook({serverData});
|
|
37
37
|
}
|
|
@@ -40,7 +40,12 @@ var ServerBootstrapServices = class {
|
|
|
40
40
|
this.serverDataCallbacks = [];
|
|
41
41
|
this._serviceAPI = {
|
|
42
42
|
addServerDataCallback: this.registerServerDataCallbacks.bind(this),
|
|
43
|
-
addLoaderPlugin:
|
|
43
|
+
addLoaderPlugin: (hooks) => {
|
|
44
|
+
if (hooks.loadModule) {
|
|
45
|
+
throw new Error("`loadModule` loader hook is not supported on the server.");
|
|
46
|
+
}
|
|
47
|
+
return loader.services.addLoaderPlugin(hooks);
|
|
48
|
+
}
|
|
44
49
|
};
|
|
45
50
|
}
|
|
46
51
|
get serviceAPI() {
|
package/build/cjs/utils.cjs
CHANGED
|
@@ -37,7 +37,6 @@ __export(exports, {
|
|
|
37
37
|
getRenderTimeout: () => getRenderTimeout,
|
|
38
38
|
getServerBootstrapServices: () => getServerBootstrapServices
|
|
39
39
|
});
|
|
40
|
-
var import_package = __toModule(require("@lwrjs/config/package"));
|
|
41
40
|
var import_diagnostics = __toModule(require("@lwrjs/diagnostics"));
|
|
42
41
|
var import_instrumentation = __toModule(require("@lwrjs/instrumentation"));
|
|
43
42
|
var import_shared_utils = __toModule(require("@lwrjs/shared-utils"));
|
|
@@ -53,28 +52,35 @@ function getRenderTimeout() {
|
|
|
53
52
|
function createSsrErrorMessage(specifier, e) {
|
|
54
53
|
return `Server-side rendering for "${specifier}" failed. Falling back to client-side rendering. Reason: ${(0, import_diagnostics.stringifyError)(e)}`;
|
|
55
54
|
}
|
|
56
|
-
async function getLoaderShim(resourceRegistry, runtimeEnvironment) {
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
|
|
55
|
+
async function getLoaderShim(resourceRegistry, runtimeEnvironment, bootstrapConfig) {
|
|
56
|
+
const {debug} = runtimeEnvironment;
|
|
57
|
+
const useDebug = debug && !(0, import_shared_utils.isLambdaEnv)();
|
|
58
|
+
const specifier = (0, import_shared_utils.getFeatureFlags)().LEGACY_LOADER ? useDebug ? "lwr-loader-shim-legacy.bundle.js" : "lwr-loader-shim-legacy.bundle.min.js" : useDebug ? "lwr-loader-shim.bundle.js" : "lwr-loader-shim.bundle.min.js";
|
|
59
|
+
const resource = await resourceRegistry.getResource({specifier, version: bootstrapConfig.lwrVersion}, runtimeEnvironment, {ignoreDebug: !useDebug});
|
|
60
|
+
if (!resource?.content && !resource?.stream) {
|
|
60
61
|
throw new Error("Failed to find the loader shim");
|
|
61
62
|
}
|
|
62
63
|
let result = "";
|
|
63
|
-
|
|
64
|
-
result
|
|
64
|
+
if (resource.content) {
|
|
65
|
+
result = resource.content;
|
|
66
|
+
} else {
|
|
67
|
+
const stream = resource.stream();
|
|
68
|
+
for await (const chunk of stream) {
|
|
69
|
+
result += chunk;
|
|
70
|
+
}
|
|
65
71
|
}
|
|
66
72
|
result += `
|
|
67
73
|
//# sourceURL=${resource.entry}`;
|
|
68
74
|
return result;
|
|
69
75
|
}
|
|
70
|
-
function getLoaderId(config) {
|
|
76
|
+
function getLoaderId(config, bootstrapConfig) {
|
|
71
77
|
if (config.requiredModules) {
|
|
72
78
|
const id = config.requiredModules.find((specifier) => specifier.startsWith("lwr/loader"));
|
|
73
79
|
if (id) {
|
|
74
80
|
return id;
|
|
75
81
|
}
|
|
76
82
|
}
|
|
77
|
-
const version = (0, import_shared_utils.normalizeVersionToUri)(
|
|
83
|
+
const version = (0, import_shared_utils.normalizeVersionToUri)(bootstrapConfig.lwrVersion);
|
|
78
84
|
return (0, import_shared_utils.getFeatureFlags)().LEGACY_LOADER ? `lwr/loaderLegacy/v/${version}` : `lwr/loader/v/${version}`;
|
|
79
85
|
}
|
|
80
86
|
function getLoaderConfig(bootstrapModule, config, runtimeParams, serverData) {
|
|
@@ -121,7 +127,7 @@ var FetchController = class {
|
|
|
121
127
|
this.controllers.add(controller);
|
|
122
128
|
const fetchFunction = this.noOpActivated ? this.fetchNoOp(request, updatedInit) : this.fetchEndowment(request, updatedInit);
|
|
123
129
|
const fetchPromise = fetchFunction.catch((error) => {
|
|
124
|
-
if (
|
|
130
|
+
if (error && error?.stack.startsWith("AbortError")) {
|
|
125
131
|
return this.handleAbortError(request, error);
|
|
126
132
|
} else {
|
|
127
133
|
throw error;
|
|
@@ -138,9 +144,15 @@ var FetchController = class {
|
|
|
138
144
|
});
|
|
139
145
|
this.controllers.clear();
|
|
140
146
|
};
|
|
147
|
+
this.deactivateKillSwitch = () => {
|
|
148
|
+
this.killSwitchActivated = false;
|
|
149
|
+
};
|
|
141
150
|
this.activateNoOp = () => {
|
|
142
151
|
this.noOpActivated = true;
|
|
143
152
|
};
|
|
153
|
+
this.deactivateNoOp = () => {
|
|
154
|
+
this.noOpActivated = false;
|
|
155
|
+
};
|
|
144
156
|
this.killSwitchActivated = false;
|
|
145
157
|
this.noOpActivated = false;
|
|
146
158
|
this.controllers = new Set();
|
|
@@ -59,9 +59,9 @@ var LwcViewProvider = class extends import_base_view_provider.default {
|
|
|
59
59
|
filePath: specifier,
|
|
60
60
|
viewId,
|
|
61
61
|
properties: viewProperties,
|
|
62
|
-
render: async (runtimeParams) => {
|
|
63
|
-
const {config, moduleBundler, resourceRegistry
|
|
64
|
-
const debug =
|
|
62
|
+
render: async (runtimeParams, runtimeEnvironment) => {
|
|
63
|
+
const {config, moduleBundler, resourceRegistry} = this;
|
|
64
|
+
const {debug} = runtimeEnvironment;
|
|
65
65
|
const element = (0, import_shared_utils.moduleSpecifierToKebabCase)(specifier);
|
|
66
66
|
return (0, import_instrumentation.getTracer)().trace({
|
|
67
67
|
name: import_instrumentation.ViewSpan.RenderPage,
|
|
@@ -71,7 +71,7 @@ var LwcViewProvider = class extends import_base_view_provider.default {
|
|
|
71
71
|
if (!route) {
|
|
72
72
|
throw new Error(`Unable to resolve configuration for view: ${viewId.id}`);
|
|
73
73
|
}
|
|
74
|
-
const {results = {}, errors} = await (0, import_renderer.getRenderer)(config, moduleBundler, resourceRegistry).render({[specifier]: {specifier, props: {}}}, route, runtimeEnvironment, Object(runtimeParams));
|
|
74
|
+
const {results = {}, errors} = await (0, import_renderer.getRenderer)(config, moduleBundler, resourceRegistry).render({[specifier]: {specifier, props: {}}}, route, runtimeEnvironment, Object(runtimeParams), void 0, true);
|
|
75
75
|
if (errors) {
|
|
76
76
|
const errorString = Object.values(errors).join(", ");
|
|
77
77
|
if (!debug && !(0, import_shared_utils.getFeatureFlags)().SSR_WITH_CSR_FALLBACK) {
|
|
@@ -65,7 +65,7 @@ function lwcSsrViewTransformer(options, {config, moduleBundler, resourceRegistry
|
|
|
65
65
|
if (!route) {
|
|
66
66
|
throw new Error(`Unable to resolve configuration for view: ${viewContext.view.id}`);
|
|
67
67
|
}
|
|
68
|
-
const {results, errors} = await (0, import_renderer.getRenderer)(config, moduleBundler, resourceRegistry).render(ssrModules, route, viewContext.runtimeEnvironment, viewContext.runtimeParams, metadata.serverData);
|
|
68
|
+
const {results, errors} = await (0, import_renderer.getRenderer)(config, moduleBundler, resourceRegistry).render(ssrModules, route, viewContext.runtimeEnvironment, viewContext.runtimeParams, metadata.serverData, false);
|
|
69
69
|
for (const root in results) {
|
|
70
70
|
const {html, props, cache} = results[root];
|
|
71
71
|
const {tagName, startOffset, endOffset, hydrate} = ssrModules[root];
|
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
import type { ClientBootstrapConfig, ProviderAppConfig, PublicModuleBundler, PublicResourceRegistry, RuntimeEnvironment, RuntimeParams, ServerData, ServerServiceAPI } from '@lwrjs/types';
|
|
1
|
+
import type { ClientBootstrapConfig, NormalizedLwrAppBootstrapConfig, ProviderAppConfig, PublicModuleBundler, PublicResourceRegistry, RuntimeEnvironment, RuntimeParams, ServerData, ServerServiceAPI } from '@lwrjs/types';
|
|
2
|
+
export declare const FETCH_ABORT_KEY = "__fetchAbortId__";
|
|
2
3
|
export interface Context extends Record<string, any> {
|
|
3
4
|
LWR: ClientBootstrapConfig;
|
|
4
5
|
}
|
|
5
6
|
export interface ContextController {
|
|
6
7
|
enableNoOpFetch: () => void;
|
|
7
|
-
|
|
8
|
+
disableNoOpFetch: () => void;
|
|
9
|
+
enableFetchKillSwitch: () => void;
|
|
10
|
+
disableFetchKillSwitch: () => void;
|
|
8
11
|
}
|
|
9
12
|
export interface ModuleLoader {
|
|
10
13
|
load: (specifier: string, aliases?: string[]) => Promise<any>;
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
getFetchController: () => ContextController;
|
|
15
|
+
resetGlobalContext: () => void;
|
|
16
|
+
getContext: () => Context;
|
|
17
|
+
clearRegistry: Function;
|
|
18
|
+
services: ServerServiceAPI;
|
|
13
19
|
}
|
|
14
|
-
export declare function createModuleLoader(config: ProviderAppConfig, resourceRegistry: PublicResourceRegistry, bundleRegistry: PublicModuleBundler, runtimeEnvironment: RuntimeEnvironment, runtimeParams: RuntimeParams, serverData: ServerData): Promise<ModuleLoader>;
|
|
20
|
+
export declare function createModuleLoader(config: ProviderAppConfig, resourceRegistry: PublicResourceRegistry, bundleRegistry: PublicModuleBundler, runtimeEnvironment: RuntimeEnvironment, runtimeParams: RuntimeParams, serverData: ServerData, bootstrapConfig: NormalizedLwrAppBootstrapConfig): Promise<ModuleLoader>;
|
|
15
21
|
//# sourceMappingURL=moduleLoader.d.ts.map
|
package/build/es/moduleLoader.js
CHANGED
|
@@ -1,44 +1,56 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import crypto from 'crypto';
|
|
3
3
|
import { getTracer } from '@lwrjs/instrumentation';
|
|
4
|
-
import { explodeSpecifier, getSpecifier } from '@lwrjs/shared-utils';
|
|
4
|
+
import { explodeSpecifier, getLocalDevOverrideUrl, getSpecifier, isLocalDev } from '@lwrjs/shared-utils';
|
|
5
5
|
import { FetchController, createFetchEndowment, getLoaderConfig, getLoaderId, getLoaderShim, } from './utils.js';
|
|
6
|
+
export const FETCH_ABORT_KEY = '__fetchAbortId__';
|
|
6
7
|
const BOOTSTRAP_SPECIFIER = '@lwrjs/ssr-bootstrap';
|
|
7
|
-
export function createModuleLoader(config, resourceRegistry, bundleRegistry, runtimeEnvironment, runtimeParams, serverData) {
|
|
8
|
+
export function createModuleLoader(config, resourceRegistry, bundleRegistry, runtimeEnvironment, runtimeParams, serverData, bootstrapConfig) {
|
|
8
9
|
const createLoader = runtimeEnvironment.format === 'amd' ? createAMDModuleLoader : createESMModuleLoader;
|
|
9
|
-
return createLoader(config, resourceRegistry, bundleRegistry, runtimeEnvironment, runtimeParams, serverData);
|
|
10
|
+
return createLoader(config, resourceRegistry, bundleRegistry, runtimeEnvironment, runtimeParams, serverData, bootstrapConfig);
|
|
10
11
|
}
|
|
11
|
-
async function createAMDModuleLoader(config, resourceRegistry, bundleRegistry, runtimeEnvironment, runtimeParams, serverData) {
|
|
12
|
+
async function createAMDModuleLoader(config, resourceRegistry, bundleRegistry, runtimeEnvironment, runtimeParams, serverData, bootstrapConfig) {
|
|
12
13
|
// creating a render context to avoid polluting globals
|
|
13
14
|
const loaderConfig = getLoaderConfig(BOOTSTRAP_SPECIFIER, config, runtimeParams, serverData);
|
|
14
15
|
const { context, controller } = createContext(loaderConfig, runtimeParams);
|
|
16
|
+
const contextKeyMap = new Map(Object.keys(context).map((key) => [key, true]));
|
|
15
17
|
// attaching custom init to delay bootstrap module evaluation
|
|
16
18
|
let run;
|
|
17
19
|
context.LWR.customInit = async (lwr) => {
|
|
18
20
|
run = lwr.initializeApp;
|
|
19
21
|
};
|
|
20
22
|
// instantiate modules with shadowed globals
|
|
23
|
+
const useEval = process.env.SSR_DEBUG === 'true';
|
|
21
24
|
const init = (source) => {
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
if (!useEval) {
|
|
26
|
+
const fn = new Function('globalThis', ...Object.keys(context), source);
|
|
27
|
+
fn(context, ...Object.values(context));
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
// use eval to ensure source map line numbers are correct for debugging
|
|
31
|
+
((context) => {
|
|
32
|
+
eval(`${Object.keys(context).reduce((c, k) => c + `const ${k} = context['${k}'];`, 'globalThis=context;')} ${source}`);
|
|
33
|
+
})(context);
|
|
34
|
+
}
|
|
24
35
|
};
|
|
25
36
|
// load and instantiate the loader shim
|
|
26
|
-
init(await getLoaderShim(resourceRegistry, runtimeEnvironment));
|
|
37
|
+
init(await getLoaderShim(resourceRegistry, runtimeEnvironment, bootstrapConfig));
|
|
27
38
|
if (!run) {
|
|
28
39
|
throw new Error('Failed to init loader shim: custom init not called');
|
|
29
40
|
}
|
|
30
41
|
// manually define bootstrap module to export the loader
|
|
31
42
|
const p = new Promise((resolve) => {
|
|
32
|
-
context.LWR.define(BOOTSTRAP_SPECIFIER, [getLoaderId(context.LWR)], (l) => resolve(l));
|
|
43
|
+
context.LWR.define(BOOTSTRAP_SPECIFIER, [getLoaderId(context.LWR, bootstrapConfig)], (l) => resolve(l));
|
|
33
44
|
});
|
|
34
45
|
// execute the bootstrap module
|
|
35
46
|
run();
|
|
36
47
|
// loader API should be available after bootstrap module evaluation
|
|
37
|
-
const { load, services } = await p;
|
|
48
|
+
const { load, services, clearRegistry } = await p;
|
|
38
49
|
// todo: could this use the loader's module cache?
|
|
39
50
|
const visited = new Set(['lwc']);
|
|
40
51
|
return {
|
|
41
52
|
services,
|
|
53
|
+
clearRegistry,
|
|
42
54
|
load: async (specifier, aliases) => {
|
|
43
55
|
const injectBundle = async (bundle, aliases) => {
|
|
44
56
|
if (visited.has(bundle) && specifier !== bundle) {
|
|
@@ -47,10 +59,13 @@ async function createAMDModuleLoader(config, resourceRegistry, bundleRegistry, r
|
|
|
47
59
|
visited.add(bundle);
|
|
48
60
|
const moduleId = explodeSpecifier(bundle);
|
|
49
61
|
const def = await bundleRegistry.getModuleBundle(moduleId, runtimeEnvironment, runtimeParams);
|
|
50
|
-
if (typeof def.src === 'string') {
|
|
51
|
-
|
|
62
|
+
if (typeof def.src === 'string' && !def.code.includes('//# sourceURL=')) {
|
|
63
|
+
// @view bundles are stored in a special location during local-dev
|
|
64
|
+
const srcUrl = isLocalDev()
|
|
65
|
+
? getLocalDevOverrideUrl(config.cacheDir, moduleId.specifier, def.src)
|
|
66
|
+
: def.src;
|
|
67
|
+
def.code += `\n//# sourceURL=${path.resolve(srcUrl)}`;
|
|
52
68
|
}
|
|
53
|
-
// todo: could this be a loader hook?
|
|
54
69
|
const staticImports = def.bundleRecord.imports?.map((dep) => getSpecifier(dep)) ?? [];
|
|
55
70
|
const dynamicImports = def.bundleRecord.dynamicImports?.map((dep) => getSpecifier(dep)) ?? [];
|
|
56
71
|
if (staticImports.length || dynamicImports.length) {
|
|
@@ -59,6 +74,7 @@ async function createAMDModuleLoader(config, resourceRegistry, bundleRegistry, r
|
|
|
59
74
|
}
|
|
60
75
|
// inject the bundle into the loader's module registry
|
|
61
76
|
init(def.code);
|
|
77
|
+
// TODO: fix duplicate aliases problem
|
|
62
78
|
// create aliases
|
|
63
79
|
if (aliases?.length) {
|
|
64
80
|
for (const alias of aliases) {
|
|
@@ -69,7 +85,7 @@ async function createAMDModuleLoader(config, resourceRegistry, bundleRegistry, r
|
|
|
69
85
|
}), [def.id], (mod) => mod);
|
|
70
86
|
}
|
|
71
87
|
}
|
|
72
|
-
//
|
|
88
|
+
// TODO: remove this once we confirm it's no longer needed in CLWR
|
|
73
89
|
if (def.specifier.startsWith('@app') || def.specifier.startsWith('@salesforce')) {
|
|
74
90
|
context.LWR.define(getSpecifier(def), [def.specifier], (mod) => mod);
|
|
75
91
|
}
|
|
@@ -87,7 +103,27 @@ async function createAMDModuleLoader(config, resourceRegistry, bundleRegistry, r
|
|
|
87
103
|
module: mod,
|
|
88
104
|
};
|
|
89
105
|
},
|
|
90
|
-
|
|
106
|
+
getFetchController: () => controller,
|
|
107
|
+
resetGlobalContext: () => {
|
|
108
|
+
// Clear the context of all of the keys that may be added by module evaluation.
|
|
109
|
+
// Do not delete keys that we added ourselves.
|
|
110
|
+
for (const key in context) {
|
|
111
|
+
if (Object.prototype.hasOwnProperty.call(context, key) && !contextKeyMap.has(key)) {
|
|
112
|
+
delete context[key];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Reset LWR globals as needed.
|
|
116
|
+
// Note: we only need to reset serverData today, since it is the only
|
|
117
|
+
// property that mutates between page renders.
|
|
118
|
+
context.LWR = {
|
|
119
|
+
...context.LWR,
|
|
120
|
+
serverData: undefined,
|
|
121
|
+
};
|
|
122
|
+
},
|
|
123
|
+
// Note: only used for testing
|
|
124
|
+
getContext: () => {
|
|
125
|
+
return context;
|
|
126
|
+
},
|
|
91
127
|
};
|
|
92
128
|
}
|
|
93
129
|
async function createESMModuleLoader() {
|
|
@@ -113,7 +149,7 @@ function createContext(LWR, runtimeParams) {
|
|
|
113
149
|
// browser api polyfills
|
|
114
150
|
crypto,
|
|
115
151
|
CustomEvent: Event,
|
|
116
|
-
// global api
|
|
152
|
+
// global fetch api override
|
|
117
153
|
fetch: fetchController.controlledFetch,
|
|
118
154
|
};
|
|
119
155
|
return {
|
|
@@ -122,9 +158,15 @@ function createContext(LWR, runtimeParams) {
|
|
|
122
158
|
enableNoOpFetch: () => {
|
|
123
159
|
fetchController.activateNoOp();
|
|
124
160
|
},
|
|
125
|
-
|
|
161
|
+
disableNoOpFetch: () => {
|
|
162
|
+
fetchController.deactivateNoOp();
|
|
163
|
+
},
|
|
164
|
+
enableFetchKillSwitch: () => {
|
|
126
165
|
fetchController.activateKillSwitch();
|
|
127
166
|
},
|
|
167
|
+
disableFetchKillSwitch: () => {
|
|
168
|
+
fetchController.deactivateKillSwitch();
|
|
169
|
+
},
|
|
128
170
|
},
|
|
129
171
|
};
|
|
130
172
|
}
|
package/build/es/renderer.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { NormalizedLwrErrorRoute, NormalizedLwrRoute, ProviderAppConfig, PublicModuleBundler, PublicResourceRegistry, RuntimeEnvironment, RuntimeParams, ServerData, SsrDataResponse } from '@lwrjs/types';
|
|
2
|
+
import { LRUCache } from 'lru-cache';
|
|
3
|
+
import { TaskPool } from '@lwrjs/shared-utils';
|
|
4
|
+
import { ModuleLoader } from './moduleLoader.js';
|
|
2
5
|
export interface Component {
|
|
3
6
|
specifier: string;
|
|
4
7
|
props?: {
|
|
@@ -20,10 +23,18 @@ export interface RenderResults {
|
|
|
20
23
|
* @returns singleton
|
|
21
24
|
*/
|
|
22
25
|
export declare function getRenderer(config: ProviderAppConfig, bundleRegistry: PublicModuleBundler, resourceRegistry: PublicResourceRegistry): Renderer;
|
|
26
|
+
interface BootstrapContext {
|
|
27
|
+
loader: Promise<ModuleLoader>;
|
|
28
|
+
bootstrapServiceEvaluationMap: Map<string, boolean>;
|
|
29
|
+
}
|
|
23
30
|
export declare class Renderer {
|
|
24
31
|
config: ProviderAppConfig;
|
|
25
32
|
bundleRegistry: PublicModuleBundler;
|
|
26
33
|
resourceRegistry: PublicResourceRegistry;
|
|
34
|
+
contextPerEnv: LRUCache<string, BootstrapContext>;
|
|
35
|
+
cachedBootstrapContext: BootstrapContext | undefined;
|
|
36
|
+
pendingRender: Promise<RenderResults> | undefined;
|
|
37
|
+
pendingRenders: TaskPool;
|
|
27
38
|
constructor(config: ProviderAppConfig, bundleRegistry: PublicModuleBundler, resourceRegistry: PublicResourceRegistry);
|
|
28
39
|
/**
|
|
29
40
|
* Render components to HTML strings
|
|
@@ -41,7 +52,9 @@ export declare class Renderer {
|
|
|
41
52
|
* @param serverData - render data TODO serverData is modified (add test?)
|
|
42
53
|
* @returns render results and errors per component
|
|
43
54
|
*/
|
|
44
|
-
render(components: Record<string, Component>, route: NormalizedLwrRoute | NormalizedLwrErrorRoute, runtimeEnvironment: RuntimeEnvironment, runtimeParams: RuntimeParams, serverData?: ServerData): Promise<RenderResults>;
|
|
55
|
+
render(components: Record<string, Component>, route: NormalizedLwrRoute | NormalizedLwrErrorRoute, runtimeEnvironment: RuntimeEnvironment, runtimeParams: RuntimeParams, serverData?: ServerData, isFirstOf2PassSSR?: boolean): Promise<RenderResults>;
|
|
45
56
|
private renderComponents;
|
|
57
|
+
private getCachedBootstrapContext;
|
|
46
58
|
}
|
|
59
|
+
export {};
|
|
47
60
|
//# sourceMappingURL=renderer.d.ts.map
|
package/build/es/renderer.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
// TODO: investigate perf impact W-16056356
|
|
2
|
+
import { LRUCache } from 'lru-cache';
|
|
1
3
|
import { LwrUnresolvableError, createSingleDiagnosticError, descriptions, logger, stringifyError, } from '@lwrjs/diagnostics';
|
|
2
|
-
import { moduleSpecifierToKebabCase } from '@lwrjs/shared-utils';
|
|
4
|
+
import { buildEnvironmentContext, getCacheKeyFromJson, getSpecifier, isLambdaEnv, moduleSpecifierToKebabCase, getFeatureFlags, TaskPool, } from '@lwrjs/shared-utils';
|
|
3
5
|
import { ViewSpan, getTracer } from '@lwrjs/instrumentation';
|
|
4
6
|
import { getServerBootstrapServices, getRenderTimeout } from './utils.js';
|
|
5
7
|
import { createModuleLoader } from './moduleLoader.js';
|
|
@@ -24,6 +26,19 @@ export class Renderer {
|
|
|
24
26
|
this.config = config;
|
|
25
27
|
this.bundleRegistry = bundleRegistry;
|
|
26
28
|
this.resourceRegistry = resourceRegistry;
|
|
29
|
+
// Prevent number of cached contexts from growing unbounded.
|
|
30
|
+
// In reality, the number of env permutations should be relatively small
|
|
31
|
+
this.contextPerEnv = new LRUCache({
|
|
32
|
+
max: 50,
|
|
33
|
+
dispose: () => {
|
|
34
|
+
logger.info('evicted bootstrap context from renderer cache');
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
// TODO: remove the task pool altogether once W-16104831 and W-16047359 are resolved.
|
|
38
|
+
// Until the above issues are resolved, we can only handle one request at a time.
|
|
39
|
+
// Since Lambda's can only handle 1 request at a time anyways, this is fine for now.
|
|
40
|
+
const taskPoolSize = getFeatureFlags().SINGLE_RENDER_MODE ? 1 : 15;
|
|
41
|
+
this.pendingRenders = new TaskPool(taskPoolSize);
|
|
27
42
|
}
|
|
28
43
|
/**
|
|
29
44
|
* Render components to HTML strings
|
|
@@ -41,46 +56,80 @@ export class Renderer {
|
|
|
41
56
|
* @param serverData - render data TODO serverData is modified (add test?)
|
|
42
57
|
* @returns render results and errors per component
|
|
43
58
|
*/
|
|
44
|
-
async render(components, route, runtimeEnvironment, runtimeParams, serverData = {}) {
|
|
45
|
-
let
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
this.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
async render(components, route, runtimeEnvironment, runtimeParams, serverData = {}, isFirstOf2PassSSR) {
|
|
60
|
+
let result;
|
|
61
|
+
// Only use a timeout when running in a lambda environment or if we've manually specified a timeout (for local testing purposes)
|
|
62
|
+
if (isLambdaEnv() || process.env.SSR_TIMEOUT !== undefined) {
|
|
63
|
+
let timerId;
|
|
64
|
+
const timeout = new Promise((_, reject) => {
|
|
65
|
+
timerId = setTimeout(() => {
|
|
66
|
+
reject(createSingleDiagnosticError({
|
|
67
|
+
description: descriptions.UNRESOLVABLE.SSR_TIMEOUT(route.id, getRenderTimeout()),
|
|
68
|
+
}, LwrUnresolvableError));
|
|
69
|
+
}, getRenderTimeout());
|
|
70
|
+
});
|
|
71
|
+
result = (await this.pendingRenders.execute(async () => {
|
|
72
|
+
return Promise.race([
|
|
73
|
+
timeout,
|
|
74
|
+
// todo: abort the render if timeout occurs
|
|
75
|
+
this.renderComponents(components, route, runtimeEnvironment, runtimeParams, serverData, isFirstOf2PassSSR),
|
|
76
|
+
]);
|
|
77
|
+
}));
|
|
78
|
+
clearTimeout(timerId);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
result = (await this.pendingRenders.execute(async () => {
|
|
82
|
+
return this.renderComponents(components, route, runtimeEnvironment, runtimeParams, serverData, isFirstOf2PassSSR);
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
59
85
|
return result;
|
|
60
86
|
}
|
|
61
|
-
async renderComponents(components, route, runtimeEnvironment, runtimeParams, serverData = {}) {
|
|
87
|
+
async renderComponents(components, route, runtimeEnvironment, runtimeParams, serverData = {}, isFirstOf2PassSSR = false) {
|
|
62
88
|
const results = {};
|
|
63
89
|
const errors = {};
|
|
64
90
|
const roots = Object.keys(components);
|
|
65
91
|
const services = getServerBootstrapServices(route);
|
|
66
|
-
|
|
92
|
+
// TODO: not needed once W-16104831 is resolved
|
|
93
|
+
const reevaluateModules = getFeatureFlags().REEVALUATE_MODULES === true;
|
|
94
|
+
const singleRenderMode = getFeatureFlags().SINGLE_RENDER_MODE;
|
|
95
|
+
let loader, loaderPromise, bootstrapServiceEvaluationMap;
|
|
67
96
|
try {
|
|
68
|
-
|
|
97
|
+
// We want a new context (and cache it for re-use) for each LWR env context.
|
|
98
|
+
// E.g. if we `env.basePath: '/'` vs `env.basePath: '/foo` will result
|
|
99
|
+
// in the new contexts created and used for by the renderer.
|
|
100
|
+
//
|
|
101
|
+
// TODO: W-16104831: when `REEVALUATE_MODULES` is enabled, we only have a single context/loader, regardless of env
|
|
102
|
+
const bootstrapContext = this.getCachedBootstrapContext(route, runtimeEnvironment, runtimeParams, serverData, reevaluateModules);
|
|
103
|
+
loaderPromise = bootstrapContext.loader;
|
|
104
|
+
bootstrapServiceEvaluationMap = bootstrapContext.bootstrapServiceEvaluationMap;
|
|
105
|
+
loader = await loaderPromise;
|
|
106
|
+
// Re-enable fetch that was disabled at the end of the previous page render via `enableFetchKillSwitch`
|
|
107
|
+
loader.getFetchController().disableFetchKillSwitch();
|
|
69
108
|
// load and alias the LWC server engine
|
|
70
109
|
// this MUST be done first in case bootstrap services depend on LWC
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
110
|
+
const engineServerSpecifier = getSpecifier({
|
|
111
|
+
specifier: '@lwc/engine-server',
|
|
112
|
+
version: route.bootstrap.lwcVersion,
|
|
113
|
+
});
|
|
114
|
+
const engine = await loader.load(engineServerSpecifier, ['lwc']);
|
|
115
|
+
// By default, we only run bootstrap services once per page render.
|
|
116
|
+
const shouldRunBootstrapServices = !bootstrapServiceEvaluationMap?.has(route.id);
|
|
117
|
+
let serverBootstrapServices;
|
|
118
|
+
if (shouldRunBootstrapServices) {
|
|
119
|
+
bootstrapServiceEvaluationMap?.set(route.id, true);
|
|
120
|
+
// bootstrap services MUST load/evaluate before any module resolution (except LWC)
|
|
121
|
+
// this ensures any loader `resolveHook` is registered first
|
|
122
|
+
const serviceModules = await Promise.all(services.map((specifier) => loader?.load(specifier)));
|
|
123
|
+
// set up the server-side Service API for the loader
|
|
124
|
+
serverBootstrapServices = createServerBootstrapServices(loader);
|
|
125
|
+
// evaluate the default function from each service module, passing in the Service API
|
|
126
|
+
// this is where the loader and server data hooks are set
|
|
127
|
+
for (const service of serviceModules) {
|
|
128
|
+
// eslint-disable-next-line
|
|
129
|
+
const error = await evaluateServerBootstrapModule(service, serverBootstrapServices.serviceAPI);
|
|
130
|
+
if (error) {
|
|
131
|
+
errors[service.specifier] = error;
|
|
132
|
+
}
|
|
84
133
|
}
|
|
85
134
|
}
|
|
86
135
|
// load root component modules (and dependencies ofc)
|
|
@@ -110,8 +159,10 @@ export class Renderer {
|
|
|
110
159
|
results[component.specifier] = data;
|
|
111
160
|
}
|
|
112
161
|
}
|
|
113
|
-
|
|
114
|
-
|
|
162
|
+
if (serverBootstrapServices) {
|
|
163
|
+
// now that we have server data, run the server data hooks
|
|
164
|
+
serverBootstrapServices.evaluateServerDataHooks(serverData);
|
|
165
|
+
}
|
|
115
166
|
// exit early when preloading data
|
|
116
167
|
if (!route.bootstrap.ssr) {
|
|
117
168
|
if (Object.keys(errors).length) {
|
|
@@ -119,8 +170,11 @@ export class Renderer {
|
|
|
119
170
|
}
|
|
120
171
|
return { results };
|
|
121
172
|
}
|
|
122
|
-
//
|
|
123
|
-
|
|
173
|
+
// TODO: W-16047359 - reactivate this outside of the Lambda
|
|
174
|
+
if (getFeatureFlags().SINGLE_RENDER_MODE) {
|
|
175
|
+
// Disable async APIs before rendering components
|
|
176
|
+
loader.getFetchController().enableNoOpFetch();
|
|
177
|
+
}
|
|
124
178
|
// render components
|
|
125
179
|
for (const component of componentModules) {
|
|
126
180
|
// skip rendering if an error has already occurred for the component
|
|
@@ -148,9 +202,62 @@ export class Renderer {
|
|
|
148
202
|
};
|
|
149
203
|
}
|
|
150
204
|
finally {
|
|
151
|
-
//
|
|
152
|
-
|
|
205
|
+
// TODO: W-16047359 - remove this check
|
|
206
|
+
if (singleRenderMode) {
|
|
207
|
+
loader?.getFetchController().disableNoOpFetch();
|
|
208
|
+
// activate fetch controller kill switch
|
|
209
|
+
loader?.getFetchController().enableFetchKillSwitch();
|
|
210
|
+
}
|
|
211
|
+
// At the end of 2-pass SSR, clean up for the next page render
|
|
212
|
+
if (!isFirstOf2PassSSR) {
|
|
213
|
+
// since we have a single global context that is maintained across page renders,
|
|
214
|
+
// we need to clean it up so that state such as globalThis.LWR.serverData is not preserved
|
|
215
|
+
loader?.resetGlobalContext();
|
|
216
|
+
// TODO: not needed once W-16104831 is resolved
|
|
217
|
+
// When `REEVALUATE_MODULES` is enabled, we clear the loader's module registry + bootstrap services
|
|
218
|
+
// at the end of every render so that module state is not preserved.
|
|
219
|
+
if (reevaluateModules) {
|
|
220
|
+
// clear bootstrap services so that next page render can call bootstrap services again
|
|
221
|
+
bootstrapServiceEvaluationMap?.delete(route.id);
|
|
222
|
+
loader?.clearRegistry();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
getCachedBootstrapContext(route, runtimeEnvironment, runtimeParams, serverData, reevaluateModules) {
|
|
228
|
+
let loader, bootstrapServiceEvaluationMap;
|
|
229
|
+
// when `REEEVALUTE_MODULES` is true, we only have a single context/loader, regardless of the env
|
|
230
|
+
if (reevaluateModules) {
|
|
231
|
+
if (!this.cachedBootstrapContext) {
|
|
232
|
+
this.cachedBootstrapContext = {
|
|
233
|
+
loader: createModuleLoader(this.config, this.resourceRegistry, this.bundleRegistry, runtimeEnvironment, runtimeParams, serverData, route.bootstrap),
|
|
234
|
+
bootstrapServiceEvaluationMap: new Map(),
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
return this.cachedBootstrapContext;
|
|
238
|
+
}
|
|
239
|
+
// The cache key is derived from LWR env context AND the host header,
|
|
240
|
+
// because we associate the host header to each fetch context.
|
|
241
|
+
const envContext = buildEnvironmentContext(runtimeParams);
|
|
242
|
+
const { host, requestDepth } = runtimeParams;
|
|
243
|
+
const contextCacheKey = getCacheKeyFromJson({ envContext, host, requestDepth });
|
|
244
|
+
const bootstrapContext = this.contextPerEnv.get(contextCacheKey);
|
|
245
|
+
if (!bootstrapContext) {
|
|
246
|
+
loader = createModuleLoader(this.config, this.resourceRegistry, this.bundleRegistry, runtimeEnvironment, runtimeParams, serverData, route.bootstrap);
|
|
247
|
+
bootstrapServiceEvaluationMap = new Map();
|
|
248
|
+
this.contextPerEnv.set(contextCacheKey, {
|
|
249
|
+
loader,
|
|
250
|
+
bootstrapServiceEvaluationMap,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
loader = bootstrapContext.loader;
|
|
255
|
+
bootstrapServiceEvaluationMap = bootstrapContext.bootstrapServiceEvaluationMap;
|
|
153
256
|
}
|
|
257
|
+
return {
|
|
258
|
+
loader: loader,
|
|
259
|
+
bootstrapServiceEvaluationMap,
|
|
260
|
+
};
|
|
154
261
|
}
|
|
155
262
|
}
|
|
156
263
|
/**
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { ServerServiceAPI, ServerData } from '@lwrjs/types';
|
|
2
|
-
import type { ModuleLoader } from './moduleLoader';
|
|
2
|
+
import type { ModuleLoader } from './moduleLoader.js';
|
|
3
3
|
export declare class ServerBootstrapServices {
|
|
4
4
|
private serverDataCallbacks;
|
|
5
5
|
_serviceAPI: ServerServiceAPI;
|
|
6
|
-
evaluateServerDataHooks(serverData
|
|
6
|
+
evaluateServerDataHooks(serverData?: ServerData): void;
|
|
7
7
|
constructor(loader: ModuleLoader);
|
|
8
8
|
get serviceAPI(): ServerServiceAPI;
|
|
9
9
|
private registerServerDataCallbacks;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ViewSpan, getTracer } from '@lwrjs/instrumentation';
|
|
2
2
|
import { logger, stringifyError } from '@lwrjs/diagnostics';
|
|
3
3
|
export class ServerBootstrapServices {
|
|
4
|
-
evaluateServerDataHooks(serverData) {
|
|
4
|
+
evaluateServerDataHooks(serverData = {}) {
|
|
5
5
|
// now that we have server data, run the server data hooks
|
|
6
6
|
for (const serverDataHook of this.serverDataCallbacks) {
|
|
7
7
|
serverDataHook({ serverData });
|
|
@@ -11,8 +11,12 @@ export class ServerBootstrapServices {
|
|
|
11
11
|
this.serverDataCallbacks = [];
|
|
12
12
|
this._serviceAPI = {
|
|
13
13
|
addServerDataCallback: this.registerServerDataCallbacks.bind(this),
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
addLoaderPlugin: (hooks) => {
|
|
15
|
+
if (hooks.loadModule) {
|
|
16
|
+
throw new Error('`loadModule` loader hook is not supported on the server.');
|
|
17
|
+
}
|
|
18
|
+
return loader.services.addLoaderPlugin(hooks);
|
|
19
|
+
},
|
|
16
20
|
};
|
|
17
21
|
}
|
|
18
22
|
get serviceAPI() {
|
package/build/es/utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ClientBootstrapConfig, EnvironmentContext, LwrStringBuilder, NormalizedLwrErrorRoute, NormalizedLwrRoute, ProviderAppConfig, PublicResourceRegistry, RuntimeEnvironment, RuntimeParams, ServerData, SsrDataResponse } from '@lwrjs/types';
|
|
1
|
+
import type { ClientBootstrapConfig, EnvironmentContext, LwrStringBuilder, NormalizedLwrAppBootstrapConfig, NormalizedLwrErrorRoute, NormalizedLwrRoute, ProviderAppConfig, PublicResourceRegistry, RuntimeEnvironment, RuntimeParams, ServerData, SsrDataResponse } from '@lwrjs/types';
|
|
2
2
|
import { TraceFn } from '@lwrjs/instrumentation';
|
|
3
3
|
interface ServerEnvironment extends EnvironmentContext {
|
|
4
4
|
SSR: boolean;
|
|
@@ -7,8 +7,8 @@ export declare const SSR_PROPS_ATTR = "data-lwr-props-id";
|
|
|
7
7
|
export declare function getPropsId(): string;
|
|
8
8
|
export declare function getRenderTimeout(): number;
|
|
9
9
|
export declare function createSsrErrorMessage(specifier: string, e: any): string;
|
|
10
|
-
export declare function getLoaderShim(resourceRegistry: PublicResourceRegistry, runtimeEnvironment: RuntimeEnvironment): Promise<string>;
|
|
11
|
-
export declare function getLoaderId(config: ClientBootstrapConfig): string;
|
|
10
|
+
export declare function getLoaderShim(resourceRegistry: PublicResourceRegistry, runtimeEnvironment: RuntimeEnvironment, bootstrapConfig: NormalizedLwrAppBootstrapConfig): Promise<string>;
|
|
11
|
+
export declare function getLoaderId(config: ClientBootstrapConfig, bootstrapConfig: NormalizedLwrAppBootstrapConfig): string;
|
|
12
12
|
export declare function getLoaderConfig(bootstrapModule: string, config: ProviderAppConfig, runtimeParams: RuntimeParams, serverData: ServerData): ClientBootstrapConfig & {
|
|
13
13
|
env: ServerEnvironment;
|
|
14
14
|
};
|
|
@@ -34,11 +34,13 @@ export declare class FetchController {
|
|
|
34
34
|
* After SSR is complete the kill switch will abort any pending fetch requests.
|
|
35
35
|
*/
|
|
36
36
|
activateKillSwitch: () => void;
|
|
37
|
+
deactivateKillSwitch: () => void;
|
|
37
38
|
/**
|
|
38
39
|
* During SSR renderComponent (which is synchronous) Do not even call any fetch requests
|
|
39
40
|
* since they would not complete before SSR is done.
|
|
40
41
|
*/
|
|
41
42
|
activateNoOp: () => void;
|
|
43
|
+
deactivateNoOp: () => void;
|
|
42
44
|
private handleAbortError;
|
|
43
45
|
/**
|
|
44
46
|
* Create a fetch API that never calls a request.
|
package/build/es/utils.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { lwrVersion } from '@lwrjs/config/package';
|
|
2
1
|
import { logger, stringifyError } from '@lwrjs/diagnostics';
|
|
3
2
|
import { ViewSpan } from '@lwrjs/instrumentation';
|
|
4
|
-
import { REQUEST_DEPTH_HEADER, buildEnvironmentContext, getFeatureFlags, normalizeVersionToUri, } from '@lwrjs/shared-utils';
|
|
3
|
+
import { REQUEST_DEPTH_HEADER, buildEnvironmentContext, getFeatureFlags, normalizeVersionToUri, isLambdaEnv, } from '@lwrjs/shared-utils';
|
|
5
4
|
const DEFAULT_SSR_TIMEOUT = 5000; // 5 seconds, override with process.env.SSR_TIMEOUT
|
|
6
5
|
export const SSR_PROPS_ATTR = 'data-lwr-props-id';
|
|
7
6
|
export function getPropsId() {
|
|
@@ -14,22 +13,39 @@ export function getRenderTimeout() {
|
|
|
14
13
|
export function createSsrErrorMessage(specifier, e) {
|
|
15
14
|
return `Server-side rendering for "${specifier}" failed. Falling back to client-side rendering. Reason: ${stringifyError(e)}`;
|
|
16
15
|
}
|
|
17
|
-
export async function getLoaderShim(resourceRegistry, runtimeEnvironment) {
|
|
16
|
+
export async function getLoaderShim(resourceRegistry, runtimeEnvironment, bootstrapConfig) {
|
|
17
|
+
const { debug } = runtimeEnvironment;
|
|
18
|
+
// debug resources are not available in deployed lambda env
|
|
19
|
+
const useDebug = debug && !isLambdaEnv();
|
|
18
20
|
const specifier = getFeatureFlags().LEGACY_LOADER
|
|
19
|
-
?
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
? useDebug
|
|
22
|
+
? 'lwr-loader-shim-legacy.bundle.js'
|
|
23
|
+
: 'lwr-loader-shim-legacy.bundle.min.js'
|
|
24
|
+
: useDebug
|
|
25
|
+
? 'lwr-loader-shim.bundle.js'
|
|
26
|
+
: 'lwr-loader-shim.bundle.min.js';
|
|
27
|
+
const resource = await resourceRegistry.getResource({ specifier, version: bootstrapConfig.lwrVersion }, runtimeEnvironment,
|
|
28
|
+
// HACK: this code is tricky because resource IDs are different between prod vs debug ("lwr-loader-shim.bundle.min.js" vs "lwr-loader-shim.bundle.js").
|
|
29
|
+
// 1. In debug mode on Lambda (during SSR), we need to ignore runtimeEnvironment.debug because we will always ask for the prod version (lwr-loader-shim.bundle.min.js)
|
|
30
|
+
// 2. But when we generate the view, we can't ignore runtimeEnvironment.debug because we need the debug version of the loader shim (lwr-loader-shim.bundle.js)
|
|
31
|
+
{ ignoreDebug: !useDebug });
|
|
32
|
+
if (!resource?.content && !resource?.stream) {
|
|
23
33
|
throw new Error('Failed to find the loader shim');
|
|
24
34
|
}
|
|
25
35
|
let result = '';
|
|
26
|
-
|
|
27
|
-
result
|
|
36
|
+
if (resource.content) {
|
|
37
|
+
result = resource.content;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
const stream = resource.stream();
|
|
41
|
+
for await (const chunk of stream) {
|
|
42
|
+
result += chunk;
|
|
43
|
+
}
|
|
28
44
|
}
|
|
29
45
|
result += `\n//# sourceURL=${resource.entry}`;
|
|
30
46
|
return result;
|
|
31
47
|
}
|
|
32
|
-
export function getLoaderId(config) {
|
|
48
|
+
export function getLoaderId(config, bootstrapConfig) {
|
|
33
49
|
// TODO W-15509657 - hack: checking `requiredModules` because the loader may not be the same version as the current runtime
|
|
34
50
|
if (config.requiredModules) {
|
|
35
51
|
const id = config.requiredModules.find((specifier) => specifier.startsWith('lwr/loader'));
|
|
@@ -38,7 +54,7 @@ export function getLoaderId(config) {
|
|
|
38
54
|
}
|
|
39
55
|
}
|
|
40
56
|
// default to the active LWR version
|
|
41
|
-
const version = normalizeVersionToUri(lwrVersion);
|
|
57
|
+
const version = normalizeVersionToUri(bootstrapConfig.lwrVersion);
|
|
42
58
|
return getFeatureFlags().LEGACY_LOADER ? `lwr/loaderLegacy/v/${version}` : `lwr/loader/v/${version}`;
|
|
43
59
|
}
|
|
44
60
|
export function getLoaderConfig(bootstrapModule, config, runtimeParams, serverData) {
|
|
@@ -101,7 +117,7 @@ export class FetchController {
|
|
|
101
117
|
const fetchPromise = fetchFunction
|
|
102
118
|
.catch((error) => {
|
|
103
119
|
// Check if the error is an AbortError
|
|
104
|
-
if (
|
|
120
|
+
if (error && error?.stack.startsWith('AbortError')) {
|
|
105
121
|
return this.handleAbortError(request, error);
|
|
106
122
|
}
|
|
107
123
|
else {
|
|
@@ -124,6 +140,9 @@ export class FetchController {
|
|
|
124
140
|
});
|
|
125
141
|
this.controllers.clear(); // Clear the set as all fetch calls have been aborted
|
|
126
142
|
};
|
|
143
|
+
this.deactivateKillSwitch = () => {
|
|
144
|
+
this.killSwitchActivated = false;
|
|
145
|
+
};
|
|
127
146
|
/**
|
|
128
147
|
* During SSR renderComponent (which is synchronous) Do not even call any fetch requests
|
|
129
148
|
* since they would not complete before SSR is done.
|
|
@@ -131,6 +150,9 @@ export class FetchController {
|
|
|
131
150
|
this.activateNoOp = () => {
|
|
132
151
|
this.noOpActivated = true;
|
|
133
152
|
};
|
|
153
|
+
this.deactivateNoOp = () => {
|
|
154
|
+
this.noOpActivated = false;
|
|
155
|
+
};
|
|
134
156
|
this.killSwitchActivated = false;
|
|
135
157
|
this.noOpActivated = false;
|
|
136
158
|
this.controllers = new Set();
|
|
@@ -32,10 +32,10 @@ export default class LwcViewProvider extends BaseViewProvider {
|
|
|
32
32
|
filePath: specifier,
|
|
33
33
|
viewId,
|
|
34
34
|
properties: viewProperties,
|
|
35
|
-
render: async (runtimeParams) => {
|
|
35
|
+
render: async (runtimeParams, runtimeEnvironment) => {
|
|
36
36
|
// SSR the root component (without passing any public properties)
|
|
37
|
-
const { config, moduleBundler, resourceRegistry
|
|
38
|
-
const debug =
|
|
37
|
+
const { config, moduleBundler, resourceRegistry } = this;
|
|
38
|
+
const { debug } = runtimeEnvironment;
|
|
39
39
|
const element = moduleSpecifierToKebabCase(specifier);
|
|
40
40
|
return getTracer().trace({
|
|
41
41
|
name: ViewSpan.RenderPage,
|
|
@@ -45,7 +45,9 @@ export default class LwcViewProvider extends BaseViewProvider {
|
|
|
45
45
|
if (!route) {
|
|
46
46
|
throw new Error(`Unable to resolve configuration for view: ${viewId.id}`);
|
|
47
47
|
}
|
|
48
|
-
const { results = {}, errors } = await getRenderer(config, moduleBundler, resourceRegistry).render({ [specifier]: { specifier, props: {} } }, route, runtimeEnvironment, Object(runtimeParams)
|
|
48
|
+
const { results = {}, errors } = 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);
|
|
49
51
|
// Handle errors: fallback to CSR or throw
|
|
50
52
|
if (errors) {
|
|
51
53
|
const errorString = Object.values(errors).join(', ');
|
|
@@ -57,7 +57,7 @@ export default function lwcSsrViewTransformer(options, { config, moduleBundler,
|
|
|
57
57
|
if (!route) {
|
|
58
58
|
throw new Error(`Unable to resolve configuration for view: ${viewContext.view.id}`);
|
|
59
59
|
}
|
|
60
|
-
const { results, errors } = await getRenderer(config, moduleBundler, resourceRegistry).render(ssrModules, route, viewContext.runtimeEnvironment, viewContext.runtimeParams, metadata.serverData);
|
|
60
|
+
const { results, errors } = await getRenderer(config, moduleBundler, resourceRegistry).render(ssrModules, route, viewContext.runtimeEnvironment, viewContext.runtimeParams, metadata.serverData, false);
|
|
61
61
|
for (const root in results) {
|
|
62
62
|
const { html, props, cache } = results[root];
|
|
63
63
|
const { tagName, startOffset, endOffset, hydrate } = ssrModules[root];
|
|
@@ -126,9 +126,7 @@ function handleErrors(errors, customElements, specifiers, debug, serverDebug) {
|
|
|
126
126
|
? (ce.props = {
|
|
127
127
|
[HYDRATE_DIRECTIVE]: HYDRATE_CLIENT_VALUE,
|
|
128
128
|
})
|
|
129
|
-
:
|
|
130
|
-
// @ts-ignore - TS thinks that props may still be undefined
|
|
131
|
-
(ce.props[HYDRATE_DIRECTIVE] = HYDRATE_CLIENT_VALUE);
|
|
129
|
+
: (ce.props[HYDRATE_DIRECTIVE] = HYDRATE_CLIENT_VALUE);
|
|
132
130
|
}
|
|
133
131
|
// Log error with stack details
|
|
134
132
|
const errMessage = createSsrErrorMessage(specifier, err);
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
|
-
"version": "0.13.0
|
|
7
|
+
"version": "0.13.0",
|
|
8
8
|
"homepage": "https://developer.salesforce.com/docs/platform/lwr/overview",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
35
|
"build": "tsc -b",
|
|
36
|
-
"clean": "
|
|
36
|
+
"clean": "rimraf build node_modules",
|
|
37
37
|
"test": "jest"
|
|
38
38
|
},
|
|
39
39
|
"files": [
|
|
@@ -42,15 +42,16 @@
|
|
|
42
42
|
"build/**/*.d.ts"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@lwrjs/config": "0.13.0
|
|
46
|
-
"@lwrjs/diagnostics": "0.13.0
|
|
47
|
-
"@lwrjs/instrumentation": "0.13.0
|
|
48
|
-
"@lwrjs/
|
|
45
|
+
"@lwrjs/config": "0.13.0",
|
|
46
|
+
"@lwrjs/diagnostics": "0.13.0",
|
|
47
|
+
"@lwrjs/instrumentation": "0.13.0",
|
|
48
|
+
"@lwrjs/loader": "0.13.0",
|
|
49
|
+
"@lwrjs/shared-utils": "0.13.0",
|
|
49
50
|
"fs-extra": "^11.2.0",
|
|
50
|
-
"lru-cache": "^10.
|
|
51
|
+
"lru-cache": "^10.4.3"
|
|
51
52
|
},
|
|
52
53
|
"devDependencies": {
|
|
53
|
-
"@lwrjs/types": "0.13.0
|
|
54
|
+
"@lwrjs/types": "0.13.0",
|
|
54
55
|
"jest": "^26.6.3",
|
|
55
56
|
"mock-fs": "^5.2.0",
|
|
56
57
|
"ts-jest": "^26.5.6"
|
|
@@ -61,5 +62,5 @@
|
|
|
61
62
|
"volta": {
|
|
62
63
|
"extends": "../../../package.json"
|
|
63
64
|
},
|
|
64
|
-
"gitHead": "
|
|
65
|
+
"gitHead": "21dc6b8ffd2e633f36b46daf9e1563992c5143b9"
|
|
65
66
|
}
|