146 lines
3.8 KiB
JavaScript
146 lines
3.8 KiB
JavaScript
import { options, Fragment } from 'preact';
|
|
|
|
/**
|
|
* Get human readable name of the component/dom node
|
|
* @param {import('./internal').VNode} vnode
|
|
* @param {import('./internal').VNode} vnode
|
|
* @returns {string}
|
|
*/
|
|
export function getDisplayName(vnode) {
|
|
if (vnode.type === Fragment) {
|
|
return 'Fragment';
|
|
} else if (typeof vnode.type == 'function') {
|
|
return vnode.type.displayName || vnode.type.name;
|
|
} else if (typeof vnode.type == 'string') {
|
|
return vnode.type;
|
|
}
|
|
|
|
return '#text';
|
|
}
|
|
|
|
/**
|
|
* Used to keep track of the currently rendered `vnode` and print it
|
|
* in debug messages.
|
|
*/
|
|
let renderStack = [];
|
|
|
|
/**
|
|
* Keep track of the current owners. An owner describes a component
|
|
* which was responsible to render a specific `vnode`. This exclude
|
|
* children that are passed via `props.children`, because they belong
|
|
* to the parent owner.
|
|
*
|
|
* ```jsx
|
|
* const Foo = props => <div>{props.children}</div> // div's owner is Foo
|
|
* const Bar = props => {
|
|
* return (
|
|
* <Foo><span /></Foo> // Foo's owner is Bar, span's owner is Bar
|
|
* )
|
|
* }
|
|
* ```
|
|
*
|
|
* Note: A `vnode` may be hoisted to the root scope due to compiler
|
|
* optimiztions. In these cases the `_owner` will be different.
|
|
*/
|
|
let ownerStack = [];
|
|
|
|
/**
|
|
* Get the currently rendered `vnode`
|
|
* @returns {import('./internal').VNode | null}
|
|
*/
|
|
export function getCurrentVNode() {
|
|
return renderStack.length > 0 ? renderStack[renderStack.length - 1] : null;
|
|
}
|
|
|
|
/**
|
|
* If the user doesn't have `@babel/plugin-transform-react-jsx-source`
|
|
* somewhere in his tool chain we can't print the filename and source
|
|
* location of a component. In that case we just omit that, but we'll
|
|
* print a helpful message to the console, notifying the user of it.
|
|
*/
|
|
let showJsxSourcePluginWarning = true;
|
|
|
|
/**
|
|
* Check if a `vnode` is a possible owner.
|
|
* @param {import('./internal').VNode} vnode
|
|
*/
|
|
function isPossibleOwner(vnode) {
|
|
return typeof vnode.type == 'function' && vnode.type != Fragment;
|
|
}
|
|
|
|
/**
|
|
* Return the component stack that was captured up to this point.
|
|
* @param {import('./internal').VNode} vnode
|
|
* @returns {string}
|
|
*/
|
|
export function getOwnerStack(vnode) {
|
|
const stack = [vnode];
|
|
let next = vnode;
|
|
while (next._owner != null) {
|
|
stack.push(next._owner);
|
|
next = next._owner;
|
|
}
|
|
|
|
return stack.reduce((acc, owner) => {
|
|
acc += ` in ${getDisplayName(owner)}`;
|
|
|
|
const source = owner.__source;
|
|
if (source) {
|
|
acc += ` (at ${source.fileName}:${source.lineNumber})`;
|
|
} else if (showJsxSourcePluginWarning) {
|
|
console.warn(
|
|
'Add @babel/plugin-transform-react-jsx-source to get a more detailed component stack. Note that you should not add it to production builds of your App for bundle size reasons.'
|
|
);
|
|
}
|
|
showJsxSourcePluginWarning = false;
|
|
|
|
return (acc += '\n');
|
|
}, '');
|
|
}
|
|
|
|
/**
|
|
* Setup code to capture the component trace while rendering. Note that
|
|
* we cannot simply traverse `vnode._parent` upwards, because we have some
|
|
* debug messages for `this.setState` where the `vnode` is `undefined`.
|
|
*/
|
|
export function setupComponentStack() {
|
|
let oldDiff = options._diff;
|
|
let oldDiffed = options.diffed;
|
|
let oldRoot = options._root;
|
|
let oldVNode = options.vnode;
|
|
let oldRender = options._render;
|
|
|
|
options.diffed = vnode => {
|
|
if (isPossibleOwner(vnode)) {
|
|
ownerStack.pop();
|
|
}
|
|
renderStack.pop();
|
|
if (oldDiffed) oldDiffed(vnode);
|
|
};
|
|
|
|
options._diff = vnode => {
|
|
if (isPossibleOwner(vnode)) {
|
|
renderStack.push(vnode);
|
|
}
|
|
if (oldDiff) oldDiff(vnode);
|
|
};
|
|
|
|
options._root = (vnode, parent) => {
|
|
ownerStack = [];
|
|
if (oldRoot) oldRoot(vnode, parent);
|
|
};
|
|
|
|
options.vnode = vnode => {
|
|
vnode._owner =
|
|
ownerStack.length > 0 ? ownerStack[ownerStack.length - 1] : null;
|
|
if (oldVNode) oldVNode(vnode);
|
|
};
|
|
|
|
options._render = vnode => {
|
|
if (isPossibleOwner(vnode)) {
|
|
ownerStack.push(vnode);
|
|
}
|
|
|
|
if (oldRender) oldRender(vnode);
|
|
};
|
|
}
|