@lwrjs/view-registry 0.15.0-alpha.8 → 0.15.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.
@@ -51,7 +51,12 @@ var LwrViewRegistry = class {
51
51
  this.globalData = context.globalData;
52
52
  this.appEmitter = context.appEmitter;
53
53
  const observer = context.appObserver;
54
- this.viewDefinitions = new import_lru_cache.LRUCache({max: parseInt(process.env.VIEW_CACHE_SIZE || "500", 10)});
54
+ this.viewDefinitions = new import_lru_cache.LRUCache({
55
+ max: parseInt(process.env.VIEW_CACHE_SIZE ?? "500", 10),
56
+ dispose: (_value, key) => {
57
+ import_diagnostics.logger.verbose(`View evicted from cache ${key}`);
58
+ }
59
+ });
55
60
  observer.onViewSourceChange(({payload}) => this.onViewSourceChange(payload));
56
61
  observer.onModuleDefinitionChange(({payload}) => this.onModuleDefinitionChange(payload));
57
62
  observer.onAssetSourceChange(({payload}) => this.onAssetSourceChange(payload));
@@ -140,28 +145,20 @@ var LwrViewRegistry = class {
140
145
  const {contentTemplate, rootComponent} = viewId;
141
146
  const compiledViewCacheKey = (0, import_shared_utils.getCacheKeyFromJson)({contentTemplate, rootComponent});
142
147
  import_diagnostics.logger.debug(`[view-registry][getView] compiledViewCacheKey=${compiledViewCacheKey}`);
143
- 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)) {
144
151
  return this.compiledViews.get(compiledViewCacheKey);
145
152
  }
146
153
  const compiledView = await this.delegateGetView(viewId);
147
- this.compiledViews.set(compiledViewCacheKey, compiledView);
154
+ if (!skipCaching) {
155
+ this.compiledViews.set(compiledViewCacheKey, compiledView);
156
+ }
148
157
  return compiledView;
149
158
  }
150
159
  hasViewDefinition(view, viewParams, runtimeEnvironment, runtimeParams, renderOptions) {
151
- const {id, bootstrap, rootComponent, contentTemplate, layoutTemplate} = view;
152
160
  const {freezeAssets, viewParamCacheKey} = (0, import_utils.normalizeRenderOptions)(this.runtimeEnvironment, renderOptions);
153
- const viewDefId = (0, import_shared_utils.getCacheKeyFromJson)({
154
- id,
155
- bootstrap,
156
- rootComponent,
157
- contentTemplate,
158
- layoutTemplate,
159
- freezeAssets,
160
- locale: runtimeParams?.locale,
161
- basePath: runtimeParams?.basePath,
162
- debug: runtimeEnvironment.debug,
163
- nonceEnabled: (0, import_shared_utils.getFeatureFlags)().ENABLE_NONCE
164
- });
161
+ const viewDefId = (0, import_utils.getViewDefCacheKey)(view, runtimeEnvironment, freezeAssets, runtimeParams);
165
162
  import_diagnostics.logger.debug(`[view-registry][hasViewDefinition] viewDefId=${viewDefId}`);
166
163
  const viewParamKey = viewParamCacheKey ? (0, import_shared_utils.getCacheKeyFromJson)(viewParamCacheKey) : (0, import_shared_utils.getCacheKeyFromJson)(viewParams);
167
164
  import_diagnostics.logger.debug(`[view-registry][hasViewDefinition] viewParamKey=${viewParamKey}`);
@@ -173,22 +170,16 @@ var LwrViewRegistry = class {
173
170
  }
174
171
  return false;
175
172
  }
