hadars 0.1.28 → 0.1.29

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/cli.js CHANGED
@@ -242,6 +242,9 @@ function pushComponentScope() {
242
242
  function popComponentScope(saved) {
243
243
  s().localIdCounter = saved;
244
244
  }
245
+ function componentCalledUseId() {
246
+ return s().localIdCounter > 0;
247
+ }
245
248
  function snapshotContext() {
246
249
  const st = s();
247
250
  return { tree: { ...st.currentTreeContext }, localId: st.localIdCounter };
@@ -251,6 +254,121 @@ function restoreContext(snap) {
251
254
  st.currentTreeContext = { ...snap.tree };
252
255
  st.localIdCounter = snap.localId;
253
256
  }
257
+ function getTreeId() {
258
+ const { id, overflow } = s().currentTreeContext;
259
+ if (id === 1)
260
+ return overflow;
261
+ const stripped = (id & ~(1 << 31 - Math.clz32(id))).toString(32);
262
+ return stripped + overflow;
263
+ }
264
+ function makeId() {
265
+ const st = s();
266
+ const treeId = getTreeId();
267
+ const n = st.localIdCounter++;
268
+ let id = "\xAB" + st.idPrefix + "R" + treeId;
269
+ if (n > 0)
270
+ id += "H" + n.toString(32);
271
+ return id + "\xBB";
272
+ }
273
+
274
+ // src/slim-react/hooks.ts
275
+ function useState(initialState) {
276
+ const value = typeof initialState === "function" ? initialState() : initialState;
277
+ return [value, () => {
278
+ }];
279
+ }
280
+ function useReducer(_reducer, initialState) {
281
+ return [initialState, () => {
282
+ }];
283
+ }
284
+ function useEffect(_effect, _deps) {
285
+ }
286
+ function useLayoutEffect(_effect, _deps) {
287
+ }
288
+ function useInsertionEffect(_effect, _deps) {
289
+ }
290
+ function useRef(initialValue) {
291
+ return { current: initialValue };
292
+ }
293
+ function useMemo(factory, _deps) {
294
+ return factory();
295
+ }
296
+ function useCallback(callback, _deps) {
297
+ return callback;
298
+ }
299
+ function useDebugValue(_value, _format) {
300
+ }
301
+ function useImperativeHandle(_ref, _createHandle, _deps) {
302
+ }
303
+ function useSyncExternalStore(_subscribe, getSnapshot, getServerSnapshot) {
304
+ return (getServerSnapshot || getSnapshot)();
305
+ }
306
+ function useTransition() {
307
+ return [false, (cb) => cb()];
308
+ }
309
+ function useDeferredValue(value) {
310
+ return value;
311
+ }
312
+ function useOptimistic(passthrough) {
313
+ return [passthrough, () => {
314
+ }];
315
+ }
316
+ function useActionState(_action, initialState, _permalink) {
317
+ return [initialState, () => {
318
+ }, false];
319
+ }
320
+ function use(usable) {
321
+ if (typeof usable === "object" && usable !== null && ("_currentValue" in usable || "_defaultValue" in usable)) {
322
+ return getContextValue(usable);
323
+ }
324
+ const promise = usable;
325
+ if (promise.status === "fulfilled")
326
+ return promise.value;
327
+ if (promise.status === "rejected")
328
+ throw promise.reason;
329
+ throw promise;
330
+ }
331
+
332
+ // src/slim-react/dispatcher.ts
333
+ import ReactPkg from "react";
334
+ var _internals = ReactPkg.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
335
+ var slimDispatcher = {
336
+ useId: makeId,
337
+ readContext: (ctx) => getContextValue(ctx),
338
+ useContext: (ctx) => getContextValue(ctx),
339
+ useState,
340
+ useReducer,
341
+ useEffect,
342
+ useLayoutEffect,
343
+ useInsertionEffect,
344
+ useRef,
345
+ useMemo,
346
+ useCallback,
347
+ useDebugValue,
348
+ useImperativeHandle,
349
+ useSyncExternalStore,
350
+ useTransition,
351
+ useDeferredValue,
352
+ useOptimistic,
353
+ useActionState,
354
+ use,
355
+ // React internals that compiled output may call
356
+ useMemoCache: (size) => new Array(size).fill(void 0),
357
+ useCacheRefresh: () => () => {
358
+ },
359
+ useHostTransitionStatus: () => false
360
+ };
361
+ function installDispatcher() {
362
+ if (!_internals)
363
+ return null;
364
+ const prev = _internals.H;
365
+ _internals.H = slimDispatcher;
366
+ return prev;
367
+ }
368
+ function restoreDispatcher(prev) {
369
+ if (_internals)
370
+ _internals.H = prev;
371
+ }
254
372
 
255
373
  // src/slim-react/render.ts
256
374
  var VOID_ELEMENTS = /* @__PURE__ */ new Set([
@@ -655,6 +773,7 @@ function renderComponent(type, props, writer, isSvg) {
655
773
  return;
656
774
  }
657
775
  let result;
776
+ const prevDispatcher = installDispatcher();
658
777
  try {
659
778
  if (type.prototype && typeof type.prototype.render === "function") {
660
779
  const instance = new type(props);
@@ -668,12 +787,20 @@ function renderComponent(type, props, writer, isSvg) {
668
787
  result = type(props);
669
788
  }
670
789
  } catch (e) {
790
+ restoreDispatcher(prevDispatcher);
671
791
  popComponentScope(savedScope);
672
792
  if (isProvider)
673
793
  popContextValue(ctx, prevCtxValue);
674
794
  throw e;
675
795
  }
796
+ restoreDispatcher(prevDispatcher);
797
+ let savedIdTree;
798
+ if (!(result instanceof Promise) && componentCalledUseId()) {
799
+ savedIdTree = pushTreeContext(1, 0);
800
+ }
676
801
  const finish = () => {
802
+ if (savedIdTree !== void 0)
803
+ popTreeContext(savedIdTree);
677
804
  popComponentScope(savedScope);
678
805
  if (isProvider)
679
806
  popContextValue(ctx, prevCtxValue);
@@ -682,22 +809,33 @@ function renderComponent(type, props, writer, isSvg) {
682
809
  const m = captureMap();
683
810
  return result.then((resolved) => {
684
811
  swapContextMap(m);
812
+ let asyncSavedIdTree;
813
+ if (componentCalledUseId()) {
814
+ asyncSavedIdTree = pushTreeContext(1, 0);
815
+ }
816
+ const asyncFinish = () => {
817
+ if (asyncSavedIdTree !== void 0)
818
+ popTreeContext(asyncSavedIdTree);
819
+ popComponentScope(savedScope);
820
+ if (isProvider)
821
+ popContextValue(ctx, prevCtxValue);
822
+ };
685
823
  const r2 = renderNode(resolved, writer, isSvg);
686
824
  if (r2 && typeof r2.then === "function") {
687
825
  const m2 = captureMap();
688
826
  return r2.then(
689
827
  () => {
690
828
  swapContextMap(m2);
691
- finish();
829
+ asyncFinish();
692
830
  },
693
831
  (e) => {
694
832
  swapContextMap(m2);
695
- finish();
833
+ asyncFinish();
696
834
  throw e;
697
835
  }
698
836
  );
699
837
  }
700
- finish();
838
+ asyncFinish();
701
839
  }, (e) => {
702
840
  swapContextMap(m);
703
841
  finish();
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/slim-react/index.ts
@@ -182,6 +192,9 @@ function pushComponentScope() {
182
192
  function popComponentScope(saved) {
183
193
  s().localIdCounter = saved;
184
194
  }
195
+ function componentCalledUseId() {
196
+ return s().localIdCounter > 0;
197
+ }
185
198
  function snapshotContext() {
186
199
  const st = s();
187
200
  return { tree: { ...st.currentTreeContext }, localId: st.localIdCounter };
@@ -202,10 +215,10 @@ function makeId() {
202
215
  const st = s();
203
216
  const treeId = getTreeId();
204
217
  const n = st.localIdCounter++;
205
- let id = "_" + st.idPrefix + "R_" + treeId;
218
+ let id = "\xAB" + st.idPrefix + "R" + treeId;
206
219
  if (n > 0)
207
220
  id += "H" + n.toString(32);
208
- return id + "_";
221
+ return id + "\xBB";
209
222
  }
210
223
 
211
224
  // src/slim-react/hooks.ts
@@ -298,6 +311,47 @@ function createContext(defaultValue) {
298
311
  return context;
299
312
  }
300
313
 
314
+ // src/slim-react/dispatcher.ts
315
+ var import_react = __toESM(require("react"), 1);
316
+ var _internals = import_react.default.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
317
+ var slimDispatcher = {
318
+ useId: makeId,
319
+ readContext: (ctx) => getContextValue(ctx),
320
+ useContext: (ctx) => getContextValue(ctx),
321
+ useState,
322
+ useReducer,
323
+ useEffect,
324
+ useLayoutEffect,
325
+ useInsertionEffect,
326
+ useRef,
327
+ useMemo,
328
+ useCallback,
329
+ useDebugValue,
330
+ useImperativeHandle,
331
+ useSyncExternalStore,
332
+ useTransition,
333
+ useDeferredValue,
334
+ useOptimistic,
335
+ useActionState,
336
+ use,
337
+ // React internals that compiled output may call
338
+ useMemoCache: (size) => new Array(size).fill(void 0),
339
+ useCacheRefresh: () => () => {
340
+ },
341
+ useHostTransitionStatus: () => false
342
+ };
343
+ function installDispatcher() {
344
+ if (!_internals)
345
+ return null;
346
+ const prev = _internals.H;
347
+ _internals.H = slimDispatcher;
348
+ return prev;
349
+ }
350
+ function restoreDispatcher(prev) {
351
+ if (_internals)
352
+ _internals.H = prev;
353
+ }
354
+
301
355
  // src/slim-react/render.ts
302
356
  var VOID_ELEMENTS = /* @__PURE__ */ new Set([
303
357
  "area",
@@ -701,6 +755,7 @@ function renderComponent(type, props, writer, isSvg) {
701
755
  return;
702
756
  }
703
757
  let result;
758
+ const prevDispatcher = installDispatcher();
704
759
  try {
705
760
  if (type.prototype && typeof type.prototype.render === "function") {
706
761
  const instance = new type(props);
@@ -714,12 +769,20 @@ function renderComponent(type, props, writer, isSvg) {
714
769
  result = type(props);
715
770
  }
716
771
  } catch (e) {
772
+ restoreDispatcher(prevDispatcher);
717
773
  popComponentScope(savedScope);
718
774
  if (isProvider)
719
775
  popContextValue(ctx, prevCtxValue);
720
776
  throw e;
721
777
  }
778
+ restoreDispatcher(prevDispatcher);
779
+ let savedIdTree;
780
+ if (!(result instanceof Promise) && componentCalledUseId()) {
781
+ savedIdTree = pushTreeContext(1, 0);
782
+ }
722
783
  const finish = () => {
784
+ if (savedIdTree !== void 0)
785
+ popTreeContext(savedIdTree);
723
786
  popComponentScope(savedScope);
724
787
  if (isProvider)
725
788
  popContextValue(ctx, prevCtxValue);
@@ -728,22 +791,33 @@ function renderComponent(type, props, writer, isSvg) {
728
791
  const m = captureMap();
729
792
  return result.then((resolved) => {
730
793
  swapContextMap(m);
794
+ let asyncSavedIdTree;
795
+ if (componentCalledUseId()) {
796
+ asyncSavedIdTree = pushTreeContext(1, 0);
797
+ }
798
+ const asyncFinish = () => {
799
+ if (asyncSavedIdTree !== void 0)
800
+ popTreeContext(asyncSavedIdTree);
801
+ popComponentScope(savedScope);
802
+ if (isProvider)
803
+ popContextValue(ctx, prevCtxValue);
804
+ };
731
805
  const r2 = renderNode(resolved, writer, isSvg);
732
806
  if (r2 && typeof r2.then === "function") {
733
807
  const m2 = captureMap();
734
808
  return r2.then(
735
809
  () => {
736
810
  swapContextMap(m2);
737
- finish();
811
+ asyncFinish();
738
812
  },
739
813
  (e) => {
740
814
  swapContextMap(m2);
741
- finish();
815
+ asyncFinish();
742
816
  throw e;
743
817
  }
744
818
  );
745
819
  }
746
- finish();
820
+ asyncFinish();
747
821
  }, (e) => {
748
822
  swapContextMap(m);
749
823
  finish();
@@ -91,6 +91,9 @@ function pushComponentScope() {
91
91
  function popComponentScope(saved) {
92
92
  s().localIdCounter = saved;
93
93
  }
94
+ function componentCalledUseId() {
95
+ return s().localIdCounter > 0;
96
+ }
94
97
  function snapshotContext() {
95
98
  const st = s();
96
99
  return { tree: { ...st.currentTreeContext }, localId: st.localIdCounter };
@@ -111,10 +114,10 @@ function makeId() {
111
114
  const st = s();
112
115
  const treeId = getTreeId();
113
116
  const n = st.localIdCounter++;
114
- let id = "_" + st.idPrefix + "R_" + treeId;
117
+ let id = "\xAB" + st.idPrefix + "R" + treeId;
115
118
  if (n > 0)
116
119
  id += "H" + n.toString(32);
117
- return id + "_";
120
+ return id + "\xBB";
118
121
  }
119
122
 
120
123
  // src/slim-react/hooks.ts
@@ -207,6 +210,47 @@ function createContext(defaultValue) {
207
210
  return context;
208
211
  }
209
212
 
213
+ // src/slim-react/dispatcher.ts
214
+ import ReactPkg from "react";
215
+ var _internals = ReactPkg.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
216
+ var slimDispatcher = {
217
+ useId: makeId,
218
+ readContext: (ctx) => getContextValue(ctx),
219
+ useContext: (ctx) => getContextValue(ctx),
220
+ useState,
221
+ useReducer,
222
+ useEffect,
223
+ useLayoutEffect,
224
+ useInsertionEffect,
225
+ useRef,
226
+ useMemo,
227
+ useCallback,
228
+ useDebugValue,
229
+ useImperativeHandle,
230
+ useSyncExternalStore,
231
+ useTransition,
232
+ useDeferredValue,
233
+ useOptimistic,
234
+ useActionState,
235
+ use,
236
+ // React internals that compiled output may call
237
+ useMemoCache: (size) => new Array(size).fill(void 0),
238
+ useCacheRefresh: () => () => {
239
+ },
240
+ useHostTransitionStatus: () => false
241
+ };
242
+ function installDispatcher() {
243
+ if (!_internals)
244
+ return null;
245
+ const prev = _internals.H;
246
+ _internals.H = slimDispatcher;
247
+ return prev;
248
+ }
249
+ function restoreDispatcher(prev) {
250
+ if (_internals)
251
+ _internals.H = prev;
252
+ }
253
+
210
254
  // src/slim-react/render.ts
211
255
  var VOID_ELEMENTS = /* @__PURE__ */ new Set([
212
256
  "area",
@@ -610,6 +654,7 @@ function renderComponent(type, props, writer, isSvg) {
610
654
  return;
611
655
  }
612
656
  let result;
657
+ const prevDispatcher = installDispatcher();
613
658
  try {
614
659
  if (type.prototype && typeof type.prototype.render === "function") {
615
660
  const instance = new type(props);
@@ -623,12 +668,20 @@ function renderComponent(type, props, writer, isSvg) {
623
668
  result = type(props);
624
669
  }
625
670
  } catch (e) {
671
+ restoreDispatcher(prevDispatcher);
626
672
  popComponentScope(savedScope);
627
673
  if (isProvider)
628
674
  popContextValue(ctx, prevCtxValue);
629
675
  throw e;
630
676
  }
677
+ restoreDispatcher(prevDispatcher);
678
+ let savedIdTree;
679
+ if (!(result instanceof Promise) && componentCalledUseId()) {
680
+ savedIdTree = pushTreeContext(1, 0);
681
+ }
631
682
  const finish = () => {
683
+ if (savedIdTree !== void 0)
684
+ popTreeContext(savedIdTree);
632
685
  popComponentScope(savedScope);
633
686
  if (isProvider)
634
687
  popContextValue(ctx, prevCtxValue);
@@ -637,22 +690,33 @@ function renderComponent(type, props, writer, isSvg) {
637
690
  const m = captureMap();
638
691
  return result.then((resolved) => {
639
692
  swapContextMap(m);
693
+ let asyncSavedIdTree;
694
+ if (componentCalledUseId()) {
695
+ asyncSavedIdTree = pushTreeContext(1, 0);
696
+ }
697
+ const asyncFinish = () => {
698
+ if (asyncSavedIdTree !== void 0)
699
+ popTreeContext(asyncSavedIdTree);
700
+ popComponentScope(savedScope);
701
+ if (isProvider)
702
+ popContextValue(ctx, prevCtxValue);
703
+ };
640
704
  const r2 = renderNode(resolved, writer, isSvg);
641
705
  if (r2 && typeof r2.then === "function") {
642
706
  const m2 = captureMap();
643
707
  return r2.then(
644
708
  () => {
645
709
  swapContextMap(m2);
646
- finish();
710
+ asyncFinish();
647
711
  },
648
712
  (e) => {
649
713
  swapContextMap(m2);
650
- finish();
714
+ asyncFinish();
651
715
  throw e;
652
716
  }
653
717
  );
654
718
  }
655
- finish();
719
+ asyncFinish();
656
720
  }, (e) => {
657
721
  swapContextMap(m);
658
722
  finish();
@@ -149,6 +149,9 @@ function pushComponentScope() {
149
149
  function popComponentScope(saved) {
150
150
  s().localIdCounter = saved;
151
151
  }
152
+ function componentCalledUseId() {
153
+ return s().localIdCounter > 0;
154
+ }
152
155
  function snapshotContext() {
153
156
  const st = s();
154
157
  return { tree: { ...st.currentTreeContext }, localId: st.localIdCounter };
@@ -158,6 +161,121 @@ function restoreContext(snap) {
158
161
  st.currentTreeContext = { ...snap.tree };
159
162
  st.localIdCounter = snap.localId;
160
163
  }
164
+ function getTreeId() {
165
+ const { id, overflow } = s().currentTreeContext;
166
+ if (id === 1)
167
+ return overflow;
168
+ const stripped = (id & ~(1 << 31 - Math.clz32(id))).toString(32);
169
+ return stripped + overflow;
170
+ }
171
+ function makeId() {
172
+ const st = s();
173
+ const treeId = getTreeId();
174
+ const n = st.localIdCounter++;
175
+ let id = "\xAB" + st.idPrefix + "R" + treeId;
176
+ if (n > 0)
177
+ id += "H" + n.toString(32);
178
+ return id + "\xBB";
179
+ }
180
+
181
+ // src/slim-react/hooks.ts
182
+ function useState(initialState) {
183
+ const value = typeof initialState === "function" ? initialState() : initialState;
184
+ return [value, () => {
185
+ }];
186
+ }
187
+ function useReducer(_reducer, initialState) {
188
+ return [initialState, () => {
189
+ }];
190
+ }
191
+ function useEffect(_effect, _deps) {
192
+ }
193
+ function useLayoutEffect(_effect, _deps) {
194
+ }
195
+ function useInsertionEffect(_effect, _deps) {
196
+ }
197
+ function useRef(initialValue) {
198
+ return { current: initialValue };
199
+ }
200
+ function useMemo(factory, _deps) {
201
+ return factory();
202
+ }
203
+ function useCallback(callback, _deps) {
204
+ return callback;
205
+ }
206
+ function useDebugValue(_value, _format) {
207
+ }
208
+ function useImperativeHandle(_ref, _createHandle, _deps) {
209
+ }
210
+ function useSyncExternalStore(_subscribe, getSnapshot, getServerSnapshot) {
211
+ return (getServerSnapshot || getSnapshot)();
212
+ }
213
+ function useTransition() {
214
+ return [false, (cb) => cb()];
215
+ }
216
+ function useDeferredValue(value) {
217
+ return value;
218
+ }
219
+ function useOptimistic(passthrough) {
220
+ return [passthrough, () => {
221
+ }];
222
+ }
223
+ function useActionState(_action, initialState, _permalink) {
224
+ return [initialState, () => {
225
+ }, false];
226
+ }
227
+ function use(usable) {
228
+ if (typeof usable === "object" && usable !== null && ("_currentValue" in usable || "_defaultValue" in usable)) {
229
+ return getContextValue(usable);
230
+ }
231
+ const promise = usable;
232
+ if (promise.status === "fulfilled")
233
+ return promise.value;
234
+ if (promise.status === "rejected")
235
+ throw promise.reason;
236
+ throw promise;
237
+ }
238
+
239
+ // src/slim-react/dispatcher.ts
240
+ import ReactPkg from "react";
241
+ var _internals = ReactPkg.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
242
+ var slimDispatcher = {
243
+ useId: makeId,
244
+ readContext: (ctx) => getContextValue(ctx),
245
+ useContext: (ctx) => getContextValue(ctx),
246
+ useState,
247
+ useReducer,
248
+ useEffect,
249
+ useLayoutEffect,
250
+ useInsertionEffect,
251
+ useRef,
252
+ useMemo,
253
+ useCallback,
254
+ useDebugValue,
255
+ useImperativeHandle,
256
+ useSyncExternalStore,
257
+ useTransition,
258
+ useDeferredValue,
259
+ useOptimistic,
260
+ useActionState,
261
+ use,
262
+ // React internals that compiled output may call
263
+ useMemoCache: (size) => new Array(size).fill(void 0),
264
+ useCacheRefresh: () => () => {
265
+ },
266
+ useHostTransitionStatus: () => false
267
+ };
268
+ function installDispatcher() {
269
+ if (!_internals)
270
+ return null;
271
+ const prev = _internals.H;
272
+ _internals.H = slimDispatcher;
273
+ return prev;
274
+ }
275
+ function restoreDispatcher(prev) {
276
+ if (_internals)
277
+ _internals.H = prev;
278
+ }
161
279
 
162
280
  // src/slim-react/render.ts
163
281
  var VOID_ELEMENTS = /* @__PURE__ */ new Set([
@@ -562,6 +680,7 @@ function renderComponent(type, props, writer, isSvg) {
562
680
  return;
563
681
  }
564
682
  let result;
683
+ const prevDispatcher = installDispatcher();
565
684
  try {
566
685
  if (type.prototype && typeof type.prototype.render === "function") {
567
686
  const instance = new type(props);
@@ -575,12 +694,20 @@ function renderComponent(type, props, writer, isSvg) {
575
694
  result = type(props);
576
695
  }
577
696
  } catch (e) {
697
+ restoreDispatcher(prevDispatcher);
578
698
  popComponentScope(savedScope);
579
699
  if (isProvider)
580
700
  popContextValue(ctx, prevCtxValue);
581
701
  throw e;
582
702
  }
703
+ restoreDispatcher(prevDispatcher);
704
+ let savedIdTree;
705
+ if (!(result instanceof Promise) && componentCalledUseId()) {
706
+ savedIdTree = pushTreeContext(1, 0);
707
+ }
583
708
  const finish = () => {
709
+ if (savedIdTree !== void 0)
710
+ popTreeContext(savedIdTree);
584
711
  popComponentScope(savedScope);
585
712
  if (isProvider)
586
713
  popContextValue(ctx, prevCtxValue);
@@ -589,22 +716,33 @@ function renderComponent(type, props, writer, isSvg) {
589
716
  const m = captureMap();
590
717
  return result.then((resolved) => {
591
718
  swapContextMap(m);
719
+ let asyncSavedIdTree;
720
+ if (componentCalledUseId()) {
721
+ asyncSavedIdTree = pushTreeContext(1, 0);
722
+ }
723
+ const asyncFinish = () => {
724
+ if (asyncSavedIdTree !== void 0)
725
+ popTreeContext(asyncSavedIdTree);
726
+ popComponentScope(savedScope);
727
+ if (isProvider)
728
+ popContextValue(ctx, prevCtxValue);
729
+ };
592
730
  const r2 = renderNode(resolved, writer, isSvg);
593
731
  if (r2 && typeof r2.then === "function") {
594
732
  const m2 = captureMap();
595
733
  return r2.then(
596
734
  () => {
597
735
  swapContextMap(m2);
598
- finish();
736
+ asyncFinish();
599
737
  },
600
738
  (e) => {
601
739
  swapContextMap(m2);
602
- finish();
740
+ asyncFinish();
603
741
  throw e;
604
742
  }
605
743
  );
606
744
  }
607
- finish();
745
+ asyncFinish();
608
746
  }, (e) => {
609
747
  swapContextMap(m);
610
748
  finish();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hadars",
3
- "version": "0.1.28",
3
+ "version": "0.1.29",
4
4
  "description": "Minimal SSR framework for React — rspack, HMR, TypeScript, Bun/Node/Deno",
5
5
  "module": "./dist/index.js",
6
6
  "type": "module",
@@ -0,0 +1,69 @@
1
+ /**
2
+ * React dispatcher shim for slim-react SSR.
3
+ *
4
+ * During a slim-react render, `React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE.H`
5
+ * is null, so any component that calls `React.useId()` (or another hook) via
6
+ * React's own package will hit `resolveDispatcher()` → null → error.
7
+ *
8
+ * We install a minimal dispatcher object for the duration of each component
9
+ * call so that `React.useId()` routes through slim-react's tree-aware
10
+ * `makeId()`. All other hooks already have working SSR stubs in hooks.ts;
11
+ * they are forwarded here so libraries that call them via `React.*` also work.
12
+ */
13
+
14
+ import { makeId, getContextValue } from "./renderContext";
15
+ import {
16
+ useState, useReducer, useEffect, useLayoutEffect, useInsertionEffect,
17
+ useRef, useMemo, useCallback, useDebugValue, useImperativeHandle,
18
+ useSyncExternalStore, useTransition, useDeferredValue,
19
+ useOptimistic, useActionState, use,
20
+ } from "./hooks";
21
+
22
+ import ReactPkg from "react";
23
+
24
+ // React 19 exposes its shared internals under this key.
25
+ const _internals: { H: object | null } | undefined =
26
+ (ReactPkg as any).__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
27
+
28
+ // The dispatcher object we install. We keep a stable reference so the same
29
+ // object is reused across every component call.
30
+ const slimDispatcher: Record<string, unknown> = {
31
+ useId: makeId,
32
+ readContext: (ctx: any) => getContextValue(ctx),
33
+ useContext: (ctx: any) => getContextValue(ctx),
34
+ useState,
35
+ useReducer,
36
+ useEffect,
37
+ useLayoutEffect,
38
+ useInsertionEffect,
39
+ useRef,
40
+ useMemo,
41
+ useCallback,
42
+ useDebugValue,
43
+ useImperativeHandle,
44
+ useSyncExternalStore,
45
+ useTransition,
46
+ useDeferredValue,
47
+ useOptimistic,
48
+ useActionState,
49
+ use,
50
+ // React internals that compiled output may call
51
+ useMemoCache: (size: number) => new Array(size).fill(undefined),
52
+ useCacheRefresh: () => () => {},
53
+ useHostTransitionStatus: () => false,
54
+ };
55
+
56
+ /**
57
+ * Install the slim dispatcher and return the previous value.
58
+ * Call `restoreDispatcher(prev)` when the component finishes.
59
+ */
60
+ export function installDispatcher(): object | null {
61
+ if (!_internals) return null;
62
+ const prev = _internals.H;
63
+ _internals.H = slimDispatcher;
64
+ return prev;
65
+ }
66
+
67
+ export function restoreDispatcher(prev: object | null): void {
68
+ if (_internals) _internals.H = prev;
69
+ }
@@ -28,6 +28,7 @@ import {
28
28
  popTreeContext,
29
29
  pushComponentScope,
30
30
  popComponentScope,
31
+ componentCalledUseId,
31
32
  snapshotContext,
32
33
  restoreContext,
33
34
  pushContextValue,
@@ -35,7 +36,9 @@ import {
35
36
  getContextValue,
36
37
  swapContextMap,
37
38
  captureMap,
39
+ type TreeContext,
38
40
  } from "./renderContext";
41
+ import { installDispatcher, restoreDispatcher } from "./dispatcher";
39
42
 
40
43
  // ---------------------------------------------------------------------------
41
44
  // HTML helpers
@@ -630,6 +633,7 @@ function renderComponent(
630
633
  }
631
634
 
632
635
  let result: SlimNode;
636
+ const prevDispatcher = installDispatcher();
633
637
  try {
634
638
  if (type.prototype && typeof type.prototype.render === "function") {
635
639
  const instance = new (type as any)(props);
@@ -643,12 +647,25 @@ function renderComponent(
643
647
  result = type(props);
644
648
  }
645
649
  } catch (e) {
650
+ restoreDispatcher(prevDispatcher);
646
651
  popComponentScope(savedScope);
647
652
  if (isProvider) popContextValue(ctx, prevCtxValue);
648
653
  throw e;
649
654
  }
655
+ restoreDispatcher(prevDispatcher);
656
+
657
+ // React 19 finishFunctionComponent: if the component called useId, push a
658
+ // tree-context slot for the component's OUTPUT children — matching React 19's
659
+ // `pushTreeContext(keyPath, 1, 0)` call inside finishFunctionComponent.
660
+ // This ensures that useId IDs produced by child components of a useId-calling
661
+ // component are tree-positioned identically to React's own renderer.
662
+ let savedIdTree: TreeContext | undefined;
663
+ if (!(result instanceof Promise) && componentCalledUseId()) {
664
+ savedIdTree = pushTreeContext(1, 0);
665
+ }
650
666
 
651
667
  const finish = () => {
668
+ if (savedIdTree !== undefined) popTreeContext(savedIdTree);
652
669
  popComponentScope(savedScope);
653
670
  if (isProvider) popContextValue(ctx, prevCtxValue);
654
671
  };
@@ -658,15 +675,25 @@ function renderComponent(
658
675
  const m = captureMap();
659
676
  return result.then((resolved) => {
660
677
  swapContextMap(m);
678
+ // Check useId after the async body has finished executing.
679
+ let asyncSavedIdTree: TreeContext | undefined;
680
+ if (componentCalledUseId()) {
681
+ asyncSavedIdTree = pushTreeContext(1, 0);
682
+ }
683
+ const asyncFinish = () => {
684
+ if (asyncSavedIdTree !== undefined) popTreeContext(asyncSavedIdTree);
685
+ popComponentScope(savedScope);
686
+ if (isProvider) popContextValue(ctx, prevCtxValue);
687
+ };
661
688
  const r = renderNode(resolved, writer, isSvg);
662
689
  if (r && typeof (r as any).then === "function") {
663
690
  const m2 = captureMap();
664
691
  return (r as Promise<void>).then(
665
- () => { swapContextMap(m2); finish(); },
666
- (e) => { swapContextMap(m2); finish(); throw e; },
692
+ () => { swapContextMap(m2); asyncFinish(); },
693
+ (e) => { swapContextMap(m2); asyncFinish(); throw e; },
667
694
  );
668
695
  }
669
- finish();
696
+ asyncFinish();
670
697
  }, (e) => { swapContextMap(m); finish(); throw e; });
671
698
  }
672
699
 
@@ -160,6 +160,11 @@ export function popComponentScope(saved: number) {
160
160
  s().localIdCounter = saved;
161
161
  }
162
162
 
163
+ /** True if the current component has called useId at least once. */
164
+ export function componentCalledUseId(): boolean {
165
+ return s().localIdCounter > 0;
166
+ }
167
+
163
168
  export function snapshotContext(): { tree: TreeContext; localId: number } {
164
169
  const st = s();
165
170
  return { tree: { ...st.currentTreeContext }, localId: st.localIdCounter };
@@ -187,19 +192,20 @@ function getTreeId(): string {
187
192
  /**
188
193
  * Generate a `useId`-compatible ID for the current call site.
189
194
  *
190
- * Format: `_<idPrefix>R_<treeId>_`
191
- * with an optional `H<n>` suffix when the same component calls useId
192
- * more than once (matching React 19's `localIdCounter` behaviour).
195
+ * Format: `«<idPrefix>R<treeId>»` (React 19.1+)
196
+ * with an optional `H<n>` suffix for the n-th useId call in the same
197
+ * component (matching React 19's `localIdCounter` behaviour).
193
198
  *
194
- * This matches React 19's `mountId` output on both the Fizz SSR renderer
195
- * and the client hydration path, so the IDs produced here will agree with
196
- * the real React runtime during `hydrateRoot`.
199
+ * React 19.1 switched from `_R_<id>_` to `«R<id>»` (U+00AB / U+00BB).
200
+ * This matches React 19.1's `mountId` output on the Fizz SSR renderer and
201
+ * the client hydration path, so the IDs produced here will agree with the
202
+ * real React runtime during `hydrateRoot`.
197
203
  */
198
204
  export function makeId(): string {
199
205
  const st = s();
200
206
  const treeId = getTreeId();
201
207
  const n = st.localIdCounter++;
202
- let id = "_" + st.idPrefix + "R_" + treeId;
208
+ let id = "\u00ab" + st.idPrefix + "R" + treeId;
203
209
  if (n > 0) id += "H" + n.toString(32);
204
- return id + "_";
210
+ return id + "\u00bb";
205
211
  }