@modern-js/runtime 2.71.0 → 2.71.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.
@@ -91,7 +91,7 @@ async function buildShellBeforeTemplate(beforeAppTemplate, options) {
91
91
  }
92
92
  const cssChunks = matchedRouteManifests ? matchedRouteManifests === null || matchedRouteManifests === void 0 ? void 0 : matchedRouteManifests.reduce((chunks, routeManifest2) => {
93
93
  const { referenceCssAssets = [] } = routeManifest2;
94
- const _cssChunks = referenceCssAssets.filter((asset) => (asset === null || asset === void 0 ? void 0 : asset.endsWith(".css")) && !template.includes(asset));
94
+ const _cssChunks = referenceCssAssets.filter((asset) => (asset === null || asset === void 0 ? void 0 : asset.endsWith(".css")) && !(0, import_utils.hasStylesheetLink)(template, asset));
95
95
  return [
96
96
  ...chunks,
97
97
  ..._cssChunks
@@ -145,15 +145,9 @@ class LoadableCollector {
145
145
  const { template, chunkSet, config, entryName } = this.options;
146
146
  const { enableInlineStyles } = config;
147
147
  const atrributes = (0, import_utils.attributesToString)(this.generateAttributes());
148
- const linkRegExp = /<link .*?href="([^"]+)".*?>/g;
149
- const matchs = template.matchAll(linkRegExp);
150
- const existedLinks = [];
151
- for (const match of matchs) {
152
- existedLinks.push(match[1]);
153
- }
154
148
  const css = await Promise.all(chunks.filter((chunk) => {
155
149
  var _this_existsAssets;
156
- return !existedLinks.includes(chunk.url) && !((_this_existsAssets = this.existsAssets) === null || _this_existsAssets === void 0 ? void 0 : _this_existsAssets.includes(chunk.path));
150
+ return !(0, import_utils.hasStylesheetLink)(template, chunk.url) && !((_this_existsAssets = this.existsAssets) === null || _this_existsAssets === void 0 ? void 0 : _this_existsAssets.includes(chunk.path));
157
151
  }).map(async (chunk) => {
158
152
  const link = `<link${atrributes} href="${chunk.url}" rel="stylesheet" />`;
159
153
  if ((0, import_utils.checkIsNode)() && checkIsInline(chunk, enableInlineStyles)) {
@@ -22,6 +22,7 @@ __export(utils_exports, {
22
22
  checkIsNode: () => checkIsNode,
23
23
  getSSRConfigByEntry: () => getSSRConfigByEntry,
24
24
  getSSRMode: () => getSSRMode,
25
+ hasStylesheetLink: () => hasStylesheetLink,
25
26
  safeReplace: () => safeReplace,
26
27
  serializeErrors: () => serializeErrors
27
28
  });
@@ -75,12 +76,37 @@ function getSSRMode(ssrConfig) {
75
76
  }
76
77
  return (ssrConfig === null || ssrConfig === void 0 ? void 0 : ssrConfig.mode) === "stream" ? "stream" : "string";
77
78
  }
79
+ const getLinkAttributes = (linkTag) => {
80
+ const attributes = /* @__PURE__ */ new Map();
81
+ const attributeRegExp = /([^\s"'<>/=]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
82
+ let match;
83
+ while (match = attributeRegExp.exec(linkTag)) {
84
+ const [, name, doubleQuotedValue, singleQuotedValue, unquotedValue] = match;
85
+ if (name.toLowerCase() === "link") {
86
+ continue;
87
+ }
88
+ var _ref, _ref1;
89
+ attributes.set(name.toLowerCase(), (_ref1 = (_ref = doubleQuotedValue !== null && doubleQuotedValue !== void 0 ? doubleQuotedValue : singleQuotedValue) !== null && _ref !== void 0 ? _ref : unquotedValue) !== null && _ref1 !== void 0 ? _ref1 : "");
90
+ }
91
+ return attributes;
92
+ };
93
+ const hasStylesheetLink = (template, href) => {
94
+ var _template_match;
95
+ const linkTags = (_template_match = template.match(/<link\b[^>]*>/gi)) !== null && _template_match !== void 0 ? _template_match : [];
96
+ return linkTags.some((linkTag) => {
97
+ const attributes = getLinkAttributes(linkTag);
98
+ const linkHref = attributes.get("href");
99
+ const rel = attributes.get("rel");
100
+ return linkHref === href && (rel === null || rel === void 0 ? void 0 : rel.split(/\s+/).some((relToken) => relToken.toLowerCase() === "stylesheet"));
101
+ });
102
+ };
78
103
  // Annotate the CommonJS export names for ESM import in node:
79
104
  0 && (module.exports = {
80
105
  attributesToString,
81
106
  checkIsNode,
82
107
  getSSRConfigByEntry,
83
108
  getSSRMode,
109
+ hasStylesheetLink,
84
110
  safeReplace,
85
111
  serializeErrors
86
112
  });
@@ -6,7 +6,7 @@ import { matchRoutes } from "@modern-js/runtime-utils/router";
6
6
  import { CHUNK_CSS_PLACEHOLDER } from "../constants";
7
7
  import { createReplaceHelemt } from "../helmet";
8
8
  import { buildHtml } from "../shared";
9
- import { checkIsNode, safeReplace } from "../utils";
9
+ import { checkIsNode, hasStylesheetLink, safeReplace } from "../utils";
10
10
  var readAsset = function() {
11
11
  var _ref = _async_to_generator(function(chunk) {
12
12
  var fs, path, filepath;
@@ -95,7 +95,7 @@ function _buildShellBeforeTemplate() {
95
95
  cssChunks = matchedRouteManifests ? matchedRouteManifests === null || matchedRouteManifests === void 0 ? void 0 : matchedRouteManifests.reduce(function(chunks, routeManifest2) {
96
96
  var _routeManifest_referenceCssAssets = routeManifest2.referenceCssAssets, referenceCssAssets = _routeManifest_referenceCssAssets === void 0 ? [] : _routeManifest_referenceCssAssets;
97
97
  var _cssChunks = referenceCssAssets.filter(function(asset) {
98
- return (asset === null || asset === void 0 ? void 0 : asset.endsWith(".css")) && !template.includes(asset);
98
+ return (asset === null || asset === void 0 ? void 0 : asset.endsWith(".css")) && !hasStylesheetLink(template, asset);
99
99
  });
100
100
  return _to_consumable_array(chunks).concat(_to_consumable_array(_cssChunks));
101
101
  }, []) : [];
@@ -6,7 +6,7 @@ import { _ as _object_spread } from "@swc/helpers/_/_object_spread";
6
6
  import { _ as _to_consumable_array } from "@swc/helpers/_/_to_consumable_array";
7
7
  import { _ as _ts_generator } from "@swc/helpers/_/_ts_generator";
8
8
  import { ChunkExtractor } from "@loadable/server";
9
- import { attributesToString, checkIsNode } from "../utils";
9
+ import { attributesToString, checkIsNode, hasStylesheetLink } from "../utils";
10
10
  var extname = function(uri) {
11
11
  if (typeof uri !== "string" || !uri.includes(".")) {
12
12
  return "";
@@ -232,41 +232,18 @@ var LoadableCollector = /* @__PURE__ */ function() {
232
232
  _proto.emitStyleAssets = function emitStyleAssets(chunks) {
233
233
  var _this = this;
234
234
  return _async_to_generator(function() {
235
- var _this_options, template, chunkSet, config, entryName, enableInlineStyles, atrributes, linkRegExp, matchs, existedLinks, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, match, css;
235
+ var _this_options, template, chunkSet, config, entryName, enableInlineStyles, atrributes, css;
236
236
  return _ts_generator(this, function(_state) {
237
237
  switch (_state.label) {
238
238
  case 0:
239
239
  _this_options = _this.options, template = _this_options.template, chunkSet = _this_options.chunkSet, config = _this_options.config, entryName = _this_options.entryName;
240
240
  enableInlineStyles = config.enableInlineStyles;
241
241
  atrributes = attributesToString(_this.generateAttributes());
242
- linkRegExp = /<link .*?href="([^"]+)".*?>/g;
243
- matchs = template.matchAll(linkRegExp);
244
- existedLinks = [];
245
- _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = void 0;
246
- try {
247
- for (_iterator = matchs[Symbol.iterator](); !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
248
- match = _step.value;
249
- existedLinks.push(match[1]);
250
- }
251
- } catch (err) {
252
- _didIteratorError = true;
253
- _iteratorError = err;
254
- } finally {
255
- try {
256
- if (!_iteratorNormalCompletion && _iterator.return != null) {
257
- _iterator.return();
258
- }
259
- } finally {
260
- if (_didIteratorError) {
261
- throw _iteratorError;
262
- }
263
- }
264
- }
265
242
  return [
266
243
  4,
267
244
  Promise.all(chunks.filter(function(chunk) {
268
245
  var _this_existsAssets;
269
- return !existedLinks.includes(chunk.url) && !((_this_existsAssets = _this.existsAssets) === null || _this_existsAssets === void 0 ? void 0 : _this_existsAssets.includes(chunk.path));
246
+ return !hasStylesheetLink(template, chunk.url) && !((_this_existsAssets = _this.existsAssets) === null || _this_existsAssets === void 0 ? void 0 : _this_existsAssets.includes(chunk.path));
270
247
  }).map(function() {
271
248
  var _ref = _async_to_generator(function(chunk) {
272
249
  var link;
@@ -70,11 +70,38 @@ function getSSRMode(ssrConfig) {
70
70
  }
71
71
  return (ssrConfig === null || ssrConfig === void 0 ? void 0 : ssrConfig.mode) === "stream" ? "stream" : "string";
72
72
  }
73
+ var getLinkAttributes = function(linkTag) {
74
+ var attributes = /* @__PURE__ */ new Map();
75
+ var attributeRegExp = /([^\s"'<>/=]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
76
+ var match;
77
+ while (match = attributeRegExp.exec(linkTag)) {
78
+ var _match = _sliced_to_array(match, 5), name = _match[1], doubleQuotedValue = _match[2], singleQuotedValue = _match[3], unquotedValue = _match[4];
79
+ if (name.toLowerCase() === "link") {
80
+ continue;
81
+ }
82
+ var _ref, _ref1;
83
+ attributes.set(name.toLowerCase(), (_ref1 = (_ref = doubleQuotedValue !== null && doubleQuotedValue !== void 0 ? doubleQuotedValue : singleQuotedValue) !== null && _ref !== void 0 ? _ref : unquotedValue) !== null && _ref1 !== void 0 ? _ref1 : "");
84
+ }
85
+ return attributes;
86
+ };
87
+ var hasStylesheetLink = function(template, href) {
88
+ var _template_match;
89
+ var linkTags = (_template_match = template.match(/<link\b[^>]*>/gi)) !== null && _template_match !== void 0 ? _template_match : [];
90
+ return linkTags.some(function(linkTag) {
91
+ var attributes = getLinkAttributes(linkTag);
92
+ var linkHref = attributes.get("href");
93
+ var rel = attributes.get("rel");
94
+ return linkHref === href && (rel === null || rel === void 0 ? void 0 : rel.split(/\s+/).some(function(relToken) {
95
+ return relToken.toLowerCase() === "stylesheet";
96
+ }));
97
+ });
98
+ };
73
99
  export {
74
100
  attributesToString,
75
101
  checkIsNode,
76
102
  getSSRConfigByEntry,
77
103
  getSSRMode,
104
+ hasStylesheetLink,
78
105
  safeReplace,
79
106
  serializeErrors
80
107
  };
@@ -2,7 +2,7 @@ import { matchRoutes } from "@modern-js/runtime-utils/router";
2
2
  import { CHUNK_CSS_PLACEHOLDER } from "../constants";
3
3
  import { createReplaceHelemt } from "../helmet";
4
4
  import { buildHtml } from "../shared";
5
- import { checkIsNode, safeReplace } from "../utils";
5
+ import { checkIsNode, hasStylesheetLink, safeReplace } from "../utils";
6
6
  const readAsset = async (chunk) => {
7
7
  const fs = await import("fs/promises");
8
8
  const path = await import("path");
@@ -58,7 +58,7 @@ async function buildShellBeforeTemplate(beforeAppTemplate, options) {
58
58
  }
59
59
  const cssChunks = matchedRouteManifests ? matchedRouteManifests === null || matchedRouteManifests === void 0 ? void 0 : matchedRouteManifests.reduce((chunks, routeManifest2) => {
60
60
  const { referenceCssAssets = [] } = routeManifest2;
61
- const _cssChunks = referenceCssAssets.filter((asset) => (asset === null || asset === void 0 ? void 0 : asset.endsWith(".css")) && !template.includes(asset));
61
+ const _cssChunks = referenceCssAssets.filter((asset) => (asset === null || asset === void 0 ? void 0 : asset.endsWith(".css")) && !hasStylesheetLink(template, asset));
62
62
  return [
63
63
  ...chunks,
64
64
  ..._cssChunks
@@ -1,5 +1,5 @@
1
1
  import { ChunkExtractor } from "@loadable/server";
2
- import { attributesToString, checkIsNode } from "../utils";
2
+ import { attributesToString, checkIsNode, hasStylesheetLink } from "../utils";
3
3
  const extname = (uri) => {
4
4
  if (typeof uri !== "string" || !uri.includes(".")) {
5
5
  return "";
@@ -112,15 +112,9 @@ class LoadableCollector {
112
112
  const { template, chunkSet, config, entryName } = this.options;
113
113
  const { enableInlineStyles } = config;
114
114
  const atrributes = attributesToString(this.generateAttributes());
115
- const linkRegExp = /<link .*?href="([^"]+)".*?>/g;
116
- const matchs = template.matchAll(linkRegExp);
117
- const existedLinks = [];
118
- for (const match of matchs) {
119
- existedLinks.push(match[1]);
120
- }
121
115
  const css = await Promise.all(chunks.filter((chunk) => {
122
116
  var _this_existsAssets;
123
- return !existedLinks.includes(chunk.url) && !((_this_existsAssets = this.existsAssets) === null || _this_existsAssets === void 0 ? void 0 : _this_existsAssets.includes(chunk.path));
117
+ return !hasStylesheetLink(template, chunk.url) && !((_this_existsAssets = this.existsAssets) === null || _this_existsAssets === void 0 ? void 0 : _this_existsAssets.includes(chunk.path));
124
118
  }).map(async (chunk) => {
125
119
  const link = `<link${atrributes} href="${chunk.url}" rel="stylesheet" />`;
126
120
  if (checkIsNode() && checkIsInline(chunk, enableInlineStyles)) {
@@ -47,11 +47,36 @@ function getSSRMode(ssrConfig) {
47
47
  }
48
48
  return (ssrConfig === null || ssrConfig === void 0 ? void 0 : ssrConfig.mode) === "stream" ? "stream" : "string";
49
49
  }
50
+ const getLinkAttributes = (linkTag) => {
51
+ const attributes = /* @__PURE__ */ new Map();
52
+ const attributeRegExp = /([^\s"'<>/=]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
53
+ let match;
54
+ while (match = attributeRegExp.exec(linkTag)) {
55
+ const [, name, doubleQuotedValue, singleQuotedValue, unquotedValue] = match;
56
+ if (name.toLowerCase() === "link") {
57
+ continue;
58
+ }
59
+ var _ref, _ref1;
60
+ attributes.set(name.toLowerCase(), (_ref1 = (_ref = doubleQuotedValue !== null && doubleQuotedValue !== void 0 ? doubleQuotedValue : singleQuotedValue) !== null && _ref !== void 0 ? _ref : unquotedValue) !== null && _ref1 !== void 0 ? _ref1 : "");
61
+ }
62
+ return attributes;
63
+ };
64
+ const hasStylesheetLink = (template, href) => {
65
+ var _template_match;
66
+ const linkTags = (_template_match = template.match(/<link\b[^>]*>/gi)) !== null && _template_match !== void 0 ? _template_match : [];
67
+ return linkTags.some((linkTag) => {
68
+ const attributes = getLinkAttributes(linkTag);
69
+ const linkHref = attributes.get("href");
70
+ const rel = attributes.get("rel");
71
+ return linkHref === href && (rel === null || rel === void 0 ? void 0 : rel.split(/\s+/).some((relToken) => relToken.toLowerCase() === "stylesheet"));
72
+ });
73
+ };
50
74
  export {
51
75
  attributesToString,
52
76
  checkIsNode,
53
77
  getSSRConfigByEntry,
54
78
  getSSRMode,
79
+ hasStylesheetLink,
55
80
  safeReplace,
56
81
  serializeErrors
57
82
  };
@@ -24,3 +24,10 @@ export declare function getSSRConfigByEntry(entryName: string, ssr?: ServerUserC
24
24
  loaderFailureMode?: "clientRender" | "errorBoundary";
25
25
  };
26
26
  export declare function getSSRMode(ssrConfig?: SSRConfig): 'string' | 'stream' | false;
27
+ /**
28
+ * Whether the template already contains a `<link rel="stylesheet">` for `href`.
29
+ * Other link rels (e.g. `<link rel="prefetch">` emitted by `performance.prefetch`,
30
+ * or `<link rel="preload" as="style">`) may reference the same css URL but do not
31
+ * apply styles, so they must not block stylesheet injection during SSR.
32
+ */
33
+ export declare const hasStylesheetLink: (template: string, href: string) => boolean;
package/package.json CHANGED
@@ -15,7 +15,7 @@
15
15
  "modern",
16
16
  "modern.js"
17
17
  ],
18
- "version": "2.71.0",
18
+ "version": "2.71.1",
19
19
  "engines": {
20
20
  "node": ">=14.17.6"
21
21
  },
@@ -218,13 +218,13 @@
218
218
  "react-is": "^18",
219
219
  "react-side-effect": "^2.1.2",
220
220
  "styled-components": "^5.3.1",
221
- "@modern-js/plugin-data-loader": "2.71.0",
222
- "@modern-js/plugin": "2.71.0",
223
- "@modern-js/render": "2.71.0",
224
- "@modern-js/runtime-utils": "2.71.0",
225
- "@modern-js/plugin-v2": "2.71.0",
226
- "@modern-js/types": "2.71.0",
227
- "@modern-js/utils": "2.71.0"
221
+ "@modern-js/plugin": "2.71.1",
222
+ "@modern-js/plugin-data-loader": "2.71.1",
223
+ "@modern-js/plugin-v2": "2.71.1",
224
+ "@modern-js/render": "2.71.1",
225
+ "@modern-js/runtime-utils": "2.71.1",
226
+ "@modern-js/types": "2.71.1",
227
+ "@modern-js/utils": "2.71.1"
228
228
  },
229
229
  "peerDependencies": {
230
230
  "react": ">=17",
@@ -232,7 +232,7 @@
232
232
  },
233
233
  "devDependencies": {
234
234
  "@remix-run/web-fetch": "^4.1.3",
235
- "@rsbuild/core": "1.7.5",
235
+ "@rsbuild/core": "1.7.6",
236
236
  "@testing-library/react": "^13.4.0",
237
237
  "@types/cookie": "0.6.0",
238
238
  "@types/invariant": "^2.2.30",
@@ -247,11 +247,11 @@
247
247
  "ts-jest": "^29.1.0",
248
248
  "ts-node": "^10.9.1",
249
249
  "typescript": "^5",
250
- "webpack": "^5.103.0",
251
- "@modern-js/app-tools": "2.71.0",
250
+ "webpack": "^5.108.0",
251
+ "@modern-js/app-tools": "2.71.1",
252
252
  "@scripts/build": "2.66.0",
253
253
  "@scripts/jest-config": "2.66.0",
254
- "@modern-js/core": "2.71.0"
254
+ "@modern-js/core": "2.71.1"
255
255
  },
256
256
  "sideEffects": false,
257
257
  "publishConfig": {