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.cjs CHANGED
@@ -74,64 +74,95 @@ var Tag = {
74
74
  };
75
75
  var CONTAINER_TYPE = "";
76
76
 
77
+ // src/performance.ts
78
+ function mark(name, details) {
79
+ if (!globalThis.TEST_RENDERER_ENABLE_PROFILING) {
80
+ return;
81
+ }
82
+ performance.mark(`test-renderer/${name}`, { detail: details });
83
+ }
84
+ function measureStart(name) {
85
+ if (!globalThis.TEST_RENDERER_ENABLE_PROFILING) {
86
+ return;
87
+ }
88
+ performance.mark(`test-renderer/${name}:start`);
89
+ }
90
+ function measureEnd(name, details) {
91
+ if (!globalThis.TEST_RENDERER_ENABLE_PROFILING) {
92
+ return;
93
+ }
94
+ performance.mark(`test-renderer/${name}:end`);
95
+ performance.measure(`test-renderer/${name}`, {
96
+ start: `test-renderer/${name}:start`,
97
+ end: `test-renderer/${name}:end`,
98
+ detail: details
99
+ });
100
+ }
101
+
102
+ // src/reconciler.ts
103
+ var import_react_reconciler = __toESM(require("react-reconciler"), 1);
104
+ var import_constants3 = require("react-reconciler/constants");
105
+
77
106
  // src/query-all.ts
