Initial commit
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
// Walks the React fiber tree (via __REACT_DEVTOOLS_GLOBAL_HOOK__) to find framer-motion
|
||||
// components and dump their animate/variants/transition/whileInView/whileHover props.
|
||||
// Called from __CLONE_FINALIZE__.
|
||||
(() => {
|
||||
function cssPath(el) {
|
||||
if (!el || el.nodeType !== 1) return null;
|
||||
if (el.id) return `#${el.id}`;
|
||||
const parts = [];
|
||||
while (el && el.nodeType === 1 && parts.length < 10) {
|
||||
let s = el.tagName.toLowerCase();
|
||||
const p = el.parentNode;
|
||||
if (p) {
|
||||
const sibs = Array.from(p.children).filter((c) => c.tagName === el.tagName);
|
||||
if (sibs.length > 1) s += `:nth-of-type(${sibs.indexOf(el) + 1})`;
|
||||
}
|
||||
parts.unshift(s);
|
||||
el = el.parentNode;
|
||||
}
|
||||
return parts.join(' > ');
|
||||
}
|
||||
|
||||
function isMotionComponent(fiber) {
|
||||
const type = fiber?.type;
|
||||
if (!type) return false;
|
||||
if (type.$$typeof && type.render && type.render.displayName?.startsWith('motion.')) return true;
|
||||
const dn = type.displayName || type.name;
|
||||
if (typeof dn === 'string' && (dn.startsWith('motion.') || dn === 'MotionComponent')) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function serializeProp(v) {
|
||||
try {
|
||||
if (v == null) return v;
|
||||
if (typeof v === 'function') return null;
|
||||
if (typeof v === 'object') {
|
||||
const out = {};
|
||||
for (const k of Object.keys(v)) {
|
||||
const val = v[k];
|
||||
if (typeof val === 'function') continue;
|
||||
if (val && typeof val === 'object' && val.nodeType) continue;
|
||||
out[k] = val;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
return v;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function findFibers(fiber, out) {
|
||||
if (!fiber) return;
|
||||
if (isMotionComponent(fiber)) {
|
||||
const props = fiber.memoizedProps || {};
|
||||
let node = fiber.stateNode;
|
||||
if (!(node instanceof Element)) {
|
||||
// Walk down to the first DOM host for selector extraction.
|
||||
let cursor = fiber.child;
|
||||
while (cursor) {
|
||||
if (cursor.stateNode instanceof Element) {
|
||||
node = cursor.stateNode;
|
||||
break;
|
||||
}
|
||||
cursor = cursor.child;
|
||||
}
|
||||
}
|
||||
out.push({
|
||||
selector: node instanceof Element ? cssPath(node) : null,
|
||||
initial: serializeProp(props.initial),
|
||||
animate: serializeProp(props.animate),
|
||||
exit: serializeProp(props.exit),
|
||||
variants: serializeProp(props.variants),
|
||||
transition: serializeProp(props.transition),
|
||||
whileHover: serializeProp(props.whileHover),
|
||||
whileTap: serializeProp(props.whileTap),
|
||||
whileInView: serializeProp(props.whileInView),
|
||||
viewport: serializeProp(props.viewport),
|
||||
});
|
||||
}
|
||||
if (fiber.child) findFibers(fiber.child, out);
|
||||
if (fiber.sibling) findFibers(fiber.sibling, out);
|
||||
}
|
||||
|
||||
const prevFinalize = window.__CLONE_FINALIZE__;
|
||||
window.__CLONE_FINALIZE__ = function () {
|
||||
if (prevFinalize)
|
||||
try {
|
||||
prevFinalize();
|
||||
} catch {}
|
||||
try {
|
||||
const hook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
if (!hook || !hook.renderers) return;
|
||||
for (const [, renderer] of hook.renderers) {
|
||||
if (!renderer) continue;
|
||||
const roots = renderer.findFiberByHostInstance ? null : renderer.roots || null;
|
||||
const scanFrom = [];
|
||||
if (hook.getFiberRoots) {
|
||||
const rootSet = hook.getFiberRoots(1) || new Set();
|
||||
for (const r of rootSet) scanFrom.push(r.current);
|
||||
}
|
||||
for (const fiber of scanFrom) findFibers(fiber, window.__CLONE_CAPTURE__.framer);
|
||||
}
|
||||
} catch (e) {
|
||||
window.__CLONE_CAPTURE__.framer.push({ error: String(e) });
|
||||
}
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user