@lwrjs/view-registry 0.15.0-alpha.26 → 0.15.0-alpha.28

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.
@@ -145,28 +145,20 @@ var LwrViewRegistry = class {
145
145
  const {contentTemplate, rootComponent} = viewId;
146
146
  const compiledViewCacheKey = (0, import_shared_utils.getCacheKeyFromJson)({contentTemplate, rootComponent});
147
147
  import_diagnostics.logger.debug(`[view-registry][getView] compiledViewCacheKey=${compiledViewCacheKey}`);
148
- if (this.compiledViews.has(compiledViewCacheKey)) {
148
+ const route = this.globalConfig.routes.find((r) => r.id === viewId.id);
149
+ const skipCaching = route?.cache?.ttl === 0;
150
+ if (!skipCaching && this.compiledViews.has(compiledViewCacheKey)) {
149
151
  return this.compiledViews.get(compiledViewCacheKey);
150
152
  }
151
153
  const compiledView = await this.delegateGetView(viewId);
152
- this.compiledViews.set(compiledViewCacheKey, compiledView);
154
+ if (!skipCaching) {
155
+ this.compiledViews.set(compiledViewCacheKey, compiledView);
156
+ }
153
157
  return compiledView;
154
158
  }
155
159
  hasViewDefinition(view, viewParams, runtimeEnvironment, runtimeParams, renderOptions) {
156
- const {id, bootstrap, rootComponent, contentTemplate, layoutTemplate} = view;
157
160
  const {freezeAssets, viewParamCacheKey} = (0, import_utils.normalizeRenderOptions)(this.runtimeEnvironment, renderOptions);
158
- const viewDefId = (0, import_shared_utils.getCacheKeyFromJson)({
159
- id,
160
- bootstrap,
161
- rootComponent,
162
- contentTemplate,
163
- layoutTemplate,
164
- freezeAssets,
165
- locale: runtimeParams?.locale,
166
- basePath: runtimeParams?.basePath,
167
- debug: runtimeEnvironment.debug,
168
- nonceEnabled: (0, import_shared_utils.getFeatureFlags)().ENABLE_NONCE
169
- });
161
+ const viewDefId = (0, import_utils.getViewDefCacheKey)(view, runtimeEnvironment, freezeAssets, runtimeParams);
170
162
  import_diagnostics.logger.debug(`[view-registry][hasViewDefinition] viewDefId=${viewDefId}`);
171
163
  const viewParamKey = viewParamCacheKey ? (0, import_shared_utils.getCacheKeyFromJson)(viewParamCacheKey) : (0, import_shared_utils.getCacheKeyFromJson)(viewParams);
172
164
  import_diagnostics.logger.debug(`[view-registry][hasViewDefinition] viewParamKey=${viewParamKey}`);
@@ -182,14 +174,7 @@ var LwrViewRegistry = class {
182
174
  runtimeParams.requestCache = runtimeParams.requestCache ?? {};
183
175
  try {
184
176
  const {freezeAssets, viewParamCacheKey} = (0, import_utils.normalizeRenderOptions)(this.runtimeEnvironment, renderOptions);
185
- const viewDefCacheKey = (0, import_shared_utils.getCacheKeyFromJson)({
186
- ...view,
187
- freezeAssets,
188
- locale: runtimeParams?.locale,
189
- basePath: runtimeParams?.basePath,
190
- debug: runtimeEnvironment.debug,
191
- nonceEnabled: (0, import_shared_utils.getFeatureFlags)().ENABLE_NONCE
192
- });
177
+ const viewDefCacheKey = (0, import_utils.getViewDefCacheKey)(view, runtimeEnvironment, freezeAssets, runtimeParams);
193
178
  import_diagnostics.logger.debug(`[view-registry][getViewDefinition] viewDefCacheKey=${viewDefCacheKey}`);
194
179
  const viewParamKey = viewParamCacheKey ? (0, import_shared_utils.getCacheKeyFromJson)(viewParamCacheKey) : (0, import_shared_utils.getCacheKeyFromJson)(viewParams);
195
180
  import_diagnostics.logger.debug(`[view-registry][getViewDefinition] viewParamKey=${viewParamKey}`);
@@ -208,7 +193,7 @@ var LwrViewRegistry = class {
208
193
  view: view.id,
209
194
  ssr: view.bootstrap?.ssr === true
210
195
  }
211
- }, () => this.renderView(view, updatableViewParams, runtimeEnvironment, runtimeParams, renderOptions)));
196
+ }, () => this.renderView(view, updatableViewParams, runtimeEnvironment, runtimeParams, pendingViewDefCacheKey, renderOptions)));
212
197
  viewDefinition.nonce = (0, import_utils.getViewNonce)(updatableViewParams);
213
198
  const route = this.globalConfig.routes.find((r) => r.id === view.id);
214
199
  const maxViewCacheTtl = (0, import_shared_utils.getFeatureFlags)().MAX_VIEW_CACHE_TTL;
@@ -232,7 +217,7 @@ var LwrViewRegistry = class {
232
217
  throw (0, import_diagnostics.createSingleDiagnosticError)({description: import_diagnostics.descriptions.SERVER.UNEXPECTED_ERROR(message)}, import_diagnostics.LwrServerError);
233
218
  }
234
219
  }
235
- async renderView(view, viewParams, runtimeEnvironment, runtimeParams, renderOptions) {
220
+ async renderView(view, viewParams, runtimeEnvironment, runtimeParams, viewCacheKey, renderOptions) {
236
221
  const {id, contentTemplate, rootComponent, layoutTemplate} = view;
237
222
  const lwrResourcesId = `__LWR_RESOURCES__${Date.now()}`;
238
223
  const renderedContent = await this.render({id, contentTemplate, rootComponent}, {...viewParams, lwr_resources: lwrResourcesId}, runtimeParams, runtimeEnvironment);
@@ -245,7 +230,8 @@ var LwrViewRegistry = class {
245
230
  runtimeEnvironment,
246
231
  runtimeParams,
247
232
  renderOptions: normalizedRenderOptions,
248
- contentIds: {lwrResourcesId}
233
+ contentIds: {lwrResourcesId},
234
+ viewCacheKey
249
235
  });
250
236
  return renderedViewDef2;
251
237
  }
@@ -290,7 +276,8 @@ var LwrViewRegistry = class {
290
276
  runtimeParams,
291
277
  renderOptions: normalizedRenderOptions,
292
278
  contentIds: {lwrResourcesId},
293
- importer: renderedContent.compiledView.filePath
279
+ importer: renderedContent.compiledView.filePath,
280
+ viewCacheKey
294
281
  });
295
282
  return renderedViewDef;
296
283
  }
@@ -318,7 +305,8 @@ var LwrViewRegistry = class {
318
305
  runtimeParams,
319
306
  renderOptions,
320
307
  contentIds,
321
- importer
308
+ importer,
309
+ viewCacheKey
322
310
  } = viewContext;
323
311
  const {skipMetadataCollection, freezeAssets} = renderOptions;
324
312
  const {lwrResourcesId} = contentIds;
@@ -329,19 +317,37 @@ var LwrViewRegistry = class {
329
317
  metadata: renderedViewMetadata,
330
318
  compiledView: {immutable = true}
331
319
  } = renderedView;
