Skip to content

Commit 47f5d85

Browse files
committed
sources
1 parent 8ea0549 commit 47f5d85

13 files changed

+238
-112
lines changed

README.md

+24
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,30 @@ import { isValidFiber } from 'bippy';
397397
console.log(isValidFiber(fiber));
398398
```
399399

400+
## getFiberFromHostInstance
401+
402+
returns the fiber associated with a given host instance (e.g., a DOM element).
403+
404+
```typescript
405+
import { getFiberFromHostInstance } from 'bippy';
406+
407+
const fiber = getFiberFromHostInstance(document.querySelector('div'));
408+
console.log(fiber);
409+
```
410+
411+
## getLatestFiber
412+
413+
returns the latest fiber (since it may be double-buffered). usually use this in combination with `getFiberFromHostInstance`.
414+
415+
```typescript
416+
import { getLatestFiber } from 'bippy';
417+
418+
const latestFiber = getLatestFiber(
419+
getFiberFromHostInstance(document.querySelector('div'))
420+
);
421+
console.log(latestFiber);
422+
```
423+
400424
## examples
401425

402426
the best way to understand bippy is to [read the source code](https://github.com/aidenybai/bippy/blob/main/src/core.ts). here are some examples of how you can use it:

packages/bippy/package.json

+20
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,26 @@
5757
"default": "./dist/source.cjs"
5858
}
5959
},
60+
"./jsx-runtime": {
61+
"import": {
62+
"types": "./dist/jsx-runtime.d.ts",
63+
"default": "./dist/jsx-runtime.js"
64+
},
65+
"require": {
66+
"types": "./dist/jsx-runtime.d.cts",
67+
"default": "./dist/jsx-runtime.cjs"
68+
}
69+
},
70+
"./jsx-dev-runtime": {
71+
"import": {
72+
"types": "./dist/jsx-dev-runtime.d.ts",
73+
"default": "./dist/jsx-dev-runtime.js"
74+
},
75+
"require": {
76+
"types": "./dist/jsx-dev-runtime.d.cts",
77+
"default": "./dist/jsx-dev-runtime.cjs"
78+
}
79+
},
6080
"./experiments/inspect": {
6181
"import": {
6282
"types": "./dist/experiments/inspect.d.ts",

packages/bippy/src/core.ts

+25
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,26 @@ export const isInstrumentationActive = (): boolean => {
524524
);
525525
};
526526

527+
/**
528+
* Returns the latest fiber (since it may be double-buffered).
529+
*/
530+
export const getLatestFiber = (fiber: Fiber): Fiber => {
531+
const alternate = fiber.alternate;
532+
if (!alternate) return fiber;
533+
if (alternate.actualStartTime && fiber.actualStartTime) {
534+
return alternate.actualStartTime > fiber.actualStartTime
535+
? alternate
536+
: fiber;
537+
}
538+
for (const root of _fiberRoots) {
539+
const latestFiber = traverseFiber(root.current, (innerFiber) => {
540+
if (innerFiber === fiber) return true;
541+
});
542+
return latestFiber || alternate;
543+
}
544+
return fiber;
545+
};
546+
527547
export type RenderPhase = 'mount' | 'update' | 'unmount';
528548

529549
export type RenderHandler = <S>(
@@ -923,6 +943,8 @@ export const getFiberFromHostInstance = <T>(hostInstance: T): Fiber | null => {
923943

924944
export const INSTALL_ERROR = new Error();
925945

946+
export const _fiberRoots = new Set<FiberRoot>();
947+
926948
export const secure = (
927949
options: InstrumentationOptions,
928950
secureOptions: {
@@ -976,6 +998,9 @@ export const secure = (
976998
const onCommitFiberRoot = options.onCommitFiberRoot;
977999
if (onCommitFiberRoot) {
9781000
options.onCommitFiberRoot = (rendererID, root, priority) => {
1001+
if (!_fiberRoots.has(root)) {
1002+
_fiberRoots.add(root);
1003+
}
9791004
try {
9801005
onCommitFiberRoot(rendererID, root, priority);
9811006
} catch (err) {

packages/bippy/src/experiments/inspect.tsx

+10
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ import {
2121
getNearestHostFiber,
2222
getRDTHook,
2323
hasRDTHook,
24+
isCompositeFiber,
2425
isInstrumentationActive,
2526
traverseFiber,
2627
} from '../index.js';
28+
import { getFiberSource } from '../source.js';
2729

2830
const FIBER_PROP_EXPLANATIONS: Record<string, string> = {
2931
tag: 'Numeric type identifier for this fiber (e.g. 1=FunctionComponent, 5=HostComponent)',
@@ -252,6 +254,14 @@ export const RawInspector = React.memo(
252254
const getFiberForDisplay = useCallback(() => {
253255
if (selectedFiber) return selectedFiber;
254256
const fiber = getFiberFromHostInstance(element);
257+
const parentFiber = traverseFiber(
258+
fiber,
259+
(f) => {
260+
if (isCompositeFiber(f)) return true;
261+
},
262+
true,
263+
);
264+
console.log('fiber', parentFiber && getFiberSource(parentFiber));
255265

256266
return fiber;
257267
}, [selectedFiber, element]);

packages/bippy/src/index.ts

+3-8
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
1-
import { getRDTHook, isClientEnvironment } from './rdt-hook.js';
1+
import { safelyInstallRDTHook } from './rdt-hook.js';
22

3-
export * from './core.js';
3+
safelyInstallRDTHook();
44

5-
try {
6-
// __REACT_DEVTOOLS_GLOBAL_HOOK__ must exist before React is ever executed
7-
if (isClientEnvironment()) {
8-
getRDTHook();
9-
}
10-
} catch {}
5+
export * from './core.js';

packages/bippy/src/jsx-dev-runtime.ts

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import {
2+
Fragment,
3+
jsxDEV as jsxDEVImpl,
4+
type JSXSource,
5+
} from 'react/jsx-dev-runtime';
6+
7+
export * from 'react/jsx-dev-runtime';
8+
9+
export { Fragment };
10+
11+
export const jsxDEV = (
12+
type: React.ElementType,
13+
originalProps: unknown,
14+
key: React.Key | undefined,
15+
isStatic: boolean,
16+
source?: JSXSource,
17+
self?: unknown,
18+
) => {
19+
let props = originalProps;
20+
try {
21+
if (
22+
originalProps &&
23+
typeof originalProps === 'object' &&
24+
source &&
25+
String(type) !== 'Symbol(react.fragment)'
26+
) {
27+
// prevent attributes from rendering in DOM for host fibers
28+
if (typeof type === 'string') {
29+
const proto = Object.getPrototypeOf(originalProps);
30+
const descriptors = Object.getOwnPropertyDescriptors(originalProps);
31+
descriptors.__source = {
32+
value: source,
33+
enumerable: false,
34+
configurable: true,
35+
writable: true,
36+
};
37+
props = Object.create(proto, descriptors);
38+
} else {
39+
// @ts-expect-error
40+
props.__source = source;
41+
}
42+
}
43+
} catch {}
44+
return jsxDEVImpl(type, props, key, isStatic, source, self);
45+
};

packages/bippy/src/jsx-runtime.ts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import './index.js';
2+
export * from 'react/jsx-runtime';

packages/bippy/src/rdt-hook.ts

+39-14
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,23 @@ export const installRDTHook = (
7272
};
7373
try {
7474
objectDefineProperty(globalThis, '__REACT_DEVTOOLS_GLOBAL_HOOK__', {
75-
value: rdtHook,
75+
get() {
76+
return rdtHook;
77+
},
78+
set(newHook) {
79+
if (newHook && typeof newHook === 'object') {
80+
const ourRenderers = rdtHook.renderers;
81+
globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__ = newHook;
82+
if (ourRenderers.size > 0) {
83+
ourRenderers.forEach((renderer, id) => {
84+
newHook.renderers.set(id, renderer);
85+
});
86+
patchRDTHook(onActive);
87+
}
88+
}
89+
},
7690
configurable: true,
77-
writable: true,
91+
enumerable: true,
7892
});
7993
// [!] this is a hack for chrome extensions - if we install before React DevTools, we could accidently prevent React DevTools from installing:
8094
// https://github.com/facebook/react/blob/18eaf51bd51fed8dfed661d64c306759101d0bfd/packages/react-devtools-extensions/src/contentScripts/installHook.js#L30C6-L30C27
@@ -110,7 +124,6 @@ export const patchRDTHook = (onActive?: () => unknown): void => {
110124
const rdtHook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
111125
if (!rdtHook) return;
112126
if (!rdtHook._instrumentationSource) {
113-
isReactRefreshOverride = isReactRefresh(rdtHook);
114127
rdtHook.checkDCE = checkDCE;
115128
rdtHook.supportsFiber = true;
116129
rdtHook.supportsFlight = true;
@@ -128,21 +141,21 @@ export const patchRDTHook = (onActive?: () => unknown): void => {
128141
isReactRefreshOverride = true;
129142
// but since the underlying implementation doens't care,
130143
// it's ok: https://github.com/facebook/react/blob/18eaf51bd51fed8dfed661d64c306759101d0bfd/packages/react-refresh/src/ReactFreshRuntime.js#L430
131-
// @ts-expect-error this is not actually a ReactRenderer,
132-
let nextID = rdtHook.inject(null);
144+
const nextID = rdtHook.inject({
145+
// @ts-expect-error this is not actually a ReactRenderer,
146+
scheduleRefresh() {},
147+
});
133148
if (nextID) {
134149
rdtHook._instrumentationIsActive = true;
135150
}
136-
rdtHook.inject = () => nextID++;
137-
} else {
138-
rdtHook.inject = (renderer) => {
139-
const id = prevInject(renderer);
140-
rdtHook._instrumentationIsActive = true;
141-
// biome-ignore lint/complexity/noForEach: prefer forEach for Set
142-
onActiveListeners.forEach((listener) => listener());
143-
return id;
144-
};
145151
}
152+
rdtHook.inject = (renderer) => {
153+
const id = prevInject(renderer);
154+
rdtHook._instrumentationIsActive = true;
155+
// biome-ignore lint/complexity/noForEach: prefer forEach for Set
156+
onActiveListeners.forEach((listener) => listener());
157+
return id;
158+
};
146159
}
147160
if (
148161
rdtHook.renderers.size ||
@@ -183,3 +196,15 @@ export const isClientEnvironment = (): boolean => {
183196
window.navigator?.product === 'ReactNative'),
184197
);
185198
};
199+
200+
/**
201+
* Usually used purely for side effect
202+
*/
203+
export const safelyInstallRDTHook = () => {
204+
try {
205+
// __REACT_DEVTOOLS_GLOBAL_HOOK__ must exist before React is ever executed
206+
if (isClientEnvironment()) {
207+
getRDTHook();
208+
}
209+
} catch {}
210+
};

packages/bippy/src/source.ts

+20-5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
getRDTHook,
1111
getDisplayName,
1212
} from './index.js';
13+
import React from 'react';
1314

1415
interface FiberSource {
1516
fileName: string;
@@ -107,7 +108,7 @@ const parseStackFrame = async (frame: string): Promise<FiberSource | null> => {
107108
column: columnNumber,
108109
});
109110
return {
110-
fileName: result.source || '',
111+
fileName: sourcemap.file || '',
111112
lineNumber: result.line || 0,
112113
columnNumber: result.column || 0,
113114
};
@@ -244,8 +245,7 @@ const describeNativeComponentFrame = (
244245
s--;
245246
c--;
246247
if (c < 0 || sampleLines[s] !== controlLines[c]) {
247-
// biome-ignore lint/style/useTemplate: <explanation>
248-
let frame = '\n' + sampleLines[s].replace(' at new ', ' at ');
248+
let frame = `\n${sampleLines[s].replace(' at new ', ' at ')}`;
249249
const displayName = getDisplayName(fn);
250250
if (displayName && frame.includes('<anonymous>')) {
251251
frame = frame.replace('<anonymous>', displayName);
@@ -270,6 +270,13 @@ const describeNativeComponentFrame = (
270270
return syntheticFrame;
271271
};
272272

273+
const ReactSharedInternals =
274+
// biome-ignore lint/suspicious/noExplicitAny: OK
275+
(React as any)
276+
?.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE ||
277+
// biome-ignore lint/suspicious/noExplicitAny: OK
278+
(React as any)?.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
279+
273280
export const getFiberSource = async (
274281
fiber: Fiber,
275282
): Promise<FiberSource | null> => {
@@ -286,15 +293,23 @@ export const getFiberSource = async (
286293
};
287294
}
288295

296+
// passed by bippy's jsx-dev-runtime
297+
if (fiber.memoizedProps?.__source) {
298+
return fiber.memoizedProps.__source as FiberSource;
299+
}
300+
289301
const rdtHook = getRDTHook();
290-
let currentDispatcherRef: React.MutableRefObject<unknown> | undefined;
302+
303+
let currentDispatcherRef: React.MutableRefObject<unknown> | undefined =
304+
ReactSharedInternals?.ReactCurrentDispatcher || ReactSharedInternals?.H;
291305
for (const renderer of rdtHook.renderers.values()) {
292306
// biome-ignore lint/suspicious/noExplicitAny: OK
293307
currentDispatcherRef = (renderer as any).currentDispatcherRef;
294308
if (currentDispatcherRef) {
295309
break;
296310
}
297311
}
312+
298313
if (!currentDispatcherRef) {
299314
return null;
300315
}
@@ -317,7 +332,7 @@ export const getFiberSource = async (
317332
const frame = describeNativeComponentFrame(
318333
componentFunction,
319334
fiber.tag === ClassComponentTag,
320-
currentDispatcherRef,
335+
ReactSharedInternals,
321336
);
322337
return parseStackFrame(frame);
323338
};

packages/bippy/tsup.config.ts

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ export default defineConfig([
4040
entry: [
4141
'./src/index.ts',
4242
'./src/core.ts',
43+
'./src/jsx-runtime.ts',
44+
'./src/jsx-dev-runtime.ts',
4345
'./src/experiments/inspect.tsx',
4446
'./src/experiments/shrinkwrap.ts',
4547
'./src/source.ts',

packages/kitchen-sink/package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@
55
"scripts": {
66
"preview": "vite preview",
77
"build": "vite build",
8-
"dev": "vite"
8+
"dev": "rm -rf node_modules/.vite && pnpm i && vite"
99
},
1010
"dependencies": {
1111
"@codesandbox/sandpack-react": "^2.19.10",
1212
"@codesandbox/sandpack-themes": "^2.0.21",
1313
"@tanstack/react-router": "^1.95.1",
1414
"bippy": "^0.3.0",
1515
"clsx": "^2.1.1",
16-
"react": "18.3.1",
17-
"react-dom": "18.3.1",
16+
"react": "19.0.0",
17+
"react-dom": "19.0.0",
1818
"react-inspector": "^6.0.2",
1919
"sugar-high": "^0.7.5",
2020
"tailwind-merge": "^2.6.0"

packages/kitchen-sink/vite.config.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@ export default defineConfig({
1313
plugins: [
1414
mkcert(),
1515
TanStackRouterVite(),
16-
// react({
17-
// // babel: {
18-
// // plugins: [['babel-plugin-react-compiler', {}]],
19-
// // },
20-
// }),
16+
react({
17+
jsxImportSource: 'bippy/dist',
18+
// babel: {
19+
// plugins: [['babel-plugin-react-compiler', {}]],
20+
// },
21+
}),
2122
tailwindcss(),
2223
],
2324
define: {

0 commit comments

Comments
 (0)