UNPKG

react-dev-inspector

Version:

dev-tool for inspect react components and jump to local IDE for component code.

176 lines (175 loc) 7.06 kB
import { isNativeTagFiber, isReactSymbolFiber, isForwardRef, getDirectParentFiber, getFiberName, getElementFiberUpward, } from './fiber'; /** * react fiber property `_debugSource` created by `@babel/plugin-transform-react-jsx-source` * https://github.com/babel/babel/blob/v7.16.4/packages/babel-plugin-transform-react-jsx-source/src/index.js * * and injected `__source` property used by `React.createElement`, then pass to `ReactElement` * https://github.com/facebook/react/blob/v18.0.0/packages/react/src/ReactElement.js#L189 * https://github.com/facebook/react/blob/v18.0.0/packages/react/src/ReactElement.js#L389 * https://github.com/facebook/react/blob/v18.0.0/packages/react/src/ReactElement.js#L447 * * finally, used by `createFiberFromElement` to become a fiber property `_debugSource`. * https://github.com/facebook/react/blob/v18.0.0/packages/react-reconciler/src/ReactFiber.new.js#L648-L649 */ export const getCodeInfoFromDebugSource = (fiber) => { var _a, _b; if (!fiber) return undefined; const debugSource = ((_a = fiber._debugSource) !== null && _a !== void 0 ? _a : (_b = fiber._debugOwner) === null || _b === void 0 ? void 0 : _b._debugSource); if (!debugSource) return undefined; const { fileName, lineNumber, columnNumber, } = debugSource; if (fileName && lineNumber) { return { lineNumber: String(lineNumber), columnNumber: String(columnNumber !== null && columnNumber !== void 0 ? columnNumber : 1), /** * `fileName` in `_debugSource` is absolutely * --- * * compatible with the incorrect `fileName: "</xxx/file>"` by [rspack](https://github.com/web-infra-dev/rspack) */ absolutePath: fileName.match(/^<.*>$/) ? fileName.replace(/^<|>$/g, '') : fileName, }; } return undefined; }; /** * code location data-attribute props inject by `react-dev-inspector/plugins/babel` */ export const getCodeInfoFromProps = (fiber) => { if (!(fiber === null || fiber === void 0 ? void 0 : fiber.pendingProps)) return undefined; const { 'data-inspector-line': lineNumber, 'data-inspector-column': columnNumber, 'data-inspector-relative-path': relativePath, } = fiber.pendingProps; if (lineNumber && columnNumber && relativePath) { return { lineNumber, columnNumber, relativePath, }; } return undefined; }; export const getCodeInfoFromFiber = (fiber) => { const codeInfos = [ getCodeInfoFromDebugSource(fiber), getCodeInfoFromProps(fiber), ].filter(Boolean); if (!codeInfos.length) return undefined; return Object.assign({}, ...codeInfos); }; /** * give a `base` dom fiber, * and will try to get the human friendly react component `reference` fiber from it; * * rules and examples see below: * ******************************************************* * * if parent is html native tag, `reference` is considered to be as same as `base` * * div div * └─ h1 └─ h1 (<--base) <--reference * └─ span (<--base) <--reference └─ span * * ******************************************************* * * if parent is NOT html native tag, * and parent ONLY have one child (the `base` itself), * then `reference` is considered to be the parent. * * Title <--reference Title * └─ h1 (<--base) └─ h1 (<--base) <--reference * └─ span └─ span * └─ div * * ******************************************************* * * while follow the last one, * "parent" is considered to skip continuous Provider/Customer/ForwardRef components * * Title <- reference Title <- reference * └─ TitleName [ForwardRef] └─ TitleName [ForwardRef] * └─ Context.Customer └─ Context.Customer * └─ Context.Customer └─ Context.Customer * └─ h1 (<- base) └─ h1 (<- base) * └─ span └─ span * └─ div * * Title * └─ TitleName [ForwardRef] * └─ Context.Customer * └─ Context.Customer * └─ h1 (<- base) <- reference * └─ span * └─ div */ export const getReferenceFiber = (baseFiber) => { if (!baseFiber) return undefined; const directParent = getDirectParentFiber(baseFiber); if (!directParent) return undefined; const isParentNative = isNativeTagFiber(directParent); const isOnlyOneChild = !directParent.child.sibling; let referenceFiber = (!isParentNative && isOnlyOneChild) ? directParent : baseFiber; // fallback for cannot find code-info fiber when traverse to root const originReferenceFiber = referenceFiber; while (referenceFiber) { if (getCodeInfoFromFiber(referenceFiber)) return referenceFiber; referenceFiber = referenceFiber.return; } return originReferenceFiber; }; export const getElementCodeInfo = (element) => { const fiber = getElementFiberUpward(element); const referenceFiber = getReferenceFiber(fiber); return getCodeInfoFromFiber(referenceFiber); }; export const getNamedFiber = (baseFiber) => { var _a, _b; let fiber = baseFiber; // fallback for cannot find code-info fiber when traverse to root let originNamedFiber; while (fiber) { let parent = (_a = fiber.return) !== null && _a !== void 0 ? _a : undefined; let forwardParent; while (isReactSymbolFiber(parent)) { if (isForwardRef(parent)) { forwardParent = parent; } parent = (_b = parent === null || parent === void 0 ? void 0 : parent.return) !== null && _b !== void 0 ? _b : undefined; } if (forwardParent) { fiber = forwardParent; } if (getFiberName(fiber)) { if (!originNamedFiber) originNamedFiber = fiber; if (getCodeInfoFromFiber(fiber)) return fiber; } fiber = parent; } return originNamedFiber; }; export const getElementInspect = (element) => { const fiber = getElementFiberUpward(element); const referenceFiber = getReferenceFiber(fiber); const namedFiber = getNamedFiber(referenceFiber); const fiberName = getFiberName(namedFiber); const nodeName = element.nodeName.toLowerCase(); const title = fiberName ? `${nodeName} in <${fiberName}>` : nodeName; return { fiber: referenceFiber, name: fiberName, title, }; };