176
- async getViewDefinition(view, viewParams, runtimeEnvironment, runtimeParams, renderOptions) {
173
+ async getViewDefinition(view, viewParams, runtimeEnvironment, runtimeParams = {requestCache: {}}, renderOptions) {
174
+ runtimeParams.requestCache = runtimeParams.requestCache ?? {};
177
175
  try {
178
176
  const {freezeAssets, viewParamCacheKey} = (0, import_utils.normalizeRenderOptions)(this.runtimeEnvironment, renderOptions);
179
- const viewDefCacheKey = (0, import_shared_utils.getCacheKeyFromJson)({
180
- ...view,
181
- freezeAssets,
182
- locale: runtimeParams?.locale,
183
- basePath: runtimeParams?.basePath,
184
- debug: runtimeEnvironment.debug,
185
- nonceEnabled: (0, import_shared_utils.getFeatureFlags)().ENABLE_NONCE
186
- });
177
+ const viewDefCacheKey = (0, import_utils.getViewDefCacheKey)(view, runtimeEnvironment, freezeAssets, runtimeParams);
187
178
  import_diagnostics.logger.debug(`[view-registry][getViewDefinition] viewDefCacheKey=${viewDefCacheKey}`);
188
179
  const viewParamKey = viewParamCacheKey ? (0, import_shared_utils.getCacheKeyFromJson)(viewParamCacheKey) : (0, import_shared_utils.getCacheKeyFromJson)(viewParams);
189
180
  import_diagnostics.logger.debug(`[view-registry][getViewDefinition] viewParamKey=${viewParamKey}`);
190
- if (this.viewDefinitions.has(viewDefCacheKey)) {
191
- const viewDefinition2 = this.viewDefinitions.get(viewDefCacheKey);
181
+ if (this.viewDefinitions.has(viewDefCacheKey) || runtimeParams.requestCache[viewDefCacheKey]) {
182
+ const viewDefinition2 = this.viewDefinitions.get(viewDefCacheKey) || runtimeParams.requestCache[viewDefCacheKey];
192
183
  if (viewDefinition2 && viewDefinition2.paramKey === viewParamKey && viewDefinition2.viewDefinition.immutable) {
193
184
  return viewDefinition2.viewDefinition;
194
185
  }
@@ -202,18 +193,19 @@ var LwrViewRegistry = class {
202
193
  view: view.id,
203
194
  ssr: view.bootstrap?.ssr === true
204
195
  }
205
- }, () => this.renderView(view, updatableViewParams, runtimeEnvironment, runtimeParams, renderOptions)));
196
+ }, () => this.renderView(view, updatableViewParams, runtimeEnvironment, runtimeParams, pendingViewDefCacheKey, renderOptions)));
206
197
  viewDefinition.nonce = (0, import_utils.getViewNonce)(updatableViewParams);
207
198
  const route = this.globalConfig.routes.find((r) => r.id === view.id);
208
199
  const maxViewCacheTtl = (0, import_shared_utils.getFeatureFlags)().MAX_VIEW_CACHE_TTL;
209
200
  const maxTtl = maxViewCacheTtl && parseInt(maxViewCacheTtl, 10);
210
201
  const leastTtl = (0, import_shared_utils.shortestTtl)(viewDefinition.cache?.ttl, route?.cache?.ttl, maxTtl);
211
202
  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});
203
+ const viewDefCacheEntry = {view, viewDefinition, paramKey: viewParamKey};
204
+ if (view.bootstrap?.includeCookiesForSSR) {
205
+ runtimeParams.requestCache[viewDefCacheKey] = viewDefCacheEntry;
206
+ } else {
207
+ this.viewDefinitions.set(viewDefCacheKey, viewDefCacheEntry, {ttl});
208
+ }
217
209
  return viewDefinition;
218
210
  } catch (err) {
219
211
  if (err instanceof import_diagnostics.DiagnosticsError) {
@@ -225,10 +217,10 @@ var LwrViewRegistry = class {
225
217
  throw (0, import_diagnostics.createSingleDiagnosticError)({description: import_diagnostics.descriptions.SERVER.UNEXPECTED_ERROR(message)}, import_diagnostics.LwrServerError);
226
218
  }
227
219
  }
228
- async renderView(view, viewParams, runtimeEnvironment, runtimeParams = {}, renderOptions) {
220
+ async renderView(view, viewParams, runtimeEnvironment, runtimeParams, viewCacheKey, renderOptions) {
229
221
  const {id, contentTemplate, rootComponent, layoutTemplate} = view;
230
222
  const lwrResourcesId = `__LWR_RESOURCES__${Date.now()}`;
231
- const renderedContent = await this.render({id, contentTemplate, rootComponent}, {...viewParams, lwr_resources: lwrResourcesId}, runtimeParams, runtimeEnvironment, renderOptions);
223
+ const renderedContent = await this.render({id, contentTemplate, rootComponent}, {...viewParams, lwr_resources: lwrResourcesId}, runtimeParams, runtimeEnvironment);
232
224
  let normalizedRenderOptions = (0, import_utils.normalizeRenderOptions)(this.runtimeEnvironment, renderedContent.options, renderOptions);
233
225
  const layout = layoutTemplate || renderedContent.compiledView.layoutTemplate;
234
226
  if (!layout) {
@@ -238,7 +230,8 @@ var LwrViewRegistry = class {
238
230
  runtimeEnvironment,
239
231
  runtimeParams,
240
232
  renderOptions: normalizedRenderOptions,
241
- contentIds: {lwrResourcesId}
233
+ contentIds: {lwrResourcesId},
234
+ viewCacheKey
242
235
  });
243
236
  return renderedViewDef2;
244
237
  }
@@ -272,9 +265,11 @@ var LwrViewRegistry = class {
272
265
  serverDebug: {
273
266
  ...renderedContent.metadata.serverDebug,
274
267
  ...renderedLayout.metadata.serverDebug
275
- }
268
+ },
269
+ serverBundles: renderedContent.metadata.serverBundles
276
270
  },
277
- cache: renderedContent.cache
271
+ cache: renderedContent.cache,
272
+ status: renderedContent.status
278
273
  }, {
279
274
  view: {...view, layoutTemplate: layoutTemplatePath},
280
275
  viewParams,
@@ -282,11 +277,12 @@ var LwrViewRegistry = class {
282
277
  runtimeParams,
283
278
  renderOptions: normalizedRenderOptions,
284
279
  contentIds: {lwrResourcesId},
285
- importer: renderedContent.compiledView.filePath
280
+ importer: renderedContent.compiledView.filePath,
281
+ viewCacheKey
286
282
  });
