@bleedingdev/modern-js-runtime-extensions 3.9.0-ultramodern.17 → 3.9.0-ultramodern.18
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/rendererHead.js +18 -5
- package/dist/cjs/request-policy/policyCore.js +1 -1
- package/dist/cjs/request-policy/transport.js +2 -2
- package/dist/esm/rendererHead.mjs +14 -5
- package/dist/esm/request-policy/policyCore.mjs +1 -1
- package/dist/esm/request-policy/transport.mjs +2 -2
- package/dist/esm-node/rendererHead.mjs +14 -5
- package/dist/esm-node/request-policy/policyCore.mjs +1 -1
- package/dist/esm-node/request-policy/transport.mjs +2 -2
- package/dist/types/helmetCompat.d.ts +1 -1
- package/dist/types/rendererHead.d.ts +5 -3
- package/package.json +4 -16
- package/src/helmetCompat.ts +1 -1
- package/src/rendererHead.ts +25 -8
- package/src/request-policy/policyCore.ts +1 -1
- package/src/request-policy/transport.ts +2 -2
- package/tests/headRuntime.test.tsx +1 -1
- package/tests/rendererHead.test.ts +52 -9
- package/tests/shellStream.test.ts +2 -2
- package/dist/cjs/reactContext.js +0 -45
- package/dist/esm/reactContext.mjs +0 -7
- package/dist/esm-node/reactContext.mjs +0 -8
- package/dist/types/reactContext.d.ts +0 -6
- package/src/reactContext.ts +0 -16
package/dist/cjs/rendererHead.js
CHANGED
|
@@ -57,8 +57,9 @@ var __webpack_exports__ = {};
|
|
|
57
57
|
"createWebHeadMarkerStripper",
|
|
58
58
|
"default",
|
|
59
59
|
"finishHeadRender",
|
|
60
|
-
"
|
|
60
|
+
"LATE_HEAD_MESSAGE",
|
|
61
61
|
"beginHeadRender",
|
|
62
|
+
"abortHeadRender",
|
|
62
63
|
"completeHeadRender",
|
|
63
64
|
"createHeadChunkProcessor",
|
|
64
65
|
"publishHeadRender",
|
|
@@ -71,6 +72,7 @@ var __webpack_exports__ = {};
|
|
|
71
72
|
const MARKER_SUFFIX = '"></template>';
|
|
72
73
|
const RECORD_ID_WIDTH = 12;
|
|
73
74
|
const MARKER_LENGTH = MARKER_PREFIX.length + 1 + 36 + RECORD_ID_WIDTH + MARKER_SUFFIX.length;
|
|
75
|
+
const LATE_HEAD_MESSAGE = "<Helmet> rendered in a boundary that completed after the shell; head tags dropped. Move it above the Suspense boundary or set server.ssrByEntries[entry].mode to 'string'.";
|
|
74
76
|
const HEAD_STATE = Symbol('modern.rendererHead');
|
|
75
77
|
const getState = (context)=>{
|
|
76
78
|
const headContext = context;
|
|
@@ -91,7 +93,12 @@ var __webpack_exports__ = {};
|
|
|
91
93
|
transaction.nextRecordId += 1;
|
|
92
94
|
return token;
|
|
93
95
|
};
|
|
94
|
-
const
|
|
96
|
+
const dropLateHead = (transaction)=>{
|
|
97
|
+
if (transaction.lateHeadReported) return;
|
|
98
|
+
transaction.lateHeadReported = true;
|
|
99
|
+
transaction.reportLateHead(LATE_HEAD_MESSAGE);
|
|
100
|
+
};
|
|
101
|
+
const beginHeadRender = (context, reportLateHead)=>{
|
|
95
102
|
const state = getState(context);
|
|
96
103
|
state.transaction = {
|
|
97
104
|
recordsByToken: new Map(),
|
|
@@ -100,7 +107,9 @@ var __webpack_exports__ = {};
|
|
|
100
107
|
nextRecordId: 0,
|
|
101
108
|
nonce: globalThis.crypto.randomUUID(),
|
|
102
109
|
previousRecords: state.publishedRecords,
|
|
103
|
-
sealed: false
|
|
110
|
+
sealed: false,
|
|
111
|
+
reportLateHead,
|
|
112
|
+
lateHeadReported: false
|
|
104
113
|
};
|
|
105
114
|
state.publish?.([]);
|
|
106
115
|
};
|
|
@@ -111,7 +120,6 @@ var __webpack_exports__ = {};
|
|
|
111
120
|
const hadPublisher = void 0 !== state.publish;
|
|
112
121
|
state.publish = (records)=>publish(records);
|
|
113
122
|
if (!hadPublisher) state.publish([]);
|
|
114
|
-
if (transaction.sealed) return null;
|
|
115
123
|
const token = createToken(transaction);
|
|
116
124
|
transaction.recordsByToken.set(token, createRecord());
|
|
117
125
|
return {
|
|
@@ -141,7 +149,9 @@ var __webpack_exports__ = {};
|
|
|
141
149
|
const token = html.slice(tokenStart, end);
|
|
142
150
|
if (transaction.recordsByToken.has(token)) {
|
|
143
151
|
stripped += html.slice(cursor, start);
|
|
144
|
-
if (
|
|
152
|
+
if (transaction.sealed) {
|
|
153
|
+
if (!transaction.committedTokens.has(token)) dropLateHead(transaction);
|
|
154
|
+
} else if (!transaction.committedTokens.has(token)) {
|
|
145
155
|
transaction.committedTokens.add(token);
|
|
146
156
|
transaction.committedRecords.push(transaction.recordsByToken.get(token));
|
|
147
157
|
}
|
|
@@ -227,6 +237,7 @@ var __webpack_exports__ = {};
|
|
|
227
237
|
});
|
|
228
238
|
};
|
|
229
239
|
__webpack_require__.d(__webpack_exports__, {}, {
|
|
240
|
+
LATE_HEAD_MESSAGE: LATE_HEAD_MESSAGE,
|
|
230
241
|
abortHeadRender: abortHeadRender,
|
|
231
242
|
beginHeadRender: beginHeadRender,
|
|
232
243
|
collectHeadRecord: collectHeadRecord,
|
|
@@ -238,6 +249,7 @@ var __webpack_exports__ = {};
|
|
|
238
249
|
publishHeadRender: publishHeadRender
|
|
239
250
|
});
|
|
240
251
|
})();
|
|
252
|
+
exports.LATE_HEAD_MESSAGE = __webpack_exports__.LATE_HEAD_MESSAGE;
|
|
241
253
|
exports.abortHeadRender = __webpack_exports__.abortHeadRender;
|
|
242
254
|
exports.beginHeadRender = __webpack_exports__.beginHeadRender;
|
|
243
255
|
exports.collectHeadRecord = __webpack_exports__.collectHeadRecord;
|
|
@@ -248,6 +260,7 @@ exports.createWebHeadMarkerStripper = __webpack_exports__.createWebHeadMarkerStr
|
|
|
248
260
|
exports.finishHeadRender = __webpack_exports__.finishHeadRender;
|
|
249
261
|
exports.publishHeadRender = __webpack_exports__.publishHeadRender;
|
|
250
262
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
263
|
+
"LATE_HEAD_MESSAGE",
|
|
251
264
|
"abortHeadRender",
|
|
252
265
|
"beginHeadRender",
|
|
253
266
|
"collectHeadRecord",
|
|
@@ -87,7 +87,7 @@ const shouldRetryWithDefaults = (method, statusCode, error, retryableStatusCodes
|
|
|
87
87
|
const shouldRetry = (retryOptions, context, retryableStatusCodes)=>{
|
|
88
88
|
if ('function' == typeof retryOptions?.shouldRetry) try {
|
|
89
89
|
return retryOptions.shouldRetry(context);
|
|
90
|
-
} catch
|
|
90
|
+
} catch {
|
|
91
91
|
return false;
|
|
92
92
|
}
|
|
93
93
|
return shouldRetryWithDefaults(context.method, context.statusCode, context.error, retryableStatusCodes);
|
|
@@ -104,7 +104,7 @@ const emitDegradedEvent = (transport, event)=>{
|
|
|
104
104
|
if ('function' != typeof transport?.onDegraded) return;
|
|
105
105
|
try {
|
|
106
106
|
transport.onDegraded(event);
|
|
107
|
-
} catch
|
|
107
|
+
} catch {}
|
|
108
108
|
};
|
|
109
109
|
const withTimeout = async (promise, timeoutMs, signalBuilder)=>{
|
|
110
110
|
if (!timeoutMs || timeoutMs <= 0) {
|
|
@@ -4,6 +4,7 @@ const MARKER_PREFIX = `<template ${MARKER_ATTRIBUTE}="`;
|
|
|
4
4
|
const MARKER_SUFFIX = '"></template>';
|
|
5
5
|
const RECORD_ID_WIDTH = 12;
|
|
6
6
|
const MARKER_LENGTH = MARKER_PREFIX.length + 1 + 36 + RECORD_ID_WIDTH + MARKER_SUFFIX.length;
|
|
7
|
+
const LATE_HEAD_MESSAGE = "<Helmet> rendered in a boundary that completed after the shell; head tags dropped. Move it above the Suspense boundary or set server.ssrByEntries[entry].mode to 'string'.";
|
|
7
8
|
const HEAD_STATE = Symbol('modern.rendererHead');
|
|
8
9
|
const getState = (context)=>{
|
|
9
10
|
const headContext = context;
|
|
@@ -24,7 +25,12 @@ const createToken = (transaction)=>{
|
|
|
24
25
|
transaction.nextRecordId += 1;
|
|
25
26
|
return token;
|
|
26
27
|
};
|
|
27
|
-
const
|
|
28
|
+
const dropLateHead = (transaction)=>{
|
|
29
|
+
if (transaction.lateHeadReported) return;
|
|
30
|
+
transaction.lateHeadReported = true;
|
|
31
|
+
transaction.reportLateHead(LATE_HEAD_MESSAGE);
|
|
32
|
+
};
|
|
33
|
+
const beginHeadRender = (context, reportLateHead)=>{
|
|
28
34
|
const state = getState(context);
|
|
29
35
|
state.transaction = {
|
|
30
36
|
recordsByToken: new Map(),
|
|
@@ -33,7 +39,9 @@ const beginHeadRender = (context)=>{
|
|
|
33
39
|
nextRecordId: 0,
|
|
34
40
|
nonce: globalThis.crypto.randomUUID(),
|
|
35
41
|
previousRecords: state.publishedRecords,
|
|
36
|
-
sealed: false
|
|
42
|
+
sealed: false,
|
|
43
|
+
reportLateHead,
|
|
44
|
+
lateHeadReported: false
|
|
37
45
|
};
|
|
38
46
|
state.publish?.([]);
|
|
39
47
|
};
|
|
@@ -44,7 +52,6 @@ const collectHeadRecord = (context, createRecord, publish)=>{
|
|
|
44
52
|
const hadPublisher = void 0 !== state.publish;
|
|
45
53
|
state.publish = (records)=>publish(records);
|
|
46
54
|
if (!hadPublisher) state.publish([]);
|
|
47
|
-
if (transaction.sealed) return null;
|
|
48
55
|
const token = createToken(transaction);
|
|
49
56
|
transaction.recordsByToken.set(token, createRecord());
|
|
50
57
|
return {
|
|
@@ -74,7 +81,9 @@ const consumeMarkers = (context, html)=>{
|
|
|
74
81
|
const token = html.slice(tokenStart, end);
|
|
75
82
|
if (transaction.recordsByToken.has(token)) {
|
|
76
83
|
stripped += html.slice(cursor, start);
|
|
77
|
-
if (
|
|
84
|
+
if (transaction.sealed) {
|
|
85
|
+
if (!transaction.committedTokens.has(token)) dropLateHead(transaction);
|
|
86
|
+
} else if (!transaction.committedTokens.has(token)) {
|
|
78
87
|
transaction.committedTokens.add(token);
|
|
79
88
|
transaction.committedRecords.push(transaction.recordsByToken.get(token));
|
|
80
89
|
}
|
|
@@ -159,4 +168,4 @@ const createWebHeadMarkerStripper = (context)=>{
|
|
|
159
168
|
}
|
|
160
169
|
});
|
|
161
170
|
};
|
|
162
|
-
export { abortHeadRender, beginHeadRender, collectHeadRecord, collectHeadState, completeHeadRender, createHeadChunkProcessor, createWebHeadMarkerStripper, finishHeadRender, publishHeadRender };
|
|
171
|
+
export { LATE_HEAD_MESSAGE, abortHeadRender, beginHeadRender, collectHeadRecord, collectHeadState, completeHeadRender, createHeadChunkProcessor, createWebHeadMarkerStripper, finishHeadRender, publishHeadRender };
|
|
@@ -58,7 +58,7 @@ const shouldRetryWithDefaults = (method, statusCode, error, retryableStatusCodes
|
|
|
58
58
|
const shouldRetry = (retryOptions, context, retryableStatusCodes)=>{
|
|
59
59
|
if ('function' == typeof retryOptions?.shouldRetry) try {
|
|
60
60
|
return retryOptions.shouldRetry(context);
|
|
61
|
-
} catch
|
|
61
|
+
} catch {
|
|
62
62
|
return false;
|
|
63
63
|
}
|
|
64
64
|
return shouldRetryWithDefaults(context.method, context.statusCode, context.error, retryableStatusCodes);
|
|
@@ -75,7 +75,7 @@ const emitDegradedEvent = (transport, event)=>{
|
|
|
75
75
|
if ('function' != typeof transport?.onDegraded) return;
|
|
76
76
|
try {
|
|
77
77
|
transport.onDegraded(event);
|
|
78
|
-
} catch
|
|
78
|
+
} catch {}
|
|
79
79
|
};
|
|
80
80
|
const withTimeout = async (promise, timeoutMs, signalBuilder)=>{
|
|
81
81
|
if (!timeoutMs || timeoutMs <= 0) {
|
|
@@ -5,6 +5,7 @@ const MARKER_PREFIX = `<template ${MARKER_ATTRIBUTE}="`;
|
|
|
5
5
|
const MARKER_SUFFIX = '"></template>';
|
|
6
6
|
const RECORD_ID_WIDTH = 12;
|
|
7
7
|
const MARKER_LENGTH = MARKER_PREFIX.length + 1 + 36 + RECORD_ID_WIDTH + MARKER_SUFFIX.length;
|
|
8
|
+
const LATE_HEAD_MESSAGE = "<Helmet> rendered in a boundary that completed after the shell; head tags dropped. Move it above the Suspense boundary or set server.ssrByEntries[entry].mode to 'string'.";
|
|
8
9
|
const HEAD_STATE = Symbol('modern.rendererHead');
|
|
9
10
|
const getState = (context)=>{
|
|
10
11
|
const headContext = context;
|
|
@@ -25,7 +26,12 @@ const createToken = (transaction)=>{
|
|
|
25
26
|
transaction.nextRecordId += 1;
|
|
26
27
|
return token;
|
|
27
28
|
};
|
|
28
|
-
const
|
|
29
|
+
const dropLateHead = (transaction)=>{
|
|
30
|
+
if (transaction.lateHeadReported) return;
|
|
31
|
+
transaction.lateHeadReported = true;
|
|
32
|
+
transaction.reportLateHead(LATE_HEAD_MESSAGE);
|
|
33
|
+
};
|
|
34
|
+
const beginHeadRender = (context, reportLateHead)=>{
|
|
29
35
|
const state = getState(context);
|
|
30
36
|
state.transaction = {
|
|
31
37
|
recordsByToken: new Map(),
|
|
@@ -34,7 +40,9 @@ const beginHeadRender = (context)=>{
|
|
|
34
40
|
nextRecordId: 0,
|
|
35
41
|
nonce: globalThis.crypto.randomUUID(),
|
|
36
42
|
previousRecords: state.publishedRecords,
|
|
37
|
-
sealed: false
|
|
43
|
+
sealed: false,
|
|
44
|
+
reportLateHead,
|
|
45
|
+
lateHeadReported: false
|
|
38
46
|
};
|
|
39
47
|
state.publish?.([]);
|
|
40
48
|
};
|
|
@@ -45,7 +53,6 @@ const collectHeadRecord = (context, createRecord, publish)=>{
|
|
|
45
53
|
const hadPublisher = void 0 !== state.publish;
|
|
46
54
|
state.publish = (records)=>publish(records);
|
|
47
55
|
if (!hadPublisher) state.publish([]);
|
|
48
|
-
if (transaction.sealed) return null;
|
|
49
56
|
const token = createToken(transaction);
|
|
50
57
|
transaction.recordsByToken.set(token, createRecord());
|
|
51
58
|
return {
|
|
@@ -75,7 +82,9 @@ const consumeMarkers = (context, html)=>{
|
|
|
75
82
|
const token = html.slice(tokenStart, end);
|
|
76
83
|
if (transaction.recordsByToken.has(token)) {
|
|
77
84
|
stripped += html.slice(cursor, start);
|
|
78
|
-
if (
|
|
85
|
+
if (transaction.sealed) {
|
|
86
|
+
if (!transaction.committedTokens.has(token)) dropLateHead(transaction);
|
|
87
|
+
} else if (!transaction.committedTokens.has(token)) {
|
|
79
88
|
transaction.committedTokens.add(token);
|
|
80
89
|
transaction.committedRecords.push(transaction.recordsByToken.get(token));
|
|
81
90
|
}
|
|
@@ -160,4 +169,4 @@ const createWebHeadMarkerStripper = (context)=>{
|
|
|
160
169
|
}
|
|
161
170
|
});
|
|
162
171
|
};
|
|
163
|
-
export { abortHeadRender, beginHeadRender, collectHeadRecord, collectHeadState, completeHeadRender, createHeadChunkProcessor, createWebHeadMarkerStripper, finishHeadRender, publishHeadRender };
|
|
172
|
+
export { LATE_HEAD_MESSAGE, abortHeadRender, beginHeadRender, collectHeadRecord, collectHeadState, completeHeadRender, createHeadChunkProcessor, createWebHeadMarkerStripper, finishHeadRender, publishHeadRender };
|
|
@@ -59,7 +59,7 @@ const shouldRetryWithDefaults = (method, statusCode, error, retryableStatusCodes
|
|
|
59
59
|
const shouldRetry = (retryOptions, context, retryableStatusCodes)=>{
|
|
60
60
|
if ('function' == typeof retryOptions?.shouldRetry) try {
|
|
61
61
|
return retryOptions.shouldRetry(context);
|
|
62
|
-
} catch
|
|
62
|
+
} catch {
|
|
63
63
|
return false;
|
|
64
64
|
}
|
|
65
65
|
return shouldRetryWithDefaults(context.method, context.statusCode, context.error, retryableStatusCodes);
|
|
@@ -76,7 +76,7 @@ const emitDegradedEvent = (transport, event)=>{
|
|
|
76
76
|
if ('function' != typeof transport?.onDegraded) return;
|
|
77
77
|
try {
|
|
78
78
|
transport.onDegraded(event);
|
|
79
|
-
} catch
|
|
79
|
+
} catch {}
|
|
80
80
|
};
|
|
81
81
|
const withTimeout = async (promise, timeoutMs, signalBuilder)=>{
|
|
82
82
|
if (!timeoutMs || timeoutMs <= 0) {
|
|
@@ -63,7 +63,7 @@ export type HelmetCompatState<Element> = {
|
|
|
63
63
|
style: HeadDatum<Element>;
|
|
64
64
|
title: HeadDatum<Element>;
|
|
65
65
|
};
|
|
66
|
-
export declare const renderHeadMarker: <Element>(react: ElementFactory<Element>, marker: MarkerProps |
|
|
66
|
+
export declare const renderHeadMarker: <Element>(react: ElementFactory<Element>, marker: MarkerProps | undefined) => Element | null;
|
|
67
67
|
export declare const markPriorityHeadRecord: (record: object, prioritize: boolean | undefined) => void;
|
|
68
68
|
export declare const isPriorityHeadTag: (tag: object) => boolean;
|
|
69
69
|
export declare const headTagsToComponents: <Element>(react: ElementFactory<Element>, tagName: string, tags: HeadTag[]) => Element[];
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
declare const MARKER_ATTRIBUTE = "data-modern-helmet";
|
|
2
2
|
export type RendererHeadMarkerProps = Record<typeof MARKER_ATTRIBUTE, string>;
|
|
3
3
|
export * from './helmetCompat.js';
|
|
4
|
-
export
|
|
5
|
-
export declare const
|
|
4
|
+
export type ReportLateHead = (message: string) => void;
|
|
5
|
+
export declare const LATE_HEAD_MESSAGE = "<Helmet> rendered in a boundary that completed after the shell; head tags dropped. Move it above the Suspense boundary or set server.ssrByEntries[entry].mode to 'string'.";
|
|
6
|
+
export declare const beginHeadRender: (context: object, reportLateHead: ReportLateHead) => void;
|
|
7
|
+
export declare const collectHeadRecord: <RecordType>(context: object, createRecord: () => RecordType, publish: (records: RecordType[]) => void) => RendererHeadMarkerProps | undefined;
|
|
6
8
|
export declare const collectHeadState: <RecordType, StateType>(context: object, createRecord: () => RecordType, derive: (records: RecordType[]) => StateType, target: {
|
|
7
9
|
helmet?: StateType;
|
|
8
|
-
}) => RendererHeadMarkerProps |
|
|
10
|
+
}) => RendererHeadMarkerProps | undefined;
|
|
9
11
|
export declare const publishHeadRender: (context: object) => void;
|
|
10
12
|
export declare const finishHeadRender: (context: object) => void;
|
|
11
13
|
export declare const completeHeadRender: (context: object, html: string) => string;
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"directory": "packages/runtime/runtime-extensions"
|
|
12
12
|
},
|
|
13
13
|
"license": "MIT",
|
|
14
|
-
"version": "3.9.0-ultramodern.
|
|
14
|
+
"version": "3.9.0-ultramodern.18",
|
|
15
15
|
"engines": {
|
|
16
16
|
"node": ">=26.7.0"
|
|
17
17
|
},
|
|
@@ -70,14 +70,6 @@
|
|
|
70
70
|
},
|
|
71
71
|
"default": "./dist/esm/localisedLoaderIdentity.mjs"
|
|
72
72
|
},
|
|
73
|
-
"./react-context": {
|
|
74
|
-
"types": "./dist/types/reactContext.d.ts",
|
|
75
|
-
"node": {
|
|
76
|
-
"import": "./dist/esm-node/reactContext.mjs",
|
|
77
|
-
"require": "./dist/cjs/reactContext.js"
|
|
78
|
-
},
|
|
79
|
-
"default": "./dist/esm/reactContext.mjs"
|
|
80
|
-
},
|
|
81
73
|
"./context-extensions": {
|
|
82
74
|
"types": "./dist/types/contextExtensions.d.ts",
|
|
83
75
|
"node": {
|
|
@@ -222,10 +214,6 @@
|
|
|
222
214
|
"./dist/types/localisedLoaderIdentity.d.ts",
|
|
223
215
|
"./src/localisedLoaderIdentity.ts"
|
|
224
216
|
],
|
|
225
|
-
"react-context": [
|
|
226
|
-
"./dist/types/reactContext.d.ts",
|
|
227
|
-
"./src/reactContext.ts"
|
|
228
|
-
],
|
|
229
217
|
"context-extensions": [
|
|
230
218
|
"./dist/types/contextExtensions.d.ts",
|
|
231
219
|
"./src/contextExtensions.ts"
|
|
@@ -286,7 +274,7 @@
|
|
|
286
274
|
},
|
|
287
275
|
"devDependencies": {
|
|
288
276
|
"@loadable/component": "5.16.7",
|
|
289
|
-
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.
|
|
277
|
+
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.18",
|
|
290
278
|
"@rsbuild/core": "2.2.9",
|
|
291
279
|
"@rslib/core": "1.0.2",
|
|
292
280
|
"@scripts/rstest-config": "2.66.0",
|
|
@@ -315,8 +303,8 @@
|
|
|
315
303
|
}
|
|
316
304
|
},
|
|
317
305
|
"dependencies": {
|
|
318
|
-
"@modern-js/create-request": "npm:@bleedingdev/modern-js-create-request@3.9.0-ultramodern.
|
|
319
|
-
"@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.9.0-ultramodern.
|
|
306
|
+
"@modern-js/create-request": "npm:@bleedingdev/modern-js-create-request@3.9.0-ultramodern.18",
|
|
307
|
+
"@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.9.0-ultramodern.18",
|
|
320
308
|
"react-helmet-async": "3.0.0"
|
|
321
309
|
},
|
|
322
310
|
"scripts": {
|
package/src/helmetCompat.ts
CHANGED
|
@@ -149,7 +149,7 @@ const createElementFactory = <Element>(react: ElementFactory<Element>) =>
|
|
|
149
149
|
|
|
150
150
|
export const renderHeadMarker = <Element>(
|
|
151
151
|
react: ElementFactory<Element>,
|
|
152
|
-
marker: MarkerProps |
|
|
152
|
+
marker: MarkerProps | undefined,
|
|
153
153
|
): Element | null =>
|
|
154
154
|
marker ? createElementFactory(react)('template', marker) : null;
|
|
155
155
|
|
package/src/rendererHead.ts
CHANGED
|
@@ -9,6 +9,10 @@ export type RendererHeadMarkerProps = Record<typeof MARKER_ATTRIBUTE, string>;
|
|
|
9
9
|
export * from './helmetCompat';
|
|
10
10
|
|
|
11
11
|
type PublishRecords = (records: unknown[]) => void;
|
|
12
|
+
export type ReportLateHead = (message: string) => void;
|
|
13
|
+
|
|
14
|
+
export const LATE_HEAD_MESSAGE =
|
|
15
|
+
"<Helmet> rendered in a boundary that completed after the shell; head tags dropped. Move it above the Suspense boundary or set server.ssrByEntries[entry].mode to 'string'.";
|
|
12
16
|
|
|
13
17
|
type HeadTransaction = {
|
|
14
18
|
recordsByToken: Map<string, unknown>;
|
|
@@ -18,6 +22,8 @@ type HeadTransaction = {
|
|
|
18
22
|
nonce: string;
|
|
19
23
|
previousRecords: unknown[];
|
|
20
24
|
sealed: boolean;
|
|
25
|
+
reportLateHead: ReportLateHead;
|
|
26
|
+
lateHeadReported: boolean;
|
|
21
27
|
};
|
|
22
28
|
|
|
23
29
|
type HeadState = {
|
|
@@ -48,7 +54,16 @@ const createToken = (transaction: HeadTransaction): string => {
|
|
|
48
54
|
return token;
|
|
49
55
|
};
|
|
50
56
|
|
|
51
|
-
|
|
57
|
+
const dropLateHead = (transaction: HeadTransaction): void => {
|
|
58
|
+
if (transaction.lateHeadReported) return;
|
|
59
|
+
transaction.lateHeadReported = true;
|
|
60
|
+
transaction.reportLateHead(LATE_HEAD_MESSAGE);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const beginHeadRender = (
|
|
64
|
+
context: object,
|
|
65
|
+
reportLateHead: ReportLateHead,
|
|
66
|
+
): void => {
|
|
52
67
|
const state = getState(context);
|
|
53
68
|
state.transaction = {
|
|
54
69
|
recordsByToken: new Map(),
|
|
@@ -58,6 +73,8 @@ export const beginHeadRender = (context: object): void => {
|
|
|
58
73
|
nonce: globalThis.crypto.randomUUID(),
|
|
59
74
|
previousRecords: state.publishedRecords,
|
|
60
75
|
sealed: false,
|
|
76
|
+
reportLateHead,
|
|
77
|
+
lateHeadReported: false,
|
|
61
78
|
};
|
|
62
79
|
state.publish?.([]);
|
|
63
80
|
};
|
|
@@ -66,7 +83,7 @@ export const collectHeadRecord = <RecordType>(
|
|
|
66
83
|
context: object,
|
|
67
84
|
createRecord: () => RecordType,
|
|
68
85
|
publish: (records: RecordType[]) => void,
|
|
69
|
-
): RendererHeadMarkerProps |
|
|
86
|
+
): RendererHeadMarkerProps | undefined => {
|
|
70
87
|
const state = getState(context);
|
|
71
88
|
const transaction = state.transaction;
|
|
72
89
|
if (transaction === undefined) {
|
|
@@ -78,10 +95,8 @@ export const collectHeadRecord = <RecordType>(
|
|
|
78
95
|
if (!hadPublisher) {
|
|
79
96
|
state.publish([]);
|
|
80
97
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
|
|
98
|
+
// A Helmet rendered after the seal still gets a marker: it is dropped, and
|
|
99
|
+
// reported, only if its boundary actually commits to the output.
|
|
85
100
|
const token = createToken(transaction);
|
|
86
101
|
transaction.recordsByToken.set(token, createRecord());
|
|
87
102
|
return { [MARKER_ATTRIBUTE]: token };
|
|
@@ -92,7 +107,7 @@ export const collectHeadState = <RecordType, StateType>(
|
|
|
92
107
|
createRecord: () => RecordType,
|
|
93
108
|
derive: (records: RecordType[]) => StateType,
|
|
94
109
|
target: { helmet?: StateType },
|
|
95
|
-
): RendererHeadMarkerProps |
|
|
110
|
+
): RendererHeadMarkerProps | undefined =>
|
|
96
111
|
collectHeadRecord(context, createRecord, records => {
|
|
97
112
|
target.helmet = derive(records);
|
|
98
113
|
});
|
|
@@ -123,7 +138,9 @@ const consumeMarkers = (context: object, html: string): string => {
|
|
|
123
138
|
stripped += html.slice(cursor, end + MARKER_SUFFIX.length);
|
|
124
139
|
} else {
|
|
125
140
|
stripped += html.slice(cursor, start);
|
|
126
|
-
if (
|
|
141
|
+
if (transaction.sealed) {
|
|
142
|
+
if (!transaction.committedTokens.has(token)) dropLateHead(transaction);
|
|
143
|
+
} else if (!transaction.committedTokens.has(token)) {
|
|
127
144
|
transaction.committedTokens.add(token);
|
|
128
145
|
transaction.committedRecords.push(
|
|
129
146
|
transaction.recordsByToken.get(token),
|
|
@@ -131,7 +131,7 @@ const shouldRetry = (
|
|
|
131
131
|
if (typeof retryOptions?.shouldRetry === 'function') {
|
|
132
132
|
try {
|
|
133
133
|
return retryOptions.shouldRetry(context);
|
|
134
|
-
} catch
|
|
134
|
+
} catch {
|
|
135
135
|
return false;
|
|
136
136
|
}
|
|
137
137
|
}
|
|
@@ -176,7 +176,7 @@ const emitDegradedEvent = (
|
|
|
176
176
|
|
|
177
177
|
try {
|
|
178
178
|
transport.onDegraded(event);
|
|
179
|
-
} catch
|
|
179
|
+
} catch {
|
|
180
180
|
// best-effort telemetry only
|
|
181
181
|
}
|
|
182
182
|
};
|
|
@@ -10,7 +10,7 @@ const getHelmetData = (context: object) => getHelmetContext(context)?.helmet;
|
|
|
10
10
|
const createServerContext = () => ({ isBrowser: false }) as any;
|
|
11
11
|
|
|
12
12
|
const renderWithContext = (context: any, node: React.ReactNode) => {
|
|
13
|
-
rendererHead.beginHeadRender(context);
|
|
13
|
+
rendererHead.beginHeadRender(context, () => {});
|
|
14
14
|
try {
|
|
15
15
|
const html = renderToString(
|
|
16
16
|
wrapServerRoot(node, {
|
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
collectHeadRecord,
|
|
6
6
|
completeHeadRender,
|
|
7
7
|
createHeadChunkProcessor,
|
|
8
|
+
LATE_HEAD_MESSAGE,
|
|
9
|
+
publishHeadRender,
|
|
8
10
|
} from '../src';
|
|
9
11
|
import { createNodeHeadMarkerStripper, pipeNodeHeadStream } from '../src/node';
|
|
10
12
|
|
|
@@ -15,7 +17,7 @@ describe('renderer head transactions', () => {
|
|
|
15
17
|
it('commits only markers observed in completed output', () => {
|
|
16
18
|
const context = {};
|
|
17
19
|
let published: string[] = [];
|
|
18
|
-
beginHeadRender(context);
|
|
20
|
+
beginHeadRender(context, () => {});
|
|
19
21
|
const committed = collectHeadRecord(
|
|
20
22
|
context,
|
|
21
23
|
() => 'committed',
|
|
@@ -35,9 +37,50 @@ describe('renderer head transactions', () => {
|
|
|
35
37
|
expect(published).toEqual(['committed']);
|
|
36
38
|
});
|
|
37
39
|
|
|
40
|
+
it('reports head records whose markers commit after the seal', () => {
|
|
41
|
+
const context = {};
|
|
42
|
+
const reports: string[] = [];
|
|
43
|
+
let published: string[] = [];
|
|
44
|
+
const publish = (records: string[]) => {
|
|
45
|
+
published = records;
|
|
46
|
+
};
|
|
47
|
+
beginHeadRender(context, message => reports.push(message));
|
|
48
|
+
const shell = collectHeadRecord(context, () => 'shell', publish)!;
|
|
49
|
+
// A boundary rendered before the seal but flushed after it.
|
|
50
|
+
const flushedLate = collectHeadRecord(context, () => 'flushed', publish)!;
|
|
51
|
+
const processor = createHeadChunkProcessor(context);
|
|
52
|
+
expect(processor.push(`a${marker(shell)}`)).toBe('a');
|
|
53
|
+
publishHeadRender(context);
|
|
54
|
+
// A boundary rendered after the seal.
|
|
55
|
+
const renderedLate = collectHeadRecord(context, () => 'rendered', publish)!;
|
|
56
|
+
// A retry that renders a Helmet and then suspends again never commits.
|
|
57
|
+
collectHeadRecord(context, () => 'discarded', publish);
|
|
58
|
+
expect(reports).toEqual([]);
|
|
59
|
+
|
|
60
|
+
expect(processor.push(`b${marker(flushedLate)}${marker(shell)}`)).toBe('b');
|
|
61
|
+
expect(processor.finish(`c${marker(renderedLate)}`)).toBe('c');
|
|
62
|
+
|
|
63
|
+
expect(published).toEqual(['shell']);
|
|
64
|
+
expect(reports).toEqual([LATE_HEAD_MESSAGE]);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('does not report a late Helmet whose boundary never commits', () => {
|
|
68
|
+
const context = {};
|
|
69
|
+
const reports: string[] = [];
|
|
70
|
+
beginHeadRender(context, message => reports.push(message));
|
|
71
|
+
publishHeadRender(context);
|
|
72
|
+
collectHeadRecord(
|
|
73
|
+
context,
|
|
74
|
+
() => 'discarded',
|
|
75
|
+
() => {},
|
|
76
|
+
);
|
|
77
|
+
expect(completeHeadRender(context, 'done')).toBe('done');
|
|
78
|
+
expect(reports).toEqual([]);
|
|
79
|
+
});
|
|
80
|
+
|
|
38
81
|
it('preserves user templates that are not current transaction markers', () => {
|
|
39
82
|
const context = {};
|
|
40
|
-
beginHeadRender(context);
|
|
83
|
+
beginHeadRender(context, () => {});
|
|
41
84
|
const userTemplate =
|
|
42
85
|
'<template data-modern-helmet="h00000000-0000-0000-0000-000000000000000000000000"></template>';
|
|
43
86
|
|
|
@@ -46,7 +89,7 @@ describe('renderer head transactions', () => {
|
|
|
46
89
|
|
|
47
90
|
it('strips markers split across chunks without corrupting unicode', () => {
|
|
48
91
|
const sampleContext = {};
|
|
49
|
-
beginHeadRender(sampleContext);
|
|
92
|
+
beginHeadRender(sampleContext, () => {});
|
|
50
93
|
const sampleProps = collectHeadRecord(
|
|
51
94
|
sampleContext,
|
|
52
95
|
() => 'head',
|
|
@@ -56,7 +99,7 @@ describe('renderer head transactions', () => {
|
|
|
56
99
|
|
|
57
100
|
for (let split = 1; split < sampleHtml.length; split += 1) {
|
|
58
101
|
const context = {};
|
|
59
|
-
beginHeadRender(context);
|
|
102
|
+
beginHeadRender(context, () => {});
|
|
60
103
|
const props = collectHeadRecord(
|
|
61
104
|
context,
|
|
62
105
|
() => 'head',
|
|
@@ -78,12 +121,12 @@ describe('renderer head transactions', () => {
|
|
|
78
121
|
published = records;
|
|
79
122
|
};
|
|
80
123
|
|
|
81
|
-
beginHeadRender(context);
|
|
124
|
+
beginHeadRender(context, () => {});
|
|
82
125
|
const previous = collectHeadRecord(context, () => 'previous', publish)!;
|
|
83
126
|
completeHeadRender(context, marker(previous));
|
|
84
127
|
expect(published).toEqual(['previous']);
|
|
85
128
|
|
|
86
|
-
beginHeadRender(context);
|
|
129
|
+
beginHeadRender(context, () => {});
|
|
87
130
|
expect(published).toEqual([]);
|
|
88
131
|
collectHeadRecord(context, () => 'provisional', publish);
|
|
89
132
|
abortHeadRender(context);
|
|
@@ -93,7 +136,7 @@ describe('renderer head transactions', () => {
|
|
|
93
136
|
it('strips split markers from a Node stream', async () => {
|
|
94
137
|
const context = {};
|
|
95
138
|
let published: string[] = [];
|
|
96
|
-
beginHeadRender(context);
|
|
139
|
+
beginHeadRender(context, () => {});
|
|
97
140
|
const props = collectHeadRecord(
|
|
98
141
|
context,
|
|
99
142
|
() => 'node',
|
|
@@ -127,7 +170,7 @@ describe('renderer head transactions', () => {
|
|
|
127
170
|
const prefix = Buffer.from('x'.repeat(9 * 1024));
|
|
128
171
|
const unicodeTail = Buffer.from('č尾');
|
|
129
172
|
const input = Buffer.concat([prefix, unicodeTail]);
|
|
130
|
-
beginHeadRender(context);
|
|
173
|
+
beginHeadRender(context, () => {});
|
|
131
174
|
const stripper = createNodeHeadMarkerStripper(context, terminalMarker);
|
|
132
175
|
const output = new Promise<string>((resolve, reject) => {
|
|
133
176
|
let rendered = '';
|
|
@@ -152,7 +195,7 @@ describe('renderer head transactions', () => {
|
|
|
152
195
|
const terminalMarker = '<!-- shell stream end -->';
|
|
153
196
|
const input = Buffer.from(`shell${terminalMarker}č尾`);
|
|
154
197
|
const markerOffset = Buffer.byteLength('shell') + 7;
|
|
155
|
-
beginHeadRender(context);
|
|
198
|
+
beginHeadRender(context, () => {});
|
|
156
199
|
const stripper = createNodeHeadMarkerStripper(context, terminalMarker);
|
|
157
200
|
const output = new Promise<string>((resolve, reject) => {
|
|
158
201
|
let rendered = '';
|
|
@@ -62,10 +62,10 @@ describe('conserving web shell stream', () => {
|
|
|
62
62
|
const publish = (records: string[]) => {
|
|
63
63
|
published = records;
|
|
64
64
|
};
|
|
65
|
-
beginHeadRender(context);
|
|
65
|
+
beginHeadRender(context, () => {});
|
|
66
66
|
const previous = collectHeadRecord(context, () => 'previous', publish)!;
|
|
67
67
|
completeHeadRender(context, headMarker(previous));
|
|
68
|
-
beginHeadRender(context);
|
|
68
|
+
beginHeadRender(context, () => {});
|
|
69
69
|
collectHeadRecord(context, () => 'incomplete', publish);
|
|
70
70
|
const bytes = encoder.encode(`α${'x'.repeat(9 * 1024)}尾`);
|
|
71
71
|
const stream = createConservingWebShellStream(
|
package/dist/cjs/reactContext.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __webpack_require__ = {};
|
|
3
|
-
(()=>{
|
|
4
|
-
__webpack_require__.d = (exports1, getters, values)=>{
|
|
5
|
-
var define = (defs, kind)=>{
|
|
6
|
-
for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
7
|
-
enumerable: true,
|
|
8
|
-
[kind]: defs[key]
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
define(getters, "get");
|
|
12
|
-
define(values, "value");
|
|
13
|
-
};
|
|
14
|
-
})();
|
|
15
|
-
(()=>{
|
|
16
|
-
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
17
|
-
})();
|
|
18
|
-
(()=>{
|
|
19
|
-
__webpack_require__.r = (exports1)=>{
|
|
20
|
-
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
21
|
-
value: 'Module'
|
|
22
|
-
});
|
|
23
|
-
Object.defineProperty(exports1, '__esModule', {
|
|
24
|
-
value: true
|
|
25
|
-
});
|
|
26
|
-
};
|
|
27
|
-
})();
|
|
28
|
-
var __webpack_exports__ = {};
|
|
29
|
-
__webpack_require__.r(__webpack_exports__);
|
|
30
|
-
function getRuntimeReactContext(kind, create) {
|
|
31
|
-
const key = Symbol.for(`@modern-js/runtime:react-context:v1:${kind}`);
|
|
32
|
-
const contexts = globalThis;
|
|
33
|
-
contexts[key] ??= create();
|
|
34
|
-
return contexts[key];
|
|
35
|
-
}
|
|
36
|
-
__webpack_require__.d(__webpack_exports__, {
|
|
37
|
-
getRuntimeReactContext: ()=>getRuntimeReactContext
|
|
38
|
-
});
|
|
39
|
-
exports.getRuntimeReactContext = __webpack_exports__.getRuntimeReactContext;
|
|
40
|
-
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
41
|
-
"getRuntimeReactContext"
|
|
42
|
-
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
43
|
-
Object.defineProperty(exports, '__esModule', {
|
|
44
|
-
value: true
|
|
45
|
-
});
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import "node:module";
|
|
2
|
-
function getRuntimeReactContext(kind, create) {
|
|
3
|
-
const key = Symbol.for(`@modern-js/runtime:react-context:v1:${kind}`);
|
|
4
|
-
const contexts = globalThis;
|
|
5
|
-
contexts[key] ??= create();
|
|
6
|
-
return contexts[key];
|
|
7
|
-
}
|
|
8
|
-
export { getRuntimeReactContext };
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Federation can bundle a runtime subpath separately from its shared root.
|
|
3
|
-
* Share React context identity across those copies, leaving request values to
|
|
4
|
-
* React's providers. Public and internal contexts retain separate identities.
|
|
5
|
-
*/
|
|
6
|
-
export declare function getRuntimeReactContext<T>(kind: 'public' | 'internal', create: () => T): T;
|
package/src/reactContext.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Federation can bundle a runtime subpath separately from its shared root.
|
|
3
|
-
* Share React context identity across those copies, leaving request values to
|
|
4
|
-
* React's providers. Public and internal contexts retain separate identities.
|
|
5
|
-
*/
|
|
6
|
-
export function getRuntimeReactContext<T>(
|
|
7
|
-
kind: 'public' | 'internal',
|
|
8
|
-
create: () => T,
|
|
9
|
-
): T {
|
|
10
|
-
const key = Symbol.for(`@modern-js/runtime:react-context:v1:${kind}`);
|
|
11
|
-
const contexts = globalThis as typeof globalThis & {
|
|
12
|
-
[key: symbol]: unknown;
|
|
13
|
-
};
|
|
14
|
-
contexts[key] ??= create();
|
|
15
|
-
return contexts[key] as T;
|
|
16
|
-
}
|