hadars 0.1.23 → 0.1.24
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-EZUCZHGV.js +11 -0
- package/dist/cli.js +100 -36
- package/dist/index.js +2 -0
- package/dist/slim-react/index.cjs +89 -41
- package/dist/slim-react/index.d.ts +1 -0
- package/dist/slim-react/index.js +92 -41
- package/dist/slim-react/jsx-runtime.js +1 -0
- package/dist/ssr-render-worker.js +91 -36
- package/dist/ssr-watch.js +10 -0
- package/package.json +1 -1
- package/src/slim-react/context.ts +3 -1
- package/src/slim-react/hooks.ts +3 -3
- package/src/slim-react/index.ts +2 -4
- package/src/slim-react/render.ts +37 -32
- package/src/slim-react/renderContext.ts +63 -4
- package/src/utils/rspack.ts +5 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined")
|
|
5
|
+
return require.apply(this, arguments);
|
|
6
|
+
throw new Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
__require
|
|
11
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
+
}) : x)(function(x) {
|
|
5
|
+
if (typeof require !== "undefined")
|
|
6
|
+
return require.apply(this, arguments);
|
|
7
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
8
|
+
});
|
|
2
9
|
|
|
3
10
|
// cli.ts
|
|
4
11
|
import { spawn as spawn2 } from "node:child_process";
|
|
@@ -160,6 +167,40 @@ function createElement(type, props, ...children) {
|
|
|
160
167
|
}
|
|
161
168
|
|
|
162
169
|
// src/slim-react/renderContext.ts
|
|
170
|
+
var CONTEXT_STORE_KEY = "__slimReactContextStore";
|
|
171
|
+
var _g = globalThis;
|
|
172
|
+
if (!_g[CONTEXT_STORE_KEY]) {
|
|
173
|
+
try {
|
|
174
|
+
const { AsyncLocalStorage } = __require("node:async_hooks");
|
|
175
|
+
_g[CONTEXT_STORE_KEY] = new AsyncLocalStorage();
|
|
176
|
+
} catch {
|
|
177
|
+
_g[CONTEXT_STORE_KEY] = null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
var _contextStore = _g[CONTEXT_STORE_KEY];
|
|
181
|
+
function runWithContextStore(fn) {
|
|
182
|
+
return _contextStore ? _contextStore.run(/* @__PURE__ */ new Map(), fn) : fn();
|
|
183
|
+
}
|
|
184
|
+
function getContextValue(context) {
|
|
185
|
+
const store = _contextStore?.getStore();
|
|
186
|
+
if (store && store.has(context))
|
|
187
|
+
return store.get(context);
|
|
188
|
+
const c = context;
|
|
189
|
+
return "_defaultValue" in c ? c._defaultValue : c._currentValue;
|
|
190
|
+
}
|
|
191
|
+
function pushContextValue(context, value) {
|
|
192
|
+
const store = _contextStore?.getStore();
|
|
193
|
+
const c = context;
|
|
194
|
+
const prev = store && store.has(context) ? store.get(context) : "_defaultValue" in c ? c._defaultValue : c._currentValue;
|
|
195
|
+
if (store)
|
|
196
|
+
store.set(context, value);
|
|
197
|
+
return prev;
|
|
198
|
+
}
|
|
199
|
+
function popContextValue(context, prev) {
|
|
200
|
+
const store = _contextStore?.getStore();
|
|
201
|
+
if (store)
|
|
202
|
+
store.set(context, prev);
|
|
203
|
+
}
|
|
163
204
|
var GLOBAL_KEY = "__slimReactRenderState";
|
|
164
205
|
var EMPTY = { id: 0, overflow: "", bits: 0 };
|
|
165
206
|
function s() {
|
|
@@ -572,7 +613,7 @@ function renderComponent(type, props, writer, isSvg) {
|
|
|
572
613
|
}
|
|
573
614
|
if (typeOf === REACT_CONSUMER) {
|
|
574
615
|
const ctx2 = type._context;
|
|
575
|
-
const value = ctx2
|
|
616
|
+
const value = ctx2 ? getContextValue(ctx2) : void 0;
|
|
576
617
|
const result2 = typeof props.children === "function" ? props.children(value) : null;
|
|
577
618
|
const savedScope2 = pushComponentScope();
|
|
578
619
|
const finish2 = () => popComponentScope(savedScope2);
|
|
@@ -588,18 +629,20 @@ function renderComponent(type, props, writer, isSvg) {
|
|
|
588
629
|
let ctx;
|
|
589
630
|
if (isProvider) {
|
|
590
631
|
ctx = type._context ?? type;
|
|
591
|
-
prevCtxValue = ctx.
|
|
592
|
-
ctx._currentValue = props.value;
|
|
632
|
+
prevCtxValue = pushContextValue(ctx, props.value);
|
|
593
633
|
}
|
|
594
634
|
const savedScope = pushComponentScope();
|
|
595
635
|
if (isProvider && typeof type !== "function") {
|
|
596
636
|
const finish2 = () => {
|
|
597
637
|
popComponentScope(savedScope);
|
|
598
|
-
ctx
|
|
638
|
+
popContextValue(ctx, prevCtxValue);
|
|
599
639
|
};
|
|
600
640
|
const r2 = renderChildren(props.children, writer, isSvg);
|
|
601
641
|
if (r2 && typeof r2.then === "function") {
|
|
602
|
-
return r2.then(finish2)
|
|
642
|
+
return r2.then(finish2, (e) => {
|
|
643
|
+
finish2();
|
|
644
|
+
throw e;
|
|
645
|
+
});
|
|
603
646
|
}
|
|
604
647
|
finish2();
|
|
605
648
|
return;
|
|
@@ -620,26 +663,35 @@ function renderComponent(type, props, writer, isSvg) {
|
|
|
620
663
|
} catch (e) {
|
|
621
664
|
popComponentScope(savedScope);
|
|
622
665
|
if (isProvider)
|
|
623
|
-
ctx
|
|
666
|
+
popContextValue(ctx, prevCtxValue);
|
|
624
667
|
throw e;
|
|
625
668
|
}
|
|
626
669
|
const finish = () => {
|
|
627
670
|
popComponentScope(savedScope);
|
|
628
671
|
if (isProvider)
|
|
629
|
-
ctx
|
|
672
|
+
popContextValue(ctx, prevCtxValue);
|
|
630
673
|
};
|
|
631
674
|
if (result instanceof Promise) {
|
|
632
675
|
return result.then((resolved) => {
|
|
633
676
|
const r2 = renderNode(resolved, writer, isSvg);
|
|
634
677
|
if (r2 && typeof r2.then === "function") {
|
|
635
|
-
return r2.then(finish)
|
|
678
|
+
return r2.then(finish, (e) => {
|
|
679
|
+
finish();
|
|
680
|
+
throw e;
|
|
681
|
+
});
|
|
636
682
|
}
|
|
637
683
|
finish();
|
|
684
|
+
}, (e) => {
|
|
685
|
+
finish();
|
|
686
|
+
throw e;
|
|
638
687
|
});
|
|
639
688
|
}
|
|
640
689
|
const r = renderNode(result, writer, isSvg);
|
|
641
690
|
if (r && typeof r.then === "function") {
|
|
642
|
-
return r.then(finish)
|
|
691
|
+
return r.then(finish, (e) => {
|
|
692
|
+
finish();
|
|
693
|
+
throw e;
|
|
694
|
+
});
|
|
643
695
|
}
|
|
644
696
|
finish();
|
|
645
697
|
}
|
|
@@ -723,35 +775,37 @@ async function renderSuspense(props, writer, isSvg = false) {
|
|
|
723
775
|
}
|
|
724
776
|
writer.write("<!--/$-->");
|
|
725
777
|
}
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
778
|
+
function renderToString(element) {
|
|
779
|
+
return runWithContextStore(async () => {
|
|
780
|
+
for (let attempt = 0; attempt < MAX_SUSPENSE_RETRIES; attempt++) {
|
|
781
|
+
resetRenderState();
|
|
782
|
+
const chunks = [];
|
|
783
|
+
const writer = {
|
|
784
|
+
lastWasText: false,
|
|
785
|
+
write(c) {
|
|
786
|
+
chunks.push(c);
|
|
787
|
+
this.lastWasText = false;
|
|
788
|
+
},
|
|
789
|
+
text(s2) {
|
|
790
|
+
chunks.push(s2);
|
|
791
|
+
this.lastWasText = true;
|
|
792
|
+
}
|
|
793
|
+
};
|
|
794
|
+
try {
|
|
795
|
+
const r = renderNode(element, writer);
|
|
796
|
+
if (r && typeof r.then === "function")
|
|
797
|
+
await r;
|
|
798
|
+
return chunks.join("");
|
|
799
|
+
} catch (error) {
|
|
800
|
+
if (error && typeof error.then === "function") {
|
|
801
|
+
await error;
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
804
|
+
throw error;
|
|
750
805
|
}
|
|
751
|
-
throw error;
|
|
752
806
|
}
|
|
753
|
-
|
|
754
|
-
|
|
807
|
+
throw new Error("[slim-react] renderToString exceeded maximum retries");
|
|
808
|
+
});
|
|
755
809
|
}
|
|
756
810
|
|
|
757
811
|
// src/utils/response.tsx
|
|
@@ -1039,6 +1093,16 @@ var buildCompilerConfig = (entry, opts, includeHotPlugin) => {
|
|
|
1039
1093
|
"@emotion/styled": path.resolve(process.cwd(), "node_modules", "@emotion", "styled")
|
|
1040
1094
|
} : void 0;
|
|
1041
1095
|
const externals = isServerBuild ? [
|
|
1096
|
+
// Node.js built-ins — must not be bundled; resolved by the runtime.
|
|
1097
|
+
// Both the bare name and the node: prefix are listed because rspack
|
|
1098
|
+
// may encounter either form depending on how the import is written.
|
|
1099
|
+
"node:async_hooks",
|
|
1100
|
+
"async_hooks",
|
|
1101
|
+
"node:fs",
|
|
1102
|
+
"node:path",
|
|
1103
|
+
"node:os",
|
|
1104
|
+
"node:stream",
|
|
1105
|
+
"node:util",
|
|
1042
1106
|
// react / react-dom are replaced by slim-react via alias above — not external.
|
|
1043
1107
|
// emotion should be external on server builds to avoid client/browser code
|
|
1044
1108
|
"@emotion/react",
|
package/dist/index.js
CHANGED
|
@@ -100,6 +100,40 @@ function createElement(type, props, ...children) {
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
// src/slim-react/renderContext.ts
|
|
103
|
+
var CONTEXT_STORE_KEY = "__slimReactContextStore";
|
|
104
|
+
var _g = globalThis;
|
|
105
|
+
if (!_g[CONTEXT_STORE_KEY]) {
|
|
106
|
+
try {
|
|
107
|
+
const { AsyncLocalStorage } = require("async_hooks");
|
|
108
|
+
_g[CONTEXT_STORE_KEY] = new AsyncLocalStorage();
|
|
109
|
+
} catch {
|
|
110
|
+
_g[CONTEXT_STORE_KEY] = null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
var _contextStore = _g[CONTEXT_STORE_KEY];
|
|
114
|
+
function runWithContextStore(fn) {
|
|
115
|
+
return _contextStore ? _contextStore.run(/* @__PURE__ */ new Map(), fn) : fn();
|
|
116
|
+
}
|
|
117
|
+
function getContextValue(context) {
|
|
118
|
+
const store = _contextStore?.getStore();
|
|
119
|
+
if (store && store.has(context))
|
|
120
|
+
return store.get(context);
|
|
121
|
+
const c = context;
|
|
122
|
+
return "_defaultValue" in c ? c._defaultValue : c._currentValue;
|
|
123
|
+
}
|
|
124
|
+
function pushContextValue(context, value) {
|
|
125
|
+
const store = _contextStore?.getStore();
|
|
126
|
+
const c = context;
|
|
127
|
+
const prev = store && store.has(context) ? store.get(context) : "_defaultValue" in c ? c._defaultValue : c._currentValue;
|
|
128
|
+
if (store)
|
|
129
|
+
store.set(context, value);
|
|
130
|
+
return prev;
|
|
131
|
+
}
|
|
132
|
+
function popContextValue(context, prev) {
|
|
133
|
+
const store = _contextStore?.getStore();
|
|
134
|
+
if (store)
|
|
135
|
+
store.set(context, prev);
|
|
136
|
+
}
|
|
103
137
|
var GLOBAL_KEY = "__slimReactRenderState";
|
|
104
138
|
var EMPTY = { id: 0, overflow: "", bits: 0 };
|
|
105
139
|
function s() {
|
|
@@ -225,8 +259,8 @@ function useActionState(_action, initialState, _permalink) {
|
|
|
225
259
|
}, false];
|
|
226
260
|
}
|
|
227
261
|
function use(usable) {
|
|
228
|
-
if (typeof usable === "object" && usable !== null && "_currentValue" in usable) {
|
|
229
|
-
return usable
|
|
262
|
+
if (typeof usable === "object" && usable !== null && ("_currentValue" in usable || "_defaultValue" in usable)) {
|
|
263
|
+
return getContextValue(usable);
|
|
230
264
|
}
|
|
231
265
|
const promise = usable;
|
|
232
266
|
if (promise.status === "fulfilled")
|
|
@@ -242,6 +276,7 @@ function startTransition(callback) {
|
|
|
242
276
|
// src/slim-react/context.ts
|
|
243
277
|
function createContext(defaultValue) {
|
|
244
278
|
const context = {
|
|
279
|
+
_defaultValue: defaultValue,
|
|
245
280
|
_currentValue: defaultValue,
|
|
246
281
|
Provider: null,
|
|
247
282
|
Consumer: null
|
|
@@ -617,7 +652,7 @@ function renderComponent(type, props, writer, isSvg) {
|
|
|
617
652
|
}
|
|
618
653
|
if (typeOf === REACT_CONSUMER) {
|
|
619
654
|
const ctx2 = type._context;
|
|
620
|
-
const value = ctx2
|
|
655
|
+
const value = ctx2 ? getContextValue(ctx2) : void 0;
|
|
621
656
|
const result2 = typeof props.children === "function" ? props.children(value) : null;
|
|
622
657
|
const savedScope2 = pushComponentScope();
|
|
623
658
|
const finish2 = () => popComponentScope(savedScope2);
|
|
@@ -633,18 +668,20 @@ function renderComponent(type, props, writer, isSvg) {
|
|
|
633
668
|
let ctx;
|
|
634
669
|
if (isProvider) {
|
|
635
670
|
ctx = type._context ?? type;
|
|
636
|
-
prevCtxValue = ctx.
|
|
637
|
-
ctx._currentValue = props.value;
|
|
671
|
+
prevCtxValue = pushContextValue(ctx, props.value);
|
|
638
672
|
}
|
|
639
673
|
const savedScope = pushComponentScope();
|
|
640
674
|
if (isProvider && typeof type !== "function") {
|
|
641
675
|
const finish2 = () => {
|
|
642
676
|
popComponentScope(savedScope);
|
|
643
|
-
ctx
|
|
677
|
+
popContextValue(ctx, prevCtxValue);
|
|
644
678
|
};
|
|
645
679
|
const r2 = renderChildren(props.children, writer, isSvg);
|
|
646
680
|
if (r2 && typeof r2.then === "function") {
|
|
647
|
-
return r2.then(finish2)
|
|
681
|
+
return r2.then(finish2, (e) => {
|
|
682
|
+
finish2();
|
|
683
|
+
throw e;
|
|
684
|
+
});
|
|
648
685
|
}
|
|
649
686
|
finish2();
|
|
650
687
|
return;
|
|
@@ -665,26 +702,35 @@ function renderComponent(type, props, writer, isSvg) {
|
|
|
665
702
|
} catch (e) {
|
|
666
703
|
popComponentScope(savedScope);
|
|
667
704
|
if (isProvider)
|
|
668
|
-
ctx
|
|
705
|
+
popContextValue(ctx, prevCtxValue);
|
|
669
706
|
throw e;
|
|
670
707
|
}
|
|
671
708
|
const finish = () => {
|
|
672
709
|
popComponentScope(savedScope);
|
|
673
710
|
if (isProvider)
|
|
674
|
-
ctx
|
|
711
|
+
popContextValue(ctx, prevCtxValue);
|
|
675
712
|
};
|
|
676
713
|
if (result instanceof Promise) {
|
|
677
714
|
return result.then((resolved) => {
|
|
678
715
|
const r2 = renderNode(resolved, writer, isSvg);
|
|
679
716
|
if (r2 && typeof r2.then === "function") {
|
|
680
|
-
return r2.then(finish)
|
|
717
|
+
return r2.then(finish, (e) => {
|
|
718
|
+
finish();
|
|
719
|
+
throw e;
|
|
720
|
+
});
|
|
681
721
|
}
|
|
682
722
|
finish();
|
|
723
|
+
}, (e) => {
|
|
724
|
+
finish();
|
|
725
|
+
throw e;
|
|
683
726
|
});
|
|
684
727
|
}
|
|
685
728
|
const r = renderNode(result, writer, isSvg);
|
|
686
729
|
if (r && typeof r.then === "function") {
|
|
687
|
-
return r.then(finish)
|
|
730
|
+
return r.then(finish, (e) => {
|
|
731
|
+
finish();
|
|
732
|
+
throw e;
|
|
733
|
+
});
|
|
688
734
|
}
|
|
689
735
|
finish();
|
|
690
736
|
}
|
|
@@ -770,7 +816,7 @@ async function renderSuspense(props, writer, isSvg = false) {
|
|
|
770
816
|
}
|
|
771
817
|
function renderToStream(element) {
|
|
772
818
|
const encoder = new TextEncoder();
|
|
773
|
-
return new ReadableStream({
|
|
819
|
+
return runWithContextStore(() => new ReadableStream({
|
|
774
820
|
async start(controller) {
|
|
775
821
|
resetRenderState();
|
|
776
822
|
const writer = {
|
|
@@ -793,42 +839,44 @@ function renderToStream(element) {
|
|
|
793
839
|
controller.error(error);
|
|
794
840
|
}
|
|
795
841
|
}
|
|
796
|
-
});
|
|
842
|
+
}));
|
|
797
843
|
}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
844
|
+
function renderToString(element) {
|
|
845
|
+
return runWithContextStore(async () => {
|
|
846
|
+
for (let attempt = 0; attempt < MAX_SUSPENSE_RETRIES; attempt++) {
|
|
847
|
+
resetRenderState();
|
|
848
|
+
const chunks = [];
|
|
849
|
+
const writer = {
|
|
850
|
+
lastWasText: false,
|
|
851
|
+
write(c) {
|
|
852
|
+
chunks.push(c);
|
|
853
|
+
this.lastWasText = false;
|
|
854
|
+
},
|
|
855
|
+
text(s2) {
|
|
856
|
+
chunks.push(s2);
|
|
857
|
+
this.lastWasText = true;
|
|
858
|
+
}
|
|
859
|
+
};
|
|
860
|
+
try {
|
|
861
|
+
const r = renderNode(element, writer);
|
|
862
|
+
if (r && typeof r.then === "function")
|
|
863
|
+
await r;
|
|
864
|
+
return chunks.join("");
|
|
865
|
+
} catch (error) {
|
|
866
|
+
if (error && typeof error.then === "function") {
|
|
867
|
+
await error;
|
|
868
|
+
continue;
|
|
869
|
+
}
|
|
870
|
+
throw error;
|
|
822
871
|
}
|
|
823
|
-
throw error;
|
|
824
872
|
}
|
|
825
|
-
|
|
826
|
-
|
|
873
|
+
throw new Error("[slim-react] renderToString exceeded maximum retries");
|
|
874
|
+
});
|
|
827
875
|
}
|
|
828
876
|
|
|
829
877
|
// src/slim-react/index.ts
|
|
830
878
|
function useContext(context) {
|
|
831
|
-
return context
|
|
879
|
+
return getContextValue(context);
|
|
832
880
|
}
|
|
833
881
|
var Suspense = SUSPENSE_TYPE;
|
|
834
882
|
function isValidElement(obj) {
|
|
@@ -49,6 +49,7 @@ declare function startTransition(callback: () => void): void;
|
|
|
49
49
|
* save / restore around Provider renders (handled by the renderer).
|
|
50
50
|
*/
|
|
51
51
|
interface Context<T> {
|
|
52
|
+
_defaultValue: T;
|
|
52
53
|
_currentValue: T;
|
|
53
54
|
Provider: ContextProvider<T>;
|
|
54
55
|
Consumer: (props: {
|
package/dist/slim-react/index.js
CHANGED
|
@@ -7,8 +7,45 @@ import {
|
|
|
7
7
|
createElement,
|
|
8
8
|
jsx
|
|
9
9
|
} from "../chunk-OS3V4CPN.js";
|
|
10
|
+
import {
|
|
11
|
+
__require
|
|
12
|
+
} from "../chunk-EZUCZHGV.js";
|
|
10
13
|
|
|
11
14
|
// src/slim-react/renderContext.ts
|
|
15
|
+
var CONTEXT_STORE_KEY = "__slimReactContextStore";
|
|
16
|
+
var _g = globalThis;
|
|
17
|
+
if (!_g[CONTEXT_STORE_KEY]) {
|
|
18
|
+
try {
|
|
19
|
+
const { AsyncLocalStorage } = __require("node:async_hooks");
|
|
20
|
+
_g[CONTEXT_STORE_KEY] = new AsyncLocalStorage();
|
|
21
|
+
} catch {
|
|
22
|
+
_g[CONTEXT_STORE_KEY] = null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
var _contextStore = _g[CONTEXT_STORE_KEY];
|
|
26
|
+
function runWithContextStore(fn) {
|
|
27
|
+
return _contextStore ? _contextStore.run(/* @__PURE__ */ new Map(), fn) : fn();
|
|
28
|
+
}
|
|
29
|
+
function getContextValue(context) {
|
|
30
|
+
const store = _contextStore?.getStore();
|
|
31
|
+
if (store && store.has(context))
|
|
32
|
+
return store.get(context);
|
|
33
|
+
const c = context;
|
|
34
|
+
return "_defaultValue" in c ? c._defaultValue : c._currentValue;
|
|
35
|
+
}
|
|
36
|
+
function pushContextValue(context, value) {
|
|
37
|
+
const store = _contextStore?.getStore();
|
|
38
|
+
const c = context;
|
|
39
|
+
const prev = store && store.has(context) ? store.get(context) : "_defaultValue" in c ? c._defaultValue : c._currentValue;
|
|
40
|
+
if (store)
|
|
41
|
+
store.set(context, value);
|
|
42
|
+
return prev;
|
|
43
|
+
}
|
|
44
|
+
function popContextValue(context, prev) {
|
|
45
|
+
const store = _contextStore?.getStore();
|
|
46
|
+
if (store)
|
|
47
|
+
store.set(context, prev);
|
|
48
|
+
}
|
|
12
49
|
var GLOBAL_KEY = "__slimReactRenderState";
|
|
13
50
|
var EMPTY = { id: 0, overflow: "", bits: 0 };
|
|
14
51
|
function s() {
|
|
@@ -134,8 +171,8 @@ function useActionState(_action, initialState, _permalink) {
|
|
|
134
171
|
}, false];
|
|
135
172
|
}
|
|
136
173
|
function use(usable) {
|
|
137
|
-
if (typeof usable === "object" && usable !== null && "_currentValue" in usable) {
|
|
138
|
-
return usable
|
|
174
|
+
if (typeof usable === "object" && usable !== null && ("_currentValue" in usable || "_defaultValue" in usable)) {
|
|
175
|
+
return getContextValue(usable);
|
|
139
176
|
}
|
|
140
177
|
const promise = usable;
|
|
141
178
|
if (promise.status === "fulfilled")
|
|
@@ -151,6 +188,7 @@ function startTransition(callback) {
|
|
|
151
188
|
// src/slim-react/context.ts
|
|
152
189
|
function createContext(defaultValue) {
|
|
153
190
|
const context = {
|
|
191
|
+
_defaultValue: defaultValue,
|
|
154
192
|
_currentValue: defaultValue,
|
|
155
193
|
Provider: null,
|
|
156
194
|
Consumer: null
|
|
@@ -526,7 +564,7 @@ function renderComponent(type, props, writer, isSvg) {
|
|
|
526
564
|
}
|
|
527
565
|
if (typeOf === REACT_CONSUMER) {
|
|
528
566
|
const ctx2 = type._context;
|
|
529
|
-
const value = ctx2
|
|
567
|
+
const value = ctx2 ? getContextValue(ctx2) : void 0;
|
|
530
568
|
const result2 = typeof props.children === "function" ? props.children(value) : null;
|
|
531
569
|
const savedScope2 = pushComponentScope();
|
|
532
570
|
const finish2 = () => popComponentScope(savedScope2);
|
|
@@ -542,18 +580,20 @@ function renderComponent(type, props, writer, isSvg) {
|
|
|
542
580
|
let ctx;
|
|
543
581
|
if (isProvider) {
|
|
544
582
|
ctx = type._context ?? type;
|
|
545
|
-
prevCtxValue = ctx.
|
|
546
|
-
ctx._currentValue = props.value;
|
|
583
|
+
prevCtxValue = pushContextValue(ctx, props.value);
|
|
547
584
|
}
|
|
548
585
|
const savedScope = pushComponentScope();
|
|
549
586
|
if (isProvider && typeof type !== "function") {
|
|
550
587
|
const finish2 = () => {
|
|
551
588
|
popComponentScope(savedScope);
|
|
552
|
-
ctx
|
|
589
|
+
popContextValue(ctx, prevCtxValue);
|
|
553
590
|
};
|
|
554
591
|
const r2 = renderChildren(props.children, writer, isSvg);
|
|
555
592
|
if (r2 && typeof r2.then === "function") {
|
|
556
|
-
return r2.then(finish2)
|
|
593
|
+
return r2.then(finish2, (e) => {
|
|
594
|
+
finish2();
|
|
595
|
+
throw e;
|
|
596
|
+
});
|
|
557
597
|
}
|
|
558
598
|
finish2();
|
|
559
599
|
return;
|
|
@@ -574,26 +614,35 @@ function renderComponent(type, props, writer, isSvg) {
|
|
|
574
614
|
} catch (e) {
|
|
575
615
|
popComponentScope(savedScope);
|
|
576
616
|
if (isProvider)
|
|
577
|
-
ctx
|
|
617
|
+
popContextValue(ctx, prevCtxValue);
|
|
578
618
|
throw e;
|
|
579
619
|
}
|
|
580
620
|
const finish = () => {
|
|
581
621
|
popComponentScope(savedScope);
|
|
582
622
|
if (isProvider)
|
|
583
|
-
ctx
|
|
623
|
+
popContextValue(ctx, prevCtxValue);
|
|
584
624
|
};
|
|
585
625
|
if (result instanceof Promise) {
|
|
586
626
|
return result.then((resolved) => {
|
|
587
627
|
const r2 = renderNode(resolved, writer, isSvg);
|
|
588
628
|
if (r2 && typeof r2.then === "function") {
|
|
589
|
-
return r2.then(finish)
|
|
629
|
+
return r2.then(finish, (e) => {
|
|
630
|
+
finish();
|
|
631
|
+
throw e;
|
|
632
|
+
});
|
|
590
633
|
}
|
|
591
634
|
finish();
|
|
635
|
+
}, (e) => {
|
|
636
|
+
finish();
|
|
637
|
+
throw e;
|
|
592
638
|
});
|
|
593
639
|
}
|
|
594
640
|
const r = renderNode(result, writer, isSvg);
|
|
595
641
|
if (r && typeof r.then === "function") {
|
|
596
|
-
return r.then(finish)
|
|
642
|
+
return r.then(finish, (e) => {
|
|
643
|
+
finish();
|
|
644
|
+
throw e;
|
|
645
|
+
});
|
|
597
646
|
}
|
|
598
647
|
finish();
|
|
599
648
|
}
|
|
@@ -679,7 +728,7 @@ async function renderSuspense(props, writer, isSvg = false) {
|
|
|
679
728
|
}
|
|
680
729
|
function renderToStream(element) {
|
|
681
730
|
const encoder = new TextEncoder();
|
|
682
|
-
return new ReadableStream({
|
|
731
|
+
return runWithContextStore(() => new ReadableStream({
|
|
683
732
|
async start(controller) {
|
|
684
733
|
resetRenderState();
|
|
685
734
|
const writer = {
|
|
@@ -702,42 +751,44 @@ function renderToStream(element) {
|
|
|
702
751
|
controller.error(error);
|
|
703
752
|
}
|
|
704
753
|
}
|
|
705
|
-
});
|
|
754
|
+
}));
|
|
706
755
|
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
756
|
+
function renderToString(element) {
|
|
757
|
+
return runWithContextStore(async () => {
|
|
758
|
+
for (let attempt = 0; attempt < MAX_SUSPENSE_RETRIES; attempt++) {
|
|
759
|
+
resetRenderState();
|
|
760
|
+
const chunks = [];
|
|
761
|
+
const writer = {
|
|
762
|
+
lastWasText: false,
|
|
763
|
+
write(c) {
|
|
764
|
+
chunks.push(c);
|
|
765
|
+
this.lastWasText = false;
|
|
766
|
+
},
|
|
767
|
+
text(s2) {
|
|
768
|
+
chunks.push(s2);
|
|
769
|
+
this.lastWasText = true;
|
|
770
|
+
}
|
|
771
|
+
};
|
|
772
|
+
try {
|
|
773
|
+
const r = renderNode(element, writer);
|
|
774
|
+
if (r && typeof r.then === "function")
|
|
775
|
+
await r;
|
|
776
|
+
return chunks.join("");
|
|
777
|
+
} catch (error) {
|
|
778
|
+
if (error && typeof error.then === "function") {
|
|
779
|
+
await error;
|
|
780
|
+
continue;
|
|
781
|
+
}
|
|
782
|
+
throw error;
|
|
731
783
|
}
|
|
732
|
-
throw error;
|
|
733
784
|
}
|
|
734
|
-
|
|
735
|
-
|
|
785
|
+
throw new Error("[slim-react] renderToString exceeded maximum retries");
|
|
786
|
+
});
|
|
736
787
|
}
|
|
737
788
|
|
|
738
789
|
// src/slim-react/index.ts
|
|
739
790
|
function useContext(context) {
|
|
740
|
-
return context
|
|
791
|
+
return getContextValue(context);
|
|
741
792
|
}
|
|
742
793
|
var Suspense = SUSPENSE_TYPE;
|
|
743
794
|
function isValidElement(obj) {
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined")
|
|
5
|
+
return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
8
|
+
|
|
1
9
|
// src/ssr-render-worker.ts
|
|
2
10
|
import { workerData, parentPort } from "node:worker_threads";
|
|
3
11
|
import { pathToFileURL } from "node:url";
|
|
@@ -67,6 +75,40 @@ function createElement(type, props, ...children) {
|
|
|
67
75
|
}
|
|
68
76
|
|
|
69
77
|
// src/slim-react/renderContext.ts
|
|
78
|
+
var CONTEXT_STORE_KEY = "__slimReactContextStore";
|
|
79
|
+
var _g = globalThis;
|
|
80
|
+
if (!_g[CONTEXT_STORE_KEY]) {
|
|
81
|
+
try {
|
|
82
|
+
const { AsyncLocalStorage } = __require("node:async_hooks");
|
|
83
|
+
_g[CONTEXT_STORE_KEY] = new AsyncLocalStorage();
|
|
84
|
+
} catch {
|
|
85
|
+
_g[CONTEXT_STORE_KEY] = null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
var _contextStore = _g[CONTEXT_STORE_KEY];
|
|
89
|
+
function runWithContextStore(fn) {
|
|
90
|
+
return _contextStore ? _contextStore.run(/* @__PURE__ */ new Map(), fn) : fn();
|
|
91
|
+
}
|
|
92
|
+
function getContextValue(context) {
|
|
93
|
+
const store = _contextStore?.getStore();
|
|
94
|
+
if (store && store.has(context))
|
|
95
|
+
return store.get(context);
|
|
96
|
+
const c = context;
|
|
97
|
+
return "_defaultValue" in c ? c._defaultValue : c._currentValue;
|
|
98
|
+
}
|
|
99
|
+
function pushContextValue(context, value) {
|
|
100
|
+
const store = _contextStore?.getStore();
|
|
101
|
+
const c = context;
|
|
102
|
+
const prev = store && store.has(context) ? store.get(context) : "_defaultValue" in c ? c._defaultValue : c._currentValue;
|
|
103
|
+
if (store)
|
|
104
|
+
store.set(context, value);
|
|
105
|
+
return prev;
|
|
106
|
+
}
|
|
107
|
+
function popContextValue(context, prev) {
|
|
108
|
+
const store = _contextStore?.getStore();
|
|
109
|
+
if (store)
|
|
110
|
+
store.set(context, prev);
|
|
111
|
+
}
|
|
70
112
|
var GLOBAL_KEY = "__slimReactRenderState";
|
|
71
113
|
var EMPTY = { id: 0, overflow: "", bits: 0 };
|
|
72
114
|
function s() {
|
|
@@ -479,7 +521,7 @@ function renderComponent(type, props, writer, isSvg) {
|
|
|
479
521
|
}
|
|
480
522
|
if (typeOf === REACT_CONSUMER) {
|
|
481
523
|
const ctx2 = type._context;
|
|
482
|
-
const value = ctx2
|
|
524
|
+
const value = ctx2 ? getContextValue(ctx2) : void 0;
|
|
483
525
|
const result2 = typeof props.children === "function" ? props.children(value) : null;
|
|
484
526
|
const savedScope2 = pushComponentScope();
|
|
485
527
|
const finish2 = () => popComponentScope(savedScope2);
|
|
@@ -495,18 +537,20 @@ function renderComponent(type, props, writer, isSvg) {
|
|
|
495
537
|
let ctx;
|
|
496
538
|
if (isProvider) {
|
|
497
539
|
ctx = type._context ?? type;
|
|
498
|
-
prevCtxValue = ctx.
|
|
499
|
-
ctx._currentValue = props.value;
|
|
540
|
+
prevCtxValue = pushContextValue(ctx, props.value);
|
|
500
541
|
}
|
|
501
542
|
const savedScope = pushComponentScope();
|
|
502
543
|
if (isProvider && typeof type !== "function") {
|
|
503
544
|
const finish2 = () => {
|
|
504
545
|
popComponentScope(savedScope);
|
|
505
|
-
ctx
|
|
546
|
+
popContextValue(ctx, prevCtxValue);
|
|
506
547
|
};
|
|
507
548
|
const r2 = renderChildren(props.children, writer, isSvg);
|
|
508
549
|
if (r2 && typeof r2.then === "function") {
|
|
509
|
-
return r2.then(finish2)
|
|
550
|
+
return r2.then(finish2, (e) => {
|
|
551
|
+
finish2();
|
|
552
|
+
throw e;
|
|
553
|
+
});
|
|
510
554
|
}
|
|
511
555
|
finish2();
|
|
512
556
|
return;
|
|
@@ -527,26 +571,35 @@ function renderComponent(type, props, writer, isSvg) {
|
|
|
527
571
|
} catch (e) {
|
|
528
572
|
popComponentScope(savedScope);
|
|
529
573
|
if (isProvider)
|
|
530
|
-
ctx
|
|
574
|
+
popContextValue(ctx, prevCtxValue);
|
|
531
575
|
throw e;
|
|
532
576
|
}
|
|
533
577
|
const finish = () => {
|
|
534
578
|
popComponentScope(savedScope);
|
|
535
579
|
if (isProvider)
|
|
536
|
-
ctx
|
|
580
|
+
popContextValue(ctx, prevCtxValue);
|
|
537
581
|
};
|
|
538
582
|
if (result instanceof Promise) {
|
|
539
583
|
return result.then((resolved) => {
|
|
540
584
|
const r2 = renderNode(resolved, writer, isSvg);
|
|
541
585
|
if (r2 && typeof r2.then === "function") {
|
|
542
|
-
return r2.then(finish)
|
|
586
|
+
return r2.then(finish, (e) => {
|
|
587
|
+
finish();
|
|
588
|
+
throw e;
|
|
589
|
+
});
|
|
543
590
|
}
|
|
544
591
|
finish();
|
|
592
|
+
}, (e) => {
|
|
593
|
+
finish();
|
|
594
|
+
throw e;
|
|
545
595
|
});
|
|
546
596
|
}
|
|
547
597
|
const r = renderNode(result, writer, isSvg);
|
|
548
598
|
if (r && typeof r.then === "function") {
|
|
549
|
-
return r.then(finish)
|
|
599
|
+
return r.then(finish, (e) => {
|
|
600
|
+
finish();
|
|
601
|
+
throw e;
|
|
602
|
+
});
|
|
550
603
|
}
|
|
551
604
|
finish();
|
|
552
605
|
}
|
|
@@ -630,35 +683,37 @@ async function renderSuspense(props, writer, isSvg = false) {
|
|
|
630
683
|
}
|
|
631
684
|
writer.write("<!--/$-->");
|
|
632
685
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
686
|
+
function renderToString(element) {
|
|
687
|
+
return runWithContextStore(async () => {
|
|
688
|
+
for (let attempt = 0; attempt < MAX_SUSPENSE_RETRIES; attempt++) {
|
|
689
|
+
resetRenderState();
|
|
690
|
+
const chunks = [];
|
|
691
|
+
const writer = {
|
|
692
|
+
lastWasText: false,
|
|
693
|
+
write(c) {
|
|
694
|
+
chunks.push(c);
|
|
695
|
+
this.lastWasText = false;
|
|
696
|
+
},
|
|
697
|
+
text(s2) {
|
|
698
|
+
chunks.push(s2);
|
|
699
|
+
this.lastWasText = true;
|
|
700
|
+
}
|
|
701
|
+
};
|
|
702
|
+
try {
|
|
703
|
+
const r = renderNode(element, writer);
|
|
704
|
+
if (r && typeof r.then === "function")
|
|
705
|
+
await r;
|
|
706
|
+
return chunks.join("");
|
|
707
|
+
} catch (error) {
|
|
708
|
+
if (error && typeof error.then === "function") {
|
|
709
|
+
await error;
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
throw error;
|
|
657
713
|
}
|
|
658
|
-
throw error;
|
|
659
714
|
}
|
|
660
|
-
|
|
661
|
-
|
|
715
|
+
throw new Error("[slim-react] renderToString exceeded maximum retries");
|
|
716
|
+
});
|
|
662
717
|
}
|
|
663
718
|
|
|
664
719
|
// src/ssr-render-worker.ts
|
package/dist/ssr-watch.js
CHANGED
|
@@ -179,6 +179,16 @@ var buildCompilerConfig = (entry2, opts, includeHotPlugin) => {
|
|
|
179
179
|
"@emotion/styled": path.resolve(process.cwd(), "node_modules", "@emotion", "styled")
|
|
180
180
|
} : void 0;
|
|
181
181
|
const externals = isServerBuild ? [
|
|
182
|
+
// Node.js built-ins — must not be bundled; resolved by the runtime.
|
|
183
|
+
// Both the bare name and the node: prefix are listed because rspack
|
|
184
|
+
// may encounter either form depending on how the import is written.
|
|
185
|
+
"node:async_hooks",
|
|
186
|
+
"async_hooks",
|
|
187
|
+
"node:fs",
|
|
188
|
+
"node:path",
|
|
189
|
+
"node:os",
|
|
190
|
+
"node:stream",
|
|
191
|
+
"node:util",
|
|
182
192
|
// react / react-dom are replaced by slim-react via alias above — not external.
|
|
183
193
|
// emotion should be external on server builds to avoid client/browser code
|
|
184
194
|
"@emotion/react",
|
package/package.json
CHANGED
|
@@ -9,7 +9,8 @@ import type { SlimNode } from "./types";
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
export interface Context<T> {
|
|
12
|
-
|
|
12
|
+
_defaultValue: T;
|
|
13
|
+
_currentValue: T; // kept for external compat (real React contexts passed to useContext)
|
|
13
14
|
Provider: ContextProvider<T>;
|
|
14
15
|
Consumer: (props: { children: (value: T) => SlimNode }) => SlimNode;
|
|
15
16
|
}
|
|
@@ -23,6 +24,7 @@ export type ContextProvider<T> = ((props: {
|
|
|
23
24
|
|
|
24
25
|
export function createContext<T>(defaultValue: T): Context<T> {
|
|
25
26
|
const context: Context<T> = {
|
|
27
|
+
_defaultValue: defaultValue,
|
|
26
28
|
_currentValue: defaultValue,
|
|
27
29
|
Provider: null!,
|
|
28
30
|
Consumer: null!,
|
package/src/slim-react/hooks.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* compatible libraries to work during server-side rendering.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { makeId } from "./renderContext";
|
|
9
|
+
import { makeId, getContextValue } from "./renderContext";
|
|
10
10
|
|
|
11
11
|
// ---- useState ----
|
|
12
12
|
export function useState<T>(
|
|
@@ -115,9 +115,9 @@ export function use<T>(
|
|
|
115
115
|
if (
|
|
116
116
|
typeof usable === "object" &&
|
|
117
117
|
usable !== null &&
|
|
118
|
-
"_currentValue" in usable
|
|
118
|
+
("_currentValue" in usable || "_defaultValue" in usable)
|
|
119
119
|
) {
|
|
120
|
-
return (usable as
|
|
120
|
+
return getContextValue<T>(usable as object);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
// Promise – Suspense protocol
|
package/src/slim-react/index.ts
CHANGED
|
@@ -67,12 +67,10 @@ export {
|
|
|
67
67
|
import { createContext } from "./context";
|
|
68
68
|
export { createContext, type Context } from "./context";
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
// Actually useContext reads from the context object, let's export a
|
|
72
|
-
// single implementation that lives close to createContext:
|
|
70
|
+
import { getContextValue } from "./renderContext";
|
|
73
71
|
import type { Context } from "./context";
|
|
74
72
|
export function useContext<T>(context: Context<T>): T {
|
|
75
|
-
return context
|
|
73
|
+
return getContextValue<T>(context);
|
|
76
74
|
}
|
|
77
75
|
|
|
78
76
|
// ---- Rendering ----
|
package/src/slim-react/render.ts
CHANGED
|
@@ -30,6 +30,10 @@ import {
|
|
|
30
30
|
popComponentScope,
|
|
31
31
|
snapshotContext,
|
|
32
32
|
restoreContext,
|
|
33
|
+
pushContextValue,
|
|
34
|
+
popContextValue,
|
|
35
|
+
getContextValue,
|
|
36
|
+
runWithContextStore,
|
|
33
37
|
} from "./renderContext";
|
|
34
38
|
|
|
35
39
|
// ---------------------------------------------------------------------------
|
|
@@ -566,7 +570,7 @@ function renderComponent(
|
|
|
566
570
|
// React.Consumer (React 19) — call the children render prop with the current value
|
|
567
571
|
if (typeOf === REACT_CONSUMER) {
|
|
568
572
|
const ctx = (type as any)._context;
|
|
569
|
-
const value = ctx
|
|
573
|
+
const value = ctx ? getContextValue(ctx) : undefined;
|
|
570
574
|
const result: SlimNode =
|
|
571
575
|
typeof props.children === "function" ? props.children(value) : null;
|
|
572
576
|
const savedScope = pushComponentScope();
|
|
@@ -594,8 +598,7 @@ function renderComponent(
|
|
|
594
598
|
if (isProvider) {
|
|
595
599
|
// Resolve the actual context object from any provider variant
|
|
596
600
|
ctx = (type as any)._context ?? type;
|
|
597
|
-
prevCtxValue = ctx.
|
|
598
|
-
ctx._currentValue = props.value;
|
|
601
|
+
prevCtxValue = pushContextValue(ctx, props.value);
|
|
599
602
|
}
|
|
600
603
|
|
|
601
604
|
// Each component gets a fresh local-ID counter (for multiple useId calls).
|
|
@@ -606,11 +609,11 @@ function renderComponent(
|
|
|
606
609
|
if (isProvider && typeof type !== "function") {
|
|
607
610
|
const finish = () => {
|
|
608
611
|
popComponentScope(savedScope);
|
|
609
|
-
ctx
|
|
612
|
+
popContextValue(ctx, prevCtxValue);
|
|
610
613
|
};
|
|
611
614
|
const r = renderChildren(props.children, writer, isSvg);
|
|
612
615
|
if (r && typeof (r as any).then === "function") {
|
|
613
|
-
return (r as Promise<void>).then(finish);
|
|
616
|
+
return (r as Promise<void>).then(finish, (e) => { finish(); throw e; });
|
|
614
617
|
}
|
|
615
618
|
finish();
|
|
616
619
|
return;
|
|
@@ -631,13 +634,13 @@ function renderComponent(
|
|
|
631
634
|
}
|
|
632
635
|
} catch (e) {
|
|
633
636
|
popComponentScope(savedScope);
|
|
634
|
-
if (isProvider) ctx
|
|
637
|
+
if (isProvider) popContextValue(ctx, prevCtxValue);
|
|
635
638
|
throw e;
|
|
636
639
|
}
|
|
637
640
|
|
|
638
641
|
const finish = () => {
|
|
639
642
|
popComponentScope(savedScope);
|
|
640
|
-
if (isProvider) ctx
|
|
643
|
+
if (isProvider) popContextValue(ctx, prevCtxValue);
|
|
641
644
|
};
|
|
642
645
|
|
|
643
646
|
// Async component
|
|
@@ -645,16 +648,16 @@ function renderComponent(
|
|
|
645
648
|
return result.then((resolved) => {
|
|
646
649
|
const r = renderNode(resolved, writer, isSvg);
|
|
647
650
|
if (r && typeof (r as any).then === "function") {
|
|
648
|
-
return (r as Promise<void>).then(finish);
|
|
651
|
+
return (r as Promise<void>).then(finish, (e) => { finish(); throw e; });
|
|
649
652
|
}
|
|
650
653
|
finish();
|
|
651
|
-
});
|
|
654
|
+
}, (e) => { finish(); throw e; });
|
|
652
655
|
}
|
|
653
656
|
|
|
654
657
|
const r = renderNode(result, writer, isSvg);
|
|
655
658
|
|
|
656
659
|
if (r && typeof (r as any).then === "function") {
|
|
657
|
-
return (r as Promise<void>).then(finish);
|
|
660
|
+
return (r as Promise<void>).then(finish, (e) => { finish(); throw e; });
|
|
658
661
|
}
|
|
659
662
|
finish();
|
|
660
663
|
}
|
|
@@ -803,7 +806,7 @@ async function renderSuspense(
|
|
|
803
806
|
export function renderToStream(element: SlimNode): ReadableStream<Uint8Array> {
|
|
804
807
|
const encoder = new TextEncoder();
|
|
805
808
|
|
|
806
|
-
return new ReadableStream({
|
|
809
|
+
return runWithContextStore(() => new ReadableStream({
|
|
807
810
|
async start(controller) {
|
|
808
811
|
resetRenderState();
|
|
809
812
|
|
|
@@ -827,7 +830,7 @@ export function renderToStream(element: SlimNode): ReadableStream<Uint8Array> {
|
|
|
827
830
|
controller.error(error);
|
|
828
831
|
}
|
|
829
832
|
},
|
|
830
|
-
});
|
|
833
|
+
}));
|
|
831
834
|
}
|
|
832
835
|
|
|
833
836
|
/**
|
|
@@ -835,28 +838,30 @@ export function renderToStream(element: SlimNode): ReadableStream<Uint8Array> {
|
|
|
835
838
|
* Retries the full tree when a component throws a Promise (Suspense protocol),
|
|
836
839
|
* so useServerData and similar hooks work without requiring explicit <Suspense>.
|
|
837
840
|
*/
|
|
838
|
-
export
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
841
|
+
export function renderToString(element: SlimNode): Promise<string> {
|
|
842
|
+
return runWithContextStore(async () => {
|
|
843
|
+
for (let attempt = 0; attempt < MAX_SUSPENSE_RETRIES; attempt++) {
|
|
844
|
+
resetRenderState();
|
|
845
|
+
const chunks: string[] = [];
|
|
846
|
+
const writer: Writer = {
|
|
847
|
+
lastWasText: false,
|
|
848
|
+
write(c) { chunks.push(c); this.lastWasText = false; },
|
|
849
|
+
text(s) { chunks.push(s); this.lastWasText = true; },
|
|
850
|
+
};
|
|
851
|
+
try {
|
|
852
|
+
const r = renderNode(element, writer);
|
|
853
|
+
if (r && typeof (r as any).then === "function") await r;
|
|
854
|
+
return chunks.join("");
|
|
855
|
+
} catch (error) {
|
|
856
|
+
if (error && typeof (error as any).then === "function") {
|
|
857
|
+
await (error as Promise<unknown>);
|
|
858
|
+
continue;
|
|
859
|
+
}
|
|
860
|
+
throw error;
|
|
855
861
|
}
|
|
856
|
-
throw error;
|
|
857
862
|
}
|
|
858
|
-
|
|
859
|
-
|
|
863
|
+
throw new Error("[slim-react] renderToString exceeded maximum retries");
|
|
864
|
+
});
|
|
860
865
|
}
|
|
861
866
|
|
|
862
867
|
/** Alias matching React 18+ server API naming. */
|
|
@@ -1,12 +1,71 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Render-time context for tree-position-based `useId
|
|
2
|
+
* Render-time context for tree-position-based `useId` and React Context values.
|
|
3
3
|
*
|
|
4
4
|
* State lives on `globalThis` rather than module-level variables so that
|
|
5
5
|
* multiple slim-react instances (the render worker's direct import and the
|
|
6
|
-
* SSR bundle's bundled copy) share the same
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* SSR bundle's bundled copy) share the same singletons without coordination.
|
|
7
|
+
*
|
|
8
|
+
* Context values are stored in an AsyncLocalStorage<Map> so each concurrent
|
|
9
|
+
* SSR request gets its own isolated scope that propagates through all awaits.
|
|
10
|
+
* Call `runWithContextStore` at the start of every render to establish the scope.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// Shared AsyncLocalStorage instance — kept on globalThis so both copies of
|
|
14
|
+
// slim-react (direct import + SSR bundle) use the same store.
|
|
15
|
+
// The import is done with require() inside a try/catch so that bundlers that
|
|
16
|
+
// cannot resolve node:async_hooks (e.g. rspack without target:node set) do
|
|
17
|
+
// not fail at build time — the SSR render process always runs in Node.js and
|
|
18
|
+
// will find the module at runtime regardless.
|
|
19
|
+
const CONTEXT_STORE_KEY = "__slimReactContextStore";
|
|
20
|
+
const _g = globalThis as any;
|
|
21
|
+
if (!_g[CONTEXT_STORE_KEY]) {
|
|
22
|
+
try {
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
24
|
+
const { AsyncLocalStorage } = require("node:async_hooks") as typeof import("node:async_hooks");
|
|
25
|
+
_g[CONTEXT_STORE_KEY] = new AsyncLocalStorage<Map<object, unknown>>();
|
|
26
|
+
} catch {
|
|
27
|
+
// Fallback: no-op store — context values fall back to _defaultValue.
|
|
28
|
+
// This should never happen in a real SSR environment.
|
|
29
|
+
_g[CONTEXT_STORE_KEY] = null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const _contextStore: { run<T>(store: Map<object,unknown>, fn: () => T): T; getStore(): Map<object,unknown> | undefined } | null = _g[CONTEXT_STORE_KEY];
|
|
33
|
+
|
|
34
|
+
/** Wrap a render entry-point so it gets its own isolated context-value scope. */
|
|
35
|
+
export function runWithContextStore<T>(fn: () => T): T {
|
|
36
|
+
return _contextStore ? _contextStore.run(new Map(), fn) : fn();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Read the current value for a context within the active render.
|
|
41
|
+
* Falls back to `_defaultValue` (or `_currentValue` for external contexts).
|
|
42
|
+
*/
|
|
43
|
+
export function getContextValue<T>(context: object): T {
|
|
44
|
+
const store = _contextStore?.getStore();
|
|
45
|
+
if (store && store.has(context)) return store.get(context) as T;
|
|
46
|
+
const c = context as any;
|
|
47
|
+
return ("_defaultValue" in c ? c._defaultValue : c._currentValue) as T;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Push a new value for a context Provider onto the per-request store.
|
|
52
|
+
* Returns the previous value so the caller can restore it later.
|
|
9
53
|
*/
|
|
54
|
+
export function pushContextValue(context: object, value: unknown): unknown {
|
|
55
|
+
const store = _contextStore?.getStore();
|
|
56
|
+
const c = context as any;
|
|
57
|
+
const prev = store && store.has(context)
|
|
58
|
+
? store.get(context)
|
|
59
|
+
: ("_defaultValue" in c ? c._defaultValue : c._currentValue);
|
|
60
|
+
if (store) store.set(context, value);
|
|
61
|
+
return prev;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Restore a previously saved context value (called by Provider on exit). */
|
|
65
|
+
export function popContextValue(context: object, prev: unknown): void {
|
|
66
|
+
const store = _contextStore?.getStore();
|
|
67
|
+
if (store) store.set(context, prev);
|
|
68
|
+
}
|
|
10
69
|
|
|
11
70
|
export interface TreeContext {
|
|
12
71
|
id: number;
|
package/src/utils/rspack.ts
CHANGED
|
@@ -226,6 +226,11 @@ const buildCompilerConfig = (
|
|
|
226
226
|
} : undefined;
|
|
227
227
|
|
|
228
228
|
const externals = isServerBuild ? [
|
|
229
|
+
// Node.js built-ins — must not be bundled; resolved by the runtime.
|
|
230
|
+
// Both the bare name and the node: prefix are listed because rspack
|
|
231
|
+
// may encounter either form depending on how the import is written.
|
|
232
|
+
'node:async_hooks', 'async_hooks',
|
|
233
|
+
'node:fs', 'node:path', 'node:os', 'node:stream', 'node:util',
|
|
229
234
|
// react / react-dom are replaced by slim-react via alias above — not external.
|
|
230
235
|
// emotion should be external on server builds to avoid client/browser code
|
|
231
236
|
'@emotion/react',
|