test-renderer 0.14.0 → 0.15.0

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/index.js CHANGED
@@ -41,64 +41,95 @@ var Tag = {
41
41
  };
42
42
  var CONTAINER_TYPE = "";
43
43
 
44
+ // src/performance.ts
45
+ function mark(name, details) {
46
+ if (!globalThis.TEST_RENDERER_ENABLE_PROFILING) {
47
+ return;
48
+ }
49
+ performance.mark(`test-renderer/${name}`, { detail: details });
50
+ }
51
+ function measureStart(name) {
52
+ if (!globalThis.TEST_RENDERER_ENABLE_PROFILING) {
53
+ return;
54
+ }
55
+ performance.mark(`test-renderer/${name}:start`);
56
+ }
57
+ function measureEnd(name, details) {
58
+ if (!globalThis.TEST_RENDERER_ENABLE_PROFILING) {
59
+ return;
60
+ }
61
+ performance.mark(`test-renderer/${name}:end`);
62
+ performance.measure(`test-renderer/${name}`, {
63
+ start: `test-renderer/${name}:start`,
64
+ end: `test-renderer/${name}:end`,
65
+ detail: details
66
+ });
67
+ }
68
+
69
+ // src/reconciler.ts
70
+ import ReactReconciler from "react-reconciler";
71
+ import { DefaultEventPriority, NoEventPriority } from "react-reconciler/constants";
72
+
44
73
  // src/query-all.ts
