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/README.md +69 -31
- package/dist/index.cjs +254 -75
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -17
- package/dist/index.d.ts +28 -17
- package/dist/index.js +254 -75
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
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(
|
|
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
|
-
|
|
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(
|
|
59
|
-
results.push(
|
|
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/
|
|
66
|
-
function
|
|
94
|
+
// src/to-json.ts
|
|
95
|
+
function containerToJson(container) {
|
|
67
96
|
return {
|
|
68
97
|
type: CONTAINER_TYPE,
|
|
69
98
|
props: {},
|
|
70
|
-
children:
|
|
99
|
+
children: childrenToJson(container.children),
|
|
71
100
|
$$typeof: /* @__PURE__ */ Symbol.for("react.test.json")
|
|
72
101
|
};
|
|
73
102
|
}
|
|
74
|
-
function
|
|
75
|
-
|
|
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:
|
|
112
|
+
children: childrenToJson(instance.children),
|
|
83
113
|
$$typeof: /* @__PURE__ */ Symbol.for("react.test.json")
|
|
84
114
|
};
|
|
85
115
|
}
|
|
86
|
-
function
|
|
87
|
-
|
|
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
|
|
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 =
|
|
127
|
+
const renderedChild = instanceToJson(child);
|
|
97
128
|
if (renderedChild != null) {
|
|
98
129
|
result.push(renderedChild);
|
|
99
130
|
}
|
|
100
131
|
} else {
|
|
101
|
-
const renderedChild =
|
|
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/
|
|
141
|
+
// src/test-instance.ts
|
|
111
142
|
var instanceMap = /* @__PURE__ */ new WeakMap();
|
|
112
|
-
var
|
|
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
|
|
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
|
|
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 ?
|
|
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
|
|
168
|
-
if (
|
|
169
|
-
return
|
|
201
|
+
const testInstance = instanceMap.get(instance);
|
|
202
|
+
if (testInstance) {
|
|
203
|
+
return testInstance;
|
|
170
204
|
}
|
|
171
|
-
const result = new
|
|
205
|
+
const result = new _TestInstance(instance);
|
|
172
206
|
instanceMap.set(instance, result);
|
|
173
207
|
return result;
|
|
174
208
|
}
|
|
175
209
|
};
|
|
176
|
-
function
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
392
|
+
shouldSetTextContent(type, _props) {
|
|
393
|
+
mark("reconciler/shouldSetTextContent", { type, result: false });
|
|
350
394
|
return false;
|
|
351
395
|
},
|
|
352
|
-
setCurrentUpdatePriority(
|
|
353
|
-
|
|
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
|
-
|
|
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
|
|
413
|
-
|
|
414
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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,
|
|
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,
|
|
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.
|
|
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(
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
-
(
|
|
964
|
+
(_a = options == null ? void 0 : options.isStrictMode) != null ? _a : false,
|
|
797
965
|
false,
|
|
798
966
|
// concurrentUpdatesByDefaultOverride
|
|
799
|
-
(
|
|
800
|
-
(
|
|
801
|
-
(
|
|
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
|
-
(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1009
|
+
return TestInstance.fromInstance(container);
|
|
831
1010
|
}
|
|
832
1011
|
};
|
|
833
1012
|
}
|