melody-devtools
Advanced tools
Comparing version 1.2.0-commit.f9b4ed0d to 1.2.0-f61c830.4
438
lib/index.js
@@ -1,437 +0,1 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
var melodyIdom = require('melody-idom'); | ||
/** | ||
* Copyright 2017 trivago N.V. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS-IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
/** | ||
* Return a ReactElement-compatible object for the current state of a preact | ||
* component. | ||
*/ | ||
function createReactElement(component) { | ||
if (!component.constructor.displayName) { | ||
component.constructor.displayName = component.displayName; | ||
} | ||
return { | ||
type: component.constructor, | ||
key: component.el && melodyIdom.getNodeData(component.el).key, | ||
ref: null, // Unsupported | ||
props: component.props || component.ownProps | ||
}; | ||
} | ||
/** | ||
* 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) { | ||
var childNodes = node.nodeType === Node.ELEMENT_NODE ? Array.from(node.childNodes) : []; | ||
var isText = node.nodeType === Node.TEXT_NODE; | ||
return { | ||
// --- ReactDOMComponent interface | ||
_currentElement: isText ? node.textContent : { | ||
type: node.nodeName.toLowerCase() | ||
}, | ||
_renderedChildren: childNodes.map(function (child) { | ||
var nodeData = melodyIdom.getNodeData(child); | ||
if (nodeData && nodeData.componentInstance) { | ||
return updateReactComponent(nodeData.componentInstance); | ||
} | ||
return updateReactComponent(child); | ||
}), | ||
_stringText: isText ? node.textContent : null, | ||
publicInstance: node, | ||
// --- 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: node | ||
}; | ||
} | ||
/** | ||
* 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) { | ||
var _currentElement = createReactElement(component); | ||
var node = component.el; | ||
var instance = { | ||
// --- ReactDOMComponent properties | ||
name: component.displayName, | ||
getName: function getName() { | ||
return component.displayName; | ||
}, | ||
_currentElement: _currentElement, | ||
props: component.props || component.ownProps, | ||
state: component.state || component.renderProps, | ||
// forceUpdate: () => { | ||
// enqueueComponent(component); | ||
// }, | ||
// setState: (state) => { | ||
// if (component.state) { | ||
// Object.assign(component.state, state); | ||
// enqueueComponent(component); | ||
// } | ||
// }, | ||
// --- Additional properties used by preact devtools | ||
node: 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; | ||
instance._instance = { | ||
get props() { | ||
return component.props || component.ownProps; | ||
}, | ||
get state() { | ||
return component.state || component.renderProps; | ||
}, | ||
get refs() { | ||
return component.refs; | ||
}, | ||
setState: function setState(state) { | ||
Object.assign(component.state, state); | ||
}, | ||
forceUpdate: function forceUpdate() { | ||
melodyIdom.enqueueComponent(component); | ||
}, | ||
dispatch: function dispatch(action) { | ||
if (component.dispatch) { | ||
component.dispatch(action); | ||
} | ||
} | ||
}; | ||
// 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.childInstance) { | ||
instance._renderedComponent = updateReactComponent(component.childInstance); | ||
} else if (node) { | ||
// 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. | ||
*/ | ||
var instanceMap = typeof Map === 'function' && 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) { | ||
var newInstance = componentOrNode instanceof Node ? createReactDOMComponent(componentOrNode) : createReactCompositeComponent(componentOrNode); | ||
if (instanceMap.has(componentOrNode)) { | ||
var 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(function (child) { | ||
var nodeData = melodyIdom.getNodeData(child); | ||
if (nodeData && nodeData.componentInstance) { | ||
roots[nextRootKey(roots)] = updateReactComponent(nodeData.componentInstance); | ||
} else { | ||
findRoots(child, roots); | ||
} | ||
}); | ||
} | ||
/** | ||
* 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 | ||
var ComponentTree = { | ||
getNodeFromInstance: function getNodeFromInstance(instance) { | ||
return instance.node; | ||
}, | ||
getClosestInstanceFromNode: function getClosestInstanceFromNode(rootNode) { | ||
var node = rootNode; | ||
while (node && !melodyIdom.getNodeData(node).componentInstance) { | ||
node = node.parentNode; | ||
} | ||
return node ? updateReactComponent(melodyIdom.getNodeData(node).componentInstance) : null; | ||
} | ||
}; | ||
// Map of root ID (the ID is unimportant) to component instance. | ||
var 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. | ||
var 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: function _renderNewRootComponent() /* instance, ... */{} | ||
}; | ||
// ReactReconciler-like object | ||
var 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: function mountComponent() /* instance, ... */{}, | ||
performUpdateIfNecessary: function performUpdateIfNecessary() /* instance, ... */{}, | ||
receiveComponent: function receiveComponent() /* instance, ... */{}, | ||
unmountComponent: function unmountComponent() /* instance, ... */{} | ||
}; | ||
/** Notify devtools that a new component instance has been mounted into the DOM. */ | ||
var componentAdded = function componentAdded(component) { | ||
var instance = updateReactComponent(component); | ||
if (isRootComponent(component)) { | ||
instance._rootID = nextRootKey(roots); | ||
roots[instance._rootID] = instance; | ||
Mount._renderNewRootComponent(instance); | ||
} | ||
visitNonCompositeChildren(instance, function (childInst) { | ||
childInst._inDevTools = true; | ||
Reconciler.mountComponent(childInst); | ||
}); | ||
Reconciler.mountComponent(instance); | ||
}; | ||
/** Notify devtools that a component has been updated with new props/state. */ | ||
var componentUpdated = function componentUpdated(component) { | ||
var prevRenderedChildren = []; | ||
visitNonCompositeChildren(instanceMap.get(component), function (childInst) { | ||
prevRenderedChildren.push(childInst); | ||
}); | ||
// Notify devtools about updates to this component and any non-composite | ||
// children | ||
var instance = updateReactComponent(component); | ||
Reconciler.receiveComponent(instance); | ||
visitNonCompositeChildren(instance, function (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(function (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. */ | ||
var componentRemoved = function componentRemoved(component) { | ||
var instance = updateReactComponent(component); | ||
visitNonCompositeChildren(function (childInst) { | ||
instanceMap.delete(childInst.node); | ||
Reconciler.unmountComponent(childInst); | ||
}); | ||
Reconciler.unmountComponent(instance); | ||
instanceMap.delete(component); | ||
if (instance._rootID) { | ||
delete roots[instance._rootID]; | ||
} | ||
}; | ||
return { | ||
componentAdded: componentAdded, | ||
componentUpdated: componentUpdated, | ||
componentRemoved: componentRemoved, | ||
// Interfaces passed to devtools via __REACT_DEVTOOLS_GLOBAL_HOOK__.inject() | ||
ComponentTree: ComponentTree, | ||
Mount: Mount, | ||
Reconciler: Reconciler | ||
}; | ||
} | ||
/** | ||
* Return `true` if a preact component is a top level component rendered by | ||
* `render()` into a container Element. | ||
*/ | ||
function isRootComponent(component) { | ||
var parentInstance = melodyIdom.getParent(component); | ||
if (parentInstance === undefined) { | ||
return true; | ||
} else if (parentInstance.childInstance !== undefined) { | ||
return isRootComponent(parentInstance); | ||
} | ||
return false; | ||
} | ||
/** | ||
* 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.componentInstance) { | ||
visitor(component._renderedComponent); | ||
visitNonCompositeChildren(component._renderedComponent, visitor); | ||
} | ||
} else if (component._renderedChildren) { | ||
component._renderedChildren.forEach(function (child) { | ||
visitor(child); | ||
if (!child.componentInstance) { | ||
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. | ||
*/ | ||
function initDevTools() { | ||
if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') { | ||
// React DevTools are not installed | ||
return; | ||
} | ||
// Notify devtools when preact components are mounted, updated or unmounted | ||
var bridge = createDevToolsBridge(); | ||
var nextAfterMount = melodyIdom.options.afterMount; | ||
melodyIdom.options.afterMount = function (component) { | ||
bridge.componentAdded(component); | ||
if (nextAfterMount) { | ||
nextAfterMount(component); | ||
} | ||
}; | ||
var nextAfterUpdate = melodyIdom.options.afterUpdate; | ||
melodyIdom.options.afterUpdate = function (component) { | ||
bridge.componentUpdated(component); | ||
if (nextAfterUpdate) { | ||
nextAfterUpdate(component); | ||
} | ||
}; | ||
var nextBeforeUnmount = melodyIdom.options.beforeUnmount; | ||
melodyIdom.options.beforeUnmount = function (component) { | ||
bridge.componentRemoved(component); | ||
if (nextBeforeUnmount) { | ||
nextBeforeUnmount(component); | ||
} | ||
}; | ||
// Notify devtools about this instance of "React" | ||
__REACT_DEVTOOLS_GLOBAL_HOOK__.inject(bridge); | ||
return function () { | ||
melodyIdom.options.afterMount = nextAfterMount; | ||
melodyIdom.options.afterUpdate = nextAfterUpdate; | ||
melodyIdom.options.beforeUnmount = nextBeforeUnmount; | ||
}; | ||
} | ||
exports.initDevTools = initDevTools; | ||
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var c=require("melody-idom");function r(n){return n.constructor.displayName||(n.constructor.displayName=n.displayName),{type:n.constructor,key:n.el&&c.getNodeData(n.el).key,ref:null,props:n.props||n.ownProps}}function t(n){var e=n.nodeType===Node.ELEMENT_NODE?Array.from(n.childNodes):[],o=n.nodeType===Node.TEXT_NODE;return{_currentElement:o?n.textContent:{type:n.nodeName.toLowerCase()},_renderedChildren:e.map(function(n){var e=c.getNodeData(n);return e&&e.componentInstance?p(e.componentInstance):p(n)}),_stringText:o?n.textContent:null,publicInstance:n,_inDevTools:!1,node:n}}function a(e){var n=r(e),o=e.el,t={name:e.displayName,getName:function(){return e.displayName},_currentElement:n,props:e.props||e.ownProps,state:e.state||e.renderProps,node:o};return t._instance={get props(){return e.props||e.ownProps},get state(){return e.state||e.renderProps},get refs(){return e.refs},setState:function(n){Object.assign(e.state,n)},forceUpdate:function(){c.enqueueComponent(e)},dispatch:function(n){e.dispatch&&e.dispatch(n)}},e.childInstance?t._renderedComponent=p(e.childInstance):o&&(t._renderedComponent=p(o)),t}var d="function"==typeof Map&&new Map;function p(n){var e=n instanceof Node?t(n):a(n);if(d.has(n)){var o=d.get(n);return Object.assign(o,e),o}return d.set(n,e),e}function u(n){return"."+Object.keys(n).length}function i(n,o){Array.from(n.childNodes).forEach(function(n){var e=c.getNodeData(n);e&&e.componentInstance?o[u(o)]=p(e.componentInstance):i(n,o)})}function n(){var n={getNodeFromInstance:function(n){return n.node},getClosestInstanceFromNode:function(n){for(var e=n;e&&!c.getNodeData(e).componentInstance;)e=e.parentNode;return e?p(c.getNodeData(e).componentInstance):null}},o={};i(document.body,o);var t={_instancesByReactRootID:o,_renderNewRootComponent:function(){}},r={mountComponent:function(){},performUpdateIfNecessary:function(){},receiveComponent:function(){},unmountComponent:function(){}};return{componentAdded:function(n){var e=p(n);s(n)&&(e._rootID=u(o),o[e._rootID]=e,t._renderNewRootComponent(e)),m(e,function(n){n._inDevTools=!0,r.mountComponent(n)}),r.mountComponent(e)},componentUpdated:function(n){var e=[];m(d.get(n),function(n){e.push(n)});var o=p(n);r.receiveComponent(o),m(o,function(n){n._inDevTools?r.receiveComponent(n):(n._inDevTools=!0,r.mountComponent(n))}),e.forEach(function(n){document.body.contains(n.node)||(d.delete(n.node),r.unmountComponent(n))})},componentRemoved:function(n){var e=p(n);m(function(n){d.delete(n.node),r.unmountComponent(n)}),r.unmountComponent(e),d.delete(n),e._rootID&&delete o[e._rootID]},ComponentTree:n,Mount:t,Reconciler:r}}function s(n){var e=c.getParent(n);return void 0===e||void 0!==e.childInstance&&s(e)}function m(n,e){n._renderedComponent?n._renderedComponent.componentInstance||(e(n._renderedComponent),m(n._renderedComponent,e)):n._renderedChildren&&n._renderedChildren.forEach(function(n){e(n),n.componentInstance||m(n,e)})}function e(){if("undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__){var e=n(),o=c.options.afterMount;c.options.afterMount=function(n){e.componentAdded(n),o&&o(n)};var t=c.options.afterUpdate;c.options.afterUpdate=function(n){e.componentUpdated(n),t&&t(n)};var r=c.options.beforeUnmount;return c.options.beforeUnmount=function(n){e.componentRemoved(n),r&&r(n)},__REACT_DEVTOOLS_GLOBAL_HOOK__.inject(e),function(){c.options.afterMount=o,c.options.afterUpdate=t,c.options.beforeUnmount=r}}}exports.initDevTools=e; |
{ | ||
"name": "melody-devtools", | ||
"version": "1.2.0-commit.f9b4ed0d", | ||
"version": "1.2.0-f61c830.4+f61c830", | ||
"description": "", | ||
@@ -16,5 +16,3 @@ "main": "./lib/index.js", | ||
}, | ||
"devDependencies": { | ||
"rollup-plugin-babel": "^2.6.1" | ||
} | ||
"gitHead": "f61c8303bc847d41f4005c1c969510332ece7d1d" | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
0
5
44217
803
3
1