main
js 256 lines 8.6 KB
Raw
1 'use strict';
2
3 const fs = require('fs');
4 const nodePath = require('path');
5 const inlinedHostConfigs = require('../shared/inlinedHostConfigs');
6
7 function resolveEntryFork(resolvedEntry, isFBBundle) {
8 // Pick which entry point fork to use:
9 // .modern.fb.js
10 // .classic.fb.js
11 // .fb.js
12 // .stable.js
13 // .experimental.js
14 // .js
15 // or any of those plus .development.js
16
17 if (isFBBundle) {
18 // FB builds for react-dom need to alias both react-dom and react-dom/client to the same
19 // entrypoint since there is only a single build for them.
20 if (
21 resolvedEntry.endsWith('react-dom/index.js') ||
22 resolvedEntry.endsWith('react-dom/client.js') ||
23 resolvedEntry.endsWith('react-dom/unstable_testing.js')
24 ) {
25 let specifier;
26 let entrypoint;
27 if (resolvedEntry.endsWith('index.js')) {
28 specifier = 'react-dom';
29 entrypoint = __EXPERIMENTAL__
30 ? 'src/ReactDOMFB.modern.js'
31 : 'src/ReactDOMFB.js';
32 } else if (resolvedEntry.endsWith('client.js')) {
33 specifier = 'react-dom/client';
34 entrypoint = __EXPERIMENTAL__
35 ? 'src/ReactDOMFB.modern.js'
36 : 'src/ReactDOMFB.js';
37 } else {
38 // must be unstable_testing
39 specifier = 'react-dom/unstable_testing';
40 entrypoint = __EXPERIMENTAL__
41 ? 'src/ReactDOMTestingFB.modern.js'
42 : 'src/ReactDOMTestingFB.js';
43 }
44
45 resolvedEntry = nodePath.join(resolvedEntry, '..', entrypoint);
46 const devEntry = resolvedEntry.replace('.js', '.development.js');
47 if (__DEV__ && fs.existsSync(devEntry)) {
48 return devEntry;
49 }
50 if (fs.existsSync(resolvedEntry)) {
51 return resolvedEntry;
52 }
53 const fbReleaseChannel = __EXPERIMENTAL__ ? 'www-modern' : 'www-classic';
54 throw new Error(
55 `${fbReleaseChannel} tests are expected to alias ${specifier} to ${entrypoint} but this file was not found`
56 );
57 }
58 const resolvedFBEntry = resolvedEntry.replace(
59 '.js',
60 __EXPERIMENTAL__ ? '.modern.fb.js' : '.classic.fb.js'
61 );
62 const devFBEntry = resolvedFBEntry.replace('.js', '.development.js');
63 if (__DEV__ && fs.existsSync(devFBEntry)) {
64 return devFBEntry;
65 }
66 if (fs.existsSync(resolvedFBEntry)) {
67 return resolvedFBEntry;
68 }
69 const resolvedGenericFBEntry = resolvedEntry.replace('.js', '.fb.js');
70 const devGenericFBEntry = resolvedGenericFBEntry.replace(
71 '.js',
72 '.development.js'
73 );
74 if (__DEV__ && fs.existsSync(devGenericFBEntry)) {
75 return devGenericFBEntry;
76 }
77 if (fs.existsSync(resolvedGenericFBEntry)) {
78 return resolvedGenericFBEntry;
79 }
80 // Even if it's a FB bundle we fallthrough to pick stable or experimental if we don't have an FB fork.
81 }
82 const resolvedForkedEntry = resolvedEntry.replace(
83 '.js',
84 __EXPERIMENTAL__ ? '.experimental.js' : '.stable.js'
85 );
86 const devForkedEntry = resolvedForkedEntry.replace('.js', '.development.js');
87 if (__DEV__ && fs.existsSync(devForkedEntry)) {
88 return devForkedEntry;
89 }
90 if (fs.existsSync(resolvedForkedEntry)) {
91 return resolvedForkedEntry;
92 }
93 const plainDevEntry = resolvedEntry.replace('.js', '.development.js');
94 if (__DEV__ && fs.existsSync(plainDevEntry)) {
95 return plainDevEntry;
96 }
97 // Just use the plain .js one.
98 return resolvedEntry;
99 }
100
101 function mockReact() {
102 jest.mock('react', () => {
103 const resolvedEntryPoint = resolveEntryFork(
104 require.resolve('react'),
105 global.__WWW__ || global.__XPLAT__,
106 global.__DEV__
107 );
108 return jest.requireActual(resolvedEntryPoint);
109 });
110 // Make it possible to import this module inside
111 // the React package itself.
112 jest.mock('shared/ReactSharedInternals', () => {
113 return jest.requireActual('react/src/ReactSharedInternalsClient');
114 });
115 }
116
117 // When we want to unmock React we really need to mock it again.
118 global.__unmockReact = mockReact;
119
120 mockReact();
121
122 jest.mock('react/react.react-server', () => {
123 // If we're requiring an RSC environment, use those internals instead.
124 jest.mock('shared/ReactSharedInternals', () => {
125 return jest.requireActual('react/src/ReactSharedInternalsServer');
126 });
127 const resolvedEntryPoint = resolveEntryFork(
128 require.resolve('react/src/ReactServer'),
129 global.__WWW__ || global.__XPLAT__,
130 global.__DEV__
131 );
132 return jest.requireActual(resolvedEntryPoint);
133 });
134
135 // When testing the custom renderer code path through `react-reconciler`,
136 // turn the export into a function, and use the argument as host config.
137 const shimHostConfigPath = 'react-reconciler/src/ReactFiberConfig';
138 jest.mock('react-reconciler', () => {
139 return config => {
140 jest.mock(shimHostConfigPath, () => config);
141 return jest.requireActual('react-reconciler');
142 };
143 });
144 const shimServerStreamConfigPath = 'react-server/src/ReactServerStreamConfig';
145 const shimServerConfigPath = 'react-server/src/ReactFizzConfig';
146 const shimFlightServerConfigPath = 'react-server/src/ReactFlightServerConfig';
147 jest.mock('react-server', () => {
148 return config => {
149 jest.mock(shimServerStreamConfigPath, () => config);
150 jest.mock(shimServerConfigPath, () => config);
151 return jest.requireActual('react-server');
152 };
153 });
154 jest.mock('react-server/flight', () => {
155 return config => {
156 jest.mock(shimServerStreamConfigPath, () => config);
157 jest.mock(shimServerConfigPath, () => config);
158 jest.mock('react-server/src/ReactFlightServerConfigBundlerCustom', () => ({
159 isClientReference: config.isClientReference,
160 isServerReference: config.isServerReference,
161 getClientReferenceKey: config.getClientReferenceKey,
162 resolveClientReferenceMetadata: config.resolveClientReferenceMetadata,
163 }));
164 jest.mock(shimFlightServerConfigPath, () =>
165 jest.requireActual(
166 'react-server/src/forks/ReactFlightServerConfig.custom'
167 )
168 );
169 return jest.requireActual('react-server/flight');
170 };
171 });
172 const shimFlightClientConfigPath = 'react-client/src/ReactFlightClientConfig';
173 jest.mock('react-client/flight', () => {
174 return config => {
175 jest.mock(shimFlightClientConfigPath, () => config);
176 return jest.requireActual('react-client/flight');
177 };
178 });
179
180 const configPaths = [
181 'react-reconciler/src/ReactFiberConfig',
182 'react-client/src/ReactFlightClientConfig',
183 'react-server/src/ReactServerStreamConfig',
184 'react-server/src/ReactFizzConfig',
185 'react-server/src/ReactFlightServerConfig',
186 ];
187
188 function mockAllConfigs(rendererInfo) {
189 configPaths.forEach(path => {
190 // We want the reconciler to pick up the host config for this renderer.
191 jest.mock(path, () => {
192 let idx = path.lastIndexOf('/');
193 let forkPath = path.slice(0, idx) + '/forks' + path.slice(idx);
194 let parts = rendererInfo.shortName.split('-');
195 while (parts.length) {
196 try {
197 const candidate = `${forkPath}.${parts.join('-')}.js`;
198 fs.statSync(nodePath.join(process.cwd(), 'packages', candidate));
199 return jest.requireActual(candidate);
200 } catch (error) {
201 if (error.code !== 'ENOENT') {
202 throw error;
203 }
204 // try without a part
205 }
206 parts.pop();
207 }
208 throw new Error(
209 `Expected to find a fork for ${path} but did not find one.`
210 );
211 });
212 });
213 }
214
215 // But for inlined host configs (such as React DOM, Native, etc), we
216 // mock their named entry points to establish a host config mapping.
217 inlinedHostConfigs.forEach(rendererInfo => {
218 if (rendererInfo.shortName === 'custom') {
219 // There is no inline entry point for the custom renderers.
220 // Instead, it's handled by the generic `react-reconciler` entry point above.
221 return;
222 }
223 rendererInfo.entryPoints.forEach(entryPoint => {
224 jest.mock(entryPoint, () => {
225 mockAllConfigs(rendererInfo);
226 const resolvedEntryPoint = resolveEntryFork(
227 require.resolve(entryPoint),
228 global.__WWW__ || global.__XPLAT__,
229 global.__DEV__
230 );
231 return jest.requireActual(resolvedEntryPoint);
232 });
233 });
234 });
235
236 jest.mock('react-server/src/ReactFlightServer', () => {
237 // If we're requiring an RSC environment, use those internals instead.
238 jest.mock('shared/ReactSharedInternals', () => {
239 return jest.requireActual('react/src/ReactSharedInternalsServer');
240 });
241 return jest.requireActual('react-server/src/ReactFlightServer');
242 });
243
244 // Make it possible to import this module inside
245 // the ReactDOM package itself.
246 jest.mock('shared/ReactDOMSharedInternals', () =>
247 jest.requireActual('react-dom/src/ReactDOMSharedInternals')
248 );
249
250 jest.mock('scheduler', () => jest.requireActual('scheduler/unstable_mock'));
251
252 if (global.__PERSISTENT__) {
253 jest.mock('react-noop-renderer', () =>
254 jest.requireActual('react-noop-renderer/persistent')
255 );
256 }