@samitouri / QOS-React-2 / commits / 4ba20579da

Scheduling Profiler: De-emphasize React internal frames (#22588)

This commit adds code to all React bundles to explicitly register the beginning and ending of the module. This is done by creating Error objects (which capture the file name, line number, and column number) and passing them explicitly to a DevTools hook (when present). Next, as the Scheduling Profiler logs metadata to the User Timing API, it prints these module ranges along with other metadata (like Lane values and profiler version number). Lastly, the Scheduling Profiler UI compares stack frames to these ranges when drawing the flame graph and dims or de-emphasizes frames that fall within an internal module. The net effect of this is that user code (and 3rd party code) stands out clearly in the flame graph while React internal modules are dimmed. Internal module ranges are completely optional. Older profiling samples, or ones recorded without the React DevTools extension installed, will simply not dim the internal frames.

Brian Vaughn committed Oct 21, 2021 at 14:40 UTC 4ba20579daf119639567f69a0cb38128532754c6
21 files changed +543 -15
packages/react-devtools-extensions/src/checkForDuplicateInstallations.js
+5 -3
@@ -9,13 +9,15 @@
9
10 declare var chrome: any;
11
12 -import {__DEBUG__} from 'react-devtools-shared/src/constants';
12 +import {
13 + INTERNAL_EXTENSION_ID,
14 + LOCAL_EXTENSION_ID,
15 + __DEBUG__,
16 +} from 'react-devtools-shared/src/constants';
17 import {getBrowserName} from './utils';
18 import {
19 EXTENSION_INSTALL_CHECK,
20 EXTENSION_INSTALLATION_TYPE,
17 - INTERNAL_EXTENSION_ID,
18 - LOCAL_EXTENSION_ID,
21 } from './constants';
22
23 const IS_CHROME = getBrowserName() === 'Chrome';
packages/react-devtools-extensions/src/constants.js
+6 -4
@@ -7,6 +7,12 @@
7 * @flow strict-local
8 */
9
10 +import {
11 + CHROME_WEBSTORE_EXTENSION_ID,
12 + INTERNAL_EXTENSION_ID,
13 + LOCAL_EXTENSION_ID,
14 +} from 'react-devtools-shared/src/constants';
15 +
16 declare var chrome: any;
17
18 export const CURRENT_EXTENSION_ID = chrome.runtime.id;
@@ -15,10 +21,6 @@ export const EXTENSION_INSTALL_CHECK = 'extension-install-check';
21 export const SHOW_DUPLICATE_EXTENSION_WARNING =
22 'show-duplicate-extension-warning';
23
18 -export const CHROME_WEBSTORE_EXTENSION_ID = 'fmkadmapgofadopljbjfkapdkoienihi';
19 -export const INTERNAL_EXTENSION_ID = 'dnjnjgbfilfphmojnmhliehogmojhclc';
20 -export const LOCAL_EXTENSION_ID = 'ikiahnapldjmdmpkmfhjdjilojjhgcbf';
21 -
24 export const EXTENSION_INSTALLATION_TYPE:
25 | 'public'
26 | 'internal'
packages/react-devtools-scheduling-profiler/src/CanvasPage.js
+1
@@ -374,6 +374,7 @@ function AutoSizedCanvas({
374 surface,
375 defaultFrame,
376 data.flamechart,
377 + data.internalModuleSourceToRanges,
378 data.duration,
379 );
380 flamechartViewRef.current = flamechartView;
packages/react-devtools-scheduling-profiler/src/content-views/FlamechartView.js
+34 -6
@@ -11,6 +11,7 @@ import type {
11 Flamechart,
12 FlamechartStackFrame,
13 FlamechartStackLayer,
14 + InternalModuleSourceToRanges,
15 } from '../types';
16 import type {
17 Interaction,
@@ -30,6 +31,7 @@ import {
31 rectIntersectsRect,
32 verticallyStackedLayout,
33 } from '../view-base';
34 +import {isInternalModule} from './utils/moduleFilters';
35 import {
36 durationToWidth,
37 positioningScaleFactor,
@@ -76,6 +78,8 @@ class FlamechartStackLayerView extends View {
78 /** A set of `stackLayer`'s frames, for efficient lookup. */
79 _stackFrameSet: Set<FlamechartStackFrame>;
80
81 + _internalModuleSourceToRanges: InternalModuleSourceToRanges;
82 +
83 _intrinsicSize: Size;
84
85 _hoveredStackFrame: FlamechartStackFrame | null = null;
@@ -85,11 +89,13 @@ class FlamechartStackLayerView extends View {
89 surface: Surface,
90 frame: Rect,
91 stackLayer: FlamechartStackLayer,
92 + internalModuleSourceToRanges: InternalModuleSourceToRanges,
93 duration: number,
94 ) {
95 super(surface, frame);
96 this._stackLayer = stackLayer;
97 this._stackFrameSet = new Set(stackLayer);
98 + this._internalModuleSourceToRanges = internalModuleSourceToRanges;
99 this._intrinsicSize = {
100 width: duration,
101 height: FLAMECHART_FRAME_HEIGHT,
@@ -160,9 +166,19 @@ class FlamechartStackLayerView extends View {
166 }
167
168 const showHoverHighlight = _hoveredStackFrame === _stackLayer[i];
163 - context.fillStyle = showHoverHighlight
164 - ? hoverColorForStackFrame(stackFrame)
165 - : defaultColorForStackFrame(stackFrame);
169 +
170 + let textFillStyle;
171 + if (isInternalModule(this._internalModuleSourceToRanges, stackFrame)) {
172 + context.fillStyle = showHoverHighlight
173 + ? COLORS.INTERNAL_MODULE_FRAME_HOVER
174 + : COLORS.INTERNAL_MODULE_FRAME;
175 + textFillStyle = COLORS.INTERNAL_MODULE_FRAME_TEXT;
176 + } else {
177 + context.fillStyle = showHoverHighlight
178 + ? hoverColorForStackFrame(stackFrame)
179 + : defaultColorForStackFrame(stackFrame);
180 + textFillStyle = COLORS.TEXT_COLOR;
181 + }
182
183 const drawableRect = intersectionOfRects(nodeRect, visibleArea);
184 context.fillRect(
@@ -172,7 +188,9 @@ class FlamechartStackLayerView extends View {
188 drawableRect.size.height,
189 );
190
175 - drawText(name, context, nodeRect, drawableRect);
191 + drawText(name, context, nodeRect, drawableRect, {
192 + fillStyle: textFillStyle,
193 + });
194 }
195
196 // Render bottom border.
@@ -264,13 +282,22 @@ export class FlamechartView extends View {
282 surface: Surface,
283 frame: Rect,
284 flamechart: Flamechart,
285 + internalModuleSourceToRanges: InternalModuleSourceToRanges,
286 duration: number,
287 ) {
288 super(surface, frame, layeredLayout);
270 - this.setDataAndUpdateSubviews(flamechart, duration);
289 + this.setDataAndUpdateSubviews(
290 + flamechart,
291 + internalModuleSourceToRanges,
292 + duration,
293 + );
294 }
295
273 - setDataAndUpdateSubviews(flamechart: Flamechart, duration: number) {
296 + setDataAndUpdateSubviews(
297 + flamechart: Flamechart,
298 + internalModuleSourceToRanges: InternalModuleSourceToRanges,
299 + duration: number,
300 + ) {
301 const {surface, frame, _onHover, _hoveredStackFrame} = this;
302
303 // Clear existing rows on data update
@@ -285,6 +312,7 @@ export class FlamechartView extends View {
312 surface,
313 frame,
314 stackLayer,
315 + internalModuleSourceToRanges,
316 duration,
317 );
318 this._verticalStackView.addSubview(rowView);
packages/react-devtools-scheduling-profiler/src/content-views/constants.js
+12
@@ -45,6 +45,9 @@ export const MIN_INTERVAL_SIZE_PX = 70;
45 // TODO Replace this with "export let" vars
46 export let COLORS = {
47 BACKGROUND: '',
48 + INTERNAL_MODULE_FRAME: '',
49 + INTERNAL_MODULE_FRAME_HOVER: '',
50 + INTERNAL_MODULE_FRAME_TEXT: '',
51 NATIVE_EVENT: '',
52 NATIVE_EVENT_HOVER: '',
53 NETWORK_PRIMARY: '',
@@ -107,6 +110,15 @@ export function updateColorsToMatchTheme(element: Element): boolean {
110
111 COLORS = {
112 BACKGROUND: computedStyle.getPropertyValue('--color-background'),
113 + INTERNAL_MODULE_FRAME: computedStyle.getPropertyValue(
114 + '--color-scheduling-profiler-internal-module',
115 + ),
116 + INTERNAL_MODULE_FRAME_HOVER: computedStyle.getPropertyValue(
117 + '--color-scheduling-profiler-internal-module-hover',
118 + ),
119 + INTERNAL_MODULE_FRAME_TEXT: computedStyle.getPropertyValue(
120 + '--color-scheduling-profiler-internal-module-text',
121 + ),
122 NATIVE_EVENT: computedStyle.getPropertyValue(
123 '--color-scheduling-profiler-native-event',
124 ),
packages/react-devtools-scheduling-profiler/src/content-views/utils/__tests__/__modules__/module-one.js new
+16
@@ -0,0 +1,16 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +export const outerErrorA = new Error();
11 +
12 +export const moduleStartError = new Error();
13 +export const innerError = new Error();
14 +export const moduleStopError = new Error();
15 +
16 +export const outerErrorB = new Error();
packages/react-devtools-scheduling-profiler/src/content-views/utils/__tests__/__modules__/module-two.js new
+18
@@ -0,0 +1,18 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +export const moduleAStartError = new Error();
11 +export const innerErrorA = new Error();
12 +export const moduleAStopError = new Error();
13 +
14 +export const outerError = new Error();
15 +
16 +export const moduleBStartError = new Error();
17 +export const innerErrorB = new Error();
18 +export const moduleBStopError = new Error();
packages/react-devtools-scheduling-profiler/src/content-views/utils/__tests__/moduleFilters-test.js new
+79
@@ -0,0 +1,79 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +import {isInternalModule} from '../moduleFilters';
11 +
12 +describe('isInternalModule', () => {
13 + let map;
14 +
15 + function createFlamechartStackFrame(scriptUrl, locationLine, locationColumn) {
16 + return {
17 + name: 'test',
18 + timestamp: 0,
19 + duration: 1,
20 + scriptUrl,
21 + locationLine,
22 + locationColumn,
23 + };
24 + }
25 +
26 + function createStackFrame(fileName, lineNumber, columnNumber) {
27 + return {
28 + columnNumber: columnNumber,
29 + lineNumber: lineNumber,
30 + fileName: fileName,
31 + functionName: 'test',
32 + source: ` at test (${fileName}:${lineNumber}:${columnNumber})`,
33 + };
34 + }
35 +
36 + beforeEach(() => {
37 + map = new Map();
38 + map.set('foo', [
39 + [createStackFrame('foo', 10, 0), createStackFrame('foo', 15, 100)],
40 + ]);
41 + map.set('bar', [
42 + [createStackFrame('bar', 10, 0), createStackFrame('bar', 15, 100)],
43 + [createStackFrame('bar', 20, 0), createStackFrame('bar', 25, 100)],
44 + ]);
45 + });
46 +
47 + it('should properly identify stack frames within the provided module ranges', () => {
48 + expect(
49 + isInternalModule(map, createFlamechartStackFrame('foo', 10, 0)),
50 + ).toBe(true);
51 + expect(
52 + isInternalModule(map, createFlamechartStackFrame('foo', 12, 35)),
53 + ).toBe(true);
54 + expect(
55 + isInternalModule(map, createFlamechartStackFrame('foo', 15, 100)),
56 + ).toBe(true);
57 + expect(
58 + isInternalModule(map, createFlamechartStackFrame('bar', 12, 0)),
59 + ).toBe(true);
60 + expect(
61 + isInternalModule(map, createFlamechartStackFrame('bar', 22, 125)),
62 + ).toBe(true);
63 + });
64 +
65 + it('should properly identify stack frames outside of the provided module ranges', () => {
66 + expect(isInternalModule(map, createFlamechartStackFrame('foo', 9, 0))).toBe(
67 + false,
68 + );
69 + expect(
70 + isInternalModule(map, createFlamechartStackFrame('foo', 15, 101)),
71 + ).toBe(false);
72 + expect(
73 + isInternalModule(map, createFlamechartStackFrame('bar', 17, 0)),
74 + ).toBe(false);
75 + expect(
76 + isInternalModule(map, createFlamechartStackFrame('baz', 12, 0)),
77 + ).toBe(false);
78 + });
79 +});
packages/react-devtools-scheduling-profiler/src/content-views/utils/moduleFilters.js new
+69
@@ -0,0 +1,69 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +import type {
11 + FlamechartStackFrame,
12 + InternalModuleSourceToRanges,
13 +} from '../../types';
14 +
15 +import {
16 + CHROME_WEBSTORE_EXTENSION_ID,
17 + INTERNAL_EXTENSION_ID,
18 + LOCAL_EXTENSION_ID,
19 +} from 'react-devtools-shared/src/constants';
20 +
21 +export function isInternalModule(
22 + internalModuleSourceToRanges: InternalModuleSourceToRanges,
23 + flamechartStackFrame: FlamechartStackFrame,
24 +): boolean {
25 + const {locationColumn, locationLine, scriptUrl} = flamechartStackFrame;
26 +
27 + if (scriptUrl == null || locationColumn == null || locationLine == null) {
28 + // This could indicate a browser-internal API like performance.mark().
29 + return false;
30 + }
31 +
32 + // Internal modules are only registered if DevTools was running when the profile was captured,
33 + // but DevTools should also hide its own frames to avoid over-emphasizing them.
34 + if (
35 + // Handle webpack-internal:// sources
36 + scriptUrl.includes('/react-devtools') ||
37 + scriptUrl.includes('/react_devtools') ||
38 + // Filter out known extension IDs
39 + scriptUrl.includes(CHROME_WEBSTORE_EXTENSION_ID) ||
40 + scriptUrl.includes(INTERNAL_EXTENSION_ID) ||
41 + scriptUrl.includes(LOCAL_EXTENSION_ID)
42 + // Unfortunately this won't get everything, like relatively loaded chunks or Web Worker files.
43 + ) {
44 + return true;
45 + }
46 +
47 + // Filter out React internal packages.
48 + const ranges = internalModuleSourceToRanges.get(scriptUrl);
49 + if (ranges != null) {
50 + for (let i = 0; i < ranges.length; i++) {
51 + const [startStackFrame, stopStackFrame] = ranges[i];
52 +
53 + const isAfterStart =
54 + locationLine > startStackFrame.lineNumber ||
55 + (locationLine === startStackFrame.lineNumber &&
56 + locationColumn >= startStackFrame.columnNumber);
57 + const isBeforeStop =
58 + locationLine < stopStackFrame.lineNumber ||
59 + (locationLine === stopStackFrame.lineNumber &&
60 + locationColumn <= stopStackFrame.columnNumber);
61 +
62 + if (isAfterStart && isBeforeStop) {
63 + return true;
64 + }
65 + }
66 + }
67 +
68 + return false;
69 +}
packages/react-devtools-scheduling-profiler/src/import-worker/__tests__/preprocessData-test.internal.js
+4
@@ -282,6 +282,7 @@ describe('preprocessData', () => {
282 "componentMeasures": Array [],
283 "duration": 0.005,
284 "flamechart": Array [],
285 + "internalModuleSourceToRanges": Map {},
286 "laneToLabelMap": Map {
287 0 => "Sync",
288 1 => "InputContinuousHydration",
@@ -449,6 +450,7 @@ describe('preprocessData', () => {
450 "componentMeasures": Array [],
451 "duration": 0.011,
452 "flamechart": Array [],
453 + "internalModuleSourceToRanges": Map {},
454 "laneToLabelMap": Map {
455 0 => "Sync",
456 1 => "InputContinuousHydration",
@@ -636,6 +638,7 @@ describe('preprocessData', () => {
638 "componentMeasures": Array [],
639 "duration": 0.013,
640 "flamechart": Array [],
641 + "internalModuleSourceToRanges": Map {},
642 "laneToLabelMap": Map {
643 0 => "Sync",
644 1 => "InputContinuousHydration",
@@ -914,6 +917,7 @@ describe('preprocessData', () => {
917 ],
918 "duration": 0.031,
919 "flamechart": Array [],
920 + "internalModuleSourceToRanges": Map {},
921 "laneToLabelMap": Map {
922 0 => "Sync",
923 1 => "InputContinuousHydration",
packages/react-devtools-scheduling-profiler/src/import-worker/preprocessData.js
+59
@@ -13,6 +13,7 @@ import {
13 } from '@elg/speedscope';
14 import type {TimelineEvent} from '@elg/speedscope';
15 import type {
16 + ErrorStackFrame,
17 BatchUID,
18 Flamechart,
19 Milliseconds,
@@ -30,6 +31,7 @@ import type {
31 import {REACT_TOTAL_NUM_LANES, SCHEDULING_PROFILER_VERSION} from '../constants';
32 import InvalidProfileError from './InvalidProfileError';
33 import {getBatchRange} from '../utils/getBatchRange';
34 +import ErrorStackParser from 'error-stack-parser';
35
36 type MeasureStackElement = {|
37 type: ReactMeasureType,
@@ -43,6 +45,8 @@ type ProcessorState = {|
45 asyncProcessingPromises: Promise<any>[],
46 batchUID: BatchUID,
47 currentReactComponentMeasure: ReactComponentMeasure | null,
48 + internalModuleCurrentStackFrame: ErrorStackFrame | null,
49 + internalModuleStackStringSet: Set<string>,
50 measureStack: MeasureStackElement[],
51 nativeEventStack: NativeEvent[],
52 nextRenderShouldGenerateNewBatchID: boolean,
@@ -793,6 +797,49 @@ function processTimelineEvent(
797 );
798 } // eslint-disable-line brace-style
799
800 + // Internal module ranges
801 + else if (name.startsWith('--react-internal-module-start-')) {
802 + const stackFrameStart = name.substr(30);
803 +
804 + if (!state.internalModuleStackStringSet.has(stackFrameStart)) {
805 + state.internalModuleStackStringSet.add(stackFrameStart);
806 +
807 + const parsedStackFrameStart = parseStackFrame(stackFrameStart);
808 +
809 + state.internalModuleCurrentStackFrame = parsedStackFrameStart;
810 + }
811 + } else if (name.startsWith('--react-internal-module-stop-')) {
812 + const stackFrameStop = name.substr(19);
813 +
814 + if (!state.internalModuleStackStringSet.has(stackFrameStop)) {
815 + state.internalModuleStackStringSet.add(stackFrameStop);
816 +
817 + const parsedStackFrameStop = parseStackFrame(stackFrameStop);
818 +
819 + if (
820 + parsedStackFrameStop !== null &&
821 + state.internalModuleCurrentStackFrame !== null
822 + ) {
823 + const parsedStackFrameStart = state.internalModuleCurrentStackFrame;
824 +
825 + state.internalModuleCurrentStackFrame = null;
826 +
827 + const range = [parsedStackFrameStart, parsedStackFrameStop];
828 + const ranges = currentProfilerData.internalModuleSourceToRanges.get(
829 + parsedStackFrameStart.fileName,
830 + );
831 + if (ranges == null) {
832 + currentProfilerData.internalModuleSourceToRanges.set(
833 + parsedStackFrameStart.fileName,
834 + [range],
835 + );
836 + } else {
837 + ranges.push(range);
838 + }
839 + }
840 + }
841 + } // eslint-disable-line brace-style
842 +
843 // Other user timing marks/measures
844 else if (ph === 'R' || ph === 'n') {
845 // User Timing mark
@@ -855,6 +902,15 @@ function preprocessFlamechart(rawData: TimelineEvent[]): Flamechart {
902 return flamechart;
903 }
904
905 +function parseStackFrame(stackFrame: string): ErrorStackFrame | null {
906 + const error = new Error();
907 + error.stack = stackFrame;
908 +
909 + const frames = ErrorStackParser.parse(error);
910 +
911 + return frames.length === 1 ? frames[0] : null;
912 +}
913 +
914 export default async function preprocessData(
915 timeline: TimelineEvent[],
916 ): Promise<ReactProfilerData> {
@@ -870,6 +926,7 @@ export default async function preprocessData(
926 componentMeasures: [],
927 duration: 0,
928 flamechart,
929 + internalModuleSourceToRanges: new Map(),
930 laneToLabelMap: new Map(),
931 laneToReactMeasureMap,
932 nativeEvents: [],
@@ -913,6 +970,8 @@ export default async function preprocessData(
970 asyncProcessingPromises: [],
971 batchUID: 0,
972 currentReactComponentMeasure: null,
973 + internalModuleCurrentStackFrame: null,
974 + internalModuleStackStringSet: new Set(),
975 measureStack: [],
976 nativeEventStack: [],
977 nextRenderShouldGenerateNewBatchID: true,
packages/react-devtools-scheduling-profiler/src/types.js
+12
@@ -17,6 +17,12 @@ export type Return<T> = Return_<*, T>;
17
18 // Project types
19
20 +export type ErrorStackFrame = {
21 + fileName: string,
22 + lineNumber: number,
23 + columnNumber: number,
24 +};
25 +
26 export type Milliseconds = number;
27
28 export type ReactLane = number;
@@ -169,11 +175,17 @@ export type ViewState = {|
175 viewToMutableViewStateMap: Map<string, mixed>,
176 |};
177
178 +export type InternalModuleSourceToRanges = Map<
179 + string,
180 + Array<[ErrorStackFrame, ErrorStackFrame]>,
181 +>;
182 +
183 export type ReactProfilerData = {|
184 batchUIDToMeasuresMap: Map<BatchUID, ReactMeasure[]>,
185 componentMeasures: ReactComponentMeasure[],
186 duration: number,
187 flamechart: Flamechart,
188 + internalModuleSourceToRanges: InternalModuleSourceToRanges,
189 laneToLabelMap: Map<ReactLane, string>,
190 laneToReactMeasureMap: Map<ReactLane, ReactMeasure[]>,
191 nativeEvents: NativeEvent[],
packages/react-devtools-shared/src/backend/types.js
+5
@@ -420,6 +420,11 @@ export type DevToolsHook = {
420 didError?: boolean,
421 ) => void,
422
423 + // Scheduling Profiler internal module filtering
424 + getInternalModuleRanges: () => Array<[string, string]>,
425 + registerInternalModuleStart: (moduleStartError: Error) => void,
426 + registerInternalModuleStop: (moduleStopError: Error) => void,
427 +
428 // Testing
429 dangerous_setTargetConsoleForTesting?: (fakeConsole: Object) => void,
430 ...
packages/react-devtools-shared/src/constants.js
+10
@@ -7,6 +7,10 @@
7 * @flow
8 */
9
10 +export const CHROME_WEBSTORE_EXTENSION_ID = 'fmkadmapgofadopljbjfkapdkoienihi';
11 +export const INTERNAL_EXTENSION_ID = 'dnjnjgbfilfphmojnmhliehogmojhclc';
12 +export const LOCAL_EXTENSION_ID = 'ikiahnapldjmdmpkmfhjdjilojjhgcbf';
13 +
14 // Flip this flag to true to enable verbose console debug logging.
15 export const __DEBUG__ = false;
16
@@ -147,6 +151,9 @@ export const THEME_STYLES: {[style: Theme | DisplayDensity]: any} = {
151 '--color-resize-bar-active': '#dcdcdc',
152 '--color-resize-bar-border': '#d1d1d1',
153 '--color-resize-bar-dot': '#333333',
154 + '--color-scheduling-profiler-internal-module': '#d1d1d1',
155 + '--color-scheduling-profiler-internal-module-hover': '#c9c9c9',
156 + '--color-scheduling-profiler-internal-module-text': '#444',
157 '--color-scheduling-profiler-native-event': '#ccc',
158 '--color-scheduling-profiler-native-event-hover': '#aaa',
159 '--color-scheduling-profiler-network-primary': '#fcf3dc',
@@ -288,6 +295,9 @@ export const THEME_STYLES: {[style: Theme | DisplayDensity]: any} = {
295 '--color-resize-bar-active': '#31363f',
296 '--color-resize-bar-border': '#3d424a',
297 '--color-resize-bar-dot': '#cfd1d5',
298 + '--color-scheduling-profiler-internal-module': '#303542',
299 + '--color-scheduling-profiler-internal-module-hover': '#363b4a',
300 + '--color-scheduling-profiler-internal-module-text': '#7f8899',
301 '--color-scheduling-profiler-native-event': '#b2b2b2',
302 '--color-scheduling-profiler-native-event-hover': '#949494',
303 '--color-scheduling-profiler-network-primary': '#fcf3dc',
packages/react-devtools-shared/src/hook.js
+41
@@ -490,6 +490,40 @@ export function installHook(target: any): DevToolsHook | null {
490 }
491 }
492
493 + type StackFrameString = string;
494 +
495 + const openModuleRangesStack: Array<StackFrameString> = [];
496 + const moduleRanges: Array<[StackFrameString, StackFrameString]> = [];
497 +
498 + function getTopStackFrameString(error: Error): StackFrameString | null {
499 + const frames = error.stack.split('\n');
500 + const frame = frames.length > 1 ? frames[1] : null;
501 + return frame;
502 + }
503 +
504 + function getInternalModuleRanges(): Array<
505 + [StackFrameString, StackFrameString],
506 + > {
507 + return moduleRanges;
508 + }
509 +
510 + function registerInternalModuleStart(error: Error) {
511 + const startStackFrame = getTopStackFrameString(error);
512 + if (startStackFrame !== null) {
513 + openModuleRangesStack.push(startStackFrame);
514 + }
515 + }
516 +
517 + function registerInternalModuleStop(error: Error) {
518 + if (openModuleRangesStack.length > 0) {
519 + const startStackFrame = openModuleRangesStack.pop();
520 + const stopStackFrame = getTopStackFrameString(error);
521 + if (stopStackFrame !== null) {
522 + moduleRanges.push([startStackFrame, stopStackFrame]);
523 + }
524 + }
525 + }
526 +
527 // TODO: More meaningful names for "rendererInterfaces" and "renderers".
528 const fiberRoots = {};
529 const rendererInterfaces = new Map();
@@ -520,6 +554,13 @@ export function installHook(target: any): DevToolsHook | null {
554 onCommitFiberRoot,
555 onPostCommitFiberRoot,
556 setStrictMode,
557 +
558 + // Schedule Profiler runtime helpers.
559 + // These internal React modules to report their own boundaries
560 + // which in turn enables the profiler to dim or filter internal frames.
561 + getInternalModuleRanges,
562 + registerInternalModuleStart,
563 + registerInternalModuleStop,
564 };
565
566 if (__TEST__) {
packages/react-reconciler/src/SchedulingProfiler.js
+17
@@ -98,6 +98,22 @@ function markVersionMetadata() {
98 markAndClear(`--profiler-version-${SCHEDULING_PROFILER_VERSION}`);
99 }
100
101 +function markInternalModuleRanges() {
102 + /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */
103 + if (
104 + typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
105 + typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.getInternalModuleRanges === 'function'
106 + ) {
107 + const ranges = __REACT_DEVTOOLS_GLOBAL_HOOK__.getInternalModuleRanges();
108 + for (let i = 0; i < ranges.length; i++) {
109 + const [startStackFrame, stopStackFrame] = ranges[i];
110 +
111 + markAndClear(`--react-internal-module-start-${startStackFrame}`);
112 + markAndClear(`--react-internal-module-stop-${stopStackFrame}`);
113 + }
114 + }
115 +}
116 +
117 export function markCommitStarted(lanes: Lanes): void {
118 if (enableSchedulingProfiler) {
119 if (supportsUserTimingV3) {
@@ -114,6 +130,7 @@ export function markCommitStarted(lanes: Lanes): void {
130 // we can log this data only once (when started) and remove the per-commit logging.
131 markVersionMetadata();
132 markLaneToLabelMetadata();
133 + markInternalModuleRanges();
134 }
135 }
136 }
packages/shared/registerInternalModuleStart.js new
+20
@@ -0,0 +1,20 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +/* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */
11 +
12 +// Don't require this file directly; it's embedded by Rollup during build.
13 +
14 +if (
15 + typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
16 + typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart ===
17 + 'function'
18 +) {
19 + __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error());
20 +}
packages/shared/registerInternalModuleStop.js new
+20
@@ -0,0 +1,20 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +/* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */
11 +
12 +// Don't require this file directly; it's embedded by Rollup during build.
13 +
14 +if (
15 + typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
16 + typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop ===
17 + 'function'
18 +) {
19 + __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(new Error());
20 +}
scripts/rollup/build.js
+2 -1
@@ -440,7 +440,8 @@ function getPlugins(
440 bundleType,
441 globalName,
442 filename,
443 - moduleType
443 + moduleType,
444 + bundle.wrapWithModuleBoundaries
445 );
446 },
447 },
scripts/rollup/bundles.js
+62
@@ -80,6 +80,7 @@ const bundles = [
80 entry: 'react',
81 global: 'React',
82 minifyWithProdErrorCodes: false,
83 + wrapWithModuleBoundaries: true,
84 externals: ['ReactNativeInternalFeatureFlags'],
85 },
86
@@ -90,6 +91,7 @@ const bundles = [
91 entry: 'react/unstable-shared-subset',
92 global: 'React',
93 minifyWithProdErrorCodes: true,
94 + wrapWithModuleBoundaries: false,
95 externals: [],
96 },
97
@@ -108,6 +110,7 @@ const bundles = [
110 entry: 'react/jsx-runtime',
111 global: 'JSXRuntime',
112 minifyWithProdErrorCodes: true,
113 + wrapWithModuleBoundaries: false,
114 externals: ['react', 'ReactNativeInternalFeatureFlags'],
115 },
116
@@ -128,6 +131,7 @@ const bundles = [
131 entry: 'react/jsx-dev-runtime',
132 global: 'JSXDEVRuntime',
133 minifyWithProdErrorCodes: false,
134 + wrapWithModuleBoundaries: false,
135 externals: ['react', 'ReactNativeInternalFeatureFlags'],
136 },
137
@@ -138,6 +142,7 @@ const bundles = [
142 entry: 'react-fetch/index.browser',
143 global: 'ReactFetch',
144 minifyWithProdErrorCodes: true,
145 + wrapWithModuleBoundaries: false,
146 externals: ['react'],
147 },
148
@@ -148,6 +153,7 @@ const bundles = [
153 entry: 'react-fetch/index.node',
154 global: 'ReactFetch',
155 minifyWithProdErrorCodes: false,
156 + wrapWithModuleBoundaries: false,
157 externals: ['react', 'http', 'https'],
158 },
159
@@ -158,6 +164,7 @@ const bundles = [
164 entry: 'react-fs/index.browser.server',
165 global: 'ReactFilesystem',
166 minifyWithProdErrorCodes: true,
167 + wrapWithModuleBoundaries: false,
168 externals: [],
169 },
170
@@ -168,6 +175,7 @@ const bundles = [
175 entry: 'react-fs/index.node.server',
176 global: 'ReactFilesystem',
177 minifyWithProdErrorCodes: false,
178 + wrapWithModuleBoundaries: false,
179 externals: ['react', 'fs/promises', 'path'],
180 },
181
@@ -178,6 +186,7 @@ const bundles = [
186 entry: 'react-pg/index.browser.server',
187 global: 'ReactPostgres',
188 minifyWithProdErrorCodes: true,
189 + wrapWithModuleBoundaries: false,
190 externals: [],
191 },
192
@@ -188,6 +197,7 @@ const bundles = [
197 entry: 'react-pg/index.node.server',
198 global: 'ReactPostgres',
199 minifyWithProdErrorCodes: false,
200 + wrapWithModuleBoundaries: false,
201 externals: ['react', 'pg'],
202 },
203
@@ -208,6 +218,7 @@ const bundles = [
218 entry: 'react-dom',
219 global: 'ReactDOM',
220 minifyWithProdErrorCodes: true,
221 + wrapWithModuleBoundaries: true,
222 externals: ['react'],
223 },
224
@@ -219,6 +230,7 @@ const bundles = [
230 global: 'ReactDOMForked',
231 enableNewReconciler: true,
232 minifyWithProdErrorCodes: true,
233 + wrapWithModuleBoundaries: true,
234 externals: ['react'],
235 },
236
@@ -229,6 +241,7 @@ const bundles = [
241 entry: 'react-dom/test-utils',
242 global: 'ReactTestUtils',
243 minifyWithProdErrorCodes: false,
244 + wrapWithModuleBoundaries: false,
245 externals: ['react', 'react-dom'],
246 },
247
@@ -240,6 +253,7 @@ const bundles = [
253 entry: 'react-dom/testing',
254 global: 'ReactDOMTesting',
255 minifyWithProdErrorCodes: true,
256 + wrapWithModuleBoundaries: false,
257 externals: ['react'],
258 },
259
@@ -253,6 +267,7 @@ const bundles = [
267 name: 'react-dom-server-legacy.browser',
268 global: 'ReactDOMServer',
269 minifyWithProdErrorCodes: true,
270 + wrapWithModuleBoundaries: false,
271 externals: ['react'],
272 babel: opts =>
273 Object.assign({}, opts, {
@@ -268,6 +283,7 @@ const bundles = [
283 name: 'react-dom-server-legacy.node',
284 externals: ['react', 'stream'],
285 minifyWithProdErrorCodes: false,
286 + wrapWithModuleBoundaries: false,
287 babel: opts =>
288 Object.assign({}, opts, {
289 plugins: opts.plugins.concat([
@@ -284,6 +300,7 @@ const bundles = [
300 name: 'react-dom-server.browser',
301 global: 'ReactDOMServer',
302 minifyWithProdErrorCodes: true,
303 + wrapWithModuleBoundaries: false,
304 externals: ['react'],
305 },
306 {
@@ -293,6 +310,7 @@ const bundles = [
310 name: 'react-dom-server.node',
311 global: 'ReactDOMServer',
312 minifyWithProdErrorCodes: false,
313 + wrapWithModuleBoundaries: false,
314 externals: ['react'],
315 },
316 {
@@ -301,6 +319,7 @@ const bundles = [
319 entry: 'react-server-dom-relay/src/ReactDOMServerFB',
320 global: 'ReactDOMServer',
321 minifyWithProdErrorCodes: false,
322 + wrapWithModuleBoundaries: false,
323 externals: ['react'],
324 },
325
@@ -311,6 +330,7 @@ const bundles = [
330 entry: 'react-server-dom-webpack/writer.browser.server',
331 global: 'ReactServerDOMWriter',
332 minifyWithProdErrorCodes: false,
333 + wrapWithModuleBoundaries: false,
334 externals: ['react'],
335 },
336 {
@@ -319,6 +339,7 @@ const bundles = [
339 entry: 'react-server-dom-webpack/writer.node.server',
340 global: 'ReactServerDOMWriter',
341 minifyWithProdErrorCodes: false,
342 + wrapWithModuleBoundaries: false,
343 externals: ['react'],
344 },
345
@@ -329,6 +350,7 @@ const bundles = [
350 entry: 'react-server-dom-webpack',
351 global: 'ReactServerDOMReader',
352 minifyWithProdErrorCodes: true,
353 + wrapWithModuleBoundaries: false,
354 externals: ['react'],
355 },
356
@@ -339,6 +361,7 @@ const bundles = [
361 entry: 'react-server-dom-webpack/plugin',
362 global: 'ReactServerWebpackPlugin',
363 minifyWithProdErrorCodes: false,
364 + wrapWithModuleBoundaries: false,
365 externals: ['fs', 'path', 'url', 'neo-async'],
366 },
367
@@ -349,6 +372,7 @@ const bundles = [
372 entry: 'react-server-dom-webpack/node-loader',
373 global: 'ReactServerWebpackNodeLoader',
374 minifyWithProdErrorCodes: false,
375 + wrapWithModuleBoundaries: false,
376 externals: ['acorn'],
377 },
378
@@ -359,6 +383,7 @@ const bundles = [
383 entry: 'react-server-dom-webpack/node-register',
384 global: 'ReactFlightWebpackNodeRegister',
385 minifyWithProdErrorCodes: false,
386 + wrapWithModuleBoundaries: false,
387 externals: ['url', 'module'],
388 },
389
@@ -369,6 +394,7 @@ const bundles = [
394 entry: 'react-server-dom-relay/server',
395 global: 'ReactFlightDOMRelayServer', // TODO: Rename to Writer
396 minifyWithProdErrorCodes: false,
397 + wrapWithModuleBoundaries: false,
398 externals: [
399 'react',
400 'ReactFlightDOMRelayServerIntegration',
@@ -383,6 +409,7 @@ const bundles = [
409 entry: 'react-server-dom-relay',
410 global: 'ReactFlightDOMRelayClient', // TODO: Rename to Reader
411 minifyWithProdErrorCodes: true,
412 + wrapWithModuleBoundaries: false,
413 externals: [
414 'react',
415 'ReactFlightDOMRelayClientIntegration',
@@ -397,6 +424,7 @@ const bundles = [
424 entry: 'react-server-native-relay/server',
425 global: 'ReactFlightNativeRelayServer', // TODO: Rename to Writer
426 minifyWithProdErrorCodes: false,
427 + wrapWithModuleBoundaries: false,
428 externals: [
429 'react',
430 'ReactFlightNativeRelayServerIntegration',
@@ -412,6 +440,7 @@ const bundles = [
440 entry: 'react-server-native-relay',
441 global: 'ReactFlightNativeRelayClient', // TODO: Rename to Reader
442 minifyWithProdErrorCodes: true,
443 + wrapWithModuleBoundaries: false,
444 externals: [
445 'react',
446 'ReactFlightNativeRelayClientIntegration',
@@ -427,6 +456,7 @@ const bundles = [
456 entry: 'react-suspense-test-utils',
457 global: 'ReactSuspenseTestUtils',
458 minifyWithProdErrorCodes: false,
459 + wrapWithModuleBoundaries: false,
460 externals: ['react'],
461 },
462
@@ -445,6 +475,7 @@ const bundles = [
475 global: 'ReactART',
476 externals: ['react'],
477 minifyWithProdErrorCodes: true,
478 + wrapWithModuleBoundaries: true,
479 babel: opts =>
480 Object.assign({}, opts, {
481 // Include JSX
@@ -468,6 +499,7 @@ const bundles = [
499 global: 'ReactNativeRenderer',
500 externals: ['react-native', 'ReactNativeInternalFeatureFlags'],
501 minifyWithProdErrorCodes: false,
502 + wrapWithModuleBoundaries: true,
503 babel: opts =>
504 Object.assign({}, opts, {
505 plugins: opts.plugins.concat([
@@ -482,6 +514,7 @@ const bundles = [
514 global: 'ReactNativeRenderer',
515 externals: ['react-native'],
516 minifyWithProdErrorCodes: false,
517 + wrapWithModuleBoundaries: true,
518 babel: opts =>
519 Object.assign({}, opts, {
520 plugins: opts.plugins.concat([
@@ -500,6 +533,7 @@ const bundles = [
533 global: 'ReactFabric',
534 externals: ['react-native', 'ReactNativeInternalFeatureFlags'],
535 minifyWithProdErrorCodes: false,
536 + wrapWithModuleBoundaries: true,
537 babel: opts =>
538 Object.assign({}, opts, {
539 plugins: opts.plugins.concat([
@@ -514,6 +548,7 @@ const bundles = [
548 global: 'ReactFabric',
549 externals: ['react-native'],
550 minifyWithProdErrorCodes: false,
551 + wrapWithModuleBoundaries: true,
552 babel: opts =>
553 Object.assign({}, opts, {
554 plugins: opts.plugins.concat([
@@ -544,6 +579,7 @@ const bundles = [
579 'ReactNativeInternalFeatureFlags',
580 ],
581 minifyWithProdErrorCodes: false,
582 + wrapWithModuleBoundaries: false,
583 babel: opts =>
584 Object.assign({}, opts, {
585 plugins: opts.plugins.concat([
@@ -559,6 +595,7 @@ const bundles = [
595 entry: 'react-noop-renderer',
596 global: 'ReactNoopRenderer',
597 minifyWithProdErrorCodes: true,
598 + wrapWithModuleBoundaries: false,
599 externals: ['react', 'scheduler', 'scheduler/unstable_mock', 'expect'],
600 },
601
@@ -569,6 +606,7 @@ const bundles = [
606 entry: 'react-noop-renderer/persistent',
607 global: 'ReactNoopRendererPersistent',
608 minifyWithProdErrorCodes: true,
609 + wrapWithModuleBoundaries: false,
610 externals: ['react', 'scheduler', 'expect'],
611 },
612
@@ -579,6 +617,7 @@ const bundles = [
617 entry: 'react-noop-renderer/server',
618 global: 'ReactNoopRendererServer',
619 minifyWithProdErrorCodes: true,
620 + wrapWithModuleBoundaries: false,
621 externals: ['react', 'scheduler', 'expect'],
622 },
623
@@ -589,6 +628,7 @@ const bundles = [
628 entry: 'react-noop-renderer/flight-server',
629 global: 'ReactNoopFlightServer',
630 minifyWithProdErrorCodes: false,
631 + wrapWithModuleBoundaries: false,
632 externals: [
633 'react',
634 'scheduler',
@@ -604,6 +644,7 @@ const bundles = [
644 entry: 'react-noop-renderer/flight-client',
645 global: 'ReactNoopFlightClient',
646 minifyWithProdErrorCodes: false,
647 + wrapWithModuleBoundaries: false,
648 externals: [
649 'react',
650 'scheduler',
@@ -619,6 +660,7 @@ const bundles = [
660 entry: 'react-reconciler',
661 global: 'ReactReconciler',
662 minifyWithProdErrorCodes: true,
663 + wrapWithModuleBoundaries: false,
664 externals: ['react'],
665 },
666
@@ -629,6 +671,7 @@ const bundles = [
671 entry: 'react-server',
672 global: 'ReactServer',
673 minifyWithProdErrorCodes: false,
674 + wrapWithModuleBoundaries: false,
675 externals: ['react'],
676 },
677
@@ -639,6 +682,7 @@ const bundles = [
682 entry: 'react-server/flight',
683 global: 'ReactFlightServer',
684 minifyWithProdErrorCodes: false,
685 + wrapWithModuleBoundaries: false,
686 externals: ['react'],
687 },
688
@@ -649,6 +693,7 @@ const bundles = [
693 entry: 'react-client/flight',
694 global: 'ReactFlightClient',
695 minifyWithProdErrorCodes: true,
696 + wrapWithModuleBoundaries: false,
697 externals: ['react'],
698 },
699
@@ -659,6 +704,7 @@ const bundles = [
704 entry: 'react-reconciler/reflection',
705 global: 'ReactFiberTreeReflection',
706 minifyWithProdErrorCodes: true,
707 + wrapWithModuleBoundaries: false,
708 externals: [],
709 },
710
@@ -669,6 +715,7 @@ const bundles = [
715 entry: 'react-reconciler/constants',
716 global: 'ReactReconcilerConstants',
717 minifyWithProdErrorCodes: true,
718 + wrapWithModuleBoundaries: false,
719 externals: [],
720 },
721
@@ -686,6 +733,7 @@ const bundles = [
733 entry: 'react-is',
734 global: 'ReactIs',
735 minifyWithProdErrorCodes: true,
736 + wrapWithModuleBoundaries: false,
737 externals: [],
738 },
739
@@ -696,6 +744,7 @@ const bundles = [
744 entry: 'react-debug-tools',
745 global: 'ReactDebugTools',
746 minifyWithProdErrorCodes: false,
747 + wrapWithModuleBoundaries: false,
748 externals: [],
749 },
750
@@ -708,6 +757,7 @@ const bundles = [
757 entry: 'react-cache',
758 global: 'ReactCacheOld',
759 minifyWithProdErrorCodes: false,
760 + wrapWithModuleBoundaries: false,
761 externals: ['react', 'scheduler'],
762 },
763
@@ -719,6 +769,7 @@ const bundles = [
769 global: 'createSubscription',
770 externals: ['react'],
771 minifyWithProdErrorCodes: true,
772 + wrapWithModuleBoundaries: true,
773 babel: opts =>
774 Object.assign({}, opts, {
775 plugins: opts.plugins.concat([
@@ -734,6 +785,7 @@ const bundles = [
785 entry: 'use-subscription',
786 global: 'useSubscription',
787 minifyWithProdErrorCodes: true,
788 + wrapWithModuleBoundaries: true,
789 externals: ['react'],
790 },
791
@@ -744,6 +796,7 @@ const bundles = [
796 entry: 'use-sync-external-store',
797 global: 'useSyncExternalStore',
798 minifyWithProdErrorCodes: true,
799 + wrapWithModuleBoundaries: true,
800 externals: ['react'],
801 },
802
@@ -754,6 +807,7 @@ const bundles = [
807 entry: 'use-sync-external-store/extra',
808 global: 'useSyncExternalStoreExtra',
809 minifyWithProdErrorCodes: true,
810 + wrapWithModuleBoundaries: true,
811 externals: ['react', 'use-sync-external-store'],
812 },
813
@@ -764,6 +818,7 @@ const bundles = [
818 entry: 'use-sync-external-store/index.native',
819 global: 'useSyncExternalStoreNative',
820 minifyWithProdErrorCodes: true,
821 + wrapWithModuleBoundaries: true,
822 externals: ['react', 'ReactNativeInternalFeatureFlags'],
823 },
824
@@ -783,6 +838,7 @@ const bundles = [
838 entry: 'scheduler',
839 global: 'Scheduler',
840 minifyWithProdErrorCodes: true,
841 + wrapWithModuleBoundaries: true,
842 externals: ['ReactNativeInternalFeatureFlags'],
843 },
844
@@ -802,6 +858,7 @@ const bundles = [
858 entry: 'scheduler/unstable_mock',
859 global: 'SchedulerMock',
860 minifyWithProdErrorCodes: false,
861 + wrapWithModuleBoundaries: false,
862 externals: ['ReactNativeInternalFeatureFlags'],
863 },
864
@@ -818,6 +875,7 @@ const bundles = [
875 entry: 'scheduler/unstable_post_task',
876 global: 'SchedulerPostTask',
877 minifyWithProdErrorCodes: true,
878 + wrapWithModuleBoundaries: false,
879 externals: [],
880 },
881
@@ -828,6 +886,7 @@ const bundles = [
886 entry: 'jest-react',
887 global: 'JestReact',
888 minifyWithProdErrorCodes: false,
889 + wrapWithModuleBoundaries: false,
890 externals: ['react', 'scheduler', 'scheduler/unstable_mock'],
891 },
892
@@ -842,6 +901,7 @@ const bundles = [
901 entry: 'eslint-plugin-react-hooks',
902 global: 'ESLintPluginReactHooks',
903 minifyWithProdErrorCodes: false,
904 + wrapWithModuleBoundaries: false,
905 externals: [],
906 },
907
@@ -852,6 +912,7 @@ const bundles = [
912 entry: 'react-refresh/babel',
913 global: 'ReactFreshBabelPlugin',
914 minifyWithProdErrorCodes: false,
915 + wrapWithModuleBoundaries: false,
916 externals: [],
917 },
918 {
@@ -860,6 +921,7 @@ const bundles = [
921 entry: 'react-refresh/runtime',
922 global: 'ReactFreshRuntime',
923 minifyWithProdErrorCodes: false,
924 + wrapWithModuleBoundaries: false,
925 externals: [],
926 },
927 ];
scripts/rollup/wrappers.js
+51 -1
@@ -1,5 +1,7 @@
1 'use strict';
2
3 +const {resolve} = require('path');
4 +const {readFileSync} = require('fs');
5 const {bundleTypes, moduleTypes} = require('./bundles');
6 const reactVersion = require('../../package.json').version;
7
@@ -25,6 +27,26 @@ const {
27
28 const {RECONCILER} = moduleTypes;
29
30 +function registerInternalModuleStart(globalName) {
31 + const path = resolve(
32 + __dirname,
33 + '..',
34 + '..',
35 + 'packages/shared/registerInternalModuleStart.js'
36 + );
37 + return String(readFileSync(path)).trim();
38 +}
39 +
40 +function registerInternalModuleStop(globalName) {
41 + const path = resolve(
42 + __dirname,
43 + '..',
44 + '..',
45 + 'packages/shared/registerInternalModuleStop.js'
46 + );
47 + return String(readFileSync(path)).trim();
48 +}
49 +
50 const license = ` * Copyright (c) Facebook, Inc. and its affiliates.
51 *
52 * This source code is licensed under the MIT license found in the
@@ -319,7 +341,35 @@ ${source}
341 },
342 };
343
322 -function wrapBundle(source, bundleType, globalName, filename, moduleType) {
344 +function wrapBundle(
345 + source,
346 + bundleType,
347 + globalName,
348 + filename,
349 + moduleType,
350 + wrapWithModuleBoundaries
351 +) {
352 + if (wrapWithModuleBoundaries) {
353 + switch (bundleType) {
354 + case NODE_DEV:
355 + case NODE_PROFILING:
356 + case FB_WWW_DEV:
357 + case FB_WWW_PROFILING:
358 + case RN_OSS_DEV:
359 + case RN_OSS_PROFILING:
360 + case RN_FB_DEV:
361 + case RN_FB_PROFILING:
362 + // Certain DEV and Profiling bundles should self-register their own module boundaries with DevTools.
363 + // This allows the Scheduling Profiler to de-emphasize (dim) internal stack frames.
364 + source = `
365 + ${registerInternalModuleStart(globalName)}
366 + ${source}
367 + ${registerInternalModuleStop(globalName)}
368 + `;
369 + break;
370 + }
371 + }
372 +
373 if (moduleType === RECONCILER) {
374 // Standalone reconciler is only used by third-party renderers.
375 // It is handled separately.