332
- const linkedMetadata = skipMetadataCollection ? renderedViewMetadata : await (0, import_shared_utils.extractMetadataFromHtml)(renderedViewContent, renderedViewMetadata, this.globalConfig);
320
+ const {linkedMetadata, stringBuilder} = await (0, import_instrumentation.getTracer)().trace({
321
+ name: import_instrumentation.ViewSpan.ParseView,
322
+ attributes: {
323
+ view: view.id,
324
+ ssr: view.bootstrap?.ssr === true
325
+ }
326
+ }, async () => {
327
+ const linkedMetadata2 = skipMetadataCollection ? renderedViewMetadata : await (0, import_shared_utils.extractMetadataFromHtml)(renderedViewContent, renderedViewMetadata, this.globalConfig);
328
+ const stringBuilder2 = (0, import_shared_utils.createStringBuilder)(renderedViewContent);
329
+ return {linkedMetadata: linkedMetadata2, stringBuilder: stringBuilder2};
330
+ });
333
331
  const mergedViewContext = {
334
332
  ...viewContext,
335
333
  config: this.globalConfig,
336
334
  runtimeEnvironment,
337
335
  importer: importer || renderedView.compiledView.filePath
338
336
  };
339
- const stringBuilder = (0, import_shared_utils.createStringBuilder)(renderedViewContent);
340
337
  let pageTtl = renderedView.cache.ttl;
338
+ let pageRedirect = renderedView.redirect;
341
339
  for (const viewTransformer of this.viewTransformers) {
342
- const linkResults = await viewTransformer.link?.(stringBuilder, mergedViewContext, linkedMetadata);
340
+ const linkResults = await (0, import_instrumentation.getTracer)().trace({
341
+ name: import_instrumentation.ViewSpan.Transform,
342
+ attributes: {
343
+ name: viewTransformer.name
344
+ }
345
+ }, () => viewTransformer.link?.(stringBuilder, mergedViewContext, linkedMetadata));
343
346
  const ttl = linkResults && linkResults.cache?.ttl;
344
347
  pageTtl = (0, import_shared_utils.shortestTtl)(ttl || void 0, pageTtl);
348
+ if (!pageRedirect) {
349
+ pageRedirect = linkResults ? linkResults.redirect : void 0;
350
+ }
345
351
  }
346
352
  const linkedAssetContent = stringBuilder.toString();
347
353
  if (linkedAssetContent.includes(lwrResourcesId)) {
@@ -353,7 +359,9 @@ var LwrViewRegistry = class {
353
359
  resourceRegistry,
354
360
  runtimeEnvironment,
355
361
  runtimeParams,
356
- bundleConfig: this.globalConfig.bundleConfig
362
+ bundleConfig: this.globalConfig.bundleConfig,
363
+ unsafeEnableViewLinkCaching: this.globalConfig.unsafeEnableViewLinkCaching,
364
+ viewLinkCacheKey: viewCacheKey
357
365
  });
358
366
  return {
359
367
  renderedView: linkedView,
@@ -363,7 +371,7 @@ var LwrViewRegistry = class {
363
371
  ...viewRecord
364
372
  },
365
373
  cache: {ttl: pageTtl},
366
- redirect: renderedView.redirect
374
+ redirect: pageRedirect
367
375
  };
368
376
  }
369
377
  return {
@@ -374,7 +382,7 @@ var LwrViewRegistry = class {
374
382
  moduleResources: []
375
383
  },
376
384
  cache: {ttl: pageTtl},
377
- redirect: renderedView.redirect
385
+ redirect: pageRedirect
378
386
  };
379
387
  }
380
388
  };
@@ -32,6 +32,7 @@ var import_identity = __toModule(require("@lwrjs/app-service/identity"));
32
32
  var import_utils = __toModule(require("../utils.cjs"));
33
33
  var import_utils2 = __toModule(require("./utils.cjs"));
34
34
  var import_preload_utils = __toModule(require("./preload-utils.cjs"));
