@angular/ssr 21.1.3 → 21.2.0-next.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@angular/ssr",
3
- "version": "21.1.3",
3
+ "version": "21.2.0-next.1",
4
4
  "description": "Angular server side rendering utilities",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -17,10 +17,10 @@
17
17
  "tslib": "^2.3.0"
18
18
  },
19
19
  "peerDependencies": {
20
- "@angular/common": "^21.0.0",
21
- "@angular/core": "^21.0.0",
22
- "@angular/platform-server": "^21.0.0",
23
- "@angular/router": "^21.0.0"
20
+ "@angular/common": "^21.0.0 || ^21.2.0-next.0",
21
+ "@angular/core": "^21.0.0 || ^21.2.0-next.0",
22
+ "@angular/platform-server": "^21.0.0 || ^21.2.0-next.0",
23
+ "@angular/router": "^21.0.0 || ^21.2.0-next.0"
24
24
  },
25
25
  "peerDependenciesMeta": {
26
26
  "@angular/platform-server": {
@@ -29,14 +29,14 @@
29
29
  },
30
30
  "devDependencies": {
31
31
  "@angular-devkit/schematics": "workspace:*",
32
- "@angular/common": "21.1.3",
33
- "@angular/compiler": "21.1.3",
34
- "@angular/core": "21.1.3",
35
- "@angular/platform-browser": "21.1.3",
36
- "@angular/platform-server": "21.1.3",
37
- "@angular/router": "21.1.3",
32
+ "@angular/common": "21.2.0-next.2",
33
+ "@angular/compiler": "21.2.0-next.2",
34
+ "@angular/core": "21.2.0-next.2",
35
+ "@angular/platform-browser": "21.2.0-next.2",
36
+ "@angular/platform-server": "21.2.0-next.2",
37
+ "@angular/router": "21.2.0-next.2",
38
38
  "@schematics/angular": "workspace:*",
39
- "beasties": "0.3.5"
39
+ "beasties": "0.4.1"
40
40
  },
41
41
  "sideEffects": false,
42
42
  "schematics": "./schematics/collection.json",
@@ -472,6 +472,30 @@ Package: postcss-media-query-parser
472
472
  License: MIT
473
473
 
474
474
 
475
+ --------------------------------------------------------------------------------
476
+ Package: postcss-safe-parser
477
+ License: MIT
478
+
479
+ The MIT License (MIT)
480
+
481
+ Copyright 2013 Andrey Sitnik <andrey@sitnik.ru>
482
+
483
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
484
+ this software and associated documentation files (the "Software"), to deal in
485
+ the Software without restriction, including without limitation the rights to
486
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
487
+ the Software, and to permit persons to whom the Software is furnished to do so,
488
+ subject to the following conditions:
489
+
490
+ The above copyright notice and this permission notice shall be included in all
491
+ copies or substantial portions of the Software.
492
+
493
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
494
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
495
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
496
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
497
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
498
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
475
499
  --------------------------------------------------------------------------------
476
500
  Package: unenv
477
501
  License: MIT
@@ -5640,6 +5640,139 @@ function requireDist () {
5640
5640
  var distExports = requireDist();
5641
5641
  var mediaParser = /*@__PURE__*/getDefaultExportFromCjs(distExports);
5642
5642
 
5643
+ var safeParser$1;
5644
+ var hasRequiredSafeParser;
5645
+
5646
+ function requireSafeParser () {
5647
+ if (hasRequiredSafeParser) return safeParser$1;
5648
+ hasRequiredSafeParser = 1;
5649
+ let Comment = requireComment();
5650
+ let Parser = requireParser();
5651
+ let tokenizer = requireTokenize();
5652
+
5653
+ class SafeParser extends Parser {
5654
+ checkMissedSemicolon() {}
5655
+
5656
+ comment(token) {
5657
+ let node = new Comment();
5658
+ this.init(node, token[2]);
5659
+ let pos =
5660
+ this.input.fromOffset(token[3]) ||
5661
+ this.input.fromOffset(this.input.css.length - 1);
5662
+ node.source.end = {
5663
+ column: pos.col,
5664
+ line: pos.line,
5665
+ offset: token[3] + 1
5666
+ };
5667
+
5668
+ let text = token[1].slice(2);
5669
+ if (text.slice(-2) === '*/') text = text.slice(0, -2);
5670
+
5671
+ if (/^\s*$/.test(text)) {
5672
+ node.text = '';
5673
+ node.raws.left = text;
5674
+ node.raws.right = '';
5675
+ } else {
5676
+ let match = text.match(/^(\s*)([^]*\S)(\s*)$/);
5677
+ node.text = match[2];
5678
+ node.raws.left = match[1];
5679
+ node.raws.right = match[3];
5680
+ }
5681
+ }
5682
+
5683
+ createTokenizer() {
5684
+ this.tokenizer = tokenizer(this.input, { ignoreErrors: true });
5685
+ }
5686
+
5687
+ decl(tokens) {
5688
+ if (tokens.length > 1 && tokens.some(i => i[0] === 'word')) {
5689
+ super.decl(tokens);
5690
+ }
5691
+ }
5692
+
5693
+ doubleColon() {}
5694
+
5695
+ endFile() {
5696
+ if (this.current.nodes && this.current.nodes.length) {
5697
+ this.current.raws.semicolon = this.semicolon;
5698
+ }
5699
+ this.current.raws.after = (this.current.raws.after || '') + this.spaces;
5700
+
5701
+ while (this.current.parent) {
5702
+ this.current = this.current.parent;
5703
+ this.current.raws.after = '';
5704
+ }
5705
+ this.root.source.end = this.getPosition(this.tokenizer.position());
5706
+ }
5707
+
5708
+ precheckMissedSemicolon(tokens) {
5709
+ let colon = this.colon(tokens);
5710
+ if (colon === false) return
5711
+
5712
+ let nextStart, prevEnd;
5713
+ for (nextStart = colon - 1; nextStart >= 0; nextStart--) {
5714
+ if (tokens[nextStart][0] === 'word') break
5715
+ }
5716
+ if (nextStart === 0 || nextStart < 0) return
5717
+
5718
+ for (prevEnd = nextStart - 1; prevEnd >= 0; prevEnd--) {
5719
+ if (tokens[prevEnd][0] !== 'space') {
5720
+ prevEnd += 1;
5721
+ break
5722
+ }
5723
+ }
5724
+
5725
+ let other = tokens.slice(nextStart);
5726
+ let spaces = tokens.slice(prevEnd, nextStart);
5727
+ tokens.splice(prevEnd, tokens.length - prevEnd);
5728
+ this.spaces = spaces.map(i => i[1]).join('');
5729
+
5730
+ this.decl(other);
5731
+ }
5732
+
5733
+ unclosedBracket() {}
5734
+
5735
+ unexpectedClose() {
5736
+ this.current.raws.after += '}';
5737
+ }
5738
+
5739
+ unknownWord(tokens) {
5740
+ this.spaces += tokens.map(i => i[1]).join('');
5741
+ }
5742
+
5743
+ unnamedAtrule(node) {
5744
+ node.name = '';
5745
+ }
5746
+ }
5747
+
5748
+ safeParser$1 = SafeParser;
5749
+ return safeParser$1;
5750
+ }
5751
+
5752
+ var safeParse;
5753
+ var hasRequiredSafeParse;
5754
+
5755
+ function requireSafeParse () {
5756
+ if (hasRequiredSafeParse) return safeParse;
5757
+ hasRequiredSafeParse = 1;
5758
+ let { Input } = requirePostcss();
5759
+
5760
+ let SafeParser = requireSafeParser();
5761
+
5762
+ safeParse = function safeParse(css, opts) {
5763
+ let input = new Input(css, opts);
5764
+
5765
+ let parser = new SafeParser(input);
5766
+ parser.parse();
5767
+
5768
+ return parser.root
5769
+ };
5770
+ return safeParse;
5771
+ }
5772
+
5773
+ var safeParseExports = requireSafeParse();
5774
+ var safeParser = /*@__PURE__*/getDefaultExportFromCjs(safeParseExports);
5775
+
5643
5776
  var boolbase$1;
5644
5777
  var hasRequiredBoolbase;
5645
5778
 
@@ -7870,11 +8003,11 @@ function getAtomFeed(feedRoot) {
7870
8003
  if (href) {
7871
8004
  entry.link = href;
7872
8005
  }
7873
- const description = fetch("summary", children) || fetch("content", children);
8006
+ const description = fetch$1("summary", children) || fetch$1("content", children);
7874
8007
  if (description) {
7875
8008
  entry.description = description;
7876
8009
  }
7877
- const pubDate = fetch("updated", children);
8010
+ const pubDate = fetch$1("updated", children);
7878
8011
  if (pubDate) {
7879
8012
  entry.pubDate = new Date(pubDate);
7880
8013
  }
@@ -7888,7 +8021,7 @@ function getAtomFeed(feedRoot) {
7888
8021
  feed.link = href;
7889
8022
  }
7890
8023
  addConditionally(feed, "description", "subtitle", childs);
7891
- const updated = fetch("updated", childs);
8024
+ const updated = fetch$1("updated", childs);
7892
8025
  if (updated) {
7893
8026
  feed.updated = new Date(updated);
7894
8027
  }
@@ -7914,7 +8047,7 @@ function getRssFeed(feedRoot) {
7914
8047
  addConditionally(entry, "title", "title", children);
7915
8048
  addConditionally(entry, "link", "link", children);
7916
8049
  addConditionally(entry, "description", "description", children);
7917
- const pubDate = fetch("pubDate", children) || fetch("dc:date", children);
8050
+ const pubDate = fetch$1("pubDate", children) || fetch$1("dc:date", children);
7918
8051
  if (pubDate)
7919
8052
  entry.pubDate = new Date(pubDate);
7920
8053
  return entry;
@@ -7923,7 +8056,7 @@ function getRssFeed(feedRoot) {
7923
8056
  addConditionally(feed, "title", "title", childs);
7924
8057
  addConditionally(feed, "link", "link", childs);
7925
8058
  addConditionally(feed, "description", "description", childs);
7926
- const updated = fetch("lastBuildDate", childs);
8059
+ const updated = fetch$1("lastBuildDate", childs);
7927
8060
  if (updated) {
7928
8061
  feed.updated = new Date(updated);
7929
8062
  }
@@ -7988,7 +8121,7 @@ function getOneElement(tagName, node) {
7988
8121
  * @param recurse Whether to recurse into child nodes.
7989
8122
  * @returns The text content of the element.
7990
8123
  */
7991
- function fetch(tagName, where, recurse = false) {
8124
+ function fetch$1(tagName, where, recurse = false) {
7992
8125
  return textContent(getElementsByTagName(tagName, where, recurse, 1)).trim();
7993
8126
  }
7994
8127
  /**
@@ -8001,7 +8134,7 @@ function fetch(tagName, where, recurse = false) {
8001
8134
  * @param recurse Whether to recurse into child nodes.
8002
8135
  */
8003
8136
  function addConditionally(obj, prop, tagName, where, recurse = false) {
8004
- const val = fetch(tagName, where, recurse);
8137
+ const val = fetch$1(tagName, where, recurse);
8005
8138
  if (val)
8006
8139
  obj[prop] = val;
8007
8140
  }
@@ -11320,7 +11453,10 @@ function parseDocument(data, options) {
11320
11453
  var picocolors_browserExports = /*@__PURE__*/ requirePicocolors_browser();
11321
11454
  var pc = /*@__PURE__*/getDefaultExportFromCjs(picocolors_browserExports);
11322
11455
 
11323
- function parseStylesheet(stylesheet) {
11456
+ function parseStylesheet(stylesheet, options) {
11457
+ if (options?.safeParser) {
11458
+ return safeParser(stylesheet);
11459
+ }
11324
11460
  return parse$2(stylesheet);
11325
11461
  }
11326
11462
  function serializeStylesheet(ast, options) {
@@ -11408,16 +11544,33 @@ function walkStyleRulesWithReverseMirror(node, node2, iterator) {
11408
11544
  const rule2 = rules2?.[index];
11409
11545
  if (hasNestedRules(rule)) {
11410
11546
  walkStyleRulesWithReverseMirror(rule, rule2, iterator);
11547
+ if ("nodes" in rule && rule.nodes?.length === 0 && isRemovableIfEmpty(rule)) {
11548
+ return false;
11549
+ }
11411
11550
  }
11412
11551
  rule._other = rule2;
11413
11552
  rule.filterSelectors = filterSelectors;
11414
11553
  return iterator(rule) !== false;
11415
11554
  }
11416
11555
  );
11556
+ if (node2.nodes) {
11557
+ node2.nodes = node2.nodes.filter((rule) => {
11558
+ if ("nodes" in rule && rule.nodes?.length === 0 && isRemovableIfEmpty(rule)) {
11559
+ return false;
11560
+ }
11561
+ return true;
11562
+ });
11563
+ }
11417
11564
  }
11418
11565
  function hasNestedRules(rule) {
11419
11566
  return "nodes" in rule && !!rule.nodes?.length && (!("name" in rule) || rule.name !== "keyframes" && rule.name !== "-webkit-keyframes") && rule.nodes.some((n) => n.type === "rule" || n.type === "atrule");
11420
11567
  }
11568
+ function isRemovableIfEmpty(rule) {
11569
+ if (!("name" in rule) || rule.type !== "atrule") {
11570
+ return false;
11571
+ }
11572
+ return rule.name === "media" || rule.name === "supports";
11573
+ }
11421
11574
  function splitFilter(a, b, predicate) {
11422
11575
  const aOut = [];
11423
11576
  const bOut = [];
@@ -11426,7 +11579,7 @@ function splitFilter(a, b, predicate) {
11426
11579
  if (predicate(item, index, a, b)) {
11427
11580
  aOut.push(item);
11428
11581
  } else {
11429
- bOut.push(item);
11582
+ bOut.push(b?.[index] ?? item);
11430
11583
  }
11431
11584
  }
11432
11585
  return [aOut, bOut];
@@ -11841,9 +11994,21 @@ class Beasties {
11841
11994
  }
11842
11995
  if (this.options.external !== false) {
11843
11996
  const externalSheets = [...document.querySelectorAll('link[rel="stylesheet"]')];
11844
- await Promise.all(
11845
- externalSheets.map((link) => this.embedLinkedStylesheet(link, document))
11846
- );
11997
+ const hasCustomEmbed = this.embedLinkedStylesheet !== Beasties.prototype.embedLinkedStylesheet;
11998
+ if (hasCustomEmbed) {
11999
+ for (const link of externalSheets) {
12000
+ await this.embedLinkedStylesheet(link, document);
12001
+ }
12002
+ } else {
12003
+ const sheets = await Promise.all(
12004
+ externalSheets.map((link) => this.fetchStylesheet(link, document))
12005
+ );
12006
+ for (const sheet of sheets) {
12007
+ if (sheet) {
12008
+ this.embedFetchedStylesheet(sheet, document);
12009
+ }
12010
+ }
12011
+ }
11847
12012
  }
11848
12013
  const styles = this.getAffectedStyleTags(document);
11849
12014
  for (const style of styles) {
@@ -11890,12 +12055,27 @@ class Beasties {
11890
12055
  async getCssAsset(href, _style) {
11891
12056
  const outputPath = this.options.path;
11892
12057
  const publicPath = this.options.publicPath;
11893
- let normalizedPath = href.replace(/^\//, "");
11894
- const pathPrefix = `${(publicPath || "").replace(/(^\/|\/$)/g, "")}/`;
11895
- if (normalizedPath.startsWith(pathPrefix)) {
12058
+ let normalizedPath = href.replace(/^\/(?!\/)|[?#].*$/g, "");
12059
+ const pathPrefix = `${(publicPath || "").replace(/(^\/(?!\/)|\/$)/g, "")}/`;
12060
+ if (normalizedPath.startsWith(pathPrefix) && !(pathPrefix === "/" && normalizedPath.startsWith("//"))) {
11896
12061
  normalizedPath = normalizedPath.substring(pathPrefix.length).replace(/^\//, "");
11897
12062
  }
11898
- if (/^https?:\/\//.test(normalizedPath) || href.startsWith("//")) {
12063
+ const isRemote = /^https?:\/\//.test(normalizedPath) || normalizedPath.startsWith("//");
12064
+ if (isRemote) {
12065
+ if (this.options.remote === true) {
12066
+ try {
12067
+ const absoluteUrl = href.startsWith("//") ? `https:${href}` : href;
12068
+ const response = await fetch(absoluteUrl);
12069
+ if (!response.ok) {
12070
+ this.logger.warn?.(`Failed to fetch ${absoluteUrl} (${response.status})`);
12071
+ return void 0;
12072
+ }
12073
+ return await response.text();
12074
+ } catch (error) {
12075
+ this.logger.warn?.(`Error fetching ${href}: ${error.message}`);
12076
+ return void 0;
12077
+ }
12078
+ }
11899
12079
  return void 0;
11900
12080
  }
11901
12081
  const filename = _pathModule.resolve(outputPath, normalizedPath);
@@ -11935,6 +12115,7 @@ class Beasties {
11935
12115
  styleSheetsIncluded.push(cssFile);
11936
12116
  const style = document.createElement("style");
11937
12117
  style.$$external = true;
12118
+ style.$$name = cssFile;
11938
12119
  return this.getCssAsset(cssFile, style).then((sheet) => [sheet, style]);
11939
12120
  })
11940
12121
  );
@@ -11946,19 +12127,27 @@ class Beasties {
11946
12127
  }
11947
12128
  }
11948
12129
  /**
11949
- * Inline the target stylesheet referred to by a <link rel="stylesheet"> (assuming it passes `options.filter`)
12130
+ * Fetch CSS content for a linked stylesheet
11950
12131
  */
11951
- async embedLinkedStylesheet(link, document) {
12132
+ async fetchStylesheet(link, document) {
11952
12133
  const href = link.getAttribute("href");
11953
- if (!href?.endsWith(".css")) {
12134
+ const pathname = href?.split("?")[0]?.split("#")[0];
12135
+ if (!pathname?.endsWith(".css")) {
11954
12136
  return void 0;
11955
12137
  }
11956
12138
  const style = document.createElement("style");
11957
12139
  style.$$external = true;
11958
12140
  const sheet = await this.getCssAsset(href, style);
11959
12141
  if (!sheet) {
11960
- return;
12142
+ return void 0;
11961
12143
  }
12144
+ return { link, href, sheet, style };
12145
+ }
12146
+ /**
12147
+ * Embed a fetched stylesheet into the document
12148
+ */
12149
+ embedFetchedStylesheet(data, document) {
12150
+ const { link, href, sheet, style } = data;
11962
12151
  style.textContent = sheet;
11963
12152
  style.$$name = href;
11964
12153
  style.$$links = [link];
@@ -12005,6 +12194,7 @@ class Beasties {
12005
12194
  } else if (preloadMode === "swap-high") {
12006
12195
  link.setAttribute("rel", "alternate stylesheet preload");
12007
12196
  link.setAttribute("title", "styles");
12197
+ link.setAttribute("as", "style");
12008
12198
  link.setAttribute("onload", `this.title='';this.rel='stylesheet'`);
12009
12199
  noscriptFallback = true;
12010
12200
  } else if (preloadMode === "swap-low") {
@@ -12036,6 +12226,15 @@ class Beasties {
12036
12226
  link.setAttribute("as", "style");
12037
12227
  }
12038
12228
  }
12229
+ /**
12230
+ * Inline the target stylesheet referred to by a <link rel="stylesheet"> (assuming it passes `options.filter`)
12231
+ */
12232
+ async embedLinkedStylesheet(link, document) {
12233
+ const sheet = await this.fetchStylesheet(link, document);
12234
+ if (sheet) {
12235
+ this.embedFetchedStylesheet(sheet, document);
12236
+ }
12237
+ }
12039
12238
  /**
12040
12239
  * Prune the source CSS files
12041
12240
  */
@@ -12077,8 +12276,8 @@ class Beasties {
12077
12276
  const before = sheet;
12078
12277
  if (!sheet)
12079
12278
  return;
12080
- const ast = parseStylesheet(sheet);
12081
- const astInverse = options.pruneSource ? parseStylesheet(sheet) : null;
12279
+ const ast = parseStylesheet(sheet, { safeParser: this.options.safeParser !== false });
12280
+ const astInverse = options.pruneSource ? parseStylesheet(sheet, { safeParser: this.options.safeParser !== false }) : null;
12082
12281
  let criticalFonts = "";
12083
12282
  const failedSelectors = [];
12084
12283
  const criticalKeyframeNames = /* @__PURE__ */ new Set();