test-renderer 0.15.0 → 0.16.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 +2 -0
- package/dist/index.cjs +560 -1006
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +77 -81
- package/dist/index.js +544 -980
- package/dist/index.js.map +1 -1
- package/package.json +39 -33
- package/dist/index.d.cts +0 -130
package/dist/index.js
CHANGED
|
@@ -1,1016 +1,580 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
var __spreadValues = (a, b) => {
|
|
9
|
-
for (var prop in b || (b = {}))
|
|
10
|
-
if (__hasOwnProp.call(b, prop))
|
|
11
|
-
__defNormalProp(a, prop, b[prop]);
|
|
12
|
-
if (__getOwnPropSymbols)
|
|
13
|
-
for (var prop of __getOwnPropSymbols(b)) {
|
|
14
|
-
if (__propIsEnum.call(b, prop))
|
|
15
|
-
__defNormalProp(a, prop, b[prop]);
|
|
16
|
-
}
|
|
17
|
-
return a;
|
|
18
|
-
};
|
|
19
|
-
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
-
var __objRest = (source, exclude) => {
|
|
21
|
-
var target = {};
|
|
22
|
-
for (var prop in source)
|
|
23
|
-
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
24
|
-
target[prop] = source[prop];
|
|
25
|
-
if (source != null && __getOwnPropSymbols)
|
|
26
|
-
for (var prop of __getOwnPropSymbols(source)) {
|
|
27
|
-
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
28
|
-
target[prop] = source[prop];
|
|
29
|
-
}
|
|
30
|
-
return target;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
// src/renderer.ts
|
|
34
|
-
import { ConcurrentRoot } from "react-reconciler/constants";
|
|
35
|
-
|
|
36
|
-
// src/constants.ts
|
|
37
|
-
var Tag = {
|
|
38
|
-
Container: "CONTAINER",
|
|
39
|
-
Instance: "INSTANCE",
|
|
40
|
-
Text: "TEXT"
|
|
1
|
+
import { ConcurrentRoot, DefaultEventPriority, NoEventPriority } from "react-reconciler/constants.js";
|
|
2
|
+
import ReactReconciler from "react-reconciler";
|
|
3
|
+
//#region src/constants.ts
|
|
4
|
+
const Tag = {
|
|
5
|
+
Container: "CONTAINER",
|
|
6
|
+
Instance: "INSTANCE",
|
|
7
|
+
Text: "TEXT"
|
|
41
8
|
};
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
// src/performance.ts
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/performance.ts
|
|
45
11
|
function mark(name, details) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
performance.mark(`test-renderer/${name}`, { detail: details });
|
|
12
|
+
if (!globalThis.TEST_RENDERER_ENABLE_PROFILING) return;
|
|
13
|
+
performance.mark(`test-renderer/${name}`, { detail: details });
|
|
50
14
|
}
|
|
51
15
|
function measureStart(name) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
performance.mark(`test-renderer/${name}:start`);
|
|
16
|
+
if (!globalThis.TEST_RENDERER_ENABLE_PROFILING) return;
|
|
17
|
+
performance.mark(`test-renderer/${name}:start`);
|
|
56
18
|
}
|
|
57
19
|
function measureEnd(name, details) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
detail: details
|
|
66
|
-
});
|
|
20
|
+
if (!globalThis.TEST_RENDERER_ENABLE_PROFILING) return;
|
|
21
|
+
performance.mark(`test-renderer/${name}:end`);
|
|
22
|
+
performance.measure(`test-renderer/${name}`, {
|
|
23
|
+
start: `test-renderer/${name}:start`,
|
|
24
|
+
end: `test-renderer/${name}:end`,
|
|
25
|
+
detail: details
|
|
26
|
+
});
|
|
67
27
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/query-all.ts
|
|
30
|
+
/**
|
|
31
|
+
* Find all descendant elements matching the predicate.
|
|
32
|
+
*
|
|
33
|
+
* @param instance - Root TestInstance to search from.
|
|
34
|
+
* @param predicate - Function that returns true for matching elements.
|
|
35
|
+
* @param options - Optional query configuration.
|
|
36
|
+
* @returns Array of matching elements in tree order.
|
|
37
|
+
*/
|
|
74
38
|
function queryAll(instance, predicate, options) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
results.push(...matchingDescendants);
|
|
91
|
-
return results;
|
|
39
|
+
const includeSelf = options?.includeSelf ?? false;
|
|
40
|
+
const matchDeepestOnly = options?.matchDeepestOnly ?? false;
|
|
41
|
+
const results = [];
|
|
42
|
+
const matchingDescendants = [];
|
|
43
|
+
instance.children.forEach((child) => {
|
|
44
|
+
if (typeof child === "string") return;
|
|
45
|
+
matchingDescendants.push(...queryAll(child, predicate, {
|
|
46
|
+
...options,
|
|
47
|
+
includeSelf: true
|
|
48
|
+
}));
|
|
49
|
+
});
|
|
50
|
+
if (includeSelf && (matchingDescendants.length === 0 || !matchDeepestOnly) && predicate(instance)) results.push(instance);
|
|
51
|
+
results.push(...matchingDescendants);
|
|
52
|
+
return results;
|
|
92
53
|
}
|
|
93
|
-
|
|
94
|
-
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/to-json.ts
|
|
95
56
|
function containerToJson(container) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
57
|
+
return {
|
|
58
|
+
type: "",
|
|
59
|
+
props: {},
|
|
60
|
+
children: childrenToJson(container.children),
|
|
61
|
+
$$typeof: Symbol.for("react.test.json")
|
|
62
|
+
};
|
|
102
63
|
}
|
|
103
64
|
function instanceToJson(instance) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
$$typeof: /* @__PURE__ */ Symbol.for("react.test.json")
|
|
114
|
-
};
|
|
65
|
+
const shouldExcludeHidden = instance.rootContainer.config.transformHiddenInstanceProps == null;
|
|
66
|
+
if (instance.isHidden && shouldExcludeHidden) return null;
|
|
67
|
+
const { children: _children, ...restProps } = instance.props;
|
|
68
|
+
return {
|
|
69
|
+
type: instance.type,
|
|
70
|
+
props: restProps,
|
|
71
|
+
children: childrenToJson(instance.children),
|
|
72
|
+
$$typeof: Symbol.for("react.test.json")
|
|
73
|
+
};
|
|
115
74
|
}
|
|
116
75
|
function textInstanceToJson(instance) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
return instance.text;
|
|
76
|
+
const shouldExcludeHidden = instance.rootContainer.config.transformHiddenInstanceProps == null;
|
|
77
|
+
if (instance.isHidden && shouldExcludeHidden) return null;
|
|
78
|
+
return instance.text;
|
|
122
79
|
}
|
|
123
80
|
function childrenToJson(children) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if (renderedChild != null) {
|
|
134
|
-
result.push(renderedChild);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
return result;
|
|
81
|
+
const result = [];
|
|
82
|
+
for (const child of children) if (child.tag === Tag.Instance) {
|
|
83
|
+
const renderedChild = instanceToJson(child);
|
|
84
|
+
if (renderedChild != null) result.push(renderedChild);
|
|
85
|
+
} else {
|
|
86
|
+
const renderedChild = textInstanceToJson(child);
|
|
87
|
+
if (renderedChild != null) result.push(renderedChild);
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
139
90
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
instanceMap.set(instance, result);
|
|
207
|
-
return result;
|
|
208
|
-
}
|
|
91
|
+
//#endregion
|
|
92
|
+
//#region src/test-instance.ts
|
|
93
|
+
const instanceMap = /* @__PURE__ */ new WeakMap();
|
|
94
|
+
/**
|
|
95
|
+
* Represents a rendered host element in the test renderer tree.
|
|
96
|
+
* Provides a DOM-like API for querying and inspecting rendered components.
|
|
97
|
+
*/
|
|
98
|
+
var TestInstance = class TestInstance {
|
|
99
|
+
constructor(instance) {
|
|
100
|
+
this.instance = instance;
|
|
101
|
+
}
|
|
102
|
+
/** The element type (e.g., "div", "span"). Empty string for container. */
|
|
103
|
+
get type() {
|
|
104
|
+
return this.instance.tag === Tag.Instance ? this.instance.type : "";
|
|
105
|
+
}
|
|
106
|
+
/** The element's props object. */
|
|
107
|
+
get props() {
|
|
108
|
+
return this.instance.tag === Tag.Instance ? this.instance.props : {};
|
|
109
|
+
}
|
|
110
|
+
/** The parent element, or null if this is the root container. */
|
|
111
|
+
get parent() {
|
|
112
|
+
const parentInstance = this.instance.parent;
|
|
113
|
+
if (parentInstance == null) return null;
|
|
114
|
+
return TestInstance.fromInstance(parentInstance);
|
|
115
|
+
}
|
|
116
|
+
/** Array of child nodes (elements and text strings). Hidden children are excluded by default. */
|
|
117
|
+
get children() {
|
|
118
|
+
const shouldExcludeHiddenChildren = (this.instance.tag === Tag.Container ? this.instance : this.instance.rootContainer).config.transformHiddenInstanceProps == null;
|
|
119
|
+
return this.instance.children.filter((child) => !child.isHidden || !shouldExcludeHiddenChildren).map((child) => getTestNodeForInstance(child));
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Access to the underlying React Fiber node. This is an unstable API that exposes
|
|
123
|
+
* internal react-reconciler structures which may change without warning in future
|
|
124
|
+
* React versions. Use with caution and only when absolutely necessary.
|
|
125
|
+
*
|
|
126
|
+
* @returns The Fiber node for this instance, or null if this is a container.
|
|
127
|
+
*/
|
|
128
|
+
get unstable_fiber() {
|
|
129
|
+
return this.instance.tag === Tag.Instance ? this.instance.unstable_fiber : null;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Convert this element to a JSON representation suitable for snapshots.
|
|
133
|
+
*
|
|
134
|
+
* @returns JSON element or null if the element is hidden and hidden nodes are excluded.
|
|
135
|
+
*/
|
|
136
|
+
toJSON() {
|
|
137
|
+
return this.instance.tag === Tag.Container ? containerToJson(this.instance) : instanceToJson(this.instance);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Find all descendant elements matching the predicate.
|
|
141
|
+
*
|
|
142
|
+
* @param predicate - Function that returns true for matching elements.
|
|
143
|
+
* @param options - Optional query configuration.
|
|
144
|
+
* @returns Array of matching elements.
|
|
145
|
+
*/
|
|
146
|
+
queryAll(predicate, options) {
|
|
147
|
+
return queryAll(this, predicate, options);
|
|
148
|
+
}
|
|
149
|
+
/** @internal */
|
|
150
|
+
static fromInstance(instance) {
|
|
151
|
+
const testInstance = instanceMap.get(instance);
|
|
152
|
+
if (testInstance) return testInstance;
|
|
153
|
+
const result = new TestInstance(instance);
|
|
154
|
+
instanceMap.set(instance, result);
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
209
157
|
};
|
|
210
158
|
function getTestNodeForInstance(instance) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
return TestInstance.fromInstance(instance);
|
|
216
|
-
}
|
|
159
|
+
switch (instance.tag) {
|
|
160
|
+
case Tag.Text: return instance.text;
|
|
161
|
+
case Tag.Instance: return TestInstance.fromInstance(instance);
|
|
162
|
+
}
|
|
217
163
|
}
|
|
218
|
-
|
|
219
|
-
|
|
164
|
+
//#endregion
|
|
165
|
+
//#region src/utils.ts
|
|
220
166
|
function formatComponentList(names) {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if (names.length === 2) {
|
|
228
|
-
return `<${names[0]}> or <${names[1]}>`;
|
|
229
|
-
}
|
|
230
|
-
const allButLast = names.slice(0, -1);
|
|
231
|
-
const last = names[names.length - 1];
|
|
232
|
-
return `${allButLast.map((name) => `<${name}>`).join(", ")}, or <${last}>`;
|
|
167
|
+
if (names.length === 0) return "";
|
|
168
|
+
if (names.length === 1) return `<${names[0]}>`;
|
|
169
|
+
if (names.length === 2) return `<${names[0]}> or <${names[1]}>`;
|
|
170
|
+
const allButLast = names.slice(0, -1);
|
|
171
|
+
const last = names[names.length - 1];
|
|
172
|
+
return `${allButLast.map((name) => `<${name}>`).join(", ")}, or <${last}>`;
|
|
233
173
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
* #### `supportsMicrotasks`
|
|
541
|
-
*
|
|
542
|
-
* Set this to `true` to indicate that your renderer supports `scheduleMicrotask`. We use microtasks as part
|
|
543
|
-
* of our discrete event implementation in React DOM. If you're not sure if your renderer should support this,
|
|
544
|
-
* you probably should. The option to not implement `scheduleMicrotask` exists so that platforms with more control
|
|
545
|
-
* over user events, like React Native, can choose to use a different mechanism.
|
|
546
|
-
*/
|
|
547
|
-
supportsMicrotasks: true,
|
|
548
|
-
/**
|
|
549
|
-
* #### `scheduleMicrotask(fn)`
|
|
550
|
-
*
|
|
551
|
-
* Optional. You can proxy this to `queueMicrotask` or its equivalent in your environment.
|
|
552
|
-
*/
|
|
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
|
-
},
|
|
561
|
-
/**
|
|
562
|
-
* #### `isPrimaryRenderer`
|
|
563
|
-
*
|
|
564
|
-
* This is a property (not a function) that should be set to `true` if your renderer is the main one on the
|
|
565
|
-
* page. For example, if you're writing a renderer for the Terminal, it makes sense to set it to `true`, but
|
|
566
|
-
* if your renderer is used *on top of* React DOM or some other existing renderer, set it to `false`.
|
|
567
|
-
*/
|
|
568
|
-
isPrimaryRenderer: true,
|
|
569
|
-
/**
|
|
570
|
-
* Whether the renderer shouldn't trigger missing `act()` warnings
|
|
571
|
-
*/
|
|
572
|
-
warnsIfNotActing: true,
|
|
573
|
-
getInstanceFromNode(node) {
|
|
574
|
-
mark("reconciler/getInstanceFromNode");
|
|
575
|
-
const instance = nodeToInstanceMap.get(node);
|
|
576
|
-
if (instance !== void 0) {
|
|
577
|
-
return instance.unstable_fiber;
|
|
578
|
-
}
|
|
579
|
-
return null;
|
|
580
|
-
},
|
|
581
|
-
beforeActiveInstanceBlur() {
|
|
582
|
-
mark("reconciler/beforeActiveInstanceBlur");
|
|
583
|
-
},
|
|
584
|
-
afterActiveInstanceBlur() {
|
|
585
|
-
mark("reconciler/afterActiveInstanceBlur");
|
|
586
|
-
},
|
|
587
|
-
prepareScopeUpdate(scopeInstance, instance) {
|
|
588
|
-
mark("reconciler/prepareScopeUpdate");
|
|
589
|
-
nodeToInstanceMap.set(scopeInstance, instance);
|
|
590
|
-
},
|
|
591
|
-
getInstanceFromScope(scopeInstance) {
|
|
592
|
-
var _a;
|
|
593
|
-
mark("reconciler/getInstanceFromScope");
|
|
594
|
-
return (_a = nodeToInstanceMap.get(scopeInstance)) != null ? _a : null;
|
|
595
|
-
},
|
|
596
|
-
detachDeletedInstance(_node) {
|
|
597
|
-
mark("reconciler/detachDeletedInstance");
|
|
598
|
-
},
|
|
599
|
-
/**
|
|
600
|
-
* #### `appendChild(parentInstance, child)`
|
|
601
|
-
*
|
|
602
|
-
* This method should mutate the `parentInstance` and add the child to its list of children. For example,
|
|
603
|
-
* in the DOM this would translate to a `parentInstance.appendChild(child)` call.
|
|
604
|
-
*
|
|
605
|
-
* Although this method currently runs in the commit phase, you still should not mutate any other nodes
|
|
606
|
-
* in it. If you need to do some additional work when a node is definitely connected to the visible tree, look at `commitMount`.
|
|
607
|
-
*/
|
|
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
|
-
},
|
|
617
|
-
/**
|
|
618
|
-
* #### `appendChildToContainer(container, child)`
|
|
619
|
-
*
|
|
620
|
-
* Same as `appendChild`, but for when a node is attached to the root container. This is useful if attaching
|
|
621
|
-
* to the root has a slightly different implementation, or if the root container nodes are of a different
|
|
622
|
-
* type than the rest of the tree.
|
|
623
|
-
*/
|
|
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
|
-
},
|
|
632
|
-
/**
|
|
633
|
-
* #### `insertBefore(parentInstance, child, beforeChild)`
|
|
634
|
-
*
|
|
635
|
-
* This method should mutate the `parentInstance` and place the `child` before `beforeChild` in the list of
|
|
636
|
-
* its children. For example, in the DOM this would translate to a `parentInstance.insertBefore(child, beforeChild)` call.
|
|
637
|
-
*
|
|
638
|
-
* Note that React uses this method both for insertions and for reordering nodes. Similar to DOM, it is expected
|
|
639
|
-
* that you can call `insertBefore` to reposition an existing child. Do not mutate any other parts of the tree from it.
|
|
640
|
-
*/
|
|
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
|
-
},
|
|
651
|
-
/**
|
|
652
|
-
* #### `insertInContainerBefore(container, child, beforeChild)
|
|
653
|
-
*
|
|
654
|
-
* Same as `insertBefore`, but for when a node is attached to the root container. This is useful if attaching
|
|
655
|
-
* to the root has a slightly different implementation, or if the root container nodes are of a different type
|
|
656
|
-
* than the rest of the tree.
|
|
657
|
-
*/
|
|
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
|
-
},
|
|
667
|
-
/**
|
|
668
|
-
* #### `removeChild(parentInstance, child)`
|
|
669
|
-
*
|
|
670
|
-
* This method should mutate the `parentInstance` to remove the `child` from the list of its children.
|
|
671
|
-
*
|
|
672
|
-
* React will only call it for the top-level node that is being removed. It is expected that garbage collection
|
|
673
|
-
* would take care of the whole subtree. You are not expected to traverse the child tree in it.
|
|
674
|
-
*/
|
|
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
|
-
},
|
|
684
|
-
/**
|
|
685
|
-
* #### `removeChildFromContainer(container, child)`
|
|
686
|
-
*
|
|
687
|
-
* Same as `removeChild`, but for when a node is detached from the root container. This is useful if attaching
|
|
688
|
-
* to the root has a slightly different implementation, or if the root container nodes are of a different type
|
|
689
|
-
* than the rest of the tree.
|
|
690
|
-
*/
|
|
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
|
-
},
|
|
699
|
-
/**
|
|
700
|
-
* #### `resetTextContent(instance)`
|
|
701
|
-
*
|
|
702
|
-
* If you returned `true` from `shouldSetTextContent` for the previous props, but returned `false` from
|
|
703
|
-
* `shouldSetTextContent` for the next props, React will call this method so that you can clear the text
|
|
704
|
-
* content you were managing manually. For example, in the DOM you could set `node.textContent = ''`.
|
|
705
|
-
*
|
|
706
|
-
* If you never return `true` from `shouldSetTextContent`, you can leave it empty.
|
|
707
|
-
*/
|
|
708
|
-
resetTextContent(instance) {
|
|
709
|
-
mark("reconciler/resetTextContent", { type: instance.type });
|
|
710
|
-
},
|
|
711
|
-
/**
|
|
712
|
-
* #### `commitTextUpdate(textInstance, prevText, nextText)`
|
|
713
|
-
*
|
|
714
|
-
* This method should mutate the `textInstance` and update its text content to `nextText`.
|
|
715
|
-
*
|
|
716
|
-
* Here, `textInstance` is a node created by `createTextInstance`.
|
|
717
|
-
*/
|
|
718
|
-
commitTextUpdate(textInstance, oldText, newText) {
|
|
719
|
-
mark("reconciler/commitTextUpdate", { oldText, newText });
|
|
720
|
-
textInstance.text = newText;
|
|
721
|
-
},
|
|
722
|
-
/**
|
|
723
|
-
* #### `commitMount(instance, type, props, internalHandle)`
|
|
724
|
-
*
|
|
725
|
-
* This method is only called if you returned `true` from `finalizeInitialChildren` for this instance.
|
|
726
|
-
*
|
|
727
|
-
* It lets you do some additional work after the node is actually attached to the tree on the screen for
|
|
728
|
-
* the first time. For example, the DOM renderer uses it to trigger focus on nodes with the `autoFocus` attribute.
|
|
729
|
-
*
|
|
730
|
-
* Note that `commitMount` does not mirror `removeChild` one to one because `removeChild` is only called for
|
|
731
|
-
* the top-level removed node. This is why ideally `commitMount` should not mutate any nodes other than the
|
|
732
|
-
* `instance` itself. For example, if it registers some events on some node above, it will be your responsibility
|
|
733
|
-
* to traverse the tree in `removeChild` and clean them up, which is not ideal.
|
|
734
|
-
*
|
|
735
|
-
* The `internalHandle` data structure is meant to be opaque. If you bend the rules and rely on its internal
|
|
736
|
-
* fields, be aware that it may change significantly between versions. You're taking on additional maintenance
|
|
737
|
-
* risk by reading from it, and giving up all guarantees if you write something to it.
|
|
738
|
-
*
|
|
739
|
-
* If you never return `true` from `finalizeInitialChildren`, you can leave it empty.
|
|
740
|
-
*/
|
|
741
|
-
commitMount(_instance, type, _props, _internalHandle) {
|
|
742
|
-
mark("reconciler/commitMount", { type });
|
|
743
|
-
},
|
|
744
|
-
/**
|
|
745
|
-
* #### `commitUpdate(instance, type, prevProps, nextProps, internalHandle)`
|
|
746
|
-
*
|
|
747
|
-
* This method should mutate the `instance` according to the set of changes in `updatePayload`. Here, `updatePayload`
|
|
748
|
-
* is the object that you've returned from `prepareUpdate` and has an arbitrary structure that makes sense for your
|
|
749
|
-
* renderer. For example, the DOM renderer returns an update payload like `[prop1, value1, prop2, value2, ...]` from
|
|
750
|
-
* `prepareUpdate`, and that structure gets passed into `commitUpdate`. Ideally, all the diffing and calculation
|
|
751
|
-
* should happen inside `prepareUpdate` so that `commitUpdate` can be fast and straightforward.
|
|
752
|
-
*
|
|
753
|
-
* The `internalHandle` data structure is meant to be opaque. If you bend the rules and rely on its internal fields,
|
|
754
|
-
* be aware that it may change significantly between versions. You're taking on additional maintenance risk by
|
|
755
|
-
* reading from it, and giving up all guarantees if you write something to it.
|
|
756
|
-
*/
|
|
757
|
-
// @ts-expect-error @types/react-reconciler types don't fully match react-reconciler's actual Flow types.
|
|
758
|
-
// Correctness is verified through tests.
|
|
759
|
-
commitUpdate(instance, type, _prevProps, nextProps, internalHandle) {
|
|
760
|
-
mark("reconciler/commitUpdate", { type });
|
|
761
|
-
instance.type = type;
|
|
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
|
-
}
|
|
772
|
-
instance.unstable_fiber = internalHandle;
|
|
773
|
-
},
|
|
774
|
-
/**
|
|
775
|
-
* #### `hideInstance(instance)`
|
|
776
|
-
*
|
|
777
|
-
* This method should make the `instance` invisible without removing it from the tree. For example, it can apply
|
|
778
|
-
* visual styling to hide it. It is used by Suspense to hide the tree while the fallback is visible.
|
|
779
|
-
*/
|
|
780
|
-
hideInstance(instance) {
|
|
781
|
-
mark("reconciler/hideInstance", { type: instance.type });
|
|
782
|
-
if (instance.isHidden) {
|
|
783
|
-
return;
|
|
784
|
-
}
|
|
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
|
-
}
|
|
792
|
-
},
|
|
793
|
-
/**
|
|
794
|
-
* #### `hideTextInstance(textInstance)`
|
|
795
|
-
*
|
|
796
|
-
* Same as `hideInstance`, but for nodes created by `createTextInstance`.
|
|
797
|
-
*/
|
|
798
|
-
hideTextInstance(textInstance) {
|
|
799
|
-
mark("reconciler/hideTextInstance", { text: textInstance.text });
|
|
800
|
-
textInstance.isHidden = true;
|
|
801
|
-
},
|
|
802
|
-
/**
|
|
803
|
-
* #### `unhideInstance(instance, props)`
|
|
804
|
-
*
|
|
805
|
-
* This method should make the `instance` visible, undoing what `hideInstance` did.
|
|
806
|
-
*/
|
|
807
|
-
unhideInstance(instance, _props) {
|
|
808
|
-
mark("reconciler/unhideInstance", { type: instance.type });
|
|
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
|
-
}
|
|
815
|
-
},
|
|
816
|
-
/**
|
|
817
|
-
* #### `unhideTextInstance(textInstance, text)`
|
|
818
|
-
*
|
|
819
|
-
* Same as `unhideInstance`, but for nodes created by `createTextInstance`.
|
|
820
|
-
*/
|
|
821
|
-
unhideTextInstance(textInstance, _text) {
|
|
822
|
-
mark("reconciler/unhideTextInstance", { text: textInstance.text });
|
|
823
|
-
textInstance.isHidden = false;
|
|
824
|
-
},
|
|
825
|
-
/**
|
|
826
|
-
* #### `clearContainer(container)`
|
|
827
|
-
*
|
|
828
|
-
* This method should mutate the `container` root node and remove all children from it.
|
|
829
|
-
*/
|
|
830
|
-
clearContainer(container) {
|
|
831
|
-
mark("reconciler/clearContainer");
|
|
832
|
-
container.children.forEach((child) => {
|
|
833
|
-
child.parent = null;
|
|
834
|
-
});
|
|
835
|
-
container.children.splice(0);
|
|
836
|
-
},
|
|
837
|
-
/**
|
|
838
|
-
* #### `maySuspendCommit(type, props)`
|
|
839
|
-
*
|
|
840
|
-
* This method is called during render to determine if the Host Component type and props require
|
|
841
|
-
* some kind of loading process to complete before committing an update.
|
|
842
|
-
*/
|
|
843
|
-
maySuspendCommit(type, _props) {
|
|
844
|
-
mark("reconciler/maySuspendCommit", { type });
|
|
845
|
-
return false;
|
|
846
|
-
},
|
|
847
|
-
/**
|
|
848
|
-
* #### `preloadInstance(type, props)`
|
|
849
|
-
*
|
|
850
|
-
* This method may be called during render if the Host Component type and props might suspend a commit.
|
|
851
|
-
* It can be used to initiate any work that might shorten the duration of a suspended commit.
|
|
852
|
-
*/
|
|
853
|
-
preloadInstance(type, _props) {
|
|
854
|
-
mark("reconciler/preloadInstance", { type });
|
|
855
|
-
return true;
|
|
856
|
-
},
|
|
857
|
-
/**
|
|
858
|
-
* #### `startSuspendingCommit()`
|
|
859
|
-
*
|
|
860
|
-
* This method is called just before the commit phase. Use it to set up any necessary state while any Host
|
|
861
|
-
* Components that might suspend this commit are evaluated to determine if the commit must be suspended.
|
|
862
|
-
*/
|
|
863
|
-
startSuspendingCommit() {
|
|
864
|
-
mark("reconciler/startSuspendingCommit");
|
|
865
|
-
},
|
|
866
|
-
/**
|
|
867
|
-
* #### `suspendInstance(type, props)`
|
|
868
|
-
*
|
|
869
|
-
* This method is called after `startSuspendingCommit` for each Host Component that indicated it might
|
|
870
|
-
* suspend a commit.
|
|
871
|
-
*/
|
|
872
|
-
suspendInstance(type, _props) {
|
|
873
|
-
mark("reconciler/suspendInstance", { type });
|
|
874
|
-
},
|
|
875
|
-
/**
|
|
876
|
-
* #### `waitForCommitToBeReady()`
|
|
877
|
-
*
|
|
878
|
-
* This method is called after all `suspendInstance` calls are complete.
|
|
879
|
-
*
|
|
880
|
-
* Return `null` if the commit can happen immediately.
|
|
881
|
-
* Return `(initiateCommit: Function) => Function` if the commit must be suspended. The argument to this
|
|
882
|
-
* callback will initiate the commit when called. The return value is a cancellation function that the
|
|
883
|
-
* Reconciler can use to abort the commit.
|
|
884
|
-
*/
|
|
885
|
-
waitForCommitToBeReady(type, _props) {
|
|
886
|
-
mark("reconciler/waitForCommitToBeReady", { type });
|
|
887
|
-
return null;
|
|
888
|
-
},
|
|
889
|
-
// -------------------
|
|
890
|
-
// Hydration Methods
|
|
891
|
-
// (optional)
|
|
892
|
-
// You can optionally implement hydration to "attach" to the existing tree during the initial render instead
|
|
893
|
-
// of creating it from scratch. For example, the DOM renderer uses this to attach to an HTML markup.
|
|
894
|
-
//
|
|
895
|
-
// To support hydration, you need to declare `supportsHydration: true` and then implement the methods in
|
|
896
|
-
// the "Hydration" section [listed in this file](https://github.com/facebook/react/blob/master/packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js).
|
|
897
|
-
// File an issue if you need help.
|
|
898
|
-
// -------------------
|
|
899
|
-
supportsHydration: false,
|
|
900
|
-
NotPendingTransition: null,
|
|
901
|
-
resetFormInstance(_form) {
|
|
902
|
-
mark("reconciler/resetFormInstance");
|
|
903
|
-
},
|
|
904
|
-
requestPostPaintCallback(_callback) {
|
|
905
|
-
mark("reconciler/requestPostPaintCallback");
|
|
906
|
-
}
|
|
907
|
-
};
|
|
908
|
-
var TestReconciler = ReactReconciler(hostConfig);
|
|
174
|
+
//#endregion
|
|
175
|
+
//#region src/reconciler.ts
|
|
176
|
+
const nodeToInstanceMap = /* @__PURE__ */ new WeakMap();
|
|
177
|
+
let currentUpdatePriority = NoEventPriority;
|
|
178
|
+
const TestReconciler = ReactReconciler({
|
|
179
|
+
supportsMutation: true,
|
|
180
|
+
supportsPersistence: false,
|
|
181
|
+
createInstance(type, props, rootContainer, _hostContext, internalHandle) {
|
|
182
|
+
mark("reconciler/createInstance", { type });
|
|
183
|
+
return {
|
|
184
|
+
tag: Tag.Instance,
|
|
185
|
+
type,
|
|
186
|
+
props,
|
|
187
|
+
propsBeforeHiding: null,
|
|
188
|
+
isHidden: false,
|
|
189
|
+
children: [],
|
|
190
|
+
parent: null,
|
|
191
|
+
rootContainer,
|
|
192
|
+
unstable_fiber: internalHandle
|
|
193
|
+
};
|
|
194
|
+
},
|
|
195
|
+
createTextInstance(text, rootContainer, hostContext, _internalHandle) {
|
|
196
|
+
mark("reconciler/createTextInstance", { text });
|
|
197
|
+
if (rootContainer.config.textComponentTypes && !hostContext.isInsideText) {
|
|
198
|
+
const componentTypes = rootContainer.config.publicTextComponentTypes ?? rootContainer.config.textComponentTypes;
|
|
199
|
+
throw new Error(`Invariant Violation: Text strings must be rendered within a ${formatComponentList(componentTypes)} component. Detected attempt to render "${text}" string within a <${hostContext.type}> component.`);
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
tag: Tag.Text,
|
|
203
|
+
text,
|
|
204
|
+
parent: null,
|
|
205
|
+
rootContainer,
|
|
206
|
+
isHidden: false
|
|
207
|
+
};
|
|
208
|
+
},
|
|
209
|
+
appendInitialChild(parentInstance, child) {
|
|
210
|
+
if (globalThis.TEST_RENDERER_ENABLE_PROFILING) mark("reconciler/appendInitialChild", {
|
|
211
|
+
parentType: parentInstance.type,
|
|
212
|
+
childType: formatInstanceType(child)
|
|
213
|
+
});
|
|
214
|
+
appendChild(parentInstance, child);
|
|
215
|
+
},
|
|
216
|
+
finalizeInitialChildren(instance, _type, _props, _rootContainer, _hostContext) {
|
|
217
|
+
mark("reconciler/finalizeInitialChildren", { type: instance.type });
|
|
218
|
+
return false;
|
|
219
|
+
},
|
|
220
|
+
shouldSetTextContent(type, _props) {
|
|
221
|
+
mark("reconciler/shouldSetTextContent", {
|
|
222
|
+
type,
|
|
223
|
+
result: false
|
|
224
|
+
});
|
|
225
|
+
return false;
|
|
226
|
+
},
|
|
227
|
+
setCurrentUpdatePriority(priority) {
|
|
228
|
+
mark("reconciler/setCurrentUpdatePriority", { priority });
|
|
229
|
+
currentUpdatePriority = priority;
|
|
230
|
+
},
|
|
231
|
+
getCurrentUpdatePriority() {
|
|
232
|
+
return currentUpdatePriority;
|
|
233
|
+
},
|
|
234
|
+
resolveUpdatePriority() {
|
|
235
|
+
const priority = currentUpdatePriority || DefaultEventPriority;
|
|
236
|
+
mark("reconciler/resolveUpdatePriority", { priority });
|
|
237
|
+
return priority;
|
|
238
|
+
},
|
|
239
|
+
trackSchedulerEvent() {
|
|
240
|
+
mark("reconciler/trackSchedulerEvent");
|
|
241
|
+
},
|
|
242
|
+
resolveEventType() {
|
|
243
|
+
mark("reconciler/resolveEventType");
|
|
244
|
+
return null;
|
|
245
|
+
},
|
|
246
|
+
resolveEventTimeStamp() {
|
|
247
|
+
const timestamp = -1.1;
|
|
248
|
+
mark("reconciler/resolveEventTimeStamp", { timestamp });
|
|
249
|
+
return timestamp;
|
|
250
|
+
},
|
|
251
|
+
shouldAttemptEagerTransition() {
|
|
252
|
+
mark("reconciler/shouldAttemptEagerTransition", { result: false });
|
|
253
|
+
return false;
|
|
254
|
+
},
|
|
255
|
+
getRootHostContext(rootContainer) {
|
|
256
|
+
mark("reconciler/getRootHostContext");
|
|
257
|
+
return {
|
|
258
|
+
type: "ROOT",
|
|
259
|
+
config: rootContainer.config,
|
|
260
|
+
isInsideText: false
|
|
261
|
+
};
|
|
262
|
+
},
|
|
263
|
+
getChildHostContext(parentHostContext, type) {
|
|
264
|
+
mark("reconciler/getChildHostContext", { type });
|
|
265
|
+
const isInsideText = Boolean(parentHostContext.config.textComponentTypes?.includes(type));
|
|
266
|
+
return {
|
|
267
|
+
...parentHostContext,
|
|
268
|
+
type,
|
|
269
|
+
isInsideText
|
|
270
|
+
};
|
|
271
|
+
},
|
|
272
|
+
getPublicInstance(instance) {
|
|
273
|
+
if (globalThis.TEST_RENDERER_ENABLE_PROFILING) mark("reconciler/getPublicInstance", { type: formatInstanceType(instance) });
|
|
274
|
+
switch (instance.tag) {
|
|
275
|
+
case Tag.Instance: {
|
|
276
|
+
const testInstance = TestInstance.fromInstance(instance);
|
|
277
|
+
nodeToInstanceMap.set(testInstance, instance);
|
|
278
|
+
return testInstance;
|
|
279
|
+
}
|
|
280
|
+
default: return null;
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
prepareForCommit(_containerInfo) {
|
|
284
|
+
mark("reconciler/prepareForCommit");
|
|
285
|
+
measureStart("react/commit");
|
|
286
|
+
return null;
|
|
287
|
+
},
|
|
288
|
+
resetAfterCommit(_containerInfo) {
|
|
289
|
+
measureEnd("react/commit");
|
|
290
|
+
mark("reconciler/resetAfterCommit");
|
|
291
|
+
},
|
|
292
|
+
preparePortalMount(_containerInfo) {
|
|
293
|
+
mark("reconciler/preparePortalMount");
|
|
294
|
+
},
|
|
295
|
+
scheduleTimeout(fn, delay) {
|
|
296
|
+
const id = setTimeout(() => {
|
|
297
|
+
mark("reconciler/scheduled timeout:start");
|
|
298
|
+
fn();
|
|
299
|
+
mark("reconciler/scheduled timeout:end");
|
|
300
|
+
}, delay);
|
|
301
|
+
mark("reconciler/scheduleTimeout", { id });
|
|
302
|
+
return id;
|
|
303
|
+
},
|
|
304
|
+
cancelTimeout(id) {
|
|
305
|
+
mark("reconciler/cancelTimeout", { id });
|
|
306
|
+
clearTimeout(id);
|
|
307
|
+
},
|
|
308
|
+
noTimeout: -1,
|
|
309
|
+
supportsMicrotasks: true,
|
|
310
|
+
scheduleMicrotask(fn) {
|
|
311
|
+
mark("reconciler/scheduleMicrotask");
|
|
312
|
+
queueMicrotask(() => {
|
|
313
|
+
mark("reconciler/scheduled microtask:start");
|
|
314
|
+
fn();
|
|
315
|
+
mark("reconciler/scheduled microtask:end");
|
|
316
|
+
});
|
|
317
|
+
},
|
|
318
|
+
isPrimaryRenderer: true,
|
|
319
|
+
warnsIfNotActing: true,
|
|
320
|
+
getInstanceFromNode(node) {
|
|
321
|
+
mark("reconciler/getInstanceFromNode");
|
|
322
|
+
const instance = nodeToInstanceMap.get(node);
|
|
323
|
+
if (instance !== void 0) return instance.unstable_fiber;
|
|
324
|
+
return null;
|
|
325
|
+
},
|
|
326
|
+
beforeActiveInstanceBlur() {
|
|
327
|
+
mark("reconciler/beforeActiveInstanceBlur");
|
|
328
|
+
},
|
|
329
|
+
afterActiveInstanceBlur() {
|
|
330
|
+
mark("reconciler/afterActiveInstanceBlur");
|
|
331
|
+
},
|
|
332
|
+
prepareScopeUpdate(scopeInstance, instance) {
|
|
333
|
+
mark("reconciler/prepareScopeUpdate");
|
|
334
|
+
nodeToInstanceMap.set(scopeInstance, instance);
|
|
335
|
+
},
|
|
336
|
+
getInstanceFromScope(scopeInstance) {
|
|
337
|
+
mark("reconciler/getInstanceFromScope");
|
|
338
|
+
return nodeToInstanceMap.get(scopeInstance) ?? null;
|
|
339
|
+
},
|
|
340
|
+
detachDeletedInstance(_node) {
|
|
341
|
+
mark("reconciler/detachDeletedInstance");
|
|
342
|
+
},
|
|
343
|
+
appendChild(parentInstance, child) {
|
|
344
|
+
if (globalThis.TEST_RENDERER_ENABLE_PROFILING) mark("reconciler/appendChild", {
|
|
345
|
+
parentType: parentInstance.type,
|
|
346
|
+
childType: formatInstanceType(child)
|
|
347
|
+
});
|
|
348
|
+
appendChild(parentInstance, child);
|
|
349
|
+
},
|
|
350
|
+
appendChildToContainer(container, child) {
|
|
351
|
+
if (globalThis.TEST_RENDERER_ENABLE_PROFILING) mark("reconciler/appendChildToContainer", { childType: formatInstanceType(child) });
|
|
352
|
+
appendChild(container, child);
|
|
353
|
+
},
|
|
354
|
+
insertBefore(parentInstance, child, beforeChild) {
|
|
355
|
+
if (globalThis.TEST_RENDERER_ENABLE_PROFILING) mark("reconciler/insertBefore", {
|
|
356
|
+
parentType: parentInstance.type,
|
|
357
|
+
childType: formatInstanceType(child),
|
|
358
|
+
beforeChildType: formatInstanceType(beforeChild)
|
|
359
|
+
});
|
|
360
|
+
insertBefore(parentInstance, child, beforeChild);
|
|
361
|
+
},
|
|
362
|
+
insertInContainerBefore(container, child, beforeChild) {
|
|
363
|
+
if (globalThis.TEST_RENDERER_ENABLE_PROFILING) mark("reconciler/insertInContainerBefore", {
|
|
364
|
+
childType: formatInstanceType(child),
|
|
365
|
+
beforeChildType: formatInstanceType(beforeChild)
|
|
366
|
+
});
|
|
367
|
+
insertBefore(container, child, beforeChild);
|
|
368
|
+
},
|
|
369
|
+
removeChild(parentInstance, child) {
|
|
370
|
+
if (globalThis.TEST_RENDERER_ENABLE_PROFILING) mark("reconciler/removeChild", {
|
|
371
|
+
parentType: parentInstance.type,
|
|
372
|
+
childType: formatInstanceType(child)
|
|
373
|
+
});
|
|
374
|
+
removeChild(parentInstance, child);
|
|
375
|
+
},
|
|
376
|
+
removeChildFromContainer(container, child) {
|
|
377
|
+
if (globalThis.TEST_RENDERER_ENABLE_PROFILING) mark("reconciler/removeChildFromContainer", { childType: formatInstanceType(child) });
|
|
378
|
+
removeChild(container, child);
|
|
379
|
+
},
|
|
380
|
+
resetTextContent(instance) {
|
|
381
|
+
mark("reconciler/resetTextContent", { type: instance.type });
|
|
382
|
+
},
|
|
383
|
+
commitTextUpdate(textInstance, oldText, newText) {
|
|
384
|
+
mark("reconciler/commitTextUpdate", {
|
|
385
|
+
oldText,
|
|
386
|
+
newText
|
|
387
|
+
});
|
|
388
|
+
textInstance.text = newText;
|
|
389
|
+
},
|
|
390
|
+
commitMount(_instance, type, _props, _internalHandle) {
|
|
391
|
+
mark("reconciler/commitMount", { type });
|
|
392
|
+
},
|
|
393
|
+
commitUpdate(instance, type, _prevProps, nextProps, internalHandle) {
|
|
394
|
+
mark("reconciler/commitUpdate", { type });
|
|
395
|
+
instance.type = type;
|
|
396
|
+
if (instance.isHidden && instance.rootContainer.config.transformHiddenInstanceProps != null) {
|
|
397
|
+
instance.propsBeforeHiding = nextProps;
|
|
398
|
+
instance.props = instance.rootContainer.config.transformHiddenInstanceProps({
|
|
399
|
+
props: nextProps,
|
|
400
|
+
type: instance.type
|
|
401
|
+
});
|
|
402
|
+
} else {
|
|
403
|
+
instance.props = nextProps;
|
|
404
|
+
instance.propsBeforeHiding = null;
|
|
405
|
+
}
|
|
406
|
+
instance.unstable_fiber = internalHandle;
|
|
407
|
+
},
|
|
408
|
+
hideInstance(instance) {
|
|
409
|
+
mark("reconciler/hideInstance", { type: instance.type });
|
|
410
|
+
if (instance.isHidden) return;
|
|
411
|
+
instance.isHidden = true;
|
|
412
|
+
instance.propsBeforeHiding = instance.props;
|
|
413
|
+
const transformHiddenInstanceProps = instance.rootContainer.config.transformHiddenInstanceProps;
|
|
414
|
+
if (transformHiddenInstanceProps) {
|
|
415
|
+
const { props, type } = instance;
|
|
416
|
+
instance.props = transformHiddenInstanceProps({
|
|
417
|
+
props,
|
|
418
|
+
type
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
},
|
|
422
|
+
hideTextInstance(textInstance) {
|
|
423
|
+
mark("reconciler/hideTextInstance", { text: textInstance.text });
|
|
424
|
+
textInstance.isHidden = true;
|
|
425
|
+
},
|
|
426
|
+
unhideInstance(instance, _props) {
|
|
427
|
+
mark("reconciler/unhideInstance", { type: instance.type });
|
|
428
|
+
instance.isHidden = false;
|
|
429
|
+
if (instance.rootContainer.config.transformHiddenInstanceProps && instance.propsBeforeHiding) {
|
|
430
|
+
instance.props = instance.propsBeforeHiding;
|
|
431
|
+
instance.propsBeforeHiding = null;
|
|
432
|
+
}
|
|
433
|
+
},
|
|
434
|
+
unhideTextInstance(textInstance, _text) {
|
|
435
|
+
mark("reconciler/unhideTextInstance", { text: textInstance.text });
|
|
436
|
+
textInstance.isHidden = false;
|
|
437
|
+
},
|
|
438
|
+
clearContainer(container) {
|
|
439
|
+
mark("reconciler/clearContainer");
|
|
440
|
+
container.children.forEach((child) => {
|
|
441
|
+
child.parent = null;
|
|
442
|
+
});
|
|
443
|
+
container.children.splice(0);
|
|
444
|
+
},
|
|
445
|
+
maySuspendCommit(type, _props) {
|
|
446
|
+
mark("reconciler/maySuspendCommit", { type });
|
|
447
|
+
return false;
|
|
448
|
+
},
|
|
449
|
+
preloadInstance(type, _props) {
|
|
450
|
+
mark("reconciler/preloadInstance", { type });
|
|
451
|
+
return true;
|
|
452
|
+
},
|
|
453
|
+
startSuspendingCommit() {
|
|
454
|
+
mark("reconciler/startSuspendingCommit");
|
|
455
|
+
},
|
|
456
|
+
suspendInstance(type, _props) {
|
|
457
|
+
mark("reconciler/suspendInstance", { type });
|
|
458
|
+
},
|
|
459
|
+
waitForCommitToBeReady(type, _props) {
|
|
460
|
+
mark("reconciler/waitForCommitToBeReady", { type });
|
|
461
|
+
return null;
|
|
462
|
+
},
|
|
463
|
+
supportsHydration: false,
|
|
464
|
+
NotPendingTransition: null,
|
|
465
|
+
resetFormInstance(_form) {
|
|
466
|
+
mark("reconciler/resetFormInstance");
|
|
467
|
+
},
|
|
468
|
+
requestPostPaintCallback(_callback) {
|
|
469
|
+
mark("reconciler/requestPostPaintCallback");
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
/**
|
|
473
|
+
* This method should mutate the `parentInstance` and add the child to its list of children. For example,
|
|
474
|
+
* in the DOM this would translate to a `parentInstance.appendChild(child)` call.
|
|
475
|
+
*
|
|
476
|
+
* Although this method currently runs in the commit phase, you still should not mutate any other nodes
|
|
477
|
+
* in it. If you need to do some additional work when a node is definitely connected to the visible tree,
|
|
478
|
+
* look at `commitMount`.
|
|
479
|
+
*/
|
|
909
480
|
function appendChild(parentInstance, child) {
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
child.parent = parentInstance;
|
|
915
|
-
parentInstance.children.push(child);
|
|
481
|
+
const index = parentInstance.children.indexOf(child);
|
|
482
|
+
if (index !== -1) parentInstance.children.splice(index, 1);
|
|
483
|
+
child.parent = parentInstance;
|
|
484
|
+
parentInstance.children.push(child);
|
|
916
485
|
}
|
|
486
|
+
/**
|
|
487
|
+
* This method should mutate the `parentInstance` and place the `child` before `beforeChild` in the list
|
|
488
|
+
* of its children. For example, in the DOM this would translate to a
|
|
489
|
+
* `parentInstance.insertBefore(child, beforeChild)` call.
|
|
490
|
+
*
|
|
491
|
+
* Note that React uses this method both for insertions and for reordering nodes. Similar to DOM, it is
|
|
492
|
+
* expected that you can call `insertBefore` to reposition an existing child. Do not mutate any other parts
|
|
493
|
+
* of the tree from it.
|
|
494
|
+
*/
|
|
917
495
|
function insertBefore(parentInstance, child, beforeChild) {
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
const beforeIndex = parentInstance.children.indexOf(beforeChild);
|
|
924
|
-
parentInstance.children.splice(beforeIndex, 0, child);
|
|
496
|
+
const index = parentInstance.children.indexOf(child);
|
|
497
|
+
if (index !== -1) parentInstance.children.splice(index, 1);
|
|
498
|
+
child.parent = parentInstance;
|
|
499
|
+
const beforeIndex = parentInstance.children.indexOf(beforeChild);
|
|
500
|
+
parentInstance.children.splice(beforeIndex, 0, child);
|
|
925
501
|
}
|
|
502
|
+
/**
|
|
503
|
+
* This method should mutate the `parentInstance` to remove the `child` from the list of its children.
|
|
504
|
+
*
|
|
505
|
+
* React will only call it for the top-level node that is being removed. It is expected that garbage
|
|
506
|
+
* collection would take care of the whole subtree. You are not expected to traverse the child tree in it.
|
|
507
|
+
*/
|
|
926
508
|
function removeChild(parentInstance, child) {
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
509
|
+
const index = parentInstance.children.indexOf(child);
|
|
510
|
+
parentInstance.children.splice(index, 1);
|
|
511
|
+
child.parent = null;
|
|
930
512
|
}
|
|
931
513
|
function formatInstanceType(instance) {
|
|
932
|
-
|
|
514
|
+
return instance.tag === Tag.Text ? `text: "${instance.text}"` : instance.type;
|
|
933
515
|
}
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
516
|
+
//#endregion
|
|
517
|
+
//#region src/renderer.ts
|
|
518
|
+
const defaultOnUncaughtError = (error, errorInfo) => {
|
|
519
|
+
console.error("Uncaught error:", error, errorInfo);
|
|
938
520
|
};
|
|
939
|
-
|
|
940
|
-
|
|
521
|
+
const defaultOnCaughtError = (error, errorInfo) => {
|
|
522
|
+
console.error("Caught error:", error, errorInfo);
|
|
941
523
|
};
|
|
942
|
-
|
|
943
|
-
|
|
524
|
+
const defaultOnRecoverableError = (error, errorInfo) => {
|
|
525
|
+
console.error("Recoverable error:", error, errorInfo);
|
|
944
526
|
};
|
|
527
|
+
/**
|
|
528
|
+
* Create a new test renderer root instance.
|
|
529
|
+
*
|
|
530
|
+
* @param options - Optional configuration for the renderer.
|
|
531
|
+
* @returns A Root instance with render, unmount, and container properties.
|
|
532
|
+
*/
|
|
945
533
|
function createRoot(options) {
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
};
|
|
989
|
-
const unmount = () => {
|
|
990
|
-
if (container == null) {
|
|
991
|
-
return;
|
|
992
|
-
}
|
|
993
|
-
measureStart("unmount");
|
|
994
|
-
try {
|
|
995
|
-
TestReconciler.updateContainer(null, containerFiber, null, null);
|
|
996
|
-
} finally {
|
|
997
|
-
measureEnd("unmount");
|
|
998
|
-
}
|
|
999
|
-
containerFiber = null;
|
|
1000
|
-
container = null;
|
|
1001
|
-
};
|
|
1002
|
-
return {
|
|
1003
|
-
render,
|
|
1004
|
-
unmount,
|
|
1005
|
-
get container() {
|
|
1006
|
-
if (container == null) {
|
|
1007
|
-
throw new Error("Cannot access .container on unmounted test renderer");
|
|
1008
|
-
}
|
|
1009
|
-
return TestInstance.fromInstance(container);
|
|
1010
|
-
}
|
|
1011
|
-
};
|
|
534
|
+
measureStart("createRoot");
|
|
535
|
+
let container = {
|
|
536
|
+
tag: Tag.Container,
|
|
537
|
+
parent: null,
|
|
538
|
+
children: [],
|
|
539
|
+
isHidden: false,
|
|
540
|
+
config: {
|
|
541
|
+
textComponentTypes: options?.textComponentTypes,
|
|
542
|
+
publicTextComponentTypes: options?.publicTextComponentTypes,
|
|
543
|
+
transformHiddenInstanceProps: options?.transformHiddenInstanceProps
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
let containerFiber = TestReconciler.createContainer(container, ConcurrentRoot, null, options?.isStrictMode ?? false, false, options?.identifierPrefix ?? "", options?.onUncaughtError ?? defaultOnUncaughtError, options?.onCaughtError ?? defaultOnCaughtError, options?.onRecoverableError ?? defaultOnRecoverableError, null);
|
|
547
|
+
measureEnd("createRoot");
|
|
548
|
+
const render = (element) => {
|
|
549
|
+
if (containerFiber == null) throw new Error("Cannot render after unmount");
|
|
550
|
+
measureStart("render");
|
|
551
|
+
try {
|
|
552
|
+
TestReconciler.updateContainer(element, containerFiber, null, null);
|
|
553
|
+
} finally {
|
|
554
|
+
measureEnd("render", { elementType: String(element.type) });
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
const unmount = () => {
|
|
558
|
+
if (container == null) return;
|
|
559
|
+
measureStart("unmount");
|
|
560
|
+
try {
|
|
561
|
+
TestReconciler.updateContainer(null, containerFiber, null, null);
|
|
562
|
+
} finally {
|
|
563
|
+
measureEnd("unmount");
|
|
564
|
+
}
|
|
565
|
+
containerFiber = null;
|
|
566
|
+
container = null;
|
|
567
|
+
};
|
|
568
|
+
return {
|
|
569
|
+
render,
|
|
570
|
+
unmount,
|
|
571
|
+
get container() {
|
|
572
|
+
if (container == null) throw new Error("Cannot access .container on unmounted test renderer");
|
|
573
|
+
return TestInstance.fromInstance(container);
|
|
574
|
+
}
|
|
575
|
+
};
|
|
1012
576
|
}
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
577
|
+
//#endregion
|
|
578
|
+
export { createRoot };
|
|
579
|
+
|
|
1016
580
|
//# sourceMappingURL=index.js.map
|