[Fire] Add initial build infrastructure (#14359)
Dan Abramov committed
Nov 30, 2018 at 11:52 UTC
16e120438c5332d7590bc74b2cbd882f19a0e3b1
10 files changed
+948
-1
fixtures/unstable-fire/index.html
new
+14
@@ -0,0 +1,14 @@
1
+<html>
2
+ <body>
3
+ <script src="../../build/node_modules/react/umd/react.development.js"></script>
4
+ <script src="../../build/node_modules/react-dom/umd/react-dom-unstable-fire.development.js"></script>
5
+ <script src="https://unpkg.com/babel-standalone@6/babel.js"></script>
6
+ <div id="container"></div>
7
+ <script type="text/babel">
8
+ ReactFire.render(
9
+ <h1>Hello World!</h1>,
10
+ document.getElementById('container')
11
+ );
12
+ </script>
13
+ </body>
14
+</html>
\ No newline at end of file
packages/react-dom/package.json
+1
@@ -31,6 +31,7 @@
31
"server.browser.js",
32
"server.node.js",
33
"test-utils.js",
34
+ "unstable-fire.js",
35
"unstable-native-dependencies.js",
36
"cjs/",
37
"umd/"
packages/react-dom/src/fire/ReactFire.js
new
+855
@@ -0,0 +1,855 @@
1
+/**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+// This file is copy paste from ReactDOM with adjusted paths
11
+// and a different host config import (react-reconciler/inline.fire).
12
+// TODO: real implementation.
13
+// console.log('Hello from Fire entry point.');
14
+
15
+import type {ReactNodeList} from 'shared/ReactTypes';
16
+// TODO: This type is shared between the reconciler and ReactDOM, but will
17
+// eventually be lifted out to the renderer.
18
+import type {
19
+ FiberRoot,
20
+ Batch as FiberRootBatch,
21
+} from 'react-reconciler/src/ReactFiberRoot';
22
+import type {Container} from '../client/ReactDOMHostConfig';
23
+
24
+import '../shared/checkReact';
25
+import '../client/ReactDOMClientInjection';
26
+
27
+import {
28
+ computeUniqueAsyncExpiration,
29
+ findHostInstanceWithNoPortals,
30
+ updateContainerAtExpirationTime,
31
+ flushRoot,
32
+ createContainer,
33
+ updateContainer,
34
+ batchedUpdates,
35
+ unbatchedUpdates,
36
+ interactiveUpdates,
37
+ flushInteractiveUpdates,
38
+ flushSync,
39
+ flushControlled,
40
+ injectIntoDevTools,
41
+ getPublicRootInstance,
42
+ findHostInstance,
43
+ findHostInstanceWithWarning,
44
+} from 'react-reconciler/inline.fire';
45
+import {createPortal as createPortalImpl} from 'shared/ReactPortal';
46
+import {canUseDOM} from 'shared/ExecutionEnvironment';
47
+import {setBatchingImplementation} from 'events/ReactGenericBatching';
48
+import {
49
+ setRestoreImplementation,
50
+ enqueueStateRestore,
51
+ restoreStateIfNeeded,
52
+} from 'events/ReactControlledComponent';
53
+import {
54
+ injection as EventPluginHubInjection,
55
+ runEventsInBatch,
56
+} from 'events/EventPluginHub';
57
+import {eventNameDispatchConfigs} from 'events/EventPluginRegistry';
58
+import {
59
+ accumulateTwoPhaseDispatches,
60
+ accumulateDirectDispatches,
61
+} from 'events/EventPropagators';
62
+import {has as hasInstance} from 'shared/ReactInstanceMap';
63
+import ReactVersion from 'shared/ReactVersion';
64
+import ReactSharedInternals from 'shared/ReactSharedInternals';
65
+import getComponentName from 'shared/getComponentName';
66
+import invariant from 'shared/invariant';
67
+import lowPriorityWarning from 'shared/lowPriorityWarning';
68
+import warningWithoutStack from 'shared/warningWithoutStack';
69
+import {enableStableConcurrentModeAPIs} from 'shared/ReactFeatureFlags';
70
+
71
+import {
72
+ getInstanceFromNode,
73
+ getNodeFromInstance,
74
+ getFiberCurrentPropsFromNode,
75
+ getClosestInstanceFromNode,
76
+} from '../client/ReactDOMComponentTree';
77
+import {restoreControlledState} from '../client/ReactDOMComponent';
78
+import {dispatchEvent} from '../events/ReactDOMEventListener';
79
+import {
80
+ ELEMENT_NODE,
81
+ COMMENT_NODE,
82
+ DOCUMENT_NODE,
83
+ DOCUMENT_FRAGMENT_NODE,
84
+} from '../shared/HTMLNodeType';
85
+import {ROOT_ATTRIBUTE_NAME} from '../shared/DOMProperty';
86
+
87
+const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
88
+
89
+let topLevelUpdateWarnings;
90
+let warnOnInvalidCallback;
91
+let didWarnAboutUnstableCreatePortal = false;
92
+
93
+if (__DEV__) {
94
+ if (
95
+ typeof Map !== 'function' ||
96
+ // $FlowIssue Flow incorrectly thinks Map has no prototype
97
+ Map.prototype == null ||
98
+ typeof Map.prototype.forEach !== 'function' ||
99
+ typeof Set !== 'function' ||
100
+ // $FlowIssue Flow incorrectly thinks Set has no prototype
101
+ Set.prototype == null ||
102
+ typeof Set.prototype.clear !== 'function' ||
103
+ typeof Set.prototype.forEach !== 'function'
104
+ ) {
105
+ warningWithoutStack(
106
+ false,
107
+ 'React depends on Map and Set built-in types. Make sure that you load a ' +
108
+ 'polyfill in older browsers. https://fb.me/react-polyfills',
109
+ );
110
+ }
111
+
112
+ topLevelUpdateWarnings = (container: DOMContainer) => {
113
+ if (container._reactRootContainer && container.nodeType !== COMMENT_NODE) {
114
+ const hostInstance = findHostInstanceWithNoPortals(
115
+ container._reactRootContainer._internalRoot.current,
116
+ );
117
+ if (hostInstance) {
118
+ warningWithoutStack(
119
+ hostInstance.parentNode === container,
120
+ 'render(...): It looks like the React-rendered content of this ' +
121
+ 'container was removed without using React. This is not ' +
122
+ 'supported and will cause errors. Instead, call ' +
123
+ 'ReactDOM.unmountComponentAtNode to empty a container.',
124
+ );
125
+ }
126
+ }
127
+
128
+ const isRootRenderedBySomeReact = !!container._reactRootContainer;
129
+ const rootEl = getReactRootElementInContainer(container);
130
+ const hasNonRootReactChild = !!(rootEl && getInstanceFromNode(rootEl));
131
+
132
+ warningWithoutStack(
133
+ !hasNonRootReactChild || isRootRenderedBySomeReact,
134
+ 'render(...): Replacing React-rendered children with a new root ' +
135
+ 'component. If you intended to update the children of this node, ' +
136
+ 'you should instead have the existing children update their state ' +
137
+ 'and render the new components instead of calling ReactDOM.render.',
138
+ );
139
+
140
+ warningWithoutStack(
141
+ container.nodeType !== ELEMENT_NODE ||
142
+ !((container: any): Element).tagName ||
143
+ ((container: any): Element).tagName.toUpperCase() !== 'BODY',
144
+ 'render(): Rendering components directly into document.body is ' +
145
+ 'discouraged, since its children are often manipulated by third-party ' +
146
+ 'scripts and browser extensions. This may lead to subtle ' +
147
+ 'reconciliation issues. Try rendering into a container element created ' +
148
+ 'for your app.',
149
+ );
150
+ };
151
+
152
+ warnOnInvalidCallback = function(callback: mixed, callerName: string) {
153
+ warningWithoutStack(
154
+ callback === null || typeof callback === 'function',
155
+ '%s(...): Expected the last optional `callback` argument to be a ' +
156
+ 'function. Instead received: %s.',
157
+ callerName,
158
+ callback,
159
+ );
160
+ };
161
+}
162
+
163
+setRestoreImplementation(restoreControlledState);
164
+
165
+export type DOMContainer =
166
+ | (Element & {
167
+ _reactRootContainer: ?Root,
168
+ })
169
+ | (Document & {
170
+ _reactRootContainer: ?Root,
171
+ });
172
+
173
+type Batch = FiberRootBatch & {
174
+ render(children: ReactNodeList): Work,
175
+ then(onComplete: () => mixed): void,
176
+ commit(): void,
177
+
178
+ // The ReactRoot constructor is hoisted but the prototype methods are not. If
179
+ // we move ReactRoot to be above ReactBatch, the inverse error occurs.
180
+ // $FlowFixMe Hoisting issue.
181
+ _root: Root,
182
+ _hasChildren: boolean,
183
+ _children: ReactNodeList,
184
+
185
+ _callbacks: Array<() => mixed> | null,
186
+ _didComplete: boolean,
187
+};
188
+
189
+type Root = {
190
+ render(children: ReactNodeList, callback: ?() => mixed): Work,
191
+ unmount(callback: ?() => mixed): Work,
192
+ legacy_renderSubtreeIntoContainer(
193
+ parentComponent: ?React$Component<any, any>,
194
+ children: ReactNodeList,
195
+ callback: ?() => mixed,
196
+ ): Work,
197
+ createBatch(): Batch,
198
+
199
+ _internalRoot: FiberRoot,
200
+};
201
+
202
+function ReactBatch(root: ReactRoot) {
203
+ const expirationTime = computeUniqueAsyncExpiration();
204
+ this._expirationTime = expirationTime;
205
+ this._root = root;
206
+ this._next = null;
207
+ this._callbacks = null;
208
+ this._didComplete = false;
209
+ this._hasChildren = false;
210
+ this._children = null;
211
+ this._defer = true;
212
+}
213
+ReactBatch.prototype.render = function(children: ReactNodeList) {
214
+ invariant(
215
+ this._defer,
216
+ 'batch.render: Cannot render a batch that already committed.',
217
+ );
218
+ this._hasChildren = true;
219
+ this._children = children;
220
+ const internalRoot = this._root._internalRoot;
221
+ const expirationTime = this._expirationTime;
222
+ const work = new ReactWork();
223
+ updateContainerAtExpirationTime(
224
+ children,
225
+ internalRoot,
226
+ null,
227
+ expirationTime,
228
+ work._onCommit,
229
+ );
230
+ return work;
231
+};
232
+ReactBatch.prototype.then = function(onComplete: () => mixed) {
233
+ if (this._didComplete) {
234
+ onComplete();
235
+ return;
236
+ }
237
+ let callbacks = this._callbacks;
238
+ if (callbacks === null) {
239
+ callbacks = this._callbacks = [];
240
+ }
241
+ callbacks.push(onComplete);
242
+};
243
+ReactBatch.prototype.commit = function() {
244
+ const internalRoot = this._root._internalRoot;
245
+ let firstBatch = internalRoot.firstBatch;
246
+ invariant(
247
+ this._defer && firstBatch !== null,
248
+ 'batch.commit: Cannot commit a batch multiple times.',
249
+ );
250
+
251
+ if (!this._hasChildren) {
252
+ // This batch is empty. Return.
253
+ this._next = null;
254
+ this._defer = false;
255
+ return;
256
+ }
257
+
258
+ let expirationTime = this._expirationTime;
259
+
260
+ // Ensure this is the first batch in the list.
261
+ if (firstBatch !== this) {
262
+ // This batch is not the earliest batch. We need to move it to the front.
263
+ // Update its expiration time to be the expiration time of the earliest
264
+ // batch, so that we can flush it without flushing the other batches.
265
+ if (this._hasChildren) {
266
+ expirationTime = this._expirationTime = firstBatch._expirationTime;
267
+ // Rendering this batch again ensures its children will be the final state
268
+ // when we flush (updates are processed in insertion order: last
269
+ // update wins).
270
+ // TODO: This forces a restart. Should we print a warning?
271
+ this.render(this._children);
272
+ }
273
+
274
+ // Remove the batch from the list.
275
+ let previous = null;
276
+ let batch = firstBatch;
277
+ while (batch !== this) {
278
+ previous = batch;
279
+ batch = batch._next;
280
+ }
281
+ invariant(
282
+ previous !== null,
283
+ 'batch.commit: Cannot commit a batch multiple times.',
284
+ );
285
+ previous._next = batch._next;
286
+
287
+ // Add it to the front.
288
+ this._next = firstBatch;
289
+ firstBatch = internalRoot.firstBatch = this;
290
+ }
291
+
292
+ // Synchronously flush all the work up to this batch's expiration time.
293
+ this._defer = false;
294
+ flushRoot(internalRoot, expirationTime);
295
+
296
+ // Pop the batch from the list.
297
+ const next = this._next;
298
+ this._next = null;
299
+ firstBatch = internalRoot.firstBatch = next;
300
+
301
+ // Append the next earliest batch's children to the update queue.
302
+ if (firstBatch !== null && firstBatch._hasChildren) {
303
+ firstBatch.render(firstBatch._children);
304
+ }
305
+};
306
+ReactBatch.prototype._onComplete = function() {
307
+ if (this._didComplete) {
308
+ return;
309
+ }
310
+ this._didComplete = true;
311
+ const callbacks = this._callbacks;
312
+ if (callbacks === null) {
313
+ return;
314
+ }
315
+ // TODO: Error handling.
316
+ for (let i = 0; i < callbacks.length; i++) {
317
+ const callback = callbacks[i];
318
+ callback();
319
+ }
320
+};
321
+
322
+type Work = {
323
+ then(onCommit: () => mixed): void,
324
+ _onCommit: () => void,
325
+ _callbacks: Array<() => mixed> | null,
326
+ _didCommit: boolean,
327
+};
328
+
329
+function ReactWork() {
330
+ this._callbacks = null;
331
+ this._didCommit = false;
332
+ // TODO: Avoid need to bind by replacing callbacks in the update queue with
333
+ // list of Work objects.
334
+ this._onCommit = this._onCommit.bind(this);
335
+}
336
+ReactWork.prototype.then = function(onCommit: () => mixed): void {
337
+ if (this._didCommit) {
338
+ onCommit();
339
+ return;
340
+ }
341
+ let callbacks = this._callbacks;
342
+ if (callbacks === null) {
343
+ callbacks = this._callbacks = [];
344
+ }
345
+ callbacks.push(onCommit);
346
+};
347
+ReactWork.prototype._onCommit = function(): void {
348
+ if (this._didCommit) {
349
+ return;
350
+ }
351
+ this._didCommit = true;
352
+ const callbacks = this._callbacks;
353
+ if (callbacks === null) {
354
+ return;
355
+ }
356
+ // TODO: Error handling.
357
+ for (let i = 0; i < callbacks.length; i++) {
358
+ const callback = callbacks[i];
359
+ invariant(
360
+ typeof callback === 'function',
361
+ 'Invalid argument passed as callback. Expected a function. Instead ' +
362
+ 'received: %s',
363
+ callback,
364
+ );
365
+ callback();
366
+ }
367
+};
368
+
369
+function ReactRoot(
370
+ container: Container,
371
+ isConcurrent: boolean,
372
+ hydrate: boolean,
373
+) {
374
+ const root = createContainer(container, isConcurrent, hydrate);
375
+ this._internalRoot = root;
376
+}
377
+ReactRoot.prototype.render = function(
378
+ children: ReactNodeList,
379
+ callback: ?() => mixed,
380
+): Work {
381
+ const root = this._internalRoot;
382
+ const work = new ReactWork();
383
+ callback = callback === undefined ? null : callback;
384
+ if (__DEV__) {
385
+ warnOnInvalidCallback(callback, 'render');
386
+ }
387
+ if (callback !== null) {
388
+ work.then(callback);
389
+ }
390
+ updateContainer(children, root, null, work._onCommit);
391
+ return work;
392
+};
393
+ReactRoot.prototype.unmount = function(callback: ?() => mixed): Work {
394
+ const root = this._internalRoot;
395
+ const work = new ReactWork();
396
+ callback = callback === undefined ? null : callback;
397
+ if (__DEV__) {
398
+ warnOnInvalidCallback(callback, 'render');
399
+ }
400
+ if (callback !== null) {
401
+ work.then(callback);
402
+ }
403
+ updateContainer(null, root, null, work._onCommit);
404
+ return work;
405
+};
406
+ReactRoot.prototype.legacy_renderSubtreeIntoContainer = function(
407
+ parentComponent: ?React$Component<any, any>,
408
+ children: ReactNodeList,
409
+ callback: ?() => mixed,
410
+): Work {
411
+ const root = this._internalRoot;
412
+ const work = new ReactWork();
413
+ callback = callback === undefined ? null : callback;
414
+ if (__DEV__) {
415
+ warnOnInvalidCallback(callback, 'render');
416
+ }
417
+ if (callback !== null) {
418
+ work.then(callback);
419
+ }
420
+ updateContainer(children, root, parentComponent, work._onCommit);
421
+ return work;
422
+};
423
+ReactRoot.prototype.createBatch = function(): Batch {
424
+ const batch = new ReactBatch(this);
425
+ const expirationTime = batch._expirationTime;
426
+
427
+ const internalRoot = this._internalRoot;
428
+ const firstBatch = internalRoot.firstBatch;
429
+ if (firstBatch === null) {
430
+ internalRoot.firstBatch = batch;
431
+ batch._next = null;
432
+ } else {
433
+ // Insert sorted by expiration time then insertion order
434
+ let insertAfter = null;
435
+ let insertBefore = firstBatch;
436
+ while (
437
+ insertBefore !== null &&
438
+ insertBefore._expirationTime >= expirationTime
439
+ ) {
440
+ insertAfter = insertBefore;
441
+ insertBefore = insertBefore._next;
442
+ }
443
+ batch._next = insertBefore;
444
+ if (insertAfter !== null) {
445
+ insertAfter._next = batch;
446
+ }
447
+ }
448
+
449
+ return batch;
450
+};
451
+
452
+/**
453
+ * True if the supplied DOM node is a valid node element.
454
+ *
455
+ * @param {?DOMElement} node The candidate DOM node.
456
+ * @return {boolean} True if the DOM is a valid DOM node.
457
+ * @internal
458
+ */
459
+function isValidContainer(node) {
460
+ return !!(
461
+ node &&
462
+ (node.nodeType === ELEMENT_NODE ||
463
+ node.nodeType === DOCUMENT_NODE ||
464
+ node.nodeType === DOCUMENT_FRAGMENT_NODE ||
465
+ (node.nodeType === COMMENT_NODE &&
466
+ node.nodeValue === ' react-mount-point-unstable '))
467
+ );
468
+}
469
+
470
+function getReactRootElementInContainer(container: any) {
471
+ if (!container) {
472
+ return null;
473
+ }
474
+
475
+ if (container.nodeType === DOCUMENT_NODE) {
476
+ return container.documentElement;
477
+ } else {
478
+ return container.firstChild;
479
+ }
480
+}
481
+
482
+function shouldHydrateDueToLegacyHeuristic(container) {
483
+ const rootElement = getReactRootElementInContainer(container);
484
+ return !!(
485
+ rootElement &&
486
+ rootElement.nodeType === ELEMENT_NODE &&
487
+ rootElement.hasAttribute(ROOT_ATTRIBUTE_NAME)
488
+ );
489
+}
490
+
491
+setBatchingImplementation(
492
+ batchedUpdates,
493
+ interactiveUpdates,
494
+ flushInteractiveUpdates,
495
+);
496
+
497
+let warnedAboutHydrateAPI = false;
498
+
499
+function legacyCreateRootFromDOMContainer(
500
+ container: DOMContainer,
501
+ forceHydrate: boolean,
502
+): Root {
503
+ const shouldHydrate =
504
+ forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
505
+ // First clear any existing content.
506
+ if (!shouldHydrate) {
507
+ let warned = false;
508
+ let rootSibling;
509
+ while ((rootSibling = container.lastChild)) {
510
+ if (__DEV__) {
511
+ if (
512
+ !warned &&
513
+ rootSibling.nodeType === ELEMENT_NODE &&
514
+ (rootSibling: any).hasAttribute(ROOT_ATTRIBUTE_NAME)
515
+ ) {
516
+ warned = true;
517
+ warningWithoutStack(
518
+ false,
519
+ 'render(): Target node has markup rendered by React, but there ' +
520
+ 'are unrelated nodes as well. This is most commonly caused by ' +
521
+ 'white-space inserted around server-rendered markup.',
522
+ );
523
+ }
524
+ }
525
+ container.removeChild(rootSibling);
526
+ }
527
+ }
528
+ if (__DEV__) {
529
+ if (shouldHydrate && !forceHydrate && !warnedAboutHydrateAPI) {
530
+ warnedAboutHydrateAPI = true;
531
+ lowPriorityWarning(
532
+ false,
533
+ 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
534
+ 'will stop working in React v17. Replace the ReactDOM.render() call ' +
535
+ 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
536
+ );
537
+ }
538
+ }
539
+ // Legacy roots are not async by default.
540
+ const isConcurrent = false;
541
+ return new ReactRoot(container, isConcurrent, shouldHydrate);
542
+}
543
+
544
+function legacyRenderSubtreeIntoContainer(
545
+ parentComponent: ?React$Component<any, any>,
546
+ children: ReactNodeList,
547
+ container: DOMContainer,
548
+ forceHydrate: boolean,
549
+ callback: ?Function,
550
+) {
551
+ // TODO: Ensure all entry points contain this check
552
+ invariant(
553
+ isValidContainer(container),
554
+ 'Target container is not a DOM element.',
555
+ );
556
+
557
+ if (__DEV__) {
558
+ topLevelUpdateWarnings(container);
559
+ }
560
+
561
+ // TODO: Without `any` type, Flow says "Property cannot be accessed on any
562
+ // member of intersection type." Whyyyyyy.
563
+ let root: Root = (container._reactRootContainer: any);
564
+ if (!root) {
565
+ // Initial mount
566
+ root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
567
+ container,
568
+ forceHydrate,
569
+ );
570
+ if (typeof callback === 'function') {
571
+ const originalCallback = callback;
572
+ callback = function() {
573
+ const instance = getPublicRootInstance(root._internalRoot);
574
+ originalCallback.call(instance);
575
+ };
576
+ }
577
+ // Initial mount should not be batched.
578
+ unbatchedUpdates(() => {
579
+ if (parentComponent != null) {
580
+ root.legacy_renderSubtreeIntoContainer(
581
+ parentComponent,
582
+ children,
583
+ callback,
584
+ );
585
+ } else {
586
+ root.render(children, callback);
587
+ }
588
+ });
589
+ } else {
590
+ if (typeof callback === 'function') {
591
+ const originalCallback = callback;
592
+ callback = function() {
593
+ const instance = getPublicRootInstance(root._internalRoot);
594
+ originalCallback.call(instance);
595
+ };
596
+ }
597
+ // Update
598
+ if (parentComponent != null) {
599
+ root.legacy_renderSubtreeIntoContainer(
600
+ parentComponent,
601
+ children,
602
+ callback,
603
+ );
604
+ } else {
605
+ root.render(children, callback);
606
+ }
607
+ }
608
+ return getPublicRootInstance(root._internalRoot);
609
+}
610
+
611
+function createPortal(
612
+ children: ReactNodeList,
613
+ container: DOMContainer,
614
+ key: ?string = null,
615
+) {
616
+ invariant(
617
+ isValidContainer(container),
618
+ 'Target container is not a DOM element.',
619
+ );
620
+ // TODO: pass ReactDOM portal implementation as third argument
621
+ return createPortalImpl(children, container, null, key);
622
+}
623
+
624
+const ReactDOM: Object = {
625
+ createPortal,
626
+
627
+ findDOMNode(
628
+ componentOrElement: Element | ?React$Component<any, any>,
629
+ ): null | Element | Text {
630
+ if (__DEV__) {
631
+ let owner = (ReactCurrentOwner.current: any);
632
+ if (owner !== null && owner.stateNode !== null) {
633
+ const warnedAboutRefsInRender =
634
+ owner.stateNode._warnedAboutRefsInRender;
635
+ warningWithoutStack(
636
+ warnedAboutRefsInRender,
637
+ '%s is accessing findDOMNode inside its render(). ' +
638
+ 'render() should be a pure function of props and state. It should ' +
639
+ 'never access something that requires stale data from the previous ' +
640
+ 'render, such as refs. Move this logic to componentDidMount and ' +
641
+ 'componentDidUpdate instead.',
642
+ getComponentName(owner.type) || 'A component',
643
+ );
644
+ owner.stateNode._warnedAboutRefsInRender = true;
645
+ }
646
+ }
647
+ if (componentOrElement == null) {
648
+ return null;
649
+ }
650
+ if ((componentOrElement: any).nodeType === ELEMENT_NODE) {
651
+ return (componentOrElement: any);
652
+ }
653
+ if (__DEV__) {
654
+ return findHostInstanceWithWarning(componentOrElement, 'findDOMNode');
655
+ }
656
+ return findHostInstance(componentOrElement);
657
+ },
658
+
659
+ hydrate(element: React$Node, container: DOMContainer, callback: ?Function) {
660
+ // TODO: throw or warn if we couldn't hydrate?
661
+ return legacyRenderSubtreeIntoContainer(
662
+ null,
663
+ element,
664
+ container,
665
+ true,
666
+ callback,
667
+ );
668
+ },
669
+
670
+ render(
671
+ element: React$Element<any>,
672
+ container: DOMContainer,
673
+ callback: ?Function,
674
+ ) {
675
+ return legacyRenderSubtreeIntoContainer(
676
+ null,
677
+ element,
678
+ container,
679
+ false,
680
+ callback,
681
+ );
682
+ },
683
+
684
+ unstable_renderSubtreeIntoContainer(
685
+ parentComponent: React$Component<any, any>,
686
+ element: React$Element<any>,
687
+ containerNode: DOMContainer,
688
+ callback: ?Function,
689
+ ) {
690
+ invariant(
691
+ parentComponent != null && hasInstance(parentComponent),
692
+ 'parentComponent must be a valid React Component',
693
+ );
694
+ return legacyRenderSubtreeIntoContainer(
695
+ parentComponent,
696
+ element,
697
+ containerNode,
698
+ false,
699
+ callback,
700
+ );
701
+ },
702
+
703
+ unmountComponentAtNode(container: DOMContainer) {
704
+ invariant(
705
+ isValidContainer(container),
706
+ 'unmountComponentAtNode(...): Target container is not a DOM element.',
707
+ );
708
+
709
+ if (container._reactRootContainer) {
710
+ if (__DEV__) {
711
+ const rootEl = getReactRootElementInContainer(container);
712
+ const renderedByDifferentReact = rootEl && !getInstanceFromNode(rootEl);
713
+ warningWithoutStack(
714
+ !renderedByDifferentReact,
715
+ "unmountComponentAtNode(): The node you're attempting to unmount " +
716
+ 'was rendered by another copy of React.',
717
+ );
718
+ }
719
+
720
+ // Unmount should not be batched.
721
+ unbatchedUpdates(() => {
722
+ legacyRenderSubtreeIntoContainer(null, null, container, false, () => {
723
+ container._reactRootContainer = null;
724
+ });
725
+ });
726
+ // If you call unmountComponentAtNode twice in quick succession, you'll
727
+ // get `true` twice. That's probably fine?
728
+ return true;
729
+ } else {
730
+ if (__DEV__) {
731
+ const rootEl = getReactRootElementInContainer(container);
732
+ const hasNonRootReactChild = !!(rootEl && getInstanceFromNode(rootEl));
733
+
734
+ // Check if the container itself is a React root node.
735
+ const isContainerReactRoot =
736
+ container.nodeType === ELEMENT_NODE &&
737
+ isValidContainer(container.parentNode) &&
738
+ !!container.parentNode._reactRootContainer;
739
+
740
+ warningWithoutStack(
741
+ !hasNonRootReactChild,
742
+ "unmountComponentAtNode(): The node you're attempting to unmount " +
743
+ 'was rendered by React and is not a top-level container. %s',
744
+ isContainerReactRoot
745
+ ? 'You may have accidentally passed in a React root node instead ' +
746
+ 'of its container.'
747
+ : 'Instead, have the parent component update its state and ' +
748
+ 'rerender in order to remove this component.',
749
+ );
750
+ }
751
+
752
+ return false;
753
+ }
754
+ },
755
+
756
+ // Temporary alias since we already shipped React 16 RC with it.
757
+ // TODO: remove in React 17.
758
+ unstable_createPortal(...args) {
759
+ if (!didWarnAboutUnstableCreatePortal) {
760
+ didWarnAboutUnstableCreatePortal = true;
761
+ lowPriorityWarning(
762
+ false,
763
+ 'The ReactDOM.unstable_createPortal() alias has been deprecated, ' +
764
+ 'and will be removed in React 17+. Update your code to use ' +
765
+ 'ReactDOM.createPortal() instead. It has the exact same API, ' +
766
+ 'but without the "unstable_" prefix.',
767
+ );
768
+ }
769
+ return createPortal(...args);
770
+ },
771
+
772
+ unstable_batchedUpdates: batchedUpdates,
773
+
774
+ unstable_interactiveUpdates: interactiveUpdates,
775
+
776
+ flushSync: flushSync,
777
+
778
+ unstable_createRoot: createRoot,
779
+ unstable_flushControlled: flushControlled,
780
+
781
+ __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
782
+ // Keep in sync with ReactDOMUnstableNativeDependencies.js
783
+ // and ReactTestUtils.js. This is an array for better minification.
784
+ Events: [
785
+ getInstanceFromNode,
786
+ getNodeFromInstance,
787
+ getFiberCurrentPropsFromNode,
788
+ EventPluginHubInjection.injectEventPluginsByName,
789
+ eventNameDispatchConfigs,
790
+ accumulateTwoPhaseDispatches,
791
+ accumulateDirectDispatches,
792
+ enqueueStateRestore,
793
+ restoreStateIfNeeded,
794
+ dispatchEvent,
795
+ runEventsInBatch,
796
+ ],
797
+ },
798
+};
799
+
800
+type RootOptions = {
801
+ hydrate?: boolean,
802
+};
803
+
804
+function createRoot(container: DOMContainer, options?: RootOptions): ReactRoot {
805
+ const functionName = enableStableConcurrentModeAPIs
806
+ ? 'createRoot'
807
+ : 'unstable_createRoot';
808
+ invariant(
809
+ isValidContainer(container),
810
+ '%s(...): Target container is not a DOM element.',
811
+ functionName,
812
+ );
813
+ const hydrate = options != null && options.hydrate === true;
814
+ return new ReactRoot(container, true, hydrate);
815
+}
816
+
817
+if (enableStableConcurrentModeAPIs) {
818
+ ReactDOM.createRoot = createRoot;
819
+ ReactDOM.unstable_createRoot = undefined;
820
+}
821
+
822
+const foundDevTools = injectIntoDevTools({
823
+ findFiberByHostInstance: getClosestInstanceFromNode,
824
+ bundleType: __DEV__ ? 1 : 0,
825
+ version: ReactVersion,
826
+ rendererPackageName: 'react-dom',
827
+});
828
+
829
+if (__DEV__) {
830
+ if (!foundDevTools && canUseDOM && window.top === window.self) {
831
+ // If we're in Chrome or Firefox, provide a download link if not installed.
832
+ if (
833
+ (navigator.userAgent.indexOf('Chrome') > -1 &&
834
+ navigator.userAgent.indexOf('Edge') === -1) ||
835
+ navigator.userAgent.indexOf('Firefox') > -1
836
+ ) {
837
+ const protocol = window.location.protocol;
838
+ // Don't warn in exotic cases like chrome-extension://.
839
+ if (/^(https?|file):$/.test(protocol)) {
840
+ console.info(
841
+ '%cDownload the React DevTools ' +
842
+ 'for a better development experience: ' +
843
+ 'https://fb.me/react-devtools' +
844
+ (protocol === 'file:'
845
+ ? '\nYou might need to use a local HTTP server (instead of file://): ' +
846
+ 'https://fb.me/react-devtools-faq'
847
+ : ''),
848
+ 'font-weight:bold',
849
+ );
850
+ }
851
+ }
852
+ }
853
+}
854
+
855
+export default ReactDOM;
packages/react-dom/src/fire/ReactFireHostConfig.js
new
+14
@@ -0,0 +1,14 @@
1
+/**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+// This is just to get the setup running.
11
+// TODO: real implementation.
12
+// console.log('Hello from Fire host config.');
13
+
14
+export * from '../client/ReactDOMHostConfig';
packages/react-dom/unstable-fire.js
new
+16
@@ -0,0 +1,16 @@
1
+/**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+'use strict';
11
+
12
+const ReactFire = require('./src/fire/ReactFire');
13
+
14
+// TODO: decide on the top-level export form.
15
+// This is hacky but makes it work with both Rollup and Jest.
16
+module.exports = ReactFire.default || ReactFire;
packages/react-reconciler/inline.fire.js
new
+11
@@ -0,0 +1,11 @@
1
+/**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+// This file intentionally does *not* have the Flow annotation.
9
+// Don't add it. See `./inline-typed.js` for an explanation.
10
+
11
+export * from './src/ReactFiberReconciler';
packages/react-reconciler/src/forks/ReactFiberHostConfig.fire.js
new
+10
@@ -0,0 +1,10 @@
1
+/**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+export * from 'react-dom/src/fire/ReactFireHostConfig';
scripts/jest/setupFire.js
+2
@@ -1,5 +1,7 @@
1
'use strict';
2
3
+jest.mock('react-dom', () => require.requireActual('react-dom/unstable-fire'));
4
+
5
jest.mock('shared/ReactFeatureFlags', () => {
6
const ReactFeatureFlags = require.requireActual('shared/ReactFeatureFlags');
7
ReactFeatureFlags.disableInputAttributeSyncing = true;
scripts/rollup/bundles.js
+20
-1
@@ -91,7 +91,26 @@ const bundles = [
91
externals: ['react'],
92
},
93
94
- //******* Test Utils *******/
94
+ /******* React Fire *******/
95
+ {
96
+ bundleTypes: [
97
+ UMD_DEV,
98
+ UMD_PROD,
99
+ UMD_PROFILING,
100
+ NODE_DEV,
101
+ NODE_PROD,
102
+ NODE_PROFILING,
103
+ FB_WWW_DEV,
104
+ FB_WWW_PROD,
105
+ FB_WWW_PROFILING,
106
+ ],
107
+ moduleType: RENDERER,
108
+ entry: 'react-dom/unstable-fire',
109
+ global: 'ReactFire',
110
+ externals: ['react'],
111
+ },
112
+
113
+ /******* Test Utils *******/
114
{
115
moduleType: RENDERER_UTILS,
116
bundleTypes: [FB_WWW_DEV, NODE_DEV, NODE_PROD, UMD_DEV, UMD_PROD],
scripts/shared/inlinedHostConfigs.js
+5
@@ -12,6 +12,11 @@ module.exports = [
12
entryPoints: ['react-dom'],
13
isFlowTyped: true,
14
},
15
+ {
16
+ shortName: 'fire',
17
+ entryPoints: ['react-dom/unstable-fire'],
18
+ isFlowTyped: true,
19
+ },
20
{
21
shortName: 'art',
22
entryPoints: ['react-art'],