@angular/ssr 22.2.0-next.5 → 22.2.0-next.7

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/fesm2022/ssr.mjs CHANGED
@@ -3,7 +3,7 @@ import { APP_BASE_HREF, PlatformLocation } from '@angular/common';
3
3
  import { ɵConsole as _Console, ApplicationRef, REQUEST, makeEnvironmentProviders, provideEnvironmentInitializer, inject, InjectionToken, ɵENABLE_ROOT_COMPONENT_BOOTSTRAP as _ENABLE_ROOT_COMPONENT_BOOTSTRAP, Compiler, createEnvironmentInjector, EnvironmentInjector, runInInjectionContext, ɵresetCompiledComponents as _resetCompiledComponents, REQUEST_CONTEXT, RESPONSE_INIT, LOCALE_ID } from '@angular/core';
4
4
  import { platformServer, INITIAL_CONFIG, ɵSERVER_CONTEXT as _SERVER_CONTEXT, ɵrenderInternal as _renderInternal, provideServerRendering as provideServerRendering$1 } from '@angular/platform-server';
5
5
  import { ActivatedRoute, Router, ROUTES, ɵloadChildren as _loadChildren } from '@angular/router';
6
- import Beasties from '../third_party/beasties/index.js';
6
+ import { createProcessor } from 'beasties/runtime';
7
7
 
8
8
  class ServerAssets {
9
9
  manifest;
@@ -962,197 +962,7 @@ class ServerRouter {
962
962
  }
963
963
  }
964
964
 
