@samitouri / QOS-React / commits / 16e120438c

[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'],