@modern-js/runtime 3.3.0 → 3.4.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.
- package/dist/cjs/core/server/stream/beforeTemplate.js +1 -1
- package/dist/cjs/core/server/stream/createReadableStream.js +7 -5
- package/dist/cjs/core/server/stream/createReadableStream.worker.js +6 -2
- package/dist/cjs/core/server/string/loadable.js +1 -5
- package/dist/cjs/core/server/utils.js +23 -0
- package/dist/esm/core/server/stream/beforeTemplate.mjs +2 -2
- package/dist/esm/core/server/stream/createReadableStream.mjs +7 -5
- package/dist/esm/core/server/stream/createReadableStream.worker.mjs +6 -2
- package/dist/esm/core/server/string/loadable.mjs +2 -6
- package/dist/esm/core/server/utils.mjs +21 -1
- package/dist/esm-node/core/server/stream/beforeTemplate.mjs +2 -2
- package/dist/esm-node/core/server/stream/createReadableStream.mjs +7 -5
- package/dist/esm-node/core/server/stream/createReadableStream.worker.mjs +6 -2
- package/dist/esm-node/core/server/string/loadable.mjs +2 -6
- package/dist/esm-node/core/server/utils.mjs +21 -1
- package/dist/types/core/server/utils.d.ts +7 -0
- package/package.json +10 -10
|
@@ -86,7 +86,7 @@ async function buildShellBeforeTemplate(beforeAppTemplate, options) {
|
|
|
86
86
|
if (asyncEntry) matchedRouteManifests?.push(asyncEntry);
|
|
87
87
|
const cssChunks = matchedRouteManifests ? matchedRouteManifests?.reduce((chunks, routeManifest)=>{
|
|
88
88
|
const { referenceCssAssets = [] } = routeManifest;
|
|
89
|
-
const _cssChunks = referenceCssAssets.filter((asset)=>asset?.endsWith('.css') && !
|
|
89
|
+
const _cssChunks = referenceCssAssets.filter((asset)=>asset?.endsWith('.css') && !(0, external_utils_js_namespaceObject.hasStylesheetLink)(template, asset));
|
|
90
90
|
return [
|
|
91
91
|
...chunks,
|
|
92
92
|
..._cssChunks
|
|
@@ -92,12 +92,14 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
92
92
|
try {
|
|
93
93
|
if (shellChunkStatus !== external_shared_js_namespaceObject.ShellChunkStatus.FINISH) {
|
|
94
94
|
chunkVec.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
const concatedChunk = Buffer.concat(chunkVec).toString('utf-8');
|
|
96
|
+
const markerIndex = concatedChunk.indexOf(external_common_js_namespaceObject.ESCAPED_SHELL_STREAM_END_MARK);
|
|
97
|
+
if (-1 !== markerIndex) {
|
|
98
|
+
const beforeMark = concatedChunk.slice(0, markerIndex);
|
|
99
|
+
const afterMark = concatedChunk.slice(markerIndex + external_common_js_namespaceObject.ESCAPED_SHELL_STREAM_END_MARK.length);
|
|
99
100
|
shellChunkStatus = external_shared_js_namespaceObject.ShellChunkStatus.FINISH;
|
|
100
|
-
this.push(`${shellBefore}${
|
|
101
|
+
this.push(`${shellBefore}${beforeMark}${shellAfter}`);
|
|
102
|
+
if (afterMark) this.push(afterMark);
|
|
101
103
|
if (pendingScripts.length > 0) for (const s of pendingScripts)this.push(s);
|
|
102
104
|
}
|
|
103
105
|
} else this.push(chunk);
|
|
@@ -107,9 +107,13 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
107
107
|
if (shellChunkStatus !== external_shared_js_namespaceObject.ShellChunkStatus.FINISH) {
|
|
108
108
|
chunkVec.push(new TextDecoder().decode(value));
|
|
109
109
|
const concatedChunk = chunkVec.join('');
|
|
110
|
-
|
|
110
|
+
const markerIndex = concatedChunk.indexOf(external_common_js_namespaceObject.ESCAPED_SHELL_STREAM_END_MARK);
|
|
111
|
+
if (-1 !== markerIndex) {
|
|
112
|
+
const beforeMark = concatedChunk.slice(0, markerIndex);
|
|
113
|
+
const afterMark = concatedChunk.slice(markerIndex + external_common_js_namespaceObject.ESCAPED_SHELL_STREAM_END_MARK.length);
|
|
111
114
|
shellChunkStatus = external_shared_js_namespaceObject.ShellChunkStatus.FINISH;
|
|
112
|
-
safeEnqueue((0, external_shared_js_namespaceObject.encodeForWebStream)(`${shellBefore}${
|
|
115
|
+
safeEnqueue((0, external_shared_js_namespaceObject.encodeForWebStream)(`${shellBefore}${beforeMark}${shellAfter}`));
|
|
116
|
+
if (afterMark) safeEnqueue((0, external_shared_js_namespaceObject.encodeForWebStream)(afterMark));
|
|
113
117
|
flushPendingScripts();
|
|
114
118
|
}
|
|
115
119
|
} else safeEnqueue(value);
|
|
@@ -116,11 +116,7 @@ class LoadableCollector {
|
|
|
116
116
|
const { template, chunkSet, config, entryName } = this.options;
|
|
117
117
|
const { inlineStyles } = config;
|
|
118
118
|
const atrributes = (0, external_utils_js_namespaceObject.attributesToString)(this.generateAttributes());
|
|
119
|
-
const
|
|
120
|
-
const matchs = template.matchAll(linkRegExp);
|
|
121
|
-
const existedLinks = [];
|
|
122
|
-
for (const match of matchs)existedLinks.push(match[1]);
|
|
123
|
-
const css = await Promise.all(chunks.filter((chunk)=>!existedLinks.includes(chunk.url) && !this.existsAssets?.includes(chunk.path)).map(async (chunk)=>{
|
|
119
|
+
const css = await Promise.all(chunks.filter((chunk)=>!(0, external_utils_js_namespaceObject.hasStylesheetLink)(template, chunk.url) && !this.existsAssets?.includes(chunk.path)).map(async (chunk)=>{
|
|
124
120
|
const link = `<link${atrributes} href="${chunk.url}" rel="stylesheet" />`;
|
|
125
121
|
if ((0, external_utils_js_namespaceObject.checkIsNode)() && checkIsInline(chunk, inlineStyles)) return readAsset(chunk).then((content)=>`<style>${content}</style>`).catch((_)=>link);
|
|
126
122
|
return link;
|
|
@@ -32,6 +32,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
32
32
|
checkIsNode: ()=>checkIsNode,
|
|
33
33
|
getSSRConfigByEntry: ()=>getSSRConfigByEntry,
|
|
34
34
|
getSSRMode: ()=>getSSRMode,
|
|
35
|
+
hasStylesheetLink: ()=>hasStylesheetLink,
|
|
35
36
|
safeReplace: ()=>safeReplace,
|
|
36
37
|
serializeErrors: ()=>serializeErrors
|
|
37
38
|
});
|
|
@@ -73,10 +74,31 @@ function getSSRMode(ssrConfig) {
|
|
|
73
74
|
const result = ssrConfig?.mode === 'string' ? 'string' : 'stream';
|
|
74
75
|
return result;
|
|
75
76
|
}
|
|
77
|
+
const getLinkAttributes = (linkTag)=>{
|
|
78
|
+
const attributes = new Map();
|
|
79
|
+
const attributeRegExp = /([^\s"'<>/=]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
|
|
80
|
+
let match;
|
|
81
|
+
while(match = attributeRegExp.exec(linkTag)){
|
|
82
|
+
const [, name, doubleQuotedValue, singleQuotedValue, unquotedValue] = match;
|
|
83
|
+
if ('link' === name.toLowerCase()) continue;
|
|
84
|
+
attributes.set(name.toLowerCase(), doubleQuotedValue ?? singleQuotedValue ?? unquotedValue ?? '');
|
|
85
|
+
}
|
|
86
|
+
return attributes;
|
|
87
|
+
};
|
|
88
|
+
const hasStylesheetLink = (template, href)=>{
|
|
89
|
+
const linkTags = template.match(/<link\b[^>]*>/gi) ?? [];
|
|
90
|
+
return linkTags.some((linkTag)=>{
|
|
91
|
+
const attributes = getLinkAttributes(linkTag);
|
|
92
|
+
const linkHref = attributes.get('href');
|
|
93
|
+
const rel = attributes.get('rel');
|
|
94
|
+
return linkHref === href && rel?.split(/\s+/).some((relToken)=>'stylesheet' === relToken.toLowerCase());
|
|
95
|
+
});
|
|
96
|
+
};
|
|
76
97
|
exports.attributesToString = __webpack_exports__.attributesToString;
|
|
77
98
|
exports.checkIsNode = __webpack_exports__.checkIsNode;
|
|
78
99
|
exports.getSSRConfigByEntry = __webpack_exports__.getSSRConfigByEntry;
|
|
79
100
|
exports.getSSRMode = __webpack_exports__.getSSRMode;
|
|
101
|
+
exports.hasStylesheetLink = __webpack_exports__.hasStylesheetLink;
|
|
80
102
|
exports.safeReplace = __webpack_exports__.safeReplace;
|
|
81
103
|
exports.serializeErrors = __webpack_exports__.serializeErrors;
|
|
82
104
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
@@ -84,6 +106,7 @@ for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
|
84
106
|
"checkIsNode",
|
|
85
107
|
"getSSRConfigByEntry",
|
|
86
108
|
"getSSRMode",
|
|
109
|
+
"hasStylesheetLink",
|
|
87
110
|
"safeReplace",
|
|
88
111
|
"serializeErrors"
|
|
89
112
|
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
@@ -3,7 +3,7 @@ import react_helmet from "react-helmet";
|
|
|
3
3
|
import { CHUNK_CSS_PLACEHOLDER } from "../constants.mjs";
|
|
4
4
|
import { createReplaceHelemt } from "../helmet.mjs";
|
|
5
5
|
import { buildHtml } from "../shared.mjs";
|
|
6
|
-
import { checkIsNode, safeReplace } from "../utils.mjs";
|
|
6
|
+
import { checkIsNode, hasStylesheetLink, safeReplace } from "../utils.mjs";
|
|
7
7
|
const readAsset = async (chunk)=>{
|
|
8
8
|
const fs = await import("fs/promises");
|
|
9
9
|
const path = await import("path");
|
|
@@ -44,7 +44,7 @@ async function buildShellBeforeTemplate(beforeAppTemplate, options) {
|
|
|
44
44
|
if (asyncEntry) matchedRouteManifests?.push(asyncEntry);
|
|
45
45
|
const cssChunks = matchedRouteManifests ? matchedRouteManifests?.reduce((chunks, routeManifest)=>{
|
|
46
46
|
const { referenceCssAssets = [] } = routeManifest;
|
|
47
|
-
const _cssChunks = referenceCssAssets.filter((asset)=>asset?.endsWith('.css') && !template
|
|
47
|
+
const _cssChunks = referenceCssAssets.filter((asset)=>asset?.endsWith('.css') && !hasStylesheetLink(template, asset));
|
|
48
48
|
return [
|
|
49
49
|
...chunks,
|
|
50
50
|
..._cssChunks
|
|
@@ -60,12 +60,14 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
60
60
|
try {
|
|
61
61
|
if (shellChunkStatus !== ShellChunkStatus.FINISH) {
|
|
62
62
|
chunkVec.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
const concatedChunk = Buffer.concat(chunkVec).toString('utf-8');
|
|
64
|
+
const markerIndex = concatedChunk.indexOf(ESCAPED_SHELL_STREAM_END_MARK);
|
|
65
|
+
if (-1 !== markerIndex) {
|
|
66
|
+
const beforeMark = concatedChunk.slice(0, markerIndex);
|
|
67
|
+
const afterMark = concatedChunk.slice(markerIndex + ESCAPED_SHELL_STREAM_END_MARK.length);
|
|
67
68
|
shellChunkStatus = ShellChunkStatus.FINISH;
|
|
68
|
-
this.push(`${shellBefore}${
|
|
69
|
+
this.push(`${shellBefore}${beforeMark}${shellAfter}`);
|
|
70
|
+
if (afterMark) this.push(afterMark);
|
|
69
71
|
if (pendingScripts.length > 0) for (const s of pendingScripts)this.push(s);
|
|
70
72
|
}
|
|
71
73
|
} else this.push(chunk);
|
|
@@ -75,9 +75,13 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
75
75
|
if (shellChunkStatus !== ShellChunkStatus.FINISH) {
|
|
76
76
|
chunkVec.push(new TextDecoder().decode(value));
|
|
77
77
|
const concatedChunk = chunkVec.join('');
|
|
78
|
-
|
|
78
|
+
const markerIndex = concatedChunk.indexOf(ESCAPED_SHELL_STREAM_END_MARK);
|
|
79
|
+
if (-1 !== markerIndex) {
|
|
80
|
+
const beforeMark = concatedChunk.slice(0, markerIndex);
|
|
81
|
+
const afterMark = concatedChunk.slice(markerIndex + ESCAPED_SHELL_STREAM_END_MARK.length);
|
|
79
82
|
shellChunkStatus = ShellChunkStatus.FINISH;
|
|
80
|
-
safeEnqueue(encodeForWebStream(`${shellBefore}${
|
|
83
|
+
safeEnqueue(encodeForWebStream(`${shellBefore}${beforeMark}${shellAfter}`));
|
|
84
|
+
if (afterMark) safeEnqueue(encodeForWebStream(afterMark));
|
|
81
85
|
flushPendingScripts();
|
|
82
86
|
}
|
|
83
87
|
} else safeEnqueue(value);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ChunkExtractor } from "@loadable/server";
|
|
2
|
-
import { attributesToString, checkIsNode } from "../utils.mjs";
|
|
2
|
+
import { attributesToString, checkIsNode, hasStylesheetLink } from "../utils.mjs";
|
|
3
3
|
const extname = (uri)=>{
|
|
4
4
|
if ('string' != typeof uri || !uri.includes('.')) return '';
|
|
5
5
|
return `.${uri?.split('.').pop()}` || '';
|
|
@@ -84,11 +84,7 @@ class LoadableCollector {
|
|
|
84
84
|
const { template, chunkSet, config, entryName } = this.options;
|
|
85
85
|
const { inlineStyles } = config;
|
|
86
86
|
const atrributes = attributesToString(this.generateAttributes());
|
|
87
|
-
const
|
|
88
|
-
const matchs = template.matchAll(linkRegExp);
|
|
89
|
-
const existedLinks = [];
|
|
90
|
-
for (const match of matchs)existedLinks.push(match[1]);
|
|
91
|
-
const css = await Promise.all(chunks.filter((chunk)=>!existedLinks.includes(chunk.url) && !this.existsAssets?.includes(chunk.path)).map(async (chunk)=>{
|
|
87
|
+
const css = await Promise.all(chunks.filter((chunk)=>!hasStylesheetLink(template, chunk.url) && !this.existsAssets?.includes(chunk.path)).map(async (chunk)=>{
|
|
92
88
|
const link = `<link${atrributes} href="${chunk.url}" rel="stylesheet" />`;
|
|
93
89
|
if (checkIsNode() && checkIsInline(chunk, inlineStyles)) return readAsset(chunk).then((content)=>`<style>${content}</style>`).catch((_)=>link);
|
|
94
90
|
return link;
|
|
@@ -36,4 +36,24 @@ function getSSRMode(ssrConfig) {
|
|
|
36
36
|
const result = ssrConfig?.mode === 'string' ? 'string' : 'stream';
|
|
37
37
|
return result;
|
|
38
38
|
}
|
|
39
|
-
|
|
39
|
+
const getLinkAttributes = (linkTag)=>{
|
|
40
|
+
const attributes = new Map();
|
|
41
|
+
const attributeRegExp = /([^\s"'<>/=]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
|
|
42
|
+
let match;
|
|
43
|
+
while(match = attributeRegExp.exec(linkTag)){
|
|
44
|
+
const [, name, doubleQuotedValue, singleQuotedValue, unquotedValue] = match;
|
|
45
|
+
if ('link' === name.toLowerCase()) continue;
|
|
46
|
+
attributes.set(name.toLowerCase(), doubleQuotedValue ?? singleQuotedValue ?? unquotedValue ?? '');
|
|
47
|
+
}
|
|
48
|
+
return attributes;
|
|
49
|
+
};
|
|
50
|
+
const hasStylesheetLink = (template, href)=>{
|
|
51
|
+
const linkTags = template.match(/<link\b[^>]*>/gi) ?? [];
|
|
52
|
+
return linkTags.some((linkTag)=>{
|
|
53
|
+
const attributes = getLinkAttributes(linkTag);
|
|
54
|
+
const linkHref = attributes.get('href');
|
|
55
|
+
const rel = attributes.get('rel');
|
|
56
|
+
return linkHref === href && rel?.split(/\s+/).some((relToken)=>'stylesheet' === relToken.toLowerCase());
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
export { attributesToString, checkIsNode, getSSRConfigByEntry, getSSRMode, hasStylesheetLink, safeReplace, serializeErrors };
|
|
@@ -4,7 +4,7 @@ import react_helmet from "react-helmet";
|
|
|
4
4
|
import { CHUNK_CSS_PLACEHOLDER } from "../constants.mjs";
|
|
5
5
|
import { createReplaceHelemt } from "../helmet.mjs";
|
|
6
6
|
import { buildHtml } from "../shared.mjs";
|
|
7
|
-
import { checkIsNode, safeReplace } from "../utils.mjs";
|
|
7
|
+
import { checkIsNode, hasStylesheetLink, safeReplace } from "../utils.mjs";
|
|
8
8
|
import { fileURLToPath as __rspack_fileURLToPath } from "node:url";
|
|
9
9
|
import { dirname as __rspack_dirname } from "node:path";
|
|
10
10
|
var beforeTemplate_dirname = __rspack_dirname(__rspack_fileURLToPath(import.meta.url));
|
|
@@ -48,7 +48,7 @@ async function buildShellBeforeTemplate(beforeAppTemplate, options) {
|
|
|
48
48
|
if (asyncEntry) matchedRouteManifests?.push(asyncEntry);
|
|
49
49
|
const cssChunks = matchedRouteManifests ? matchedRouteManifests?.reduce((chunks, routeManifest)=>{
|
|
50
50
|
const { referenceCssAssets = [] } = routeManifest;
|
|
51
|
-
const _cssChunks = referenceCssAssets.filter((asset)=>asset?.endsWith('.css') && !template
|
|
51
|
+
const _cssChunks = referenceCssAssets.filter((asset)=>asset?.endsWith('.css') && !hasStylesheetLink(template, asset));
|
|
52
52
|
return [
|
|
53
53
|
...chunks,
|
|
54
54
|
..._cssChunks
|
|
@@ -61,12 +61,14 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
61
61
|
try {
|
|
62
62
|
if (shellChunkStatus !== ShellChunkStatus.FINISH) {
|
|
63
63
|
chunkVec.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
const concatedChunk = Buffer.concat(chunkVec).toString('utf-8');
|
|
65
|
+
const markerIndex = concatedChunk.indexOf(ESCAPED_SHELL_STREAM_END_MARK);
|
|
66
|
+
if (-1 !== markerIndex) {
|
|
67
|
+
const beforeMark = concatedChunk.slice(0, markerIndex);
|
|
68
|
+
const afterMark = concatedChunk.slice(markerIndex + ESCAPED_SHELL_STREAM_END_MARK.length);
|
|
68
69
|
shellChunkStatus = ShellChunkStatus.FINISH;
|
|
69
|
-
this.push(`${shellBefore}${
|
|
70
|
+
this.push(`${shellBefore}${beforeMark}${shellAfter}`);
|
|
71
|
+
if (afterMark) this.push(afterMark);
|
|
70
72
|
if (pendingScripts.length > 0) for (const s of pendingScripts)this.push(s);
|
|
71
73
|
}
|
|
72
74
|
} else this.push(chunk);
|
|
@@ -76,9 +76,13 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
76
76
|
if (shellChunkStatus !== ShellChunkStatus.FINISH) {
|
|
77
77
|
chunkVec.push(new TextDecoder().decode(value));
|
|
78
78
|
const concatedChunk = chunkVec.join('');
|
|
79
|
-
|
|
79
|
+
const markerIndex = concatedChunk.indexOf(ESCAPED_SHELL_STREAM_END_MARK);
|
|
80
|
+
if (-1 !== markerIndex) {
|
|
81
|
+
const beforeMark = concatedChunk.slice(0, markerIndex);
|
|
82
|
+
const afterMark = concatedChunk.slice(markerIndex + ESCAPED_SHELL_STREAM_END_MARK.length);
|
|
80
83
|
shellChunkStatus = ShellChunkStatus.FINISH;
|
|
81
|
-
safeEnqueue(encodeForWebStream(`${shellBefore}${
|
|
84
|
+
safeEnqueue(encodeForWebStream(`${shellBefore}${beforeMark}${shellAfter}`));
|
|
85
|
+
if (afterMark) safeEnqueue(encodeForWebStream(afterMark));
|
|
82
86
|
flushPendingScripts();
|
|
83
87
|
}
|
|
84
88
|
} else safeEnqueue(value);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
import { ChunkExtractor } from "@loadable/server";
|
|
3
|
-
import { attributesToString, checkIsNode } from "../utils.mjs";
|
|
3
|
+
import { attributesToString, checkIsNode, hasStylesheetLink } from "../utils.mjs";
|
|
4
4
|
import { fileURLToPath as __rspack_fileURLToPath } from "node:url";
|
|
5
5
|
import { dirname as __rspack_dirname } from "node:path";
|
|
6
6
|
var loadable_dirname = __rspack_dirname(__rspack_fileURLToPath(import.meta.url));
|
|
@@ -88,11 +88,7 @@ class LoadableCollector {
|
|
|
88
88
|
const { template, chunkSet, config, entryName } = this.options;
|
|
89
89
|
const { inlineStyles } = config;
|
|
90
90
|
const atrributes = attributesToString(this.generateAttributes());
|
|
91
|
-
const
|
|
92
|
-
const matchs = template.matchAll(linkRegExp);
|
|
93
|
-
const existedLinks = [];
|
|
94
|
-
for (const match of matchs)existedLinks.push(match[1]);
|
|
95
|
-
const css = await Promise.all(chunks.filter((chunk)=>!existedLinks.includes(chunk.url) && !this.existsAssets?.includes(chunk.path)).map(async (chunk)=>{
|
|
91
|
+
const css = await Promise.all(chunks.filter((chunk)=>!hasStylesheetLink(template, chunk.url) && !this.existsAssets?.includes(chunk.path)).map(async (chunk)=>{
|
|
96
92
|
const link = `<link${atrributes} href="${chunk.url}" rel="stylesheet" />`;
|
|
97
93
|
if (checkIsNode() && checkIsInline(chunk, inlineStyles)) return readAsset(chunk).then((content)=>`<style>${content}</style>`).catch((_)=>link);
|
|
98
94
|
return link;
|
|
@@ -37,4 +37,24 @@ function getSSRMode(ssrConfig) {
|
|
|
37
37
|
const result = ssrConfig?.mode === 'string' ? 'string' : 'stream';
|
|
38
38
|
return result;
|
|
39
39
|
}
|
|
40
|
-
|
|
40
|
+
const getLinkAttributes = (linkTag)=>{
|
|
41
|
+
const attributes = new Map();
|
|
42
|
+
const attributeRegExp = /([^\s"'<>/=]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
|
|
43
|
+
let match;
|
|
44
|
+
while(match = attributeRegExp.exec(linkTag)){
|
|
45
|
+
const [, name, doubleQuotedValue, singleQuotedValue, unquotedValue] = match;
|
|
46
|
+
if ('link' === name.toLowerCase()) continue;
|
|
47
|
+
attributes.set(name.toLowerCase(), doubleQuotedValue ?? singleQuotedValue ?? unquotedValue ?? '');
|
|
48
|
+
}
|
|
49
|
+
return attributes;
|
|
50
|
+
};
|
|
51
|
+
const hasStylesheetLink = (template, href)=>{
|
|
52
|
+
const linkTags = template.match(/<link\b[^>]*>/gi) ?? [];
|
|
53
|
+
return linkTags.some((linkTag)=>{
|
|
54
|
+
const attributes = getLinkAttributes(linkTag);
|
|
55
|
+
const linkHref = attributes.get('href');
|
|
56
|
+
const rel = attributes.get('rel');
|
|
57
|
+
return linkHref === href && rel?.split(/\s+/).some((relToken)=>'stylesheet' === relToken.toLowerCase());
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
export { attributesToString, checkIsNode, getSSRConfigByEntry, getSSRMode, hasStylesheetLink, safeReplace, serializeErrors };
|
|
@@ -23,3 +23,10 @@ export declare function getSSRConfigByEntry(entryName: string, ssr?: ServerUserC
|
|
|
23
23
|
loaderFailureMode?: "clientRender" | "errorBoundary";
|
|
24
24
|
};
|
|
25
25
|
export declare function getSSRMode(ssrConfig?: SSRConfig): 'string' | 'stream' | false;
|
|
26
|
+
/**
|
|
27
|
+
* Whether the template already contains a `<link rel="stylesheet">` for `href`.
|
|
28
|
+
* Other link rels (e.g. `<link rel="prefetch">` emitted by `performance.prefetch`,
|
|
29
|
+
* or `<link rel="preload" as="style">`) may reference the same css URL but do not
|
|
30
|
+
* apply styles, so they must not block stylesheet injection during SSR.
|
|
31
|
+
*/
|
|
32
|
+
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": "3.
|
|
18
|
+
"version": "3.4.0",
|
|
19
19
|
"engines": {
|
|
20
20
|
"node": ">=20"
|
|
21
21
|
},
|
|
@@ -203,7 +203,7 @@
|
|
|
203
203
|
"dependencies": {
|
|
204
204
|
"@loadable/component": "5.16.7",
|
|
205
205
|
"@loadable/server": "5.16.7",
|
|
206
|
-
"@swc/core": "1.15.
|
|
206
|
+
"@swc/core": "1.15.41",
|
|
207
207
|
"@swc/helpers": "^0.5.17",
|
|
208
208
|
"@swc/plugin-loadable-components": "^11.12.0",
|
|
209
209
|
"@types/loadable__component": "^5.13.10",
|
|
@@ -215,12 +215,12 @@
|
|
|
215
215
|
"isbot": "3.8.0",
|
|
216
216
|
"react-helmet": "^6.1.0",
|
|
217
217
|
"react-is": "^18.3.1",
|
|
218
|
-
"@modern-js/plugin": "3.
|
|
219
|
-
"@modern-js/
|
|
220
|
-
"@modern-js/
|
|
221
|
-
"@modern-js/
|
|
222
|
-
"@modern-js/types": "3.
|
|
223
|
-
"@modern-js/utils": "3.
|
|
218
|
+
"@modern-js/plugin": "3.4.0",
|
|
219
|
+
"@modern-js/plugin-data-loader": "3.4.0",
|
|
220
|
+
"@modern-js/runtime-utils": "3.4.0",
|
|
221
|
+
"@modern-js/render": "3.4.0",
|
|
222
|
+
"@modern-js/types": "3.4.0",
|
|
223
|
+
"@modern-js/utils": "3.4.0"
|
|
224
224
|
},
|
|
225
225
|
"peerDependencies": {
|
|
226
226
|
"react": ">=17.0.2",
|
|
@@ -229,7 +229,7 @@
|
|
|
229
229
|
"devDependencies": {
|
|
230
230
|
"@remix-run/web-fetch": "^4.1.3",
|
|
231
231
|
"@rsbuild/core": "2.0.10",
|
|
232
|
-
"@rslib/core": "0.22.
|
|
232
|
+
"@rslib/core": "0.22.1",
|
|
233
233
|
"@testing-library/dom": "^10.4.1",
|
|
234
234
|
"@testing-library/react": "^16.3.2",
|
|
235
235
|
"@types/cookie": "0.6.0",
|
|
@@ -240,7 +240,7 @@
|
|
|
240
240
|
"react-dom": "^19.2.7",
|
|
241
241
|
"ts-node": "^10.9.2",
|
|
242
242
|
"typescript": "^5",
|
|
243
|
-
"@modern-js/app-tools": "3.
|
|
243
|
+
"@modern-js/app-tools": "3.4.0",
|
|
244
244
|
"@modern-js/rslib": "2.68.10",
|
|
245
245
|
"@scripts/rstest-config": "2.66.0"
|
|
246
246
|
},
|