Skip to content

Commit e487669

Browse files
committed
chore: Update scripts, dependencies, and core functionality
1 parent 5be04cf commit e487669

File tree

6 files changed

+106
-75
lines changed

6 files changed

+106
-75
lines changed

packages/kitchen-sink/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22
"name": "@react-scan/kitchen-sink",
33
"version": "0.0.0",
44
"scripts": {
5-
"build": "vite build",
5+
"build": "rm -rf node_modules/.vite && vite build",
66
"postbuild": "node ../../scripts/version-warning.mjs",
7-
"dev": "vite",
7+
"dev": "rm -rf node_modules/.vite && vite",
88
"preview": "vite preview"
99
},
1010
"dependencies": {
1111
"@vercel/analytics": "^1.4.0",
1212
"babel-plugin-react-compiler": "19.0.0-beta-a7bf2bd-20241110",
13+
"bippy": "^0.0.21",
1314
"react": "19.0.0-rc.1",
1415
"react-dom": "19.0.0-rc.1",
1516
"react-scan": "^0.0.34",

packages/kitchen-sink/src/index.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import './styles.css';
66

77
scan({
88
enabled: true,
9+
dangerouslyForceRunInProduction: true,
910
// monitor: {
1011
// url: 'https://localhost:3000/api/scan',
1112
// },

packages/kitchen-sink/vite.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export default defineConfig({
1313
Inspect(),
1414
],
1515
resolve:
16-
process.env.NODE_ENV === 'production'
16+
process.env.NODE_ENV === 'production' && !process.env.TEST
1717
? {}
1818
: {
1919
alias: {

packages/scan/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-scan",
3-
"version": "0.0.49",
3+
"version": "0.0.50",
44
"description": "Scan your React app for renders",
55
"keywords": [
66
"react",
@@ -242,7 +242,7 @@
242242
"@preact/signals": "^1.3.1",
243243
"@rollup/pluginutils": "^5.1.3",
244244
"@types/node": "^20.17.9",
245-
"bippy": "^0.0.20",
245+
"bippy": "^0.0.23",
246246
"esbuild": "^0.24.0",
247247
"estree-walker": "^3.0.3",
248248
"kleur": "^4.1.5",

packages/scan/src/core/index.ts

Lines changed: 86 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ import type * as React from 'react';
33
import { type Signal, signal } from '@preact/signals';
44
import {
55
getDisplayName,
6+
getRDTHook,
67
getTimings,
78
getType,
89
isCompositeFiber,
910
isInstrumentationActive,
1011
traverseFiber,
12+
detectReactBuildType,
1113
} from 'bippy';
1214
import {
1315
type ActiveOutline,
@@ -49,6 +51,13 @@ export interface Options {
4951
*/
5052
includeChildren?: boolean;
5153

54+
/**
55+
* Force React Scan to run in production (not recommended)
56+
*
57+
* @default false
58+
*/
59+
dangerouslyForceRunInProduction?: boolean;
60+
5261
/**
5362
* Enable/disable geiger sound
5463
*
@@ -196,6 +205,7 @@ export const ReactScanInternals: Internals = {
196205
report: undefined,
197206
alwaysShowLabels: false,
198207
animationSpeed: 'fast',
208+
dangerouslyForceRunInProduction: false,
199209
}),
200210
onRender: null,
201211
scheduledOutlines: [],
@@ -307,65 +317,96 @@ export const isValidFiber = (fiber: Fiber) => {
307317
export const start = () => {
308318
if (typeof window === 'undefined') return;
309319

310-
const existingRoot = document.querySelector('react-scan-root');
311-
if (existingRoot) {
312-
return;
313-
}
320+
const audioContext =
321+
typeof window !== 'undefined'
322+
? new (window.AudioContext ||
323+
// @ts-expect-error -- This is a fallback for Safari
324+
window.webkitAudioContext)()
325+
: null;
314326

315-
// Create container for shadow DOM
316-
const container = document.createElement('div');
317-
container.id = 'react-scan-root';
327+
let ctx: ReturnType<typeof initReactScanOverlay> | null = null;
328+
// TODO: dynamic enable, and inspect-off check
318329

319-
const shadow = container.attachShadow({ mode: 'open' });
330+
const instrumentation = createInstrumentation('devtools', {
331+
onActive() {
332+
if (!Store.monitor.value) {
333+
const rdtHook = getRDTHook();
334+
let isProduction = false;
335+
for (const renderer of rdtHook.renderers.values()) {
336+
const buildType = detectReactBuildType(renderer);
337+
if (
338+
buildType === 'production' &&
339+
!ReactScanInternals.options.value.dangerouslyForceRunInProduction
340+
) {
341+
isProduction = true;
342+
}
343+
}
344+
if (isProduction) {
345+
setOptions({ enabled: false, showToolbar: false });
346+
return;
347+
}
320348

321-
const fragment = document.createDocumentFragment();
349+
const existingRoot = document.querySelector('react-scan-root');
350+
if (existingRoot) {
351+
return;
352+
}
322353

323-
// add styles
324-
const cssStyles = document.createElement('style');
325-
cssStyles.textContent = styles;
354+
const container = document.createElement('div');
355+
container.id = 'react-scan-root';
326356

327-
// Create SVG sprite sheet node directly
328-
const iconSprite = new DOMParser().parseFromString(
329-
ICONS,
330-
'image/svg+xml',
331-
).documentElement;
332-
shadow.appendChild(iconSprite);
357+
const shadow = container.attachShadow({ mode: 'open' });
333358

334-
// add toolbar root
335-
const root = document.createElement('div');
336-
root.id = 'react-scan-toolbar-root';
337-
root.className = 'absolute z-2147483647';
359+
const fragment = document.createDocumentFragment();
338360

339-
fragment.appendChild(cssStyles);
340-
fragment.appendChild(root);
361+
const cssStyles = document.createElement('style');
362+
cssStyles.textContent = styles;
341363

342-
shadow.appendChild(fragment);
364+
const iconSprite = new DOMParser().parseFromString(
365+
ICONS,
366+
'image/svg+xml',
367+
).documentElement;
368+
shadow.appendChild(iconSprite);
343369

344-
// Add container to document first (so shadow DOM is available)
345-
document.documentElement.appendChild(container);
370+
const root = document.createElement('div');
371+
root.id = 'react-scan-toolbar-root';
372+
root.className = 'absolute z-2147483647';
346373

347-
const options = ReactScanInternals.options.value;
374+
fragment.appendChild(cssStyles);
375+
fragment.appendChild(root);
348376

349-
const ctx = initReactScanOverlay();
350-
if (!ctx) return;
377+
shadow.appendChild(fragment);
351378

352-
createInspectElementStateMachine(shadow);
379+
document.documentElement.appendChild(container);
353380

354-
const audioContext =
355-
typeof window !== 'undefined'
356-
? new (window.AudioContext ||
357-
// @ts-expect-error -- This is a fallback for Safari
358-
window.webkitAudioContext)()
359-
: null;
381+
ctx = initReactScanOverlay();
382+
if (!ctx) return;
360383

361-
globalThis.__REACT_SCAN__ = {
362-
ReactScanInternals,
363-
};
384+
createInspectElementStateMachine(shadow);
385+
386+
globalThis.__REACT_SCAN__ = {
387+
ReactScanInternals,
388+
};
389+
390+
if (ReactScanInternals.options.value.showToolbar) {
391+
toolbarContainer = createToolbar(shadow);
392+
}
393+
394+
// Add this right after creating the container
395+
container.setAttribute('part', 'scan-root');
396+
397+
// Add this before creating the Shadow DOM
398+
const mainStyles = document.createElement('style');
399+
mainStyles.textContent = `
400+
html[data-theme="light"] react-scan-root::part(scan-root) {
401+
--icon-color: rgba(0, 0, 0, 0.8);
402+
}
403+
404+
html[data-theme="dark"] react-scan-root::part(scan-root) {
405+
--icon-color: rgba(255, 255, 255, 0.8);
406+
}
407+
`;
408+
document.head.appendChild(mainStyles);
364409

365-
// TODO: dynamic enable, and inspect-off check
366-
const instrumentation = createInstrumentation('devtools', {
367-
onActive() {
368-
if (!Store.monitor.value) {
369410
const existingOverlay = document.querySelector('react-scan-overlay');
370411
if (existingOverlay) {
371412
return;
@@ -388,7 +429,7 @@ export const start = () => {
388429
},
389430
isValidFiber,
390431
onRender(fiber, renders) {
391-
if (ReactScanInternals.instrumentation?.isPaused.value) {
432+
if (Boolean(ReactScanInternals.instrumentation?.isPaused.value) || !ctx) {
392433
// don't draw if it's paused
393434
return;
394435
}
@@ -430,26 +471,6 @@ export const start = () => {
430471

431472
ReactScanInternals.instrumentation = instrumentation;
432473

433-
if (options.showToolbar) {
434-
toolbarContainer = createToolbar(shadow);
435-
}
436-
437-
// Add this right after creating the container
438-
container.setAttribute('part', 'scan-root');
439-
440-
// Add this before creating the Shadow DOM
441-
const mainStyles = document.createElement('style');
442-
mainStyles.textContent = `
443-
html[data-theme="light"] react-scan-root::part(scan-root) {
444-
--icon-color: rgba(0, 0, 0, 0.8);
445-
}
446-
447-
html[data-theme="dark"] react-scan-root::part(scan-root) {
448-
--icon-color: rgba(255, 255, 255, 0.8);
449-
}
450-
`;
451-
document.head.appendChild(mainStyles);
452-
453474
// TODO: add an visual error indicator that it didn't load
454475
if (!Store.monitor.value) {
455476
setTimeout(() => {

pnpm-lock.yaml

Lines changed: 13 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)