hadars 0.1.16 → 0.1.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/chunk-TGSIYGY2.js +40 -0
- package/dist/cli.js +695 -165
- 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 +874 -0
- package/dist/slim-react/index.d.ts +180 -0
- package/dist/slim-react/index.js +784 -0
- package/dist/slim-react/jsx-runtime.cjs +51 -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 +619 -108
- package/dist/ssr-watch.js +37 -13
- package/dist/utils/Head.tsx +3 -6
- package/index.ts +1 -1
- package/package.json +2 -2
- package/src/build.ts +55 -49
- 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 +686 -0
- package/src/slim-react/renderContext.ts +105 -0
- package/src/slim-react/types.ts +27 -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 +45 -15
- package/src/utils/segmentCache.ts +87 -0
package/dist/cli.js
CHANGED
|
@@ -134,24 +134,509 @@ 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 FRAGMENT_TYPE = Symbol.for("react.fragment");
|
|
140
|
+
var SUSPENSE_TYPE = Symbol.for("react.suspense");
|
|
141
|
+
|
|
142
|
+
// src/slim-react/jsx.ts
|
|
143
|
+
var Fragment = FRAGMENT_TYPE;
|
|
144
|
+
function createElement(type, props, ...children) {
|
|
145
|
+
const normalizedProps = { ...props || {} };
|
|
146
|
+
if (children.length === 1) {
|
|
147
|
+
normalizedProps.children = children[0];
|
|
148
|
+
} else if (children.length > 1) {
|
|
149
|
+
normalizedProps.children = children;
|
|
150
|
+
}
|
|
151
|
+
const key = normalizedProps.key ?? null;
|
|
152
|
+
delete normalizedProps.key;
|
|
153
|
+
return {
|
|
154
|
+
$$typeof: SLIM_ELEMENT,
|
|
155
|
+
type,
|
|
156
|
+
props: normalizedProps,
|
|
157
|
+
key
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// src/slim-react/renderContext.ts
|
|
162
|
+
var GLOBAL_KEY = "__slimReactRenderState";
|
|
163
|
+
var EMPTY = { id: 0, overflow: "", bits: 0 };
|
|
164
|
+
function s() {
|
|
165
|
+
const g = globalThis;
|
|
166
|
+
if (!g[GLOBAL_KEY]) {
|
|
167
|
+
g[GLOBAL_KEY] = { currentTreeContext: { ...EMPTY }, localIdCounter: 0, idPrefix: "" };
|
|
168
|
+
}
|
|
169
|
+
return g[GLOBAL_KEY];
|
|
170
|
+
}
|
|
171
|
+
function resetRenderState() {
|
|
172
|
+
const st = s();
|
|
173
|
+
st.currentTreeContext = { ...EMPTY };
|
|
174
|
+
st.localIdCounter = 0;
|
|
175
|
+
}
|
|
176
|
+
function pushTreeContext(totalChildren, index) {
|
|
177
|
+
const st = s();
|
|
178
|
+
const saved = { ...st.currentTreeContext };
|
|
179
|
+
const pendingBits = 32 - Math.clz32(totalChildren);
|
|
180
|
+
const slot = index + 1;
|
|
181
|
+
const totalBits = st.currentTreeContext.bits + pendingBits;
|
|
182
|
+
if (totalBits <= 30) {
|
|
183
|
+
st.currentTreeContext = {
|
|
184
|
+
id: st.currentTreeContext.id << pendingBits | slot,
|
|
185
|
+
overflow: st.currentTreeContext.overflow,
|
|
186
|
+
bits: totalBits
|
|
187
|
+
};
|
|
188
|
+
} else {
|
|
189
|
+
let newOverflow = st.currentTreeContext.overflow;
|
|
190
|
+
if (st.currentTreeContext.bits > 0)
|
|
191
|
+
newOverflow += st.currentTreeContext.id.toString(32);
|
|
192
|
+
st.currentTreeContext = { id: 1 << pendingBits | slot, overflow: newOverflow, bits: pendingBits };
|
|
193
|
+
}
|
|
194
|
+
return saved;
|
|
195
|
+
}
|
|
196
|
+
function popTreeContext(saved) {
|
|
197
|
+
s().currentTreeContext = saved;
|
|
198
|
+
}
|
|
199
|
+
function pushComponentScope() {
|
|
200
|
+
const st = s();
|
|
201
|
+
const saved = st.localIdCounter;
|
|
202
|
+
st.localIdCounter = 0;
|
|
203
|
+
return saved;
|
|
204
|
+
}
|
|
205
|
+
function popComponentScope(saved) {
|
|
206
|
+
s().localIdCounter = saved;
|
|
207
|
+
}
|
|
208
|
+
function snapshotContext() {
|
|
209
|
+
const st = s();
|
|
210
|
+
return { tree: { ...st.currentTreeContext }, localId: st.localIdCounter };
|
|
211
|
+
}
|
|
212
|
+
function restoreContext(snap) {
|
|
213
|
+
const st = s();
|
|
214
|
+
st.currentTreeContext = { ...snap.tree };
|
|
215
|
+
st.localIdCounter = snap.localId;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// src/slim-react/render.ts
|
|
219
|
+
var VOID_ELEMENTS = /* @__PURE__ */ new Set([
|
|
220
|
+
"area",
|
|
221
|
+
"base",
|
|
222
|
+
"br",
|
|
223
|
+
"col",
|
|
224
|
+
"embed",
|
|
225
|
+
"hr",
|
|
226
|
+
"img",
|
|
227
|
+
"input",
|
|
228
|
+
"link",
|
|
229
|
+
"meta",
|
|
230
|
+
"param",
|
|
231
|
+
"source",
|
|
232
|
+
"track",
|
|
233
|
+
"wbr"
|
|
234
|
+
]);
|
|
235
|
+
function escapeHtml(str) {
|
|
236
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
237
|
+
}
|
|
238
|
+
function escapeAttr(str) {
|
|
239
|
+
return str.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
240
|
+
}
|
|
241
|
+
function styleObjectToString(style) {
|
|
242
|
+
return Object.entries(style).map(([key, value]) => {
|
|
243
|
+
const cssKey = key.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
|
|
244
|
+
return `${cssKey}:${value}`;
|
|
245
|
+
}).join(";");
|
|
246
|
+
}
|
|
247
|
+
var SVG_ATTR_MAP = {
|
|
248
|
+
// Presentation / geometry
|
|
249
|
+
accentHeight: "accent-height",
|
|
250
|
+
alignmentBaseline: "alignment-baseline",
|
|
251
|
+
arabicForm: "arabic-form",
|
|
252
|
+
baselineShift: "baseline-shift",
|
|
253
|
+
capHeight: "cap-height",
|
|
254
|
+
clipPath: "clip-path",
|
|
255
|
+
clipRule: "clip-rule",
|
|
256
|
+
colorInterpolation: "color-interpolation",
|
|
257
|
+
colorInterpolationFilters: "color-interpolation-filters",
|
|
258
|
+
colorProfile: "color-profile",
|
|
259
|
+
dominantBaseline: "dominant-baseline",
|
|
260
|
+
enableBackground: "enable-background",
|
|
261
|
+
fillOpacity: "fill-opacity",
|
|
262
|
+
fillRule: "fill-rule",
|
|
263
|
+
floodColor: "flood-color",
|
|
264
|
+
floodOpacity: "flood-opacity",
|
|
265
|
+
fontFamily: "font-family",
|
|
266
|
+
fontSize: "font-size",
|
|
267
|
+
fontSizeAdjust: "font-size-adjust",
|
|
268
|
+
fontStretch: "font-stretch",
|
|
269
|
+
fontStyle: "font-style",
|
|
270
|
+
fontVariant: "font-variant",
|
|
271
|
+
fontWeight: "font-weight",
|
|
272
|
+
glyphName: "glyph-name",
|
|
273
|
+
glyphOrientationHorizontal: "glyph-orientation-horizontal",
|
|
274
|
+
glyphOrientationVertical: "glyph-orientation-vertical",
|
|
275
|
+
horizAdvX: "horiz-adv-x",
|
|
276
|
+
horizOriginX: "horiz-origin-x",
|
|
277
|
+
imageRendering: "image-rendering",
|
|
278
|
+
letterSpacing: "letter-spacing",
|
|
279
|
+
lightingColor: "lighting-color",
|
|
280
|
+
markerEnd: "marker-end",
|
|
281
|
+
markerMid: "marker-mid",
|
|
282
|
+
markerStart: "marker-start",
|
|
283
|
+
overlinePosition: "overline-position",
|
|
284
|
+
overlineThickness: "overline-thickness",
|
|
285
|
+
paintOrder: "paint-order",
|
|
286
|
+
panose1: "panose-1",
|
|
287
|
+
pointerEvents: "pointer-events",
|
|
288
|
+
renderingIntent: "rendering-intent",
|
|
289
|
+
shapeRendering: "shape-rendering",
|
|
290
|
+
stopColor: "stop-color",
|
|
291
|
+
stopOpacity: "stop-opacity",
|
|
292
|
+
strikethroughPosition: "strikethrough-position",
|
|
293
|
+
strikethroughThickness: "strikethrough-thickness",
|
|
294
|
+
strokeDasharray: "stroke-dasharray",
|
|
295
|
+
strokeDashoffset: "stroke-dashoffset",
|
|
296
|
+
strokeLinecap: "stroke-linecap",
|
|
297
|
+
strokeLinejoin: "stroke-linejoin",
|
|
298
|
+
strokeMiterlimit: "stroke-miterlimit",
|
|
299
|
+
strokeOpacity: "stroke-opacity",
|
|
300
|
+
strokeWidth: "stroke-width",
|
|
301
|
+
textAnchor: "text-anchor",
|
|
302
|
+
textDecoration: "text-decoration",
|
|
303
|
+
textRendering: "text-rendering",
|
|
304
|
+
underlinePosition: "underline-position",
|
|
305
|
+
underlineThickness: "underline-thickness",
|
|
306
|
+
unicodeBidi: "unicode-bidi",
|
|
307
|
+
unicodeRange: "unicode-range",
|
|
308
|
+
unitsPerEm: "units-per-em",
|
|
309
|
+
vAlphabetic: "v-alphabetic",
|
|
310
|
+
vHanging: "v-hanging",
|
|
311
|
+
vIdeographic: "v-ideographic",
|
|
312
|
+
vMathematical: "v-mathematical",
|
|
313
|
+
vertAdvY: "vert-adv-y",
|
|
314
|
+
vertOriginX: "vert-origin-x",
|
|
315
|
+
vertOriginY: "vert-origin-y",
|
|
316
|
+
wordSpacing: "word-spacing",
|
|
317
|
+
writingMode: "writing-mode",
|
|
318
|
+
xHeight: "x-height",
|
|
319
|
+
// Namespace-prefixed
|
|
320
|
+
xlinkActuate: "xlink:actuate",
|
|
321
|
+
xlinkArcrole: "xlink:arcrole",
|
|
322
|
+
xlinkHref: "xlink:href",
|
|
323
|
+
xlinkRole: "xlink:role",
|
|
324
|
+
xlinkShow: "xlink:show",
|
|
325
|
+
xlinkTitle: "xlink:title",
|
|
326
|
+
xlinkType: "xlink:type",
|
|
327
|
+
xmlBase: "xml:base",
|
|
328
|
+
xmlLang: "xml:lang",
|
|
329
|
+
xmlSpace: "xml:space",
|
|
330
|
+
xmlns: "xmlns",
|
|
331
|
+
xmlnsXlink: "xmlns:xlink",
|
|
332
|
+
// Filter / lighting
|
|
333
|
+
baseFrequency: "baseFrequency",
|
|
334
|
+
colorInterpolation_filters: "color-interpolation-filters",
|
|
335
|
+
diffuseConstant: "diffuseConstant",
|
|
336
|
+
edgeMode: "edgeMode",
|
|
337
|
+
filterUnits: "filterUnits",
|
|
338
|
+
gradientTransform: "gradientTransform",
|
|
339
|
+
gradientUnits: "gradientUnits",
|
|
340
|
+
kernelMatrix: "kernelMatrix",
|
|
341
|
+
kernelUnitLength: "kernelUnitLength",
|
|
342
|
+
lengthAdjust: "lengthAdjust",
|
|
343
|
+
limitingConeAngle: "limitingConeAngle",
|
|
344
|
+
markerHeight: "markerHeight",
|
|
345
|
+
markerWidth: "markerWidth",
|
|
346
|
+
maskContentUnits: "maskContentUnits",
|
|
347
|
+
maskUnits: "maskUnits",
|
|
348
|
+
numOctaves: "numOctaves",
|
|
349
|
+
pathLength: "pathLength",
|
|
350
|
+
patternContentUnits: "patternContentUnits",
|
|
351
|
+
patternTransform: "patternTransform",
|
|
352
|
+
patternUnits: "patternUnits",
|
|
353
|
+
pointsAtX: "pointsAtX",
|
|
354
|
+
pointsAtY: "pointsAtY",
|
|
355
|
+
pointsAtZ: "pointsAtZ",
|
|
356
|
+
preserveAspectRatio: "preserveAspectRatio",
|
|
357
|
+
primitiveUnits: "primitiveUnits",
|
|
358
|
+
refX: "refX",
|
|
359
|
+
refY: "refY",
|
|
360
|
+
repeatCount: "repeatCount",
|
|
361
|
+
repeatDur: "repeatDur",
|
|
362
|
+
specularConstant: "specularConstant",
|
|
363
|
+
specularExponent: "specularExponent",
|
|
364
|
+
spreadMethod: "spreadMethod",
|
|
365
|
+
startOffset: "startOffset",
|
|
366
|
+
stdDeviation: "stdDeviation",
|
|
367
|
+
stitchTiles: "stitchTiles",
|
|
368
|
+
surfaceScale: "surfaceScale",
|
|
369
|
+
systemLanguage: "systemLanguage",
|
|
370
|
+
tableValues: "tableValues",
|
|
371
|
+
targetX: "targetX",
|
|
372
|
+
targetY: "targetY",
|
|
373
|
+
textLength: "textLength",
|
|
374
|
+
viewBox: "viewBox",
|
|
375
|
+
xChannelSelector: "xChannelSelector",
|
|
376
|
+
yChannelSelector: "yChannelSelector"
|
|
377
|
+
};
|
|
378
|
+
function renderAttributes(props, isSvg) {
|
|
379
|
+
let attrs = "";
|
|
380
|
+
for (const [key, value] of Object.entries(props)) {
|
|
381
|
+
if (key === "children" || key === "key" || key === "ref" || key === "dangerouslySetInnerHTML")
|
|
382
|
+
continue;
|
|
383
|
+
if (key.startsWith("on") && key.length > 2 && key[2] === key[2].toUpperCase())
|
|
384
|
+
continue;
|
|
385
|
+
let attrName;
|
|
386
|
+
if (isSvg && key in SVG_ATTR_MAP) {
|
|
387
|
+
attrName = SVG_ATTR_MAP[key];
|
|
388
|
+
} else {
|
|
389
|
+
attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key === "tabIndex" ? "tabindex" : key;
|
|
390
|
+
}
|
|
391
|
+
if (value === false || value == null)
|
|
392
|
+
continue;
|
|
393
|
+
if (value === true) {
|
|
394
|
+
attrs += ` ${attrName}`;
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
if (key === "style" && typeof value === "object") {
|
|
398
|
+
attrs += ` style="${escapeAttr(styleObjectToString(value))}"`;
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
attrs += ` ${attrName}="${escapeAttr(String(value))}"`;
|
|
402
|
+
}
|
|
403
|
+
return attrs;
|
|
404
|
+
}
|
|
405
|
+
var BufferWriter = class {
|
|
406
|
+
chunks = [];
|
|
407
|
+
write(chunk) {
|
|
408
|
+
this.chunks.push(chunk);
|
|
409
|
+
}
|
|
410
|
+
flush(target) {
|
|
411
|
+
for (const c of this.chunks)
|
|
412
|
+
target.write(c);
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
function renderNode(node, writer, isSvg = false) {
|
|
416
|
+
if (node == null || typeof node === "boolean")
|
|
417
|
+
return;
|
|
418
|
+
if (typeof node === "string") {
|
|
419
|
+
writer.write(escapeHtml(node));
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
if (typeof node === "number") {
|
|
423
|
+
writer.write(String(node));
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
if (Array.isArray(node)) {
|
|
427
|
+
return renderChildArray(node, writer, isSvg);
|
|
428
|
+
}
|
|
429
|
+
if (typeof node === "object" && node !== null && Symbol.iterator in node && !("$$typeof" in node)) {
|
|
430
|
+
return renderChildArray(
|
|
431
|
+
Array.from(node),
|
|
432
|
+
writer,
|
|
433
|
+
isSvg
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
if (typeof node === "object" && node !== null && "$$typeof" in node && node.$$typeof === SLIM_ELEMENT) {
|
|
437
|
+
const element = node;
|
|
438
|
+
const { type, props } = element;
|
|
439
|
+
if (type === FRAGMENT_TYPE) {
|
|
440
|
+
return renderChildren(props.children, writer, isSvg);
|
|
441
|
+
}
|
|
442
|
+
if (type === SUSPENSE_TYPE) {
|
|
443
|
+
return renderSuspense(props, writer, isSvg);
|
|
444
|
+
}
|
|
445
|
+
if (typeof type === "function") {
|
|
446
|
+
return renderComponent(type, props, writer, isSvg);
|
|
447
|
+
}
|
|
448
|
+
if (typeof type === "string") {
|
|
449
|
+
return renderHostElement(type, props, writer, isSvg);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
function renderHostElement(tag, props, writer, isSvg) {
|
|
454
|
+
const enteringSvg = tag === "svg";
|
|
455
|
+
const childSvg = isSvg || enteringSvg;
|
|
456
|
+
const effectiveProps = enteringSvg && !props.xmlns ? { xmlns: "http://www.w3.org/2000/svg", ...props } : props;
|
|
457
|
+
writer.write(`<${tag}${renderAttributes(effectiveProps, childSvg)}>`);
|
|
458
|
+
const childContext = tag === "foreignObject" ? false : childSvg;
|
|
459
|
+
let inner = void 0;
|
|
460
|
+
if (props.dangerouslySetInnerHTML) {
|
|
461
|
+
writer.write(props.dangerouslySetInnerHTML.__html);
|
|
462
|
+
} else {
|
|
463
|
+
inner = renderChildren(props.children, writer, childContext);
|
|
464
|
+
}
|
|
465
|
+
if (inner && typeof inner.then === "function") {
|
|
466
|
+
return inner.then(() => {
|
|
467
|
+
if (!VOID_ELEMENTS.has(tag))
|
|
468
|
+
writer.write(`</${tag}>`);
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
if (!VOID_ELEMENTS.has(tag))
|
|
472
|
+
writer.write(`</${tag}>`);
|
|
473
|
+
}
|
|
474
|
+
var REACT_MEMO = Symbol.for("react.memo");
|
|
475
|
+
var REACT_FORWARD_REF = Symbol.for("react.forward_ref");
|
|
476
|
+
var REACT_PROVIDER = Symbol.for("react.provider");
|
|
477
|
+
var REACT_CONTEXT = Symbol.for("react.context");
|
|
478
|
+
function renderComponent(type, props, writer, isSvg) {
|
|
479
|
+
const typeOf = type?.$$typeof;
|
|
480
|
+
if (typeOf === REACT_MEMO) {
|
|
481
|
+
return renderNode(
|
|
482
|
+
{ $$typeof: SLIM_ELEMENT, type: type.type, props, key: null },
|
|
483
|
+
writer,
|
|
484
|
+
isSvg
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
if (typeOf === REACT_FORWARD_REF) {
|
|
488
|
+
return renderComponent(type.render, props, writer, isSvg);
|
|
489
|
+
}
|
|
490
|
+
const isProvider = "_context" in type || typeOf === REACT_PROVIDER || typeOf === REACT_CONTEXT && "value" in props;
|
|
491
|
+
let prevCtxValue;
|
|
492
|
+
let ctx;
|
|
493
|
+
if (isProvider) {
|
|
494
|
+
ctx = type._context ?? type;
|
|
495
|
+
prevCtxValue = ctx._currentValue;
|
|
496
|
+
ctx._currentValue = props.value;
|
|
497
|
+
}
|
|
498
|
+
const savedScope = pushComponentScope();
|
|
499
|
+
let result;
|
|
500
|
+
try {
|
|
501
|
+
if (type.prototype && typeof type.prototype.render === "function") {
|
|
502
|
+
const instance = new type(props);
|
|
503
|
+
result = instance.render();
|
|
504
|
+
} else {
|
|
505
|
+
result = type(props);
|
|
506
|
+
}
|
|
507
|
+
} catch (e) {
|
|
508
|
+
popComponentScope(savedScope);
|
|
509
|
+
if (isProvider)
|
|
510
|
+
ctx._currentValue = prevCtxValue;
|
|
511
|
+
throw e;
|
|
512
|
+
}
|
|
513
|
+
const finish = () => {
|
|
514
|
+
popComponentScope(savedScope);
|
|
515
|
+
if (isProvider)
|
|
516
|
+
ctx._currentValue = prevCtxValue;
|
|
517
|
+
};
|
|
518
|
+
if (result instanceof Promise) {
|
|
519
|
+
return result.then((resolved) => {
|
|
520
|
+
const r2 = renderNode(resolved, writer, isSvg);
|
|
521
|
+
if (r2 && typeof r2.then === "function") {
|
|
522
|
+
return r2.then(finish);
|
|
523
|
+
}
|
|
524
|
+
finish();
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
const r = renderNode(result, writer, isSvg);
|
|
528
|
+
if (r && typeof r.then === "function") {
|
|
529
|
+
return r.then(finish);
|
|
530
|
+
}
|
|
531
|
+
finish();
|
|
151
532
|
}
|
|
533
|
+
function isTextLike(node) {
|
|
534
|
+
return typeof node === "string" || typeof node === "number";
|
|
535
|
+
}
|
|
536
|
+
function renderChildArray(children, writer, isSvg) {
|
|
537
|
+
const totalChildren = children.length;
|
|
538
|
+
for (let i = 0; i < totalChildren; i++) {
|
|
539
|
+
if (i > 0 && isTextLike(children[i]) && isTextLike(children[i - 1])) {
|
|
540
|
+
writer.write("<!-- -->");
|
|
541
|
+
}
|
|
542
|
+
const savedTree = pushTreeContext(totalChildren, i);
|
|
543
|
+
const r = renderNode(children[i], writer, isSvg);
|
|
544
|
+
if (r && typeof r.then === "function") {
|
|
545
|
+
return r.then(() => {
|
|
546
|
+
popTreeContext(savedTree);
|
|
547
|
+
return renderChildArrayFrom(children, i + 1, writer, isSvg);
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
popTreeContext(savedTree);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
function renderChildArrayFrom(children, startIndex, writer, isSvg) {
|
|
554
|
+
const totalChildren = children.length;
|
|
555
|
+
for (let i = startIndex; i < totalChildren; i++) {
|
|
556
|
+
if (i > 0 && isTextLike(children[i]) && isTextLike(children[i - 1])) {
|
|
557
|
+
writer.write("<!-- -->");
|
|
558
|
+
}
|
|
559
|
+
const savedTree = pushTreeContext(totalChildren, i);
|
|
560
|
+
const r = renderNode(children[i], writer, isSvg);
|
|
561
|
+
if (r && typeof r.then === "function") {
|
|
562
|
+
return r.then(() => {
|
|
563
|
+
popTreeContext(savedTree);
|
|
564
|
+
return renderChildArrayFrom(children, i + 1, writer, isSvg);
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
popTreeContext(savedTree);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
function renderChildren(children, writer, isSvg = false) {
|
|
571
|
+
if (children == null)
|
|
572
|
+
return;
|
|
573
|
+
if (Array.isArray(children)) {
|
|
574
|
+
return renderChildArray(children, writer, isSvg);
|
|
575
|
+
}
|
|
576
|
+
return renderNode(children, writer, isSvg);
|
|
577
|
+
}
|
|
578
|
+
var MAX_SUSPENSE_RETRIES = 25;
|
|
579
|
+
async function renderSuspense(props, writer, isSvg = false) {
|
|
580
|
+
const { children, fallback } = props;
|
|
581
|
+
let attempts = 0;
|
|
582
|
+
const snap = snapshotContext();
|
|
583
|
+
while (attempts < MAX_SUSPENSE_RETRIES) {
|
|
584
|
+
restoreContext(snap);
|
|
585
|
+
try {
|
|
586
|
+
const buffer = new BufferWriter();
|
|
587
|
+
const r = renderNode(children, buffer, isSvg);
|
|
588
|
+
if (r && typeof r.then === "function") {
|
|
589
|
+
await r;
|
|
590
|
+
}
|
|
591
|
+
writer.write("<!--$-->");
|
|
592
|
+
buffer.flush(writer);
|
|
593
|
+
writer.write("<!--/$-->");
|
|
594
|
+
return;
|
|
595
|
+
} catch (error) {
|
|
596
|
+
if (error && typeof error.then === "function") {
|
|
597
|
+
await error;
|
|
598
|
+
attempts++;
|
|
599
|
+
} else {
|
|
600
|
+
throw error;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
restoreContext(snap);
|
|
605
|
+
writer.write("<!--$?-->");
|
|
606
|
+
if (fallback) {
|
|
607
|
+
const r = renderNode(fallback, writer, isSvg);
|
|
608
|
+
if (r && typeof r.then === "function")
|
|
609
|
+
await r;
|
|
610
|
+
}
|
|
611
|
+
writer.write("<!--/$-->");
|
|
612
|
+
}
|
|
613
|
+
async function renderToString(element) {
|
|
614
|
+
for (let attempt = 0; attempt < MAX_SUSPENSE_RETRIES; attempt++) {
|
|
615
|
+
resetRenderState();
|
|
616
|
+
const chunks = [];
|
|
617
|
+
const writer = { write(c) {
|
|
618
|
+
chunks.push(c);
|
|
619
|
+
} };
|
|
620
|
+
try {
|
|
621
|
+
const r = renderNode(element, writer);
|
|
622
|
+
if (r && typeof r.then === "function")
|
|
623
|
+
await r;
|
|
624
|
+
return chunks.join("");
|
|
625
|
+
} catch (error) {
|
|
626
|
+
if (error && typeof error.then === "function") {
|
|
627
|
+
await error;
|
|
628
|
+
continue;
|
|
629
|
+
}
|
|
630
|
+
throw error;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
throw new Error("[slim-react] renderToString exceeded maximum retries");
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// src/utils/response.tsx
|
|
152
637
|
var ESC = { "&": "&", "<": "<", ">": ">", '"': """ };
|
|
153
|
-
var escAttr = (
|
|
154
|
-
var escText = (
|
|
638
|
+
var escAttr = (s2) => s2.replace(/[&<>"]/g, (c) => ESC[c] ?? c);
|
|
639
|
+
var escText = (s2) => s2.replace(/[&<>]/g, (c) => ESC[c] ?? c);
|
|
155
640
|
var ATTR = {
|
|
156
641
|
className: "class",
|
|
157
642
|
htmlFor: "for",
|
|
@@ -195,77 +680,57 @@ var getHeadHtml = (seoData) => {
|
|
|
195
680
|
var getReactResponse = async (req, opts) => {
|
|
196
681
|
const App = opts.document.body;
|
|
197
682
|
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
683
|
const context = {
|
|
208
|
-
head: {
|
|
209
|
-
title: "Hadars App",
|
|
210
|
-
meta: {},
|
|
211
|
-
link: {},
|
|
212
|
-
style: {},
|
|
213
|
-
script: {},
|
|
214
|
-
status: 200
|
|
215
|
-
},
|
|
216
|
-
_unsuspend: unsuspend
|
|
684
|
+
head: { title: "Hadars App", meta: {}, link: {}, style: {}, script: {}, status: 200 }
|
|
217
685
|
};
|
|
218
686
|
let props = {
|
|
219
687
|
...getInitProps ? await getInitProps(req) : {},
|
|
220
688
|
location: req.location,
|
|
221
689
|
context
|
|
222
690
|
};
|
|
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;
|
|
691
|
+
const unsuspend = { cache: /* @__PURE__ */ new Map() };
|
|
692
|
+
globalThis.__hadarsUnsuspend = unsuspend;
|
|
693
|
+
try {
|
|
694
|
+
let html = await renderToString(createElement(App, props));
|
|
695
|
+
if (getAfterRenderProps) {
|
|
696
|
+
props = await getAfterRenderProps(props, html);
|
|
697
|
+
await renderToString(
|
|
698
|
+
createElement(App, { ...props, location: req.location, context })
|
|
699
|
+
);
|
|
246
700
|
}
|
|
701
|
+
} finally {
|
|
702
|
+
globalThis.__hadarsUnsuspend = null;
|
|
247
703
|
}
|
|
704
|
+
const { context: _, ...restProps } = getFinalProps ? await getFinalProps(props) : props;
|
|
248
705
|
const serverData = {};
|
|
249
|
-
for (const [
|
|
250
|
-
if (
|
|
251
|
-
serverData[
|
|
706
|
+
for (const [key, entry] of unsuspend.cache) {
|
|
707
|
+
if (entry.status === "fulfilled")
|
|
708
|
+
serverData[key] = entry.value;
|
|
252
709
|
}
|
|
253
|
-
const { context: _, ...restProps } = getFinalProps ? await getFinalProps(props) : props;
|
|
254
710
|
const clientProps = {
|
|
255
711
|
...restProps,
|
|
256
712
|
location: req.location,
|
|
257
713
|
...Object.keys(serverData).length > 0 ? { __serverData: serverData } : {}
|
|
258
714
|
};
|
|
259
|
-
const ReactPage =
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
715
|
+
const ReactPage = createElement(
|
|
716
|
+
Fragment,
|
|
717
|
+
null,
|
|
718
|
+
createElement(
|
|
719
|
+
"div",
|
|
720
|
+
{ id: "app" },
|
|
721
|
+
createElement(App, { ...props, location: req.location, context })
|
|
722
|
+
),
|
|
723
|
+
createElement("script", {
|
|
724
|
+
id: "hadars",
|
|
725
|
+
type: "application/json",
|
|
726
|
+
dangerouslySetInnerHTML: {
|
|
727
|
+
__html: JSON.stringify({ hadars: { props: clientProps } }).replace(/</g, "\\u003c")
|
|
728
|
+
}
|
|
729
|
+
})
|
|
730
|
+
);
|
|
267
731
|
return {
|
|
268
732
|
ReactPage,
|
|
733
|
+
unsuspend,
|
|
269
734
|
status: context.head.status,
|
|
270
735
|
headHtml: getHeadHtml(context.head),
|
|
271
736
|
renderPayload: {
|
|
@@ -280,12 +745,12 @@ import rspack from "@rspack/core";
|
|
|
280
745
|
import ReactRefreshPlugin from "@rspack/plugin-react-refresh";
|
|
281
746
|
import path from "node:path";
|
|
282
747
|
import { fileURLToPath } from "node:url";
|
|
283
|
-
import
|
|
748
|
+
import pathMod from "node:path";
|
|
284
749
|
import { existsSync } from "node:fs";
|
|
285
750
|
var __dirname = process.cwd();
|
|
286
|
-
var packageDir =
|
|
287
|
-
var clientScriptPath =
|
|
288
|
-
var loaderPath = existsSync(
|
|
751
|
+
var packageDir = pathMod.dirname(fileURLToPath(import.meta.url));
|
|
752
|
+
var clientScriptPath = pathMod.resolve(packageDir, "template.html");
|
|
753
|
+
var loaderPath = existsSync(pathMod.resolve(packageDir, "loader.cjs")) ? pathMod.resolve(packageDir, "loader.cjs") : pathMod.resolve(packageDir, "loader.ts");
|
|
289
754
|
var getConfigBase = (mode) => {
|
|
290
755
|
const isDev = mode === "development";
|
|
291
756
|
return {
|
|
@@ -445,25 +910,21 @@ var buildCompilerConfig = (entry, opts, includeHotPlugin) => {
|
|
|
445
910
|
const isServerBuild = Boolean(
|
|
446
911
|
opts.output && typeof opts.output === "object" && (opts.output.library || String(opts.output.filename || "").includes("ssr"))
|
|
447
912
|
);
|
|
913
|
+
const slimReactIndex = pathMod.resolve(packageDir, "slim-react", "index.js");
|
|
914
|
+
const slimReactJsx = pathMod.resolve(packageDir, "slim-react", "jsx-runtime.js");
|
|
448
915
|
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
|
|
916
|
+
// Route all React imports to slim-react for SSR.
|
|
917
|
+
react: slimReactIndex,
|
|
918
|
+
"react/jsx-runtime": slimReactJsx,
|
|
919
|
+
"react/jsx-dev-runtime": slimReactJsx,
|
|
920
|
+
// Keep emotion on the project's node_modules (server-safe entry).
|
|
456
921
|
"@emotion/react": path.resolve(process.cwd(), "node_modules", "@emotion", "react"),
|
|
457
922
|
"@emotion/server": path.resolve(process.cwd(), "node_modules", "@emotion", "server"),
|
|
458
923
|
"@emotion/cache": path.resolve(process.cwd(), "node_modules", "@emotion", "cache"),
|
|
459
924
|
"@emotion/styled": path.resolve(process.cwd(), "node_modules", "@emotion", "styled")
|
|
460
925
|
} : void 0;
|
|
461
926
|
const externals = isServerBuild ? [
|
|
462
|
-
|
|
463
|
-
"react-dom",
|
|
464
|
-
// keep common aliases external as well
|
|
465
|
-
"react/jsx-runtime",
|
|
466
|
-
"react/jsx-dev-runtime",
|
|
927
|
+
// react / react-dom are replaced by slim-react via alias above — not external.
|
|
467
928
|
// emotion should be external on server builds to avoid client/browser code
|
|
468
929
|
"@emotion/react",
|
|
469
930
|
"@emotion/server",
|
|
@@ -507,16 +968,44 @@ var buildCompilerConfig = (entry, opts, includeHotPlugin) => {
|
|
|
507
968
|
clean: false
|
|
508
969
|
},
|
|
509
970
|
mode: opts.mode,
|
|
971
|
+
// Persist transformed modules to disk — subsequent starts only recompile
|
|
972
|
+
// changed files, making repeat dev starts significantly faster.
|
|
973
|
+
cache: true,
|
|
510
974
|
externals,
|
|
511
975
|
...optimization !== void 0 ? { optimization } : {},
|
|
512
976
|
plugins: [
|
|
513
977
|
new rspack.HtmlRspackPlugin({
|
|
514
978
|
publicPath: base || "/",
|
|
515
|
-
template: opts.htmlTemplate ?
|
|
979
|
+
template: opts.htmlTemplate ? pathMod.resolve(process.cwd(), opts.htmlTemplate) : clientScriptPath,
|
|
516
980
|
scriptLoading: "module",
|
|
517
981
|
filename: "out.html",
|
|
518
|
-
inject: "
|
|
982
|
+
inject: "head",
|
|
983
|
+
minify: opts.mode === "production"
|
|
519
984
|
}),
|
|
985
|
+
// Add `async` to the emitted module script so DOMContentLoaded fires
|
|
986
|
+
// as soon as HTML is parsed — without waiting for the bundle to execute.
|
|
987
|
+
// `<script type="module" async>` is valid: it downloads in parallel and
|
|
988
|
+
// executes without blocking DOMContentLoaded, while retaining module
|
|
989
|
+
// semantics (strict mode, ES imports, etc.).
|
|
990
|
+
{
|
|
991
|
+
apply(compiler) {
|
|
992
|
+
compiler.hooks.emit.tapAsync("HadarsAsyncModuleScript", (compilation, cb) => {
|
|
993
|
+
const asset = compilation.assets["out.html"];
|
|
994
|
+
if (asset) {
|
|
995
|
+
const html = asset.source();
|
|
996
|
+
const updated = html.replace(
|
|
997
|
+
/(<script\b[^>]*\btype="module"[^>]*)(>)/g,
|
|
998
|
+
(match, before, end) => before.includes("async") ? match : `${before} async${end}`
|
|
999
|
+
);
|
|
1000
|
+
compilation.assets["out.html"] = {
|
|
1001
|
+
source: () => updated,
|
|
1002
|
+
size: () => Buffer.byteLength(updated)
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
cb();
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
},
|
|
520
1009
|
isDev && new ReactRefreshPlugin(),
|
|
521
1010
|
includeHotPlugin && isDev && new rspack.HotModuleReplacementPlugin(),
|
|
522
1011
|
...extraPlugins
|
|
@@ -774,15 +1263,55 @@ async function tryServeFile(filePath) {
|
|
|
774
1263
|
|
|
775
1264
|
// src/build.ts
|
|
776
1265
|
import { RspackDevServer } from "@rspack/dev-server";
|
|
777
|
-
import
|
|
778
|
-
import { fileURLToPath as fileURLToPath2, pathToFileURL
|
|
779
|
-
import { createRequire as createRequire2 } from "node:module";
|
|
1266
|
+
import pathMod2 from "node:path";
|
|
1267
|
+
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "node:url";
|
|
780
1268
|
import crypto from "node:crypto";
|
|
781
1269
|
import fs from "node:fs/promises";
|
|
782
1270
|
import { existsSync as existsSync2 } from "node:fs";
|
|
783
1271
|
import os from "node:os";
|
|
784
1272
|
import { spawn } from "node:child_process";
|
|
785
1273
|
import cluster from "node:cluster";
|
|
1274
|
+
|
|
1275
|
+
// src/utils/segmentCache.ts
|
|
1276
|
+
function getStore() {
|
|
1277
|
+
const g = globalThis;
|
|
1278
|
+
if (!g.__hadarsSegmentStore) {
|
|
1279
|
+
g.__hadarsSegmentStore = /* @__PURE__ */ new Map();
|
|
1280
|
+
}
|
|
1281
|
+
return g.__hadarsSegmentStore;
|
|
1282
|
+
}
|
|
1283
|
+
function setSegment(key, html, ttl) {
|
|
1284
|
+
getStore().set(key, {
|
|
1285
|
+
html,
|
|
1286
|
+
expiresAt: ttl != null ? Date.now() + ttl : null
|
|
1287
|
+
});
|
|
1288
|
+
}
|
|
1289
|
+
function processSegmentCache(html) {
|
|
1290
|
+
let prev;
|
|
1291
|
+
do {
|
|
1292
|
+
prev = html;
|
|
1293
|
+
html = html.replace(
|
|
1294
|
+
/<hadars-c([^>]*)>([\s\S]*?)<\/hadars-c>/g,
|
|
1295
|
+
(match, attrs, content) => {
|
|
1296
|
+
const cacheM = /data-cache="([^"]+)"/.exec(attrs);
|
|
1297
|
+
const keyM = /data-key="([^"]+)"/.exec(attrs);
|
|
1298
|
+
const ttlM = /data-ttl="(\d+)"/.exec(attrs);
|
|
1299
|
+
if (!cacheM || !keyM)
|
|
1300
|
+
return match;
|
|
1301
|
+
if (cacheM[1] === "miss") {
|
|
1302
|
+
setSegment(keyM[1], content, ttlM ? Number(ttlM[1]) : void 0);
|
|
1303
|
+
return content;
|
|
1304
|
+
}
|
|
1305
|
+
if (cacheM[1] === "hit")
|
|
1306
|
+
return content;
|
|
1307
|
+
return match;
|
|
1308
|
+
}
|
|
1309
|
+
);
|
|
1310
|
+
} while (html !== prev);
|
|
1311
|
+
return html;
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
// src/build.ts
|
|
786
1315
|
var encoder = new TextEncoder();
|
|
787
1316
|
async function processHtmlTemplate(templatePath) {
|
|
788
1317
|
const html = await fs.readFile(templatePath, "utf-8");
|
|
@@ -794,6 +1323,14 @@ async function processHtmlTemplate(templatePath) {
|
|
|
794
1323
|
}
|
|
795
1324
|
if (matches.length === 0)
|
|
796
1325
|
return templatePath;
|
|
1326
|
+
await ensureHadarsTmpDir();
|
|
1327
|
+
const sourceHash = crypto.createHash("md5").update(html).digest("hex").slice(0, 8);
|
|
1328
|
+
const cachedPath = pathMod2.join(HADARS_TMP_DIR, `template-${sourceHash}.html`);
|
|
1329
|
+
try {
|
|
1330
|
+
await fs.access(cachedPath);
|
|
1331
|
+
return cachedPath;
|
|
1332
|
+
} catch {
|
|
1333
|
+
}
|
|
797
1334
|
const { default: postcss } = await import("postcss");
|
|
798
1335
|
let plugins = [];
|
|
799
1336
|
try {
|
|
@@ -811,22 +1348,11 @@ async function processHtmlTemplate(templatePath) {
|
|
|
811
1348
|
console.warn("[hadars] PostCSS error processing <style> block in HTML template:", err);
|
|
812
1349
|
}
|
|
813
1350
|
}
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
return tmpPath;
|
|
1351
|
+
await fs.writeFile(cachedPath, processedHtml);
|
|
1352
|
+
return cachedPath;
|
|
817
1353
|
}
|
|
818
1354
|
var HEAD_MARKER = '<meta name="HADARS_HEAD">';
|
|
819
1355
|
var BODY_MARKER = '<meta name="HADARS_BODY">';
|
|
820
|
-
var _renderToString = null;
|
|
821
|
-
async function getRenderToString() {
|
|
822
|
-
if (!_renderToString) {
|
|
823
|
-
const req = createRequire2(pathMod3.resolve(process.cwd(), "__hadars_fake__.js"));
|
|
824
|
-
const resolved = req.resolve("react-dom/server");
|
|
825
|
-
const mod = await import(pathToFileURL2(resolved).href);
|
|
826
|
-
_renderToString = mod.renderToString;
|
|
827
|
-
}
|
|
828
|
-
return _renderToString;
|
|
829
|
-
}
|
|
830
1356
|
var RenderWorkerPool = class {
|
|
831
1357
|
workers = [];
|
|
832
1358
|
pending = /* @__PURE__ */ new Map();
|
|
@@ -931,19 +1457,18 @@ var RenderWorkerPool = class {
|
|
|
931
1457
|
}
|
|
932
1458
|
};
|
|
933
1459
|
async function buildSsrResponse(ReactPage, headHtml, status, getPrecontentHtml, unsuspendForRender) {
|
|
934
|
-
const renderToString = await getRenderToString();
|
|
935
1460
|
const responseStream = new ReadableStream({
|
|
936
1461
|
async start(controller) {
|
|
937
1462
|
const [precontentHtml, postContent] = await getPrecontentHtml(headHtml);
|
|
938
1463
|
controller.enqueue(encoder.encode(precontentHtml));
|
|
939
|
-
await Promise.resolve();
|
|
940
1464
|
let bodyHtml;
|
|
941
1465
|
try {
|
|
942
1466
|
globalThis.__hadarsUnsuspend = unsuspendForRender;
|
|
943
|
-
bodyHtml = renderToString(ReactPage);
|
|
1467
|
+
bodyHtml = await renderToString(ReactPage);
|
|
944
1468
|
} finally {
|
|
945
1469
|
globalThis.__hadarsUnsuspend = null;
|
|
946
1470
|
}
|
|
1471
|
+
bodyHtml = processSegmentCache(bodyHtml);
|
|
947
1472
|
controller.enqueue(encoder.encode(bodyHtml + postContent));
|
|
948
1473
|
controller.close();
|
|
949
1474
|
}
|
|
@@ -1072,6 +1597,8 @@ var __dirname2 = process.cwd();
|
|
|
1072
1597
|
var getSuffix = (mode) => mode === "development" ? `?v=${Date.now()}` : "";
|
|
1073
1598
|
var HadarsFolder = "./.hadars";
|
|
1074
1599
|
var StaticPath = `${HadarsFolder}/static`;
|
|
1600
|
+
var HADARS_TMP_DIR = pathMod2.join(os.tmpdir(), "hadars");
|
|
1601
|
+
var ensureHadarsTmpDir = () => fs.mkdir(HADARS_TMP_DIR, { recursive: true });
|
|
1075
1602
|
var validateOptions = (options) => {
|
|
1076
1603
|
if (!options.entry) {
|
|
1077
1604
|
throw new Error("Entry file is required");
|
|
@@ -1081,8 +1608,8 @@ var validateOptions = (options) => {
|
|
|
1081
1608
|
}
|
|
1082
1609
|
};
|
|
1083
1610
|
var resolveWorkerCmd = (packageDir2) => {
|
|
1084
|
-
const tsPath =
|
|
1085
|
-
const jsPath =
|
|
1611
|
+
const tsPath = pathMod2.resolve(packageDir2, "ssr-watch.ts");
|
|
1612
|
+
const jsPath = pathMod2.resolve(packageDir2, "ssr-watch.js");
|
|
1086
1613
|
if (isBun && existsSync2(tsPath)) {
|
|
1087
1614
|
return ["bun", tsPath];
|
|
1088
1615
|
}
|
|
@@ -1116,10 +1643,10 @@ var dev = async (options) => {
|
|
|
1116
1643
|
const handleProxy = createProxyHandler(options);
|
|
1117
1644
|
const handleWS = upgradeHandler(options);
|
|
1118
1645
|
const handler = options.fetch;
|
|
1119
|
-
const entry =
|
|
1646
|
+
const entry = pathMod2.resolve(__dirname2, options.entry);
|
|
1120
1647
|
const hmrPort = options.hmrPort ?? port + 1;
|
|
1121
|
-
const packageDir2 =
|
|
1122
|
-
const clientScriptPath2 =
|
|
1648
|
+
const packageDir2 = pathMod2.dirname(fileURLToPath2(import.meta.url));
|
|
1649
|
+
const clientScriptPath2 = pathMod2.resolve(packageDir2, "utils", "clientScript.tsx");
|
|
1123
1650
|
let clientScript = "";
|
|
1124
1651
|
try {
|
|
1125
1652
|
clientScript = (await fs.readFile(clientScriptPath2, "utf-8")).replace("$_MOD_PATH$", entry + getSuffix(options.mode));
|
|
@@ -1127,15 +1654,16 @@ var dev = async (options) => {
|
|
|
1127
1654
|
console.error("Failed to read client script from package dist, falling back to src", err);
|
|
1128
1655
|
throw err;
|
|
1129
1656
|
}
|
|
1130
|
-
|
|
1657
|
+
await ensureHadarsTmpDir();
|
|
1658
|
+
const tmpFilePath = pathMod2.join(HADARS_TMP_DIR, `client-${Date.now()}.tsx`);
|
|
1131
1659
|
await fs.writeFile(tmpFilePath, clientScript);
|
|
1132
1660
|
let ssrBuildId = crypto.randomBytes(4).toString("hex");
|
|
1133
|
-
const resolvedHtmlTemplate = options.htmlTemplate ? await processHtmlTemplate(
|
|
1661
|
+
const resolvedHtmlTemplate = options.htmlTemplate ? await processHtmlTemplate(pathMod2.resolve(__dirname2, options.htmlTemplate)) : void 0;
|
|
1134
1662
|
const clientCompiler = createClientCompiler(tmpFilePath, {
|
|
1135
1663
|
target: "web",
|
|
1136
1664
|
output: {
|
|
1137
1665
|
filename: "index.js",
|
|
1138
|
-
path:
|
|
1666
|
+
path: pathMod2.resolve(__dirname2, StaticPath)
|
|
1139
1667
|
},
|
|
1140
1668
|
base: baseURL,
|
|
1141
1669
|
mode: "development",
|
|
@@ -1234,29 +1762,31 @@ var dev = async (options) => {
|
|
|
1234
1762
|
stdoutReader = null;
|
|
1235
1763
|
}
|
|
1236
1764
|
})();
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1765
|
+
const readyPromise = Promise.all([clientBuildDone, ssrBuildDone]);
|
|
1766
|
+
readyPromise.then(() => {
|
|
1767
|
+
if (stdoutReader) {
|
|
1768
|
+
const reader = stdoutReader;
|
|
1769
|
+
(async () => {
|
|
1770
|
+
try {
|
|
1771
|
+
while (true) {
|
|
1772
|
+
const { done, value } = await reader.read();
|
|
1773
|
+
if (done)
|
|
1774
|
+
break;
|
|
1775
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
1776
|
+
try {
|
|
1777
|
+
process.stdout.write(chunk);
|
|
1778
|
+
} catch (e) {
|
|
1779
|
+
}
|
|
1780
|
+
if (chunk.includes(rebuildMarker)) {
|
|
1781
|
+
ssrBuildId = crypto.randomBytes(4).toString("hex");
|
|
1782
|
+
console.log("[hadars] SSR bundle updated, build id:", ssrBuildId);
|
|
1783
|
+
}
|
|
1254
1784
|
}
|
|
1785
|
+
} catch (e) {
|
|
1255
1786
|
}
|
|
1256
|
-
}
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
}
|
|
1787
|
+
})();
|
|
1788
|
+
}
|
|
1789
|
+
});
|
|
1260
1790
|
(async () => {
|
|
1261
1791
|
try {
|
|
1262
1792
|
const r = stderrWebStream.getReader();
|
|
@@ -1273,9 +1803,10 @@ var dev = async (options) => {
|
|
|
1273
1803
|
}
|
|
1274
1804
|
})();
|
|
1275
1805
|
const getPrecontentHtml = makePrecontentHtmlGetter(
|
|
1276
|
-
fs.readFile(
|
|
1806
|
+
readyPromise.then(() => fs.readFile(pathMod2.join(__dirname2, StaticPath, "out.html"), "utf-8"))
|
|
1277
1807
|
);
|
|
1278
1808
|
await serve(port, async (req, ctx) => {
|
|
1809
|
+
await readyPromise;
|
|
1279
1810
|
const request = parseRequest(req);
|
|
1280
1811
|
if (handler) {
|
|
1281
1812
|
const res = await handler(request);
|
|
@@ -1289,15 +1820,15 @@ var dev = async (options) => {
|
|
|
1289
1820
|
return proxied;
|
|
1290
1821
|
const url = new URL(request.url);
|
|
1291
1822
|
const path2 = url.pathname;
|
|
1292
|
-
const staticRes = await tryServeFile(
|
|
1823
|
+
const staticRes = await tryServeFile(pathMod2.join(__dirname2, StaticPath, path2));
|
|
1293
1824
|
if (staticRes)
|
|
1294
1825
|
return staticRes;
|
|
1295
|
-
const projectStaticPath =
|
|
1296
|
-
const projectRes = await tryServeFile(
|
|
1826
|
+
const projectStaticPath = pathMod2.resolve(process.cwd(), "static");
|
|
1827
|
+
const projectRes = await tryServeFile(pathMod2.join(projectStaticPath, path2));
|
|
1297
1828
|
if (projectRes)
|
|
1298
1829
|
return projectRes;
|
|
1299
|
-
const ssrComponentPath =
|
|
1300
|
-
const importPath =
|
|
1830
|
+
const ssrComponentPath = pathMod2.join(__dirname2, HadarsFolder, SSR_FILENAME);
|
|
1831
|
+
const importPath = pathToFileURL(ssrComponentPath).href + `?t=${ssrBuildId}`;
|
|
1301
1832
|
try {
|
|
1302
1833
|
const {
|
|
1303
1834
|
default: Component,
|
|
@@ -1305,7 +1836,7 @@ var dev = async (options) => {
|
|
|
1305
1836
|
getAfterRenderProps,
|
|
1306
1837
|
getFinalProps
|
|
1307
1838
|
} = await import(importPath);
|
|
1308
|
-
const { ReactPage, status, headHtml
|
|
1839
|
+
const { ReactPage, unsuspend, status, headHtml } = await getReactResponse(request, {
|
|
1309
1840
|
document: {
|
|
1310
1841
|
body: Component,
|
|
1311
1842
|
lang: "en",
|
|
@@ -1314,7 +1845,6 @@ var dev = async (options) => {
|
|
|
1314
1845
|
getFinalProps
|
|
1315
1846
|
}
|
|
1316
1847
|
});
|
|
1317
|
-
const unsuspend = renderPayload.appProps.context?._unsuspend ?? null;
|
|
1318
1848
|
return buildSsrResponse(ReactPage, headHtml, status, getPrecontentHtml, unsuspend);
|
|
1319
1849
|
} catch (err) {
|
|
1320
1850
|
console.error("[hadars] SSR render error:", err);
|
|
@@ -1328,19 +1858,20 @@ var dev = async (options) => {
|
|
|
1328
1858
|
};
|
|
1329
1859
|
var build = async (options) => {
|
|
1330
1860
|
validateOptions(options);
|
|
1331
|
-
const entry =
|
|
1332
|
-
const packageDir2 =
|
|
1333
|
-
const clientScriptPath2 =
|
|
1861
|
+
const entry = pathMod2.resolve(__dirname2, options.entry);
|
|
1862
|
+
const packageDir2 = pathMod2.dirname(fileURLToPath2(import.meta.url));
|
|
1863
|
+
const clientScriptPath2 = pathMod2.resolve(packageDir2, "utils", "clientScript.js");
|
|
1334
1864
|
let clientScript = "";
|
|
1335
1865
|
try {
|
|
1336
1866
|
clientScript = (await fs.readFile(clientScriptPath2, "utf-8")).replace("$_MOD_PATH$", entry + getSuffix(options.mode));
|
|
1337
1867
|
} catch (err) {
|
|
1338
|
-
const srcClientPath =
|
|
1868
|
+
const srcClientPath = pathMod2.resolve(packageDir2, "utils", "clientScript.tsx");
|
|
1339
1869
|
clientScript = (await fs.readFile(srcClientPath, "utf-8")).replace("$_MOD_PATH$", entry + `?v=${Date.now()}`);
|
|
1340
1870
|
}
|
|
1341
|
-
|
|
1871
|
+
await ensureHadarsTmpDir();
|
|
1872
|
+
const tmpFilePath = pathMod2.join(HADARS_TMP_DIR, `client-${Date.now()}.tsx`);
|
|
1342
1873
|
await fs.writeFile(tmpFilePath, clientScript);
|
|
1343
|
-
const resolvedHtmlTemplate = options.htmlTemplate ? await processHtmlTemplate(
|
|
1874
|
+
const resolvedHtmlTemplate = options.htmlTemplate ? await processHtmlTemplate(pathMod2.resolve(__dirname2, options.htmlTemplate)) : void 0;
|
|
1344
1875
|
console.log("Building client and server bundles in parallel...");
|
|
1345
1876
|
await Promise.all([
|
|
1346
1877
|
compileEntry(tmpFilePath, {
|
|
@@ -1348,7 +1879,7 @@ var build = async (options) => {
|
|
|
1348
1879
|
output: {
|
|
1349
1880
|
// Content hash: filename is stable when code is unchanged → better browser/CDN cache.
|
|
1350
1881
|
filename: "index.[contenthash:8].js",
|
|
1351
|
-
path:
|
|
1882
|
+
path: pathMod2.resolve(__dirname2, StaticPath)
|
|
1352
1883
|
},
|
|
1353
1884
|
base: options.baseURL,
|
|
1354
1885
|
mode: "production",
|
|
@@ -1357,11 +1888,11 @@ var build = async (options) => {
|
|
|
1357
1888
|
optimization: options.optimization,
|
|
1358
1889
|
htmlTemplate: resolvedHtmlTemplate
|
|
1359
1890
|
}),
|
|
1360
|
-
compileEntry(
|
|
1891
|
+
compileEntry(pathMod2.resolve(__dirname2, options.entry), {
|
|
1361
1892
|
output: {
|
|
1362
1893
|
iife: false,
|
|
1363
1894
|
filename: SSR_FILENAME,
|
|
1364
|
-
path:
|
|
1895
|
+
path: pathMod2.resolve(__dirname2, HadarsFolder),
|
|
1365
1896
|
publicPath: "",
|
|
1366
1897
|
library: { type: "module" }
|
|
1367
1898
|
},
|
|
@@ -1397,16 +1928,16 @@ var run = async (options) => {
|
|
|
1397
1928
|
console.log(`Starting Hadars (run) on port ${port}`);
|
|
1398
1929
|
let renderPool;
|
|
1399
1930
|
if (!isNode && workers > 1) {
|
|
1400
|
-
const packageDir2 =
|
|
1401
|
-
const workerJs =
|
|
1402
|
-
const workerTs =
|
|
1931
|
+
const packageDir2 = pathMod2.dirname(fileURLToPath2(import.meta.url));
|
|
1932
|
+
const workerJs = pathMod2.resolve(packageDir2, "ssr-render-worker.js");
|
|
1933
|
+
const workerTs = pathMod2.resolve(packageDir2, "ssr-render-worker.ts");
|
|
1403
1934
|
const workerFile = existsSync2(workerJs) ? workerJs : workerTs;
|
|
1404
|
-
const ssrBundlePath =
|
|
1935
|
+
const ssrBundlePath = pathMod2.resolve(__dirname2, HadarsFolder, SSR_FILENAME);
|
|
1405
1936
|
renderPool = new RenderWorkerPool(workerFile, workers, ssrBundlePath);
|
|
1406
1937
|
console.log(`[hadars] SSR render pool: ${workers} worker threads`);
|
|
1407
1938
|
}
|
|
1408
1939
|
const getPrecontentHtml = makePrecontentHtmlGetter(
|
|
1409
|
-
fs.readFile(
|
|
1940
|
+
fs.readFile(pathMod2.join(__dirname2, StaticPath, "out.html"), "utf-8")
|
|
1410
1941
|
);
|
|
1411
1942
|
const runHandler = async (req, ctx) => {
|
|
1412
1943
|
const request = parseRequest(req);
|
|
@@ -1422,23 +1953,23 @@ var run = async (options) => {
|
|
|
1422
1953
|
return proxied;
|
|
1423
1954
|
const url = new URL(request.url);
|
|
1424
1955
|
const path2 = url.pathname;
|
|
1425
|
-
const staticRes = await tryServeFile(
|
|
1956
|
+
const staticRes = await tryServeFile(pathMod2.join(__dirname2, StaticPath, path2));
|
|
1426
1957
|
if (staticRes)
|
|
1427
1958
|
return staticRes;
|
|
1428
|
-
const projectStaticPath =
|
|
1429
|
-
const projectRes = await tryServeFile(
|
|
1959
|
+
const projectStaticPath = pathMod2.resolve(process.cwd(), "static");
|
|
1960
|
+
const projectRes = await tryServeFile(pathMod2.join(projectStaticPath, path2));
|
|
1430
1961
|
if (projectRes)
|
|
1431
1962
|
return projectRes;
|
|
1432
1963
|
const routeClean = path2.replace(/(^\/|\/$)/g, "");
|
|
1433
1964
|
if (routeClean) {
|
|
1434
1965
|
const routeRes = await tryServeFile(
|
|
1435
|
-
|
|
1966
|
+
pathMod2.join(__dirname2, StaticPath, routeClean, "index.html")
|
|
1436
1967
|
);
|
|
1437
1968
|
if (routeRes)
|
|
1438
1969
|
return routeRes;
|
|
1439
1970
|
}
|
|
1440
|
-
const componentPath =
|
|
1441
|
-
|
|
1971
|
+
const componentPath = pathToFileURL(
|
|
1972
|
+
pathMod2.resolve(__dirname2, HadarsFolder, SSR_FILENAME)
|
|
1442
1973
|
).href;
|
|
1443
1974
|
try {
|
|
1444
1975
|
const {
|
|
@@ -1456,7 +1987,7 @@ var run = async (options) => {
|
|
|
1456
1987
|
status: wStatus
|
|
1457
1988
|
});
|
|
1458
1989
|
}
|
|
1459
|
-
const { ReactPage, status, headHtml
|
|
1990
|
+
const { ReactPage, unsuspend, status, headHtml } = await getReactResponse(request, {
|
|
1460
1991
|
document: {
|
|
1461
1992
|
body: Component,
|
|
1462
1993
|
lang: "en",
|
|
@@ -1465,7 +1996,6 @@ var run = async (options) => {
|
|
|
1465
1996
|
getFinalProps
|
|
1466
1997
|
}
|
|
1467
1998
|
});
|
|
1468
|
-
const unsuspend = renderPayload.appProps.context?._unsuspend ?? null;
|
|
1469
1999
|
return buildSsrResponse(ReactPage, headHtml, status, getPrecontentHtml, unsuspend);
|
|
1470
2000
|
} catch (err) {
|
|
1471
2001
|
console.error("[hadars] SSR render error:", err);
|