@lwrjs/view-registry 0.13.0-alpha.9 → 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/build/cjs/index.cjs +26 -27
- package/build/cjs/linkers/legacy_view_bootstrap.cjs +32 -12
- package/build/cjs/linkers/preload-utils.cjs +6 -3
- package/build/cjs/linkers/utils.cjs +7 -0
- package/build/cjs/linkers/view_bootstrap.cjs +25 -12
- package/build/cjs/utils.cjs +32 -15
- package/build/cjs/view-handler.cjs +6 -1
- package/build/es/index.d.ts +1 -1
- package/build/es/index.js +31 -29
- package/build/es/linkers/legacy_view_bootstrap.js +39 -14
- package/build/es/linkers/preload-utils.d.ts +2 -1
- package/build/es/linkers/preload-utils.js +8 -5
- package/build/es/linkers/utils.d.ts +2 -1
- package/build/es/linkers/utils.js +9 -0
- package/build/es/linkers/view_bootstrap.js +28 -11
- package/build/es/utils.d.ts +2 -2
- package/build/es/utils.js +33 -17
- package/build/es/view-handler.js +8 -1
- package/package.json +11 -8
package/build/cjs/index.cjs
CHANGED
|
@@ -136,17 +136,15 @@ var LwrViewRegistry = class {
|
|
|
136
136
|
}
|
|
137
137
|
throw new Error(`No View provider was able to resolve a view for template: ${viewId.contentTemplate}`);
|
|
138
138
|
}
|
|
139
|
-
async getView(viewId
|
|
139
|
+
async getView(viewId) {
|
|
140
140
|
const {contentTemplate, rootComponent} = viewId;
|
|
141
141
|
const compiledViewCacheKey = (0, import_shared_utils.getCacheKeyFromJson)({contentTemplate, rootComponent});
|
|
142
142
|
import_diagnostics.logger.debug(`[view-registry][getView] compiledViewCacheKey=${compiledViewCacheKey}`);
|
|
143
|
-
if (
|
|
143
|
+
if (this.compiledViews.has(compiledViewCacheKey)) {
|
|
144
144
|
return this.compiledViews.get(compiledViewCacheKey);
|
|
145
145
|
}
|
|
146
146
|
const compiledView = await this.delegateGetView(viewId);
|
|
147
|
-
|
|
148
|
-
this.compiledViews.set(compiledViewCacheKey, compiledView);
|
|
149
|
-
}
|
|
147
|
+
this.compiledViews.set(compiledViewCacheKey, compiledView);
|
|
150
148
|
return compiledView;
|
|
151
149
|
}
|
|
152
150
|
hasViewDefinition(view, viewParams, runtimeEnvironment, runtimeParams, renderOptions) {
|
|
@@ -161,7 +159,8 @@ var LwrViewRegistry = class {
|
|
|
161
159
|
freezeAssets,
|
|
162
160
|
locale: runtimeParams?.locale,
|
|
163
161
|
basePath: runtimeParams?.basePath,
|
|
164
|
-
debug: runtimeEnvironment.debug
|
|
162
|
+
debug: runtimeEnvironment.debug,
|
|
163
|
+
nonceEnabled: (0, import_shared_utils.getFeatureFlags)().ENABLE_NONCE
|
|
165
164
|
});
|
|
166
165
|
import_diagnostics.logger.debug(`[view-registry][hasViewDefinition] viewDefId=${viewDefId}`);
|
|
167
166
|
const viewParamKey = viewParamCacheKey ? (0, import_shared_utils.getCacheKeyFromJson)(viewParamCacheKey) : (0, import_shared_utils.getCacheKeyFromJson)(viewParams);
|
|
@@ -176,19 +175,19 @@ var LwrViewRegistry = class {
|
|
|
176
175
|
}
|
|
177
176
|
async getViewDefinition(view, viewParams, runtimeEnvironment, runtimeParams, renderOptions) {
|
|
178
177
|
try {
|
|
179
|
-
const {
|
|
178
|
+
const {freezeAssets, viewParamCacheKey} = (0, import_utils.normalizeRenderOptions)(this.runtimeEnvironment, renderOptions);
|
|
180
179
|
const viewDefCacheKey = (0, import_shared_utils.getCacheKeyFromJson)({
|
|
181
180
|
...view,
|
|
182
181
|
freezeAssets,
|
|
183
182
|
locale: runtimeParams?.locale,
|
|
184
183
|
basePath: runtimeParams?.basePath,
|
|
185
|
-
debug: runtimeEnvironment.debug
|
|
184
|
+
debug: runtimeEnvironment.debug,
|
|
185
|
+
nonceEnabled: (0, import_shared_utils.getFeatureFlags)().ENABLE_NONCE
|
|
186
186
|
});
|
|
187
187
|
import_diagnostics.logger.debug(`[view-registry][getViewDefinition] viewDefCacheKey=${viewDefCacheKey}`);
|
|
188
188
|
const viewParamKey = viewParamCacheKey ? (0, import_shared_utils.getCacheKeyFromJson)(viewParamCacheKey) : (0, import_shared_utils.getCacheKeyFromJson)(viewParams);
|
|
189
189
|
import_diagnostics.logger.debug(`[view-registry][getViewDefinition] viewParamKey=${viewParamKey}`);
|
|
190
|
-
|
|
191
|
-
if (cacheDisabled === false && this.viewDefinitions.has(viewDefCacheKey)) {
|
|
190
|
+
if (this.viewDefinitions.has(viewDefCacheKey)) {
|
|
192
191
|
const viewDefinition2 = this.viewDefinitions.get(viewDefCacheKey);
|
|
193
192
|
if (viewDefinition2 && viewDefinition2.paramKey === viewParamKey && viewDefinition2.viewDefinition.immutable) {
|
|
194
193
|
return viewDefinition2.viewDefinition;
|
|
@@ -205,17 +204,16 @@ var LwrViewRegistry = class {
|
|
|
205
204
|
}
|
|
206
205
|
}, () => this.renderView(view, updatableViewParams, runtimeEnvironment, runtimeParams, renderOptions)));
|
|
207
206
|
viewDefinition.nonce = (0, import_utils.getViewNonce)(updatableViewParams);
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
}
|
|
207
|
+
const route = this.globalConfig.routes.find((r) => r.id === view.id);
|
|
208
|
+
const maxViewCacheTtl = (0, import_shared_utils.getFeatureFlags)().MAX_VIEW_CACHE_TTL;
|
|
209
|
+
const maxTtl = maxViewCacheTtl && parseInt(maxViewCacheTtl, 10);
|
|
210
|
+
const leastTtl = (0, import_shared_utils.shortestTtl)(viewDefinition.cache?.ttl, route?.cache?.ttl, maxTtl);
|
|
211
|
+
const ttl = leastTtl !== void 0 ? leastTtl === 0 ? 10 : leastTtl * 1e3 : void 0;
|
|
212
|
+
this.viewDefinitions.set(viewDefCacheKey, {
|
|
213
|
+
view,
|
|
214
|
+
viewDefinition,
|
|
215
|
+
paramKey: viewParamKey
|
|
216
|
+
}, {ttl});
|
|
219
217
|
return viewDefinition;
|
|
220
218
|
} catch (err) {
|
|
221
219
|
if (err instanceof import_diagnostics.DiagnosticsError) {
|
|
@@ -230,7 +228,7 @@ var LwrViewRegistry = class {
|
|
|
230
228
|
async renderView(view, viewParams, runtimeEnvironment, runtimeParams = {}, renderOptions) {
|
|
231
229
|
const {id, contentTemplate, rootComponent, layoutTemplate} = view;
|
|
232
230
|
const lwrResourcesId = `__LWR_RESOURCES__${Date.now()}`;
|
|
233
|
-
const renderedContent = await this.render({id, contentTemplate, rootComponent}, {...viewParams, lwr_resources: lwrResourcesId}, runtimeParams, renderOptions);
|
|
231
|
+
const renderedContent = await this.render({id, contentTemplate, rootComponent}, {...viewParams, lwr_resources: lwrResourcesId}, runtimeParams, runtimeEnvironment, renderOptions);
|
|
234
232
|
let normalizedRenderOptions = (0, import_utils.normalizeRenderOptions)(this.runtimeEnvironment, renderedContent.options, renderOptions);
|
|
235
233
|
const layout = layoutTemplate || renderedContent.compiledView.layoutTemplate;
|
|
236
234
|
if (!layout) {
|
|
@@ -250,7 +248,7 @@ var LwrViewRegistry = class {
|
|
|
250
248
|
...viewParams,
|
|
251
249
|
body: renderedContent.renderedView,
|
|
252
250
|
lwr_resources: lwrResourcesId
|
|
253
|
-
}, runtimeParams);
|
|
251
|
+
}, runtimeParams, runtimeEnvironment);
|
|
254
252
|
normalizedRenderOptions = (0, import_utils.normalizeRenderOptions)(this.runtimeEnvironment, renderedLayout.options, normalizedRenderOptions);
|
|
255
253
|
const renderedViewDef = await this.link({
|
|
256
254
|
...renderedLayout,
|
|
@@ -288,16 +286,16 @@ var LwrViewRegistry = class {
|
|
|
288
286
|
});
|
|
289
287
|
return renderedViewDef;
|
|
290
288
|
}
|
|
291
|
-
async render(viewId, viewParams, runtimeParams, renderOptions) {
|
|
289
|
+
async render(viewId, viewParams, runtimeParams, runtimeEnvironment, renderOptions) {
|
|
292
290
|
const globalContext = this.globalData;
|
|
293
291
|
const {id, rootComponent, contentTemplate} = viewId;
|
|
294
|
-
const compiledView = await this.getView({id, contentTemplate, rootComponent}
|
|
292
|
+
const compiledView = await this.getView({id, contentTemplate, rootComponent});
|
|
295
293
|
const result = await compiledView.render({
|
|
296
294
|
...runtimeParams,
|
|
297
295
|
...globalContext,
|
|
298
296
|
...compiledView.properties,
|
|
299
297
|
...viewParams
|
|
300
|
-
});
|
|
298
|
+
}, runtimeEnvironment);
|
|
301
299
|
const normalizedResult = (0, import_utils.normalizeRenderedResult)(result);
|
|
302
300
|
return {
|
|
303
301
|
compiledView,
|
|
@@ -363,7 +361,8 @@ var LwrViewRegistry = class {
|
|
|
363
361
|
renderedView: linkedAssetContent,
|
|
364
362
|
immutable,
|
|
365
363
|
viewRecord: {
|
|
366
|
-
assetReferences: (0, import_utils.reduceSourceAssetReferences)(linkedMetadata.assetReferences)
|
|
364
|
+
assetReferences: (0, import_utils.reduceSourceAssetReferences)(linkedMetadata.assetReferences),
|
|
365
|
+
moduleResources: []
|
|
367
366
|
},
|
|
368
367
|
cache: {ttl: pageTtl}
|
|
369
368
|
};
|
|
@@ -49,9 +49,8 @@ async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
49
49
|
resourceRegistry,
|
|
50
50
|
viewMetadata
|
|
51
51
|
} = resourceContext;
|
|
52
|
-
const {
|
|
52
|
+
const {format, hmrEnabled, bundle, debug, minify} = runtimeEnvironment;
|
|
53
53
|
const {customElements, serverData, serverDebug} = viewMetadata;
|
|
54
|
-
const version = lwrVersion;
|
|
55
54
|
const isAMD = format === "amd";
|
|
56
55
|
const {bundleConfig} = resourceContext;
|
|
57
56
|
const {external = {}, exclude = []} = bundleConfig;
|
|
@@ -98,9 +97,11 @@ async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
98
97
|
const viewPreloads = {
|
|
99
98
|
uris: [],
|
|
100
99
|
specifiers: [],
|
|
101
|
-
groups: new Map()
|
|
100
|
+
groups: new Map(),
|
|
101
|
+
integrities: new Map()
|
|
102
102
|
};
|
|
103
103
|
const isSSR = view.bootstrap?.ssr;
|
|
104
|
+
const version = view.bootstrap?.lwrVersion;
|
|
104
105
|
const nonce = (0, import_utils.getViewNonce)(viewParams);
|
|
105
106
|
let bootstrapModuleRef, versionedSpecifier = bootstrapSpecifier;
|
|
106
107
|
const customElementsRecords = [];
|
|
@@ -128,7 +129,7 @@ async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
128
129
|
}
|
|
129
130
|
(0, import_utils.addExternalScriptNonce)(def, nonce);
|
|
130
131
|
requiredResources.push(def);
|
|
131
|
-
const errorShimDef = await resourceRegistry.getResource({specifier: "lwr-error-shim.js", version}, runtimeEnvironment, runtimeParams);
|
|
132
|
+
const errorShimDef = await resourceRegistry.getResource({specifier: "lwr-error-shim.js", version}, runtimeEnvironment, {...runtimeParams, ignoreDebug: true});
|
|
132
133
|
if (!errorShimDef) {
|
|
133
134
|
throw Error("Failed to find definition of resource: lwr-error-shim.js");
|
|
134
135
|
}
|
|
@@ -149,11 +150,13 @@ async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
149
150
|
if (!uri) {
|
|
150
151
|
throw Error(`Invalid Module Resource ${versionedSpecifier}`);
|
|
151
152
|
}
|
|
152
|
-
|
|
153
|
+
const integrity = (0, import_utils2.getBundleIntegrity)(bootstrapModuleGraph, versionedSpecifier);
|
|
154
|
+
moduleResources.push((0, import_utils.getModuleResourceByUri)(uri, runtimeEnvironment, {integrity, isPreload: false, isSSR, nonce}));
|
|
153
155
|
for (const depSpecifier of bootstrapModuleGraph.graphs[0].static) {
|
|
154
156
|
const uri2 = getPreloadUri(depSpecifier, bootstrapModuleGraph.uriMap);
|
|
155
157
|
if (uri2) {
|
|
156
|
-
(0,
|
|
158
|
+
const integrity2 = (0, import_utils2.getBundleIntegrity)(bootstrapModuleGraph, depSpecifier);
|
|
159
|
+
(0, import_preload_utils.setPreloadModulesMeta)(depSpecifier, uri2, integrity2, groups, viewPreloads);
|
|
157
160
|
}
|
|
158
161
|
}
|
|
159
162
|
if ((0, import_shared_utils.isBundler)(defRegistry)) {
|
|
@@ -190,12 +193,14 @@ async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
190
193
|
if (!isSSR || (0, import_shared_utils.getHydrateDirective)(props)) {
|
|
191
194
|
const specifier = graph.graphs[0].specifier;
|
|
192
195
|
const uri = graph.uriMap[specifier];
|
|
193
|
-
(0,
|
|
196
|
+
const integrity = (0, import_utils2.getBundleIntegrity)(graph, specifier);
|
|
197
|
+
(0, import_preload_utils.setPreloadModulesMeta)(specifier, uri, integrity, groups, viewPreloads);
|
|
194
198
|
if (bundle) {
|
|
195
199
|
for (const depSpecifier of graph.graphs[0].static) {
|
|
196
|
-
const
|
|
197
|
-
if (
|
|
198
|
-
(0,
|
|
200
|
+
const depUri = getPreloadUri(depSpecifier, graph.uriMap);
|
|
201
|
+
if (depUri) {
|
|
202
|
+
const integrity2 = (0, import_utils2.getBundleIntegrity)(graph, depSpecifier);
|
|
203
|
+
(0, import_preload_utils.setPreloadModulesMeta)(depSpecifier, depUri, integrity2, groups, viewPreloads);
|
|
199
204
|
}
|
|
200
205
|
}
|
|
201
206
|
}
|
|
@@ -214,11 +219,19 @@ async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
214
219
|
}
|
|
215
220
|
}));
|
|
216
221
|
if (viewContainsLiveElements) {
|
|
222
|
+
if ((0, import_shared_utils.isLocalDev)()) {
|
|
223
|
+
const localDevSpecifier = "lwr_local_dev/bootstrap";
|
|
224
|
+
rootComponents.push(localDevSpecifier);
|
|
225
|
+
const localDevMod = await (0, import_utils.getModuleResource)({specifier: localDevSpecifier, version: ""}, runtimeEnvironment, {}, moduleRegistry, runtimeParams);
|
|
226
|
+
if (localDevMod.src)
|
|
227
|
+
imports[localDevSpecifier] = localDevMod.src;
|
|
228
|
+
}
|
|
217
229
|
configResources.unshift((0, import_utils2.getViewBootstrapConfigurationResource)({
|
|
218
230
|
id: view.id,
|
|
219
231
|
url: viewParams?.page?.url,
|
|
220
232
|
configAsSrc: view.bootstrap?.configAsSrc || false,
|
|
221
|
-
mixedMode: view.bootstrap?.mixedMode || false
|
|
233
|
+
mixedMode: view.bootstrap?.mixedMode || false,
|
|
234
|
+
nonce: viewParams?.page?.nonce
|
|
222
235
|
}, {
|
|
223
236
|
appId: appIdentity.appName,
|
|
224
237
|
bootstrapModule: versionedSpecifier,
|
|
@@ -238,7 +251,13 @@ async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
238
251
|
}
|
|
239
252
|
const dedupedUris = [...new Set(viewPreloads.uris)];
|
|
240
253
|
for (const preloadUri of dedupedUris) {
|
|
241
|
-
|
|
254
|
+
const integrity = viewPreloads.integrities.get(preloadUri);
|
|
255
|
+
moduleResources.push((0, import_utils.getModuleResourceByUri)(preloadUri, runtimeEnvironment, {
|
|
256
|
+
integrity,
|
|
257
|
+
isPreload: true,
|
|
258
|
+
isSSR,
|
|
259
|
+
nonce
|
|
260
|
+
}));
|
|
242
261
|
}
|
|
243
262
|
const htmlResources = await Promise.all([...configResources, ...requiredResources, ...moduleResources].map(import_utils.generateHtmlTag));
|
|
244
263
|
return {
|
|
@@ -246,6 +265,7 @@ async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
246
265
|
viewRecord: {
|
|
247
266
|
resources: requiredResources,
|
|
248
267
|
customElements: customElementsRecords,
|
|
268
|
+
moduleResources,
|
|
249
269
|
bootstrapModule: bootstrapModuleRef
|
|
250
270
|
}
|
|
251
271
|
};
|
|
@@ -29,7 +29,7 @@ __export(exports, {
|
|
|
29
29
|
});
|
|
30
30
|
var import_diagnostics = __toModule(require("@lwrjs/diagnostics"));
|
|
31
31
|
var import_shared_utils = __toModule(require("@lwrjs/shared-utils"));
|
|
32
|
-
function setPreloadModulesMeta(specifier, uri, groups, preloads) {
|
|
32
|
+
function setPreloadModulesMeta(specifier, uri, integrity, groups, preloads) {
|
|
33
33
|
if (!uri) {
|
|
34
34
|
throw (0, import_diagnostics.createSingleDiagnosticError)({
|
|
35
35
|
description: import_diagnostics.descriptions.UNRESOLVABLE.PRELOAD_MODULE(specifier)
|
|
@@ -41,6 +41,7 @@ function setPreloadModulesMeta(specifier, uri, groups, preloads) {
|
|
|
41
41
|
const preloadModulesSpecifiers = preloads.specifiers;
|
|
42
42
|
const preloadBundleGroupsMap = preloads.groups;
|
|
43
43
|
const preloadModulesURIs = preloads.uris;
|
|
44
|
+
const preloadModuleIntegrities = preloads.integrities;
|
|
44
45
|
preloadModulesSpecifiers.push(specifier);
|
|
45
46
|
const {specifier: unversionedSpecifier} = (0, import_shared_utils.explodeSpecifier)(specifier);
|
|
46
47
|
const groupName = (0, import_shared_utils.getGroupName)(unversionedSpecifier, groups);
|
|
@@ -49,6 +50,7 @@ function setPreloadModulesMeta(specifier, uri, groups, preloads) {
|
|
|
49
50
|
}
|
|
50
51
|
preloadModulesURIs.push(uri);
|
|
51
52
|
groupName && preloadBundleGroupsMap.set(groupName, true);
|
|
53
|
+
preloadModuleIntegrities.set(uri, integrity);
|
|
52
54
|
}
|
|
53
55
|
async function getPreloadModulesMeta(specifier, viewPreloads, bundleConfig, moduleRegistry, defRegistry, runtimeEnvironment, runtimeParams, pending) {
|
|
54
56
|
const {exclude = [], external = {}, groups = {}} = bundleConfig;
|
|
@@ -72,9 +74,10 @@ async function getPreloadModulesMeta(specifier, viewPreloads, bundleConfig, modu
|
|
|
72
74
|
});
|
|
73
75
|
const uri = await defRegistry.resolveModuleUri(versionedModuleId, runtimeEnvironment, runtimeParams);
|
|
74
76
|
const normalizedSpecifier = versionedModuleId.version === import_shared_utils.VERSION_NOT_PROVIDED ? specifier : versionedModuleSpecifier;
|
|
75
|
-
|
|
77
|
+
const preloadModuleRecord = await defRegistry.getModuleBundle(versionedModuleId, runtimeEnvironment, runtimeParams);
|
|
78
|
+
const {integrity} = preloadModuleRecord;
|
|
79
|
+
setPreloadModulesMeta(normalizedSpecifier, uri, integrity, groups, viewPreloads);
|
|
76
80
|
if (exclude.length || Object.keys(groups).length) {
|
|
77
|
-
const preloadModuleRecord = await defRegistry.getModuleBundle(versionedModuleId, runtimeEnvironment, runtimeParams);
|
|
78
81
|
const {imports} = preloadModuleRecord.bundleRecord;
|
|
79
82
|
if (imports) {
|
|
80
83
|
if (!pending) {
|
|
@@ -25,6 +25,7 @@ var __toModule = (module2) => {
|
|
|
25
25
|
__markAsModule(exports);
|
|
26
26
|
__export(exports, {
|
|
27
27
|
flattenCustomElements: () => flattenCustomElements,
|
|
28
|
+
getBundleIntegrity: () => getBundleIntegrity,
|
|
28
29
|
getViewBootstrapConfigurationResource: () => getViewBootstrapConfigurationResource,
|
|
29
30
|
getViewHmrConfigurationResource: () => getViewHmrConfigurationResource
|
|
30
31
|
});
|
|
@@ -72,6 +73,7 @@ function getViewBootstrapConfigurationResource(viewInfo, config, runtimeEnvironm
|
|
|
72
73
|
type: CONTENT_TYPE,
|
|
73
74
|
content: configString,
|
|
74
75
|
inline: false,
|
|
76
|
+
nonce: viewInfo.nonce,
|
|
75
77
|
src: url
|
|
76
78
|
};
|
|
77
79
|
} else {
|
|
@@ -129,3 +131,8 @@ function flattenCustomElements(arr, isSSR = false) {
|
|
|
129
131
|
flatten(arr);
|
|
130
132
|
return ret;
|
|
131
133
|
}
|
|
134
|
+
function getBundleIntegrity(bootstrapModuleGraph, versionedSpecifier) {
|
|
135
|
+
const def = bootstrapModuleGraph.linkedDefinitions[versionedSpecifier];
|
|
136
|
+
const integrity = def?.integrity;
|
|
137
|
+
return integrity;
|
|
138
|
+
}
|
|
@@ -41,10 +41,9 @@ async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
41
41
|
resourceRegistry,
|
|
42
42
|
viewMetadata
|
|
43
43
|
} = resourceContext;
|
|
44
|
-
const {
|
|
44
|
+
const {format, hmrEnabled, bundle, debug, minify} = runtimeEnvironment;
|
|
45
45
|
const {customElements, serverData, serverDebug} = viewMetadata;
|
|
46
46
|
const defRegistry = bundle ? moduleBundler : moduleRegistry;
|
|
47
|
-
const version = lwrVersion;
|
|
48
47
|
const isAMD = format === "amd";
|
|
49
48
|
const depth = isAMD ? {static: import_shared_utils.GraphDepth.ALL, dynamic: 1} : {static: import_shared_utils.GraphDepth.NONE, dynamic: 1};
|
|
50
49
|
const {bundleConfig} = resourceContext;
|
|
@@ -87,9 +86,11 @@ async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
87
86
|
const viewPreloads = {
|
|
88
87
|
uris: [],
|
|
89
88
|
specifiers: [],
|
|
90
|
-
groups: new Map()
|
|
89
|
+
groups: new Map(),
|
|
90
|
+
integrities: new Map()
|
|
91
91
|
};
|
|
92
92
|
const isSSR = view.bootstrap?.ssr;
|
|
93
|
+
const version = view.bootstrap?.lwrVersion;
|
|
93
94
|
const nonce = (0, import_utils.getViewNonce)(viewParams);
|
|
94
95
|
let bootstrapModuleRef, versionedSpecifier = bootstrapSpecifier;
|
|
95
96
|
let importMetadata = {imports: {}};
|
|
@@ -108,7 +109,7 @@ async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
108
109
|
}
|
|
109
110
|
(0, import_utils.addExternalScriptNonce)(def, nonce);
|
|
110
111
|
requiredResources.push(def);
|
|
111
|
-
const errorShimDef = await resourceRegistry.getResource({specifier: "lwr-error-shim.js", version}, runtimeEnvironment, runtimeParams);
|
|
112
|
+
const errorShimDef = await resourceRegistry.getResource({specifier: "lwr-error-shim.js", version}, runtimeEnvironment, {...runtimeParams, ignoreDebug: true});
|
|
112
113
|
if (!errorShimDef) {
|
|
113
114
|
throw Error("Failed to find definition of resource: lwr-error-shim.js");
|
|
114
115
|
}
|
|
@@ -129,11 +130,13 @@ async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
129
130
|
if (!uri) {
|
|
130
131
|
throw Error(`Invalid Module Resource ${versionedSpecifier}`);
|
|
131
132
|
}
|
|
132
|
-
|
|
133
|
+
const integrity = (0, import_utils2.getBundleIntegrity)(bootstrapModuleGraph, versionedSpecifier);
|
|
134
|
+
moduleResources.push((0, import_utils.getModuleResourceByUri)(uri, runtimeEnvironment, {integrity, isPreload: false, isSSR, nonce}));
|
|
133
135
|
for (const depSpecifier of bootstrapModuleGraph.graphs[0].static) {
|
|
134
136
|
const uri2 = getPreloadUri(depSpecifier, bootstrapModuleGraph.uriMap);
|
|
135
137
|
if (uri2) {
|
|
136
|
-
(0,
|
|
138
|
+
const integrity2 = (0, import_utils2.getBundleIntegrity)(bootstrapModuleGraph, depSpecifier);
|
|
139
|
+
(0, import_preload_utils.setPreloadModulesMeta)(depSpecifier, uri2, integrity2, groups, viewPreloads);
|
|
137
140
|
}
|
|
138
141
|
}
|
|
139
142
|
if ((0, import_shared_utils.isBundler)(defRegistry)) {
|
|
@@ -171,12 +174,14 @@ async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
171
174
|
if (!isSSR || (0, import_shared_utils.getHydrateDirective)(props)) {
|
|
172
175
|
const specifier = graph.graphs[0].specifier;
|
|
173
176
|
const uri = graph.uriMap[specifier];
|
|
174
|
-
(0,
|
|
177
|
+
const integrity = (0, import_utils2.getBundleIntegrity)(graph, specifier);
|
|
178
|
+
(0, import_preload_utils.setPreloadModulesMeta)(specifier, uri, integrity, groups, viewPreloads);
|
|
175
179
|
if (bundle) {
|
|
176
180
|
for (const depSpecifier of graph.graphs[0].static) {
|
|
177
|
-
const
|
|
178
|
-
if (
|
|
179
|
-
(0,
|
|
181
|
+
const depUri = getPreloadUri(depSpecifier, graph.uriMap);
|
|
182
|
+
if (depUri) {
|
|
183
|
+
const integrity2 = (0, import_utils2.getBundleIntegrity)(graph, depSpecifier);
|
|
184
|
+
(0, import_preload_utils.setPreloadModulesMeta)(depSpecifier, depUri, integrity2, groups, viewPreloads);
|
|
180
185
|
}
|
|
181
186
|
}
|
|
182
187
|
}
|
|
@@ -200,7 +205,8 @@ async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
200
205
|
id: view.id,
|
|
201
206
|
url: viewParams?.page?.url,
|
|
202
207
|
configAsSrc: view.bootstrap?.configAsSrc || false,
|
|
203
|
-
mixedMode: view.bootstrap?.mixedMode || false
|
|
208
|
+
mixedMode: view.bootstrap?.mixedMode || false,
|
|
209
|
+
nonce: viewParams?.page?.nonce
|
|
204
210
|
}, {
|
|
205
211
|
appId: appIdentity.appName,
|
|
206
212
|
bootstrapModule: versionedSpecifier,
|
|
@@ -218,7 +224,13 @@ async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
218
224
|
}
|
|
219
225
|
const dedupedUris = [...new Set(viewPreloads.uris)];
|
|
220
226
|
for (const preloadUri of dedupedUris) {
|
|
221
|
-
|
|
227
|
+
const integrity = viewPreloads.integrities.get(preloadUri);
|
|
228
|
+
moduleResources.push((0, import_utils.getModuleResourceByUri)(preloadUri, runtimeEnvironment, {
|
|
229
|
+
integrity,
|
|
230
|
+
isPreload: true,
|
|
231
|
+
isSSR,
|
|
232
|
+
nonce
|
|
233
|
+
}));
|
|
222
234
|
}
|
|
223
235
|
const htmlResources = await Promise.all([...configResources, ...requiredResources, ...moduleResources].map(import_utils.generateHtmlTag));
|
|
224
236
|
const mapping = (0, import_shared_utils.getMappingUriPrefix)(runtimeEnvironment, runtimeParams);
|
|
@@ -234,6 +246,7 @@ async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
234
246
|
customElements: customElementsRecords,
|
|
235
247
|
endpoints,
|
|
236
248
|
importMetadata,
|
|
249
|
+
moduleResources,
|
|
237
250
|
bootstrapModule: bootstrapModuleRef
|
|
238
251
|
}
|
|
239
252
|
};
|
package/build/cjs/utils.cjs
CHANGED
|
@@ -45,15 +45,22 @@ var import_crypto = __toModule(require("crypto"));
|
|
|
45
45
|
function generateExternalStyle(src) {
|
|
46
46
|
return `<link rel="stylesheet" href="${src}">`;
|
|
47
47
|
}
|
|
48
|
-
function generateExternalScript(type = "application/javascript", src, async, defer, nonce) {
|
|
48
|
+
function generateExternalScript(type = "application/javascript", src, async, defer, nonce, integrity) {
|
|
49
49
|
let scriptLoadOrder = "";
|
|
50
50
|
if (defer) {
|
|
51
51
|
scriptLoadOrder = " defer";
|
|
52
52
|
} else if (async) {
|
|
53
53
|
scriptLoadOrder = " async";
|
|
54
54
|
}
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
let cspHashAttr = "";
|
|
56
|
+
if ((0, import_shared_utils.getFeatureFlags)().ENABLE_NONCE) {
|
|
57
|
+
if (integrity) {
|
|
58
|
+
cspHashAttr = ` integrity="${integrity}"`;
|
|
59
|
+
} else if (nonce) {
|
|
60
|
+
cspHashAttr = ` nonce="${nonce}"`;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return `<script type="${type}"${scriptLoadOrder}${cspHashAttr} src="${src}"></script>`;
|
|
57
64
|
}
|
|
58
65
|
function generateLinkPreloadTag({href, type}) {
|
|
59
66
|
if (type === "module") {
|
|
@@ -62,19 +69,27 @@ function generateLinkPreloadTag({href, type}) {
|
|
|
62
69
|
return `<link rel="preload" href="${href}" type="${type}" />`;
|
|
63
70
|
}
|
|
64
71
|
}
|
|
65
|
-
function generateExternalTag({
|
|
72
|
+
function generateExternalTag({
|
|
73
|
+
type,
|
|
74
|
+
src = "",
|
|
75
|
+
async,
|
|
76
|
+
defer,
|
|
77
|
+
isPreload,
|
|
78
|
+
nonce,
|
|
79
|
+
integrity
|
|
80
|
+
}) {
|
|
66
81
|
if (isPreload) {
|
|
67
82
|
return generateLinkPreloadTag({href: src, type});
|
|
68
83
|
} else if (type === "text/css") {
|
|
69
84
|
return generateExternalStyle(src);
|
|
70
85
|
} else {
|
|
71
|
-
return generateExternalScript(type, src, async, defer, nonce);
|
|
86
|
+
return generateExternalScript(type, src, async, defer, nonce, integrity);
|
|
72
87
|
}
|
|
73
88
|
}
|
|
74
89
|
async function generateInlineTag({specifier, type, content, stream, nonce}) {
|
|
75
90
|
const typeStr = type === "text/css" ? "" : ` type="${type}"`;
|
|
76
91
|
const tag = type === "text/css" ? "style" : "script";
|
|
77
|
-
const nonceStr = nonce ? ` nonce="${nonce}"` : "";
|
|
92
|
+
const nonceStr = (0, import_shared_utils.getFeatureFlags)().ENABLE_NONCE && nonce && nonce ? ` nonce="${nonce}"` : "";
|
|
78
93
|
if (!content && !stream) {
|
|
79
94
|
throw new Error(`Invalid inline Resource Definition: must have either "content" or "stream": "${specifier}"`);
|
|
80
95
|
}
|
|
@@ -122,7 +137,6 @@ function normalizeRenderOptions(runtimeEnvironment, overrideRenderOptions, baseR
|
|
|
122
137
|
return {
|
|
123
138
|
skipMetadataCollection: false,
|
|
124
139
|
freezeAssets: runtimeEnvironment.immutableAssets,
|
|
125
|
-
skipCaching: false,
|
|
126
140
|
viewParamCacheKey: null,
|
|
127
141
|
...baseRenderOptions,
|
|
128
142
|
...overrideRenderOptions
|
|
@@ -228,7 +242,7 @@ async function getModuleResource(moduleId, runtimeEnvironment, moduleResourceMet
|
|
|
228
242
|
};
|
|
229
243
|
}
|
|
230
244
|
function getModuleResourceByUri(uri, runtimeEnvironment, moduleResourceMeta) {
|
|
231
|
-
const {isSSR = false, isPreload = false, nonce} = moduleResourceMeta;
|
|
245
|
+
const {integrity, isSSR = false, isPreload = false, nonce} = moduleResourceMeta;
|
|
232
246
|
const {format} = runtimeEnvironment;
|
|
233
247
|
return {
|
|
234
248
|
src: uri,
|
|
@@ -236,6 +250,7 @@ function getModuleResourceByUri(uri, runtimeEnvironment, moduleResourceMeta) {
|
|
|
236
250
|
async: !isSSR && isPreload,
|
|
237
251
|
defer: isSSR,
|
|
238
252
|
isPreload: format !== "amd" && isPreload,
|
|
253
|
+
integrity,
|
|
239
254
|
nonce
|
|
240
255
|
};
|
|
241
256
|
}
|
|
@@ -259,18 +274,20 @@ async function createJsonModule(specifier, moduleRegistry, environment, params)
|
|
|
259
274
|
};
|
|
260
275
|
}
|
|
261
276
|
function getViewNonce(viewParams) {
|
|
262
|
-
return viewParams?.page?.nonce;
|
|
277
|
+
return (0, import_shared_utils.getFeatureFlags)().ENABLE_NONCE ? viewParams?.page?.nonce : void 0;
|
|
263
278
|
}
|
|
264
279
|
function generateViewNonce(viewParams) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
viewParams.page
|
|
268
|
-
|
|
269
|
-
|
|
280
|
+
if ((0, import_shared_utils.getFeatureFlags)().ENABLE_NONCE) {
|
|
281
|
+
const nonce = import_crypto.default.randomBytes(16).toString("base64");
|
|
282
|
+
if (!viewParams.page) {
|
|
283
|
+
viewParams.page = {nonce};
|
|
284
|
+
} else {
|
|
285
|
+
viewParams.page.nonce = nonce;
|
|
286
|
+
}
|
|
270
287
|
}
|
|
271
288
|
}
|
|
272
289
|
function addExternalScriptNonce(def, nonce) {
|
|
273
|
-
if (!def.inline) {
|
|
290
|
+
if (nonce && (0, import_shared_utils.getFeatureFlags)().ENABLE_NONCE && !def.inline) {
|
|
274
291
|
def.nonce = nonce;
|
|
275
292
|
}
|
|
276
293
|
}
|
|
@@ -130,6 +130,8 @@ var LwrViewHandler = class {
|
|
|
130
130
|
const paths = {rootDir, assets, contentDir, layoutsDir};
|
|
131
131
|
const locale = runtimeParams.locale;
|
|
132
132
|
const basePath = runtimeParams.basePath;
|
|
133
|
+
const assetBasePath = runtimeParams.assetBasePath;
|
|
134
|
+
const uiBasePath = runtimeParams.assetBasePath;
|
|
133
135
|
const viewApi = this.getBoundApi(viewRequest, route, runtimeEnvironment, runtimeParams);
|
|
134
136
|
const response = await (0, import_instrumentation.getTracer)().trace({
|
|
135
137
|
name: import_instrumentation.ViewSpan.ExecuteRouteHandler,
|
|
@@ -139,7 +141,7 @@ var LwrViewHandler = class {
|
|
|
139
141
|
}
|
|
140
142
|
}, async () => {
|
|
141
143
|
try {
|
|
142
|
-
return await routeHandlerFn({...viewRequest, locale, basePath}, {route, viewApi, ...paths}, routeHandlerOptions);
|
|
144
|
+
return await routeHandlerFn({...viewRequest, locale, basePath, assetBasePath, uiBasePath}, {route, viewApi, ...paths}, routeHandlerOptions);
|
|
143
145
|
} catch (err) {
|
|
144
146
|
if (err instanceof import_diagnostics.DiagnosticsError) {
|
|
145
147
|
throw err;
|
|
@@ -167,6 +169,9 @@ var LwrViewHandler = class {
|
|
|
167
169
|
async getViewResponse(viewRequest, route, runtimeEnvironment, runtimeParams = {}, view, viewParams, renderOptions) {
|
|
168
170
|
const {id, bootstrap} = route;
|
|
169
171
|
const managedView = {...view, id, bootstrap};
|
|
172
|
+
if (view.locale) {
|
|
173
|
+
runtimeParams.locale = view.locale;
|
|
174
|
+
}
|
|
170
175
|
const viewResponse = {
|
|
171
176
|
view: managedView,
|
|
172
177
|
viewParams,
|
package/build/es/index.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ export declare class LwrViewRegistry implements ViewRegistry {
|
|
|
42
42
|
addViewTransformers(transformers: ViewTransformPlugin[]): void;
|
|
43
43
|
initializeViewProviders(): Promise<void[]>;
|
|
44
44
|
delegateGetView(viewId: ViewIdentity): Promise<CompiledView>;
|
|
45
|
-
getView(viewId: ViewIdentity
|
|
45
|
+
getView(viewId: ViewIdentity): Promise<CompiledView>;
|
|
46
46
|
hasViewDefinition(view: View, viewParams: ViewParams, runtimeEnvironment: RuntimeEnvironment, runtimeParams?: RuntimeParams, renderOptions?: RenderOptions): boolean;
|
|
47
47
|
getViewDefinition(view: View, viewParams: ViewParams, runtimeEnvironment: RuntimeEnvironment, runtimeParams?: RuntimeParams, renderOptions?: RenderOptions): Promise<LinkedViewDefinition>;
|
|
48
48
|
private renderView;
|
package/build/es/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { InflightTasks, createStringBuilder, extractMetadataFromHtml, getCacheKeyFromJson, getSpecifier, isLocalDev, normalizeResourcePath, shortestTtl, } from '@lwrjs/shared-utils';
|
|
1
|
+
import { InflightTasks, createStringBuilder, extractMetadataFromHtml, getCacheKeyFromJson, getFeatureFlags, getSpecifier, isLocalDev, normalizeResourcePath, shortestTtl, } from '@lwrjs/shared-utils';
|
|
2
2
|
import { getTracer, ViewSpan } from '@lwrjs/instrumentation';
|
|
3
3
|
import { generateViewNonce, getViewNonce, normalizeRenderOptions, normalizeRenderedResult, reduceSourceAssetReferences, } from './utils.js';
|
|
4
4
|
import { linkLwrResources } from './linkers/link-lwr-resources.js';
|
|
5
|
+
// TODO: investigate perf impact W-16056356
|
|
5
6
|
import { LRUCache } from 'lru-cache';
|
|
6
7
|
import { DiagnosticsError, LwrServerError, createSingleDiagnosticError, descriptions, logger, } from '@lwrjs/diagnostics';
|
|
7
8
|
export { LwrViewHandler } from './view-handler.js';
|
|
@@ -122,19 +123,16 @@ export class LwrViewRegistry {
|
|
|
122
123
|
}
|
|
123
124
|
throw new Error(`No View provider was able to resolve a view for template: ${viewId.contentTemplate}`);
|
|
124
125
|
}
|
|
125
|
-
async getView(viewId
|
|
126
|
+
async getView(viewId) {
|
|
126
127
|
const { contentTemplate, rootComponent } = viewId;
|
|
127
128
|
const compiledViewCacheKey = getCacheKeyFromJson({ contentTemplate, rootComponent });
|
|
128
129
|
logger.debug(`[view-registry][getView] compiledViewCacheKey=${compiledViewCacheKey}`);
|
|
129
130
|
// use cached compiledView if available
|
|
130
|
-
if (
|
|
131
|
+
if (this.compiledViews.has(compiledViewCacheKey)) {
|
|
131
132
|
return this.compiledViews.get(compiledViewCacheKey);
|
|
132
133
|
}
|
|
133
134
|
const compiledView = await this.delegateGetView(viewId);
|
|
134
|
-
|
|
135
|
-
if (!skipCaching) {
|
|
136
|
-
this.compiledViews.set(compiledViewCacheKey, compiledView);
|
|
137
|
-
}
|
|
135
|
+
this.compiledViews.set(compiledViewCacheKey, compiledView);
|
|
138
136
|
return compiledView;
|
|
139
137
|
}
|
|
140
138
|
hasViewDefinition(view, viewParams, runtimeEnvironment, runtimeParams, renderOptions) {
|
|
@@ -150,6 +148,8 @@ export class LwrViewRegistry {
|
|
|
150
148
|
locale: runtimeParams?.locale,
|
|
151
149
|
basePath: runtimeParams?.basePath,
|
|
152
150
|
debug: runtimeEnvironment.debug,
|
|
151
|
+
// Add a variable on if the nonce is enabled
|
|
152
|
+
nonceEnabled: getFeatureFlags().ENABLE_NONCE,
|
|
153
153
|
});
|
|
154
154
|
logger.debug(`[view-registry][hasViewDefinition] viewDefId=${viewDefId}`);
|
|
155
155
|
// viewParams is an unbounded object and can be very large (17MB/view for developer.salesforce.com). Allowing consumers
|
|
@@ -170,13 +170,15 @@ export class LwrViewRegistry {
|
|
|
170
170
|
}
|
|
171
171
|
async getViewDefinition(view, viewParams, runtimeEnvironment, runtimeParams, renderOptions) {
|
|
172
172
|
try {
|
|
173
|
-
const {
|
|
173
|
+
const { freezeAssets, viewParamCacheKey } = normalizeRenderOptions(this.runtimeEnvironment, renderOptions);
|
|
174
174
|
const viewDefCacheKey = getCacheKeyFromJson({
|
|
175
175
|
...view,
|
|
176
176
|
freezeAssets,
|
|
177
177
|
locale: runtimeParams?.locale,
|
|
178
178
|
basePath: runtimeParams?.basePath,
|
|
179
179
|
debug: runtimeEnvironment.debug,
|
|
180
|
+
// Add a variable on if the nonce is enabled
|
|
181
|
+
nonceEnabled: getFeatureFlags().ENABLE_NONCE,
|
|
180
182
|
});
|
|
181
183
|
logger.debug(`[view-registry][getViewDefinition] viewDefCacheKey=${viewDefCacheKey}`);
|
|
182
184
|
// viewParams is an unbounded object and can be very large (17MB/view for developer.salesforce.com). Allowing consumers
|
|
@@ -186,9 +188,8 @@ export class LwrViewRegistry {
|
|
|
186
188
|
? getCacheKeyFromJson(viewParamCacheKey)
|
|
187
189
|
: getCacheKeyFromJson(viewParams);
|
|
188
190
|
logger.debug(`[view-registry][getViewDefinition] viewParamKey=${viewParamKey}`);
|
|
189
|
-
const cacheDisabled = process.env.NOCACHE === 'true' || skipCaching;
|
|
190
191
|
// important: cache key does not include the unbounded viewParams
|
|
191
|
-
if (
|
|
192
|
+
if (this.viewDefinitions.has(viewDefCacheKey)) {
|
|
192
193
|
const viewDefinition = this.viewDefinitions.get(viewDefCacheKey);
|
|
193
194
|
if (viewDefinition &&
|
|
194
195
|
viewDefinition.paramKey === viewParamKey &&
|
|
@@ -209,20 +210,19 @@ export class LwrViewRegistry {
|
|
|
209
210
|
}, () => this.renderView(view, updatableViewParams, runtimeEnvironment, runtimeParams, renderOptions)));
|
|
210
211
|
// Once the view is generated add the nonce to the response so it can be cached and then added to the headers
|
|
211
212
|
viewDefinition.nonce = getViewNonce(updatableViewParams);
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
213
|
+
const route = this.globalConfig.routes.find((r) => r.id === view.id);
|
|
214
|
+
const maxViewCacheTtl = getFeatureFlags().MAX_VIEW_CACHE_TTL;
|
|
215
|
+
// optionally set a max TTL on the view registry cache
|
|
216
|
+
// use a very low TTL if the shortest is 0, to allow the view to be fetched > once during a single request
|
|
217
|
+
const maxTtl = maxViewCacheTtl && parseInt(maxViewCacheTtl, 10);
|
|
218
|
+
const leastTtl = shortestTtl(viewDefinition.cache?.ttl, route?.cache?.ttl, maxTtl);
|
|
219
|
+
const ttl = leastTtl !== undefined ? (leastTtl === 0 ? 10 : leastTtl * 1000) : undefined;
|
|
220
|
+
// cache view definition for the shortest ttl or until it is the least recently used when ttl is undefined
|
|
221
|
+
this.viewDefinitions.set(viewDefCacheKey, {
|
|
222
|
+
view,
|
|
223
|
+
viewDefinition,
|
|
224
|
+
paramKey: viewParamKey,
|
|
225
|
+
}, { ttl });
|
|
226
226
|
return viewDefinition;
|
|
227
227
|
}
|
|
228
228
|
catch (err) {
|
|
@@ -238,7 +238,7 @@ export class LwrViewRegistry {
|
|
|
238
238
|
async renderView(view, viewParams, runtimeEnvironment, runtimeParams = {}, renderOptions) {
|
|
239
239
|
const { id, contentTemplate, rootComponent, layoutTemplate } = view;
|
|
240
240
|
const lwrResourcesId = `__LWR_RESOURCES__${Date.now()}`;
|
|
241
|
-
const renderedContent = await this.render({ id, contentTemplate, rootComponent }, { ...viewParams, lwr_resources: lwrResourcesId }, runtimeParams, renderOptions);
|
|
241
|
+
const renderedContent = await this.render({ id, contentTemplate, rootComponent }, { ...viewParams, lwr_resources: lwrResourcesId }, runtimeParams, runtimeEnvironment, renderOptions);
|
|
242
242
|
// normalize the renderOptions provided by the CompiledView content with the request options.
|
|
243
243
|
let normalizedRenderOptions = normalizeRenderOptions(this.runtimeEnvironment, renderedContent.options, renderOptions);
|
|
244
244
|
const layout = layoutTemplate || renderedContent.compiledView.layoutTemplate;
|
|
@@ -262,7 +262,7 @@ export class LwrViewRegistry {
|
|
|
262
262
|
...viewParams,
|
|
263
263
|
body: renderedContent.renderedView,
|
|
264
264
|
lwr_resources: lwrResourcesId,
|
|
265
|
-
}, runtimeParams);
|
|
265
|
+
}, runtimeParams, runtimeEnvironment);
|
|
266
266
|
normalizedRenderOptions = normalizeRenderOptions(this.runtimeEnvironment, renderedLayout.options, normalizedRenderOptions);
|
|
267
267
|
const renderedViewDef = await this.link({
|
|
268
268
|
...renderedLayout,
|
|
@@ -306,10 +306,10 @@ export class LwrViewRegistry {
|
|
|
306
306
|
});
|
|
307
307
|
return renderedViewDef;
|
|
308
308
|
}
|
|
309
|
-
async render(viewId, viewParams, runtimeParams, renderOptions) {
|
|
309
|
+
async render(viewId, viewParams, runtimeParams, runtimeEnvironment, renderOptions) {
|
|
310
310
|
const globalContext = this.globalData;
|
|
311
311
|
const { id, rootComponent, contentTemplate } = viewId;
|
|
312
|
-
const compiledView = await this.getView({ id, contentTemplate, rootComponent }
|
|
312
|
+
const compiledView = await this.getView({ id, contentTemplate, rootComponent });
|
|
313
313
|
// Get content/body of the view
|
|
314
314
|
/*
|
|
315
315
|
When rendering the compiled view, a collection variable properties can be passed to the render() to be rendered in
|
|
@@ -325,7 +325,7 @@ export class LwrViewRegistry {
|
|
|
325
325
|
...globalContext,
|
|
326
326
|
...compiledView.properties,
|
|
327
327
|
...viewParams,
|
|
328
|
-
});
|
|
328
|
+
}, runtimeEnvironment);
|
|
329
329
|
const normalizedResult = normalizeRenderedResult(result);
|
|
330
330
|
return {
|
|
331
331
|
compiledView,
|
|
@@ -350,6 +350,7 @@ export class LwrViewRegistry {
|
|
|
350
350
|
importer: importer || renderedView.compiledView.filePath,
|
|
351
351
|
};
|
|
352
352
|
const stringBuilder = createStringBuilder(renderedViewContent);
|
|
353
|
+
// Note: this is the TTL for the page NOT for the view registry cache
|
|
353
354
|
let pageTtl = renderedView.cache.ttl;
|
|
354
355
|
for (const viewTransformer of this.viewTransformers) {
|
|
355
356
|
// eslint-disable-next-line no-await-in-loop
|
|
@@ -389,6 +390,7 @@ export class LwrViewRegistry {
|
|
|
389
390
|
immutable,
|
|
390
391
|
viewRecord: {
|
|
391
392
|
assetReferences: reduceSourceAssetReferences(linkedMetadata.assetReferences),
|
|
393
|
+
moduleResources: [],
|
|
392
394
|
},
|
|
393
395
|
cache: { ttl: pageTtl },
|
|
394
396
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { logger } from '@lwrjs/diagnostics';
|
|
2
|
-
import { kebabCaseToModuleSpecifier, getModuleGraphs, GraphDepth, getModuleUriPrefix, explodeSpecifier, isBundler, getHydrateDirective, isGroupie, isExternalSpecifier, } from '@lwrjs/shared-utils';
|
|
2
|
+
import { kebabCaseToModuleSpecifier, getModuleGraphs, GraphDepth, getModuleUriPrefix, explodeSpecifier, isBundler, getHydrateDirective, isGroupie, isExternalSpecifier, isLocalDev, } from '@lwrjs/shared-utils';
|
|
3
3
|
import { AppResourceEnum, getAppSpecifier } from '@lwrjs/app-service/identity';
|
|
4
|
-
import { addExternalScriptNonce, generateHtmlTag, getModuleResourceByUri, getViewNonce } from '../utils.js';
|
|
5
|
-
import { flattenCustomElements, getViewBootstrapConfigurationResource, getViewHmrConfigurationResource, } from './utils.js';
|
|
4
|
+
import { addExternalScriptNonce, generateHtmlTag, getModuleResource, getModuleResourceByUri, getViewNonce, } from '../utils.js';
|
|
5
|
+
import { flattenCustomElements, getBundleIntegrity, getViewBootstrapConfigurationResource, getViewHmrConfigurationResource, } from './utils.js';
|
|
6
6
|
import { setPreloadModulesMeta, getPreloadModulesMeta } from './preload-utils.js';
|
|
7
7
|
function includeIdFactory(bundleConfig) {
|
|
8
8
|
return (moduleRef) => {
|
|
@@ -17,9 +17,8 @@ function includeIdFactory(bundleConfig) {
|
|
|
17
17
|
}
|
|
18
18
|
export async function getHtmlResources(view, viewParams, resourceContext) {
|
|
19
19
|
const { runtimeEnvironment, runtimeParams, moduleRegistry, moduleBundler, resourceRegistry, viewMetadata, } = resourceContext;
|
|
20
|
-
const {
|
|
20
|
+
const { format, hmrEnabled, bundle, debug, minify } = runtimeEnvironment;
|
|
21
21
|
const { customElements, serverData, serverDebug } = viewMetadata;
|
|
22
|
-
const version = lwrVersion;
|
|
23
22
|
const isAMD = format === 'amd';
|
|
24
23
|
const { bundleConfig } = resourceContext;
|
|
25
24
|
const { external = {}, exclude = [] } = bundleConfig;
|
|
@@ -89,9 +88,12 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
89
88
|
uris: [],
|
|
90
89
|
specifiers: [],
|
|
91
90
|
groups: new Map(),
|
|
91
|
+
integrities: new Map(),
|
|
92
92
|
};
|
|
93
93
|
// Determine if server side rendering view modules
|
|
94
94
|
const isSSR = view.bootstrap?.ssr;
|
|
95
|
+
// Determine the LWR client version
|
|
96
|
+
const version = view.bootstrap?.lwrVersion;
|
|
95
97
|
// Page level nonce for script tags
|
|
96
98
|
const nonce = getViewNonce(viewParams);
|
|
97
99
|
// ABS module and custom element references
|
|
@@ -109,7 +111,7 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
109
111
|
let def = (await resourceRegistry.getResource({ specifier: shimBundle, version }, runtimeEnvironment, runtimeParams));
|
|
110
112
|
if (!def) {
|
|
111
113
|
// HACK: fallback to looking for the other shim.
|
|
112
|
-
// TODO: remove this once
|
|
114
|
+
// TODO: remove this once KJ fixes W-15953000
|
|
113
115
|
let fallbackShimBundle;
|
|
114
116
|
if (shimBundle === 'lwr-loader-shim-legacy.bundle.js') {
|
|
115
117
|
fallbackShimBundle = 'lwr-loader-shim-legacy.bundle.min.js';
|
|
@@ -131,7 +133,10 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
131
133
|
addExternalScriptNonce(def, nonce);
|
|
132
134
|
requiredResources.push(def);
|
|
133
135
|
// Always inline the error shim script after the shim
|
|
134
|
-
const errorShimDef = (await resourceRegistry.getResource({ specifier: 'lwr-error-shim.js', version }, runtimeEnvironment,
|
|
136
|
+
const errorShimDef = (await resourceRegistry.getResource({ specifier: 'lwr-error-shim.js', version }, runtimeEnvironment,
|
|
137
|
+
// Hack there is only a prod version of the error shim so forcing ignore debug
|
|
138
|
+
// So we never check the debug metadata
|
|
139
|
+
{ ...runtimeParams, ignoreDebug: true }));
|
|
135
140
|
if (!errorShimDef) {
|
|
136
141
|
throw Error('Failed to find definition of resource: lwr-error-shim.js');
|
|
137
142
|
}
|
|
@@ -159,12 +164,14 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
159
164
|
if (!uri) {
|
|
160
165
|
throw Error(`Invalid Module Resource ${versionedSpecifier}`);
|
|
161
166
|
}
|
|
162
|
-
|
|
167
|
+
const integrity = getBundleIntegrity(bootstrapModuleGraph, versionedSpecifier);
|
|
168
|
+
moduleResources.push(getModuleResourceByUri(uri, runtimeEnvironment, { integrity, isPreload: false, isSSR, nonce }));
|
|
163
169
|
// PRELOAD the bootstrap module static dependencies as preloaded script resources
|
|
164
170
|
for (const depSpecifier of bootstrapModuleGraph.graphs[0].static) {
|
|
165
171
|
const uri = getPreloadUri(depSpecifier, bootstrapModuleGraph.uriMap);
|
|
166
172
|
if (uri) {
|
|
167
|
-
|
|
173
|
+
const integrity = getBundleIntegrity(bootstrapModuleGraph, depSpecifier);
|
|
174
|
+
setPreloadModulesMeta(depSpecifier, uri, integrity, groups, viewPreloads);
|
|
168
175
|
}
|
|
169
176
|
}
|
|
170
177
|
// PRELOAD configured preloadModules as preloaded script resources
|
|
@@ -214,13 +221,15 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
214
221
|
// PRELOAD the custom element module as a link resource
|
|
215
222
|
const specifier = graph.graphs[0].specifier;
|
|
216
223
|
const uri = graph.uriMap[specifier];
|
|
217
|
-
|
|
224
|
+
const integrity = getBundleIntegrity(graph, specifier);
|
|
225
|
+
setPreloadModulesMeta(specifier, uri, integrity, groups, viewPreloads);
|
|
218
226
|
// PRELOAD custom element static deps as link resources when bundling is ON
|
|
219
227
|
if (bundle) {
|
|
220
228
|
for (const depSpecifier of graph.graphs[0].static) {
|
|
221
|
-
const
|
|
222
|
-
if (
|
|
223
|
-
|
|
229
|
+
const depUri = getPreloadUri(depSpecifier, graph.uriMap);
|
|
230
|
+
if (depUri) {
|
|
231
|
+
const integrity = getBundleIntegrity(graph, depSpecifier);
|
|
232
|
+
setPreloadModulesMeta(depSpecifier, depUri, integrity, groups, viewPreloads);
|
|
224
233
|
}
|
|
225
234
|
}
|
|
226
235
|
}
|
|
@@ -242,12 +251,21 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
242
251
|
}
|
|
243
252
|
}));
|
|
244
253
|
if (viewContainsLiveElements) {
|
|
254
|
+
if (isLocalDev()) {
|
|
255
|
+
// ADD the client-side bootstrap module and mapping for local-dev
|
|
256
|
+
const localDevSpecifier = 'lwr_local_dev/bootstrap';
|
|
257
|
+
rootComponents.push(localDevSpecifier);
|
|
258
|
+
const localDevMod = await getModuleResource({ specifier: localDevSpecifier, version: '' }, runtimeEnvironment, {}, moduleRegistry, runtimeParams);
|
|
259
|
+
if (localDevMod.src)
|
|
260
|
+
imports[localDevSpecifier] = localDevMod.src;
|
|
261
|
+
}
|
|
245
262
|
// ADD configuration of the bootstrapModule
|
|
246
263
|
configResources.unshift(getViewBootstrapConfigurationResource({
|
|
247
264
|
id: view.id,
|
|
248
265
|
url: viewParams?.page?.url,
|
|
249
266
|
configAsSrc: view.bootstrap?.configAsSrc || false,
|
|
250
267
|
mixedMode: view.bootstrap?.mixedMode || false,
|
|
268
|
+
nonce: viewParams?.page?.nonce,
|
|
251
269
|
}, {
|
|
252
270
|
appId: appIdentity.appName,
|
|
253
271
|
bootstrapModule: versionedSpecifier,
|
|
@@ -269,7 +287,13 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
269
287
|
const dedupedUris = [...new Set(viewPreloads.uris)];
|
|
270
288
|
// PRELOAD script resources for preload module URIs after the bootstrap module
|
|
271
289
|
for (const preloadUri of dedupedUris) {
|
|
272
|
-
|
|
290
|
+
const integrity = viewPreloads.integrities.get(preloadUri);
|
|
291
|
+
moduleResources.push(getModuleResourceByUri(preloadUri, runtimeEnvironment, {
|
|
292
|
+
integrity,
|
|
293
|
+
isPreload: true,
|
|
294
|
+
isSSR,
|
|
295
|
+
nonce,
|
|
296
|
+
}));
|
|
273
297
|
}
|
|
274
298
|
// generate html partial
|
|
275
299
|
const htmlResources = await Promise.all([...configResources, ...requiredResources, ...moduleResources].map(generateHtmlTag));
|
|
@@ -278,6 +302,7 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
278
302
|
viewRecord: {
|
|
279
303
|
resources: requiredResources,
|
|
280
304
|
customElements: customElementsRecords,
|
|
305
|
+
moduleResources,
|
|
281
306
|
bootstrapModule: bootstrapModuleRef,
|
|
282
307
|
},
|
|
283
308
|
};
|
|
@@ -3,11 +3,12 @@ export type ViewPreloads = {
|
|
|
3
3
|
groups: Map<string, boolean>;
|
|
4
4
|
uris: string[];
|
|
5
5
|
specifiers: string[];
|
|
6
|
+
integrities: Map<string, string | undefined>;
|
|
6
7
|
};
|
|
7
8
|
/**
|
|
8
9
|
* keeps track of preloadModules metadata
|
|
9
10
|
*/
|
|
10
|
-
export declare function setPreloadModulesMeta(specifier: string, uri: string, groups: BundleGroups, preloads: ViewPreloads): void;
|
|
11
|
+
export declare function setPreloadModulesMeta(specifier: string, uri: string, integrity: string | undefined, groups: BundleGroups, preloads: ViewPreloads): void;
|
|
11
12
|
/**
|
|
12
13
|
* Recursively gets preloadModules metadata starting with a specifier
|
|
13
14
|
* Note: don't call me unless you got bundles
|
|
@@ -3,7 +3,7 @@ import { explodeSpecifier, getGroupName, getVersionedModuleId, normalizeVersionT
|
|
|
3
3
|
/**
|
|
4
4
|
* keeps track of preloadModules metadata
|
|
5
5
|
*/
|
|
6
|
-
export function setPreloadModulesMeta(specifier, uri, groups, preloads) {
|
|
6
|
+
export function setPreloadModulesMeta(specifier, uri, integrity, groups, preloads) {
|
|
7
7
|
// Throw a very specific error if we get this back and the uri property is not set
|
|
8
8
|
if (!uri) {
|
|
9
9
|
throw createSingleDiagnosticError({
|
|
@@ -20,6 +20,7 @@ export function setPreloadModulesMeta(specifier, uri, groups, preloads) {
|
|
|
20
20
|
const preloadModulesSpecifiers = preloads.specifiers;
|
|
21
21
|
const preloadBundleGroupsMap = preloads.groups;
|
|
22
22
|
const preloadModulesURIs = preloads.uris;
|
|
23
|
+
const preloadModuleIntegrities = preloads.integrities;
|
|
23
24
|
preloadModulesSpecifiers.push(specifier);
|
|
24
25
|
const { specifier: unversionedSpecifier } = explodeSpecifier(specifier);
|
|
25
26
|
const groupName = getGroupName(unversionedSpecifier, groups);
|
|
@@ -29,6 +30,7 @@ export function setPreloadModulesMeta(specifier, uri, groups, preloads) {
|
|
|
29
30
|
// With bundling groups, we only want to include one URI
|
|
30
31
|
preloadModulesURIs.push(uri);
|
|
31
32
|
groupName && preloadBundleGroupsMap.set(groupName, true);
|
|
33
|
+
preloadModuleIntegrities.set(uri, integrity);
|
|
32
34
|
}
|
|
33
35
|
/**
|
|
34
36
|
* Recursively gets preloadModules metadata starting with a specifier
|
|
@@ -60,12 +62,13 @@ viewPreloads, bundleConfig, moduleRegistry, defRegistry, runtimeEnvironment, run
|
|
|
60
62
|
const uri =
|
|
61
63
|
// eslint-disable-next-line no-await-in-loop
|
|
62
64
|
await defRegistry.resolveModuleUri(versionedModuleId, runtimeEnvironment, runtimeParams);
|
|
63
|
-
// fallback to
|
|
65
|
+
// fallback to un-versioned specifier if needed
|
|
64
66
|
const normalizedSpecifier = versionedModuleId.version === VERSION_NOT_PROVIDED ? specifier : versionedModuleSpecifier;
|
|
65
|
-
|
|
67
|
+
// Will set the integrity after loading the record below
|
|
68
|
+
const preloadModuleRecord = await defRegistry.getModuleBundle(versionedModuleId, runtimeEnvironment, runtimeParams);
|
|
69
|
+
const { integrity } = preloadModuleRecord;
|
|
70
|
+
setPreloadModulesMeta(normalizedSpecifier, uri, integrity, groups, viewPreloads);
|
|
66
71
|
if (exclude.length || Object.keys(groups).length) {
|
|
67
|
-
// check if we need to also preload any excluded dependencies of this preload module
|
|
68
|
-
const preloadModuleRecord = await defRegistry.getModuleBundle(versionedModuleId, runtimeEnvironment, runtimeParams);
|
|
69
72
|
const { imports } = preloadModuleRecord.bundleRecord;
|
|
70
73
|
if (imports) {
|
|
71
74
|
if (!pending) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { ClientBootstrapConfig, RuntimeEnvironment, RuntimeParams, ResourceDefinition, RenderedViewMetadata, CustomElementReference, View, ViewInfo } from '@lwrjs/types';
|
|
1
|
+
import type { ClientBootstrapConfig, RuntimeEnvironment, RuntimeParams, ResourceDefinition, RenderedViewMetadata, CustomElementReference, View, ViewInfo, FlattenedModuleGraphs } from '@lwrjs/types';
|
|
2
2
|
export declare function getViewBootstrapConfigurationResource(viewInfo: ViewInfo, config: ClientBootstrapConfig, runtimeEnvironment: RuntimeEnvironment, runtimeParams: RuntimeParams, debugMessage?: string): ResourceDefinition;
|
|
3
3
|
export declare function getViewHmrConfigurationResource(view: View, viewMetadata: RenderedViewMetadata): ResourceDefinition;
|
|
4
4
|
export declare function flattenCustomElements(arr: CustomElementReference[], isSSR?: boolean): CustomElementReference[];
|
|
5
|
+
export declare function getBundleIntegrity(bootstrapModuleGraph: FlattenedModuleGraphs, versionedSpecifier: string): string | undefined;
|
|
5
6
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -49,6 +49,10 @@ export function getViewBootstrapConfigurationResource(viewInfo, config, runtimeE
|
|
|
49
49
|
type: CONTENT_TYPE,
|
|
50
50
|
content: configString,
|
|
51
51
|
inline: false,
|
|
52
|
+
// TODO do not create an integrity for config as src. We modify this script
|
|
53
|
+
// for HMR and fingerprints
|
|
54
|
+
// integrity: createIntegrityHash(configString),
|
|
55
|
+
nonce: viewInfo.nonce,
|
|
52
56
|
src: url,
|
|
53
57
|
};
|
|
54
58
|
}
|
|
@@ -111,4 +115,9 @@ export function flattenCustomElements(arr, isSSR = false) {
|
|
|
111
115
|
flatten(arr);
|
|
112
116
|
return ret;
|
|
113
117
|
}
|
|
118
|
+
export function getBundleIntegrity(bootstrapModuleGraph, versionedSpecifier) {
|
|
119
|
+
const def = bootstrapModuleGraph.linkedDefinitions[versionedSpecifier];
|
|
120
|
+
const integrity = def?.integrity;
|
|
121
|
+
return integrity;
|
|
122
|
+
}
|
|
114
123
|
//# sourceMappingURL=utils.js.map
|
|
@@ -2,14 +2,13 @@ import { logger } from '@lwrjs/diagnostics';
|
|
|
2
2
|
import { kebabCaseToModuleSpecifier, toImportMetadata, getModuleGraphs, getMappingUriPrefix, GraphDepth, explodeSpecifier, isBundler, getHydrateDirective, isGroupie, } from '@lwrjs/shared-utils';
|
|
3
3
|
import { AppResourceEnum, getAppSpecifier } from '@lwrjs/app-service/identity';
|
|
4
4
|
import { addExternalScriptNonce, generateHtmlTag, getModuleResourceByUri, getViewNonce } from '../utils.js';
|
|
5
|
-
import { flattenCustomElements, getViewBootstrapConfigurationResource, getViewHmrConfigurationResource, } from './utils.js';
|
|
5
|
+
import { flattenCustomElements, getBundleIntegrity, getViewBootstrapConfigurationResource, getViewHmrConfigurationResource, } from './utils.js';
|
|
6
6
|
import { setPreloadModulesMeta, getPreloadModulesMeta } from './preload-utils.js';
|
|
7
7
|
export async function getHtmlResources(view, viewParams, resourceContext) {
|
|
8
8
|
const { runtimeEnvironment, runtimeParams, moduleRegistry, moduleBundler, resourceRegistry, viewMetadata, } = resourceContext;
|
|
9
|
-
const {
|
|
9
|
+
const { format, hmrEnabled, bundle, debug, minify } = runtimeEnvironment;
|
|
10
10
|
const { customElements, serverData, serverDebug } = viewMetadata;
|
|
11
11
|
const defRegistry = bundle ? moduleBundler : moduleRegistry;
|
|
12
|
-
const version = lwrVersion;
|
|
13
12
|
const isAMD = format === 'amd';
|
|
14
13
|
const depth = isAMD
|
|
15
14
|
? { static: GraphDepth.ALL, dynamic: 1 }
|
|
@@ -72,9 +71,12 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
72
71
|
uris: [],
|
|
73
72
|
specifiers: [],
|
|
74
73
|
groups: new Map(),
|
|
74
|
+
integrities: new Map(),
|
|
75
75
|
};
|
|
76
76
|
// Determine if server side rendering view modules
|
|
77
77
|
const isSSR = view.bootstrap?.ssr;
|
|
78
|
+
// Determine the LWR client version
|
|
79
|
+
const version = view.bootstrap?.lwrVersion;
|
|
78
80
|
// Page level nonce for script tags
|
|
79
81
|
const nonce = getViewNonce(viewParams);
|
|
80
82
|
// ABS module and custom element references
|
|
@@ -99,7 +101,10 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
99
101
|
addExternalScriptNonce(def, nonce);
|
|
100
102
|
requiredResources.push(def);
|
|
101
103
|
// Always inline the error shim script after the shim
|
|
102
|
-
const errorShimDef = (await resourceRegistry.getResource({ specifier: 'lwr-error-shim.js', version }, runtimeEnvironment,
|
|
104
|
+
const errorShimDef = (await resourceRegistry.getResource({ specifier: 'lwr-error-shim.js', version }, runtimeEnvironment,
|
|
105
|
+
// Hack there is only a prod version of the error shim so forcing ignore debug
|
|
106
|
+
// So we never check the debug metadata
|
|
107
|
+
{ ...runtimeParams, ignoreDebug: true }));
|
|
103
108
|
if (!errorShimDef) {
|
|
104
109
|
throw Error('Failed to find definition of resource: lwr-error-shim.js');
|
|
105
110
|
}
|
|
@@ -126,12 +131,14 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
126
131
|
if (!uri) {
|
|
127
132
|
throw Error(`Invalid Module Resource ${versionedSpecifier}`);
|
|
128
133
|
}
|
|
129
|
-
|
|
134
|
+
const integrity = getBundleIntegrity(bootstrapModuleGraph, versionedSpecifier);
|
|
135
|
+
moduleResources.push(getModuleResourceByUri(uri, runtimeEnvironment, { integrity, isPreload: false, isSSR, nonce }));
|
|
130
136
|
// PRELOAD the bootstrap module static dependencies as preloaded script resources
|
|
131
137
|
for (const depSpecifier of bootstrapModuleGraph.graphs[0].static) {
|
|
132
138
|
const uri = getPreloadUri(depSpecifier, bootstrapModuleGraph.uriMap);
|
|
133
139
|
if (uri) {
|
|
134
|
-
|
|
140
|
+
const integrity = getBundleIntegrity(bootstrapModuleGraph, depSpecifier);
|
|
141
|
+
setPreloadModulesMeta(depSpecifier, uri, integrity, groups, viewPreloads);
|
|
135
142
|
}
|
|
136
143
|
}
|
|
137
144
|
// PRELOAD configured preloadModules as preloaded script resources
|
|
@@ -183,13 +190,15 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
183
190
|
// PRELOAD the custom element module as a link resource
|
|
184
191
|
const specifier = graph.graphs[0].specifier;
|
|
185
192
|
const uri = graph.uriMap[specifier];
|
|
186
|
-
|
|
193
|
+
const integrity = getBundleIntegrity(graph, specifier);
|
|
194
|
+
setPreloadModulesMeta(specifier, uri, integrity, groups, viewPreloads);
|
|
187
195
|
// PRELOAD custom element static deps as link resources when bundling is ON
|
|
188
196
|
if (bundle) {
|
|
189
197
|
for (const depSpecifier of graph.graphs[0].static) {
|
|
190
|
-
const
|
|
191
|
-
if (
|
|
192
|
-
|
|
198
|
+
const depUri = getPreloadUri(depSpecifier, graph.uriMap);
|
|
199
|
+
if (depUri) {
|
|
200
|
+
const integrity = getBundleIntegrity(graph, depSpecifier);
|
|
201
|
+
setPreloadModulesMeta(depSpecifier, depUri, integrity, groups, viewPreloads);
|
|
193
202
|
}
|
|
194
203
|
}
|
|
195
204
|
}
|
|
@@ -219,6 +228,7 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
219
228
|
url: viewParams?.page?.url,
|
|
220
229
|
configAsSrc: view.bootstrap?.configAsSrc || false,
|
|
221
230
|
mixedMode: view.bootstrap?.mixedMode || false,
|
|
231
|
+
nonce: viewParams?.page?.nonce,
|
|
222
232
|
}, {
|
|
223
233
|
appId: appIdentity.appName,
|
|
224
234
|
bootstrapModule: versionedSpecifier,
|
|
@@ -238,7 +248,13 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
238
248
|
// PRELOAD script resources for preload module URIs after the bootstrap module
|
|
239
249
|
const dedupedUris = [...new Set(viewPreloads.uris)];
|
|
240
250
|
for (const preloadUri of dedupedUris) {
|
|
241
|
-
|
|
251
|
+
const integrity = viewPreloads.integrities.get(preloadUri);
|
|
252
|
+
moduleResources.push(getModuleResourceByUri(preloadUri, runtimeEnvironment, {
|
|
253
|
+
integrity,
|
|
254
|
+
isPreload: true,
|
|
255
|
+
isSSR,
|
|
256
|
+
nonce,
|
|
257
|
+
}));
|
|
242
258
|
}
|
|
243
259
|
// generate html partial
|
|
244
260
|
const htmlResources = await Promise.all([...configResources, ...requiredResources, ...moduleResources].map(generateHtmlTag));
|
|
@@ -255,6 +271,7 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
255
271
|
customElements: customElementsRecords,
|
|
256
272
|
endpoints,
|
|
257
273
|
importMetadata,
|
|
274
|
+
moduleResources,
|
|
258
275
|
bootstrapModule: bootstrapModuleRef,
|
|
259
276
|
},
|
|
260
277
|
};
|
package/build/es/utils.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export declare function createJsonModule(specifier: string, moduleRegistry: Publ
|
|
|
13
13
|
/**
|
|
14
14
|
* Get the nonce set on the page context of the view params
|
|
15
15
|
*/
|
|
16
|
-
export declare function getViewNonce(viewParams: ViewParams): string;
|
|
16
|
+
export declare function getViewNonce(viewParams: ViewParams): string | undefined;
|
|
17
17
|
/**
|
|
18
18
|
* Generate a nonce in the page context of the view params
|
|
19
19
|
*/
|
|
@@ -21,5 +21,5 @@ export declare function generateViewNonce(viewParams: ViewParams): void;
|
|
|
21
21
|
/**
|
|
22
22
|
* Add a nonce to a script definition in view if it is external (not inline)
|
|
23
23
|
*/
|
|
24
|
-
export declare function addExternalScriptNonce(def: ResourceDefinition, nonce
|
|
24
|
+
export declare function addExternalScriptNonce(def: ResourceDefinition, nonce?: string): void;
|
|
25
25
|
//# sourceMappingURL=utils.d.ts.map
|
package/build/es/utils.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { basename, extname } from 'path';
|
|
2
|
-
import { explodeSpecifier, getMappingUriPrefix, getSpecifier, getClientBootstrapConfigurationUri, mimeLookup, DEFAULT_TITLE, streamToString, } from '@lwrjs/shared-utils';
|
|
2
|
+
import { explodeSpecifier, getMappingUriPrefix, getSpecifier, getClientBootstrapConfigurationUri, mimeLookup, DEFAULT_TITLE, streamToString, getFeatureFlags, } from '@lwrjs/shared-utils';
|
|
3
3
|
import { AppResourceEnum, getAppSpecifier, ResourceIdentityTypes, } from '@lwrjs/app-service/identity';
|
|
4
4
|
import crypto from 'crypto';
|
|
5
5
|
function generateExternalStyle(src) {
|
|
6
6
|
return `<link rel="stylesheet" href="${src}">`;
|
|
7
7
|
}
|
|
8
|
-
function generateExternalScript(type = 'application/javascript', src, async, defer, nonce) {
|
|
8
|
+
function generateExternalScript(type = 'application/javascript', src, async, defer, nonce, integrity) {
|
|
9
|
+
// add script loader order attribute
|
|
9
10
|
let scriptLoadOrder = '';
|
|
10
11
|
if (defer) {
|
|
11
12
|
scriptLoadOrder = ' defer';
|
|
@@ -13,8 +14,19 @@ function generateExternalScript(type = 'application/javascript', src, async, def
|
|
|
13
14
|
else if (async) {
|
|
14
15
|
scriptLoadOrder = ' async';
|
|
15
16
|
}
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
// add the right CSP header attribute
|
|
18
|
+
let cspHashAttr = '';
|
|
19
|
+
// For now use the ENABLE_NONCE to gate adding nonce or integrity
|
|
20
|
+
// W-15868505 is open to create a better config
|
|
21
|
+
if (getFeatureFlags().ENABLE_NONCE) {
|
|
22
|
+
if (integrity) {
|
|
23
|
+
cspHashAttr = ` integrity="${integrity}"`;
|
|
24
|
+
}
|
|
25
|
+
else if (nonce) {
|
|
26
|
+
cspHashAttr = ` nonce="${nonce}"`;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return `<script type="${type}"${scriptLoadOrder}${cspHashAttr} src="${src}"></script>`;
|
|
18
30
|
}
|
|
19
31
|
function generateLinkPreloadTag({ href, type }) {
|
|
20
32
|
if (type === 'module') {
|
|
@@ -24,7 +36,7 @@ function generateLinkPreloadTag({ href, type }) {
|
|
|
24
36
|
return `<link rel="preload" href="${href}" type="${type}" />`;
|
|
25
37
|
}
|
|
26
38
|
}
|
|
27
|
-
function generateExternalTag({ type, src = '', async, defer, isPreload, nonce }) {
|
|
39
|
+
function generateExternalTag({ type, src = '', async, defer, isPreload, nonce, integrity, }) {
|
|
28
40
|
if (isPreload) {
|
|
29
41
|
return generateLinkPreloadTag({ href: src, type: type });
|
|
30
42
|
}
|
|
@@ -32,13 +44,13 @@ function generateExternalTag({ type, src = '', async, defer, isPreload, nonce })
|
|
|
32
44
|
return generateExternalStyle(src);
|
|
33
45
|
}
|
|
34
46
|
else {
|
|
35
|
-
return generateExternalScript(type, src, async, defer, nonce);
|
|
47
|
+
return generateExternalScript(type, src, async, defer, nonce, integrity);
|
|
36
48
|
}
|
|
37
49
|
}
|
|
38
50
|
async function generateInlineTag({ specifier, type, content, stream, nonce }) {
|
|
39
51
|
const typeStr = type === 'text/css' ? '' : ` type="${type}"`;
|
|
40
52
|
const tag = type === 'text/css' ? 'style' : 'script';
|
|
41
|
-
const nonceStr = nonce ? ` nonce="${nonce}"` : '';
|
|
53
|
+
const nonceStr = getFeatureFlags().ENABLE_NONCE && nonce && nonce ? ` nonce="${nonce}"` : '';
|
|
42
54
|
if (!content && !stream) {
|
|
43
55
|
throw new Error(`Invalid inline Resource Definition: must have either "content" or "stream": "${specifier}"`);
|
|
44
56
|
}
|
|
@@ -83,7 +95,6 @@ export function normalizeRenderOptions(runtimeEnvironment, overrideRenderOptions
|
|
|
83
95
|
// Default render options
|
|
84
96
|
skipMetadataCollection: false,
|
|
85
97
|
freezeAssets: runtimeEnvironment.immutableAssets,
|
|
86
|
-
skipCaching: false,
|
|
87
98
|
viewParamCacheKey: null,
|
|
88
99
|
...baseRenderOptions,
|
|
89
100
|
...overrideRenderOptions,
|
|
@@ -205,7 +216,7 @@ export async function getModuleResource(moduleId, runtimeEnvironment, moduleReso
|
|
|
205
216
|
};
|
|
206
217
|
}
|
|
207
218
|
export function getModuleResourceByUri(uri, runtimeEnvironment, moduleResourceMeta) {
|
|
208
|
-
const { isSSR = false, isPreload = false, nonce } = moduleResourceMeta;
|
|
219
|
+
const { integrity, isSSR = false, isPreload = false, nonce } = moduleResourceMeta;
|
|
209
220
|
const { format } = runtimeEnvironment;
|
|
210
221
|
return {
|
|
211
222
|
src: uri,
|
|
@@ -215,6 +226,7 @@ export function getModuleResourceByUri(uri, runtimeEnvironment, moduleResourceMe
|
|
|
215
226
|
// only use link preload for ESM for compat reasons.
|
|
216
227
|
// AMD should use regular script tags for preloading
|
|
217
228
|
isPreload: format !== 'amd' && isPreload,
|
|
229
|
+
integrity,
|
|
218
230
|
nonce,
|
|
219
231
|
};
|
|
220
232
|
}
|
|
@@ -243,25 +255,29 @@ export async function createJsonModule(specifier, moduleRegistry, environment, p
|
|
|
243
255
|
* Get the nonce set on the page context of the view params
|
|
244
256
|
*/
|
|
245
257
|
export function getViewNonce(viewParams) {
|
|
246
|
-
return
|
|
258
|
+
return getFeatureFlags().ENABLE_NONCE
|
|
259
|
+
? viewParams?.page?.nonce
|
|
260
|
+
: undefined;
|
|
247
261
|
}
|
|
248
262
|
/**
|
|
249
263
|
* Generate a nonce in the page context of the view params
|
|
250
264
|
*/
|
|
251
265
|
export function generateViewNonce(viewParams) {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
viewParams.page
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
266
|
+
if (getFeatureFlags().ENABLE_NONCE) {
|
|
267
|
+
const nonce = crypto.randomBytes(16).toString('base64');
|
|
268
|
+
if (!viewParams.page) {
|
|
269
|
+
viewParams.page = { nonce };
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
viewParams.page.nonce = nonce;
|
|
273
|
+
}
|
|
258
274
|
}
|
|
259
275
|
}
|
|
260
276
|
/**
|
|
261
277
|
* Add a nonce to a script definition in view if it is external (not inline)
|
|
262
278
|
*/
|
|
263
279
|
export function addExternalScriptNonce(def, nonce) {
|
|
264
|
-
if (!def.inline) {
|
|
280
|
+
if (nonce && getFeatureFlags().ENABLE_NONCE && !def.inline) {
|
|
265
281
|
def.nonce = nonce;
|
|
266
282
|
}
|
|
267
283
|
}
|
package/build/es/view-handler.js
CHANGED
|
@@ -146,6 +146,8 @@ export class LwrViewHandler {
|
|
|
146
146
|
const paths = { rootDir, assets, contentDir, layoutsDir };
|
|
147
147
|
const locale = runtimeParams.locale;
|
|
148
148
|
const basePath = runtimeParams.basePath;
|
|
149
|
+
const assetBasePath = runtimeParams.assetBasePath;
|
|
150
|
+
const uiBasePath = runtimeParams.assetBasePath;
|
|
149
151
|
const viewApi = this.getBoundApi(viewRequest, route, runtimeEnvironment, runtimeParams);
|
|
150
152
|
const response = await getTracer().trace({
|
|
151
153
|
name: ViewSpan.ExecuteRouteHandler,
|
|
@@ -155,7 +157,7 @@ export class LwrViewHandler {
|
|
|
155
157
|
},
|
|
156
158
|
}, async () => {
|
|
157
159
|
try {
|
|
158
|
-
return await routeHandlerFn({ ...viewRequest, locale, basePath }, { route: route, viewApi, ...paths }, routeHandlerOptions);
|
|
160
|
+
return await routeHandlerFn({ ...viewRequest, locale, basePath, assetBasePath, uiBasePath }, { route: route, viewApi, ...paths }, routeHandlerOptions);
|
|
159
161
|
}
|
|
160
162
|
catch (err) {
|
|
161
163
|
if (err instanceof DiagnosticsError) {
|
|
@@ -195,6 +197,11 @@ export class LwrViewHandler {
|
|
|
195
197
|
view, viewParams, renderOptions) {
|
|
196
198
|
const { id, bootstrap } = route;
|
|
197
199
|
const managedView = { ...view, id, bootstrap };
|
|
200
|
+
// If this is a localized view request (could have come from a route handler)
|
|
201
|
+
// Update the runtime params
|
|
202
|
+
if (view.locale) {
|
|
203
|
+
runtimeParams.locale = view.locale;
|
|
204
|
+
}
|
|
198
205
|
const viewResponse = {
|
|
199
206
|
view: managedView,
|
|
200
207
|
viewParams,
|
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",
|
|
@@ -29,18 +29,21 @@
|
|
|
29
29
|
"build/**/*.cjs",
|
|
30
30
|
"build/**/*.d.ts"
|
|
31
31
|
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsc -b"
|
|
34
|
+
},
|
|
32
35
|
"dependencies": {
|
|
33
|
-
"@lwrjs/app-service": "0.13.0
|
|
34
|
-
"@lwrjs/diagnostics": "0.13.0
|
|
35
|
-
"@lwrjs/instrumentation": "0.13.0
|
|
36
|
-
"@lwrjs/shared-utils": "0.13.0
|
|
37
|
-
"lru-cache": "^10.
|
|
36
|
+
"@lwrjs/app-service": "0.13.0",
|
|
37
|
+
"@lwrjs/diagnostics": "0.13.0",
|
|
38
|
+
"@lwrjs/instrumentation": "0.13.0",
|
|
39
|
+
"@lwrjs/shared-utils": "0.13.0",
|
|
40
|
+
"lru-cache": "^10.4.3"
|
|
38
41
|
},
|
|
39
42
|
"devDependencies": {
|
|
40
|
-
"@lwrjs/types": "0.13.0
|
|
43
|
+
"@lwrjs/types": "0.13.0"
|
|
41
44
|
},
|
|
42
45
|
"engines": {
|
|
43
46
|
"node": ">=18.0.0"
|
|
44
47
|
},
|
|
45
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "21dc6b8ffd2e633f36b46daf9e1563992c5143b9"
|
|
46
49
|
}
|