test-renderer 0.14.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 +71 -31
- package/dist/index.cjs +574 -841
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +86 -79
- package/dist/index.js +558 -815
- package/dist/index.js.map +1 -1
- package/package.json +39 -33
- package/dist/index.d.cts +0 -119
package/dist/index.js
CHANGED
|
@@ -1,837 +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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const includeSelf = (_a = options == null ? void 0 : options.includeSelf) != null ? _a : false;
|
|
48
|
-
const matchDeepestOnly = (_b = options == null ? void 0 : options.matchDeepestOnly) != null ? _b : false;
|
|
49
|
-
const results = [];
|
|
50
|
-
const matchingDescendants = [];
|
|
51
|
-
element.children.forEach((child) => {
|
|
52
|
-
if (typeof child === "string") {
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
matchingDescendants.push(...queryAll(child, predicate, __spreadProps(__spreadValues({}, options), { includeSelf: true })));
|
|
56
|
-
});
|
|
57
|
-
if (includeSelf && // When matchDeepestOnly = true: add current element only if no descendants match
|
|
58
|
-
(matchingDescendants.length === 0 || !matchDeepestOnly) && predicate(element)) {
|
|
59
|
-
results.push(element);
|
|
60
|
-
}
|
|
61
|
-
results.push(...matchingDescendants);
|
|
62
|
-
return results;
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/performance.ts
|
|
11
|
+
function mark(name, details) {
|
|
12
|
+
if (!globalThis.TEST_RENDERER_ENABLE_PROFILING) return;
|
|
13
|
+
performance.mark(`test-renderer/${name}`, { detail: details });
|
|
63
14
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return {
|
|
68
|
-
type: CONTAINER_TYPE,
|
|
69
|
-
props: {},
|
|
70
|
-
children: renderChildrenToJson(instance.children),
|
|
71
|
-
$$typeof: /* @__PURE__ */ Symbol.for("react.test.json")
|
|
72
|
-
};
|
|
15
|
+
function measureStart(name) {
|
|
16
|
+
if (!globalThis.TEST_RENDERER_ENABLE_PROFILING) return;
|
|
17
|
+
performance.mark(`test-renderer/${name}:start`);
|
|
73
18
|
}
|
|
74
|
-
function
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
children: renderChildrenToJson(instance.children),
|
|
83
|
-
$$typeof: /* @__PURE__ */ Symbol.for("react.test.json")
|
|
84
|
-
};
|
|
19
|
+
function measureEnd(name, details) {
|
|
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
|
+
});
|
|
85
27
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
+
*/
|
|
38
|
+
function queryAll(instance, predicate, options) {
|
|
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;
|
|
91
53
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const renderedChild = renderTextInstanceToJson(child);
|
|
102
|
-
if (renderedChild != null) {
|
|
103
|
-
result.push(renderedChild);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return result;
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/to-json.ts
|
|
56
|
+
function containerToJson(container) {
|
|
57
|
+
return {
|
|
58
|
+
type: "",
|
|
59
|
+
props: {},
|
|
60
|
+
children: childrenToJson(container.children),
|
|
61
|
+
$$typeof: Symbol.for("react.test.json")
|
|
62
|
+
};
|
|
108
63
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
64
|
+
function instanceToJson(instance) {
|
|
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
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function textInstanceToJson(instance) {
|
|
76
|
+
const shouldExcludeHidden = instance.rootContainer.config.transformHiddenInstanceProps == null;
|
|
77
|
+
if (instance.isHidden && shouldExcludeHidden) return null;
|
|
78
|
+
return instance.text;
|
|
79
|
+
}
|
|
80
|
+
function childrenToJson(children) {
|
|
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;
|
|
90
|
+
}
|
|
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
|
+
}
|
|
175
157
|
};
|
|
176
|
-
function
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
return HostElement.fromInstance(instance);
|
|
182
|
-
}
|
|
158
|
+
function getTestNodeForInstance(instance) {
|
|
159
|
+
switch (instance.tag) {
|
|
160
|
+
case Tag.Text: return instance.text;
|
|
161
|
+
case Tag.Instance: return TestInstance.fromInstance(instance);
|
|
162
|
+
}
|
|
183
163
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
import ReactReconciler from "react-reconciler";
|
|
187
|
-
import { DefaultEventPriority, NoEventPriority } from "react-reconciler/constants";
|
|
188
|
-
|
|
189
|
-
// src/utils.ts
|
|
164
|
+
//#endregion
|
|
165
|
+
//#region src/utils.ts
|
|
190
166
|
function formatComponentList(names) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
if (names.length === 2) {
|
|
198
|
-
return `<${names[0]}> or <${names[1]}>`;
|
|
199
|
-
}
|
|
200
|
-
const allButLast = names.slice(0, -1);
|
|
201
|
-
const last = names[names.length - 1];
|
|
202
|
-
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}>`;
|
|
203
173
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
prepareScopeUpdate(scopeInstance, instance) {
|
|
512
|
-
nodeToInstanceMap.set(scopeInstance, instance);
|
|
513
|
-
},
|
|
514
|
-
getInstanceFromScope(scopeInstance) {
|
|
515
|
-
var _a;
|
|
516
|
-
return (_a = nodeToInstanceMap.get(scopeInstance)) != null ? _a : null;
|
|
517
|
-
},
|
|
518
|
-
detachDeletedInstance(_node) {
|
|
519
|
-
},
|
|
520
|
-
/**
|
|
521
|
-
* #### `appendChild(parentInstance, child)`
|
|
522
|
-
*
|
|
523
|
-
* This method should mutate the `parentInstance` and add the child to its list of children. For example,
|
|
524
|
-
* in the DOM this would translate to a `parentInstance.appendChild(child)` call.
|
|
525
|
-
*
|
|
526
|
-
* Although this method currently runs in the commit phase, you still should not mutate any other nodes
|
|
527
|
-
* in it. If you need to do some additional work when a node is definitely connected to the visible tree, look at `commitMount`.
|
|
528
|
-
*/
|
|
529
|
-
appendChild,
|
|
530
|
-
/**
|
|
531
|
-
* #### `appendChildToContainer(container, child)`
|
|
532
|
-
*
|
|
533
|
-
* Same as `appendChild`, but for when a node is attached to the root container. This is useful if attaching
|
|
534
|
-
* to the root has a slightly different implementation, or if the root container nodes are of a different
|
|
535
|
-
* type than the rest of the tree.
|
|
536
|
-
*/
|
|
537
|
-
appendChildToContainer: appendChild,
|
|
538
|
-
/**
|
|
539
|
-
* #### `insertBefore(parentInstance, child, beforeChild)`
|
|
540
|
-
*
|
|
541
|
-
* This method should mutate the `parentInstance` and place the `child` before `beforeChild` in the list of
|
|
542
|
-
* its children. For example, in the DOM this would translate to a `parentInstance.insertBefore(child, beforeChild)` call.
|
|
543
|
-
*
|
|
544
|
-
* Note that React uses this method both for insertions and for reordering nodes. Similar to DOM, it is expected
|
|
545
|
-
* that you can call `insertBefore` to reposition an existing child. Do not mutate any other parts of the tree from it.
|
|
546
|
-
*/
|
|
547
|
-
insertBefore,
|
|
548
|
-
/**
|
|
549
|
-
* #### `insertInContainerBefore(container, child, beforeChild)
|
|
550
|
-
*
|
|
551
|
-
* Same as `insertBefore`, but for when a node is attached to the root container. This is useful if attaching
|
|
552
|
-
* to the root has a slightly different implementation, or if the root container nodes are of a different type
|
|
553
|
-
* than the rest of the tree.
|
|
554
|
-
*/
|
|
555
|
-
insertInContainerBefore: insertBefore,
|
|
556
|
-
/**
|
|
557
|
-
* #### `removeChild(parentInstance, child)`
|
|
558
|
-
*
|
|
559
|
-
* This method should mutate the `parentInstance` to remove the `child` from the list of its children.
|
|
560
|
-
*
|
|
561
|
-
* React will only call it for the top-level node that is being removed. It is expected that garbage collection
|
|
562
|
-
* would take care of the whole subtree. You are not expected to traverse the child tree in it.
|
|
563
|
-
*/
|
|
564
|
-
removeChild,
|
|
565
|
-
/**
|
|
566
|
-
* #### `removeChildFromContainer(container, child)`
|
|
567
|
-
*
|
|
568
|
-
* Same as `removeChild`, but for when a node is detached from the root container. This is useful if attaching
|
|
569
|
-
* to the root has a slightly different implementation, or if the root container nodes are of a different type
|
|
570
|
-
* than the rest of the tree.
|
|
571
|
-
*/
|
|
572
|
-
removeChildFromContainer: removeChild,
|
|
573
|
-
/**
|
|
574
|
-
* #### `resetTextContent(instance)`
|
|
575
|
-
*
|
|
576
|
-
* If you returned `true` from `shouldSetTextContent` for the previous props, but returned `false` from
|
|
577
|
-
* `shouldSetTextContent` for the next props, React will call this method so that you can clear the text
|
|
578
|
-
* content you were managing manually. For example, in the DOM you could set `node.textContent = ''`.
|
|
579
|
-
*
|
|
580
|
-
* If you never return `true` from `shouldSetTextContent`, you can leave it empty.
|
|
581
|
-
*/
|
|
582
|
-
resetTextContent(_instance) {
|
|
583
|
-
},
|
|
584
|
-
/**
|
|
585
|
-
* #### `commitTextUpdate(textInstance, prevText, nextText)`
|
|
586
|
-
*
|
|
587
|
-
* This method should mutate the `textInstance` and update its text content to `nextText`.
|
|
588
|
-
*
|
|
589
|
-
* Here, `textInstance` is a node created by `createTextInstance`.
|
|
590
|
-
*/
|
|
591
|
-
commitTextUpdate(textInstance, _oldText, newText) {
|
|
592
|
-
textInstance.text = newText;
|
|
593
|
-
},
|
|
594
|
-
/**
|
|
595
|
-
* #### `commitMount(instance, type, props, internalHandle)`
|
|
596
|
-
*
|
|
597
|
-
* This method is only called if you returned `true` from `finalizeInitialChildren` for this instance.
|
|
598
|
-
*
|
|
599
|
-
* It lets you do some additional work after the node is actually attached to the tree on the screen for
|
|
600
|
-
* the first time. For example, the DOM renderer uses it to trigger focus on nodes with the `autoFocus` attribute.
|
|
601
|
-
*
|
|
602
|
-
* Note that `commitMount` does not mirror `removeChild` one to one because `removeChild` is only called for
|
|
603
|
-
* the top-level removed node. This is why ideally `commitMount` should not mutate any nodes other than the
|
|
604
|
-
* `instance` itself. For example, if it registers some events on some node above, it will be your responsibility
|
|
605
|
-
* to traverse the tree in `removeChild` and clean them up, which is not ideal.
|
|
606
|
-
*
|
|
607
|
-
* The `internalHandle` data structure is meant to be opaque. If you bend the rules and rely on its internal
|
|
608
|
-
* fields, be aware that it may change significantly between versions. You're taking on additional maintenance
|
|
609
|
-
* risk by reading from it, and giving up all guarantees if you write something to it.
|
|
610
|
-
*
|
|
611
|
-
* If you never return `true` from `finalizeInitialChildren`, you can leave it empty.
|
|
612
|
-
*/
|
|
613
|
-
commitMount(_instance, _type, _props, _internalHandle) {
|
|
614
|
-
},
|
|
615
|
-
/**
|
|
616
|
-
* #### `commitUpdate(instance, type, prevProps, nextProps, internalHandle)`
|
|
617
|
-
*
|
|
618
|
-
* This method should mutate the `instance` according to the set of changes in `updatePayload`. Here, `updatePayload`
|
|
619
|
-
* is the object that you've returned from `prepareUpdate` and has an arbitrary structure that makes sense for your
|
|
620
|
-
* renderer. For example, the DOM renderer returns an update payload like `[prop1, value1, prop2, value2, ...]` from
|
|
621
|
-
* `prepareUpdate`, and that structure gets passed into `commitUpdate`. Ideally, all the diffing and calculation
|
|
622
|
-
* should happen inside `prepareUpdate` so that `commitUpdate` can be fast and straightforward.
|
|
623
|
-
*
|
|
624
|
-
* The `internalHandle` data structure is meant to be opaque. If you bend the rules and rely on its internal fields,
|
|
625
|
-
* be aware that it may change significantly between versions. You're taking on additional maintenance risk by
|
|
626
|
-
* reading from it, and giving up all guarantees if you write something to it.
|
|
627
|
-
*/
|
|
628
|
-
// @ts-expect-error @types/react-reconciler types don't fully match react-reconciler's actual Flow types.
|
|
629
|
-
// Correctness is verified through tests.
|
|
630
|
-
commitUpdate(instance, type, _prevProps, nextProps, internalHandle) {
|
|
631
|
-
instance.type = type;
|
|
632
|
-
instance.props = nextProps;
|
|
633
|
-
instance.unstable_fiber = internalHandle;
|
|
634
|
-
},
|
|
635
|
-
/**
|
|
636
|
-
* #### `hideInstance(instance)`
|
|
637
|
-
*
|
|
638
|
-
* This method should make the `instance` invisible without removing it from the tree. For example, it can apply
|
|
639
|
-
* visual styling to hide it. It is used by Suspense to hide the tree while the fallback is visible.
|
|
640
|
-
*/
|
|
641
|
-
hideInstance(instance) {
|
|
642
|
-
instance.isHidden = true;
|
|
643
|
-
},
|
|
644
|
-
/**
|
|
645
|
-
* #### `hideTextInstance(textInstance)`
|
|
646
|
-
*
|
|
647
|
-
* Same as `hideInstance`, but for nodes created by `createTextInstance`.
|
|
648
|
-
*/
|
|
649
|
-
hideTextInstance(textInstance) {
|
|
650
|
-
textInstance.isHidden = true;
|
|
651
|
-
},
|
|
652
|
-
/**
|
|
653
|
-
* #### `unhideInstance(instance, props)`
|
|
654
|
-
*
|
|
655
|
-
* This method should make the `instance` visible, undoing what `hideInstance` did.
|
|
656
|
-
*/
|
|
657
|
-
unhideInstance(instance, _props) {
|
|
658
|
-
instance.isHidden = false;
|
|
659
|
-
},
|
|
660
|
-
/**
|
|
661
|
-
* #### `unhideTextInstance(textInstance, text)`
|
|
662
|
-
*
|
|
663
|
-
* Same as `unhideInstance`, but for nodes created by `createTextInstance`.
|
|
664
|
-
*/
|
|
665
|
-
unhideTextInstance(textInstance, _text) {
|
|
666
|
-
textInstance.isHidden = false;
|
|
667
|
-
},
|
|
668
|
-
/**
|
|
669
|
-
* #### `clearContainer(container)`
|
|
670
|
-
*
|
|
671
|
-
* This method should mutate the `container` root node and remove all children from it.
|
|
672
|
-
*/
|
|
673
|
-
clearContainer(container) {
|
|
674
|
-
container.children.forEach((child) => {
|
|
675
|
-
child.parent = null;
|
|
676
|
-
});
|
|
677
|
-
container.children.splice(0);
|
|
678
|
-
},
|
|
679
|
-
/**
|
|
680
|
-
* #### `maySuspendCommit(type, props)`
|
|
681
|
-
*
|
|
682
|
-
* This method is called during render to determine if the Host Component type and props require
|
|
683
|
-
* some kind of loading process to complete before committing an update.
|
|
684
|
-
*/
|
|
685
|
-
maySuspendCommit(_type, _props) {
|
|
686
|
-
return false;
|
|
687
|
-
},
|
|
688
|
-
/**
|
|
689
|
-
* #### `preloadInstance(type, props)`
|
|
690
|
-
*
|
|
691
|
-
* This method may be called during render if the Host Component type and props might suspend a commit.
|
|
692
|
-
* It can be used to initiate any work that might shorten the duration of a suspended commit.
|
|
693
|
-
*/
|
|
694
|
-
preloadInstance(_type, _props) {
|
|
695
|
-
return true;
|
|
696
|
-
},
|
|
697
|
-
/**
|
|
698
|
-
* #### `startSuspendingCommit()`
|
|
699
|
-
*
|
|
700
|
-
* This method is called just before the commit phase. Use it to set up any necessary state while any Host
|
|
701
|
-
* Components that might suspend this commit are evaluated to determine if the commit must be suspended.
|
|
702
|
-
*/
|
|
703
|
-
startSuspendingCommit() {
|
|
704
|
-
},
|
|
705
|
-
/**
|
|
706
|
-
* #### `suspendInstance(type, props)`
|
|
707
|
-
*
|
|
708
|
-
* This method is called after `startSuspendingCommit` for each Host Component that indicated it might
|
|
709
|
-
* suspend a commit.
|
|
710
|
-
*/
|
|
711
|
-
suspendInstance() {
|
|
712
|
-
},
|
|
713
|
-
/**
|
|
714
|
-
* #### `waitForCommitToBeReady()`
|
|
715
|
-
*
|
|
716
|
-
* This method is called after all `suspendInstance` calls are complete.
|
|
717
|
-
*
|
|
718
|
-
* Return `null` if the commit can happen immediately.
|
|
719
|
-
* Return `(initiateCommit: Function) => Function` if the commit must be suspended. The argument to this
|
|
720
|
-
* callback will initiate the commit when called. The return value is a cancellation function that the
|
|
721
|
-
* Reconciler can use to abort the commit.
|
|
722
|
-
*/
|
|
723
|
-
waitForCommitToBeReady() {
|
|
724
|
-
return null;
|
|
725
|
-
},
|
|
726
|
-
// -------------------
|
|
727
|
-
// Hydration Methods
|
|
728
|
-
// (optional)
|
|
729
|
-
// You can optionally implement hydration to "attach" to the existing tree during the initial render instead
|
|
730
|
-
// of creating it from scratch. For example, the DOM renderer uses this to attach to an HTML markup.
|
|
731
|
-
//
|
|
732
|
-
// To support hydration, you need to declare `supportsHydration: true` and then implement the methods in
|
|
733
|
-
// the "Hydration" section [listed in this file](https://github.com/facebook/react/blob/master/packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js).
|
|
734
|
-
// File an issue if you need help.
|
|
735
|
-
// -------------------
|
|
736
|
-
supportsHydration: false,
|
|
737
|
-
NotPendingTransition: null,
|
|
738
|
-
resetFormInstance(_form) {
|
|
739
|
-
},
|
|
740
|
-
requestPostPaintCallback(_callback) {
|
|
741
|
-
}
|
|
742
|
-
};
|
|
743
|
-
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
|
+
*/
|
|
744
480
|
function appendChild(parentInstance, child) {
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
child.parent = parentInstance;
|
|
750
|
-
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);
|
|
751
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
|
+
*/
|
|
752
495
|
function insertBefore(parentInstance, child, beforeChild) {
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
const beforeIndex = parentInstance.children.indexOf(beforeChild);
|
|
759
|
-
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);
|
|
760
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
|
+
*/
|
|
761
508
|
function removeChild(parentInstance, child) {
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
509
|
+
const index = parentInstance.children.indexOf(child);
|
|
510
|
+
parentInstance.children.splice(index, 1);
|
|
511
|
+
child.parent = null;
|
|
765
512
|
}
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
513
|
+
function formatInstanceType(instance) {
|
|
514
|
+
return instance.tag === Tag.Text ? `text: "${instance.text}"` : instance.type;
|
|
515
|
+
}
|
|
516
|
+
//#endregion
|
|
517
|
+
//#region src/renderer.ts
|
|
518
|
+
const defaultOnUncaughtError = (error, errorInfo) => {
|
|
519
|
+
console.error("Uncaught error:", error, errorInfo);
|
|
771
520
|
};
|
|
772
|
-
|
|
773
|
-
|
|
521
|
+
const defaultOnCaughtError = (error, errorInfo) => {
|
|
522
|
+
console.error("Caught error:", error, errorInfo);
|
|
774
523
|
};
|
|
775
|
-
|
|
776
|
-
|
|
524
|
+
const defaultOnRecoverableError = (error, errorInfo) => {
|
|
525
|
+
console.error("Recoverable error:", error, errorInfo);
|
|
777
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
|
+
*/
|
|
778
533
|
function createRoot(options) {
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
container = null;
|
|
822
|
-
};
|
|
823
|
-
return {
|
|
824
|
-
render,
|
|
825
|
-
unmount,
|
|
826
|
-
get container() {
|
|
827
|
-
if (container == null) {
|
|
828
|
-
throw new Error("Cannot access .container on unmounted test renderer");
|
|
829
|
-
}
|
|
830
|
-
return HostElement.fromInstance(container);
|
|
831
|
-
}
|
|
832
|
-
};
|
|
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
|
+
};
|
|
833
576
|
}
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
577
|
+
//#endregion
|
|
578
|
+
export { createRoot };
|
|
579
|
+
|
|
837
580
|
//# sourceMappingURL=index.js.map
|