287
283
  return renderedViewDef;
288
284
  }
289
- async render(viewId, viewParams, runtimeParams, runtimeEnvironment, renderOptions) {
285
+ async render(viewId, viewParams, runtimeParams, runtimeEnvironment) {
290
286
  const globalContext = this.globalData;
291
287
  const {id, rootComponent, contentTemplate} = viewId;
292
288
  const compiledView = await this.getView({id, contentTemplate, rootComponent});
@@ -310,7 +306,8 @@ var LwrViewRegistry = class {
310
306
  runtimeParams,
311
307
  renderOptions,
312
308
  contentIds,
313
- importer
309
+ importer,
310
+ viewCacheKey
314
311
  } = viewContext;
315
312
  const {skipMetadataCollection, freezeAssets} = renderOptions;
316
313
  const {lwrResourcesId} = contentIds;
@@ -321,19 +318,37 @@ var LwrViewRegistry = class {
321
318
  metadata: renderedViewMetadata,
322
319
  compiledView: {immutable = true}
323
320
  } = renderedView;
324
- const linkedMetadata = skipMetadataCollection ? renderedViewMetadata : await (0, import_shared_utils.extractMetadataFromHtml)(renderedViewContent, renderedViewMetadata, this.globalConfig);
321
+ const {linkedMetadata, stringBuilder} = (0, import_instrumentation.getTracer)().trace({
322
+ name: import_instrumentation.ViewSpan.ParseView,
323
+ attributes: {
324
+ view: view.id,
325
+ ssr: view.bootstrap?.ssr === true
326
+ }
327
+ }, () => {
328
+ const linkedMetadata2 = skipMetadataCollection ? renderedViewMetadata : (0, import_shared_utils.extractMetadataFromHtml)(renderedViewContent, renderedViewMetadata, this.globalConfig);
329
+ const stringBuilder2 = (0, import_shared_utils.createStringBuilder)(renderedViewContent);
330
+ return {linkedMetadata: linkedMetadata2, stringBuilder: stringBuilder2};
331
+ });
325
332
  const mergedViewContext = {
326
333
  ...viewContext,
327
334
  config: this.globalConfig,
328
335
  runtimeEnvironment,
329
336
  importer: importer || renderedView.compiledView.filePath
330
337
  };
331
- const stringBuilder = (0, import_shared_utils.createStringBuilder)(renderedViewContent);
332
338
  let pageTtl = renderedView.cache.ttl;
339
+ let pageStatus = renderedView.status;
333
340
  for (const viewTransformer of this.viewTransformers) {
334
- const linkResults = await viewTransformer.link?.(stringBuilder, mergedViewContext, linkedMetadata);
341
+ const linkResults = await (0, import_instrumentation.getTracer)().trace({
342
+ name: import_instrumentation.ViewSpan.Transform,
343
+ attributes: {
344
+ name: viewTransformer.name
345
+ }
346
+ }, () => viewTransformer.link?.(stringBuilder, mergedViewContext, linkedMetadata));
335
347
  const ttl = linkResults && linkResults.cache?.ttl;
336
348
  pageTtl = (0, import_shared_utils.shortestTtl)(ttl || void 0, pageTtl);
349
+ if (!pageStatus && linkResults) {
350
+ pageStatus = linkResults.status;
351
+ }
337
352
  }
338
353
  const linkedAssetContent = stringBuilder.toString();
339
354
  if (linkedAssetContent.includes(lwrResourcesId)) {
@@ -345,16 +360,31 @@ var LwrViewRegistry = class {
345
360
  resourceRegistry,
346
361
  runtimeEnvironment,
347
362
  runtimeParams,
348
- bundleConfig: this.globalConfig.bundleConfig
363
+ bundleConfig: this.globalConfig.bundleConfig,
364
+ unsafeEnableViewLinkCaching: this.globalConfig.unsafeEnableViewLinkCaching,
365
+ viewLinkCacheKey: viewCacheKey
349
366
  });
367
+ if (viewRecord.resources?.length) {
368
+ linkedMetadata.assetReferences.push(...viewRecord.resources.reduce((res, asset) => {
369
+ if (!asset.src)
370
+ return res;
371
+ res.push({
372
+ url: asset.src,
373
+ relative: (0, import_shared_utils.isRelative)(asset.src)
374
+ });
375
+ return res;
376
+ }, []));
377
+ }
350
378
  return {
351
379
  renderedView: linkedView,
352
380
  immutable,
353
381
  viewRecord: {
354
382
  assetReferences: (0, import_utils.reduceSourceAssetReferences)(linkedMetadata.assetReferences),
355
- ...viewRecord
383
+ ...viewRecord,
384
+ serverBundles: linkedMetadata.serverBundles
356
385
  },
357
- cache: {ttl: pageTtl}
386
+ cache: {ttl: pageTtl},
387
+ status: pageStatus
358
388
  };
359
389
  }
360
390
  return {
@@ -362,9 +392,11 @@ var LwrViewRegistry = class {
362
392
  immutable,
363
393
  viewRecord: {
364
394
  assetReferences: (0, import_utils.reduceSourceAssetReferences)(linkedMetadata.assetReferences),
365
- moduleResources: []
395
+ moduleResources: [],
396
+ serverBundles: linkedMetadata.serverBundles
366
397
  },
367
- cache: {ttl: pageTtl}
398
+ cache: {ttl: pageTtl},
399
+ status: pageStatus
368
400
  };
369
401
  }
370
402
  };
