Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-render-hook

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-render-hook - npm Package Compare versions

Comparing version 0.1.4 to 1.0.0

lib/react-devtools/backend/attachRendererFiber.js

7

CHANGELOG.md

@@ -11,1 +11,8 @@ # Changelog

Remove react peer dependencies (thanks Christian Hoffmeister @choffmeister)
## v0.1.4
Updates and further information in package.json (thanks @valscion)
## v1.0.0
Update to support fiber (React v16)
Converted to use ES6 Maps with objects as keys (this must be supported by the runtime - Node v4 onwards is fine)

55

lib/componentMap.js

@@ -1,58 +0,31 @@

'use strict';
"use strict";
var nodes = new Map();
var publicInstanceToData = new Map();
var privateInstanceToData = new Map();
exports.mount = function (component) {
var rootNodeID = component.element._rootNodeID;
var elementsInRoot = nodes.get(rootNodeID);
if (elementsInRoot === undefined) {
elementsInRoot = [];
nodes.set(rootNodeID, elementsInRoot);
}
elementsInRoot.push(component);
if (component.internalInstance.stateNode) {
publicInstanceToData.set(component.internalInstance.stateNode, component);
}
privateInstanceToData.set(component.internalInstance, component);
};
exports.update = function (component) {
var existing = exports.findInternalComponent(component.element);
if (existing) {
existing.data = component.data;
}
var existing = exports.findInternalComponent(component.internalInstance);
if (existing) {
existing.data = component.data;
}
};
exports.findComponent = function (component) {
if (component && component._reactInternalInstance) {
var elementsInRoot = nodes.get(component._reactInternalInstance._rootNodeID);
if (elementsInRoot) {
for (var index = elementsInRoot.length - 1; index >= 0; --index) {
if (elementsInRoot[index].data.publicInstance === component) {
var renderedComponent = elementsInRoot[index];
if (renderedComponent.data.nodeType === 'NativeWrapper') {
return exports.findInternalComponent(renderedComponent.data.children[0]);
}
return renderedComponent;
}
}
}
}
return null;
return publicInstanceToData.get(component) || null;
};
exports.findInternalComponent = function (internalComponent) {
if (internalComponent) {
var elementsInRoot = nodes.get(internalComponent._rootNodeID);
if (elementsInRoot) {
for (var index = elementsInRoot.length - 1; index >= 0; --index) {
if (elementsInRoot[index].element === internalComponent) {
return elementsInRoot[index];
}
}
}
}
return privateInstanceToData.get(internalComponent) || null;
};
exports.clearAll = function () {
nodes.clear();
publicInstanceToData.clear();
};
//# sourceMappingURL=componentMap.js.map
'use strict';
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

@@ -69,3 +69,2 @@ var Backend = require('./react-devtools/backend/backend');

exported.findComponent = function (component) {
return ComponentMap.findComponent(component);

@@ -75,3 +74,2 @@ };

exported.findInternalComponent = function (internalComponent) {
return ComponentMap.findInternalComponent(internalComponent);

@@ -88,3 +86,3 @@ };