78
- function queryAll(element, predicate, options) {
107
+ function queryAll(instance, predicate, options) {
79
108
  var _a, _b;
80
109
  const includeSelf = (_a = options == null ? void 0 : options.includeSelf) != null ? _a : false;
81
110
  const matchDeepestOnly = (_b = options == null ? void 0 : options.matchDeepestOnly) != null ? _b : false;
82
111
  const results = [];
83
112
  const matchingDescendants = [];
84
- element.children.forEach((child) => {
113
+ instance.children.forEach((child) => {
85
114
  if (typeof child === "string") {
86
115
  return;
87
116
  }
88
117
  matchingDescendants.push(...queryAll(child, predicate, __spreadProps(__spreadValues({}, options), { includeSelf: true })));
89
118
  });
90
119
  if (includeSelf && // When matchDeepestOnly = true: add current element only if no descendants match
91
- (matchingDescendants.length === 0 || !matchDeepestOnly) && predicate(element)) {
92
- results.push(element);
120
+ (matchingDescendants.length === 0 || !matchDeepestOnly) && predicate(instance)) {
121
+ results.push(instance);
93
122
  }
94
123
  results.push(...matchingDescendants);
95
124
  return results;
96
125
  }
97
126
 
98
- // src/render-to-json.ts
99
- function renderContainerToJson(instance) {
127
+ // src/to-json.ts
128
+ function containerToJson(container) {
100
129
  return {
101
130
  type: CONTAINER_TYPE,
102
131
  props: {},
103
- children: renderChildrenToJson(instance.children),
132
+ children: childrenToJson(container.children),
104
133
  $$typeof: /* @__PURE__ */ Symbol.for("react.test.json")
105
134
  };
106
135
  }
107
- function renderInstanceToJson(instance) {
108
- if (instance.isHidden) {
136
+ function instanceToJson(instance) {
137
+ const shouldExcludeHidden = instance.rootContainer.config.transformHiddenInstanceProps == null;
138
+ if (instance.isHidden && shouldExcludeHidden) {
109
139
  return null;
110
140
  }
111
141
  const _a = instance.props, { children: _children } = _a, restProps = __objRest(_a, ["children"]);
112
142
  return {
113
143
  type: instance.type,
114
144
  props: restProps,
115
- children: renderChildrenToJson(instance.children),
145
+ children: childrenToJson(instance.children),
116
146
  $$typeof: /* @__PURE__ */ Symbol.for("react.test.json")
117
147
  };
118
148
  }
119
- function renderTextInstanceToJson(instance) {
120
- if (instance.isHidden) {
149
+ function textInstanceToJson(instance) {
150
+ const shouldExcludeHidden = instance.rootContainer.config.transformHiddenInstanceProps == null;
151
+ if (instance.isHidden && shouldExcludeHidden) {
121
152
  return null;
122
153
  }
123
154
  return instance.text;
124
155
  }
125
- function renderChildrenToJson(children) {
156
+ function childrenToJson(children) {
126
157
  const result = [];
127
158
  for (const child of children) {
128
159
  if (child.tag === Tag.Instance) {
129
- const renderedChild = renderInstanceToJson(child);
160
+ const renderedChild = instanceToJson(child);
130
161
  if (renderedChild != null) {
131
162
  result.push(renderedChild);
132
163
  }
133
164
  } else {
134
- const renderedChild = renderTextInstanceToJson(child);
165
+ const renderedChild = textInstanceToJson(child);
135
166
  if (renderedChild != null) {
136
167
  result.push(renderedChild);
137
168
  }
@@ -140,9 +171,9 @@ function renderChildrenToJson(children) {
140
171
  return result;
141
172
  }
142
173
 
143
- // src/host-element.ts
174
+ // src/test-instance.ts
144
175
  var instanceMap = /* @__PURE__ */ new WeakMap();
145
- var HostElement = class _HostElement {
176
+ var TestInstance = class _TestInstance {
146
177
  constructor(instance) {
147
178
  this.instance = instance;
148
179
  }
@@ -151,6 +182,7 @@ var HostElement = class _HostElement {
151
182
  return this.instance.tag === Tag.Instance ? this.instance.type : CONTAINER_TYPE;
152
183
  }
153
184
  /** The element's props object. */
185
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
154
186
  get props() {
155
187
  return this.instance.tag === Tag.Instance ? this.instance.props : {};
156
188
  }
@@ -160,11 +192,13 @@ var HostElement = class _HostElement {
160
192
  if (parentInstance == null) {
161
193
  return null;
162
194
  }
163
- return _HostElement.fromInstance(parentInstance);
195
+ return _TestInstance.fromInstance(parentInstance);
164
196
  }
165
- /** Array of child nodes (elements and text strings). Hidden children are excluded. */
197
+ /** Array of child nodes (elements and text strings). Hidden children are excluded by default. */
166
198
  get children() {
167
- const result = this.instance.children.filter((child) => !child.isHidden).map((child) => getHostNodeForInstance(child));
199
+ const container = this.instance.tag === Tag.Container ? this.instance : this.instance.rootContainer;
200
+ const shouldExcludeHiddenChildren = container.config.transformHiddenInstanceProps == null;
201
+ const result = this.instance.children.filter((child) => !child.isHidden || !shouldExcludeHiddenChildren).map((child) => getTestNodeForInstance(child));
168
202
  return result;
169
203
  }
170
204
  /**
@@ -180,10 +214,10 @@ var HostElement = class _HostElement {
180
214
  /**
181
215
  * Convert this element to a JSON representation suitable for snapshots.
182
216
  *
183
- * @returns JSON element or null if the element is hidden.
217
+ * @returns JSON element or null if the element is hidden and hidden nodes are excluded.
184
218
  */
185
219
  toJSON() {
186
- return this.instance.tag === Tag.Container ? renderContainerToJson(this.instance) : renderInstanceToJson(this.instance);
220
+ return this.instance.tag === Tag.Container ? containerToJson(this.instance) : instanceToJson(this.instance);
187
221
  }
188
222
  /**
189
223
  * Find all descendant elements matching the predicate.
@@ -197,28 +231,24 @@ var HostElement = class _HostElement {
197
231
  }
198
232
  /** @internal */
199
233
  static fromInstance(instance) {
200
- const hostElement = instanceMap.get(instance);
201
- if (hostElement) {
202
- return hostElement;
234
+ const testInstance = instanceMap.get(instance);
235
+ if (testInstance) {
236
+ return testInstance;
203
237
  }
204
- const result = new _HostElement(instance);
238
+ const result = new _TestInstance(instance);
205
239
  instanceMap.set(instance, result);
206
240
  return result;
207
241
  }
208
242
  };
209
- function getHostNodeForInstance(instance) {
243
+ function getTestNodeForInstance(instance) {
210
244
  switch (instance.tag) {
211
245
  case Tag.Text:
212
246
  return instance.text;
213
247
  case Tag.Instance:
214
- return HostElement.fromInstance(instance);
248
+ return TestInstance.fromInstance(instance);
215
249
  }
216
250
  }
217
251
 
218
- // src/reconciler.ts
219
- var import_react_reconciler = __toESM(require("react-reconciler"), 1);
220
- var import_constants3 = require("react-reconciler/constants");
221
-
222
252
  // src/utils.ts
223
253
  function formatComponentList(names) {
224
254
  if (names.length === 0) {
@@ -300,10 +330,12 @@ var hostConfig = {
300
330
  * something when an instance is definitely in the tree, look at `commitMount` instead.
301
331
  */
302
332
  createInstance(type, props, rootContainer, _hostContext, internalHandle) {
333
+ mark("reconciler/createInstance", { type });
303
334
  return {
304
335
  tag: Tag.Instance,
305
336
  type,
306
337
  props,
338
+ propsBeforeHiding: null,
307
339
  isHidden: false,
308
340
  children: [],
309
341
  parent: null,
@@ -319,6 +351,7 @@ var hostConfig = {
319
351
  */
320
352
  createTextInstance(text, rootContainer, hostContext, _internalHandle) {
321
353
  var _a;
354
+ mark("reconciler/createTextInstance", { text });
322
355
  if (rootContainer.config.textComponentTypes && !hostContext.isInsideText) {
323
356
  const componentTypes = (_a = rootContainer.config.publicTextComponentTypes) != null ? _a : rootContainer.config.textComponentTypes;
324
357
  throw new Error(
@@ -331,6 +364,7 @@ var hostConfig = {
331
364
  tag: Tag.Text,
332
365
  text,
333
366
  parent: null,
367
+ rootContainer,
334
368
  isHidden: false
335
369
  };
336
370
  },
@@ -344,7 +378,15 @@ var hostConfig = {
344
378
  * must not modify any other nodes. It's called while the tree is still being built up and not connected
345
379
  * to the actual tree on the screen.
346
380
  */
347
- appendInitialChild: appendChild,
381
+ appendInitialChild(parentInstance, child) {
382
+ if (globalThis.TEST_RENDERER_ENABLE_PROFILING) {
383
+ mark("reconciler/appendInitialChild", {
384
+ parentType: parentInstance.type,
385
+ childType: formatInstanceType(child)
386
+ });
387
+ }
388
+ appendChild(parentInstance, child);
389
+ },
348
390
  /**
349
391
  * #### `finalizeInitialChildren(instance, type, props, rootContainer, hostContext)`
350
392
  *
@@ -361,7 +403,8 @@ var hostConfig = {
361
403
  *
362
404
  * If you don't want to do anything here, you should return `false`.
363
405
  */
364
- finalizeInitialChildren(_instance, _type, _props, _rootContainer, _hostContext) {
406
+ finalizeInitialChildren(instance, _type, _props, _rootContainer, _hostContext) {
407
+ mark("reconciler/finalizeInitialChildren", { type: instance.type });
365
408
  return false;
366
409
  },
367
410
  /**
@@ -379,19 +422,24 @@ var hostConfig = {
379
422
  * If you don't want to do anything here, you should return `false`.
380
423
  * This method happens **in the render phase**. Do not mutate the tree from it.
381
424
  */
382
- shouldSetTextContent(_type, _props) {
425
+ shouldSetTextContent(type, _props) {
426
+ mark("reconciler/shouldSetTextContent", { type, result: false });
383
427
  return false;
384
428
  },
385
- setCurrentUpdatePriority(newPriority) {
386
- currentUpdatePriority = newPriority;
429
+ setCurrentUpdatePriority(priority) {
430
+ mark("reconciler/setCurrentUpdatePriority", { priority });
431
+ currentUpdatePriority = priority;
387
432
  },
388
433
  getCurrentUpdatePriority() {
389
434
  return currentUpdatePriority;
390
435
  },
391
436
  resolveUpdatePriority() {
392
- return currentUpdatePriority || import_constants3.DefaultEventPriority;
437
+ const priority = currentUpdatePriority || import_constants3.DefaultEventPriority;
438
+ mark("reconciler/resolveUpdatePriority", { priority });
439
+ return priority;
393
440
  },
394
441
  shouldAttemptEagerTransition() {
442
+ mark("reconciler/shouldAttemptEagerTransition", { result: false });
395
443
  return false;
396
444
  },
397
445
  /**
@@ -404,6 +452,7 @@ var hostConfig = {
404
452
  * This method happens **in the render phase**. Do not mutate the tree from it.
405
453
  */
406
454
  getRootHostContext(rootContainer) {
455
+ mark("reconciler/getRootHostContext");
407
456
  return {
408
457
  type: "ROOT",
409
458
  config: rootContainer.config,
@@ -428,6 +477,7 @@ var hostConfig = {
428
477
  */
429
478
  getChildHostContext(parentHostContext, type) {
430
479
  var _a;
480
+ mark("reconciler/getChildHostContext", { type });
431
481
  const isInsideText = Boolean((_a = parentHostContext.config.textComponentTypes) == null ? void 0 : _a.includes(type));
432
482
  return __spreadProps(__spreadValues({}, parentHostContext), { type, isInsideText });
433
483
  },
@@ -440,19 +490,19 @@ var hostConfig = {
440
490
  * If you don't want to do anything here, return `instance`.
441
491
  */
442
492
  getPublicInstance(instance) {
493
+ if (globalThis.TEST_RENDERER_ENABLE_PROFILING) {
494
+ mark("reconciler/getPublicInstance", {
495
+ type: formatInstanceType(instance)
496
+ });
497
+ }
443
498
  switch (instance.tag) {
444
499
  case Tag.Instance: {
445
- const createNodeMock = instance.rootContainer.config.createNodeMock;
446
- const mockNode = createNodeMock({
447
- type: instance.type,
448
- props: instance.props,
449
- key: null
450
- });
451
- nodeToInstanceMap.set(mockNode, instance);
452
- return mockNode;
500
+ const testInstance = TestInstance.fromInstance(instance);
501
+ nodeToInstanceMap.set(testInstance, instance);
502
+ return testInstance;
453
503
  }
454
504
  default:
455
- return instance;
505
+ return null;
456
506
  }
457
507
  },
458
508
  /**
@@ -465,6 +515,8 @@ var hostConfig = {
465
515
  * Even if you don't want to do anything here, you need to return `null` from it.
466
516
  */
467
517
  prepareForCommit(_containerInfo) {
518
+ mark("reconciler/prepareForCommit");
519
+ measureStart("react/commit");
468
520
  return null;
469
521
  },
470
522
  /**
@@ -476,6 +528,8 @@ var hostConfig = {
476
528
  * You can leave it empty.
477
529
  */
478
530
  resetAfterCommit(_containerInfo) {
531
+ measureEnd("react/commit");
532
+ mark("reconciler/resetAfterCommit");
479
533
  },
480
534
  /**
481
535
  * #### `preparePortalMount(containerInfo)`
@@ -483,19 +537,31 @@ var hostConfig = {
483
537
  * This method is called for a container that's used as a portal target. Usually you can leave it empty.
484
538
  */
485
539
  preparePortalMount(_containerInfo) {
540
+ mark("reconciler/preparePortalMount");
486
541
  },
487
542
  /**
488
543
  * #### `scheduleTimeout(fn, delay)`
489
544
  *
490
545
  * You can proxy this to `setTimeout` or its equivalent in your environment.
491
546
  */
492
- scheduleTimeout: setTimeout,
547
+ scheduleTimeout(fn, delay) {
548
+ const id = setTimeout(() => {
549
+ mark("reconciler/scheduled timeout:start");
550
+ fn();
551
+ mark("reconciler/scheduled timeout:end");
552
+ }, delay);
553
+ mark("reconciler/scheduleTimeout", { id });
554
+ return id;
555
+ },
493
556
  /**
494
557
  * #### `cancelTimeout(id)`
495
558
  *
496
559
  * You can proxy this to `clearTimeout` or its equivalent in your environment.
497
560
  */
498
- cancelTimeout: clearTimeout,
561
+ cancelTimeout(id) {
562
+ mark("reconciler/cancelTimeout", { id });
563
+ clearTimeout(id);
564
+ },
499
565
  /**
500
566
  * #### `noTimeout`
501
567
  *
@@ -517,7 +583,14 @@ var hostConfig = {
517
583
  *
518
584
  * Optional. You can proxy this to `queueMicrotask` or its equivalent in your environment.
519
585
  */
520
- scheduleMicrotask: queueMicrotask,
586
+ scheduleMicrotask(fn) {
587
+ mark("reconciler/scheduleMicrotask");
588
+ queueMicrotask(() => {
589
+ mark("reconciler/scheduled microtask:start");
590
+ fn();
591
+ mark("reconciler/scheduled microtask:end");
592
+ });
593
+ },
521
594
  /**
522
595
  * #### `isPrimaryRenderer`
523
596
  *
@@ -531,6 +604,7 @@ var hostConfig = {
531
604
  */
532
605
  warnsIfNotActing: true,
533
606
  getInstanceFromNode(node) {
607
+ mark("reconciler/getInstanceFromNode");
534
608
  const instance = nodeToInstanceMap.get(node);
535
609
  if (instance !== void 0) {
536
610
  return instance.unstable_fiber;
@@ -538,17 +612,22 @@ var hostConfig = {
538
612
  return null;
539
613
  },
540
614
  beforeActiveInstanceBlur() {
615
+ mark("reconciler/beforeActiveInstanceBlur");
541
616
  },
542
617
  afterActiveInstanceBlur() {
618
+ mark("reconciler/afterActiveInstanceBlur");
543
619
  },
544
620
  prepareScopeUpdate(scopeInstance, instance) {
621
+ mark("reconciler/prepareScopeUpdate");
545
622
  nodeToInstanceMap.set(scopeInstance, instance);
546
623
  },
547
624
  getInstanceFromScope(scopeInstance) {
548
625
  var _a;
626
+ mark("reconciler/getInstanceFromScope");
549
627
  return (_a = nodeToInstanceMap.get(scopeInstance)) != null ? _a : null;
550
628
  },
551
629
  detachDeletedInstance(_node) {
630
+ mark("reconciler/detachDeletedInstance");
552
631
  },
553
632
  /**
554
633
  * #### `appendChild(parentInstance, child)`
@@ -559,7 +638,15 @@ var hostConfig = {
559
638
  * Although this method currently runs in the commit phase, you still should not mutate any other nodes
560
639
  * in it. If you need to do some additional work when a node is definitely connected to the visible tree, look at `commitMount`.
561
640
  */
562
- appendChild,
641
+ appendChild(parentInstance, child) {
642
+ if (globalThis.TEST_RENDERER_ENABLE_PROFILING) {
643
+ mark("reconciler/appendChild", {
644
+ parentType: parentInstance.type,
645
+ childType: formatInstanceType(child)
646
+ });
647
+ }
648
+ appendChild(parentInstance, child);
649
+ },
563
650
  /**
564
651
  * #### `appendChildToContainer(container, child)`
565
652
  *
@@ -567,7 +654,14 @@ var hostConfig = {
567
654
  * to the root has a slightly different implementation, or if the root container nodes are of a different
568
655
  * type than the rest of the tree.
569
656
  */
570
- appendChildToContainer: appendChild,
657
+ appendChildToContainer(container, child) {
658
+ if (globalThis.TEST_RENDERER_ENABLE_PROFILING) {
659
+ mark("reconciler/appendChildToContainer", {
660
+ childType: formatInstanceType(child)
661
+ });
662
+ }
663
+ appendChild(container, child);
664
+ },
571
665
  /**
572
666
  * #### `insertBefore(parentInstance, child, beforeChild)`
573
667
  *
@@ -577,7 +671,16 @@ var hostConfig = {
577
671
  * Note that React uses this method both for insertions and for reordering nodes. Similar to DOM, it is expected
578
672
  * that you can call `insertBefore` to reposition an existing child. Do not mutate any other parts of the tree from it.
579
673
  */
580
- insertBefore,
674
+ insertBefore(parentInstance, child, beforeChild) {
675
+ if (globalThis.TEST_RENDERER_ENABLE_PROFILING) {
676
+ mark("reconciler/insertBefore", {
677
+ parentType: parentInstance.type,
678
+ childType: formatInstanceType(child),
679
+ beforeChildType: formatInstanceType(beforeChild)
680
+ });
681
+ }
682
+ insertBefore(parentInstance, child, beforeChild);
683
+ },
581
684
  /**
582
685
  * #### `insertInContainerBefore(container, child, beforeChild)
583
686
  *
@@ -585,7 +688,15 @@ var hostConfig = {
585
688
  * to the root has a slightly different implementation, or if the root container nodes are of a different type
586
689
  * than the rest of the tree.
587
690
  */
588
- insertInContainerBefore: insertBefore,
691
+ insertInContainerBefore(container, child, beforeChild) {
692
+ if (globalThis.TEST_RENDERER_ENABLE_PROFILING) {
693
+ mark("reconciler/insertInContainerBefore", {
694
+ childType: formatInstanceType(child),
695
+ beforeChildType: formatInstanceType(beforeChild)
696
+ });
697
+ }
698
+ insertBefore(container, child, beforeChild);
699
+ },
589
700
  /**
590
701
  * #### `removeChild(parentInstance, child)`
591
702
  *
@@ -594,7 +705,15 @@ var hostConfig = {
594
705
  * React will only call it for the top-level node that is being removed. It is expected that garbage collection
595
706
  * would take care of the whole subtree. You are not expected to traverse the child tree in it.
596
707
  */
597
- removeChild,
708
+ removeChild(parentInstance, child) {
709
+ if (globalThis.TEST_RENDERER_ENABLE_PROFILING) {
710
+ mark("reconciler/removeChild", {
711
+ parentType: parentInstance.type,
712
+ childType: formatInstanceType(child)
713
+ });
714
+ }
715
+ removeChild(parentInstance, child);
716
+ },
598
717
  /**
599
718
  * #### `removeChildFromContainer(container, child)`
600
719
  *
@@ -602,7 +721,14 @@ var hostConfig = {
602
721
  * to the root has a slightly different implementation, or if the root container nodes are of a different type
603
722
  * than the rest of the tree.
604
723
  */
605
- removeChildFromContainer: removeChild,
724
+ removeChildFromContainer(container, child) {
725
+ if (globalThis.TEST_RENDERER_ENABLE_PROFILING) {
726
+ mark("reconciler/removeChildFromContainer", {
727
+ childType: formatInstanceType(child)
728
+ });
729
+ }
730
+ removeChild(container, child);
731
+ },
606
732
  /**
607
733
  * #### `resetTextContent(instance)`
608
734
  *
@@ -612,7 +738,8 @@ var hostConfig = {
612
738
  *
613
739
  * If you never return `true` from `shouldSetTextContent`, you can leave it empty.
614
740
  */
615
- resetTextContent(_instance) {
741
+ resetTextContent(instance) {
742
+ mark("reconciler/resetTextContent", { type: instance.type });
616
743
  },
617
744
  /**
618
745
  * #### `commitTextUpdate(textInstance, prevText, nextText)`
@@ -621,7 +748,8 @@ var hostConfig = {
621
748
  *
622
749
  * Here, `textInstance` is a node created by `createTextInstance`.
623
750
  */
624
- commitTextUpdate(textInstance, _oldText, newText) {
751
+ commitTextUpdate(textInstance, oldText, newText) {
752
+ mark("reconciler/commitTextUpdate", { oldText, newText });
625
753
  textInstance.text = newText;
626
754
  },
627
755
  /**
@@ -643,7 +771,8 @@ var hostConfig = {
643
771
  *
644
772
  * If you never return `true` from `finalizeInitialChildren`, you can leave it empty.
645
773
  */
646
- commitMount(_instance, _type, _props, _internalHandle) {
774
+ commitMount(_instance, type, _props, _internalHandle) {
775
+ mark("reconciler/commitMount", { type });
647
776
  },
648
777
  /**
649
778
  * #### `commitUpdate(instance, type, prevProps, nextProps, internalHandle)`
@@ -661,8 +790,18 @@ var hostConfig = {
661
790
  // @ts-expect-error @types/react-reconciler types don't fully match react-reconciler's actual Flow types.
662
791
  // Correctness is verified through tests.
663
792
  commitUpdate(instance, type, _prevProps, nextProps, internalHandle) {
793
+ mark("reconciler/commitUpdate", { type });
664
794
  instance.type = type;
665
- instance.props = nextProps;
795
+ if (instance.isHidden && instance.rootContainer.config.transformHiddenInstanceProps != null) {
796
+ instance.propsBeforeHiding = nextProps;
797
+ instance.props = instance.rootContainer.config.transformHiddenInstanceProps({
798
+ props: nextProps,
799
+ type: instance.type
800
+ });
801
+ } else {
802
+ instance.props = nextProps;
803
+ instance.propsBeforeHiding = null;
804
+ }
666
805
  instance.unstable_fiber = internalHandle;
667
806
  },
668
807
  /**
@@ -672,7 +811,17 @@ var hostConfig = {
672
811
  * visual styling to hide it. It is used by Suspense to hide the tree while the fallback is visible.
673
812
  */
674
813
  hideInstance(instance) {
814
+ mark("reconciler/hideInstance", { type: instance.type });
815
+ if (instance.isHidden) {
816
+ return;
817
+ }
675
818
  instance.isHidden = true;
819
+ instance.propsBeforeHiding = instance.props;
820
+ const transformHiddenInstanceProps = instance.rootContainer.config.transformHiddenInstanceProps;
821
+ if (transformHiddenInstanceProps) {
822
+ const { props, type } = instance;
823
+ instance.props = transformHiddenInstanceProps({ props, type });
824
+ }
676
825
  },
677
826
  /**
678
827
  * #### `hideTextInstance(textInstance)`
@@ -680,6 +829,7 @@ var hostConfig = {
680
829
  * Same as `hideInstance`, but for nodes created by `createTextInstance`.
681
830
  */
682
831
  hideTextInstance(textInstance) {
832
+ mark("reconciler/hideTextInstance", { text: textInstance.text });
683
833
  textInstance.isHidden = true;
684
834
  },
685
835
  /**
@@ -688,7 +838,13 @@ var hostConfig = {
688
838
  * This method should make the `instance` visible, undoing what `hideInstance` did.
689
839
  */
690
840
  unhideInstance(instance, _props) {
841
+ mark("reconciler/unhideInstance", { type: instance.type });
691
842
  instance.isHidden = false;
843
+ const transformHiddenInstanceProps = instance.rootContainer.config.transformHiddenInstanceProps;
844
+ if (transformHiddenInstanceProps && instance.propsBeforeHiding) {
845
+ instance.props = instance.propsBeforeHiding;
846
+ instance.propsBeforeHiding = null;
847
+ }
692
848
  },
693
849
  /**
694
850
  * #### `unhideTextInstance(textInstance, text)`
@@ -696,6 +852,7 @@ var hostConfig = {
696
852
  * Same as `unhideInstance`, but for nodes created by `createTextInstance`.
697
853
  */
698
854
  unhideTextInstance(textInstance, _text) {
855
+ mark("reconciler/unhideTextInstance", { text: textInstance.text });
699
856
  textInstance.isHidden = false;
700
857
  },
701
858
  /**
@@ -704,6 +861,7 @@ var hostConfig = {
704
861
  * This method should mutate the `container` root node and remove all children from it.
705
862
  */
706
863
  clearContainer(container) {
864
+ mark("reconciler/clearContainer");
707
865
  container.children.forEach((child) => {
708
866
  child.parent = null;
709
867
  });
@@ -715,7 +873,8 @@ var hostConfig = {
715
873
  * This method is called during render to determine if the Host Component type and props require
716
874
  * some kind of loading process to complete before committing an update.
717
875
  */
718
- maySuspendCommit(_type, _props) {
876
+ maySuspendCommit(type, _props) {
877
+ mark("reconciler/maySuspendCommit", { type });
719
878
  return false;
720
879
  },
721
880
  /**
@@ -724,7 +883,8 @@ var hostConfig = {
724
883
  * This method may be called during render if the Host Component type and props might suspend a commit.
725
884
  * It can be used to initiate any work that might shorten the duration of a suspended commit.
726
885
  */
727
- preloadInstance(_type, _props) {
886
+ preloadInstance(type, _props) {
887
+ mark("reconciler/preloadInstance", { type });
728
888
  return true;
729
889
  },
730
890
  /**
@@ -734,6 +894,7 @@ var hostConfig = {
734
894
  * Components that might suspend this commit are evaluated to determine if the commit must be suspended.
735
895
  */
736
896
  startSuspendingCommit() {
897
+ mark("reconciler/startSuspendingCommit");
737
898
  },
738
899
  /**
739
900
  * #### `suspendInstance(type, props)`
@@ -741,7 +902,8 @@ var hostConfig = {
741
902
  * This method is called after `startSuspendingCommit` for each Host Component that indicated it might
742
903
  * suspend a commit.
743
904
  */
744
- suspendInstance() {
905
+ suspendInstance(type, _props) {
906
+ mark("reconciler/suspendInstance", { type });
745
907
  },
746
908
  /**
747
909
  * #### `waitForCommitToBeReady()`
@@ -753,7 +915,8 @@ var hostConfig = {
753
915
  * callback will initiate the commit when called. The return value is a cancellation function that the
754
916
  * Reconciler can use to abort the commit.
755
917
  */
756
- waitForCommitToBeReady() {
918
+ waitForCommitToBeReady(type, _props) {
919
+ mark("reconciler/waitForCommitToBeReady", { type });
757
920
  return null;
758
921
  },
759
922
  // -------------------
@@ -769,8 +932,10 @@ var hostConfig = {
769
932
  supportsHydration: false,
770
933
  NotPendingTransition: null,
771
934
  resetFormInstance(_form) {
935
+ mark("reconciler/resetFormInstance");
772
936
  },
773
937
  requestPostPaintCallback(_callback) {
938
+ mark("reconciler/requestPostPaintCallback");
774
939
  }
775
940
  };
776
941
  var TestReconciler = (0, import_react_reconciler.default)(hostConfig);
@@ -796,9 +961,11 @@ function removeChild(parentInstance, child) {
796
961
  parentInstance.children.splice(index, 1);
797
962
  child.parent = null;
798
963
  }
964
+ function formatInstanceType(instance) {
965
+ return instance.tag === Tag.Text ? `text: "${instance.text}"` : instance.type;
966
+ }
799
967
 
800
968
  // src/renderer.ts
801
- var defaultCreateMockNode = () => ({});
802
969
  var defaultOnUncaughtError = (error, errorInfo) => {
803
970
  console.error("Uncaught error:", error, errorInfo);
804
971
  };
@@ -809,7 +976,8 @@ var defaultOnRecoverableError = (error, errorInfo) => {
809
976
  console.error("Recoverable error:", error, errorInfo);
810
977
  };
811
978
  function createRoot(options) {
812
- var _a, _b, _c, _d, _e, _f;
979
+ var _a, _b, _c, _d, _e;
980
+ measureStart("createRoot");
813
981
  let container = {
814
982
  tag: Tag.Container,
815
983
  parent: null,
@@ -818,7 +986,7 @@ function createRoot(options) {
818
986
  config: {
819
987
  textComponentTypes: options == null ? void 0 : options.textComponentTypes,
820
988
  publicTextComponentTypes: options == null ? void 0 : options.publicTextComponentTypes,
821
- createNodeMock: (_a = options == null ? void 0 : options.createNodeMock) != null ? _a : defaultCreateMockNode
989
+ transformHiddenInstanceProps: options == null ? void 0 : options.transformHiddenInstanceProps
822
990
  }
823
991
  };
824
992
  let containerFiber = TestReconciler.createContainer(
@@ -826,30 +994,41 @@ function createRoot(options) {
826
994
  import_constants5.ConcurrentRoot,
827
995
  null,
828
996
  // hydrationCallbacks
829
- (_b = options == null ? void 0 : options.isStrictMode) != null ? _b : false,
997
+ (_a = options == null ? void 0 : options.isStrictMode) != null ? _a : false,
830
998
  false,
831
999
  // concurrentUpdatesByDefaultOverride
832
- (_c = options == null ? void 0 : options.identifierPrefix) != null ? _c : "",
833
- (_d = options == null ? void 0 : options.onUncaughtError) != null ? _d : defaultOnUncaughtError,
834
- (_e = options == null ? void 0 : options.onCaughtError) != null ? _e : defaultOnCaughtError,
1000
+ (_b = options == null ? void 0 : options.identifierPrefix) != null ? _b : "",
1001
+ (_c = options == null ? void 0 : options.onUncaughtError) != null ? _c : defaultOnUncaughtError,
1002
+ (_d = options == null ? void 0 : options.onCaughtError) != null ? _d : defaultOnCaughtError,
835
1003
  // @ts-expect-error @types/react-reconciler types don't include onRecoverableError parameter
836
1004
  // in the createContainer signature, but react-reconciler's actual Flow types do.
837
1005
  // Correctness is verified through tests.
838
- (_f = options == null ? void 0 : options.onRecoverableError) != null ? _f : defaultOnRecoverableError,
1006
+ (_e = options == null ? void 0 : options.onRecoverableError) != null ? _e : defaultOnRecoverableError,
839
1007
  null
840
1008
  // transitionCallbacks
841
1009
  );
1010
+ measureEnd("createRoot");
842
1011
  const render = (element) => {
843
1012
  if (containerFiber == null) {
844
1013
  throw new Error("Cannot render after unmount");
845
1014
  }
846
- TestReconciler.updateContainer(element, containerFiber, null, null);
1015
+ measureStart("render");
1016
+ try {
1017
+ TestReconciler.updateContainer(element, containerFiber, null, null);
1018
+ } finally {
1019
+ measureEnd("render", { elementType: String(element.type) });
1020
+ }
847
1021
  };
848
1022
  const unmount = () => {
849
1023
  if (container == null) {
850
1024
  return;
851
1025
  }
852
- TestReconciler.updateContainer(null, containerFiber, null, null);
1026
+ measureStart("unmount");
1027
+ try {
1028
+ TestReconciler.updateContainer(null, containerFiber, null, null);
1029
+ } finally {
1030
+ measureEnd("unmount");
1031
+ }
853
1032
  containerFiber = null;
854
1033
  container = null;
855
1034
  };
@@ -860,7 +1039,7 @@ function createRoot(options) {
860
1039
  if (container == null) {
861
1040
  throw new Error("Cannot access .container on unmounted test renderer");
862
1041
  }
863
- return HostElement.fromInstance(container);
1042
+ return TestInstance.fromInstance(container);
864
1043
  }
865
1044
  };
866
1045
  }