Skip to content

Commit b6bb44d

Browse files
authored
Resolve host configs at build time (#12792)
* Extract base Jest config This makes it easier to change the source config without affecting the build test config. * Statically import the host config This changes react-reconciler to import HostConfig instead of getting it through a function argument. Rather than start with packages like ReactDOM that want to inline it, I started with React Noop and ensured that *custom* renderers using react-reconciler package still work. To do this, I'm making HostConfig module in the reconciler look at a global variable by default (which, in case of the react-reconciler npm package, ends up being the host config argument in the top-level scope). This is still very broken. * Add scaffolding for importing an inlined renderer * Fix the build * ES exports for renderer methods * ES modules for host configs * Remove closures from the reconciler * Check each renderer's config with Flow * Fix uncovered Flow issue We know nextHydratableInstance doesn't get mutated inside this function, but Flow doesn't so it thinks it may be null. Help Flow. * Prettier * Get rid of enable*Reconciler flags They are not as useful anymore because for almost all cases (except third party renderers) we *know* whether it supports mutation or persistence. This refactoring means react-reconciler and react-reconciler/persistent third-party packages now ship the same thing. Not ideal, but this seems worth how simpler the code becomes. We can later look into addressing it by having a single toggle instead. * Prettier again * Fix Flow config creation issue * Fix imprecise Flow typing * Revert accidental changes
1 parent 1e8c38e commit b6bb44d

File tree

3 files changed

+163
-155
lines changed

3 files changed

+163
-155
lines changed

src/ReactTestHostConfig.js

Lines changed: 160 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,36 @@ import emptyObject from 'fbjs/lib/emptyObject';
1111

1212
import * as TestRendererScheduling from './ReactTestRendererScheduling';
1313

14+
export type Type = string;
15+
export type Props = Object;
16+
export type Container = {|
17+
children: Array<Instance | TextInstance>,
18+
createNodeMock: Function,
19+
tag: 'CONTAINER',
20+
|};
1421
export type Instance = {|
1522
type: string,
1623
props: Object,
1724
children: Array<Instance | TextInstance>,
1825
rootContainerInstance: Container,
1926
tag: 'INSTANCE',
2027
|};
21-
2228
export type TextInstance = {|
2329
text: string,
2430
tag: 'TEXT',
2531
|};
26-
27-
type Container = {|
28-
children: Array<Instance | TextInstance>,
29-
createNodeMock: Function,
30-
tag: 'CONTAINER',
31-
|};
32-
33-
type Props = Object;
32+
export type HydratableInstance = Instance | TextInstance;
33+
export type PublicInstance = Instance | TextInstance;
34+
export type HostContext = Object;
35+
export type UpdatePayload = Object;
36+
export type ChildSet = void; // Unused
3437

3538
const UPDATE_SIGNAL = {};
3639

37-
function getPublicInstance(inst: Instance | TextInstance): * {
40+
export * from 'shared/HostConfigWithNoPersistence';
41+
export * from 'shared/HostConfigWithNoHydration';
42+
43+
export function getPublicInstance(inst: Instance | TextInstance): * {
3844
switch (inst.tag) {
3945
case 'INSTANCE':
4046
const createNodeMock = inst.rootContainerInstance.createNodeMock;
@@ -47,7 +53,7 @@ function getPublicInstance(inst: Instance | TextInstance): * {
4753
}
4854
}
4955

50-
function appendChild(
56+
export function appendChild(
5157
parentInstance: Instance | Container,
5258
child: Instance | TextInstance,
5359
): void {
@@ -58,7 +64,7 @@ function appendChild(
5864
parentInstance.children.push(child);
5965
}
6066

61-
function insertBefore(
67+
export function insertBefore(
6268
parentInstance: Instance | Container,
6369
child: Instance | TextInstance,
6470
beforeChild: Instance | TextInstance,
@@ -71,148 +77,152 @@ function insertBefore(
7177
parentInstance.children.splice(beforeIndex, 0, child);
7278
}
7379

74-
function removeChild(
80+
export function removeChild(
7581
parentInstance: Instance | Container,
7682
child: Instance | TextInstance,
7783
): void {
7884
const index = parentInstance.children.indexOf(child);
7985
parentInstance.children.splice(index, 1);
8086
}
8187

82-
const ReactTestHostConfig = {
83-
getRootHostContext() {
84-
return emptyObject;
85-
},
86-
87-
getChildHostContext() {
88-
return emptyObject;
89-
},
90-
91-
prepareForCommit(): void {
92-
// noop
93-
},
94-
95-
resetAfterCommit(): void {
96-
// noop
97-
},
98-
99-
createInstance(
100-
type: string,
101-
props: Props,
102-
rootContainerInstance: Container,
103-
hostContext: Object,
104-
internalInstanceHandle: Object,
105-
): Instance {
106-
return {
107-
type,
108-
props,
109-
children: [],
110-
rootContainerInstance,
111-
tag: 'INSTANCE',
112-
};
113-
},
114-
115-
appendInitialChild(
116-
parentInstance: Instance,
117-
child: Instance | TextInstance,
118-
): void {
119-
const index = parentInstance.children.indexOf(child);
120-
if (index !== -1) {
121-
parentInstance.children.splice(index, 1);
122-
}
123-
parentInstance.children.push(child);
124-
},
125-
126-
finalizeInitialChildren(
127-
testElement: Instance,
128-
type: string,
129-
props: Props,
130-
rootContainerInstance: Container,
131-
): boolean {
132-
return false;
133-
},
134-
135-
prepareUpdate(
136-
testElement: Instance,
137-
type: string,
138-
oldProps: Props,
139-
newProps: Props,
140-
rootContainerInstance: Container,
141-
hostContext: Object,
142-
): null | {} {
143-
return UPDATE_SIGNAL;
144-
},
145-
146-
shouldSetTextContent(type: string, props: Props): boolean {
147-
return false;
148-
},
149-
150-
shouldDeprioritizeSubtree(type: string, props: Props): boolean {
151-
return false;
152-
},
153-
154-
createTextInstance(
155-
text: string,
156-
rootContainerInstance: Container,
157-
hostContext: Object,
158-
internalInstanceHandle: Object,
159-
): TextInstance {
160-
return {
161-
text,
162-
tag: 'TEXT',
163-
};
164-
},
165-
166-
getPublicInstance,
167-
168-
scheduleDeferredCallback: TestRendererScheduling.scheduleDeferredCallback,
169-
cancelDeferredCallback: TestRendererScheduling.cancelDeferredCallback,
170-
// This approach enables `now` to be mocked by tests,
171-
// Even after the reconciler has initialized and read host config values.
172-
now: () => TestRendererScheduling.nowImplementation(),
173-
174-
isPrimaryRenderer: true,
175-
176-
mutation: {
177-
commitUpdate(
178-
instance: Instance,
179-
updatePayload: {},
180-
type: string,
181-
oldProps: Props,
182-
newProps: Props,
183-
internalInstanceHandle: Object,
184-
): void {
185-
instance.type = type;
186-
instance.props = newProps;
187-
},
188-
189-
commitMount(
190-
instance: Instance,
191-
type: string,
192-
newProps: Props,
193-
internalInstanceHandle: Object,
194-
): void {
195-
// noop
196-
},
197-
198-
commitTextUpdate(
199-
textInstance: TextInstance,
200-
oldText: string,
201-
newText: string,
202-
): void {
203-
textInstance.text = newText;
204-
},
205-
resetTextContent(testElement: Instance): void {
206-
// noop
207-
},
208-
209-
appendChild: appendChild,
210-
appendChildToContainer: appendChild,
211-
insertBefore: insertBefore,
212-
insertInContainerBefore: insertBefore,
213-
removeChild: removeChild,
214-
removeChildFromContainer: removeChild,
215-
},
216-
};
217-
218-
export default ReactTestHostConfig;
88+
export function getRootHostContext(
89+
rootContainerInstance: Container,
90+
): HostContext {
91+
return emptyObject;
92+
}
93+
94+
export function getChildHostContext(
95+
parentHostContext: HostContext,
96+
type: string,
97+
rootContainerInstance: Container,
98+
): HostContext {
99+
return emptyObject;
100+
}
101+
102+
export function prepareForCommit(containerInfo: Container): void {
103+
// noop
104+
}
105+
106+
export function resetAfterCommit(containerInfo: Container): void {
107+
// noop
108+
}
109+
110+
export function createInstance(
111+
type: string,
112+
props: Props,
113+
rootContainerInstance: Container,
114+
hostContext: Object,
115+
internalInstanceHandle: Object,
116+
): Instance {
117+
return {
118+
type,
119+
props,
120+
children: [],
121+
rootContainerInstance,
122+
tag: 'INSTANCE',
123+
};
124+
}
125+
126+
export function appendInitialChild(
127+
parentInstance: Instance,
128+
child: Instance | TextInstance,
129+
): void {
130+
const index = parentInstance.children.indexOf(child);
131+
if (index !== -1) {
132+
parentInstance.children.splice(index, 1);
133+
}
134+
parentInstance.children.push(child);
135+
}
136+
137+
export function finalizeInitialChildren(
138+
testElement: Instance,
139+
type: string,
140+
props: Props,
141+
rootContainerInstance: Container,
142+
hostContext: Object,
143+
): boolean {
144+
return false;
145+
}
146+
147+
export function prepareUpdate(
148+
testElement: Instance,
149+
type: string,
150+
oldProps: Props,
151+
newProps: Props,
152+
rootContainerInstance: Container,
153+
hostContext: Object,
154+
): null | {} {
155+
return UPDATE_SIGNAL;
156+
}
157+
158+
export function shouldSetTextContent(type: string, props: Props): boolean {
159+
return false;
160+
}
161+
162+
export function shouldDeprioritizeSubtree(type: string, props: Props): boolean {
163+
return false;
164+
}
165+
166+
export function createTextInstance(
167+
text: string,
168+
rootContainerInstance: Container,
169+
hostContext: Object,
170+
internalInstanceHandle: Object,
171+
): TextInstance {
172+
return {
173+
text,
174+
tag: 'TEXT',
175+
};
176+
}
177+
178+
export const isPrimaryRenderer = true;
179+
// This approach enables `now` to be mocked by tests,
180+
// Even after the reconciler has initialized and read host config values.
181+
export const now = () => TestRendererScheduling.nowImplementation();
182+
export const scheduleDeferredCallback =
183+
TestRendererScheduling.scheduleDeferredCallback;
184+
export const cancelDeferredCallback =
185+
TestRendererScheduling.cancelDeferredCallback;
186+
187+
// -------------------
188+
// Mutation
189+
// -------------------
190+
191+
export const supportsMutation = true;
192+
193+
export function commitUpdate(
194+
instance: Instance,
195+
updatePayload: {},
196+
type: string,
197+
oldProps: Props,
198+
newProps: Props,
199+
internalInstanceHandle: Object,
200+
): void {
201+
instance.type = type;
202+
instance.props = newProps;
203+
}
204+
205+
export function commitMount(
206+
instance: Instance,
207+
type: string,
208+
newProps: Props,
209+
internalInstanceHandle: Object,
210+
): void {
211+
// noop
212+
}
213+
214+
export function commitTextUpdate(
215+
textInstance: TextInstance,
216+
oldText: string,
217+
newText: string,
218+
): void {
219+
textInstance.text = newText;
220+
}
221+
222+
export function resetTextContent(testElement: Instance): void {
223+
// noop
224+
}
225+
226+
export const appendChildToContainer = appendChild;
227+
export const insertInContainerBefore = insertBefore;
228+
export const removeChildFromContainer = removeChild;

src/ReactTestRenderer.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type {Fiber} from 'react-reconciler/src/ReactFiber';
1111
import type {FiberRoot} from 'react-reconciler/src/ReactFiberRoot';
1212
import type {Instance, TextInstance} from './ReactTestHostConfig';
1313

14-
import ReactFiberReconciler from 'react-reconciler';
14+
import * as TestRenderer from 'react-reconciler/inline.test';
1515
import {batchedUpdates} from 'events/ReactGenericBatching';
1616
import {findCurrentFiberUsingSlowPath} from 'react-reconciler/reflection';
1717
import {
@@ -30,7 +30,7 @@ import {
3030
} from 'shared/ReactTypeOfWork';
3131
import invariant from 'fbjs/lib/invariant';
3232

33-
import ReactTestHostConfig from './ReactTestHostConfig';
33+
import * as ReactTestHostConfig from './ReactTestHostConfig';
3434
import * as TestRendererScheduling from './ReactTestRendererScheduling';
3535

3636
type TestRendererOptions = {
@@ -54,8 +54,6 @@ type FindOptions = $Shape<{
5454

5555
export type Predicate = (node: ReactTestInstance) => ?boolean;
5656

57-
const TestRenderer = ReactFiberReconciler(ReactTestHostConfig);
58-
5957
const defaultTestOptions = {
6058
createNodeMock: function() {
6159
return null;

src/ReactTestRendererScheduling.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @flow
88
*/
99

10-
import type {Deadline} from 'react-reconciler/src/ReactFiberReconciler';
10+
import type {Deadline} from 'react-reconciler/src/ReactFiberScheduler';
1111

1212
// Current virtual time
1313
export let nowImplementation = () => 0;

0 commit comments

Comments
 (0)