var internalComponent = void 0;
if (component && component.element && component.data) {
if (component && component.data && component.data.publicInstance) {
internalComponent = component;

@@ -91,0 +89,0 @@ } else {

@@ -15,2 +15,3 @@ /**

var getData012 = require('./getData012');
var attachRendererFiber = require('./attachRendererFiber');

@@ -27,2 +28,7 @@ /**

// React Fiber
if (typeof renderer.findFiberByHostInstance === 'function') {
return attachRendererFiber(hook, rid, renderer);
}
// React Native

@@ -40,30 +46,30 @@ if (renderer.Mount.findNodeHandle && renderer.Mount.nativeTagToRootNodeID) {

} else if (renderer.ComponentTree) {
extras.getNativeFromReactElement = function (component) {
return renderer.ComponentTree.getNodeFromInstance(component);
};
extras.getNativeFromReactElement = function (component) {
return renderer.ComponentTree.getNodeFromInstance(component);
};
extras.getReactElementFromNative = function (node) {
return renderer.ComponentTree.getClosestInstanceFromNode(node);
};
// React DOM
} else if (renderer.Mount.getID && renderer.Mount.getNode) {
extras.getNativeFromReactElement = function (component) {
try {
return renderer.Mount.getNode(component._rootNodeID);
} catch (e) {
return undefined;
}
};
extras.getReactElementFromNative = function (node) {
return renderer.ComponentTree.getClosestInstanceFromNode(node);
};
// React DOM
} else if (renderer.Mount.getID && renderer.Mount.getNode) {
extras.getNativeFromReactElement = function (component) {
try {
return renderer.Mount.getNode(component._rootNodeID);
} catch (e) {
return undefined;
}
};
extras.getReactElementFromNative = function (node) {
var id = renderer.Mount.getID(node);
while (node && node.parentNode && !id) {
node = node.parentNode;
id = renderer.Mount.getID(node);
}
return rootNodeIDMap.get(id);
};
} else {
console.warn('Unknown react version (does not have getID), probably an unshimmed React Native');
extras.getReactElementFromNative = function (node) {
var id = renderer.Mount.getID(node);
while (node && node.parentNode && !id) {
node = node.parentNode;
id = renderer.Mount.getID(node);
}
return rootNodeIDMap.get(id);
};
} else {
console.warn('Unknown react version (does not have getID), probably an unshimmed React Native');
}

@@ -76,11 +82,11 @@ var oldMethods;

if (renderer.Mount._renderNewRootComponent) {
oldRenderRoot = decorateResult(renderer.Mount, '_renderNewRootComponent', function (element) {
hook.emit('root', { renderer: rid, element: element });
oldRenderRoot = decorateResult(renderer.Mount, '_renderNewRootComponent', function (internalInstance) {
hook.emit('root', { renderer: rid, internalInstance: internalInstance });
});
// React Native
} else if (renderer.Mount.renderComponent) {
oldRenderComponent = decorateResult(renderer.Mount, 'renderComponent', function (element) {
hook.emit('root', { renderer: rid, element: element._reactInternalInstance });
});
}
oldRenderComponent = decorateResult(renderer.Mount, 'renderComponent', function (internalInstance) {
hook.emit('root', { renderer: rid, internalInstance: internalInstance._reactInternalInstance });
});
}

@@ -102,3 +108,3 @@ if (renderer.Component) {

setTimeout(function () {
hook.emit('mount', { element: _this, data: getData012(_this), renderer: rid });
hook.emit('mount', { internalInstance: _this, data: getData012(_this), renderer: rid });
}, 0);

@@ -110,7 +116,7 @@ },

setTimeout(function () {
hook.emit('update', { element: _this2, data: getData012(_this2), renderer: rid });
hook.emit('update', { internalInstance: _this2, data: getData012(_this2), renderer: rid });
}, 0);
},
unmountComponent: function unmountComponent() {
hook.emit('unmount', { element: this, renderer: rid });
hook.emit('unmount', { internalInstance: this, renderer: rid });
rootNodeIDMap.delete(this._rootNodeID, this);

@@ -121,16 +127,16 @@ }

oldMethods = decorateMany(renderer.Reconciler, {
mountComponent: function mountComponent(element, rootID, transaction, context) {
var data = getData(element);
rootNodeIDMap.set(element._rootNodeID, element);
hook.emit('mount', { element: element, data: data, renderer: rid });
mountComponent: function mountComponent(internalInstance, rootID, transaction, context) {
var data = getData(internalInstance);
rootNodeIDMap.set(internalInstance._rootNodeID, internalInstance);
hook.emit('mount', { internalInstance: internalInstance, data: data, renderer: rid });
},
performUpdateIfNecessary: function performUpdateIfNecessary(element, nextChild, transaction, context) {
hook.emit('update', { element: element, data: getData(element), renderer: rid });
performUpdateIfNecessary: function performUpdateIfNecessary(internalInstance, nextChild, transaction, context) {
hook.emit('update', { internalInstance: internalInstance, data: getData(internalInstance), renderer: rid });
},
receiveComponent: function receiveComponent(element, nextChild, transaction, context) {
hook.emit('update', { element: element, data: getData(element), renderer: rid });
receiveComponent: function receiveComponent(internalInstance, nextChild, transaction, context) {
hook.emit('update', { internalInstance: internalInstance, data: getData(internalInstance), renderer: rid });
},
unmountComponent: function unmountComponent(element) {
hook.emit('unmount', { element: element, renderer: rid });
rootNodeIDMap.delete(element._rootNodeID, element);
unmountComponent: function unmountComponent(internalInstance) {
hook.emit('unmount', { internalInstance: internalInstance, renderer: rid });
rootNodeIDMap.delete(internalInstance._rootNodeID, internalInstance);
}

@@ -177,4 +183,4 @@ });

function walkNode(element, onMount, isPre013) {
var data = isPre013 ? getData012(element) : getData(element);
function walkNode(internalInstance, onMount, isPre013) {
var data = isPre013 ? getData012(internalInstance) : getData(internalInstance);
if (data.children && Array.isArray(data.children)) {

@@ -185,3 +191,3 @@ data.children.forEach(function (child) {

}
onMount(element, data);
onMount(internalInstance, data);
}

@@ -188,0 +194,0 @@

@@ -17,3 +17,3 @@ /**

* and whatever else is needed.
* 4. The agend then calls `.emit('react-devtools', agent)`
* 4. The agent then calls `.emit('react-devtools', agent)`
*

@@ -41,4 +41,4 @@ * Now things are hooked up.

hook.on('renderer', function (_ref) {
var id = _ref.id;
var renderer = _ref.renderer;
var id = _ref.id,
renderer = _ref.renderer;

@@ -45,0 +45,0 @@ hook.helpers[id] = attachRenderer(hook, id, renderer);

@@ -15,5 +15,6 @@ /**

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var copyWithSet = require('./copyWithSet');
var getDisplayName = require('./getDisplayName');

@@ -23,3 +24,3 @@ /**

*/
function getData(element) {
function getData(internalInstance) {
var children = null;

@@ -34,2 +35,3 @@ var props = null;

var ref = null;
var source = null;
var text = null;

@@ -41,44 +43,51 @@ var publicInstance = null;

// a plain value -- a string or number.
if ((typeof element === 'undefined' ? 'undefined' : _typeof(element)) !== 'object') {
if ((typeof internalInstance === 'undefined' ? 'undefined' : _typeof(internalInstance)) !== 'object') {
nodeType = 'Text';
text = element + '';
} else if (element._currentElement === null || element._currentElement === false) {
text = internalInstance + '';
} else if (internalInstance._currentElement === null || internalInstance._currentElement === false) {
nodeType = 'Empty';
} else if (element._renderedComponent) {
} else if (internalInstance._renderedComponent) {
nodeType = 'NativeWrapper';
children = [element._renderedComponent];
props = element._instance.props;
state = element._instance.state;
context = element._instance.context;
children = [internalInstance._renderedComponent];
props = internalInstance._instance.props;
state = internalInstance._instance.state;
context = internalInstance._instance.context;
if (context && Object.keys(context).length === 0) {
context = null;
}
} else if (element._renderedChildren) {
children = childrenList(element._renderedChildren);
} else if (element._currentElement && element._currentElement.props) {
} else if (internalInstance._renderedChildren) {
children = childrenList(internalInstance._renderedChildren);
} else if (internalInstance._currentElement && internalInstance._currentElement.props) {
// This is a native node without rendered children -- meaning the children
// prop is just a string or (in the case of the <option>) a list of
// strings & numbers.
children = element._currentElement.props.children;
children = internalInstance._currentElement.props.children;
}
if (!props && element._currentElement && element._currentElement.props) {
props = element._currentElement.props;
if (!props && internalInstance._currentElement && internalInstance._currentElement.props) {
props = internalInstance._currentElement.props;
}
// != used deliberately here to catch undefined and null
if (element._currentElement != null) {
type = element._currentElement.type;
if (element._currentElement.key) {
key = String(element._currentElement.key);
if (internalInstance._currentElement != null) {
type = internalInstance._currentElement.type;
if (internalInstance._currentElement.key) {
key = String(internalInstance._currentElement.key);
}
ref = element._currentElement.ref;
source = internalInstance._currentElement._source;
ref = internalInstance._currentElement.ref;
if (typeof type === 'string') {
name = type;
} else if (element.getName) {
if (internalInstance._nativeNode != null) {
publicInstance = internalInstance._nativeNode;
}
if (internalInstance._hostNode != null) {
publicInstance = internalInstance._hostNode;
}
} else if (typeof type === 'function') {
nodeType = 'Composite';
name = element.getName();
name = getDisplayName(type);
// 0.14 top-level wrapper
// TODO(jared): The backend should just act as if these don't exist.
if (element._renderedComponent && element._currentElement.props === element._renderedComponent._currentElement) {
if (internalInstance._renderedComponent && (internalInstance._currentElement.props === internalInstance._renderedComponent._currentElement || internalInstance._currentElement.type.isReactTopLevelWrapper)) {
nodeType = 'Wrapper';

@@ -89,20 +98,22 @@ }

}
} else if (typeof element._stringText === 'string') {
} else if (typeof internalInstance._stringText === 'string') {
nodeType = 'Text';
text = element._stringText;
text = internalInstance._stringText;
} else {
name = type && (type.displayName || type.name) || 'Unknown';
name = getDisplayName(type);
}
}
if (element._instance) {
var inst = element._instance;
if (internalInstance._instance) {
var inst = internalInstance._instance;
updater = {
setState: inst.setState && inst.setState.bind(inst),
forceUpdate: inst.forceUpdate && inst.forceUpdate.bind(inst),
setInProps: inst.forceUpdate && setInProps.bind(null, element),
setInProps: inst.forceUpdate && setInProps.bind(null, internalInstance),
setInState: inst.forceUpdate && setInState.bind(null, inst),
setInContext: inst.forceUpdate && setInContext.bind(null, inst)
};
publicInstance = inst;
if (typeof type === 'function') {
publicInstance = inst;
}

@@ -117,2 +128,11 @@ // TODO: React ART currently falls in this bucket, but this doesn't

if (typeof internalInstance.setNativeProps === 'function') {
// For editing styles in RN
updater = {
setNativeProps: function setNativeProps(nativeProps) {
internalInstance.setNativeProps(nativeProps);
}
};
}
return {

@@ -123,2 +143,3 @@ nodeType: nodeType,

ref: ref,
source: source,
name: name,

@@ -125,0 +146,0 @@ props: props,

@@ -15,7 +15,7 @@ /**

function getData012(element) {
function getData012(internalInstance) {
var children = null;
var props = element.props;
var state = element.state;
var context = element.context;
var props = internalInstance.props;
var state = internalInstance.state;
var context = internalInstance.context;
var updater = null;

@@ -29,14 +29,14 @@ var name = null;

var nodeType = 'Native';
if (element._renderedComponent) {
if (internalInstance._renderedComponent) {
nodeType = 'Wrapper';
children = [element._renderedComponent];
children = [internalInstance._renderedComponent];
if (context && Object.keys(context).length === 0) {
context = null;
}
} else if (element._renderedChildren) {
name = element.constructor.displayName;
children = childrenList(element._renderedChildren);
} else if (internalInstance._renderedChildren) {
name = internalInstance.constructor.displayName;
children = childrenList(internalInstance._renderedChildren);
} else if (typeof props.children === 'string') {
// string children
name = element.constructor.displayName;
name = internalInstance.constructor.displayName;
children = props.children;

@@ -46,12 +46,12 @@ nodeType = 'Native';

if (!props && element._currentElement && element._currentElement.props) {
props = element._currentElement.props;
if (!props && internalInstance._currentElement && internalInstance._currentElement.props) {
props = internalInstance._currentElement.props;
}
if (element._currentElement) {
type = element._currentElement.type;
if (element._currentElement.key) {
key = String(element._currentElement.key);
if (internalInstance._currentElement) {
type = internalInstance._currentElement.type;
if (internalInstance._currentElement.key) {
key = String(internalInstance._currentElement.key);
}
ref = element._currentElement.ref;
ref = internalInstance._currentElement.ref;
if (typeof type === 'string') {

@@ -69,3 +69,3 @@ name = type;

if (!name) {
name = element.constructor.displayName || 'No display name';
name = internalInstance.constructor.displayName || 'No display name';
nodeType = 'Composite';

@@ -81,11 +81,11 @@ }

if (element.forceUpdate) {
if (internalInstance.forceUpdate) {
updater = {
setState: element.setState.bind(element),
forceUpdate: element.forceUpdate.bind(element),
setInProps: element.forceUpdate && setInProps.bind(null, element),
setInState: element.forceUpdate && setInState.bind(null, element),
setInContext: element.forceUpdate && setInContext.bind(null, element)
setState: internalInstance.setState.bind(internalInstance),
forceUpdate: internalInstance.forceUpdate.bind(internalInstance),
setInProps: internalInstance.forceUpdate && setInProps.bind(null, internalInstance),
setInState: internalInstance.forceUpdate && setInState.bind(null, internalInstance),
setInContext: internalInstance.forceUpdate && setInContext.bind(null, internalInstance)
};
publicInstance = element;
publicInstance = internalInstance;
}

@@ -98,2 +98,3 @@

ref: ref,
source: null,
name: name,

@@ -100,0 +101,0 @@ props: props,

@@ -21,46 +21,199 @@ /**

}
Object.defineProperty(window, '__REACT_DEVTOOLS_GLOBAL_HOOK__', {
value: {
_renderers: {},
helpers: {},
inject: function inject(renderer) {
var id = Math.random().toString(16).slice(2);
this._renderers[id] = renderer;
this.emit('renderer', { id: id, renderer: renderer });
},
_listeners: {},
sub: function sub(evt, fn) {
var _this = this;
function detectReactBuildType(renderer) {
try {
var toString = Function.prototype.toString;
if (typeof renderer.version === 'string') {
// React DOM Fiber (16+)
if (renderer.bundleType > 0) {
// This is not a production build.
// We are currently only using 0 (PROD) and 1 (DEV)
// but might add 2 (PROFILE) in the future.
return 'development';
}
// The above should cover envification, but we should still make sure
// that the bundle code has been uglified.
var findFiberCode = toString.call(renderer.findFiberByHostInstance);
// Filter out bad results (if that is even possible):
if (findFiberCode.indexOf('function') !== 0) {
// Hope for the best if we're not sure.
return 'production';
}
this.on(evt, fn);
return function () {
return _this.off(evt, fn);
};
},
on: function on(evt, fn) {
if (!this._listeners[evt]) {
this._listeners[evt] = [];
// By now we know that it's envified--but what if it's not minified?
// This can be bad too, as it means DEV code is still there.
// FIXME: this is fragile!
// We should replace this check with check on a specially passed
// function. This also doesn't detect lack of dead code elimination
// (although this is not a problem since flat bundles).
if (findFiberCode.indexOf('getClosestInstanceFromNode') !== -1) {
return 'unminified';
}
this._listeners[evt].push(fn);
},
off: function off(evt, fn) {
if (!this._listeners[evt]) {
return;
// We're good.
return 'production';
}
if (renderer.Mount && renderer.Mount._renderNewRootComponent) {
// React DOM Stack
var renderRootCode = toString.call(renderer.Mount._renderNewRootComponent);
// Filter out bad results (if that is even possible):
if (renderRootCode.indexOf('function') !== 0) {
// Hope for the best if we're not sure.
return 'production';
}
var ix = this._listeners[evt].indexOf(fn);
if (ix !== -1) {
this._listeners[evt].splice(ix, 1);
// Check for React DOM Stack < 15.1.0 in development.
// If it contains "storedMeasure" call, it's wrapped in ReactPerf (DEV only).
// This would be true even if it's minified, as method name still matches.
if (renderRootCode.indexOf('storedMeasure') !== -1) {
return 'development';
}
if (!this._listeners[evt].length) {
this._listeners[evt] = null;
// For other versions (and configurations) it's not so easy.
// Let's quickly exclude proper production builds.
// If it contains a warning message, it's either a DEV build,
// or an PROD build without proper dead code elimination.
if (renderRootCode.indexOf('should be a pure function') !== -1) {
// Now how do we tell a DEV build from a bad PROD build?
// If we see NODE_ENV, we're going to assume this is a dev build
// because most likely it is referring to an empty shim.
if (renderRootCode.indexOf('NODE_ENV') !== -1) {
return 'development';
}
// If we see "development", we're dealing with an envified DEV build
// (such as the official React DEV UMD).
if (renderRootCode.indexOf('development') !== -1) {
return 'development';
}
// I've seen process.env.NODE_ENV !== 'production' being smartly
// replaced by `true` in DEV by Webpack. I don't know how that
// works but we can safely guard against it because `true` was
// never used in the function source since it was written.
if (renderRootCode.indexOf('true') !== -1) {
return 'development';
}
// By now either it is a production build that has not been minified,
// or (worse) this is a minified development build using non-standard
// environment (e.g. "staging"). We're going to look at whether
// the function argument name is mangled:
if (
// 0.13 to 15
renderRootCode.indexOf('nextElement') !== -1 ||
// 0.12
renderRootCode.indexOf('nextComponent') !== -1) {
// We can't be certain whether this is a development build or not,
// but it is definitely unminified.
return 'unminified';
} else {
// This is likely a minified development build.
return 'development';
}
}
},
emit: function emit(evt, data) {
if (this._listeners[evt]) {
this._listeners[evt].map(function (fn) {
return fn(data);
});
// By now we know that it's envified and dead code elimination worked,
// but what if it's still not minified? (Is this even possible?)
// Let's check matches for the first argument name.
if (
// 0.13 to 15
renderRootCode.indexOf('nextElement') !== -1 ||
// 0.12
renderRootCode.indexOf('nextComponent') !== -1) {
return 'unminified';
}
// Seems like we're using the production version.
// Now let's check if we're still on 0.14 or lower:
if (renderRootCode.indexOf('._registerComponent') !== -1) {
// TODO: we can remove the condition above once 16
// is older than a year. Since this branch only runs
// for Stack, we can flip it completely when Stack
// is old enough. The branch for Fiber is above,
// and it can check renderer.version directly.
return 'outdated';
}
// We're all good.
return 'production';
}
} catch (err) {
// Weird environments may exist.
// This code needs a higher fault tolerance
// because it runs even with closed DevTools.
// TODO: should we catch errors in all injected code, and not just this part?
}
return 'production';
}
var hook = {
// Shared between Stack and Fiber:
_renderers: {},
helpers: {},
inject: function inject(renderer) {
var id = Math.random().toString(16).slice(2);
hook._renderers[id] = renderer;
var reactBuildType = detectReactBuildType(renderer);
hook.emit('renderer', { id: id, renderer: renderer, reactBuildType: reactBuildType });
return id;
},
_listeners: {},
sub: function sub(evt, fn) {
hook.on(evt, fn);
return function () {
return hook.off(evt, fn);
};
},
on: function on(evt, fn) {
if (!hook._listeners[evt]) {
hook._listeners[evt] = [];
}
hook._listeners[evt].push(fn);
},
off: function off(evt, fn) {
if (!hook._listeners[evt]) {
return;
}
var ix = hook._listeners[evt].indexOf(fn);
if (ix !== -1) {
hook._listeners[evt].splice(ix, 1);
}
if (!hook._listeners[evt].length) {
hook._listeners[evt] = null;
}
},
emit: function emit(evt, data) {
if (hook._listeners[evt]) {
hook._listeners[evt].map(function (fn) {
return fn(data);
});
}
},
// Fiber-only:
supportsFiber: true,
_fiberRoots: {},
getFiberRoots: function getFiberRoots(rendererID) {
var roots = hook._fiberRoots;
if (!roots[rendererID]) {
roots[rendererID] = new Set();
}
return roots[rendererID];
},
onCommitFiberUnmount: function onCommitFiberUnmount(rendererID, fiber) {
// TODO: can we use hook for roots too?
if (hook.helpers[rendererID]) {
hook.helpers[rendererID].handleCommitFiberUnmount(fiber);
}
},
onCommitFiberRoot: function onCommitFiberRoot(rendererID, root) {
var mountedRoots = hook.getFiberRoots(rendererID);
var current = root.current;
var isKnownRoot = mountedRoots.has(root);
var isUnmounting = current.memoizedState == null || current.memoizedState.element == null;
// Keep track of mounted roots so we can hydrate when DevTools connect.
if (!isKnownRoot && !isUnmounting) {
mountedRoots.add(root);
} else if (isKnownRoot && isUnmounting) {
mountedRoots.delete(root);
}
if (hook.helpers[rendererID]) {
hook.helpers[rendererID].handleCommitFiberRoot(root);
}
}
};
Object.defineProperty(window, '__REACT_DEVTOOLS_GLOBAL_HOOK__', {
value: hook
});

@@ -67,0 +220,0 @@ }

{
"name": "react-render-hook",
"version": "0.1.4",
"version": "1.0.0",
"description": "Hooks to React.js rendering, to enable access to the virtual DOM (as shown in React devtools)",

@@ -24,3 +24,3 @@ "scripts": {

"babel-cli": "^6.7.5",
"babel-core": "^6.7.6",
"babel-core": "^6.25.0",
"babel-preset-es2015": "^6.6.0",

@@ -31,3 +31,5 @@ "babel-preset-react": "^6.5.0",

"babel-register": "^6.7.2",
"jsdom": "^6.5.1",
"create-react-class": "^15.6.0",
"jsdom": "^11.1.0",
"jsdom-global": "^3.0.2",
"magicpen": "^5.4.0",

@@ -34,0 +36,0 @@ "mocha": "^2.3.3",

const nodes = new Map();
const publicInstanceToData = new Map();
const privateInstanceToData = new Map();
exports.mount = function (component) {
const rootNodeID = component.element._rootNodeID;
let elementsInRoot = nodes.get(rootNodeID);
if (elementsInRoot === undefined) {
elementsInRoot = [];
nodes.set(rootNodeID, elementsInRoot);
}
elementsInRoot.push(component);
if (component.internalInstance.stateNode) {
publicInstanceToData.set(component.internalInstance.stateNode, component)
}
privateInstanceToData.set(component.internalInstance, component);
};
exports.update = function (component) {
const existing = exports.findInternalComponent(component.element);
const existing = exports.findInternalComponent(component.internalInstance);
if (existing) {

@@ -23,36 +20,11 @@ existing.data = component.data;

exports.findComponent = function (component) {
if (component && component._reactInternalInstance) {
const elementsInRoot = nodes.get(component._reactInternalInstance._rootNodeID);
if (elementsInRoot) {
for (let index = elementsInRoot.length - 1; index >= 0; --index) {
if (elementsInRoot[index].data.publicInstance === component) {
const renderedComponent = elementsInRoot[index];
if (renderedComponent.data.nodeType === 'NativeWrapper') {
return exports.findInternalComponent(renderedComponent.data.children[0]);
}
return renderedComponent;
}
}
}
}
return null;
return publicInstanceToData.get(component) || null;
};
exports.findInternalComponent = function (internalComponent) {
if (internalComponent) {
const elementsInRoot = nodes.get(internalComponent._rootNodeID);
if (elementsInRoot) {
for (let index = elementsInRoot.length - 1; index >= 0; --index) {
if (elementsInRoot[index].element === internalComponent) {
return elementsInRoot[index];
}
}
}
}
return privateInstanceToData.get(internalComponent) || null;
};
exports.clearAll = function () {
nodes.clear();
publicInstanceToData.clear();
};

@@ -68,3 +68,2 @@ const Backend = require('./react-devtools/backend/backend');

exported.findComponent = function (component) {
return ComponentMap.findComponent(component);

@@ -74,3 +73,2 @@ };

exported.findInternalComponent = function (internalComponent) {
return ComponentMap.findInternalComponent(internalComponent);

@@ -92,3 +90,3 @@ };

let internalComponent;
if (component && component.element && component.data) {
if (component && component.data && component.data.publicInstance) {
internalComponent = component;

@@ -95,0 +93,0 @@ } else {

@@ -16,2 +16,3 @@ /**

var getData012 = require('./getData012');
var attachRendererFiber = require('./attachRendererFiber');

@@ -30,2 +31,7 @@ type NodeLike = {};

// React Fiber
if (typeof renderer.findFiberByHostInstance === 'function') {
return attachRendererFiber(hook, rid, renderer);
}
// React Native

@@ -78,9 +84,9 @@ if (renderer.Mount.findNodeHandle && renderer.Mount.nativeTagToRootNodeID) {

if (renderer.Mount._renderNewRootComponent) {
oldRenderRoot = decorateResult(renderer.Mount, '_renderNewRootComponent', (element) => {
hook.emit('root', {renderer: rid, element});
oldRenderRoot = decorateResult(renderer.Mount, '_renderNewRootComponent', (internalInstance) => {
hook.emit('root', {renderer: rid, internalInstance});
});
// React Native
} else if (renderer.Mount.renderComponent) {
oldRenderComponent = decorateResult(renderer.Mount, 'renderComponent', element => {
hook.emit('root', {renderer: rid, element: element._reactInternalInstance});
oldRenderComponent = decorateResult(renderer.Mount, 'renderComponent', internalInstance => {
hook.emit('root', {renderer: rid, internalInstance: internalInstance._reactInternalInstance});
});

@@ -102,3 +108,3 @@ }

setTimeout(() => {
hook.emit('mount', {element: this, data: getData012(this), renderer: rid});
hook.emit('mount', {internalInstance: this, data: getData012(this), renderer: rid});
}, 0);

@@ -108,7 +114,7 @@ },

setTimeout(() => {
hook.emit('update', {element: this, data: getData012(this), renderer: rid});
hook.emit('update', {internalInstance: this, data: getData012(this), renderer: rid});
}, 0);
},
unmountComponent() {
hook.emit('unmount', {element: this, renderer: rid});
hook.emit('unmount', {internalInstance: this, renderer: rid});
rootNodeIDMap.delete(this._rootNodeID, this);

@@ -119,16 +125,16 @@ },

oldMethods = decorateMany(renderer.Reconciler, {
mountComponent(element, rootID, transaction, context) {
var data = getData(element);
rootNodeIDMap.set(element._rootNodeID, element);
hook.emit('mount', {element, data, renderer: rid});
mountComponent(internalInstance, rootID, transaction, context) {
var data = getData(internalInstance);
rootNodeIDMap.set(internalInstance._rootNodeID, internalInstance);
hook.emit('mount', {internalInstance, data, renderer: rid});
},
performUpdateIfNecessary(element, nextChild, transaction, context) {
hook.emit('update', {element, data: getData(element), renderer: rid});
performUpdateIfNecessary(internalInstance, nextChild, transaction, context) {
hook.emit('update', {internalInstance, data: getData(internalInstance), renderer: rid});
},
receiveComponent(element, nextChild, transaction, context) {
hook.emit('update', {element, data: getData(element), renderer: rid});
receiveComponent(internalInstance, nextChild, transaction, context) {
hook.emit('update', {internalInstance, data: getData(internalInstance), renderer: rid});
},
unmountComponent(element) {
hook.emit('unmount', {element, renderer: rid});
rootNodeIDMap.delete(element._rootNodeID, element);
unmountComponent(internalInstance) {
hook.emit('unmount', {internalInstance, renderer: rid});
rootNodeIDMap.delete(internalInstance._rootNodeID, internalInstance);
},

@@ -138,3 +144,3 @@ });

extras.walkTree = function(visit: (component: OpaqueNodeHandle, data: DataType) => void, visitRoot: (element: OpaqueNodeHandle) => void) {
extras.walkTree = function(visit: (component: OpaqueNodeHandle, data: DataType) => void, visitRoot: (internalInstance: OpaqueNodeHandle) => void) {
var onMount = (component, data) => {

@@ -176,8 +182,8 @@ rootNodeIDMap.set(component._rootNodeID, component);

function walkNode(element, onMount, isPre013) {
var data = isPre013 ? getData012(element) : getData(element);
function walkNode(internalInstance, onMount, isPre013) {
var data = isPre013 ? getData012(internalInstance) : getData(internalInstance);
if (data.children && Array.isArray(data.children)) {
data.children.forEach(child => walkNode(child, onMount, isPre013));
}
onMount(element, data);
onMount(internalInstance, data);
}

@@ -184,0 +190,0 @@

@@ -17,3 +17,3 @@ /**

* and whatever else is needed.
* 4. The agend then calls `.emit('react-devtools', agent)`
* 4. The agent then calls `.emit('react-devtools', agent)`
*

@@ -20,0 +20,0 @@ * Now things are hooked up.

@@ -15,2 +15,3 @@ /**

var copyWithSet = require('./copyWithSet');
var getDisplayName = require('./getDisplayName');

@@ -20,3 +21,3 @@ /**

*/
function getData(element: Object): DataType {
function getData(internalInstance: Object): DataType {
var children = null;

@@ -31,2 +32,3 @@ var props = null;

var ref = null;
var source = null;
var text = null;

@@ -38,44 +40,54 @@ var publicInstance = null;

// a plain value -- a string or number.
if (typeof element !== 'object') {
if (typeof internalInstance !== 'object') {
nodeType = 'Text';
text = element + '';
} else if (element._currentElement === null || element._currentElement === false) {
text = internalInstance + '';
} else if (internalInstance._currentElement === null || internalInstance._currentElement === false) {
nodeType = 'Empty';
} else if (element._renderedComponent) {
} else if (internalInstance._renderedComponent) {
nodeType = 'NativeWrapper';
children = [element._renderedComponent];
props = element._instance.props;
state = element._instance.state;
context = element._instance.context;
children = [internalInstance._renderedComponent];
props = internalInstance._instance.props;
state = internalInstance._instance.state;
context = internalInstance._instance.context;
if (context && Object.keys(context).length === 0) {
context = null;
}
} else if (element._renderedChildren) {
children = childrenList(element._renderedChildren);
} else if (element._currentElement && element._currentElement.props) {
} else if (internalInstance._renderedChildren) {
children = childrenList(internalInstance._renderedChildren);
} else if (internalInstance._currentElement && internalInstance._currentElement.props) {
// This is a native node without rendered children -- meaning the children
// prop is just a string or (in the case of the <option>) a list of
// strings & numbers.
children = element._currentElement.props.children;
children = internalInstance._currentElement.props.children;
}
if (!props && element._currentElement && element._currentElement.props) {
props = element._currentElement.props;
if (!props && internalInstance._currentElement && internalInstance._currentElement.props) {
props = internalInstance._currentElement.props;
}
// != used deliberately here to catch undefined and null
if (element._currentElement != null) {
type = element._currentElement.type;
if (element._currentElement.key) {
key = String(element._currentElement.key);
if (internalInstance._currentElement != null) {
type = internalInstance._currentElement.type;
if (internalInstance._currentElement.key) {
key = String(internalInstance._currentElement.key);
}
ref = element._currentElement.ref;
source = internalInstance._currentElement._source;
ref = internalInstance._currentElement.ref;
if (typeof type === 'string') {
name = type;
} else if (element.getName) {
if (internalInstance._nativeNode != null) {
publicInstance = internalInstance._nativeNode;
}
if (internalInstance._hostNode != null) {
publicInstance = internalInstance._hostNode;
}
} else if (typeof type === 'function') {
nodeType = 'Composite';
name = element.getName();
name = getDisplayName(type);
// 0.14 top-level wrapper
// TODO(jared): The backend should just act as if these don't exist.
if (element._renderedComponent && element._currentElement.props === element._renderedComponent._currentElement) {
if (internalInstance._renderedComponent && (
internalInstance._currentElement.props === internalInstance._renderedComponent._currentElement ||
internalInstance._currentElement.type.isReactTopLevelWrapper
)) {
nodeType = 'Wrapper';

@@ -86,20 +98,22 @@ }

}
} else if (typeof element._stringText === 'string') {
} else if (typeof internalInstance._stringText === 'string') {
nodeType = 'Text';
text = element._stringText;
text = internalInstance._stringText;
} else {
name = type && (type.displayName || type.name) || 'Unknown';
name = getDisplayName(type);
}
}
if (element._instance) {
var inst = element._instance;
if (internalInstance._instance) {
var inst = internalInstance._instance;
updater = {
setState: inst.setState && inst.setState.bind(inst),
forceUpdate: inst.forceUpdate && inst.forceUpdate.bind(inst),
setInProps: inst.forceUpdate && setInProps.bind(null, element),
setInProps: inst.forceUpdate && setInProps.bind(null, internalInstance),
setInState: inst.forceUpdate && setInState.bind(null, inst),
setInContext: inst.forceUpdate && setInContext.bind(null, inst),
};
publicInstance = inst;
if (typeof type === 'function') {
publicInstance = inst;
}

@@ -114,2 +128,11 @@ // TODO: React ART currently falls in this bucket, but this doesn't

if (typeof internalInstance.setNativeProps === 'function') {
// For editing styles in RN
updater = {
setNativeProps(nativeProps) {
internalInstance.setNativeProps(nativeProps);
},
};
}
return {

@@ -120,2 +143,3 @@ nodeType,

ref,
source,
name,

@@ -122,0 +146,0 @@ props,

@@ -16,7 +16,7 @@ /**

function getData012(element: Object): DataType {
function getData012(internalInstance: Object): DataType {
var children = null;
var props = element.props;
var state = element.state;
var context = element.context;
var props = internalInstance.props;
var state = internalInstance.state;
var context = internalInstance.context;
var updater = null;

@@ -30,14 +30,14 @@ var name = null;

var nodeType = 'Native';
if (element._renderedComponent) {
if (internalInstance._renderedComponent) {
nodeType = 'Wrapper';
children = [element._renderedComponent];
children = [internalInstance._renderedComponent];
if (context && Object.keys(context).length === 0) {
context = null;
}
} else if (element._renderedChildren) {
name = element.constructor.displayName;
children = childrenList(element._renderedChildren);
} else if (internalInstance._renderedChildren) {
name = internalInstance.constructor.displayName;
children = childrenList(internalInstance._renderedChildren);
} else if (typeof props.children === 'string') {
// string children
name = element.constructor.displayName;
name = internalInstance.constructor.displayName;
children = props.children;

@@ -47,12 +47,12 @@ nodeType = 'Native';

if (!props && element._currentElement && element._currentElement.props) {
props = element._currentElement.props;
if (!props && internalInstance._currentElement && internalInstance._currentElement.props) {
props = internalInstance._currentElement.props;
}
if (element._currentElement) {
type = element._currentElement.type;
if (element._currentElement.key) {
key = String(element._currentElement.key);
if (internalInstance._currentElement) {
type = internalInstance._currentElement.type;
if (internalInstance._currentElement.key) {
key = String(internalInstance._currentElement.key);
}
ref = element._currentElement.ref;
ref = internalInstance._currentElement.ref;
if (typeof type === 'string') {

@@ -70,3 +70,3 @@ name = type;

if (!name) {
name = element.constructor.displayName || 'No display name';
name = internalInstance.constructor.displayName || 'No display name';
nodeType = 'Composite';

@@ -82,11 +82,11 @@ }

if (element.forceUpdate) {
if (internalInstance.forceUpdate) {
updater = {
setState: element.setState.bind(element),
forceUpdate: element.forceUpdate.bind(element),
setInProps: element.forceUpdate && setInProps.bind(null, element),
setInState: element.forceUpdate && setInState.bind(null, element),
setInContext: element.forceUpdate && setInContext.bind(null, element),
setState: internalInstance.setState.bind(internalInstance),
forceUpdate: internalInstance.forceUpdate.bind(internalInstance),
setInProps: internalInstance.forceUpdate && setInProps.bind(null, internalInstance),
setInState: internalInstance.forceUpdate && setInState.bind(null, internalInstance),
setInContext: internalInstance.forceUpdate && setInContext.bind(null, internalInstance),
};
publicInstance = element;
publicInstance = internalInstance;
}

@@ -99,2 +99,3 @@

ref,
source: null,
name,

@@ -101,0 +102,0 @@ props,

@@ -23,43 +23,199 @@ /**

}
Object.defineProperty(window, '__REACT_DEVTOOLS_GLOBAL_HOOK__', {
value: ({
_renderers: {},
helpers: {},
inject: function(renderer) {
var id = Math.random().toString(16).slice(2);
this._renderers[id] = renderer;
this.emit('renderer', {id, renderer});
},
_listeners: {},
sub: function(evt, fn) {
this.on(evt, fn);
return () => this.off(evt, fn);
},
on: function(evt, fn) {
if (!this._listeners[evt]) {
this._listeners[evt] = [];
function detectReactBuildType(renderer) {
try {
var toString = Function.prototype.toString;
if (typeof renderer.version === 'string') {
// React DOM Fiber (16+)
if (renderer.bundleType > 0) {
// This is not a production build.
// We are currently only using 0 (PROD) and 1 (DEV)
// but might add 2 (PROFILE) in the future.
return 'development';
}
this._listeners[evt].push(fn);
},
off: function(evt, fn) {
if (!this._listeners[evt]) {
return;
// The above should cover envification, but we should still make sure
// that the bundle code has been uglified.
var findFiberCode = toString.call(renderer.findFiberByHostInstance);
// Filter out bad results (if that is even possible):
if (findFiberCode.indexOf('function') !== 0) {
// Hope for the best if we're not sure.
return 'production';
}
var ix = this._listeners[evt].indexOf(fn);
if (ix !== -1) {
this._listeners[evt].splice(ix, 1);
// By now we know that it's envified--but what if it's not minified?
// This can be bad too, as it means DEV code is still there.
// FIXME: this is fragile!
// We should replace this check with check on a specially passed
// function. This also doesn't detect lack of dead code elimination
// (although this is not a problem since flat bundles).
if (findFiberCode.indexOf('getClosestInstanceFromNode') !== -1) {
return 'unminified';
}
if (!this._listeners[evt].length) {
this._listeners[evt] = null;
// We're good.
return 'production';
}
if (renderer.Mount && renderer.Mount._renderNewRootComponent) {
// React DOM Stack
var renderRootCode = toString.call(renderer.Mount._renderNewRootComponent);
// Filter out bad results (if that is even possible):
if (renderRootCode.indexOf('function') !== 0) {
// Hope for the best if we're not sure.
return 'production';
}
},
emit: function(evt, data) {
if (this._listeners[evt]) {
this._listeners[evt].map(fn => fn(data));
// Check for React DOM Stack < 15.1.0 in development.
// If it contains "storedMeasure" call, it's wrapped in ReactPerf (DEV only).
// This would be true even if it's minified, as method name still matches.
if (renderRootCode.indexOf('storedMeasure') !== -1) {
return 'development';
}
},
}: Hook),
// For other versions (and configurations) it's not so easy.
// Let's quickly exclude proper production builds.
// If it contains a warning message, it's either a DEV build,
// or an PROD build without proper dead code elimination.
if (renderRootCode.indexOf('should be a pure function') !== -1) {
// Now how do we tell a DEV build from a bad PROD build?
// If we see NODE_ENV, we're going to assume this is a dev build
// because most likely it is referring to an empty shim.
if (renderRootCode.indexOf('NODE_ENV') !== -1) {
return 'development';
}
// If we see "development", we're dealing with an envified DEV build
// (such as the official React DEV UMD).
if (renderRootCode.indexOf('development') !== -1) {
return 'development';
}
// I've seen process.env.NODE_ENV !== 'production' being smartly
// replaced by `true` in DEV by Webpack. I don't know how that
// works but we can safely guard against it because `true` was
// never used in the function source since it was written.
if (renderRootCode.indexOf('true') !== -1) {
return 'development';
}
// By now either it is a production build that has not been minified,
// or (worse) this is a minified development build using non-standard
// environment (e.g. "staging"). We're going to look at whether
// the function argument name is mangled:
if (
// 0.13 to 15
renderRootCode.indexOf('nextElement') !== -1 ||
// 0.12
renderRootCode.indexOf('nextComponent') !== -1
) {
// We can't be certain whether this is a development build or not,
// but it is definitely unminified.
return 'unminified';
} else {
// This is likely a minified development build.
return 'development';
}
}
// By now we know that it's envified and dead code elimination worked,
// but what if it's still not minified? (Is this even possible?)
// Let's check matches for the first argument name.
if (
// 0.13 to 15
renderRootCode.indexOf('nextElement') !== -1 ||
// 0.12
renderRootCode.indexOf('nextComponent') !== -1
) {
return 'unminified';
}
// Seems like we're using the production version.
// Now let's check if we're still on 0.14 or lower:
if (renderRootCode.indexOf('._registerComponent') !== -1) {
// TODO: we can remove the condition above once 16
// is older than a year. Since this branch only runs
// for Stack, we can flip it completely when Stack
// is old enough. The branch for Fiber is above,
// and it can check renderer.version directly.
return 'outdated';
}
// We're all good.
return 'production';
}
} catch (err) {
// Weird environments may exist.
// This code needs a higher fault tolerance
// because it runs even with closed DevTools.
// TODO: should we catch errors in all injected code, and not just this part?
}
return 'production';
}
const hook = ({
// Shared between Stack and Fiber:
_renderers: {},
helpers: {},
inject: function(renderer) {
var id = Math.random().toString(16).slice(2);
hook._renderers[id] = renderer;
var reactBuildType = detectReactBuildType(renderer);
hook.emit('renderer', {id, renderer, reactBuildType});
return id;
},
_listeners: {},
sub: function(evt, fn) {
hook.on(evt, fn);
return () => hook.off(evt, fn);
},
on: function(evt, fn) {
if (!hook._listeners[evt]) {
hook._listeners[evt] = [];
}
hook._listeners[evt].push(fn);
},
off: function(evt, fn) {
if (!hook._listeners[evt]) {
return;
}
var ix = hook._listeners[evt].indexOf(fn);
if (ix !== -1) {
hook._listeners[evt].splice(ix, 1);
}
if (!hook._listeners[evt].length) {
hook._listeners[evt] = null;
}
},
emit: function(evt, data) {
if (hook._listeners[evt]) {
hook._listeners[evt].map(fn => fn(data));
}
},
// Fiber-only:
supportsFiber: true,
_fiberRoots: {},
getFiberRoots(rendererID) {
const roots = hook._fiberRoots;
if (!roots[rendererID]) {
roots[rendererID] = new Set();
}
return roots[rendererID];
},
onCommitFiberUnmount: function(rendererID, fiber) {
// TODO: can we use hook for roots too?
if (hook.helpers[rendererID]) {
hook.helpers[rendererID].handleCommitFiberUnmount(fiber);
}
},
onCommitFiberRoot: function(rendererID, root) {
const mountedRoots = hook.getFiberRoots(rendererID);
const current = root.current;
const isKnownRoot = mountedRoots.has(root);
const isUnmounting = current.memoizedState == null || current.memoizedState.element == null;
// Keep track of mounted roots so we can hydrate when DevTools connect.
if (!isKnownRoot && !isUnmounting) {
mountedRoots.add(root);
} else if (isKnownRoot && isUnmounting) {
mountedRoots.delete(root);
}
if (hook.helpers[rendererID]) {
hook.helpers[rendererID].handleCommitFiberRoot(root);
}
},
});
Object.defineProperty(window, '__REACT_DEVTOOLS_GLOBAL_HOOK__', {
value: (hook : Hook),
});
}
module.exports = installGlobalHook;

@@ -13,7 +13,19 @@ /**

type CompositeUpdater = {
setInProps: ?(path: Array<string>, value: any) => void,
setInState: ?(path: Array<string>, value: any) => void,
setInContext: ?(path: Array<string>, value: any) => void,
forceUpdate: ?() => void,
};
type NativeUpdater = {
setNativeProps: ?(nativeProps: {[key: string]: any}) => void,
};
export type DataType = {
nodeType: 'Native' | 'Wrapper' | 'NativeWrapper' | 'Composite' | 'Text' | 'Empty',
nodeType: 'Native' | 'Wrapper' | 'NativeWrapper' | 'Composite' | 'Text' | 'Portal' | 'Empty',
type: ?(string | AnyFn),
key: ?string,
ref: ?(string | AnyFn),
source: ?Object,
name: ?string,

@@ -25,9 +37,3 @@ props: ?Object,

text: ?string,
updater: ?{
setInProps: ?(path: Array<string>, value: any) => void,
setInState: ?(path: Array<string>, value: any) => void,
setInContext: ?(path: Array<string>, value: any) => void,
// setState: ?(newState: any) => void,
forceUpdate: ?() => void,
},
updater: ?(CompositeUpdater | NativeUpdater),
publicInstance: ?Object,

@@ -47,3 +53,16 @@ };

type BundleType =
// PROD
| 0
// DEV
| 1;
export type ReactRenderer = {
// Fiber
findHostInstanceByFiber: (fiber: Object) => ?NativeType,
findFiberByHostInstance: (hostInstance: NativeType) => ?OpaqueNodeHandle,
version: string,
bundleType: BundleType,
// Stack
Reconciler: {

@@ -90,3 +109,3 @@ mountComponent: AnyFn,

helpers: {[key: string]: Helpers},
inject: (renderer: ReactRenderer) => void,
inject: (renderer: ReactRenderer) => string | null,
emit: (evt: string, data: any) => void,

@@ -97,2 +116,3 @@ sub: (evt: string, handler: Handler) => () => void,

reactDevtoolsAgent?: ?Object,
getFiberRoots: (rendererID : string) => Set<Object>,
};
{
"dependencies": {
"adm-zip": "^0.4.7",
"babel-core": "6.3.21",

@@ -10,3 +11,6 @@ "babel-eslint": "6.0.4",

"babel-preset-stage-0": "6.3.13",
"child-process-promise": "^2.2.1",
"classnames": "2.2.1",
"cli-spinners": "^1.0.0",
"clipboard-js": "^0.3.3",
"es6-symbol": "3.0.2",

@@ -18,11 +22,16 @@ "eslint": "2.10.2",

"flow-bin": "0.23.0",
"fs-extra": "^3.0.1",
"gh-pages": "^1.0.0",
"immutable": "3.7.6",
"jest-cli": "0.9.0-fb2",
"json-loader": "0.5.4",
"log-update": "^2.0.0",
"node-libs-browser": "0.5.3",
"object-assign": "4.0.1",
"raw-loader": "^0.5.1",
"react": "0.14.3",
"react-addons-create-fragment": "0.14.3",
"react-dom": "0.14.3",
"react": "15.4.2",
"react-addons-create-fragment": "15.4.2",
"react-color": "^2.11.7",
"react-dom": "15.4.2",
"react-portal": "^3.1.0",
"webpack": "1.12.9"

@@ -33,4 +42,13 @@ },

"scripts": {
"lint": "./node_modules/.bin/eslint .",
"build:extension": "npm run build:extension:chrome && npm run build:extension:firefox",
"build:extension:chrome": "node ./shells/chrome/build",
"build:extension:firefox": "node ./shells/firefox/build",
"build:standalone": "cd packages/react-devtools-core && npm run build",
"deploy": "cd ./shells/theme-preview && ./build.sh && gh-pages -d .",
"lint": "eslint .",
"postinstall": "lerna bootstrap",
"test": "node --harmony ./node_modules/.bin/jest",
"test:chrome": "node ./shells/chrome/test",
"test:firefox": "node ./shells/firefox/test",
"test:standalone": "cd packages/react-devtools && npm start",
"typecheck": "flow check"

@@ -49,4 +67,13 @@ },

"es6"
],
"modulePathIgnorePatterns": [
"<rootDir>/shells"
]
},
"devDependencies": {
"chrome-launch": "^1.1.4",
"firefox-profile": "^1.0.2",
"lerna": "2.0.0-beta.36",
"web-ext": "^1.10.1"
}
}
# React Developer Tools [![Build Status](https://travis-ci.org/facebook/react-devtools.svg?branch=master)](https://travis-ci.org/facebook/react-devtools)
React Developer Tools is a system that allows you to inspect a React Renderer,
including the Component hierarchy, props, state, and more.
React Developer Tools lets you inspect the React component hierarchy, including component props and state.
There are shells for Chrome (adding it to the Chrome devtools), Firefox,
Atom/Nuclide, and as a standalone Electron app.
It exists both as a browser extension (for [Chrome](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi) and [Firefox](https://addons.mozilla.org/firefox/addon/react-devtools/)), and as a [standalone app](https://github.com/facebook/react-devtools/tree/master/packages/react-devtools) (works with other environments including Safari, IE, and React Native).

@@ -14,2 +12,3 @@ ![](/images/devtools-full.gif)

### Pre-packaged
The official extensions represent the current stable release.

@@ -19,12 +18,18 @@

- [Firefox extension](https://addons.mozilla.org/firefox/addon/react-devtools/)
- Standalone app (coming soon)
- [Standalone app (Safari, React Native, etc)](https://github.com/facebook/react-devtools/blob/master/packages/react-devtools/README.md)
If you inspect an element or launch the developer tools on a React page, you
should see an extra tab called **React** in the inspector.
Opera users can [enable Chrome extensions](https://addons.opera.com/extensions/details/download-chrome-extension-9/) and then install the [Chrome extension](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi).
Check out [For Hacking](#for-hacking) if you want to develop the Developer
Tools or use a pre-prelease version.
## Usage
The extension icon will light up on the websites using React:
<img src="http://i.imgur.com/3tuhIgm.png" alt="Extension icon becomes active" width="500">
On such websites, you will see a tab called React in Chrome Developer Tools:
<img src="http://i.imgur.com/jYieRqi.png" alt="React tab in DevTools" width="500">
A quick way to bring up the DevTools is to right-click on the page and press Inspect.
### Tree View

@@ -35,4 +40,3 @@

source, etc.
- Use the search bar to find components by name
- A red collapser means the component has state/context
- Differently-colored collapser means the component has state/context

@@ -48,10 +52,26 @@ ![](/images/devtools-tree-view.png)

## For Hacking
For changes that don't directly involve Chrome/Firefox/etc. APIs, there is a
"plain" shell that just renders the devtools into an html page along with a
TodoMVC test app. This is by far the quickest way to develop. Check out
[the Readme.md](/shells/plain) in `/shells/plain` for info.
### Search Bar
For other shells (Chrome, Firefox, etc.), see the respective directories in `/shells`.
- Use the search bar to find components by name
![](/images/devtools-search-new.gif)
### Handy Tips
#### Finding Component by a DOM Node
If you inspect a React element on the page using the regular **Elements** tab, then switch over to the **React** tab, that element will be automatically selected in the React tree.
#### Finding DOM Node by a Component
You can right-click any React element in the **React** tab, and choose "Find the DOM node". This will bring you to the corresponding DOM node in the **Elements** tab.
#### Displaying Element Source
You may include the [transform-react-jsx-source](https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-jsx-source) Babel plugin to see the source file and line number of React elements. This information appears in the bottom of the right panel when available. Don't forget to disable it in production! (Tip: if you use [Create React App](https://github.com/facebookincubator/create-react-app) it is already enabled in development.)
#### Usage with React Native and Safari
There is a [standalone version](https://github.com/facebook/react-devtools/blob/master/packages/react-devtools/README.md) that works with other environments such as React Native and Safari.
## FAQ

@@ -61,17 +81,16 @@

The "React" tab won't show up if React can't communicate with the
devtools. When the page loads, the devtools sets a global named
`__REACT_DEVTOOLS_GLOBAL_HOOK__`, then React communicates with that
hook during initialization.
**If you are running your app from a local `file://` URL**, don't forget to check "Allow access to file URLs" on the Chrome Extensions settings page. You can find it by opening Settings > Extensions:
(In React 0.11 and older, it was necessary to expose a global called `React`
for the devtools to function.)
![Allow access to file URLs](http://i.imgur.com/Yt1rmUp.png)
You can test this on the [React website](http://facebook.github.io/react/)
or by inspecting [Facebook](https://www.facebook.com/).
**The React tab won't show up if the site doesn't use React**, or if React can't communicate with the devtools. When the page loads, the devtools sets a global named `__REACT_DEVTOOLS_GLOBAL_HOOK__`, then React communicates with that hook during initialization. You can test this on the [React website](http://facebook.github.io/react/) or by inspecting [Facebook](https://www.facebook.com/).
Currently iframes and Chrome apps/extensions are not inspectable.
**If your app is inside of CodePen**, make sure you are registered. Then press Fork (if it's not your pen), and then choose Change View > Debug. The Debug view is inspectable with DevTools because it doesn't use an iframe.
### Does "Trace React Updates" trace renders?
**If your app is inside an iframe, a Chrome extension, React Native, or in another unusual environment**, try [the standalone version instead](https://github.com/facebook/react-devtools/tree/master/packages/react-devtools). Chrome apps are currently not inspectable.
**If you still have issues** please [report them](https://github.com/facebook/react-devtools/issues/new). Don't forget to specify your OS, browser version, extension version, and the exact instructions to reproduce the issue with a screenshot.
### Does "Highlight Updates" trace renders?
Yes, but it's also tracing if a component *may* render.

@@ -81,3 +100,3 @@ In order to fully understand what counts as an "update", you need to understand how [shouldComponentUpdate](https://facebook.github.io/react/docs/advanced-performance.html#shouldcomponentupdate-in-action) works.

Here "Trace React Updates" will draw a border around every node but C4 and C5.
Here "Highlight Updates" will draw a border around every node but C4 and C5.
Why does it trace components that don't actually update? (via shouldComponentUpdate() -> false)

@@ -87,8 +106,16 @@ This is a limitation of the system used to track updates, and will hopefully change in the future. It doesn't, however, trace the children of components that opt out, as there's no possibility of them updating.

### ProTips
## Contributing
If you inspect a React element on the page using the regular **Elements** tab,
then switch over to the **React** tab, that element will be automatically
selected in the React tree.
For changes that don't directly involve Chrome/Firefox/etc. APIs, there is a
"plain" shell that just renders the devtools into an html page along with a
TodoMVC test app. This is by far the quickest way to develop. Check out
[the Readme.md](/shells/plain) in `/shells/plain` for info.
For other shells (Chrome, Firefox, etc.), see the respective directories in `/shells`.
For a list of good contribution opportunities, check the [good first bug](https://github.com/facebook/react-devtools/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+bug%22) label. We're happy to answer any questions on those issues!
To read more about the community and guidelines for submitting pull requests,
please read the [Contributing document](CONTRIBUTING.md).
## Debugging (in Chrome)

@@ -106,5 +133,1 @@

## Contributing
To read more about the community and guidelines for submitting pull requests,
please read the [Contributing document](CONTRIBUTING.md).
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc