hadars 0.1.17 → 0.1.19
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/chunk-OS3V4CPN.js +42 -0
- package/dist/cli.js +777 -143
- package/dist/index.cjs +61 -6
- package/dist/index.d.ts +40 -1
- package/dist/index.js +58 -6
- package/dist/jsx-runtime-97ca74a5.d.ts +18 -0
- package/dist/slim-react/index.cjs +1001 -0
- package/dist/slim-react/index.d.ts +180 -0
- package/dist/slim-react/index.js +911 -0
- package/dist/slim-react/jsx-runtime.cjs +52 -0
- package/dist/slim-react/jsx-runtime.d.ts +1 -0
- package/dist/slim-react/jsx-runtime.js +10 -0
- package/dist/ssr-render-worker.js +740 -108
- package/dist/ssr-watch.js +34 -13
- package/dist/utils/Head.tsx +3 -6
- package/index.ts +1 -1
- package/package.json +3 -3
- package/src/build.ts +6 -23
- package/src/components/CacheSegment.tsx +67 -0
- package/src/index.tsx +2 -0
- package/src/slim-react/context.ts +52 -0
- package/src/slim-react/hooks.ts +137 -0
- package/src/slim-react/index.ts +225 -0
- package/src/slim-react/jsx-runtime.ts +7 -0
- package/src/slim-react/jsx.ts +53 -0
- package/src/slim-react/render.ts +863 -0
- package/src/slim-react/renderContext.ts +105 -0
- package/src/slim-react/types.ts +33 -0
- package/src/ssr-render-worker.ts +83 -118
- package/src/utils/Head.tsx +3 -6
- package/src/utils/response.tsx +42 -105
- package/src/utils/rspack.ts +42 -15
- package/src/utils/segmentCache.ts +87 -0
package/dist/cli.js
CHANGED
|
@@ -134,24 +134,630 @@ var upgradeHandler = (options) => {
|
|
|
134
134
|
return null;
|
|
135
135
|
};
|
|
136
136
|
|
|
137
|
-
// src/
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
|
|
137
|
+
// src/slim-react/types.ts
|
|
138
|
+
var SLIM_ELEMENT = Symbol.for("react.element");
|
|
139
|
+
var REACT19_ELEMENT = Symbol.for("react.transitional.element");
|
|
140
|
+
var FRAGMENT_TYPE = Symbol.for("react.fragment");
|
|
141
|
+
var SUSPENSE_TYPE = Symbol.for("react.suspense");
|
|
142
|
+
|
|
143
|
+
// src/slim-react/jsx.ts
|
|
144
|
+
var Fragment = FRAGMENT_TYPE;
|
|
145
|
+
function createElement(type, props, ...children) {
|
|
146
|
+
const normalizedProps = { ...props || {} };
|
|
147
|
+
if (children.length === 1) {
|
|
148
|
+
normalizedProps.children = children[0];
|
|
149
|
+
} else if (children.length > 1) {
|
|
150
|
+
normalizedProps.children = children;
|
|
151
|
+
}
|
|
152
|
+
const key = normalizedProps.key ?? null;
|
|
153
|
+
delete normalizedProps.key;
|
|
154
|
+
return {
|
|
155
|
+
$$typeof: SLIM_ELEMENT,
|
|
156
|
+
type,
|
|
157
|
+
props: normalizedProps,
|
|
158
|
+
key
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// src/slim-react/renderContext.ts
|
|
163
|
+
var GLOBAL_KEY = "__slimReactRenderState";
|
|
164
|
+
var EMPTY = { id: 0, overflow: "", bits: 0 };
|
|
165
|
+
function s() {
|
|
166
|
+
const g = globalThis;
|
|
167
|
+
if (!g[GLOBAL_KEY]) {
|
|
168
|
+
g[GLOBAL_KEY] = { currentTreeContext: { ...EMPTY }, localIdCounter: 0, idPrefix: "" };
|
|
169
|
+
}
|
|
170
|
+
return g[GLOBAL_KEY];
|
|
171
|
+
}
|
|
172
|
+
function resetRenderState() {
|
|
173
|
+
const st = s();
|
|
174
|
+
st.currentTreeContext = { ...EMPTY };
|
|
175
|
+
st.localIdCounter = 0;
|
|
176
|
+
}
|
|
177
|
+
function pushTreeContext(totalChildren, index) {
|
|
178
|
+
const st = s();
|
|
179
|
+
const saved = { ...st.currentTreeContext };
|
|
180
|
+
const pendingBits = 32 - Math.clz32(totalChildren);
|
|
181
|
+
const slot = index + 1;
|
|
182
|
+
const totalBits = st.currentTreeContext.bits + pendingBits;
|
|
183
|
+
if (totalBits <= 30) {
|
|
184
|
+
st.currentTreeContext = {
|
|
185
|
+
id: st.currentTreeContext.id << pendingBits | slot,
|
|
186
|
+
overflow: st.currentTreeContext.overflow,
|
|
187
|
+
bits: totalBits
|
|
188
|
+
};
|
|
189
|
+
} else {
|
|
190
|
+
let newOverflow = st.currentTreeContext.overflow;
|
|
191
|
+
if (st.currentTreeContext.bits > 0)
|
|
192
|
+
newOverflow += st.currentTreeContext.id.toString(32);
|
|
193
|
+
st.currentTreeContext = { id: 1 << pendingBits | slot, overflow: newOverflow, bits: pendingBits };
|
|
194
|
+
}
|
|
195
|
+
return saved;
|
|
196
|
+
}
|
|
197
|
+
function popTreeContext(saved) {
|
|
198
|
+
s().currentTreeContext = saved;
|
|
199
|
+
}
|
|
200
|
+
function pushComponentScope() {
|
|
201
|
+
const st = s();
|
|
202
|
+
const saved = st.localIdCounter;
|
|
203
|
+
st.localIdCounter = 0;
|
|
204
|
+
return saved;
|
|
205
|
+
}
|
|
206
|
+
function popComponentScope(saved) {
|
|
207
|
+
s().localIdCounter = saved;
|
|
208
|
+
}
|
|
209
|
+
function snapshotContext() {
|
|
210
|
+
const st = s();
|
|
211
|
+
return { tree: { ...st.currentTreeContext }, localId: st.localIdCounter };
|
|
212
|
+
}
|
|
213
|
+
function restoreContext(snap) {
|
|
214
|
+
const st = s();
|
|
215
|
+
st.currentTreeContext = { ...snap.tree };
|
|
216
|
+
st.localIdCounter = snap.localId;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// src/slim-react/render.ts
|
|
220
|
+
var VOID_ELEMENTS = /* @__PURE__ */ new Set([
|
|
221
|
+
"area",
|
|
222
|
+
"base",
|
|
223
|
+
"br",
|
|
224
|
+
"col",
|
|
225
|
+
"embed",
|
|
226
|
+
"hr",
|
|
227
|
+
"img",
|
|
228
|
+
"input",
|
|
229
|
+
"link",
|
|
230
|
+
"meta",
|
|
231
|
+
"param",
|
|
232
|
+
"source",
|
|
233
|
+
"track",
|
|
234
|
+
"wbr"
|
|
235
|
+
]);
|
|
236
|
+
function escapeHtml(str) {
|
|
237
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/'/g, "'");
|
|
238
|
+
}
|
|
239
|
+
function escapeAttr(str) {
|
|
240
|
+
return str.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
241
|
+
}
|
|
242
|
+
function styleObjectToString(style) {
|
|
243
|
+
return Object.entries(style).map(([key, value]) => {
|
|
244
|
+
const cssKey = key.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
|
|
245
|
+
return `${cssKey}:${value}`;
|
|
246
|
+
}).join(";");
|
|
247
|
+
}
|
|
248
|
+
var SVG_ATTR_MAP = {
|
|
249
|
+
// Presentation / geometry
|
|
250
|
+
accentHeight: "accent-height",
|
|
251
|
+
alignmentBaseline: "alignment-baseline",
|
|
252
|
+
arabicForm: "arabic-form",
|
|
253
|
+
baselineShift: "baseline-shift",
|
|
254
|
+
capHeight: "cap-height",
|
|
255
|
+
clipPath: "clip-path",
|
|
256
|
+
clipRule: "clip-rule",
|
|
257
|
+
colorInterpolation: "color-interpolation",
|
|
258
|
+
colorInterpolationFilters: "color-interpolation-filters",
|
|
259
|
+
colorProfile: "color-profile",
|
|
260
|
+
dominantBaseline: "dominant-baseline",
|
|
261
|
+
enableBackground: "enable-background",
|
|
262
|
+
fillOpacity: "fill-opacity",
|
|
263
|
+
fillRule: "fill-rule",
|
|
264
|
+
floodColor: "flood-color",
|
|
265
|
+
floodOpacity: "flood-opacity",
|
|
266
|
+
fontFamily: "font-family",
|
|
267
|
+
fontSize: "font-size",
|
|
268
|
+
fontSizeAdjust: "font-size-adjust",
|
|
269
|
+
fontStretch: "font-stretch",
|
|
270
|
+
fontStyle: "font-style",
|
|
271
|
+
fontVariant: "font-variant",
|
|
272
|
+
fontWeight: "font-weight",
|
|
273
|
+
glyphName: "glyph-name",
|
|
274
|
+
glyphOrientationHorizontal: "glyph-orientation-horizontal",
|
|
275
|
+
glyphOrientationVertical: "glyph-orientation-vertical",
|
|
276
|
+
horizAdvX: "horiz-adv-x",
|
|
277
|
+
horizOriginX: "horiz-origin-x",
|
|
278
|
+
imageRendering: "image-rendering",
|
|
279
|
+
letterSpacing: "letter-spacing",
|
|
280
|
+
lightingColor: "lighting-color",
|
|
281
|
+
markerEnd: "marker-end",
|
|
282
|
+
markerMid: "marker-mid",
|
|
283
|
+
markerStart: "marker-start",
|
|
284
|
+
overlinePosition: "overline-position",
|
|
285
|
+
overlineThickness: "overline-thickness",
|
|
286
|
+
paintOrder: "paint-order",
|
|
287
|
+
panose1: "panose-1",
|
|
288
|
+
pointerEvents: "pointer-events",
|
|
289
|
+
renderingIntent: "rendering-intent",
|
|
290
|
+
shapeRendering: "shape-rendering",
|
|
291
|
+
stopColor: "stop-color",
|
|
292
|
+
stopOpacity: "stop-opacity",
|
|
293
|
+
strikethroughPosition: "strikethrough-position",
|
|
294
|
+
strikethroughThickness: "strikethrough-thickness",
|
|
295
|
+
strokeDasharray: "stroke-dasharray",
|
|
296
|
+
strokeDashoffset: "stroke-dashoffset",
|
|
297
|
+
strokeLinecap: "stroke-linecap",
|
|
298
|
+
strokeLinejoin: "stroke-linejoin",
|
|
299
|
+
strokeMiterlimit: "stroke-miterlimit",
|
|
300
|
+
strokeOpacity: "stroke-opacity",
|
|
301
|
+
strokeWidth: "stroke-width",
|
|
302
|
+
textAnchor: "text-anchor",
|
|
303
|
+
textDecoration: "text-decoration",
|
|
304
|
+
textRendering: "text-rendering",
|
|
305
|
+
underlinePosition: "underline-position",
|
|
306
|
+
underlineThickness: "underline-thickness",
|
|
307
|
+
unicodeBidi: "unicode-bidi",
|
|
308
|
+
unicodeRange: "unicode-range",
|
|
309
|
+
unitsPerEm: "units-per-em",
|
|
310
|
+
vAlphabetic: "v-alphabetic",
|
|
311
|
+
vHanging: "v-hanging",
|
|
312
|
+
vIdeographic: "v-ideographic",
|
|
313
|
+
vMathematical: "v-mathematical",
|
|
314
|
+
vertAdvY: "vert-adv-y",
|
|
315
|
+
vertOriginX: "vert-origin-x",
|
|
316
|
+
vertOriginY: "vert-origin-y",
|
|
317
|
+
wordSpacing: "word-spacing",
|
|
318
|
+
writingMode: "writing-mode",
|
|
319
|
+
xHeight: "x-height",
|
|
320
|
+
// Namespace-prefixed
|
|
321
|
+
xlinkActuate: "xlink:actuate",
|
|
322
|
+
xlinkArcrole: "xlink:arcrole",
|
|
323
|
+
xlinkHref: "xlink:href",
|
|
324
|
+
xlinkRole: "xlink:role",
|
|
325
|
+
xlinkShow: "xlink:show",
|
|
326
|
+
xlinkTitle: "xlink:title",
|
|
327
|
+
xlinkType: "xlink:type",
|
|
328
|
+
xmlBase: "xml:base",
|
|
329
|
+
xmlLang: "xml:lang",
|
|
330
|
+
xmlSpace: "xml:space",
|
|
331
|
+
xmlns: "xmlns",
|
|
332
|
+
xmlnsXlink: "xmlns:xlink",
|
|
333
|
+
// Filter / lighting
|
|
334
|
+
baseFrequency: "baseFrequency",
|
|
335
|
+
colorInterpolation_filters: "color-interpolation-filters",
|
|
336
|
+
diffuseConstant: "diffuseConstant",
|
|
337
|
+
edgeMode: "edgeMode",
|
|
338
|
+
filterUnits: "filterUnits",
|
|
339
|
+
gradientTransform: "gradientTransform",
|
|
340
|
+
gradientUnits: "gradientUnits",
|
|
341
|
+
kernelMatrix: "kernelMatrix",
|
|
342
|
+
kernelUnitLength: "kernelUnitLength",
|
|
343
|
+
lengthAdjust: "lengthAdjust",
|
|
344
|
+
limitingConeAngle: "limitingConeAngle",
|
|
345
|
+
markerHeight: "markerHeight",
|
|
346
|
+
markerWidth: "markerWidth",
|
|
347
|
+
maskContentUnits: "maskContentUnits",
|
|
348
|
+
maskUnits: "maskUnits",
|
|
349
|
+
numOctaves: "numOctaves",
|
|
350
|
+
pathLength: "pathLength",
|
|
351
|
+
patternContentUnits: "patternContentUnits",
|
|
352
|
+
patternTransform: "patternTransform",
|
|
353
|
+
patternUnits: "patternUnits",
|
|
354
|
+
pointsAtX: "pointsAtX",
|
|
355
|
+
pointsAtY: "pointsAtY",
|
|
356
|
+
pointsAtZ: "pointsAtZ",
|
|
357
|
+
preserveAspectRatio: "preserveAspectRatio",
|
|
358
|
+
primitiveUnits: "primitiveUnits",
|
|
359
|
+
refX: "refX",
|
|
360
|
+
refY: "refY",
|
|
361
|
+
repeatCount: "repeatCount",
|
|
362
|
+
repeatDur: "repeatDur",
|
|
363
|
+
specularConstant: "specularConstant",
|
|
364
|
+
specularExponent: "specularExponent",
|
|
365
|
+
spreadMethod: "spreadMethod",
|
|
366
|
+
startOffset: "startOffset",
|
|
367
|
+
stdDeviation: "stdDeviation",
|
|
368
|
+
stitchTiles: "stitchTiles",
|
|
369
|
+
surfaceScale: "surfaceScale",
|
|
370
|
+
systemLanguage: "systemLanguage",
|
|
371
|
+
tableValues: "tableValues",
|
|
372
|
+
targetX: "targetX",
|
|
373
|
+
targetY: "targetY",
|
|
374
|
+
textLength: "textLength",
|
|
375
|
+
viewBox: "viewBox",
|
|
376
|
+
xChannelSelector: "xChannelSelector",
|
|
377
|
+
yChannelSelector: "yChannelSelector"
|
|
378
|
+
};
|
|
379
|
+
function renderAttributes(props, isSvg) {
|
|
380
|
+
let attrs = "";
|
|
381
|
+
for (const [key, value] of Object.entries(props)) {
|
|
382
|
+
if (key === "children" || key === "key" || key === "ref" || key === "dangerouslySetInnerHTML" || key === "suppressHydrationWarning" || key === "suppressContentEditableWarning")
|
|
383
|
+
continue;
|
|
384
|
+
if (key.startsWith("on") && key.length > 2 && key[2] === key[2].toUpperCase())
|
|
385
|
+
continue;
|
|
386
|
+
let attrName;
|
|
387
|
+
if (isSvg && key in SVG_ATTR_MAP) {
|
|
388
|
+
attrName = SVG_ATTR_MAP[key];
|
|
389
|
+
} else {
|
|
390
|
+
attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key === "tabIndex" ? "tabindex" : key === "defaultValue" ? "value" : key === "defaultChecked" ? "checked" : key;
|
|
391
|
+
}
|
|
392
|
+
if (value === false || value == null) {
|
|
393
|
+
if (value === false && (attrName.startsWith("aria-") || attrName.startsWith("data-"))) {
|
|
394
|
+
attrs += ` ${attrName}="false"`;
|
|
395
|
+
}
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
if (value === true) {
|
|
399
|
+
attrs += ` ${attrName}=""`;
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
if (key === "style" && typeof value === "object") {
|
|
403
|
+
attrs += ` style="${escapeAttr(styleObjectToString(value))}"`;
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
attrs += ` ${attrName}="${escapeAttr(String(value))}"`;
|
|
407
|
+
}
|
|
408
|
+
return attrs;
|
|
409
|
+
}
|
|
410
|
+
var BufferWriter = class {
|
|
411
|
+
chunks = [];
|
|
412
|
+
lastWasText = false;
|
|
413
|
+
write(chunk) {
|
|
414
|
+
this.chunks.push(chunk);
|
|
415
|
+
this.lastWasText = false;
|
|
416
|
+
}
|
|
417
|
+
text(s2) {
|
|
418
|
+
this.chunks.push(s2);
|
|
419
|
+
this.lastWasText = true;
|
|
420
|
+
}
|
|
421
|
+
flush(target) {
|
|
422
|
+
for (const c of this.chunks)
|
|
423
|
+
target.write(c);
|
|
424
|
+
target.lastWasText = this.lastWasText;
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
function renderNode(node, writer, isSvg = false) {
|
|
428
|
+
if (node == null || typeof node === "boolean")
|
|
429
|
+
return;
|
|
430
|
+
if (typeof node === "string") {
|
|
431
|
+
writer.text(escapeHtml(node));
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
if (typeof node === "number") {
|
|
435
|
+
writer.text(String(node));
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
if (Array.isArray(node)) {
|
|
439
|
+
return renderChildArray(node, writer, isSvg);
|
|
440
|
+
}
|
|
441
|
+
if (typeof node === "object" && node !== null && Symbol.iterator in node && !("$$typeof" in node)) {
|
|
442
|
+
return renderChildArray(
|
|
443
|
+
Array.from(node),
|
|
444
|
+
writer,
|
|
445
|
+
isSvg
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
if (typeof node === "object" && node !== null && "$$typeof" in node) {
|
|
449
|
+
const elType = node["$$typeof"];
|
|
450
|
+
if (elType !== SLIM_ELEMENT && elType !== REACT19_ELEMENT)
|
|
451
|
+
return;
|
|
452
|
+
const element = node;
|
|
453
|
+
const { type, props } = element;
|
|
454
|
+
if (type === FRAGMENT_TYPE) {
|
|
455
|
+
return renderChildren(props.children, writer, isSvg);
|
|
456
|
+
}
|
|
457
|
+
if (type === SUSPENSE_TYPE) {
|
|
458
|
+
return renderSuspense(props, writer, isSvg);
|
|
459
|
+
}
|
|
460
|
+
if (typeof type === "function") {
|
|
461
|
+
return renderComponent(type, props, writer, isSvg);
|
|
462
|
+
}
|
|
463
|
+
if (typeof type === "object" && type !== null) {
|
|
464
|
+
return renderComponent(type, props, writer, isSvg);
|
|
465
|
+
}
|
|
466
|
+
if (typeof type === "string") {
|
|
467
|
+
return renderHostElement(type, props, writer, isSvg);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
function markSelectedOptionsMulti(children, selectedValues) {
|
|
472
|
+
if (children == null || typeof children === "boolean")
|
|
473
|
+
return children;
|
|
474
|
+
if (typeof children === "string" || typeof children === "number")
|
|
475
|
+
return children;
|
|
476
|
+
if (Array.isArray(children)) {
|
|
477
|
+
return children.map((c) => markSelectedOptionsMulti(c, selectedValues));
|
|
478
|
+
}
|
|
479
|
+
if (typeof children === "object" && "$$typeof" in children) {
|
|
480
|
+
const elType = children["$$typeof"];
|
|
481
|
+
if (elType !== SLIM_ELEMENT && elType !== REACT19_ELEMENT)
|
|
482
|
+
return children;
|
|
483
|
+
const el = children;
|
|
484
|
+
if (el.type === "option") {
|
|
485
|
+
const optValue = el.props.value !== void 0 ? el.props.value : el.props.children;
|
|
486
|
+
const isSelected = selectedValues.has(String(optValue));
|
|
487
|
+
return { ...el, props: { ...el.props, selected: isSelected || void 0 } };
|
|
488
|
+
}
|
|
489
|
+
if (el.type === "optgroup" || el.type === FRAGMENT_TYPE) {
|
|
490
|
+
const newChildren = markSelectedOptionsMulti(el.props.children, selectedValues);
|
|
491
|
+
return { ...el, props: { ...el.props, children: newChildren } };
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
return children;
|
|
495
|
+
}
|
|
496
|
+
function renderHostElement(tag, props, writer, isSvg) {
|
|
497
|
+
const enteringSvg = tag === "svg";
|
|
498
|
+
const childSvg = isSvg || enteringSvg;
|
|
499
|
+
if (tag === "textarea") {
|
|
500
|
+
const textContent = props.value ?? props.defaultValue ?? props.children ?? "";
|
|
501
|
+
const filteredProps = {};
|
|
502
|
+
for (const k of Object.keys(props)) {
|
|
503
|
+
if (k !== "value" && k !== "defaultValue" && k !== "children")
|
|
504
|
+
filteredProps[k] = props[k];
|
|
505
|
+
}
|
|
506
|
+
writer.write(`<textarea${renderAttributes(filteredProps, false)}>`);
|
|
507
|
+
writer.text(escapeHtml(String(textContent)));
|
|
508
|
+
writer.write("</textarea>");
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
if (tag === "select") {
|
|
512
|
+
const selectedValue = props.value ?? props.defaultValue;
|
|
513
|
+
const filteredProps = {};
|
|
514
|
+
for (const k of Object.keys(props)) {
|
|
515
|
+
if (k !== "value" && k !== "defaultValue")
|
|
516
|
+
filteredProps[k] = props[k];
|
|
517
|
+
}
|
|
518
|
+
writer.write(`<select${renderAttributes(filteredProps, false)}>`);
|
|
519
|
+
const selectedSet = selectedValue == null ? null : Array.isArray(selectedValue) ? new Set(selectedValue.map(String)) : /* @__PURE__ */ new Set([String(selectedValue)]);
|
|
520
|
+
const patchedChildren = selectedSet != null ? markSelectedOptionsMulti(props.children, selectedSet) : props.children;
|
|
521
|
+
const inner2 = renderChildren(patchedChildren, writer, false);
|
|
522
|
+
if (inner2 && typeof inner2.then === "function") {
|
|
523
|
+
return inner2.then(() => {
|
|
524
|
+
writer.write("</select>");
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
writer.write("</select>");
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
writer.write(`<${tag}${renderAttributes(props, childSvg)}`);
|
|
531
|
+
if (VOID_ELEMENTS.has(tag)) {
|
|
532
|
+
writer.write("/>");
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
writer.write(">");
|
|
536
|
+
const childContext = tag === "foreignObject" ? false : childSvg;
|
|
537
|
+
let inner = void 0;
|
|
538
|
+
if (props.dangerouslySetInnerHTML) {
|
|
539
|
+
writer.write(props.dangerouslySetInnerHTML.__html);
|
|
540
|
+
} else {
|
|
541
|
+
inner = renderChildren(props.children, writer, childContext);
|
|
542
|
+
}
|
|
543
|
+
if (inner && typeof inner.then === "function") {
|
|
544
|
+
return inner.then(() => {
|
|
545
|
+
writer.write(`</${tag}>`);
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
writer.write(`</${tag}>`);
|
|
549
|
+
}
|
|
550
|
+
var REACT_MEMO = Symbol.for("react.memo");
|
|
551
|
+
var REACT_FORWARD_REF = Symbol.for("react.forward_ref");
|
|
552
|
+
var REACT_PROVIDER = Symbol.for("react.provider");
|
|
553
|
+
var REACT_CONTEXT = Symbol.for("react.context");
|
|
554
|
+
var REACT_CONSUMER = Symbol.for("react.consumer");
|
|
555
|
+
var REACT_LAZY = Symbol.for("react.lazy");
|
|
556
|
+
function renderComponent(type, props, writer, isSvg) {
|
|
557
|
+
const typeOf = type?.$$typeof;
|
|
558
|
+
if (typeOf === REACT_MEMO) {
|
|
559
|
+
return renderNode(
|
|
560
|
+
{ $$typeof: SLIM_ELEMENT, type: type.type, props, key: null },
|
|
561
|
+
writer,
|
|
562
|
+
isSvg
|
|
563
|
+
);
|
|
564
|
+
}
|
|
565
|
+
if (typeOf === REACT_FORWARD_REF) {
|
|
566
|
+
return renderComponent(type.render, props, writer, isSvg);
|
|
567
|
+
}
|
|
568
|
+
if (typeOf === REACT_LAZY) {
|
|
569
|
+
const resolved = type._init(type._payload);
|
|
570
|
+
const LazyComp = resolved?.default ?? resolved;
|
|
571
|
+
return renderComponent(LazyComp, props, writer, isSvg);
|
|
572
|
+
}
|
|
573
|
+
if (typeOf === REACT_CONSUMER) {
|
|
574
|
+
const ctx2 = type._context;
|
|
575
|
+
const value = ctx2?._currentValue;
|
|
576
|
+
const result2 = typeof props.children === "function" ? props.children(value) : null;
|
|
577
|
+
const savedScope2 = pushComponentScope();
|
|
578
|
+
const finish2 = () => popComponentScope(savedScope2);
|
|
579
|
+
const r2 = renderNode(result2, writer, isSvg);
|
|
580
|
+
if (r2 && typeof r2.then === "function") {
|
|
581
|
+
return r2.then(finish2);
|
|
582
|
+
}
|
|
583
|
+
finish2();
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
const isProvider = "_context" in type || typeOf === REACT_PROVIDER || typeOf === REACT_CONTEXT && "value" in props;
|
|
587
|
+
let prevCtxValue;
|
|
588
|
+
let ctx;
|
|
589
|
+
if (isProvider) {
|
|
590
|
+
ctx = type._context ?? type;
|
|
591
|
+
prevCtxValue = ctx._currentValue;
|
|
592
|
+
ctx._currentValue = props.value;
|
|
593
|
+
}
|
|
594
|
+
const savedScope = pushComponentScope();
|
|
595
|
+
if (isProvider && typeof type !== "function") {
|
|
596
|
+
const finish2 = () => {
|
|
597
|
+
popComponentScope(savedScope);
|
|
598
|
+
ctx._currentValue = prevCtxValue;
|
|
599
|
+
};
|
|
600
|
+
const r2 = renderChildren(props.children, writer, isSvg);
|
|
601
|
+
if (r2 && typeof r2.then === "function") {
|
|
602
|
+
return r2.then(finish2);
|
|
603
|
+
}
|
|
604
|
+
finish2();
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
let result;
|
|
608
|
+
try {
|
|
609
|
+
if (type.prototype && typeof type.prototype.render === "function") {
|
|
610
|
+
const instance = new type(props);
|
|
611
|
+
if (typeof type.getDerivedStateFromProps === "function") {
|
|
612
|
+
const derived = type.getDerivedStateFromProps(props, instance.state ?? {});
|
|
613
|
+
if (derived != null)
|
|
614
|
+
instance.state = { ...instance.state ?? {}, ...derived };
|
|
615
|
+
}
|
|
616
|
+
result = instance.render();
|
|
617
|
+
} else {
|
|
618
|
+
result = type(props);
|
|
619
|
+
}
|
|
620
|
+
} catch (e) {
|
|
621
|
+
popComponentScope(savedScope);
|
|
622
|
+
if (isProvider)
|
|
623
|
+
ctx._currentValue = prevCtxValue;
|
|
624
|
+
throw e;
|
|
625
|
+
}
|
|
626
|
+
const finish = () => {
|
|
627
|
+
popComponentScope(savedScope);
|
|
628
|
+
if (isProvider)
|
|
629
|
+
ctx._currentValue = prevCtxValue;
|
|
630
|
+
};
|
|
631
|
+
if (result instanceof Promise) {
|
|
632
|
+
return result.then((resolved) => {
|
|
633
|
+
const r2 = renderNode(resolved, writer, isSvg);
|
|
634
|
+
if (r2 && typeof r2.then === "function") {
|
|
635
|
+
return r2.then(finish);
|
|
636
|
+
}
|
|
637
|
+
finish();
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
const r = renderNode(result, writer, isSvg);
|
|
641
|
+
if (r && typeof r.then === "function") {
|
|
642
|
+
return r.then(finish);
|
|
643
|
+
}
|
|
644
|
+
finish();
|
|
645
|
+
}
|
|
646
|
+
function isTextLike(node) {
|
|
647
|
+
return typeof node === "string" || typeof node === "number";
|
|
648
|
+
}
|
|
649
|
+
function renderChildArray(children, writer, isSvg) {
|
|
650
|
+
const totalChildren = children.length;
|
|
651
|
+
for (let i = 0; i < totalChildren; i++) {
|
|
652
|
+
if (isTextLike(children[i]) && writer.lastWasText) {
|
|
653
|
+
writer.write("<!-- -->");
|
|
654
|
+
}
|
|
655
|
+
const savedTree = pushTreeContext(totalChildren, i);
|
|
656
|
+
const r = renderNode(children[i], writer, isSvg);
|
|
657
|
+
if (r && typeof r.then === "function") {
|
|
658
|
+
return r.then(() => {
|
|
659
|
+
popTreeContext(savedTree);
|
|
660
|
+
return renderChildArrayFrom(children, i + 1, writer, isSvg);
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
popTreeContext(savedTree);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
function renderChildArrayFrom(children, startIndex, writer, isSvg) {
|
|
667
|
+
const totalChildren = children.length;
|
|
668
|
+
for (let i = startIndex; i < totalChildren; i++) {
|
|
669
|
+
if (isTextLike(children[i]) && writer.lastWasText) {
|
|
670
|
+
writer.write("<!-- -->");
|
|
671
|
+
}
|
|
672
|
+
const savedTree = pushTreeContext(totalChildren, i);
|
|
673
|
+
const r = renderNode(children[i], writer, isSvg);
|
|
674
|
+
if (r && typeof r.then === "function") {
|
|
675
|
+
return r.then(() => {
|
|
676
|
+
popTreeContext(savedTree);
|
|
677
|
+
return renderChildArrayFrom(children, i + 1, writer, isSvg);
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
popTreeContext(savedTree);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
function renderChildren(children, writer, isSvg = false) {
|
|
684
|
+
if (children == null)
|
|
685
|
+
return;
|
|
686
|
+
if (Array.isArray(children)) {
|
|
687
|
+
return renderChildArray(children, writer, isSvg);
|
|
688
|
+
}
|
|
689
|
+
return renderNode(children, writer, isSvg);
|
|
690
|
+
}
|
|
691
|
+
var MAX_SUSPENSE_RETRIES = 25;
|
|
692
|
+
async function renderSuspense(props, writer, isSvg = false) {
|
|
693
|
+
const { children, fallback } = props;
|
|
694
|
+
let attempts = 0;
|
|
695
|
+
const snap = snapshotContext();
|
|
696
|
+
while (attempts < MAX_SUSPENSE_RETRIES) {
|
|
697
|
+
restoreContext(snap);
|
|
698
|
+
try {
|
|
699
|
+
const buffer = new BufferWriter();
|
|
700
|
+
const r = renderNode(children, buffer, isSvg);
|
|
701
|
+
if (r && typeof r.then === "function") {
|
|
702
|
+
await r;
|
|
703
|
+
}
|
|
704
|
+
writer.write("<!--$-->");
|
|
705
|
+
buffer.flush(writer);
|
|
706
|
+
writer.write("<!--/$-->");
|
|
707
|
+
return;
|
|
708
|
+
} catch (error) {
|
|
709
|
+
if (error && typeof error.then === "function") {
|
|
710
|
+
await error;
|
|
711
|
+
attempts++;
|
|
712
|
+
} else {
|
|
713
|
+
throw error;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
restoreContext(snap);
|
|
718
|
+
writer.write("<!--$?-->");
|
|
719
|
+
if (fallback) {
|
|
720
|
+
const r = renderNode(fallback, writer, isSvg);
|
|
721
|
+
if (r && typeof r.then === "function")
|
|
722
|
+
await r;
|
|
723
|
+
}
|
|
724
|
+
writer.write("<!--/$-->");
|
|
725
|
+
}
|
|
726
|
+
async function renderToString(element) {
|
|
727
|
+
for (let attempt = 0; attempt < MAX_SUSPENSE_RETRIES; attempt++) {
|
|
728
|
+
resetRenderState();
|
|
729
|
+
const chunks = [];
|
|
730
|
+
const writer = {
|
|
731
|
+
lastWasText: false,
|
|
732
|
+
write(c) {
|
|
733
|
+
chunks.push(c);
|
|
734
|
+
this.lastWasText = false;
|
|
735
|
+
},
|
|
736
|
+
text(s2) {
|
|
737
|
+
chunks.push(s2);
|
|
738
|
+
this.lastWasText = true;
|
|
739
|
+
}
|
|
740
|
+
};
|
|
741
|
+
try {
|
|
742
|
+
const r = renderNode(element, writer);
|
|
743
|
+
if (r && typeof r.then === "function")
|
|
744
|
+
await r;
|
|
745
|
+
return chunks.join("");
|
|
746
|
+
} catch (error) {
|
|
747
|
+
if (error && typeof error.then === "function") {
|
|
748
|
+
await error;
|
|
749
|
+
continue;
|
|
750
|
+
}
|
|
751
|
+
throw error;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
throw new Error("[slim-react] renderToString exceeded maximum retries");
|
|
151
755
|
}
|
|
756
|
+
|
|
757
|
+
// src/utils/response.tsx
|
|
152
758
|
var ESC = { "&": "&", "<": "<", ">": ">", '"': """ };
|
|
153
|
-
var escAttr = (
|
|
154
|
-
var escText = (
|
|
759
|
+
var escAttr = (s2) => s2.replace(/[&<>"]/g, (c) => ESC[c] ?? c);
|
|
760
|
+
var escText = (s2) => s2.replace(/[&<>]/g, (c) => ESC[c] ?? c);
|
|
155
761
|
var ATTR = {
|
|
156
762
|
className: "class",
|
|
157
763
|
htmlFor: "for",
|
|
@@ -195,77 +801,57 @@ var getHeadHtml = (seoData) => {
|
|
|
195
801
|
var getReactResponse = async (req, opts) => {
|
|
196
802
|
const App = opts.document.body;
|
|
197
803
|
const { getInitProps, getAfterRenderProps, getFinalProps } = opts.document;
|
|
198
|
-
const renderToStaticMarkup = await getStaticMarkupRenderer();
|
|
199
|
-
const unsuspend = {
|
|
200
|
-
cache: /* @__PURE__ */ new Map(),
|
|
201
|
-
hasPending: false
|
|
202
|
-
};
|
|
203
|
-
const processUnsuspend = async () => {
|
|
204
|
-
const pending = [...unsuspend.cache.values()].filter((e) => e.status === "pending").map((e) => e.promise);
|
|
205
|
-
await Promise.all(pending);
|
|
206
|
-
};
|
|
207
804
|
const context = {
|
|
208
|
-
head: {
|
|
209
|
-
title: "Hadars App",
|
|
210
|
-
meta: {},
|
|
211
|
-
link: {},
|
|
212
|
-
style: {},
|
|
213
|
-
script: {},
|
|
214
|
-
status: 200
|
|
215
|
-
},
|
|
216
|
-
_unsuspend: unsuspend
|
|
805
|
+
head: { title: "Hadars App", meta: {}, link: {}, style: {}, script: {}, status: 200 }
|
|
217
806
|
};
|
|
218
807
|
let props = {
|
|
219
808
|
...getInitProps ? await getInitProps(req) : {},
|
|
220
809
|
location: req.location,
|
|
221
810
|
context
|
|
222
811
|
};
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}
|
|
233
|
-
if (unsuspend.hasPending)
|
|
234
|
-
await processUnsuspend();
|
|
235
|
-
} while (unsuspend.hasPending && ++iters < 25);
|
|
236
|
-
if (unsuspend.hasPending) {
|
|
237
|
-
console.warn("[hadars] SSR render loop hit the 25-iteration cap \u2014 some useServerData values may not be resolved. Check for data dependencies that are never fulfilled.");
|
|
238
|
-
}
|
|
239
|
-
if (getAfterRenderProps) {
|
|
240
|
-
props = await getAfterRenderProps(props, html);
|
|
241
|
-
try {
|
|
242
|
-
globalThis.__hadarsUnsuspend = unsuspend;
|
|
243
|
-
renderToStaticMarkup(/* @__PURE__ */ jsx(App, { ...{ ...props, location: req.location, context } }));
|
|
244
|
-
} finally {
|
|
245
|
-
globalThis.__hadarsUnsuspend = null;
|
|
812
|
+
const unsuspend = { cache: /* @__PURE__ */ new Map() };
|
|
813
|
+
globalThis.__hadarsUnsuspend = unsuspend;
|
|
814
|
+
try {
|
|
815
|
+
let html = await renderToString(createElement(App, props));
|
|
816
|
+
if (getAfterRenderProps) {
|
|
817
|
+
props = await getAfterRenderProps(props, html);
|
|
818
|
+
await renderToString(
|
|
819
|
+
createElement(App, { ...props, location: req.location, context })
|
|
820
|
+
);
|
|
246
821
|
}
|
|
822
|
+
} finally {
|
|
823
|
+
globalThis.__hadarsUnsuspend = null;
|
|
247
824
|
}
|
|
825
|
+
const { context: _, ...restProps } = getFinalProps ? await getFinalProps(props) : props;
|
|
248
826
|
const serverData = {};
|
|
249
|
-
for (const [
|
|
250
|
-
if (
|
|
251
|
-
serverData[
|
|
827
|
+
for (const [key, entry] of unsuspend.cache) {
|
|
828
|
+
if (entry.status === "fulfilled")
|
|
829
|
+
serverData[key] = entry.value;
|
|
252
830
|
}
|
|
253
|
-
const { context: _, ...restProps } = getFinalProps ? await getFinalProps(props) : props;
|
|
254
831
|
const clientProps = {
|
|
255
832
|
...restProps,
|
|
256
833
|
location: req.location,
|
|
257
834
|
...Object.keys(serverData).length > 0 ? { __serverData: serverData } : {}
|
|
258
835
|
};
|
|
259
|
-
const ReactPage =
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
836
|
+
const ReactPage = createElement(
|
|
837
|
+
Fragment,
|
|
838
|
+
null,
|
|
839
|
+
createElement(
|
|
840
|
+
"div",
|
|
841
|
+
{ id: "app" },
|
|
842
|
+
createElement(App, { ...props, location: req.location, context })
|
|
843
|
+
),
|
|
844
|
+
createElement("script", {
|
|
845
|
+
id: "hadars",
|
|
846
|
+
type: "application/json",
|
|
847
|
+
dangerouslySetInnerHTML: {
|
|
848
|
+
__html: JSON.stringify({ hadars: { props: clientProps } }).replace(/</g, "\\u003c")
|
|
849
|
+
}
|
|
850
|
+
})
|
|
851
|
+
);
|
|
267
852
|
return {
|
|
268
853
|
ReactPage,
|
|
854
|
+
unsuspend,
|
|
269
855
|
status: context.head.status,
|
|
270
856
|
headHtml: getHeadHtml(context.head),
|
|
271
857
|
renderPayload: {
|
|
@@ -280,12 +866,12 @@ import rspack from "@rspack/core";
|
|
|
280
866
|
import ReactRefreshPlugin from "@rspack/plugin-react-refresh";
|
|
281
867
|
import path from "node:path";
|
|
282
868
|
import { fileURLToPath } from "node:url";
|
|
283
|
-
import
|
|
869
|
+
import pathMod from "node:path";
|
|
284
870
|
import { existsSync } from "node:fs";
|
|
285
871
|
var __dirname = process.cwd();
|
|
286
|
-
var packageDir =
|
|
287
|
-
var clientScriptPath =
|
|
288
|
-
var loaderPath = existsSync(
|
|
872
|
+
var packageDir = pathMod.dirname(fileURLToPath(import.meta.url));
|
|
873
|
+
var clientScriptPath = pathMod.resolve(packageDir, "template.html");
|
|
874
|
+
var loaderPath = existsSync(pathMod.resolve(packageDir, "loader.cjs")) ? pathMod.resolve(packageDir, "loader.cjs") : pathMod.resolve(packageDir, "loader.ts");
|
|
289
875
|
var getConfigBase = (mode) => {
|
|
290
876
|
const isDev = mode === "development";
|
|
291
877
|
return {
|
|
@@ -445,25 +1031,21 @@ var buildCompilerConfig = (entry, opts, includeHotPlugin) => {
|
|
|
445
1031
|
const isServerBuild = Boolean(
|
|
446
1032
|
opts.output && typeof opts.output === "object" && (opts.output.library || String(opts.output.filename || "").includes("ssr"))
|
|
447
1033
|
);
|
|
1034
|
+
const slimReactIndex = pathMod.resolve(packageDir, "slim-react", "index.js");
|
|
1035
|
+
const slimReactJsx = pathMod.resolve(packageDir, "slim-react", "jsx-runtime.js");
|
|
448
1036
|
const resolveAliases = isServerBuild ? {
|
|
449
|
-
//
|
|
450
|
-
react:
|
|
451
|
-
"react-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
"react/jsx-dev-runtime": path.resolve(process.cwd(), "node_modules", "react", "jsx-dev-runtime.js"),
|
|
455
|
-
// ensure emotion packages resolve to the project's node_modules so we don't pick up a browser-specific entry
|
|
1037
|
+
// Route all React imports to slim-react for SSR.
|
|
1038
|
+
react: slimReactIndex,
|
|
1039
|
+
"react/jsx-runtime": slimReactJsx,
|
|
1040
|
+
"react/jsx-dev-runtime": slimReactJsx,
|
|
1041
|
+
// Keep emotion on the project's node_modules (server-safe entry).
|
|
456
1042
|
"@emotion/react": path.resolve(process.cwd(), "node_modules", "@emotion", "react"),
|
|
457
1043
|
"@emotion/server": path.resolve(process.cwd(), "node_modules", "@emotion", "server"),
|
|
458
1044
|
"@emotion/cache": path.resolve(process.cwd(), "node_modules", "@emotion", "cache"),
|
|
459
1045
|
"@emotion/styled": path.resolve(process.cwd(), "node_modules", "@emotion", "styled")
|
|
460
1046
|
} : void 0;
|
|
461
1047
|
const externals = isServerBuild ? [
|
|
462
|
-
|
|
463
|
-
"react-dom",
|
|
464
|
-
// keep common aliases external as well
|
|
465
|
-
"react/jsx-runtime",
|
|
466
|
-
"react/jsx-dev-runtime",
|
|
1048
|
+
// react / react-dom are replaced by slim-react via alias above — not external.
|
|
467
1049
|
// emotion should be external on server builds to avoid client/browser code
|
|
468
1050
|
"@emotion/react",
|
|
469
1051
|
"@emotion/server",
|
|
@@ -515,11 +1097,36 @@ var buildCompilerConfig = (entry, opts, includeHotPlugin) => {
|
|
|
515
1097
|
plugins: [
|
|
516
1098
|
new rspack.HtmlRspackPlugin({
|
|
517
1099
|
publicPath: base || "/",
|
|
518
|
-
template: opts.htmlTemplate ?
|
|
1100
|
+
template: opts.htmlTemplate ? pathMod.resolve(process.cwd(), opts.htmlTemplate) : clientScriptPath,
|
|
519
1101
|
scriptLoading: "module",
|
|
520
1102
|
filename: "out.html",
|
|
521
|
-
inject: "
|
|
1103
|
+
inject: "head",
|
|
1104
|
+
minify: opts.mode === "production"
|
|
522
1105
|
}),
|
|
1106
|
+
// Add `async` to the emitted module script so DOMContentLoaded fires
|
|
1107
|
+
// as soon as HTML is parsed — without waiting for the bundle to execute.
|
|
1108
|
+
// `<script type="module" async>` is valid: it downloads in parallel and
|
|
1109
|
+
// executes without blocking DOMContentLoaded, while retaining module
|
|
1110
|
+
// semantics (strict mode, ES imports, etc.).
|
|
1111
|
+
{
|
|
1112
|
+
apply(compiler) {
|
|
1113
|
+
compiler.hooks.emit.tapAsync("HadarsAsyncModuleScript", (compilation, cb) => {
|
|
1114
|
+
const asset = compilation.assets["out.html"];
|
|
1115
|
+
if (asset) {
|
|
1116
|
+
const html = asset.source();
|
|
1117
|
+
const updated = html.replace(
|
|
1118
|
+
/(<script\b[^>]*\btype="module"[^>]*)(>)/g,
|
|
1119
|
+
(match, before, end) => before.includes("async") ? match : `${before} async${end}`
|
|
1120
|
+
);
|
|
1121
|
+
compilation.assets["out.html"] = {
|
|
1122
|
+
source: () => updated,
|
|
1123
|
+
size: () => Buffer.byteLength(updated)
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
cb();
|
|
1127
|
+
});
|
|
1128
|
+
}
|
|
1129
|
+
},
|
|
523
1130
|
isDev && new ReactRefreshPlugin(),
|
|
524
1131
|
includeHotPlugin && isDev && new rspack.HotModuleReplacementPlugin(),
|
|
525
1132
|
...extraPlugins
|
|
@@ -777,15 +1384,55 @@ async function tryServeFile(filePath) {
|
|
|
777
1384
|
|
|
778
1385
|
// src/build.ts
|
|
779
1386
|
import { RspackDevServer } from "@rspack/dev-server";
|
|
780
|
-
import
|
|
781
|
-
import { fileURLToPath as fileURLToPath2, pathToFileURL
|
|
782
|
-
import { createRequire as createRequire2 } from "node:module";
|
|
1387
|
+
import pathMod2 from "node:path";
|
|
1388
|
+
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "node:url";
|
|
783
1389
|
import crypto from "node:crypto";
|
|
784
1390
|
import fs from "node:fs/promises";
|
|
785
1391
|
import { existsSync as existsSync2 } from "node:fs";
|
|
786
1392
|
import os from "node:os";
|
|
787
1393
|
import { spawn } from "node:child_process";
|
|
788
1394
|
import cluster from "node:cluster";
|
|
1395
|
+
|
|
1396
|
+
// src/utils/segmentCache.ts
|
|
1397
|
+
function getStore() {
|
|
1398
|
+
const g = globalThis;
|
|
1399
|
+
if (!g.__hadarsSegmentStore) {
|
|
1400
|
+
g.__hadarsSegmentStore = /* @__PURE__ */ new Map();
|
|
1401
|
+
}
|
|
1402
|
+
return g.__hadarsSegmentStore;
|
|
1403
|
+
}
|
|
1404
|
+
function setSegment(key, html, ttl) {
|
|
1405
|
+
getStore().set(key, {
|
|
1406
|
+
html,
|
|
1407
|
+
expiresAt: ttl != null ? Date.now() + ttl : null
|
|
1408
|
+
});
|
|
1409
|
+
}
|
|
1410
|
+
function processSegmentCache(html) {
|
|
1411
|
+
let prev;
|
|
1412
|
+
do {
|
|
1413
|
+
prev = html;
|
|
1414
|
+
html = html.replace(
|
|
1415
|
+
/<hadars-c([^>]*)>([\s\S]*?)<\/hadars-c>/g,
|
|
1416
|
+
(match, attrs, content) => {
|
|
1417
|
+
const cacheM = /data-cache="([^"]+)"/.exec(attrs);
|
|
1418
|
+
const keyM = /data-key="([^"]+)"/.exec(attrs);
|
|
1419
|
+
const ttlM = /data-ttl="(\d+)"/.exec(attrs);
|
|
1420
|
+
if (!cacheM || !keyM)
|
|
1421
|
+
return match;
|
|
1422
|
+
if (cacheM[1] === "miss") {
|
|
1423
|
+
setSegment(keyM[1], content, ttlM ? Number(ttlM[1]) : void 0);
|
|
1424
|
+
return content;
|
|
1425
|
+
}
|
|
1426
|
+
if (cacheM[1] === "hit")
|
|
1427
|
+
return content;
|
|
1428
|
+
return match;
|
|
1429
|
+
}
|
|
1430
|
+
);
|
|
1431
|
+
} while (html !== prev);
|
|
1432
|
+
return html;
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
// src/build.ts
|
|
789
1436
|
var encoder = new TextEncoder();
|
|
790
1437
|
async function processHtmlTemplate(templatePath) {
|
|
791
1438
|
const html = await fs.readFile(templatePath, "utf-8");
|
|
@@ -799,7 +1446,7 @@ async function processHtmlTemplate(templatePath) {
|
|
|
799
1446
|
return templatePath;
|
|
800
1447
|
await ensureHadarsTmpDir();
|
|
801
1448
|
const sourceHash = crypto.createHash("md5").update(html).digest("hex").slice(0, 8);
|
|
802
|
-
const cachedPath =
|
|
1449
|
+
const cachedPath = pathMod2.join(HADARS_TMP_DIR, `template-${sourceHash}.html`);
|
|
803
1450
|
try {
|
|
804
1451
|
await fs.access(cachedPath);
|
|
805
1452
|
return cachedPath;
|
|
@@ -827,16 +1474,6 @@ async function processHtmlTemplate(templatePath) {
|
|
|
827
1474
|
}
|
|
828
1475
|
var HEAD_MARKER = '<meta name="HADARS_HEAD">';
|
|
829
1476
|
var BODY_MARKER = '<meta name="HADARS_BODY">';
|
|
830
|
-
var _renderToString = null;
|
|
831
|
-
async function getRenderToString() {
|
|
832
|
-
if (!_renderToString) {
|
|
833
|
-
const req = createRequire2(pathMod3.resolve(process.cwd(), "__hadars_fake__.js"));
|
|
834
|
-
const resolved = req.resolve("react-dom/server");
|
|
835
|
-
const mod = await import(pathToFileURL2(resolved).href);
|
|
836
|
-
_renderToString = mod.renderToString;
|
|
837
|
-
}
|
|
838
|
-
return _renderToString;
|
|
839
|
-
}
|
|
840
1477
|
var RenderWorkerPool = class {
|
|
841
1478
|
workers = [];
|
|
842
1479
|
pending = /* @__PURE__ */ new Map();
|
|
@@ -941,19 +1578,18 @@ var RenderWorkerPool = class {
|
|
|
941
1578
|
}
|
|
942
1579
|
};
|
|
943
1580
|
async function buildSsrResponse(ReactPage, headHtml, status, getPrecontentHtml, unsuspendForRender) {
|
|
944
|
-
const renderToString = await getRenderToString();
|
|
945
1581
|
const responseStream = new ReadableStream({
|
|
946
1582
|
async start(controller) {
|
|
947
1583
|
const [precontentHtml, postContent] = await getPrecontentHtml(headHtml);
|
|
948
1584
|
controller.enqueue(encoder.encode(precontentHtml));
|
|
949
|
-
await Promise.resolve();
|
|
950
1585
|
let bodyHtml;
|
|
951
1586
|
try {
|
|
952
1587
|
globalThis.__hadarsUnsuspend = unsuspendForRender;
|
|
953
|
-
bodyHtml = renderToString(ReactPage);
|
|
1588
|
+
bodyHtml = await renderToString(ReactPage);
|
|
954
1589
|
} finally {
|
|
955
1590
|
globalThis.__hadarsUnsuspend = null;
|
|
956
1591
|
}
|
|
1592
|
+
bodyHtml = processSegmentCache(bodyHtml);
|
|
957
1593
|
controller.enqueue(encoder.encode(bodyHtml + postContent));
|
|
958
1594
|
controller.close();
|
|
959
1595
|
}
|
|
@@ -1082,7 +1718,7 @@ var __dirname2 = process.cwd();
|
|
|
1082
1718
|
var getSuffix = (mode) => mode === "development" ? `?v=${Date.now()}` : "";
|
|
1083
1719
|
var HadarsFolder = "./.hadars";
|
|
1084
1720
|
var StaticPath = `${HadarsFolder}/static`;
|
|
1085
|
-
var HADARS_TMP_DIR =
|
|
1721
|
+
var HADARS_TMP_DIR = pathMod2.join(os.tmpdir(), "hadars");
|
|
1086
1722
|
var ensureHadarsTmpDir = () => fs.mkdir(HADARS_TMP_DIR, { recursive: true });
|
|
1087
1723
|
var validateOptions = (options) => {
|
|
1088
1724
|
if (!options.entry) {
|
|
@@ -1093,8 +1729,8 @@ var validateOptions = (options) => {
|
|
|
1093
1729
|
}
|
|
1094
1730
|
};
|
|
1095
1731
|
var resolveWorkerCmd = (packageDir2) => {
|
|
1096
|
-
const tsPath =
|
|
1097
|
-
const jsPath =
|
|
1732
|
+
const tsPath = pathMod2.resolve(packageDir2, "ssr-watch.ts");
|
|
1733
|
+
const jsPath = pathMod2.resolve(packageDir2, "ssr-watch.js");
|
|
1098
1734
|
if (isBun && existsSync2(tsPath)) {
|
|
1099
1735
|
return ["bun", tsPath];
|
|
1100
1736
|
}
|
|
@@ -1128,10 +1764,10 @@ var dev = async (options) => {
|
|
|
1128
1764
|
const handleProxy = createProxyHandler(options);
|
|
1129
1765
|
const handleWS = upgradeHandler(options);
|
|
1130
1766
|
const handler = options.fetch;
|
|
1131
|
-
const entry =
|
|
1767
|
+
const entry = pathMod2.resolve(__dirname2, options.entry);
|
|
1132
1768
|
const hmrPort = options.hmrPort ?? port + 1;
|
|
1133
|
-
const packageDir2 =
|
|
1134
|
-
const clientScriptPath2 =
|
|
1769
|
+
const packageDir2 = pathMod2.dirname(fileURLToPath2(import.meta.url));
|
|
1770
|
+
const clientScriptPath2 = pathMod2.resolve(packageDir2, "utils", "clientScript.tsx");
|
|
1135
1771
|
let clientScript = "";
|
|
1136
1772
|
try {
|
|
1137
1773
|
clientScript = (await fs.readFile(clientScriptPath2, "utf-8")).replace("$_MOD_PATH$", entry + getSuffix(options.mode));
|
|
@@ -1140,15 +1776,15 @@ var dev = async (options) => {
|
|
|
1140
1776
|
throw err;
|
|
1141
1777
|
}
|
|
1142
1778
|
await ensureHadarsTmpDir();
|
|
1143
|
-
const tmpFilePath =
|
|
1779
|
+
const tmpFilePath = pathMod2.join(HADARS_TMP_DIR, `client-${Date.now()}.tsx`);
|
|
1144
1780
|
await fs.writeFile(tmpFilePath, clientScript);
|
|
1145
1781
|
let ssrBuildId = crypto.randomBytes(4).toString("hex");
|
|
1146
|
-
const resolvedHtmlTemplate = options.htmlTemplate ? await processHtmlTemplate(
|
|
1782
|
+
const resolvedHtmlTemplate = options.htmlTemplate ? await processHtmlTemplate(pathMod2.resolve(__dirname2, options.htmlTemplate)) : void 0;
|
|
1147
1783
|
const clientCompiler = createClientCompiler(tmpFilePath, {
|
|
1148
1784
|
target: "web",
|
|
1149
1785
|
output: {
|
|
1150
1786
|
filename: "index.js",
|
|
1151
|
-
path:
|
|
1787
|
+
path: pathMod2.resolve(__dirname2, StaticPath)
|
|
1152
1788
|
},
|
|
1153
1789
|
base: baseURL,
|
|
1154
1790
|
mode: "development",
|
|
@@ -1288,7 +1924,7 @@ var dev = async (options) => {
|
|
|
1288
1924
|
}
|
|
1289
1925
|
})();
|
|
1290
1926
|
const getPrecontentHtml = makePrecontentHtmlGetter(
|
|
1291
|
-
readyPromise.then(() => fs.readFile(
|
|
1927
|
+
readyPromise.then(() => fs.readFile(pathMod2.join(__dirname2, StaticPath, "out.html"), "utf-8"))
|
|
1292
1928
|
);
|
|
1293
1929
|
await serve(port, async (req, ctx) => {
|
|
1294
1930
|
await readyPromise;
|
|
@@ -1305,15 +1941,15 @@ var dev = async (options) => {
|
|
|
1305
1941
|
return proxied;
|
|
1306
1942
|
const url = new URL(request.url);
|
|
1307
1943
|
const path2 = url.pathname;
|
|
1308
|
-
const staticRes = await tryServeFile(
|
|
1944
|
+
const staticRes = await tryServeFile(pathMod2.join(__dirname2, StaticPath, path2));
|
|
1309
1945
|
if (staticRes)
|
|
1310
1946
|
return staticRes;
|
|
1311
|
-
const projectStaticPath =
|
|
1312
|
-
const projectRes = await tryServeFile(
|
|
1947
|
+
const projectStaticPath = pathMod2.resolve(process.cwd(), "static");
|
|
1948
|
+
const projectRes = await tryServeFile(pathMod2.join(projectStaticPath, path2));
|
|
1313
1949
|
if (projectRes)
|
|
1314
1950
|
return projectRes;
|
|
1315
|
-
const ssrComponentPath =
|
|
1316
|
-
const importPath =
|
|
1951
|
+
const ssrComponentPath = pathMod2.join(__dirname2, HadarsFolder, SSR_FILENAME);
|
|
1952
|
+
const importPath = pathToFileURL(ssrComponentPath).href + `?t=${ssrBuildId}`;
|
|
1317
1953
|
try {
|
|
1318
1954
|
const {
|
|
1319
1955
|
default: Component,
|
|
@@ -1321,7 +1957,7 @@ var dev = async (options) => {
|
|
|
1321
1957
|
getAfterRenderProps,
|
|
1322
1958
|
getFinalProps
|
|
1323
1959
|
} = await import(importPath);
|
|
1324
|
-
const { ReactPage, status, headHtml
|
|
1960
|
+
const { ReactPage, unsuspend, status, headHtml } = await getReactResponse(request, {
|
|
1325
1961
|
document: {
|
|
1326
1962
|
body: Component,
|
|
1327
1963
|
lang: "en",
|
|
@@ -1330,7 +1966,6 @@ var dev = async (options) => {
|
|
|
1330
1966
|
getFinalProps
|
|
1331
1967
|
}
|
|
1332
1968
|
});
|
|
1333
|
-
const unsuspend = renderPayload.appProps.context?._unsuspend ?? null;
|
|
1334
1969
|
return buildSsrResponse(ReactPage, headHtml, status, getPrecontentHtml, unsuspend);
|
|
1335
1970
|
} catch (err) {
|
|
1336
1971
|
console.error("[hadars] SSR render error:", err);
|
|
@@ -1344,20 +1979,20 @@ var dev = async (options) => {
|
|
|
1344
1979
|
};
|
|
1345
1980
|
var build = async (options) => {
|
|
1346
1981
|
validateOptions(options);
|
|
1347
|
-
const entry =
|
|
1348
|
-
const packageDir2 =
|
|
1349
|
-
const clientScriptPath2 =
|
|
1982
|
+
const entry = pathMod2.resolve(__dirname2, options.entry);
|
|
1983
|
+
const packageDir2 = pathMod2.dirname(fileURLToPath2(import.meta.url));
|
|
1984
|
+
const clientScriptPath2 = pathMod2.resolve(packageDir2, "utils", "clientScript.js");
|
|
1350
1985
|
let clientScript = "";
|
|
1351
1986
|
try {
|
|
1352
1987
|
clientScript = (await fs.readFile(clientScriptPath2, "utf-8")).replace("$_MOD_PATH$", entry + getSuffix(options.mode));
|
|
1353
1988
|
} catch (err) {
|
|
1354
|
-
const srcClientPath =
|
|
1989
|
+
const srcClientPath = pathMod2.resolve(packageDir2, "utils", "clientScript.tsx");
|
|
1355
1990
|
clientScript = (await fs.readFile(srcClientPath, "utf-8")).replace("$_MOD_PATH$", entry + `?v=${Date.now()}`);
|
|
1356
1991
|
}
|
|
1357
1992
|
await ensureHadarsTmpDir();
|
|
1358
|
-
const tmpFilePath =
|
|
1993
|
+
const tmpFilePath = pathMod2.join(HADARS_TMP_DIR, `client-${Date.now()}.tsx`);
|
|
1359
1994
|
await fs.writeFile(tmpFilePath, clientScript);
|
|
1360
|
-
const resolvedHtmlTemplate = options.htmlTemplate ? await processHtmlTemplate(
|
|
1995
|
+
const resolvedHtmlTemplate = options.htmlTemplate ? await processHtmlTemplate(pathMod2.resolve(__dirname2, options.htmlTemplate)) : void 0;
|
|
1361
1996
|
console.log("Building client and server bundles in parallel...");
|
|
1362
1997
|
await Promise.all([
|
|
1363
1998
|
compileEntry(tmpFilePath, {
|
|
@@ -1365,7 +2000,7 @@ var build = async (options) => {
|
|
|
1365
2000
|
output: {
|
|
1366
2001
|
// Content hash: filename is stable when code is unchanged → better browser/CDN cache.
|
|
1367
2002
|
filename: "index.[contenthash:8].js",
|
|
1368
|
-
path:
|
|
2003
|
+
path: pathMod2.resolve(__dirname2, StaticPath)
|
|
1369
2004
|
},
|
|
1370
2005
|
base: options.baseURL,
|
|
1371
2006
|
mode: "production",
|
|
@@ -1374,11 +2009,11 @@ var build = async (options) => {
|
|
|
1374
2009
|
optimization: options.optimization,
|
|
1375
2010
|
htmlTemplate: resolvedHtmlTemplate
|
|
1376
2011
|
}),
|
|
1377
|
-
compileEntry(
|
|
2012
|
+
compileEntry(pathMod2.resolve(__dirname2, options.entry), {
|
|
1378
2013
|
output: {
|
|
1379
2014
|
iife: false,
|
|
1380
2015
|
filename: SSR_FILENAME,
|
|
1381
|
-
path:
|
|
2016
|
+
path: pathMod2.resolve(__dirname2, HadarsFolder),
|
|
1382
2017
|
publicPath: "",
|
|
1383
2018
|
library: { type: "module" }
|
|
1384
2019
|
},
|
|
@@ -1414,16 +2049,16 @@ var run = async (options) => {
|
|
|
1414
2049
|
console.log(`Starting Hadars (run) on port ${port}`);
|
|
1415
2050
|
let renderPool;
|
|
1416
2051
|
if (!isNode && workers > 1) {
|
|
1417
|
-
const packageDir2 =
|
|
1418
|
-
const workerJs =
|
|
1419
|
-
const workerTs =
|
|
2052
|
+
const packageDir2 = pathMod2.dirname(fileURLToPath2(import.meta.url));
|
|
2053
|
+
const workerJs = pathMod2.resolve(packageDir2, "ssr-render-worker.js");
|
|
2054
|
+
const workerTs = pathMod2.resolve(packageDir2, "ssr-render-worker.ts");
|
|
1420
2055
|
const workerFile = existsSync2(workerJs) ? workerJs : workerTs;
|
|
1421
|
-
const ssrBundlePath =
|
|
2056
|
+
const ssrBundlePath = pathMod2.resolve(__dirname2, HadarsFolder, SSR_FILENAME);
|
|
1422
2057
|
renderPool = new RenderWorkerPool(workerFile, workers, ssrBundlePath);
|
|
1423
2058
|
console.log(`[hadars] SSR render pool: ${workers} worker threads`);
|
|
1424
2059
|
}
|
|
1425
2060
|
const getPrecontentHtml = makePrecontentHtmlGetter(
|
|
1426
|
-
fs.readFile(
|
|
2061
|
+
fs.readFile(pathMod2.join(__dirname2, StaticPath, "out.html"), "utf-8")
|
|
1427
2062
|
);
|
|
1428
2063
|
const runHandler = async (req, ctx) => {
|
|
1429
2064
|
const request = parseRequest(req);
|
|
@@ -1439,23 +2074,23 @@ var run = async (options) => {
|
|
|
1439
2074
|
return proxied;
|
|
1440
2075
|
const url = new URL(request.url);
|
|
1441
2076
|
const path2 = url.pathname;
|
|
1442
|
-
const staticRes = await tryServeFile(
|
|
2077
|
+
const staticRes = await tryServeFile(pathMod2.join(__dirname2, StaticPath, path2));
|
|
1443
2078
|
if (staticRes)
|
|
1444
2079
|
return staticRes;
|
|
1445
|
-
const projectStaticPath =
|
|
1446
|
-
const projectRes = await tryServeFile(
|
|
2080
|
+
const projectStaticPath = pathMod2.resolve(process.cwd(), "static");
|
|
2081
|
+
const projectRes = await tryServeFile(pathMod2.join(projectStaticPath, path2));
|
|
1447
2082
|
if (projectRes)
|
|
1448
2083
|
return projectRes;
|
|
1449
2084
|
const routeClean = path2.replace(/(^\/|\/$)/g, "");
|
|
1450
2085
|
if (routeClean) {
|
|
1451
2086
|
const routeRes = await tryServeFile(
|
|
1452
|
-
|
|
2087
|
+
pathMod2.join(__dirname2, StaticPath, routeClean, "index.html")
|
|
1453
2088
|
);
|
|
1454
2089
|
if (routeRes)
|
|
1455
2090
|
return routeRes;
|
|
1456
2091
|
}
|
|
1457
|
-
const componentPath =
|
|
1458
|
-
|
|
2092
|
+
const componentPath = pathToFileURL(
|
|
2093
|
+
pathMod2.resolve(__dirname2, HadarsFolder, SSR_FILENAME)
|
|
1459
2094
|
).href;
|
|
1460
2095
|
try {
|
|
1461
2096
|
const {
|
|
@@ -1473,7 +2108,7 @@ var run = async (options) => {
|
|
|
1473
2108
|
status: wStatus
|
|
1474
2109
|
});
|
|
1475
2110
|
}
|
|
1476
|
-
const { ReactPage, status, headHtml
|
|
2111
|
+
const { ReactPage, unsuspend, status, headHtml } = await getReactResponse(request, {
|
|
1477
2112
|
document: {
|
|
1478
2113
|
body: Component,
|
|
1479
2114
|
lang: "en",
|
|
@@ -1482,7 +2117,6 @@ var run = async (options) => {
|
|
|
1482
2117
|
getFinalProps
|
|
1483
2118
|
}
|
|
1484
2119
|
});
|
|
1485
|
-
const unsuspend = renderPayload.appProps.context?._unsuspend ?? null;
|
|
1486
2120
|
return buildSsrResponse(ReactPage, headHtml, status, getPrecontentHtml, unsuspend);
|
|
1487
2121
|
} catch (err) {
|
|
1488
2122
|
console.error("[hadars] SSR render error:", err);
|