965
- async function sha256(data) {
966
- const encodedData = new TextEncoder().encode(data);
967
- const hashBuffer = await crypto.subtle.digest('SHA-256', encodedData);
968
- const hashParts = [];
969
- for (const h of new Uint8Array(hashBuffer)) {
970
- hashParts.push(h.toString(16).padStart(2, '0'));
971
- }
972
- return hashParts.join('');
973
- }
974
-
975
- const MEDIA_SET_HANDLER_PATTERN = /^this\.media=["'](.*)["'];?$/;
976
- const CSP_MEDIA_ATTR = 'ngCspMedia';
977
- const LINK_LOAD_SCRIPT_CONTENT = /* @__PURE__ */(() => `(() => {
978
- const CSP_MEDIA_ATTR = '${CSP_MEDIA_ATTR}';
979
- const documentElement = document.documentElement;
980
-
981
- // Listener for load events on link tags.
982
- const listener = (e) => {
983
- const target = e.target;
984
- if (
985
- !target ||
986
- target.tagName !== 'LINK' ||
987
- !target.hasAttribute(CSP_MEDIA_ATTR)
988
- ) {
989
- return;
990
- }
991
-
992
- target.media = target.getAttribute(CSP_MEDIA_ATTR);
993
- target.removeAttribute(CSP_MEDIA_ATTR);
994
-
995
- if (!document.head.querySelector(\`link[\${CSP_MEDIA_ATTR}]\`)) {
996
- documentElement.removeEventListener('load', listener);
997
- }
998
- };
999
-
1000
- documentElement.addEventListener('load', listener, true);
1001
- })();`)();
1002
- class BeastiesBase extends Beasties {}
1003
- class InlineCriticalCssProcessor extends BeastiesBase {
1004
- readFile;
1005
- outputPath;
1006
- addedCspScriptsDocuments = new WeakSet();
1007
- documentNonces = new WeakMap();
1008
- constructor(readFile, outputPath) {
1009
- super({
1010
- logger: {
1011
- warn: s => console.warn(s),
1012
- error: s => console.error(s),
1013
- info: () => {}
1014
- },
1015
- logLevel: 'warn',
1016
- path: outputPath,
1017
- publicPath: undefined,
1018
- compress: false,
1019
- pruneSource: false,
1020
- reduceInlineStyles: false,
1021
- mergeStylesheets: false,
1022
- preload: 'media',
1023
- noscriptFallback: true,
1024
- inlineFonts: true
1025
- });
1026
- this.readFile = readFile;
1027
- this.outputPath = outputPath;
1028
- }
1029
- async embedLinkedStylesheet(link, document) {
1030
- if (link.getAttribute('media') === 'print' && link.next?.name === 'noscript') {
1031
- const media = link.getAttribute('onload')?.match(MEDIA_SET_HANDLER_PATTERN);
1032
- if (media) {
1033
- link.removeAttribute('onload');
1034
- link.setAttribute('media', media[1]);
1035
- link?.next?.remove();
1036
- }
1037
- }
1038
- const returnValue = await super.embedLinkedStylesheet(link, document);
1039
- const cspNonce = this.findCspNonce(document);
1040
- if (cspNonce) {
1041
- const beastiesMedia = link.getAttribute('onload')?.match(MEDIA_SET_HANDLER_PATTERN);
1042
- if (beastiesMedia) {
1043
- link.removeAttribute('onload');
1044
- link.setAttribute(CSP_MEDIA_ATTR, beastiesMedia[1]);
1045
- this.conditionallyInsertCspLoadingScript(document, cspNonce, link);
1046
- }
1047
- document.head.children.forEach(child => {
1048
- if (child.tagName === 'style' && !child.hasAttribute('nonce')) {
1049
- child.setAttribute('nonce', cspNonce);
1050
- }
1051
- });
1052
- }
1053
- return returnValue;
1054
- }
1055
- findCspNonce(document) {
1056
- if (this.documentNonces.has(document)) {
1057
- return this.documentNonces.get(document);
1058
- }
1059
- const nonceElement = document.querySelector('[ngCspNonce], [ngcspnonce]');
1060
- const cspNonce = nonceElement?.getAttribute('ngCspNonce') || nonceElement?.getAttribute('ngcspnonce') || null;
1061
- this.documentNonces.set(document, cspNonce);
1062
- return cspNonce;
1063
- }
1064
- conditionallyInsertCspLoadingScript(document, nonce, link) {
1065
- if (this.addedCspScriptsDocuments.has(document)) {
1066
- return;
1067
- }
1068
- if (document.head.textContent.includes(LINK_LOAD_SCRIPT_CONTENT)) {
1069
- this.addedCspScriptsDocuments.add(document);
1070
- return;
1071
- }
1072
- const script = document.createElement('script');
1073
- script.setAttribute('nonce', nonce);
1074
- script.textContent = LINK_LOAD_SCRIPT_CONTENT;
1075
- document.head.insertBefore(script, link);
1076
- this.addedCspScriptsDocuments.add(document);
1077
- }
1078
- }
1079
-
1080
- class LRUCache {
1081
- capacity;
1082
- cache = new Map();
1083
- head;
1084
- tail;
1085
- constructor(capacity) {
1086
- this.capacity = capacity;
1087
- }
1088
- get(key) {
1089
- const node = this.cache.get(key);
1090
- if (node) {
1091
- this.moveToHead(node);
1092
- return node.value;
1093
- }
1094
- return undefined;
1095
- }
1096
- put(key, value) {
1097
- const cachedNode = this.cache.get(key);
1098
- if (cachedNode) {
1099
- cachedNode.value = value;
1100
- this.moveToHead(cachedNode);
1101
- return;
1102
- }
1103
- const newNode = {
1104
- key,
1105
- value,
1106
- prev: undefined,
1107
- next: undefined
1108
- };
1109
- this.cache.set(key, newNode);
1110
- this.addToHead(newNode);
1111
- if (this.cache.size > this.capacity) {
1112
- const tail = this.removeTail();
1113
- if (tail) {
1114
- this.cache.delete(tail.key);
1115
- }
1116
- }
1117
- }
1118
- addToHead(node) {
1119
- node.next = this.head;
1120
- node.prev = undefined;
1121
- if (this.head) {
1122
- this.head.prev = node;
1123
- }
1124
- this.head = node;
1125
- if (!this.tail) {
1126
- this.tail = node;
1127
- }
1128
- }
1129
- removeNode(node) {
1130
- if (node.prev) {
1131
- node.prev.next = node.next;
1132
- } else {
1133
- this.head = node.next;
1134
- }
1135
- if (node.next) {
1136
- node.next.prev = node.prev;
1137
- } else {
1138
- this.tail = node.prev;
1139
- }
1140
- }
1141
- moveToHead(node) {
1142
- this.removeNode(node);
1143
- this.addToHead(node);
1144
- }
1145
- removeTail() {
1146
- const node = this.tail;
1147
- if (node) {
1148
- this.removeNode(node);
1149
- }
1150
- return node;
1151
- }
1152
- }
1153
-
1154
965
  const WELL_KNOWN_NON_ANGULAR_URLS = new Set(['/favicon.ico', '/.well-known/appspecific/com.chrome.devtools.json']);
1155
- const MAX_INLINE_CSS_CACHE_ENTRIES = 50;
1156
966
  const SERVER_CONTEXT_VALUE = {
1157
967
  [RenderMode.Prerender]: 'ssg',
1158
968
  [RenderMode.Server]: 'ssr',
@@ -1166,20 +976,13 @@ class AngularServerApp {
1166
976
  this.options = options;
1167
977
  this.allowStaticRouteRender = this.options.allowStaticRouteRender ?? false;
1168
978
  this.hooks = options.hooks ?? new Hooks();
1169
- if (this.manifest.inlineCriticalCss) {
1170
- this.inlineCriticalCssProcessor = new InlineCriticalCssProcessor(path => {
1171
- const fileName = path.split('/').pop() ?? path;
1172
- return this.assets.getServerAsset(fileName).text();
1173
- });
1174
- }
1175
979
  }
1176
980
  manifest = getAngularAppManifest();
1177
981
  assets = new ServerAssets(this.manifest);
1178
982
  router;
1179
983
  inlineCriticalCssProcessor;
1180
984
  boostrap;
1181
- textDecoder = new TextEncoder();
1182
- criticalCssLRUCache = new LRUCache(MAX_INLINE_CSS_CACHE_ENTRIES);
985
+ textEncoder = new TextEncoder();
1183
986
  async handle(request, requestContext) {
1184
987
  const url = new URL(request.url);
1185
988
  if (WELL_KNOWN_NON_ANGULAR_URLS.has(url.pathname)) {
@@ -1315,15 +1118,15 @@ class AngularServerApp {
1315
1118
  }
1316
1119
  if (renderMode === RenderMode.Prerender) {
1317
1120
  const renderedHtml = await result.content();
1318
- const finalHtml = await this.inlineCriticalCss(renderedHtml, url);
1121
+ const finalHtml = this.inlineCriticalCss(renderedHtml);
1319
1122
  return new Response(finalHtml, responseInit);
1320
1123
  }
1321
1124
  const stream = new ReadableStream({
1322
1125
  start: async controller => {
1323
1126
  try {
1324
- const renderedHtml = await result.content();
1325
- const finalHtml = await this.inlineCriticalCssWithCache(renderedHtml, url);
1326
- controller.enqueue(finalHtml);
1127
+ let renderedHtml = await result.content();
1128
+ renderedHtml = this.inlineCriticalCss(renderedHtml);
1129
+ controller.enqueue(this.textEncoder.encode(renderedHtml));
1327
1130
  controller.close();
1328
1131
  } catch (error) {
1329
1132
  result.destroy();
@@ -1336,47 +1139,39 @@ class AngularServerApp {
1336
1139
  });
1337
1140
  return new Response(stream, responseInit);
1338
1141
  }
1339
- async inlineCriticalCss(html, url) {
1142
+ inlineCriticalCss(html) {
1340
1143
  const {
1341
- inlineCriticalCssProcessor
1342
- } = this;
1343
- if (!inlineCriticalCssProcessor) {
1144
+ criticalCssPlans,
1145
+ nonce
1146
+ } = this.manifest;
1147
+ if (!criticalCssPlans?.length) {
1344
1148
  return html;
1345
1149
  }
1346
1150
  try {
1347
- return await inlineCriticalCssProcessor.process(html);
1151
+ this.inlineCriticalCssProcessor ??= createProcessor([...criticalCssPlans], {
1152
+ preload: 'media-script',
1153
+ nonce,
1154
+ preloadFonts: true,
1155
+ inlineFonts: true,
1156
+ noscriptFallback: true,
1157
+ cache: true,
1158
+ logger: {
1159
+ warn: console.warn
1160
+ }
1161
+ }).process;
1162
+ return this.inlineCriticalCssProcessor(html);
1348
1163
  } catch (error) {
1349
- console.error(`An error occurred while inlining critical CSS for: ${url}.`, error);
1164
+ console.error('An error occurred while inlining critical CSS.', error);
1350
1165
  return html;
1351
1166
  }
1352
1167
  }
1353
- async inlineCriticalCssWithCache(html, url) {
1354
- const {
1355
- inlineCriticalCssProcessor,
1356
- criticalCssLRUCache,
1357
- textDecoder
1358
- } = this;
1359
- if (!inlineCriticalCssProcessor) {
1360
- return textDecoder.encode(html);
1361
- }
1362
- const cacheKey = url.toString();
1363
- const cached = criticalCssLRUCache.get(cacheKey);
1364
- const shaOfContentPreInlinedCss = await sha256(html);
1365
- if (cached?.shaOfContentPreInlinedCss === shaOfContentPreInlinedCss) {
1366
- return cached.contentWithCriticialCSS;
1367
- }
1368
- const processedHtml = await this.inlineCriticalCss(html, url);
1369
- const finalHtml = textDecoder.encode(processedHtml);
1370
- criticalCssLRUCache.put(cacheKey, {
1371
- shaOfContentPreInlinedCss,
1372
- contentWithCriticialCSS: finalHtml
1373
- });
1374
- return finalHtml;
1375
- }
1376
1168
  buildServerAssetPathFromRequest(request) {
1377
1169
  let {
1378
1170
  pathname: assetPath
1379
1171
  } = new URL(request.url);
1172
+ try {
1173
+ assetPath = decodeURIComponent(assetPath);
1174
+ } catch {}
1380
1175
  if (!assetPath.endsWith('/index.html')) {
1381
1176
  assetPath = joinUrlParts(assetPath, 'index.html');
1382
1177
  }
@@ -1612,5 +1407,5 @@ function createRequestHandler(handler) {
1612
1407
  return handler;
1613
1408
  }
1614
1409
 
1615
- export { AngularAppEngine, IS_DISCOVERING_ROUTES, PrerenderFallback, RenderMode, createRequestHandler, provideServerRendering, withAppShell, withRoutes, InlineCriticalCssProcessor as ɵInlineCriticalCssProcessor, destroyAngularServerApp as ɵdestroyAngularServerApp, extractRoutesAndCreateRouteTree as ɵextractRoutesAndCreateRouteTree, getOrCreateAngularServerApp as ɵgetOrCreateAngularServerApp, getRoutesFromAngularRouterConfig as ɵgetRoutesFromAngularRouterConfig, setAngularAppEngineManifest as ɵsetAngularAppEngineManifest, setAngularAppManifest as ɵsetAngularAppManifest };
1410
+ export { AngularAppEngine, IS_DISCOVERING_ROUTES, PrerenderFallback, RenderMode, createRequestHandler, provideServerRendering, withAppShell, withRoutes, destroyAngularServerApp as ɵdestroyAngularServerApp, extractRoutesAndCreateRouteTree as ɵextractRoutesAndCreateRouteTree, getOrCreateAngularServerApp as ɵgetOrCreateAngularServerApp, getRoutesFromAngularRouterConfig as ɵgetRoutesFromAngularRouterConfig, setAngularAppEngineManifest as ɵsetAngularAppEngineManifest, setAngularAppManifest as ɵsetAngularAppManifest };
1616
1411
  //# sourceMappingURL=ssr.mjs.map