45
- function queryAll(element, predicate, options) {
74
+ function queryAll(instance, predicate, options) {
46
75
  var _a, _b;
47
76
  const includeSelf = (_a = options == null ? void 0 : options.includeSelf) != null ? _a : false;
48
77
  const matchDeepestOnly = (_b = options == null ? void 0 : options.matchDeepestOnly) != null ? _b : false;
49
78
  const results = [];
50
79
  const matchingDescendants = [];
51
- element.children.forEach((child) => {
80
+ instance.children.forEach((child) => {
52
81
  if (typeof child === "string") {
53
82
  return;
54
83
  }
55
84
  matchingDescendants.push(...queryAll(child, predicate, __spreadProps(__spreadValues({}, options), { includeSelf: true })));
56
85
  });
57
86
  if (includeSelf && // When matchDeepestOnly = true: add current element only if no descendants match
58
- (matchingDescendants.length === 0 || !matchDeepestOnly) && predicate(element)) {
59
- results.push(element);
87
+ (matchingDescendants.length === 0 || !matchDeepestOnly) && predicate(instance)) {
88
+ results.push(instance);
60
89
  }
61
90
  results.push(...matchingDescendants);
62
91
  return results;
63
92
  }
64
93
 
65
- // src/render-to-json.ts
66
- function renderContainerToJson(instance) {
94
+ // src/to-json.ts
95
+ function containerToJson(container) {
67
96
  return {
68
97
  type: CONTAINER_TYPE,
69
98
  props: {},
70
- children: renderChildrenToJson(instance.children),
99
+ children: childrenToJson(container.children),
71
100
  $$typeof: /* @__PURE__ */ Symbol.for("react.test.json")
72
101
  };
73
102
  }
74
- function renderInstanceToJson(instance) {
75
- if (instance.isHidden) {
103
+ function instanceToJson(instance) {
104
+ const shouldExcludeHidden = instance.rootContainer.config.transformHiddenInstanceProps == null;
105
+ if (instance.isHidden && shouldExcludeHidden) {
76
106
  return null;
77
107
  }
78
108
  const _a = instance.props, { children: _children } = _a, restProps = __objRest(_a, ["children"]);
79
109
  return {
80
110
  type: instance.type,
81
111
  props: restProps,
82
- children: renderChildrenToJson(instance.children),
112
+ children: childrenToJson(instance.children),
83
113
  $$typeof: /* @__PURE__ */ Symbol.for("react.test.json")
84
114
  };
85
115
  }
86
- function renderTextInstanceToJson(instance) {
87
- if (instance.isHidden) {
116
+ function textInstanceToJson(instance) {
117
+ const shouldExcludeHidden = instance.rootContainer.config.transformHiddenInstanceProps == null;
118
+ if (instance.isHidden && shouldExcludeHidden) {
88
119
  return null;
89
120
  }
90
121
  return instance.text;
91
122
  }
92
- function renderChildrenToJson(children) {
123
+ function childrenToJson(children) {
93
124
  const result = [];
94
125
  for (const child of children) {
95
126
  if (child.tag === Tag.Instance) {
96
- const renderedChild = renderInstanceToJson(child);
127
+ const renderedChild = instanceToJson(child);
97
128
  if (renderedChild != null) {
98
129
  result.push(renderedChild);
99
130
  }
100
131
  } else {
101
- const renderedChild = renderTextInstanceToJson(child);
132
+ const renderedChild = textInstanceToJson(child);
102
133
  if (renderedChild != null) {
103
134
  result.push(renderedChild);
104
135
  }
@@ -107,9 +138,9 @@ function renderChildrenToJson(children) {
107
138
  return result;
108
139
  }
109
140
 
110
- // src/host-element.ts
141
+ // src/test-instance.ts
111
142
  var instanceMap = /* @__PURE__ */ new WeakMap();
112
- var HostElement = class _HostElement {
143
+ var TestInstance = class _TestInstance {
113
144
  constructor(instance) {
114
145
  this.instance = instance;
115
146
  }
@@ -118,6 +149,7 @@ var HostElement = class _HostElement {
118
149
  return this.instance.tag === Tag.Instance ? this.instance.type : CONTAINER_TYPE;
119
150
  }
120
151
  /** The element's props object. */
152
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
121
153
  get props() {
122
154
  return this.instance.tag === Tag.Instance ? this.instance.props : {};
123
155
  }
@@ -127,11 +159,13 @@ var HostElement = class _HostElement {
127
159
  if (parentInstance == null) {
128
160
  return null;
129
161
  }
130
- return _HostElement.fromInstance(parentInstance);
162
+ return _TestInstance.fromInstance(parentInstance);
131
163
  }
132
- /** Array of child nodes (elements and text strings). Hidden children are excluded. */
164
+ /** Array of child nodes (elements and text strings). Hidden children are excluded by default. */
133
165
  get children() {
134
- const result = this.instance.children.filter((child) => !child.isHidden).map((child) => getHostNodeForInstance(child));
166
+ const container = this.instance.tag === Tag.Container ? this.instance : this.instance.rootContainer;
167
+ const shouldExcludeHiddenChildren = container.config.transformHiddenInstanceProps == null;
168
+ const result = this.instance.children.filter((child) => !child.isHidden || !shouldExcludeHiddenChildren).map((child) => getTestNodeForInstance(child));
135
169
  return result;
136
170
  }
137
171
  /**
@@ -147,10 +181,10 @@ var HostElement = class _HostElement {
147
181
  /**
148
182
  * Convert this element to a JSON representation suitable for snapshots.
149
183
  *
150
- * @returns JSON element or null if the element is hidden.
184
+ * @returns JSON element or null if the element is hidden and hidden nodes are excluded.
151
185
  */
152
186
  toJSON() {
153
- return this.instance.tag === Tag.Container ? renderContainerToJson(this.instance) : renderInstanceToJson(this.instance);
187
+ return this.instance.tag === Tag.Container ? containerToJson(this.instance) : instanceToJson(this.instance);
154
188
  }
155
189
  /**
156
190
  * Find all descendant elements matching the predicate.
@@ -164,28 +198,24 @@ var HostElement = class _HostElement {
164
198
  }
165
199
  /** @internal */
166
200
  static fromInstance(instance) {
167
- const hostElement = instanceMap.get(instance);
168
- if (hostElement) {
169
- return hostElement;
201
+ const testInstance = instanceMap.get(instance);
202
+ if (testInstance) {
203
+ return testInstance;
170
204
  }
171
- const result = new _HostElement(instance);
205
+ const result = new _TestInstance(instance);
172
206
  instanceMap.set(instance, result);
173
207
  return result;
174
208
  }
175
209
  };
176
- function getHostNodeForInstance(instance) {
210
+ function getTestNodeForInstance(instance) {
177
211
  switch (instance.tag) {
178
212
  case Tag.Text:
179
213
  return instance.text;
180
214
  case Tag.Instance:
181
- return HostElement.fromInstance(instance);
215
+ return TestInstance.fromInstance(instance);
182
216
  }
183
217
  }
184
218
 
185
- // src/reconciler.ts
186
- import ReactReconciler from "react-reconciler";
187
- import { DefaultEventPriority, NoEventPriority } from "react-reconciler/constants";
188
-
189
219
  // src/utils.ts
190
220
  function formatComponentList(names) {
191
221
  if (names.length === 0) {
@@ -267,10 +297,12 @@ var hostConfig = {
267
297
  * something when an instance is definitely in the tree, look at `commitMount` instead.
268
298
  */
269
299
  createInstance(type, props, rootContainer, _hostContext, internalHandle) {
300
+ mark("reconciler/createInstance", { type });
270
301
  return {
271
302
  tag: Tag.Instance,
272
303
  type,
273
304
  props,
305
+ propsBeforeHiding: null,
274
306
  isHidden: false,
275
307
  children: [],
276
308
  parent: null,
@@ -286,6 +318,7 @@ var hostConfig = {
286
318
  */
287
319
  createTextInstance(text, rootContainer, hostContext, _internalHandle) {
288
320
  var _a;
321
+ mark("reconciler/createTextInstance", { text });
289
322
  if (rootContainer.config.textComponentTypes && !hostContext.isInsideText) {
290
323
  const componentTypes = (_a = rootContainer.config.publicTextComponentTypes) != null ? _a : rootContainer.config.textComponentTypes;
291
324
  throw new Error(
@@ -298,6 +331,7 @@ var hostConfig = {
298
331
  tag: Tag.Text,
299
332
  text,
300
333
  parent: null,
334
+ rootContainer,
301
335
  isHidden: false
302
336
  };
303
337
  },
@@ -311,7 +345,15 @@ var hostConfig = {
311
345
  * must not modify any other nodes. It's called while the tree is still being built up and not connected
312
346
  * to the actual tree on the screen.
313
347
  */
314
- appendInitialChild: appendChild,
348
+ appendInitialChild(parentInstance, child) {
349
+ if (globalThis.TEST_RENDERER_ENABLE_PROFILING) {
350
+ mark("reconciler/appendInitialChild", {
351
+ parentType: parentInstance.type,
352
+ childType: formatInstanceType(child)
353
+ });
354
+ }
355
+ appendChild(parentInstance, child);
356
+ },
315
357
  /**
316
358
  * #### `finalizeInitialChildren(instance, type, props, rootContainer, hostContext)`
317
359
  *
@@ -328,7 +370,8 @@ var hostConfig = {
328
370
  *
329
371
  * If you don't want to do anything here, you should return `false`.
330
372
  */
331
- finalizeInitialChildren(_instance, _type, _props, _rootContainer, _hostContext) {
373
+ finalizeInitialChildren(instance, _type, _props, _rootContainer, _hostContext) {
374
+ mark("reconciler/finalizeInitialChildren", { type: instance.type });
332
375
  return false;
333
376
  },
334
377
  /**
@@ -346,19 +389,24 @@ var hostConfig = {
346
389
  * If you don't want to do anything here, you should return `false`.
347
390
  * This method happens **in the render phase**. Do not mutate the tree from it.
348
391
  */
349
- shouldSetTextContent(_type, _props) {
392
+ shouldSetTextContent(type, _props) {
393
+ mark("reconciler/shouldSetTextContent", { type, result: false });
350
394
  return false;
351
395
  },
352
- setCurrentUpdatePriority(newPriority) {
353
- currentUpdatePriority = newPriority;
396
+ setCurrentUpdatePriority(priority) {
397
+ mark("reconciler/setCurrentUpdatePriority", { priority });
398
+ currentUpdatePriority = priority;
354
399
  },
355
400
  getCurrentUpdatePriority() {
356
401
  return currentUpdatePriority;
357
402
  },
358
403
  resolveUpdatePriority() {
359
- return currentUpdatePriority || DefaultEventPriority;
404
+ const priority = currentUpdatePriority || DefaultEventPriority;
405
+ mark("reconciler/resolveUpdatePriority", { priority });
406
+ return priority;
360
407
  },
361
408
  shouldAttemptEagerTransition() {
409
+ mark("reconciler/shouldAttemptEagerTransition", { result: false });
362
410
  return false;
363
411
  },
364
412
  /**
@@ -371,6 +419,7 @@ var hostConfig = {
371
419
  * This method happens **in the render phase**. Do not mutate the tree from it.
372
420
  */
373
421
  getRootHostContext(rootContainer) {
422
+ mark("reconciler/getRootHostContext");
374
423
  return {
375
424
  type: "ROOT",
376
425
  config: rootContainer.config,
@@ -395,6 +444,7 @@ var hostConfig = {
395
444
  */
396
445
  getChildHostContext(parentHostContext, type) {
397
446
  var _a;
447
+ mark("reconciler/getChildHostContext", { type });
398
448
  const isInsideText = Boolean((_a = parentHostContext.config.textComponentTypes) == null ? void 0 : _a.includes(type));
399
449
  return __spreadProps(__spreadValues({}, parentHostContext), { type, isInsideText });
400
450
  },
@@ -407,19 +457,19 @@ var hostConfig = {
407
457
  * If you don't want to do anything here, return `instance`.
408
458
  */
409
459
  getPublicInstance(instance) {
460
+ if (globalThis.TEST_RENDERER_ENABLE_PROFILING) {
461
+ mark("reconciler/getPublicInstance", {
462
+ type: formatInstanceType(instance)
463
+ });
464
+ }
410
465
  switch (instance.tag) {
411
466
  case Tag.Instance: {
412
- const createNodeMock = instance.rootContainer.config.createNodeMock;
413
- const mockNode = createNodeMock({
414
- type: instance.type,
415
- props: instance.props,
416
- key: null
417
- });
418
- nodeToInstanceMap.set(mockNode, instance);
419
- return mockNode;
467
+ const testInstance = TestInstance.fromInstance(instance);
468
+ nodeToInstanceMap.set(testInstance, instance);
469
+ return testInstance;
420
470
  }
421
471
  default:
422
- return instance;
472
+ return null;
423
473
  }
424
474
  },
425
475
  /**
@@ -432,6 +482,8 @@ var hostConfig = {
432
482
  * Even if you don't want to do anything here, you need to return `null` from it.
433
483
  */
434
484
  prepareForCommit(_containerInfo) {
485
+ mark("reconciler/prepareForCommit");
486
+ measureStart("react/commit");
435
487
  return null;
436
488
  },
437
489
  /**
@@ -443,6 +495,8 @@ var hostConfig = {
443
495
  * You can leave it empty.
444
496
  */
445
497
  resetAfterCommit(_containerInfo) {
498
+ measureEnd("react/commit");
499
+ mark("reconciler/resetAfterCommit");
446
500
  },
447
501
  /**
448
502
  * #### `preparePortalMount(containerInfo)`
@@ -450,19 +504,31 @@ var hostConfig = {
450
504
  * This method is called for a container that's used as a portal target. Usually you can leave it empty.
451
505
  */
452
506
  preparePortalMount(_containerInfo) {
507
+ mark("reconciler/preparePortalMount");
453
508
  },
454
509
  /**
455
510
  * #### `scheduleTimeout(fn, delay)`
456
511
  *
457
512
  * You can proxy this to `setTimeout` or its equivalent in your environment.
458
513
  */
459
- scheduleTimeout: setTimeout,
514
+ scheduleTimeout(fn, delay) {
515
+ const id = setTimeout(() => {
516
+ mark("reconciler/scheduled timeout:start");
517
+ fn();
518
+ mark("reconciler/scheduled timeout:end");
519
+ }, delay);
520
+ mark("reconciler/scheduleTimeout", { id });
521
+ return id;
522
+ },
460
523
  /**
461
524
  * #### `cancelTimeout(id)`
462
525
  *
463
526
  * You can proxy this to `clearTimeout` or its equivalent in your environment.
464
527
  */
465
- cancelTimeout: clearTimeout,
528
+ cancelTimeout(id) {
529
+ mark("reconciler/cancelTimeout", { id });
530
+ clearTimeout(id);
531
+ },
466
532
  /**
467
533
  * #### `noTimeout`
468
534
  *
@@ -484,7 +550,14 @@ var hostConfig = {
484
550
  *
485
551
  * Optional. You can proxy this to `queueMicrotask` or its equivalent in your environment.
486
552
  */
487
- scheduleMicrotask: queueMicrotask,
553
+ scheduleMicrotask(fn) {
554
+ mark("reconciler/scheduleMicrotask");
555
+ queueMicrotask(() => {
556
+ mark("reconciler/scheduled microtask:start");
557
+ fn();
558
+ mark("reconciler/scheduled microtask:end");
559
+ });
560
+ },
488
561
  /**
489
562
  * #### `isPrimaryRenderer`
490
563
  *
@@ -498,6 +571,7 @@ var hostConfig = {
498
571
  */
499
572
  warnsIfNotActing: true,
500
573
  getInstanceFromNode(node) {
574
+ mark("reconciler/getInstanceFromNode");
501
575
  const instance = nodeToInstanceMap.get(node);
502
576
  if (instance !== void 0) {
503
577
  return instance.unstable_fiber;
@@ -505,17 +579,22 @@ var hostConfig = {
505
579
  return null;
506
580
  },
507
581
  beforeActiveInstanceBlur() {
582
+ mark("reconciler/beforeActiveInstanceBlur");
508
583
  },
509
584
  afterActiveInstanceBlur() {
585
+ mark("reconciler/afterActiveInstanceBlur");
510
586
  },
511
587
  prepareScopeUpdate(scopeInstance, instance) {
588
+ mark("reconciler/prepareScopeUpdate");
512
589
  nodeToInstanceMap.set(scopeInstance, instance);
513
590
  },
514
591
  getInstanceFromScope(scopeInstance) {
515
592
  var _a;
593
+ mark("reconciler/getInstanceFromScope");
516
594
  return (_a = nodeToInstanceMap.get(scopeInstance)) != null ? _a : null;
517
595
  },
518
596
  detachDeletedInstance(_node) {
597
+ mark("reconciler/detachDeletedInstance");
519
598
  },
520
599
  /**
521
600
  * #### `appendChild(parentInstance, child)`
@@ -526,7 +605,15 @@ var hostConfig = {
526
605
  * Although this method currently runs in the commit phase, you still should not mutate any other nodes
527
606
  * in it. If you need to do some additional work when a node is definitely connected to the visible tree, look at `commitMount`.
528
607
  */
529
- appendChild,
608
+ appendChild(parentInstance, child) {
609
+ if (globalThis.TEST_RENDERER_ENABLE_PROFILING) {
610
+ mark("reconciler/appendChild", {
611
+ parentType: parentInstance.type,
612
+ childType: formatInstanceType(child)
613
+ });
614
+ }
615
+ appendChild(parentInstance, child);
616
+ },
530
617
  /**
531
618
  * #### `appendChildToContainer(container, child)`
532
619
  *
@@ -534,7 +621,14 @@ var hostConfig = {
534
621
  * to the root has a slightly different implementation, or if the root container nodes are of a different
535
622
  * type than the rest of the tree.
536
623
  */
537
- appendChildToContainer: appendChild,
624
+ appendChildToContainer(container, child) {
625
+ if (globalThis.TEST_RENDERER_ENABLE_PROFILING) {
626
+ mark("reconciler/appendChildToContainer", {
627
+ childType: formatInstanceType(child)
628
+ });
629
+ }
630
+ appendChild(container, child);
631
+ },
538
632
  /**
539
633
  * #### `insertBefore(parentInstance, child, beforeChild)`
540
634
  *
@@ -544,7 +638,16 @@ var hostConfig = {
544
638
  * Note that React uses this method both for insertions and for reordering nodes. Similar to DOM, it is expected
545
639
  * that you can call `insertBefore` to reposition an existing child. Do not mutate any other parts of the tree from it.
546
640
  */
547
- insertBefore,
641
+ insertBefore(parentInstance, child, beforeChild) {
642
+ if (globalThis.TEST_RENDERER_ENABLE_PROFILING) {
643
+ mark("reconciler/insertBefore", {
644
+ parentType: parentInstance.type,
645
+ childType: formatInstanceType(child),
646
+ beforeChildType: formatInstanceType(beforeChild)
647
+ });
648
+ }
649
+ insertBefore(parentInstance, child, beforeChild);
650
+ },
548
651
  /**
549
652
  * #### `insertInContainerBefore(container, child, beforeChild)
550
653
  *
@@ -552,7 +655,15 @@ var hostConfig = {
552
655
  * to the root has a slightly different implementation, or if the root container nodes are of a different type
553
656
  * than the rest of the tree.
554
657
  */
555
- insertInContainerBefore: insertBefore,
658
+ insertInContainerBefore(container, child, beforeChild) {
659
+ if (globalThis.TEST_RENDERER_ENABLE_PROFILING) {
660
+ mark("reconciler/insertInContainerBefore", {
661
+ childType: formatInstanceType(child),
662
+ beforeChildType: formatInstanceType(beforeChild)
663
+ });
664
+ }
665
+ insertBefore(container, child, beforeChild);
666
+ },
556
667
  /**
557
668
  * #### `removeChild(parentInstance, child)`
558
669
  *
@@ -561,7 +672,15 @@ var hostConfig = {
561
672
  * React will only call it for the top-level node that is being removed. It is expected that garbage collection
562
673
  * would take care of the whole subtree. You are not expected to traverse the child tree in it.
563
674
  */
564
- removeChild,
675
+ removeChild(parentInstance, child) {
676
+ if (globalThis.TEST_RENDERER_ENABLE_PROFILING) {
677
+ mark("reconciler/removeChild", {
678
+ parentType: parentInstance.type,
679
+ childType: formatInstanceType(child)
680
+ });
681
+ }
682
+ removeChild(parentInstance, child);
683
+ },
565
684
  /**
566
685
  * #### `removeChildFromContainer(container, child)`
567
686
  *
@@ -569,7 +688,14 @@ var hostConfig = {
569
688
  * to the root has a slightly different implementation, or if the root container nodes are of a different type
570
689
  * than the rest of the tree.
571
690
  */
572
- removeChildFromContainer: removeChild,
691
+ removeChildFromContainer(container, child) {
692
+ if (globalThis.TEST_RENDERER_ENABLE_PROFILING) {
693
+ mark("reconciler/removeChildFromContainer", {
694
+ childType: formatInstanceType(child)
695
+ });
696
+ }
697
+ removeChild(container, child);
698
+ },
573
699
  /**
574
700
  * #### `resetTextContent(instance)`
575
701
  *
@@ -579,7 +705,8 @@ var hostConfig = {
579
705
  *
580
706
  * If you never return `true` from `shouldSetTextContent`, you can leave it empty.
581
707
  */
582
- resetTextContent(_instance) {
708
+ resetTextContent(instance) {
709
+ mark("reconciler/resetTextContent", { type: instance.type });
583
710
  },
584
711
  /**
585
712
  * #### `commitTextUpdate(textInstance, prevText, nextText)`
@@ -588,7 +715,8 @@ var hostConfig = {
588
715
  *
589
716
  * Here, `textInstance` is a node created by `createTextInstance`.
590
717
  */
591
- commitTextUpdate(textInstance, _oldText, newText) {
718
+ commitTextUpdate(textInstance, oldText, newText) {
719
+ mark("reconciler/commitTextUpdate", { oldText, newText });
592
720
  textInstance.text = newText;
593
721
  },
594
722
  /**
@@ -610,7 +738,8 @@ var hostConfig = {
610
738
  *
611
739
  * If you never return `true` from `finalizeInitialChildren`, you can leave it empty.
612
740
  */
613
- commitMount(_instance, _type, _props, _internalHandle) {
741
+ commitMount(_instance, type, _props, _internalHandle) {
742
+ mark("reconciler/commitMount", { type });
614
743
  },
615
744
  /**
616
745
  * #### `commitUpdate(instance, type, prevProps, nextProps, internalHandle)`
@@ -628,8 +757,18 @@ var hostConfig = {
628
757
  // @ts-expect-error @types/react-reconciler types don't fully match react-reconciler's actual Flow types.
629
758
  // Correctness is verified through tests.
630
759
  commitUpdate(instance, type, _prevProps, nextProps, internalHandle) {
760
+ mark("reconciler/commitUpdate", { type });
631
761
  instance.type = type;
632
- instance.props = nextProps;
762
+ if (instance.isHidden && instance.rootContainer.config.transformHiddenInstanceProps != null) {
763
+ instance.propsBeforeHiding = nextProps;
764
+ instance.props = instance.rootContainer.config.transformHiddenInstanceProps({
765
+ props: nextProps,
766
+ type: instance.type
767
+ });
768
+ } else {
769
+ instance.props = nextProps;
770
+ instance.propsBeforeHiding = null;
771
+ }
633
772
  instance.unstable_fiber = internalHandle;
634
773
  },
635
774
  /**
@@ -639,7 +778,17 @@ var hostConfig = {
639
778
  * visual styling to hide it. It is used by Suspense to hide the tree while the fallback is visible.
640
779
  */
641
780
  hideInstance(instance) {
781
+ mark("reconciler/hideInstance", { type: instance.type });
782
+ if (instance.isHidden) {
783
+ return;
784
+ }
642
785
  instance.isHidden = true;
786
+ instance.propsBeforeHiding = instance.props;
787
+ const transformHiddenInstanceProps = instance.rootContainer.config.transformHiddenInstanceProps;
788
+ if (transformHiddenInstanceProps) {
789
+ const { props, type } = instance;
790
+ instance.props = transformHiddenInstanceProps({ props, type });
791
+ }
643
792
  },
644
793
  /**
645
794
  * #### `hideTextInstance(textInstance)`
@@ -647,6 +796,7 @@ var hostConfig = {
647
796
  * Same as `hideInstance`, but for nodes created by `createTextInstance`.
648
797
  */
649
798
  hideTextInstance(textInstance) {
799
+ mark("reconciler/hideTextInstance", { text: textInstance.text });
650
800
  textInstance.isHidden = true;
651
801
  },
652
802
  /**
@@ -655,7 +805,13 @@ var hostConfig = {
655
805
  * This method should make the `instance` visible, undoing what `hideInstance` did.
656
806
  */
657
807
  unhideInstance(instance, _props) {
808
+ mark("reconciler/unhideInstance", { type: instance.type });
658
809
  instance.isHidden = false;
810
+ const transformHiddenInstanceProps = instance.rootContainer.config.transformHiddenInstanceProps;
811
+ if (transformHiddenInstanceProps && instance.propsBeforeHiding) {
812
+ instance.props = instance.propsBeforeHiding;
813
+ instance.propsBeforeHiding = null;
814
+ }
659
815
  },
660
816
  /**
661
817
  * #### `unhideTextInstance(textInstance, text)`
@@ -663,6 +819,7 @@ var hostConfig = {
663
819
  * Same as `unhideInstance`, but for nodes created by `createTextInstance`.
664
820
  */
665
821
  unhideTextInstance(textInstance, _text) {
822
+ mark("reconciler/unhideTextInstance", { text: textInstance.text });
666
823
  textInstance.isHidden = false;
667
824
  },
668
825
  /**
@@ -671,6 +828,7 @@ var hostConfig = {
671
828
  * This method should mutate the `container` root node and remove all children from it.
672
829
  */
673
830
  clearContainer(container) {
831
+ mark("reconciler/clearContainer");
674
832
  container.children.forEach((child) => {
675
833
  child.parent = null;
676
834
  });
@@ -682,7 +840,8 @@ var hostConfig = {
682
840
  * This method is called during render to determine if the Host Component type and props require
683
841
  * some kind of loading process to complete before committing an update.
684
842
  */
685
- maySuspendCommit(_type, _props) {
843
+ maySuspendCommit(type, _props) {
844
+ mark("reconciler/maySuspendCommit", { type });
686
845
  return false;
687
846
  },
688
847
  /**
@@ -691,7 +850,8 @@ var hostConfig = {
691
850
  * This method may be called during render if the Host Component type and props might suspend a commit.
692
851
  * It can be used to initiate any work that might shorten the duration of a suspended commit.
693
852
  */
694
- preloadInstance(_type, _props) {
853
+ preloadInstance(type, _props) {
854
+ mark("reconciler/preloadInstance", { type });
695
855
  return true;
696
856
  },
697
857
  /**
@@ -701,6 +861,7 @@ var hostConfig = {
701
861
  * Components that might suspend this commit are evaluated to determine if the commit must be suspended.
702
862
  */
703
863
  startSuspendingCommit() {
864
+ mark("reconciler/startSuspendingCommit");
704
865
  },
705
866
  /**
706
867
  * #### `suspendInstance(type, props)`
@@ -708,7 +869,8 @@ var hostConfig = {
708
869
  * This method is called after `startSuspendingCommit` for each Host Component that indicated it might
709
870
  * suspend a commit.
710
871
  */
711
- suspendInstance() {
872
+ suspendInstance(type, _props) {
873
+ mark("reconciler/suspendInstance", { type });
712
874
  },
713
875
  /**
714
876
  * #### `waitForCommitToBeReady()`
@@ -720,7 +882,8 @@ var hostConfig = {
720
882
  * callback will initiate the commit when called. The return value is a cancellation function that the
721
883
  * Reconciler can use to abort the commit.
722
884
  */
723
- waitForCommitToBeReady() {
885
+ waitForCommitToBeReady(type, _props) {
886
+ mark("reconciler/waitForCommitToBeReady", { type });
724
887
  return null;
725
888
  },
726
889
  // -------------------
@@ -736,8 +899,10 @@ var hostConfig = {
736
899
  supportsHydration: false,
737
900
  NotPendingTransition: null,
738
901
  resetFormInstance(_form) {
902
+ mark("reconciler/resetFormInstance");
739
903
  },
740
904
  requestPostPaintCallback(_callback) {
905
+ mark("reconciler/requestPostPaintCallback");
741
906
  }
742
907
  };
743
908
  var TestReconciler = ReactReconciler(hostConfig);
@@ -763,9 +928,11 @@ function removeChild(parentInstance, child) {
763
928
  parentInstance.children.splice(index, 1);
764
929
  child.parent = null;
765
930
  }
931
+ function formatInstanceType(instance) {
932
+ return instance.tag === Tag.Text ? `text: "${instance.text}"` : instance.type;
933
+ }
766
934
 
767
935
  // src/renderer.ts
768
- var defaultCreateMockNode = () => ({});
769
936
  var defaultOnUncaughtError = (error, errorInfo) => {
770
937
  console.error("Uncaught error:", error, errorInfo);
771
938
  };
@@ -776,7 +943,8 @@ var defaultOnRecoverableError = (error, errorInfo) => {
776
943
  console.error("Recoverable error:", error, errorInfo);
777
944
  };
778
945
  function createRoot(options) {
779
- var _a, _b, _c, _d, _e, _f;
946
+ var _a, _b, _c, _d, _e;
947
+ measureStart("createRoot");
780
948
  let container = {
781
949
  tag: Tag.Container,
782
950
  parent: null,
@@ -785,7 +953,7 @@ function createRoot(options) {
785
953
  config: {
786
954
  textComponentTypes: options == null ? void 0 : options.textComponentTypes,
787
955
  publicTextComponentTypes: options == null ? void 0 : options.publicTextComponentTypes,
788
- createNodeMock: (_a = options == null ? void 0 : options.createNodeMock) != null ? _a : defaultCreateMockNode
956
+ transformHiddenInstanceProps: options == null ? void 0 : options.transformHiddenInstanceProps
789
957
  }
790
958
  };
791
959
  let containerFiber = TestReconciler.createContainer(
@@ -793,30 +961,41 @@ function createRoot(options) {
793
961
  ConcurrentRoot,
794
962
  null,
795
963
  // hydrationCallbacks
796
- (_b = options == null ? void 0 : options.isStrictMode) != null ? _b : false,
964
+ (_a = options == null ? void 0 : options.isStrictMode) != null ? _a : false,
797
965
  false,
798
966
  // concurrentUpdatesByDefaultOverride
799
- (_c = options == null ? void 0 : options.identifierPrefix) != null ? _c : "",
800
- (_d = options == null ? void 0 : options.onUncaughtError) != null ? _d : defaultOnUncaughtError,
801
- (_e = options == null ? void 0 : options.onCaughtError) != null ? _e : defaultOnCaughtError,
967
+ (_b = options == null ? void 0 : options.identifierPrefix) != null ? _b : "",
968
+ (_c = options == null ? void 0 : options.onUncaughtError) != null ? _c : defaultOnUncaughtError,
969
+ (_d = options == null ? void 0 : options.onCaughtError) != null ? _d : defaultOnCaughtError,
802
970
  // @ts-expect-error @types/react-reconciler types don't include onRecoverableError parameter
803
971
  // in the createContainer signature, but react-reconciler's actual Flow types do.
804
972
  // Correctness is verified through tests.
805
- (_f = options == null ? void 0 : options.onRecoverableError) != null ? _f : defaultOnRecoverableError,
973
+ (_e = options == null ? void 0 : options.onRecoverableError) != null ? _e : defaultOnRecoverableError,
806
974
  null
807
975
  // transitionCallbacks
808
976
  );
977
+ measureEnd("createRoot");
809
978
  const render = (element) => {
810
979
  if (containerFiber == null) {
811
980
  throw new Error("Cannot render after unmount");
812
981
  }
813
- TestReconciler.updateContainer(element, containerFiber, null, null);
982
+ measureStart("render");
983
+ try {
984
+ TestReconciler.updateContainer(element, containerFiber, null, null);
985
+ } finally {
986
+ measureEnd("render", { elementType: String(element.type) });
987
+ }
814
988
  };
815
989
  const unmount = () => {
816
990
  if (container == null) {
817
991
  return;
818
992
  }
819
- TestReconciler.updateContainer(null, containerFiber, null, null);
993
+ measureStart("unmount");
994
+ try {
995
+ TestReconciler.updateContainer(null, containerFiber, null, null);
996
+ } finally {
997
+ measureEnd("unmount");
998
+ }
820
999
  containerFiber = null;
821
1000
  container = null;
822
1001
  };
@@ -827,7 +1006,7 @@ function createRoot(options) {
827
1006
  if (container == null) {
828
1007
  throw new Error("Cannot access .container on unmounted test renderer");
829
1008
  }
830
- return HostElement.fromInstance(container);
1009
+ return TestInstance.fromInstance(container);
831
1010
  }
832
1011
  };
833
1012
  }