@@ -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,17 @@ function includeIdFactory(bundleConfig) {
40
41
  return true;
41
42
  };
42
43
  }
44
+ var moduleGraphsCache = new import_lru_cache.LRUCache({
45
+ max: 500,
46
+ dispose: (_value, key) => {
47
+ if ((0, import_shared_utils.isLambdaEnv)()) {
48
+ import_diagnostics.logger.warn(`Module graph evicted from cache ${key}`);
49
+ } else {
50
+ import_diagnostics.logger.verbose(`Module graph evicted from cache ${key}`);
51
+ }
52
+ }
53
+ });
54
+ var isRunningLocalDev = (0, import_shared_utils.isLocalDev)();
43
55
  async function getHtmlResources(view, viewParams, resourceContext) {
44
56
  const {
45
57
  runtimeEnvironment,
@@ -52,9 +64,10 @@ async function getHtmlResources(view, viewParams, resourceContext) {
52
64
  const {format, hmrEnabled, bundle, debug, minify} = runtimeEnvironment;
53
65
  const {customElements, serverData, serverDebug} = viewMetadata;
54
66
  const isAMD = format === "amd";
55
- const {bundleConfig} = resourceContext;
67
+ const {bundleConfig, unsafeEnableViewLinkCaching} = resourceContext;
56
68
  const {external = {}, exclude = []} = bundleConfig;
57
69
  const groups = isAMD ? bundleConfig.groups || {} : {};
70
+ const enableModuleGraphsCache = unsafeEnableViewLinkCaching && !isRunningLocalDev && !process.env.NOCACHE;
58
71
  const getPreloadUri = function(rawSpecifier, uriMap) {
59
72
  const {specifier} = (0, import_shared_utils.explodeSpecifier)(rawSpecifier);
60
73
  if (Object.keys(external).some((e) => specifier === e))
@@ -139,7 +152,14 @@ async function getHtmlResources(view, viewParams, resourceContext) {
139
152
  (0, import_utils.addExternalScriptNonce)(errorShimDef, nonce);
140
153
  requiredResources.push(errorShimDef);
141
154
  }
142
- const bootstrapModuleGraph = await (0, import_shared_utils.getModuleGraphs)(bootstrapSpecifier, {includeUris: true, includeLinkedDefinitions: true, depth}, moduleRegistry, defRegistry, runtimeEnvironment, runtimeParams, visitedCache);
155
+ let bootstrapModuleGraph;
156
+ const bootstrapModuleGraphCacheKey = (0, import_utils.getModuleGraphCacheKey)(bootstrapSpecifier, runtimeEnvironment, runtimeParams);
157
+ if (enableModuleGraphsCache && moduleGraphsCache.has(bootstrapModuleGraphCacheKey)) {
158
+ bootstrapModuleGraph = moduleGraphsCache.get(bootstrapModuleGraphCacheKey);
159
+ } else {
160
+ bootstrapModuleGraph = await (0, import_shared_utils.getModuleGraphs)(bootstrapSpecifier, {includeUris: true, includeLinkedDefinitions: true, depth}, moduleRegistry, defRegistry, runtimeEnvironment, runtimeParams, visitedCache);
161
+ enableModuleGraphsCache && moduleGraphsCache.set(bootstrapModuleGraphCacheKey, bootstrapModuleGraph);
162
+ }
143
163
  bootstrapModuleRef = {
144
164
  specifier: bootstrapModuleGraph.graphs[0].specifier,
145
165
  flatGraph: bootstrapModuleGraph,
@@ -170,7 +190,7 @@ async function getHtmlResources(view, viewParams, resourceContext) {
170
190
  for (const staticDep of bootstrapModuleGraph.graphs[0].static) {
171
191
  const uri2 = bootstrapModuleGraph.uriMap[staticDep];
172
192
  imports[staticDep] = uri2;
173
- if (services && services.length) {
193
+ if (services?.length) {
174
194
  requiredAmdModules.push(staticDep);
175
195
  }
176
196
  }
@@ -188,7 +208,20 @@ async function getHtmlResources(view, viewParams, resourceContext) {
188
208
  }
189
209
  }
190
210
  await Promise.all(flattenedElements.map(async ({tagName: element, props}) => {
191
- const graph = await (0, import_shared_utils.getModuleGraphs)((0, import_shared_utils.kebabCaseToModuleSpecifier)(element), {includeUris: true, includeLinkedDefinitions: true, depth}, moduleRegistry, defRegistry, runtimeEnvironment, runtimeParams ? runtimeParams : {}, visitedCache);
211
+ const hydrateDirective = (0, import_shared_utils.getHydrateDirective)(props);
212
+ const isSsrOnly = isSSR && !hydrateDirective;
213
+ const elementSpecifier = (0, import_shared_utils.kebabCaseToModuleSpecifier)(element);
214
+ const moduleGraphCacheKey = (0, import_utils.getModuleGraphCacheKey)(elementSpecifier, runtimeEnvironment, {
215
+ ...runtimeParams,
216
+ ssr: isSsrOnly
217
+ });
218
+ let graph;
219
+ if (enableModuleGraphsCache && moduleGraphsCache.has(moduleGraphCacheKey)) {
220
+ graph = moduleGraphsCache.get(moduleGraphCacheKey);
221
+ } else {
222
+ graph = await (0, import_shared_utils.getModuleGraphs)(elementSpecifier, {includeUris: true, includeLinkedDefinitions: true, depth}, moduleRegistry, defRegistry, runtimeEnvironment, {...runtimeParams, ssr: isSsrOnly}, visitedCache);
223
+ enableModuleGraphsCache && moduleGraphsCache.set(moduleGraphCacheKey, graph);
224
+ }
192
225
  customElementsRecords.push({elementName: element, flatGraph: graph});
193
226
  if (!isSSR || (0, import_shared_utils.getHydrateDirective)(props)) {
194
227
  const specifier = graph.graphs[0].specifier;
@@ -231,7 +264,8 @@ async function getHtmlResources(view, viewParams, resourceContext) {
231
264
  url: viewParams?.page?.url,
232
265
  configAsSrc: view.bootstrap?.configAsSrc || false,
233
266
  mixedMode: view.bootstrap?.mixedMode || false,
234
- nonce: viewParams?.page?.nonce
267
+ nonce: viewParams?.page?.nonce,
268
+ ssr: view.bootstrap?.ssr || false
235
269
  }, {
236
270
  appId: appIdentity.appName,
237
271
  bootstrapModule: versionedSpecifier,
@@ -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
  }
@@ -63,7 +63,7 @@ function getViewBootstrapConfigurationResource(viewInfo, config, runtimeEnvironm
63
63
  })});`,
64
64
  `globalThis.LWR = {...globalThis.LWR, env: ${JSON.stringify(lwrEnv)}};`,
65
65
  `globalThis.process={...globalThis.process,env:{...globalThis.process?.env,...${JSON.stringify(nodeEnv)}}};`,
66
- `globalThis.lwcRuntimeFlags = { ENABLE_MIXED_SHADOW_MODE: ${viewInfo.mixedMode} };`,
66
+ `globalThis.lwcRuntimeFlags = { ENABLE_MIXED_SHADOW_MODE: ${viewInfo.mixedMode}, ENABLE_WIRE_SYNC_EMIT: ${viewInfo.ssr} };`,
67
67
  debug && debugMessage && `console.error(${JSON.stringify(debugMessage)});`
68
68
  ].filter(Boolean).join("\n");
69
69
  if (viewInfo.configAsSrc) {
@@ -28,10 +28,23 @@ __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({
38
+ max: 500,
39
+ dispose: (_value, key) => {
40
+ if ((0, import_shared_utils.isLambdaEnv)()) {
41
+ import_diagnostics.logger.warn(`Module graph evicted from cache ${key}`);
42
+ } else {
43
+ import_diagnostics.logger.verbose(`Module graph evicted from cache ${key}`);
44
+ }
45
+ }
46
+ });
47
+ var isRunningLocalDev = (0, import_shared_utils.isLocalDev)();
35
48
  async function getHtmlResources(view, viewParams, resourceContext) {
36
49
  const {
37
50
  runtimeEnvironment,
@@ -46,9 +59,10 @@ async function getHtmlResources(view, viewParams, resourceContext) {
46
59
  const defRegistry = bundle ? moduleBundler : moduleRegistry;
47
60
  const isAMD = format === "amd";
48
61
  const depth = isAMD ? {static: import_shared_utils.GraphDepth.ALL, dynamic: 1} : {static: import_shared_utils.GraphDepth.NONE, dynamic: 1};
49
- const {bundleConfig} = resourceContext;
62
+ const {bundleConfig, unsafeEnableViewLinkCaching} = resourceContext;
50
63
  const {external = {}, exclude = []} = bundleConfig;
51
64
  const groups = isAMD ? bundleConfig.groups || {} : {};
65
+ const enableModuleGraphsCache = unsafeEnableViewLinkCaching && !isRunningLocalDev && !process.env.NOCACHE;
52
66
  const getPreloadUri = function(rawSpecifier, uriMap) {
53
67
  const {specifier} = (0, import_shared_utils.explodeSpecifier)(rawSpecifier);
54
68
  if (Object.keys(external).some((e) => specifier === e))
@@ -119,7 +133,14 @@ async function getHtmlResources(view, viewParams, resourceContext) {
119
133
  (0, import_utils.addExternalScriptNonce)(errorShimDef, nonce);
120
134
  requiredResources.push(errorShimDef);
121
135
  }
122
- const bootstrapModuleGraph = await (0, import_shared_utils.getModuleGraphs)(bootstrapSpecifier, {includeUris: true, includeLinkedDefinitions: true, depth}, moduleRegistry, defRegistry, runtimeEnvironment, runtimeParams, visitedCache);
136
+ let bootstrapModuleGraph;
137
+ const bootstrapModuleGraphCacheKey = (0, import_utils3.getModuleGraphCacheKey)(bootstrapSpecifier, runtimeEnvironment, runtimeParams);
138
+ if (enableModuleGraphsCache && moduleGraphsCache.has(bootstrapModuleGraphCacheKey)) {
139
+ bootstrapModuleGraph = moduleGraphsCache.get(bootstrapModuleGraphCacheKey);
140
+ } else {
141
+ bootstrapModuleGraph = await (0, import_shared_utils.getModuleGraphs)(bootstrapSpecifier, {includeUris: true, includeLinkedDefinitions: true, depth}, moduleRegistry, defRegistry, runtimeEnvironment, runtimeParams, visitedCache);
142
+ enableModuleGraphsCache && moduleGraphsCache.set(bootstrapModuleGraphCacheKey, bootstrapModuleGraph);
143
+ }
123
144
  bootstrapModuleRef = {
124
145
  specifier: bootstrapModuleGraph.graphs[0].specifier,
125
146
  flatGraph: bootstrapModuleGraph,
@@ -150,7 +171,7 @@ async function getHtmlResources(view, viewParams, resourceContext) {
150
171
  for (const staticDep of bootstrapModuleGraph.graphs[0].static) {
151
172
  const uri2 = bootstrapModuleGraph.uriMap[staticDep];
152
173
  imports[staticDep] = uri2;
153
- if (services && services.length) {
174
+ if (services?.length) {
154
175
  requiredAmdModules.push(staticDep);
155
176
  }
156
177
  }
@@ -169,7 +190,20 @@ async function getHtmlResources(view, viewParams, resourceContext) {
169
190
  importMetadata = await (0, import_shared_utils.toImportMetadata)(bootstrapModuleGraph, {imports: {}, index: {}}, moduleRegistry, runtimeEnvironment, runtimeParams);
170
191
  }
171
192
  for (const {tagName: element, props} of flattenedElements) {
172
- const graph = await (0, import_shared_utils.getModuleGraphs)((0, import_shared_utils.kebabCaseToModuleSpecifier)(element), {includeUris: true, includeLinkedDefinitions: true, depth}, moduleRegistry, defRegistry, runtimeEnvironment, runtimeParams ? runtimeParams : {}, visitedCache);
193
+ const hydrateDirective = (0, import_shared_utils.getHydrateDirective)(props);
194
+ const isSsrOnly = isSSR && !hydrateDirective;
195
+ const elementSpecifier = (0, import_shared_utils.kebabCaseToModuleSpecifier)(element);
196
+ const moduleGraphCacheKey = (0, import_utils3.getModuleGraphCacheKey)(elementSpecifier, runtimeEnvironment, {
197
+ ...runtimeParams,
198
+ ssr: isSsrOnly
199
+ });
200
+ let graph;
201
+ if (enableModuleGraphsCache && moduleGraphsCache.has(moduleGraphCacheKey)) {
202
+ graph = moduleGraphsCache.get(moduleGraphCacheKey);
203
+ } else {
204
+ graph = await (0, import_shared_utils.getModuleGraphs)(elementSpecifier, {includeUris: true, includeLinkedDefinitions: true, depth}, moduleRegistry, defRegistry, runtimeEnvironment, {...runtimeParams, ssr: isSsrOnly}, visitedCache);
205
+ enableModuleGraphsCache && moduleGraphsCache.set(moduleGraphCacheKey, graph);
206
+ }
173
207
  customElementsRecords.push({elementName: element, flatGraph: graph});
174
208
  if (!isSSR || (0, import_shared_utils.getHydrateDirective)(props)) {
175
209
  const specifier = graph.graphs[0].specifier;
@@ -206,7 +240,8 @@ async function getHtmlResources(view, viewParams, resourceContext) {
206
240
  url: viewParams?.page?.url,
207
241
  configAsSrc: view.bootstrap?.configAsSrc || false,
208
242
  mixedMode: view.bootstrap?.mixedMode || false,
209
- nonce: viewParams?.page?.nonce
243
+ nonce: viewParams?.page?.nonce,
244
+ ssr: view.bootstrap?.ssr || false
210
245
  }, {
211
246
  appId: appIdentity.appName,
212
247
  bootstrapModule: versionedSpecifier,
@@ -27,11 +27,15 @@ __export(exports, {
27
27
  addExternalScriptNonce: () => addExternalScriptNonce,
28
28
  createJsonModule: () => createJsonModule,
29
29
  generateHtmlTag: () => generateHtmlTag,
30
+ generateLinkHeaders: () => generateLinkHeaders,
30
31
  generatePageContext: () => generatePageContext,
31
32
  generateViewNonce: () => generateViewNonce,
33
+ getModuleGraphCacheKey: () => getModuleGraphCacheKey,
32
34
  getModuleResource: () => getModuleResource,
33
35
  getModuleResourceByUri: () => getModuleResourceByUri,
36
+ getViewDefCacheKey: () => getViewDefCacheKey,
34
37
  getViewNonce: () => getViewNonce,
38
+ isViewDefinitionResponse: () => isViewDefinitionResponse,
35
39
  isViewResponse: () => isViewResponse,
36
40
  normalizeRenderOptions: () => normalizeRenderOptions,
37
41
  normalizeRenderedResult: () => normalizeRenderedResult,
@@ -108,20 +112,23 @@ function normalizeRenderedResult({
108
112
  renderedView,
109
113
  metadata,
110
114
  options,
111
- cache
115
+ cache,
116
+ status
112
117
  }) {
113
118
  return {
114
119
  renderedView,
115
120
  metadata: {
116
121
  customElements: metadata ? metadata.customElements : [],
117
122
  assetReferences: metadata ? metadata.assetReferences : [],
123
+ serverBundles: metadata ? metadata.serverBundles : new Set(),
118
124
  serverData: metadata ? metadata.serverData : {},
119
125
  serverDebug: metadata ? metadata.serverDebug : {}
120
126
  },
121
127
  options: {
122
128
  skipMetadataCollection: options ? options.skipMetadataCollection : false
123
129
  },
124
- cache: cache || {}
130
+ cache: cache || {},
131
+ status
125
132
  };
126
133
  }
127
134
  function reduceSourceAssetReferences(assets) {
@@ -149,11 +156,16 @@ function generatePageContext({requestPath: url}, {id, contentTemplate, propertie
149
156
  const title = properties?.title || getTitleFromFilePath(contentTemplate);
150
157
  const locale = runtimeParams.locale;
151
158
  const basePath = runtimeParams.basePath;
152
- return {basePath, id, locale, title, url};
159
+ const assetBasePath = runtimeParams.assetBasePath;
160
+ const uiBasePath = runtimeParams.uiBasePath;
161
+ return {assetBasePath, basePath, id, locale, title, url, uiBasePath};
153
162
  }
154
163
  function isViewResponse(response) {
155
164
  return response.body !== void 0;
156
165
  }
166
+ function isViewDefinitionResponse(response) {
167
+ return response.view !== void 0;
168
+ }
157
169
  async function toJsonFormat(viewRequest, viewDefinition, route, runtimeEnvironment, runtimeParams, moduleRegistry) {
158
170
  const {viewRecord} = viewDefinition;
159
171
  const {bootstrap, id: appName} = route;
@@ -291,3 +303,61 @@ function addExternalScriptNonce(def, nonce) {
291
303
  def.nonce = nonce;
292
304
  }
293
305
  }
306
+ function generateLinkHeaders(assets, patterns) {
307
+ const assetConfig = {};
308
+ for (const assetRef of assets) {
309
+ const path = assetRef.override?.uri || assetRef.url;
310
+ let matched = null;
311
+ for (const pattern of patterns) {
312
+ const {match} = pattern;
313
+ const matchPatterns = Array.isArray(match) ? match : [match];
314
+ for (const filePattern of matchPatterns) {
315
+ const regex = new RegExp(filePattern);
316
+ if (regex.test(path)) {
317
+ matched = pattern.attributes;
318
+ break;
319
+ }
320
+ }
321
+ if (matched)
322
+ break;
323
+ }
324
+ if (matched) {
325
+ assetConfig[path] = matched;
326
+ }
327
+ }
328
+ return Object.keys(assetConfig).reduce((linkHeader, path) => {
329
+ linkHeader = `${linkHeader ? linkHeader + ", " : ""}<${path}>`;
330
+ const properties = assetConfig[path];
331
+ for (const prop in properties) {
332
+ linkHeader += properties[prop] !== "" ? `; ${prop}=${properties[prop]}` : `; ${prop}`;
333
+ }
334
+ return linkHeader;
335
+ }, "");
336
+ }
337
+ function getViewDefCacheKey(view, runtimeEnvironment, freezeAssets, runtimeParams) {
338
+ const {id, bootstrap, rootComponent, contentTemplate, layoutTemplate} = view;
339
+ return (0, import_shared_utils.getCacheKeyFromJson)({
340
+ id,
341
+ bootstrap,
342
+ rootComponent,
343
+ contentTemplate,
344
+ layoutTemplate,
345
+ freezeAssets,
346
+ locale: runtimeParams?.locale,
347
+ basePath: runtimeParams?.basePath,
348
+ debug: runtimeEnvironment.debug,
349
+ nonceEnabled: (0, import_shared_utils.getFeatureFlags)().ENABLE_NONCE
350
+ });
351
+ }
352
+ function getModuleGraphCacheKey(specifier, runtimeEnvironment, runtimeParams) {
353
+ const updatedParams = {
354
+ ...runtimeParams
355
+ };
356
+ delete updatedParams.url;
357
+ delete updatedParams.query;
358
+ return (0, import_shared_utils.getCacheKeyFromJson)({
359
+ specifier,
360
+ updatedParams,
361
+ runtimeEnvironment
362
+ });
363
+ }
@@ -46,9 +46,11 @@ var LwrViewHandler = class {
46
46
  }
47
47
  const {view, viewParams, renderOptions} = normalizeViewProperties(viewRequest, response, route, this.globalConfig, runtimeParams);
48
48
  const viewDefinition2 = await this.viewRegistry.getViewDefinition(view, viewParams, runtimeEnvironment, runtimeParams, renderOptions);
49
+ const link2 = !!route?.bootstrap?.preloadResources?.patterns?.length && (0, import_utils.generateLinkHeaders)(viewDefinition2.viewRecord.assetReferences || [], route?.bootstrap?.preloadResources?.patterns || []);
49
50
  return {
50
51
  ...response,
51
52
  body: viewDefinition2.renderedView,
53
+ ...!!link2 && {headers: {link: link2}},
52
54
  metadata: {
53
55
  viewDefinition: viewDefinition2
54
56
  },
@@ -56,12 +58,14 @@ var LwrViewHandler = class {
56
58
  };
57
59
  }
58
60
  const viewDefinition = await this.getDefaultRouteViewDefinition(viewRequest, route, runtimeEnvironment, runtimeParams);
61
+ const link = !!route?.bootstrap?.preloadResources?.patterns?.length && (0, import_utils.generateLinkHeaders)(viewDefinition.viewRecord.assetReferences || [], route.bootstrap?.preloadResources?.patterns || []);
59
62
  return {
60
63
  body: viewDefinition.renderedView,
61
64
  metadata: {
62
65
  viewDefinition
63
66
  },
64
- cache: viewDefinition.cache
67
+ cache: viewDefinition.cache,
68
+ ...!!link && {headers: {link}}
65
69
  };
66
70
  }
67
71
  async getViewJson(viewRequest, route, runtimeEnvironment, runtimeParams = {}) {
@@ -153,6 +157,22 @@ var LwrViewHandler = class {
153
157
  if (response?.locale) {
154
158
  runtimeParams.locale = response.locale;
155
159
  }
160
+ const preloadResources = route?.bootstrap?.preloadResources?.patterns;
161
+ if (preloadResources && preloadResources.length) {
162
+ let viewDefintionMeta = response.metadata?.viewDefinition;
163
+ if (!viewDefintionMeta && (0, import_utils.isViewDefinitionResponse)(response)) {
164
+ viewDefintionMeta = (await viewApi.getViewResponse(response.view, response.viewParams, response.renderOptions)).metadata?.viewDefinition;
165
+ }
166
+ if (viewDefintionMeta) {
167
+ const link = (0, import_utils.generateLinkHeaders)(viewDefintionMeta?.viewRecord.assetReferences || [], preloadResources);
168
+ if (link) {
169
+ response.headers = {
170
+ ...response.headers,
171
+ link
172
+ };
173
+ }
174
+ }
175
+ }
156
176
  return response;
157
177
  }
158
178
  getBoundApi(viewRequest, route, runtimeEnvironment, runtimeParams) {