diff options
Diffstat (limited to 'thirdparty/preact/devtools/devtools.js')
-rw-r--r-- | thirdparty/preact/devtools/devtools.js | 427 |
1 files changed, 0 insertions, 427 deletions
diff --git a/thirdparty/preact/devtools/devtools.js b/thirdparty/preact/devtools/devtools.js deleted file mode 100644 index 4bcbeae1e..000000000 --- a/thirdparty/preact/devtools/devtools.js +++ /dev/null @@ -1,427 +0,0 @@ -/* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */ - -import { options, Component } from 'preact'; - -// Internal helpers from preact -import { ATTR_KEY } from '../src/constants'; -import { isFunctionalComponent } from '../src/vdom/functional-component'; - -/** - * Return a ReactElement-compatible object for the current state of a preact - * component. - */ -function createReactElement(component) { - return { - type: component.constructor, - key: component.key, - ref: null, // Unsupported - props: component.props - }; -} - -/** - * Create a ReactDOMComponent-compatible object for a given DOM node rendered - * by preact. - * - * This implements the subset of the ReactDOMComponent interface that - * React DevTools requires in order to display DOM nodes in the inspector with - * the correct type and properties. - * - * @param {Node} node - */ -function createReactDOMComponent(node) { - const childNodes = node.nodeType === Node.ELEMENT_NODE ? - Array.from(node.childNodes) : []; - - const isText = node.nodeType === Node.TEXT_NODE; - - return { - // --- ReactDOMComponent interface - _currentElement: isText ? node.textContent : { - type: node.nodeName.toLowerCase(), - props: node[ATTR_KEY] - }, - _renderedChildren: childNodes.map(child => { - if (child._component) { - return updateReactComponent(child._component); - } - return updateReactComponent(child); - }), - _stringText: isText ? node.textContent : null, - - // --- Additional properties used by preact devtools - - // A flag indicating whether the devtools have been notified about the - // existence of this component instance yet. - // This is used to send the appropriate notifications when DOM components - // are added or updated between composite component updates. - _inDevTools: false, - node - }; -} - -/** - * Return the name of a component created by a `ReactElement`-like object. - * - * @param {ReactElement} element - */ -function typeName(element) { - if (typeof element.type === 'function') { - return element.type.displayName || element.type.name; - } - return element.type; -} - -/** - * Return a ReactCompositeComponent-compatible object for a given preact - * component instance. - * - * This implements the subset of the ReactCompositeComponent interface that - * the DevTools requires in order to walk the component tree and inspect the - * component's properties. - * - * See https://github.com/facebook/react-devtools/blob/e31ec5825342eda570acfc9bcb43a44258fceb28/backend/getData.js - */ -function createReactCompositeComponent(component) { - const _currentElement = createReactElement(component); - const node = component.base; - - let instance = { - // --- ReactDOMComponent properties - getName() { - return typeName(_currentElement); - }, - _currentElement: createReactElement(component), - props: component.props, - state: component.state, - forceUpdate: component.forceUpdate.bind(component), - setState: component.setState.bind(component), - - // --- Additional properties used by preact devtools - node - }; - - // React DevTools exposes the `_instance` field of the selected item in the - // component tree as `$r` in the console. `_instance` must refer to a - // React Component (or compatible) class instance with `props` and `state` - // fields and `setState()`, `forceUpdate()` methods. - instance._instance = component; - - // If the root node returned by this component instance's render function - // was itself a composite component, there will be a `_component` property - // containing the child component instance. - if (component._component) { - instance._renderedComponent = updateReactComponent(component._component); - } else { - // Otherwise, if the render() function returned an HTML/SVG element, - // create a ReactDOMComponent-like object for the DOM node itself. - instance._renderedComponent = updateReactComponent(node); - } - - return instance; -} - -/** - * Map of Component|Node to ReactDOMComponent|ReactCompositeComponent-like - * object. - * - * The same React*Component instance must be used when notifying devtools - * about the initial mount of a component and subsequent updates. - */ -let instanceMap = new Map(); - -/** - * Update (and create if necessary) the ReactDOMComponent|ReactCompositeComponent-like - * instance for a given preact component instance or DOM Node. - * - * @param {Component|Node} componentOrNode - */ -function updateReactComponent(componentOrNode) { - const newInstance = componentOrNode instanceof Node ? - createReactDOMComponent(componentOrNode) : - createReactCompositeComponent(componentOrNode); - if (instanceMap.has(componentOrNode)) { - let inst = instanceMap.get(componentOrNode); - Object.assign(inst, newInstance); - return inst; - } - instanceMap.set(componentOrNode, newInstance); - return newInstance; -} - -function nextRootKey(roots) { - return '.' + Object.keys(roots).length; -} - -/** - * Find all root component instances rendered by preact in `node`'s children - * and add them to the `roots` map. - * - * @param {DOMElement} node - * @param {[key: string] => ReactDOMComponent|ReactCompositeComponent} - */ -function findRoots(node, roots) { - Array.from(node.childNodes).forEach(child => { - if (child._component) { - roots[nextRootKey(roots)] = updateReactComponent(child._component); - } else { - findRoots(child, roots); - } - }); -} - -/** - * Map of functional component name -> wrapper class. - */ -let functionalComponentWrappers = new Map(); - -/** - * Wrap a functional component with a stateful component. - * - * preact does not record any information about the original hierarchy of - * functional components in the rendered DOM nodes. Wrapping functional components - * with a trivial wrapper allows us to recover information about the original - * component structure from the DOM. - * - * @param {VNode} vnode - */ -function wrapFunctionalComponent(vnode) { - const originalRender = vnode.nodeName; - const name = vnode.nodeName.name || '(Function.name missing)'; - const wrappers = functionalComponentWrappers; - if (!wrappers.has(originalRender)) { - let wrapper = class extends Component { - render(props, state, context) { - return originalRender(props, context); - } - }; - - // Expose the original component name. React Dev Tools will use - // this property if it exists or fall back to Function.name - // otherwise. - wrapper.displayName = name; - - wrappers.set(originalRender, wrapper); - } - vnode.nodeName = wrappers.get(originalRender); -} - -/** - * Create a bridge for exposing preact's component tree to React DevTools. - * - * It creates implementations of the interfaces that ReactDOM passes to - * devtools to enable it to query the component tree and hook into component - * updates. - * - * See https://github.com/facebook/react/blob/59ff7749eda0cd858d5ee568315bcba1be75a1ca/src/renderers/dom/ReactDOM.js - * for how ReactDOM exports its internals for use by the devtools and - * the `attachRenderer()` function in - * https://github.com/facebook/react-devtools/blob/e31ec5825342eda570acfc9bcb43a44258fceb28/backend/attachRenderer.js - * for how the devtools consumes the resulting objects. - */ -function createDevToolsBridge() { - // The devtools has different paths for interacting with the renderers from - // React Native, legacy React DOM and current React DOM. - // - // Here we emulate the interface for the current React DOM (v15+) lib. - - // ReactDOMComponentTree-like object - const ComponentTree = { - getNodeFromInstance(instance) { - return instance.node; - }, - getClosestInstanceFromNode(node) { - while (node && !node._component) { - node = node.parentNode; - } - return node ? updateReactComponent(node._component) : null; - } - }; - - // Map of root ID (the ID is unimportant) to component instance. - let roots = {}; - findRoots(document.body, roots); - - // ReactMount-like object - // - // Used by devtools to discover the list of root component instances and get - // notified when new root components are rendered. - const Mount = { - _instancesByReactRootID: roots, - - // Stub - React DevTools expects to find this method and replace it - // with a wrapper in order to observe new root components being added - _renderNewRootComponent(/* instance, ... */) { } - }; - - // ReactReconciler-like object - const Reconciler = { - // Stubs - React DevTools expects to find these methods and replace them - // with wrappers in order to observe components being mounted, updated and - // unmounted - mountComponent(/* instance, ... */) { }, - performUpdateIfNecessary(/* instance, ... */) { }, - receiveComponent(/* instance, ... */) { }, - unmountComponent(/* instance, ... */) { } - }; - - /** Notify devtools that a new component instance has been mounted into the DOM. */ - const componentAdded = component => { - const instance = updateReactComponent(component); - if (isRootComponent(component)) { - instance._rootID = nextRootKey(roots); - roots[instance._rootID] = instance; - Mount._renderNewRootComponent(instance); - } - visitNonCompositeChildren(instance, childInst => { - childInst._inDevTools = true; - Reconciler.mountComponent(childInst); - }); - Reconciler.mountComponent(instance); - }; - - /** Notify devtools that a component has been updated with new props/state. */ - const componentUpdated = component => { - const prevRenderedChildren = []; - visitNonCompositeChildren(instanceMap.get(component), childInst => { - prevRenderedChildren.push(childInst); - }); - - // Notify devtools about updates to this component and any non-composite - // children - const instance = updateReactComponent(component); - Reconciler.receiveComponent(instance); - visitNonCompositeChildren(instance, childInst => { - if (!childInst._inDevTools) { - // New DOM child component - childInst._inDevTools = true; - Reconciler.mountComponent(childInst); - } else { - // Updated DOM child component - Reconciler.receiveComponent(childInst); - } - }); - - // For any non-composite children that were removed by the latest render, - // remove the corresponding ReactDOMComponent-like instances and notify - // the devtools - prevRenderedChildren.forEach(childInst => { - if (!document.body.contains(childInst.node)) { - instanceMap.delete(childInst.node); - Reconciler.unmountComponent(childInst); - } - }); - }; - - /** Notify devtools that a component has been unmounted from the DOM. */ - const componentRemoved = component => { - const instance = updateReactComponent(component); - visitNonCompositeChildren(childInst => { - instanceMap.delete(childInst.node); - Reconciler.unmountComponent(childInst); - }); - Reconciler.unmountComponent(instance); - instanceMap.delete(component); - if (instance._rootID) { - delete roots[instance._rootID]; - } - }; - - return { - componentAdded, - componentUpdated, - componentRemoved, - - // Interfaces passed to devtools via __REACT_DEVTOOLS_GLOBAL_HOOK__.inject() - ComponentTree, - Mount, - Reconciler - }; -} - -/** - * Return `true` if a preact component is a top level component rendered by - * `render()` into a container Element. - */ -function isRootComponent(component) { - return !component.base.parentElement || !component.base.parentElement[ATTR_KEY]; -} - -/** - * Visit all child instances of a ReactCompositeComponent-like object that are - * not composite components (ie. they represent DOM elements or text) - * - * @param {Component} component - * @param {(Component) => void} visitor - */ -function visitNonCompositeChildren(component, visitor) { - if (component._renderedComponent) { - if (!component._renderedComponent._component) { - visitor(component._renderedComponent); - visitNonCompositeChildren(component._renderedComponent, visitor); - } - } else if (component._renderedChildren) { - component._renderedChildren.forEach(child => { - visitor(child); - if (!child._component) visitNonCompositeChildren(child, visitor); - }); - } -} - -/** - * Create a bridge between the preact component tree and React's dev tools - * and register it. - * - * After this function is called, the React Dev Tools should be able to detect - * "React" on the page and show the component tree. - * - * This function hooks into preact VNode creation in order to expose functional - * components correctly, so it should be called before the root component(s) - * are rendered. - * - * Returns a cleanup function which unregisters the hooks. - */ -export function initDevTools() { - if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') { - // React DevTools are not installed - return; - } - - // Hook into preact element creation in order to wrap functional components - // with stateful ones in order to make them visible in the devtools - const nextVNode = options.vnode; - options.vnode = (vnode) => { - if (isFunctionalComponent(vnode)) wrapFunctionalComponent(vnode); - if (nextVNode) return nextVNode(vnode); - }; - - // Notify devtools when preact components are mounted, updated or unmounted - const bridge = createDevToolsBridge(); - - const nextAfterMount = options.afterMount; - options.afterMount = component => { - bridge.componentAdded(component); - if (nextAfterMount) nextAfterMount(component); - }; - - const nextAfterUpdate = options.afterUpdate; - options.afterUpdate = component => { - bridge.componentUpdated(component); - if (nextAfterUpdate) nextAfterUpdate(component); - }; - - const nextBeforeUnmount = options.beforeUnmount; - options.beforeUnmount = component => { - bridge.componentRemoved(component); - if (nextBeforeUnmount) nextBeforeUnmount(component); - }; - - // Notify devtools about this instance of "React" - __REACT_DEVTOOLS_GLOBAL_HOOK__.inject(bridge); - - return () => { - options.afterMount = nextAfterMount; - options.afterUpdate = nextAfterUpdate; - options.beforeUnmount = nextBeforeUnmount; - }; -} |