35
+ var import_lru_cache = __toModule(require("lru-cache"));
35
36
  function includeIdFactory(bundleConfig) {
36
37
  return (moduleRef) => {
37
38
  if ((0, import_shared_utils.isExternalSpecifier)(moduleRef.specifier, bundleConfig)) {
@@ -40,6 +41,8 @@ function includeIdFactory(bundleConfig) {
40
41
  return true;
41
42
  };
42
43
  }
44
+ var moduleGraphsCache = new import_lru_cache.LRUCache({max: 500});
45
+ var isRunningLocalDev = (0, import_shared_utils.isLocalDev)();
43
46
  async function getHtmlResources(view, viewParams, resourceContext) {
44
47
  const {
45
48
  runtimeEnvironment,
@@ -47,14 +50,16 @@ async function getHtmlResources(view, viewParams, resourceContext) {
47
50
  moduleRegistry,
48
51
  moduleBundler,
49
52
  resourceRegistry,
50
- viewMetadata
53
+ viewMetadata,
54
+ viewLinkCacheKey
51
55
  } = resourceContext;
52
56
  const {format, hmrEnabled, bundle, debug, minify} = runtimeEnvironment;
53
57
  const {customElements, serverData, serverDebug} = viewMetadata;
54
58
  const isAMD = format === "amd";
55
- const {bundleConfig} = resourceContext;
59
+ const {bundleConfig, unsafeEnableViewLinkCaching} = resourceContext;
56
60
  const {external = {}, exclude = []} = bundleConfig;
57
61
  const groups = isAMD ? bundleConfig.groups || {} : {};
62
+ const enableModuleGraphsCache = unsafeEnableViewLinkCaching && !isRunningLocalDev && !process.env.NOCACHE;
58
63
  const getPreloadUri = function(rawSpecifier, uriMap) {
59
64
  const {specifier} = (0, import_shared_utils.explodeSpecifier)(rawSpecifier);
60
65
  if (Object.keys(external).some((e) => specifier === e))
@@ -139,7 +144,14 @@ async function getHtmlResources(view, viewParams, resourceContext) {
139
144
  (0, import_utils.addExternalScriptNonce)(errorShimDef, nonce);
140
145
  requiredResources.push(errorShimDef);
141
146
  }
142
- const bootstrapModuleGraph = await (0, import_shared_utils.getModuleGraphs)(bootstrapSpecifier, {includeUris: true, includeLinkedDefinitions: true, depth}, moduleRegistry, defRegistry, runtimeEnvironment, runtimeParams, visitedCache);
147
+ let bootstrapModuleGraph;
148
+ const bootstrapModuleGraphCacheKey = (0, import_utils.getModuleGraphCacheKey)(bootstrapSpecifier, viewLinkCacheKey);
149
+ if (enableModuleGraphsCache && moduleGraphsCache.has(bootstrapModuleGraphCacheKey)) {
150
+ bootstrapModuleGraph = moduleGraphsCache.get(bootstrapModuleGraphCacheKey);
151
+ } else {
152
+ bootstrapModuleGraph = await (0, import_shared_utils.getModuleGraphs)(bootstrapSpecifier, {includeUris: true, includeLinkedDefinitions: true, depth}, moduleRegistry, defRegistry, runtimeEnvironment, runtimeParams, visitedCache);
153
+ enableModuleGraphsCache && moduleGraphsCache.set(bootstrapModuleGraphCacheKey, bootstrapModuleGraph);
154
+ }
143
155
  bootstrapModuleRef = {
144
156
  specifier: bootstrapModuleGraph.graphs[0].specifier,
145
157
  flatGraph: bootstrapModuleGraph,
@@ -190,7 +202,14 @@ async function getHtmlResources(view, viewParams, resourceContext) {
190
202
  await Promise.all(flattenedElements.map(async ({tagName: element, props}) => {
191
203
  const hydrateDirective = (0, import_shared_utils.getHydrateDirective)(props);
192
204
  const isSsrOnly = isSSR && !hydrateDirective;
193
- const graph = await (0, import_shared_utils.getModuleGraphs)((0, import_shared_utils.kebabCaseToModuleSpecifier)(element), {includeUris: true, includeLinkedDefinitions: true, depth}, moduleRegistry, defRegistry, runtimeEnvironment, {...runtimeParams, ssr: isSsrOnly}, visitedCache);
205
+ const moduleGraphCacheKey = (0, import_utils.getModuleGraphCacheKey)(element, viewLinkCacheKey);
206
+ let graph;
207
+ if (enableModuleGraphsCache && moduleGraphsCache.has(moduleGraphCacheKey)) {
208
+ graph = moduleGraphsCache.get(moduleGraphCacheKey);
209
+ } else {
210
+ graph = await (0, import_shared_utils.getModuleGraphs)((0, import_shared_utils.kebabCaseToModuleSpecifier)(element), {includeUris: true, includeLinkedDefinitions: true, depth}, moduleRegistry, defRegistry, runtimeEnvironment, {...runtimeParams, ssr: isSsrOnly}, visitedCache);
211
+ enableModuleGraphsCache && moduleGraphsCache.set(moduleGraphCacheKey, graph);
212
+ }
194
213
  customElementsRecords.push({elementName: element, flatGraph: graph});
195
214
  if (!isSSR || (0, import_shared_utils.getHydrateDirective)(props)) {
196
215
  const specifier = graph.graphs[0].specifier;
@@ -32,16 +32,16 @@ var import_legacy_view_bootstrap = __toModule(require("./legacy_view_bootstrap.c
32
32
  async function linkLwrResources(source, view, viewParams, cxt) {
33
33
  const {lwrResourcesId, ...resourceContext} = cxt;
34
34
  const LEGACY_LOADER = !!resourceContext.runtimeEnvironment.featureFlags.LEGACY_LOADER;
35
- const {partial, viewRecord} = await (0, import_instrumentation.getTracer)().trace({
35
+ return await (0, import_instrumentation.getTracer)().trace({
36
36
  name: import_instrumentation.ViewSpan.Generate,
37
37
  attributes: {
38
38
  legacyLoader: LEGACY_LOADER
39
39
  }
40
- }, () => {
41
- return LEGACY_LOADER ? (0, import_legacy_view_bootstrap.getHtmlResources)(view, viewParams, resourceContext) : (0, import_view_bootstrap.getHtmlResources)(view, viewParams, resourceContext);
40
+ }, async () => {
41
+ const {partial, viewRecord} = await (LEGACY_LOADER ? (0, import_legacy_view_bootstrap.getHtmlResources)(view, viewParams, resourceContext) : (0, import_view_bootstrap.getHtmlResources)(view, viewParams, resourceContext));
42
+ return {
43
+ renderedView: source.replace(lwrResourcesId, () => partial),
44
+ viewRecord
45
+ };
42
46
  });
43
- return {
44
- renderedView: source.replace(lwrResourcesId, () => partial),
45
- viewRecord
46
- };
47
47
  }
@@ -28,10 +28,14 @@ __export(exports, {
28
28
  });
29
29
  var import_diagnostics = __toModule(require("@lwrjs/diagnostics"));
30
30
  var import_shared_utils = __toModule(require("@lwrjs/shared-utils"));
31
+ var import_lru_cache = __toModule(require("lru-cache"));
31
32
  var import_identity = __toModule(require("@lwrjs/app-service/identity"));
32
33
  var import_utils = __toModule(require("../utils.cjs"));
33
34
  var import_utils2 = __toModule(require("./utils.cjs"));
35
+ var import_utils3 = __toModule(require("../utils.cjs"));
34
36
  var import_preload_utils = __toModule(require("./preload-utils.cjs"));
37
+ var moduleGraphsCache = new import_lru_cache.LRUCache({max: 500});
38
+ var isRunningLocalDev = (0, import_shared_utils.isLocalDev)();
35
39
  async function getHtmlResources(view, viewParams, resourceContext) {
36
40
  const {
37
41
  runtimeEnvironment,
@@ -39,16 +43,18 @@ async function getHtmlResources(view, viewParams, resourceContext) {
39
43
  moduleRegistry,
40
44
  moduleBundler,
41
45
  resourceRegistry,
42
- viewMetadata
46
+ viewMetadata,
47
+ viewLinkCacheKey
43
48
  } = resourceContext;
44
49
  const {format, hmrEnabled, bundle, debug, minify} = runtimeEnvironment;
45
50
  const {customElements, serverData, serverDebug} = viewMetadata;
46
51
  const defRegistry = bundle ? moduleBundler : moduleRegistry;
47
52
  const isAMD = format === "amd";
48
53
  const depth = isAMD ? {static: import_shared_utils.GraphDepth.ALL, dynamic: 1} : {static: import_shared_utils.GraphDepth.NONE, dynamic: 1};
49
- const {bundleConfig} = resourceContext;
54
+ const {bundleConfig, unsafeEnableViewLinkCaching} = resourceContext;
50
55
  const {external = {}, exclude = []} = bundleConfig;
51
56
  const groups = isAMD ? bundleConfig.groups || {} : {};
57
+ const enableModuleGraphsCache = unsafeEnableViewLinkCaching && !isRunningLocalDev && !process.env.NOCACHE;
52
58
  const getPreloadUri = function(rawSpecifier, uriMap) {
53
59
  const {specifier} = (0, import_shared_utils.explodeSpecifier)(rawSpecifier);
54
60
  if (Object.keys(external).some((e) => specifier === e))
@@ -119,7 +125,14 @@ async function getHtmlResources(view, viewParams, resourceContext) {
119
125
  (0, import_utils.addExternalScriptNonce)(errorShimDef, nonce);
120
126
  requiredResources.push(errorShimDef);
121
127
  }
122
- const bootstrapModuleGraph = await (0, import_shared_utils.getModuleGraphs)(bootstrapSpecifier, {includeUris: true, includeLinkedDefinitions: true, depth}, moduleRegistry, defRegistry, runtimeEnvironment, runtimeParams, visitedCache);
128
+ let bootstrapModuleGraph;
129
+ const bootstrapModuleGraphCacheKey = (0, import_utils3.getModuleGraphCacheKey)(bootstrapSpecifier, viewLinkCacheKey);
130
+ if (enableModuleGraphsCache && moduleGraphsCache.has(bootstrapModuleGraphCacheKey)) {
131
+ bootstrapModuleGraph = moduleGraphsCache.get(bootstrapModuleGraphCacheKey);
132
+ } else {
133
+ bootstrapModuleGraph = await (0, import_shared_utils.getModuleGraphs)(bootstrapSpecifier, {includeUris: true, includeLinkedDefinitions: true, depth}, moduleRegistry, defRegistry, runtimeEnvironment, runtimeParams, visitedCache);
134
+ enableModuleGraphsCache && moduleGraphsCache.set(bootstrapModuleGraphCacheKey, bootstrapModuleGraph);
135
+ }
123
136
  bootstrapModuleRef = {
124
137
  specifier: bootstrapModuleGraph.graphs[0].specifier,
125
138
  flatGraph: bootstrapModuleGraph,
@@ -171,7 +184,14 @@ async function getHtmlResources(view, viewParams, resourceContext) {
171
184
  for (const {tagName: element, props} of flattenedElements) {
172
185
  const hydrateDirective = (0, import_shared_utils.getHydrateDirective)(props);
173
186
  const isSsrOnly = isSSR && !hydrateDirective;
174
- const graph = await (0, import_shared_utils.getModuleGraphs)((0, import_shared_utils.kebabCaseToModuleSpecifier)(element), {includeUris: true, includeLinkedDefinitions: true, depth}, moduleRegistry, defRegistry, runtimeEnvironment, {...runtimeParams, ssr: isSsrOnly}, visitedCache);
187
+ const moduleGraphCacheKey = (0, import_utils3.getModuleGraphCacheKey)(element, viewLinkCacheKey);
188
+ let graph;
189
+ if (enableModuleGraphsCache && moduleGraphsCache.has(moduleGraphCacheKey)) {
190
+ graph = moduleGraphsCache.get(moduleGraphCacheKey);
191
+ } else {
192
+ graph = await (0, import_shared_utils.getModuleGraphs)((0, import_shared_utils.kebabCaseToModuleSpecifier)(element), {includeUris: true, includeLinkedDefinitions: true, depth}, moduleRegistry, defRegistry, runtimeEnvironment, {...runtimeParams, ssr: isSsrOnly}, visitedCache);
193
+ enableModuleGraphsCache && moduleGraphsCache.set(moduleGraphCacheKey, graph);
194
+ }
175
195
  customElementsRecords.push({elementName: element, flatGraph: graph});
176
196
  if (!isSSR || (0, import_shared_utils.getHydrateDirective)(props)) {
177
197
  const specifier = graph.graphs[0].specifier;
@@ -30,8 +30,10 @@ __export(exports, {
30
30
  generateLinkHeaders: () => generateLinkHeaders,
31
31
  generatePageContext: () => generatePageContext,
32
32
  generateViewNonce: () => generateViewNonce,
33
+ getModuleGraphCacheKey: () => getModuleGraphCacheKey,
33
34
  getModuleResource: () => getModuleResource,
34
35
  getModuleResourceByUri: () => getModuleResourceByUri,
36
+ getViewDefCacheKey: () => getViewDefCacheKey,
35
37
  getViewNonce: () => getViewNonce,
36
38
  isViewDefinitionResponse: () => isViewDefinitionResponse,
37
39
  isViewResponse: () => isViewResponse,
@@ -318,3 +320,21 @@ function generateLinkHeaders(assets, patterns) {
318
320
  return linkHeader;
319
321
  }, "");
320
322
  }
323
+ function getViewDefCacheKey(view, runtimeEnvironment, freezeAssets, runtimeParams) {
324
+ const {id, bootstrap, rootComponent, contentTemplate, layoutTemplate} = view;
325
+ return (0, import_shared_utils.getCacheKeyFromJson)({
326
+ id,
327
+ bootstrap,
328
+ rootComponent,
329
+ contentTemplate,
330
+ layoutTemplate,
331
+ freezeAssets,
332
+ locale: runtimeParams?.locale,
333
+ basePath: runtimeParams?.basePath,
334
+ debug: runtimeEnvironment.debug,
335
+ nonceEnabled: (0, import_shared_utils.getFeatureFlags)().ENABLE_NONCE
336
+ });
337
+ }
338
+ function getModuleGraphCacheKey(specifier, viewLinkCacheKey) {
339
+ return specifier + viewLinkCacheKey;
340
+ }
package/build/es/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { InflightTasks, createStringBuilder, extractMetadataFromHtml, getCacheKeyFromJson, getFeatureFlags, getSpecifier, isLocalDev, normalizeResourcePath, shortestTtl, } from '@lwrjs/shared-utils';
2
2
  import { getTracer, ViewSpan } from '@lwrjs/instrumentation';
3
- import { generateViewNonce, getViewNonce, normalizeRenderOptions, normalizeRenderedResult, reduceSourceAssetReferences, } from './utils.js';
3
+ import { generateViewNonce, getViewNonce, normalizeRenderOptions, normalizeRenderedResult, reduceSourceAssetReferences, getViewDefCacheKey, } from './utils.js';
4
4
  import { linkLwrResources } from './linkers/link-lwr-resources.js';
5
5
  // TODO: investigate perf impact W-16056356
6
6
  import { LRUCache } from 'lru-cache';
@@ -132,30 +132,23 @@ export class LwrViewRegistry {
132
132
  const { contentTemplate, rootComponent } = viewId;
133
133
  const compiledViewCacheKey = getCacheKeyFromJson({ contentTemplate, rootComponent });
134
134
  logger.debug(`[view-registry][getView] compiledViewCacheKey=${compiledViewCacheKey}`);
135
+ // For now we are only supporting ttl 0 == skipCaching.
136
+ const route = this.globalConfig.routes.find((r) => r.id === viewId.id);
137
+ const skipCaching = route?.cache?.ttl === 0;
135
138
  // use cached compiledView if available
136
- if (this.compiledViews.has(compiledViewCacheKey)) {
139
+ if (!skipCaching && this.compiledViews.has(compiledViewCacheKey)) {
137
140
  return this.compiledViews.get(compiledViewCacheKey);
138
141
  }
139
142
  const compiledView = await this.delegateGetView(viewId);
140
- this.compiledViews.set(compiledViewCacheKey, compiledView);
143
+ // cache the compiled view
144
+ if (!skipCaching) {
145
+ this.compiledViews.set(compiledViewCacheKey, compiledView);
146
+ }
141
147
  return compiledView;
142
148
  }
143
149
  hasViewDefinition(view, viewParams, runtimeEnvironment, runtimeParams, renderOptions) {
144
- const { id, bootstrap, rootComponent, contentTemplate, layoutTemplate } = view;
145
150
  const { freezeAssets, viewParamCacheKey } = normalizeRenderOptions(this.runtimeEnvironment, renderOptions);
146
- const viewDefId = getCacheKeyFromJson({
147
- id,
148
- bootstrap,
149
- rootComponent,
150
- contentTemplate,
151
- layoutTemplate,
152
- freezeAssets,
153
- locale: runtimeParams?.locale,
154
- basePath: runtimeParams?.basePath,
155
- debug: runtimeEnvironment.debug,
156
- // Add a variable on if the nonce is enabled
157
- nonceEnabled: getFeatureFlags().ENABLE_NONCE,
158
- });
151
+ const viewDefId = getViewDefCacheKey(view, runtimeEnvironment, freezeAssets, runtimeParams);
159
152
  logger.debug(`[view-registry][hasViewDefinition] viewDefId=${viewDefId}`);
160
153
  // viewParams is an unbounded object and can be very large (17MB/view for developer.salesforce.com). Allowing consumers
161
154
  // to provide a simple override avoids the excess memory / performance overhead of serializing & storing the viewParams
@@ -177,15 +170,7 @@ export class LwrViewRegistry {
177
170
  runtimeParams.requestCache = runtimeParams.requestCache ?? {};
178
171
  try {
179
172
  const { freezeAssets, viewParamCacheKey } = normalizeRenderOptions(this.runtimeEnvironment, renderOptions);
180
- const viewDefCacheKey = getCacheKeyFromJson({
181
- ...view,
182
- freezeAssets,
183
- locale: runtimeParams?.locale,
184
- basePath: runtimeParams?.basePath,
185
- debug: runtimeEnvironment.debug,
186
- // Add a variable on if the nonce is enabled
187
- nonceEnabled: getFeatureFlags().ENABLE_NONCE,
188
- });
173
+ const viewDefCacheKey = getViewDefCacheKey(view, runtimeEnvironment, freezeAssets, runtimeParams);
189
174
  logger.debug(`[view-registry][getViewDefinition] viewDefCacheKey=${viewDefCacheKey}`);
190
175
  // viewParams is an unbounded object and can be very large (17MB/view for developer.salesforce.com). Allowing consumers
191
176
  // to provide a simple override avoids the excess memory / performance overhead of serializing & storing the viewParams
@@ -214,7 +199,7 @@ export class LwrViewRegistry {
214
199
  view: view.id,
215
200
  ssr: view.bootstrap?.ssr === true,
216
201
  },
217
- }, () => this.renderView(view, updatableViewParams, runtimeEnvironment, runtimeParams, renderOptions)));
202
+ }, () => this.renderView(view, updatableViewParams, runtimeEnvironment, runtimeParams, pendingViewDefCacheKey, renderOptions)));
218
203
  // Once the view is generated add the nonce to the response so it can be cached and then added to the headers
219
204
  viewDefinition.nonce = getViewNonce(updatableViewParams);
220
205
  const route = this.globalConfig.routes.find((r) => r.id === view.id);
@@ -248,7 +233,7 @@ export class LwrViewRegistry {
248
233
  throw createSingleDiagnosticError({ description: descriptions.SERVER.UNEXPECTED_ERROR(message) }, LwrServerError);
249
234
  }
250
235
  }
251
- async renderView(view, viewParams, runtimeEnvironment, runtimeParams, renderOptions) {
236
+ async renderView(view, viewParams, runtimeEnvironment, runtimeParams, viewCacheKey, renderOptions) {
252
237
  const { id, contentTemplate, rootComponent, layoutTemplate } = view;
253
238
  const lwrResourcesId = `__LWR_RESOURCES__${Date.now()}`;
254
239
  const renderedContent = await this.render({ id, contentTemplate, rootComponent }, { ...viewParams, lwr_resources: lwrResourcesId }, runtimeParams, runtimeEnvironment);
@@ -265,6 +250,7 @@ export class LwrViewRegistry {
265
250
  // using any options provided by the content, overridden with invocation provided rendering options
266
251
  renderOptions: normalizedRenderOptions,
267
252
  contentIds: { lwrResourcesId },
253
+ viewCacheKey,
268
254
  });
269
255
  return renderedViewDef;
270
256
  }
@@ -317,6 +303,7 @@ export class LwrViewRegistry {
317
303
  // TODO: We override the importer with the original view, however
318
304
  // Layouts relative paths won't work
319
305
  importer: renderedContent.compiledView.filePath,
306
+ viewCacheKey,
320
307
  });
321
308
  return renderedViewDef;
322
309
  }
@@ -347,31 +334,50 @@ export class LwrViewRegistry {
347
334
  };
348
335
  }
349
336
  async link(renderedView, viewContext) {
350
- const { view, viewParams, runtimeEnvironment: runtimeEnv, runtimeParams, renderOptions, contentIds, importer, } = viewContext;
337
+ const { view, viewParams, runtimeEnvironment: runtimeEnv, runtimeParams, renderOptions, contentIds, importer, viewCacheKey, } = viewContext;
351
338
  const { skipMetadataCollection, freezeAssets } = renderOptions;
352
339
  const { lwrResourcesId } = contentIds;
353
340
  const { moduleRegistry, resourceRegistry, moduleBundler } = this;
354
341
  const runtimeEnvironment = { ...runtimeEnv, immutableAssets: freezeAssets };
355
342
  // normalize/extract metadata
356
343
  const { renderedView: renderedViewContent, metadata: renderedViewMetadata, compiledView: { immutable = true }, } = renderedView;
357
- const linkedMetadata = skipMetadataCollection
358
- ? renderedViewMetadata
359
- : await extractMetadataFromHtml(renderedViewContent, renderedViewMetadata, this.globalConfig);
344
+ const { linkedMetadata, stringBuilder } = await getTracer().trace({
345
+ name: ViewSpan.ParseView,
346
+ attributes: {
347
+ view: view.id,
348
+ ssr: view.bootstrap?.ssr === true,
349
+ },
350
+ }, async () => {
351
+ const linkedMetadata = skipMetadataCollection
352
+ ? renderedViewMetadata
353
+ : await extractMetadataFromHtml(renderedViewContent, renderedViewMetadata, this.globalConfig);
354
+ const stringBuilder = createStringBuilder(renderedViewContent);
355
+ return { linkedMetadata, stringBuilder };
356
+ });
360
357
  const mergedViewContext = {
361
358
  ...viewContext,
362
359
  config: this.globalConfig,
363
360
  runtimeEnvironment,
364
361
  importer: importer || renderedView.compiledView.filePath,
365
362
  };
366
- const stringBuilder = createStringBuilder(renderedViewContent);
367
363
  // Note: this is the TTL for the page NOT for the view registry cache
368
364
  let pageTtl = renderedView.cache.ttl;
365
+ let pageRedirect = renderedView.redirect;
369
366
  for (const viewTransformer of this.viewTransformers) {
370
367
  // eslint-disable-next-line no-await-in-loop
371
- const linkResults = await viewTransformer.link?.(stringBuilder, mergedViewContext, linkedMetadata);
368
+ const linkResults = await getTracer().trace({
369
+ name: ViewSpan.Transform,
370
+ attributes: {
371
+ name: viewTransformer.name,
372
+ },
373
+ }, () => viewTransformer.link?.(stringBuilder, mergedViewContext, linkedMetadata));
372
374
  // Keep track of the shortest TTL from each view transformer (e.g. lwcSsrViewTransformer)
373
375
  const ttl = linkResults && linkResults.cache?.ttl;
374
376
  pageTtl = shortestTtl(ttl || undefined, pageTtl);
377
+ // Set the redirect info, if it doesn't already exist
378
+ if (!pageRedirect) {
379
+ pageRedirect = linkResults ? linkResults.redirect : undefined;
380
+ }
375
381
  }
376
382
  const linkedAssetContent = stringBuilder.toString();
377
383
  // Link LWR related resources
@@ -387,6 +393,9 @@ export class LwrViewRegistry {
387
393
  runtimeEnvironment,
388
394
  runtimeParams,
389
395
  bundleConfig: this.globalConfig.bundleConfig,
396
+ unsafeEnableViewLinkCaching: this.globalConfig.unsafeEnableViewLinkCaching,
397
+ // Use the same cache key for the link stage
398
+ viewLinkCacheKey: viewCacheKey,
390
399
  });
391
400
  return {
392
401
  // ...viewDefinition,
@@ -397,7 +406,7 @@ export class LwrViewRegistry {
397
406
  ...viewRecord,
398
407
  },
399
408
  cache: { ttl: pageTtl },
400
- redirect: renderedView.redirect,
409
+ redirect: pageRedirect,
401
410
  };
402
411
  }
403
412
  return {
@@ -408,7 +417,7 @@ export class LwrViewRegistry {
408
417
  moduleResources: [],
409
418
  },
410
419
  cache: { ttl: pageTtl },
411
- redirect: renderedView.redirect,
420
+ redirect: pageRedirect,
412
421
  };
413
422
  }
414
423
  }
@@ -1,19 +1,7 @@
1
- import type { BundleConfig, ModuleRegistry, RenderedViewMetadata, RenderedViewRecord, ResourceRegistry, RuntimeEnvironment, RuntimeParams, View, ViewParams, ModuleBundler } from '@lwrjs/types';
2
- export interface LwrResourcesLinkedContext extends ResourceContext {
3
- lwrResourcesId: string;
4
- }
5
- interface ResourceContext {
6
- viewMetadata: RenderedViewMetadata;
7
- runtimeEnvironment: RuntimeEnvironment;
8
- runtimeParams: RuntimeParams;
9
- moduleRegistry: ModuleRegistry;
10
- moduleBundler: ModuleBundler;
11
- resourceRegistry: ResourceRegistry;
12
- bundleConfig: BundleConfig;
13
- }
1
+ import type { RenderedViewRecord, View, ViewParams } from '@lwrjs/types';
2
+ import type { ResourceContext } from './link-lwr-resources.js';
14
3
  export declare function getHtmlResources(view: View, viewParams: ViewParams, resourceContext: ResourceContext): Promise<{
15
4
  partial: string;
16
5
  viewRecord: RenderedViewRecord;
17
6
  }>;
18
- export {};
19
7
  //# sourceMappingURL=legacy_view_bootstrap.d.ts.map
@@ -1,9 +1,10 @@
1
1
  import { logger } from '@lwrjs/diagnostics';
2
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, getModuleResource, getModuleResourceByUri, getViewNonce, } from '../utils.js';
4
+ import { addExternalScriptNonce, generateHtmlTag, getModuleResource, getModuleResourceByUri, getViewNonce, getModuleGraphCacheKey, } from '../utils.js';
5
5
  import { flattenCustomElements, getBundleIntegrity, getViewBootstrapConfigurationResource, getViewHmrConfigurationResource, } from './utils.js';
6
6
  import { setPreloadModulesMeta, getPreloadModulesMeta } from './preload-utils.js';
7
+ import { LRUCache } from 'lru-cache';
7
8
  function includeIdFactory(bundleConfig) {
8
9
  return (moduleRef) => {
9
10
  // Do not bundle externals, including the loader module, which is auto bundled
@@ -15,15 +16,20 @@ function includeIdFactory(bundleConfig) {
15
16
  return true;
16
17
  };
17
18
  }
19
+ // `getModuleGraphs` is very expensive and does not need to be recomputed every time.
20
+ // Note: we use the same cache key passed in from the top level view-registry for consistency
21
+ const moduleGraphsCache = new LRUCache({ max: 500 });
22
+ const isRunningLocalDev = isLocalDev();
18
23
  export async function getHtmlResources(view, viewParams, resourceContext) {
19
- const { runtimeEnvironment, runtimeParams, moduleRegistry, moduleBundler, resourceRegistry, viewMetadata, } = resourceContext;
24
+ const { runtimeEnvironment, runtimeParams, moduleRegistry, moduleBundler, resourceRegistry, viewMetadata, viewLinkCacheKey, } = resourceContext;
20
25
  const { format, hmrEnabled, bundle, debug, minify } = runtimeEnvironment;
21
26
  const { customElements, serverData, serverDebug } = viewMetadata;
22
27
  const isAMD = format === 'amd';
23
- const { bundleConfig } = resourceContext;
28
+ const { bundleConfig, unsafeEnableViewLinkCaching } = resourceContext;
24
29
  const { external = {}, exclude = [] } = bundleConfig;
25
30
  // Bundling groups is only supported in AMD for now
26
31
  const groups = isAMD ? bundleConfig.groups || {} : {};
32
+ const enableModuleGraphsCache = unsafeEnableViewLinkCaching && !isRunningLocalDev && !process.env.NOCACHE;
27
33
  const getPreloadUri = function (rawSpecifier, uriMap) {
28
34
  const { specifier } = explodeSpecifier(rawSpecifier);
29
35
  // do not preload externals; this is the app's responsibility
@@ -151,7 +157,16 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
151
157
  // Traversal of the Bootstrap Module Graph is done to get all the URLS for discoverable static dependencies.
152
158
  // Reasoning: This is to avoid unnecessary HTTP 302's during initial application module fetching.
153
159
  // Scope: ESM currently only exposes immutable URI references, optimize for AMD formats
154
- const bootstrapModuleGraph = await getModuleGraphs(bootstrapSpecifier, { includeUris: true, includeLinkedDefinitions: true, depth }, moduleRegistry, defRegistry, runtimeEnvironment, runtimeParams, visitedCache);
160
+ let bootstrapModuleGraph;
161
+ const bootstrapModuleGraphCacheKey = getModuleGraphCacheKey(bootstrapSpecifier, viewLinkCacheKey);
162
+ if (enableModuleGraphsCache && moduleGraphsCache.has(bootstrapModuleGraphCacheKey)) {
163
+ bootstrapModuleGraph = moduleGraphsCache.get(bootstrapModuleGraphCacheKey);
164
+ }
165
+ else {
166
+ bootstrapModuleGraph = await getModuleGraphs(bootstrapSpecifier, { includeUris: true, includeLinkedDefinitions: true, depth }, moduleRegistry, defRegistry, runtimeEnvironment, runtimeParams, visitedCache);
167
+ enableModuleGraphsCache &&
168
+ moduleGraphsCache.set(bootstrapModuleGraphCacheKey, bootstrapModuleGraph);
169
+ }
155
170
  bootstrapModuleRef = {
156
171
  specifier: bootstrapModuleGraph.graphs[0].specifier,
157
172
  flatGraph: bootstrapModuleGraph,
@@ -215,7 +230,15 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
215
230
  await Promise.all(flattenedElements.map(async ({ tagName: element, props }) => {
216
231
  const hydrateDirective = getHydrateDirective(props);
217
232
  const isSsrOnly = isSSR && !hydrateDirective;
218
- const graph = await getModuleGraphs(kebabCaseToModuleSpecifier(element), { includeUris: true, includeLinkedDefinitions: true, depth }, moduleRegistry, defRegistry, runtimeEnvironment, { ...runtimeParams, ssr: isSsrOnly }, visitedCache);
233
+ const moduleGraphCacheKey = getModuleGraphCacheKey(element, viewLinkCacheKey);
234
+ let graph;
235
+ if (enableModuleGraphsCache && moduleGraphsCache.has(moduleGraphCacheKey)) {
236
+ graph = moduleGraphsCache.get(moduleGraphCacheKey);
237
+ }
238
+ else {
239
+ graph = await getModuleGraphs(kebabCaseToModuleSpecifier(element), { includeUris: true, includeLinkedDefinitions: true, depth }, moduleRegistry, defRegistry, runtimeEnvironment, { ...runtimeParams, ssr: isSsrOnly }, visitedCache);
240
+ enableModuleGraphsCache && moduleGraphsCache.set(moduleGraphCacheKey, graph);
241
+ }
219
242
  // add to the viewRecord
220
243
  customElementsRecords.push({ elementName: element, flatGraph: graph });
221
244
  // Only process custom elements that are to be hydrated or are CSR-only islands
@@ -2,7 +2,7 @@ import type { ModuleRegistry, LinkedViewDefinition, RenderedViewMetadata, Resour
2
2
  export interface LwrResourcesLinkedContext extends ResourceContext {
3
3
  lwrResourcesId: string;
4
4
  }
5
- interface ResourceContext {
5
+ export interface ResourceContext {
6
6
  viewMetadata: RenderedViewMetadata;
7
7
  runtimeEnvironment: RuntimeEnvironment;
8
8
  runtimeParams: RuntimeParams;
@@ -10,6 +10,8 @@ interface ResourceContext {
10
10
  moduleBundler: ModuleBundler;
11
11
  resourceRegistry: ResourceRegistry;
12
12
  bundleConfig: BundleConfig;
13
+ unsafeEnableViewLinkCaching: boolean;
14
+ viewLinkCacheKey: string;
13
15
  }
14
16
  type LinkedResourcesViewDefinition = Pick<LinkedViewDefinition, 'renderedView' | 'viewRecord'>;
15
17
  export declare function linkLwrResources(source: string, view: View, viewParams: ViewParams, cxt: LwrResourcesLinkedContext): Promise<LinkedResourcesViewDefinition>;
@@ -4,22 +4,22 @@ import { getHtmlResources as getLegacyHtmlResource } from './legacy_view_bootstr
4
4
  export async function linkLwrResources(source, view, viewParams, cxt) {
5
5
  const { lwrResourcesId, ...resourceContext } = cxt;
6
6
  const LEGACY_LOADER = !!resourceContext.runtimeEnvironment.featureFlags.LEGACY_LOADER;
7
- const { partial, viewRecord } = await getTracer().trace({
7
+ return await getTracer().trace({
8
8
  name: ViewSpan.Generate,
9
9
  attributes: {
10
10
  legacyLoader: LEGACY_LOADER,
11
11
  },
12
- }, () => {
13
- return LEGACY_LOADER
12
+ }, async () => {
13
+ const { partial, viewRecord } = await (LEGACY_LOADER
14
14
  ? getLegacyHtmlResource(view, viewParams, resourceContext)
15
- : getHtmlResources(view, viewParams, resourceContext);
15
+ : getHtmlResources(view, viewParams, resourceContext));
16
+ // Finally replace the token with the real resources
17
+ return {
18
+ // partial may contain regex-token-like characters such as $$,
19
+ // so we want to bypass regex replacement
20
+ renderedView: source.replace(lwrResourcesId, () => partial),
21
+ viewRecord,
22
+ };
16
23
  });
17
- // Finally replace the token with the real resources
18
- return {
19
- // partial may contain regex-token-like characters such as $$,
20
- // so we want to bypass regex replacement
21
- renderedView: source.replace(lwrResourcesId, () => partial),
22
- viewRecord,
23
- };
24
24
  }
25
25
  //# sourceMappingURL=link-lwr-resources.js.map
@@ -1,19 +1,7 @@
1
- import type { BundleConfig, ModuleRegistry, RenderedViewMetadata, RenderedViewRecord, ResourceRegistry, RuntimeEnvironment, RuntimeParams, View, ModuleBundler, ViewParams } from '@lwrjs/types';
2
- export interface LwrResourcesLinkedContext extends ResourceContext {
3
- lwrResourcesId: string;
4
- }
5
- interface ResourceContext {
6
- viewMetadata: RenderedViewMetadata;
7
- runtimeEnvironment: RuntimeEnvironment;
8
- runtimeParams: RuntimeParams;
9
- moduleRegistry: ModuleRegistry;
10
- moduleBundler: ModuleBundler;
11
- resourceRegistry: ResourceRegistry;
12
- bundleConfig: BundleConfig;
13
- }
1
+ import type { RenderedViewRecord, View, ViewParams } from '@lwrjs/types';
2
+ import type { ResourceContext } from './link-lwr-resources.js';
14
3
  export declare function getHtmlResources(view: View, viewParams: ViewParams, resourceContext: ResourceContext): Promise<{
15
4
  partial: string;
16
5
  viewRecord: RenderedViewRecord;
17
6
  }>;
18
- export {};
19
7
  //# sourceMappingURL=view_bootstrap.d.ts.map
@@ -1,11 +1,17 @@
1
1
  import { logger } from '@lwrjs/diagnostics';
2
- import { kebabCaseToModuleSpecifier, toImportMetadata, getModuleGraphs, getMappingUriPrefix, GraphDepth, explodeSpecifier, isBundler, getHydrateDirective, isGroupie, } from '@lwrjs/shared-utils';
2
+ import { kebabCaseToModuleSpecifier, toImportMetadata, getModuleGraphs, getMappingUriPrefix, GraphDepth, explodeSpecifier, isBundler, getHydrateDirective, isGroupie, isLocalDev, } from '@lwrjs/shared-utils';
3
+ import { LRUCache } from 'lru-cache';
3
4
  import { AppResourceEnum, getAppSpecifier } from '@lwrjs/app-service/identity';
4
5
  import { addExternalScriptNonce, generateHtmlTag, getModuleResourceByUri, getViewNonce } from '../utils.js';
5
6
  import { flattenCustomElements, getBundleIntegrity, getViewBootstrapConfigurationResource, getViewHmrConfigurationResource, } from './utils.js';
7
+ import { getModuleGraphCacheKey } from '../utils.js';
6
8
  import { setPreloadModulesMeta, getPreloadModulesMeta } from './preload-utils.js';
9
+ // `getModuleGraphs` is very expensive and does not need to be recomputed every time.
10
+ // Note: we use the same cache key passed in from the top level view-registry for consistency
11
+ const moduleGraphsCache = new LRUCache({ max: 500 });
12
+ const isRunningLocalDev = isLocalDev();
7
13
  export async function getHtmlResources(view, viewParams, resourceContext) {
8
- const { runtimeEnvironment, runtimeParams, moduleRegistry, moduleBundler, resourceRegistry, viewMetadata, } = resourceContext;
14
+ const { runtimeEnvironment, runtimeParams, moduleRegistry, moduleBundler, resourceRegistry, viewMetadata, viewLinkCacheKey, } = resourceContext;
9
15
  const { format, hmrEnabled, bundle, debug, minify } = runtimeEnvironment;
10
16
  const { customElements, serverData, serverDebug } = viewMetadata;
11
17
  const defRegistry = bundle ? moduleBundler : moduleRegistry;
@@ -13,10 +19,11 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
13
19
  const depth = isAMD
14
20
  ? { static: GraphDepth.ALL, dynamic: 1 }
15
21
  : { static: GraphDepth.NONE, dynamic: 1 };
16
- const { bundleConfig } = resourceContext;
22
+ const { bundleConfig, unsafeEnableViewLinkCaching } = resourceContext;
17
23
  const { external = {}, exclude = [] } = bundleConfig;
18
24
  // Bundling groups is only supported in AMD for now
19
25
  const groups = isAMD ? bundleConfig.groups || {} : {};
26
+ const enableModuleGraphsCache = unsafeEnableViewLinkCaching && !isRunningLocalDev && !process.env.NOCACHE;
20
27
  const getPreloadUri = function (rawSpecifier, uriMap) {
21
28
  const { specifier } = explodeSpecifier(rawSpecifier);
22
29
  if (Object.keys(external).some((e) => specifier === e))
@@ -118,7 +125,16 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
118
125
  // ------- Application Bootstrap module
119
126
  // Traversal of the Bootstrap Module Graph is done to get all the URLS for discoverable static dependencies.
120
127
  // Reasoning: This is to avoid unnecessary HTTP 302's during initial application module fetching.
121
- const bootstrapModuleGraph = await getModuleGraphs(bootstrapSpecifier, { includeUris: true, includeLinkedDefinitions: true, depth }, moduleRegistry, defRegistry, runtimeEnvironment, runtimeParams, visitedCache);
128
+ let bootstrapModuleGraph;
129
+ const bootstrapModuleGraphCacheKey = getModuleGraphCacheKey(bootstrapSpecifier, viewLinkCacheKey);
130
+ if (enableModuleGraphsCache && moduleGraphsCache.has(bootstrapModuleGraphCacheKey)) {
131
+ bootstrapModuleGraph = moduleGraphsCache.get(bootstrapModuleGraphCacheKey);
132
+ }
133
+ else {
134
+ bootstrapModuleGraph = await getModuleGraphs(bootstrapSpecifier, { includeUris: true, includeLinkedDefinitions: true, depth }, moduleRegistry, defRegistry, runtimeEnvironment, runtimeParams, visitedCache);
135
+ enableModuleGraphsCache &&
136
+ moduleGraphsCache.set(bootstrapModuleGraphCacheKey, bootstrapModuleGraph);
137
+ }
122
138
  bootstrapModuleRef = {
123
139
  specifier: bootstrapModuleGraph.graphs[0].specifier,
124
140
  flatGraph: bootstrapModuleGraph,
@@ -183,8 +199,16 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
183
199
  for (const { tagName: element, props } of flattenedElements) {
184
200
  const hydrateDirective = getHydrateDirective(props);
185
201
  const isSsrOnly = isSSR && !hydrateDirective;
186
- // eslint-disable-next-line no-await-in-loop
187
- const graph = await getModuleGraphs(kebabCaseToModuleSpecifier(element), { includeUris: true, includeLinkedDefinitions: true, depth }, moduleRegistry, defRegistry, runtimeEnvironment, { ...runtimeParams, ssr: isSsrOnly }, visitedCache);
202
+ const moduleGraphCacheKey = getModuleGraphCacheKey(element, viewLinkCacheKey);
203
+ let graph;
204
+ if (enableModuleGraphsCache && moduleGraphsCache.has(moduleGraphCacheKey)) {
205
+ graph = moduleGraphsCache.get(moduleGraphCacheKey);
206
+ }
207
+ else {
208
+ // eslint-disable-next-line no-await-in-loop
209
+ graph = await getModuleGraphs(kebabCaseToModuleSpecifier(element), { includeUris: true, includeLinkedDefinitions: true, depth }, moduleRegistry, defRegistry, runtimeEnvironment, { ...runtimeParams, ssr: isSsrOnly }, visitedCache);
210
+ enableModuleGraphsCache && moduleGraphsCache.set(moduleGraphCacheKey, graph);
211
+ }
188
212
  // add to the viewRecord
189
213
  customElementsRecords.push({ elementName: element, flatGraph: graph });
190
214
  // Only process custom elements that are to be hydrated or are CSR-only islands
@@ -1,4 +1,4 @@
1
- import type { AssetReference, JsonCompatible, LinkedViewDefinition, LwrErrorRoute, LwrRoute, ModuleBundler, ModuleId, ModuleJson, ModuleRegistry, NormalizedRenderingResult, PublicModuleRegistry, RenderOptions, RenderedAssetReference, RenderingResult, ResourceDefinition, RouteHandlerViewResponse, RuntimeEnvironment, RuntimeParams, ViewModuleResourceContext, ViewPageContext, ViewParams, ViewRequest, ViewResponse, ViewDefinitionResponse } from '@lwrjs/types';
1
+ import type { AssetReference, JsonCompatible, LinkedViewDefinition, LwrErrorRoute, LwrRoute, ModuleBundler, ModuleId, ModuleJson, ModuleRegistry, NormalizedRenderingResult, PublicModuleRegistry, RenderOptions, RenderedAssetReference, RenderingResult, ResourceDefinition, RouteHandlerViewResponse, RuntimeEnvironment, RuntimeParams, ViewModuleResourceContext, ViewPageContext, ViewParams, ViewRequest, ViewResponse, ViewDefinitionResponse, View } from '@lwrjs/types';
2
2
  export type HTMLResource = Partial<ResourceDefinition>;
3
3
  export declare function generateHtmlTag(definition: HTMLResource): Promise<string>;
4
4
  export declare function normalizeRenderedResult({ renderedView, metadata, options, cache, redirect, }: RenderingResult): NormalizedRenderingResult;
@@ -24,4 +24,6 @@ export declare function generateViewNonce(viewParams: ViewParams): void;
24
24
  */
25
25
  export declare function addExternalScriptNonce(def: ResourceDefinition, nonce?: string): void;
26
26
  export declare function generateLinkHeaders(assets: RenderedAssetReference[], patterns: Record<string, any>[]): string;
27
+ export declare function getViewDefCacheKey(view: View, runtimeEnvironment: RuntimeEnvironment, freezeAssets: boolean, runtimeParams?: RuntimeParams): string;
28
+ export declare function getModuleGraphCacheKey(specifier: string, viewLinkCacheKey: string): string;
27
29
  //# sourceMappingURL=utils.d.ts.map
package/build/es/utils.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { basename, extname } from 'path';
2
- import { explodeSpecifier, getMappingUriPrefix, getSpecifier, getClientBootstrapConfigurationUri, mimeLookup, DEFAULT_TITLE, streamToString, getFeatureFlags, } from '@lwrjs/shared-utils';
2
+ import { explodeSpecifier, getMappingUriPrefix, getSpecifier, getClientBootstrapConfigurationUri, mimeLookup, DEFAULT_TITLE, streamToString, getFeatureFlags, getCacheKeyFromJson, } 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) {
@@ -312,4 +312,23 @@ export function generateLinkHeaders(assets, patterns) {
312
312
  return linkHeader;
313
313
  }, '');
314
314
  }
315
+ export function getViewDefCacheKey(view, runtimeEnvironment, freezeAssets, runtimeParams) {
316
+ const { id, bootstrap, rootComponent, contentTemplate, layoutTemplate } = view;
317
+ return getCacheKeyFromJson({
318
+ id,
319
+ bootstrap,
320
+ rootComponent,
321
+ contentTemplate,
322
+ layoutTemplate,
323
+ freezeAssets,
324
+ locale: runtimeParams?.locale,
325
+ basePath: runtimeParams?.basePath,
326
+ debug: runtimeEnvironment.debug,
327
+ // Add a variable on if the nonce is enabled
328
+ nonceEnabled: getFeatureFlags().ENABLE_NONCE,
329
+ });
330
+ }
331
+ export function getModuleGraphCacheKey(specifier, viewLinkCacheKey) {
332
+ return specifier + viewLinkCacheKey;
333
+ }
315
334
  //# sourceMappingURL=utils.js.map
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "version": "0.15.0-alpha.26",
7
+ "version": "0.15.0-alpha.28",
8
8
  "homepage": "https://developer.salesforce.com/docs/platform/lwr/overview",
9
9
  "repository": {
10
10
  "type": "git",
@@ -33,17 +33,20 @@
33
33
  "build": "tsc -b"
34
34
  },
35
35
  "dependencies": {
36
- "@lwrjs/app-service": "0.15.0-alpha.26",
37
- "@lwrjs/diagnostics": "0.15.0-alpha.26",
38
- "@lwrjs/instrumentation": "0.15.0-alpha.26",
39
- "@lwrjs/shared-utils": "0.15.0-alpha.26",
36
+ "@lwrjs/app-service": "0.15.0-alpha.28",
37
+ "@lwrjs/diagnostics": "0.15.0-alpha.28",
38
+ "@lwrjs/instrumentation": "0.15.0-alpha.28",
39
+ "@lwrjs/shared-utils": "0.15.0-alpha.28",
40
40
  "lru-cache": "^10.4.3"
41
41
  },
42
42
  "devDependencies": {
43
- "@lwrjs/types": "0.15.0-alpha.26"
43
+ "@lwrjs/types": "0.15.0-alpha.28"
44
44
  },
45
45
  "engines": {
46
46
  "node": ">=18.0.0"
47
47
  },
48
- "gitHead": "b73171fb2777cf921935959d60d51750747de343"
48
+ "volta": {
49
+ "extends": "../../../package.json"
50
+ },
51
+ "gitHead": "b14e7aab8eee6aa8f1b2a05cad098f9ef65e9ef4"
49
52
  }