UNPKG

arkit

Version:

Visualises JavaScript, TypeScript and Flow codebases as meaningful and committable architecture diagrams

899 lines (830 loc) 27.3 kB
/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow */ // This file is copy paste from ReactDOM with adjusted paths // and a different host config import (react-reconciler/inline.fire). // TODO: real implementation. // console.log('Hello from Fire entry point.'); import type {ReactNodeList} from 'shared/ReactTypes'; // TODO: This type is shared between the reconciler and ReactDOM, but will // eventually be lifted out to the renderer. import type { FiberRoot, Batch as FiberRootBatch, } from 'react-reconciler/src/ReactFiberRoot'; import '../shared/checkReact'; import '../client/ReactDOMClientInjection'; import { computeUniqueAsyncExpiration, findHostInstanceWithNoPortals, updateContainerAtExpirationTime, flushRoot, createContainer, updateContainer, batchedUpdates, unbatchedUpdates, interactiveUpdates, flushInteractiveUpdates, flushSync, flushControlled, injectIntoDevTools, getPublicRootInstance, findHostInstance, findHostInstanceWithWarning, } from 'react-reconciler/inline.fire'; import {createPortal as createPortalImpl} from 'shared/ReactPortal'; import {canUseDOM} from 'shared/ExecutionEnvironment'; import {setBatchingImplementation} from 'events/ReactGenericBatching'; import { setRestoreImplementation, enqueueStateRestore, restoreStateIfNeeded, } from 'events/ReactControlledComponent'; import { injection as EventPluginHubInjection, runEventsInBatch, } from 'events/EventPluginHub'; import {eventNameDispatchConfigs} from 'events/EventPluginRegistry'; import { accumulateTwoPhaseDispatches, accumulateDirectDispatches, } from 'events/EventPropagators'; import {has as hasInstance} from 'shared/ReactInstanceMap'; import ReactVersion from 'shared/ReactVersion'; import ReactSharedInternals from 'shared/ReactSharedInternals'; import getComponentName from 'shared/getComponentName'; import invariant from 'shared/invariant'; import lowPriorityWarning from 'shared/lowPriorityWarning'; import warningWithoutStack from 'shared/warningWithoutStack'; import {enableStableConcurrentModeAPIs} from 'shared/ReactFeatureFlags'; import { getInstanceFromNode, getNodeFromInstance, getFiberCurrentPropsFromNode, getClosestInstanceFromNode, } from '../client/ReactDOMComponentTree'; import {restoreControlledState} from '../client/ReactDOMComponent'; import {dispatchEvent} from '../events/ReactDOMEventListener'; import { ELEMENT_NODE, COMMENT_NODE, DOCUMENT_NODE, DOCUMENT_FRAGMENT_NODE, } from '../shared/HTMLNodeType'; import {ROOT_ATTRIBUTE_NAME} from '../shared/DOMProperty'; const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner; let topLevelUpdateWarnings; let warnOnInvalidCallback; let didWarnAboutUnstableCreatePortal = false; if (__DEV__) { if ( typeof Map !== 'function' || // $FlowIssue Flow incorrectly thinks Map has no prototype Map.prototype == null || typeof Map.prototype.forEach !== 'function' || typeof Set !== 'function' || // $FlowIssue Flow incorrectly thinks Set has no prototype Set.prototype == null || typeof Set.prototype.clear !== 'function' || typeof Set.prototype.forEach !== 'function' ) { warningWithoutStack( false, 'React depends on Map and Set built-in types. Make sure that you load a ' + 'polyfill in older browsers. https://fb.me/react-polyfills', ); } topLevelUpdateWarnings = (container: DOMContainer) => { if (container._reactRootContainer && container.nodeType !== COMMENT_NODE) { const hostInstance = findHostInstanceWithNoPortals( container._reactRootContainer._internalRoot.current, ); if (hostInstance) { warningWithoutStack( hostInstance.parentNode === container, 'render(...): It looks like the React-rendered content of this ' + 'container was removed without using React. This is not ' + 'supported and will cause errors. Instead, call ' + 'ReactDOM.unmountComponentAtNode to empty a container.', ); } } const isRootRenderedBySomeReact = !!container._reactRootContainer; const rootEl = getReactRootElementInContainer(container); const hasNonRootReactChild = !!(rootEl && getInstanceFromNode(rootEl)); warningWithoutStack( !hasNonRootReactChild || isRootRenderedBySomeReact, 'render(...): Replacing React-rendered children with a new root ' + 'component. If you intended to update the children of this node, ' + 'you should instead have the existing children update their state ' + 'and render the new components instead of calling ReactDOM.render.', ); warningWithoutStack( container.nodeType !== ELEMENT_NODE || !((container: any): Element).tagName || ((container: any): Element).tagName.toUpperCase() !== 'BODY', 'render(): Rendering components directly into document.body is ' + 'discouraged, since its children are often manipulated by third-party ' + 'scripts and browser extensions. This may lead to subtle ' + 'reconciliation issues. Try rendering into a container element created ' + 'for your app.', ); }; warnOnInvalidCallback = function(callback: mixed, callerName: string) { warningWithoutStack( callback === null || typeof callback === 'function', '%s(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callerName, callback, ); }; } setRestoreImplementation(restoreControlledState); export type DOMContainer = | (Element & { _reactRootContainer: ?Root, _reactHasBeenPassedToCreateRootDEV: ?boolean, }) | (Document & { _reactRootContainer: ?Root, _reactHasBeenPassedToCreateRootDEV: ?boolean, }); type Batch = FiberRootBatch & { render(children: ReactNodeList): Work, then(onComplete: () => mixed): void, commit(): void, // The ReactRoot constructor is hoisted but the prototype methods are not. If // we move ReactRoot to be above ReactBatch, the inverse error occurs. // $FlowFixMe Hoisting issue. _root: Root, _hasChildren: boolean, _children: ReactNodeList, _callbacks: Array<() => mixed> | null, _didComplete: boolean, }; type Root = { render(children: ReactNodeList, callback: ?() => mixed): Work, unmount(callback: ?() => mixed): Work, legacy_renderSubtreeIntoContainer( parentComponent: ?React$Component<any, any>, children: ReactNodeList, callback: ?() => mixed, ): Work, createBatch(): Batch, _internalRoot: FiberRoot, }; function ReactBatch(root: ReactRoot) { const expirationTime = computeUniqueAsyncExpiration(); this._expirationTime = expirationTime; this._root = root; this._next = null; this._callbacks = null; this._didComplete = false; this._hasChildren = false; this._children = null; this._defer = true; } ReactBatch.prototype.render = function(children: ReactNodeList) { invariant( this._defer, 'batch.render: Cannot render a batch that already committed.', ); this._hasChildren = true; this._children = children; const internalRoot = this._root._internalRoot; const expirationTime = this._expirationTime; const work = new ReactWork(); updateContainerAtExpirationTime( children, internalRoot, null, expirationTime, work._onCommit, ); return work; }; ReactBatch.prototype.then = function(onComplete: () => mixed) { if (this._didComplete) { onComplete(); return; } let callbacks = this._callbacks; if (callbacks === null) { callbacks = this._callbacks = []; } callbacks.push(onComplete); }; ReactBatch.prototype.commit = function() { const internalRoot = this._root._internalRoot; let firstBatch = internalRoot.firstBatch; invariant( this._defer && firstBatch !== null, 'batch.commit: Cannot commit a batch multiple times.', ); if (!this._hasChildren) { // This batch is empty. Return. this._next = null; this._defer = false; return; } let expirationTime = this._expirationTime; // Ensure this is the first batch in the list. if (firstBatch !== this) { // This batch is not the earliest batch. We need to move it to the front. // Update its expiration time to be the expiration time of the earliest // batch, so that we can flush it without flushing the other batches. if (this._hasChildren) { expirationTime = this._expirationTime = firstBatch._expirationTime; // Rendering this batch again ensures its children will be the final state // when we flush (updates are processed in insertion order: last // update wins). // TODO: This forces a restart. Should we print a warning? this.render(this._children); } // Remove the batch from the list. let previous = null; let batch = firstBatch; while (batch !== this) { previous = batch; batch = batch._next; } invariant( previous !== null, 'batch.commit: Cannot commit a batch multiple times.', ); previous._next = batch._next; // Add it to the front. this._next = firstBatch; firstBatch = internalRoot.firstBatch = this; } // Synchronously flush all the work up to this batch's expiration time. this._defer = false; flushRoot(internalRoot, expirationTime); // Pop the batch from the list. const next = this._next; this._next = null; firstBatch = internalRoot.firstBatch = next; // Append the next earliest batch's children to the update queue. if (firstBatch !== null && firstBatch._hasChildren) { firstBatch.render(firstBatch._children); } }; ReactBatch.prototype._onComplete = function() { if (this._didComplete) { return; } this._didComplete = true; const callbacks = this._callbacks; if (callbacks === null) { return; } // TODO: Error handling. for (let i = 0; i < callbacks.length; i++) { const callback = callbacks[i]; callback(); } }; type Work = { then(onCommit: () => mixed): void, _onCommit: () => void, _callbacks: Array<() => mixed> | null, _didCommit: boolean, }; function ReactWork() { this._callbacks = null; this._didCommit = false; // TODO: Avoid need to bind by replacing callbacks in the update queue with // list of Work objects. this._onCommit = this._onCommit.bind(this); } ReactWork.prototype.then = function(onCommit: () => mixed): void { if (this._didCommit) { onCommit(); return; } let callbacks = this._callbacks; if (callbacks === null) { callbacks = this._callbacks = []; } callbacks.push(onCommit); }; ReactWork.prototype._onCommit = function(): void { if (this._didCommit) { return; } this._didCommit = true; const callbacks = this._callbacks; if (callbacks === null) { return; } // TODO: Error handling. for (let i = 0; i < callbacks.length; i++) { const callback = callbacks[i]; invariant( typeof callback === 'function', 'Invalid argument passed as callback. Expected a function. Instead ' + 'received: %s', callback, ); callback(); } }; function ReactRoot( container: DOMContainer, isConcurrent: boolean, hydrate: boolean, ) { const root = createContainer(container, isConcurrent, hydrate); this._internalRoot = root; } ReactRoot.prototype.render = function( children: ReactNodeList, callback: ?() => mixed, ): Work { const root = this._internalRoot; const work = new ReactWork(); callback = callback === undefined ? null : callback; if (__DEV__) { warnOnInvalidCallback(callback, 'render'); } if (callback !== null) { work.then(callback); } updateContainer(children, root, null, work._onCommit); return work; }; ReactRoot.prototype.unmount = function(callback: ?() => mixed): Work { const root = this._internalRoot; const work = new ReactWork(); callback = callback === undefined ? null : callback; if (__DEV__) { warnOnInvalidCallback(callback, 'render'); } if (callback !== null) { work.then(callback); } updateContainer(null, root, null, work._onCommit); return work; }; ReactRoot.prototype.legacy_renderSubtreeIntoContainer = function( parentComponent: ?React$Component<any, any>, children: ReactNodeList, callback: ?() => mixed, ): Work { const root = this._internalRoot; const work = new ReactWork(); callback = callback === undefined ? null : callback; if (__DEV__) { warnOnInvalidCallback(callback, 'render'); } if (callback !== null) { work.then(callback); } updateContainer(children, root, parentComponent, work._onCommit); return work; }; ReactRoot.prototype.createBatch = function(): Batch { const batch = new ReactBatch(this); const expirationTime = batch._expirationTime; const internalRoot = this._internalRoot; const firstBatch = internalRoot.firstBatch; if (firstBatch === null) { internalRoot.firstBatch = batch; batch._next = null; } else { // Insert sorted by expiration time then insertion order let insertAfter = null; let insertBefore = firstBatch; while ( insertBefore !== null && insertBefore._expirationTime >= expirationTime ) { insertAfter = insertBefore; insertBefore = insertBefore._next; } batch._next = insertBefore; if (insertAfter !== null) { insertAfter._next = batch; } } return batch; }; /** * True if the supplied DOM node is a valid node element. * * @param {?DOMElement} node The candidate DOM node. * @return {boolean} True if the DOM is a valid DOM node. * @internal */ function isValidContainer(node) { return !!( node && (node.nodeType === ELEMENT_NODE || node.nodeType === DOCUMENT_NODE || node.nodeType === DOCUMENT_FRAGMENT_NODE || (node.nodeType === COMMENT_NODE && node.nodeValue === ' react-mount-point-unstable ')) ); } function getReactRootElementInContainer(container: any) { if (!container) { return null; } if (container.nodeType === DOCUMENT_NODE) { return container.documentElement; } else { return container.firstChild; } } function shouldHydrateDueToLegacyHeuristic(container) { const rootElement = getReactRootElementInContainer(container); return !!( rootElement && rootElement.nodeType === ELEMENT_NODE && rootElement.hasAttribute(ROOT_ATTRIBUTE_NAME) ); } setBatchingImplementation( batchedUpdates, interactiveUpdates, flushInteractiveUpdates, ); let warnedAboutHydrateAPI = false; function legacyCreateRootFromDOMContainer( container: DOMContainer, forceHydrate: boolean, ): Root { const shouldHydrate = forceHydrate || shouldHydrateDueToLegacyHeuristic(container); // First clear any existing content. if (!shouldHydrate) { let warned = false; let rootSibling; while ((rootSibling = container.lastChild)) { if (__DEV__) { if ( !warned && rootSibling.nodeType === ELEMENT_NODE && (rootSibling: any).hasAttribute(ROOT_ATTRIBUTE_NAME) ) { warned = true; warningWithoutStack( false, 'render(): Target node has markup rendered by React, but there ' + 'are unrelated nodes as well. This is most commonly caused by ' + 'white-space inserted around server-rendered markup.', ); } } container.removeChild(rootSibling); } } if (__DEV__) { if (shouldHydrate && !forceHydrate && !warnedAboutHydrateAPI) { warnedAboutHydrateAPI = true; lowPriorityWarning( false, 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' + 'will stop working in React v17. Replace the ReactDOM.render() call ' + 'with ReactDOM.hydrate() if you want React to attach to the server HTML.', ); } } // Legacy roots are not async by default. const isConcurrent = false; return new ReactRoot(container, isConcurrent, shouldHydrate); } function legacyRenderSubtreeIntoContainer( parentComponent: ?React$Component<any, any>, children: ReactNodeList, container: DOMContainer, forceHydrate: boolean, callback: ?Function, ) { if (__DEV__) { topLevelUpdateWarnings(container); } // TODO: Without `any` type, Flow says "Property cannot be accessed on any // member of intersection type." Whyyyyyy. let root: Root = (container._reactRootContainer: any); if (!root) { // Initial mount root = container._reactRootContainer = legacyCreateRootFromDOMContainer( container, forceHydrate, ); if (typeof callback === 'function') { const originalCallback = callback; callback = function() { const instance = getPublicRootInstance(root._internalRoot); originalCallback.call(instance); }; } // Initial mount should not be batched. unbatchedUpdates(() => { if (parentComponent != null) { root.legacy_renderSubtreeIntoContainer( parentComponent, children, callback, ); } else { root.render(children, callback); } }); } else { if (typeof callback === 'function') { const originalCallback = callback; callback = function() { const instance = getPublicRootInstance(root._internalRoot); originalCallback.call(instance); }; } // Update if (parentComponent != null) { root.legacy_renderSubtreeIntoContainer( parentComponent, children, callback, ); } else { root.render(children, callback); } } return getPublicRootInstance(root._internalRoot); } function createPortal( children: ReactNodeList, container: DOMContainer, key: ?string = null, ) { invariant( isValidContainer(container), 'Target container is not a DOM element.', ); // TODO: pass ReactDOM portal implementation as third argument return createPortalImpl(children, container, null, key); } const ReactDOM: Object = { createPortal, findDOMNode( componentOrElement: Element | ?React$Component<any, any>, ): null | Element | Text { if (__DEV__) { let owner = (ReactCurrentOwner.current: any); if (owner !== null && owner.stateNode !== null) { const warnedAboutRefsInRender = owner.stateNode._warnedAboutRefsInRender; warningWithoutStack( warnedAboutRefsInRender, '%s is accessing findDOMNode inside its render(). ' + 'render() should be a pure function of props and state. It should ' + 'never access something that requires stale data from the previous ' + 'render, such as refs. Move this logic to componentDidMount and ' + 'componentDidUpdate instead.', getComponentName(owner.type) || 'A component', ); owner.stateNode._warnedAboutRefsInRender = true; } } if (componentOrElement == null) { return null; } if ((componentOrElement: any).nodeType === ELEMENT_NODE) { return (componentOrElement: any); } if (__DEV__) { return findHostInstanceWithWarning(componentOrElement, 'findDOMNode'); } return findHostInstance(componentOrElement); }, hydrate(element: React$Node, container: DOMContainer, callback: ?Function) { invariant( isValidContainer(container), 'Target container is not a DOM element.', ); if (__DEV__) { warningWithoutStack( !container._reactHasBeenPassedToCreateRootDEV, 'You are calling ReactDOM.hydrate() on a container that was previously ' + 'passed to ReactDOM.%s(). This is not supported. ' + 'Did you mean to call createRoot(container, {hydrate: true}).render(element)?', enableStableConcurrentModeAPIs ? 'createRoot' : 'unstable_createRoot', ); } // TODO: throw or warn if we couldn't hydrate? return legacyRenderSubtreeIntoContainer( null, element, container, true, callback, ); }, render( element: React$Element<any>, container: DOMContainer, callback: ?Function, ) { invariant( isValidContainer(container), 'Target container is not a DOM element.', ); if (__DEV__) { warningWithoutStack( !container._reactHasBeenPassedToCreateRootDEV, 'You are calling ReactDOM.render() on a container that was previously ' + 'passed to ReactDOM.%s(). This is not supported. ' + 'Did you mean to call root.render(element)?', enableStableConcurrentModeAPIs ? 'createRoot' : 'unstable_createRoot', ); } return legacyRenderSubtreeIntoContainer( null, element, container, false, callback, ); }, unstable_renderSubtreeIntoContainer( parentComponent: React$Component<any, any>, element: React$Element<any>, containerNode: DOMContainer, callback: ?Function, ) { invariant( isValidContainer(containerNode), 'Target container is not a DOM element.', ); invariant( parentComponent != null && hasInstance(parentComponent), 'parentComponent must be a valid React Component', ); return legacyRenderSubtreeIntoContainer( parentComponent, element, containerNode, false, callback, ); }, unmountComponentAtNode(container: DOMContainer) { invariant( isValidContainer(container), 'unmountComponentAtNode(...): Target container is not a DOM element.', ); if (__DEV__) { warningWithoutStack( !container._reactHasBeenPassedToCreateRootDEV, 'You are calling ReactDOM.unmountComponentAtNode() on a container that was previously ' + 'passed to ReactDOM.%s(). This is not supported. Did you mean to call root.unmount()?', enableStableConcurrentModeAPIs ? 'createRoot' : 'unstable_createRoot', ); } if (container._reactRootContainer) { if (__DEV__) { const rootEl = getReactRootElementInContainer(container); const renderedByDifferentReact = rootEl && !getInstanceFromNode(rootEl); warningWithoutStack( !renderedByDifferentReact, "unmountComponentAtNode(): The node you're attempting to unmount " + 'was rendered by another copy of React.', ); } // Unmount should not be batched. unbatchedUpdates(() => { legacyRenderSubtreeIntoContainer(null, null, container, false, () => { container._reactRootContainer = null; }); }); // If you call unmountComponentAtNode twice in quick succession, you'll // get `true` twice. That's probably fine? return true; } else { if (__DEV__) { const rootEl = getReactRootElementInContainer(container); const hasNonRootReactChild = !!(rootEl && getInstanceFromNode(rootEl)); // Check if the container itself is a React root node. const isContainerReactRoot = container.nodeType === ELEMENT_NODE && isValidContainer(container.parentNode) && !!container.parentNode._reactRootContainer; warningWithoutStack( !hasNonRootReactChild, "unmountComponentAtNode(): The node you're attempting to unmount " + 'was rendered by React and is not a top-level container. %s', isContainerReactRoot ? 'You may have accidentally passed in a React root node instead ' + 'of its container.' : 'Instead, have the parent component update its state and ' + 'rerender in order to remove this component.', ); } return false; } }, // Temporary alias since we already shipped React 16 RC with it. // TODO: remove in React 17. unstable_createPortal(...args) { if (!didWarnAboutUnstableCreatePortal) { didWarnAboutUnstableCreatePortal = true; lowPriorityWarning( false, 'The ReactDOM.unstable_createPortal() alias has been deprecated, ' + 'and will be removed in React 17+. Update your code to use ' + 'ReactDOM.createPortal() instead. It has the exact same API, ' + 'but without the "unstable_" prefix.', ); } return createPortal(...args); }, unstable_batchedUpdates: batchedUpdates, unstable_interactiveUpdates: interactiveUpdates, flushSync: flushSync, unstable_createRoot: createRoot, unstable_flushControlled: flushControlled, __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: { // Keep in sync with ReactDOMUnstableNativeDependencies.js // and ReactTestUtils.js. This is an array for better minification. Events: [ getInstanceFromNode, getNodeFromInstance, getFiberCurrentPropsFromNode, EventPluginHubInjection.injectEventPluginsByName, eventNameDispatchConfigs, accumulateTwoPhaseDispatches, accumulateDirectDispatches, enqueueStateRestore, restoreStateIfNeeded, dispatchEvent, runEventsInBatch, ], }, }; type RootOptions = { hydrate?: boolean, }; function createRoot(container: DOMContainer, options?: RootOptions): ReactRoot { const functionName = enableStableConcurrentModeAPIs ? 'createRoot' : 'unstable_createRoot'; invariant( isValidContainer(container), '%s(...): Target container is not a DOM element.', functionName, ); if (__DEV__) { warningWithoutStack( !container._reactRootContainer, 'You are calling ReactDOM.%s() on a container that was previously ' + 'passed to ReactDOM.render(). This is not supported.', enableStableConcurrentModeAPIs ? 'createRoot' : 'unstable_createRoot', ); container._reactHasBeenPassedToCreateRootDEV = true; } const hydrate = options != null && options.hydrate === true; return new ReactRoot(container, true, hydrate); } if (enableStableConcurrentModeAPIs) { ReactDOM.createRoot = createRoot; ReactDOM.unstable_createRoot = undefined; } const foundDevTools = injectIntoDevTools({ findFiberByHostInstance: getClosestInstanceFromNode, bundleType: __DEV__ ? 1 : 0, version: ReactVersion, rendererPackageName: 'react-dom', }); if (__DEV__) { if (!foundDevTools && canUseDOM && window.top === window.self) { // If we're in Chrome or Firefox, provide a download link if not installed. if ( (navigator.userAgent.indexOf('Chrome') > -1 && navigator.userAgent.indexOf('Edge') === -1) || navigator.userAgent.indexOf('Firefox') > -1 ) { const protocol = window.location.protocol; // Don't warn in exotic cases like chrome-extension://. if (/^(https?|file):$/.test(protocol)) { console.info( '%cDownload the React DevTools ' + 'for a better development experience: ' + 'https://fb.me/react-devtools' + (protocol === 'file:' ? '\nYou might need to use a local HTTP server (instead of file://): ' + 'https://fb.me/react-devtools-faq' : ''), 'font-weight:bold', ); } } } } export default ReactDOM;