Skip to content

Commit

Permalink
DevTools: Separate named hooks I/O and CPU code
Browse files Browse the repository at this point in the history
I/O code has been moved into a file (loadSourceAndMetadata) that runs on the main/UI thread. CPU code has been moved into a file (parseSourceAndMetadata) that runs in a web worker (except for Jest tests).

This is being done to increase the likelihood of re-using cached source files when fetching from the network.
  • Loading branch information
Brian Vaughn committed Aug 27, 2021
1 parent 8723e77 commit de091bc
Show file tree
Hide file tree
Showing 9 changed files with 973 additions and 780 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,24 @@ describe('parseHookNames', () => {

inspectHooks = require('react-debug-tools/src/ReactDebugHooks')
.inspectHooks;
parseHookNames = require('../parseHookNames/parseHookNames').parseHookNames;

// Jest can't run the workerized version of this module.
const loadSourceAndMetadata = require('../parseHookNames/loadSourceAndMetadata')
.default;
const parseSourceAndMetadata = require('../parseHookNames/parseSourceAndMetadata')
.parseSourceAndMetadata;
parseHookNames = async hooksTree => {
const [
hooksList,
locationKeyToHookSourceAndMetadata,
] = await loadSourceAndMetadata(hooksTree);

// Runs in a Worker because it's CPU intensive:
return parseSourceAndMetadata(
hooksList,
locationKeyToHookSourceAndMetadata,
);
};

// Jest (jest-runner?) configures Errors to automatically account for source maps.
// This changes behavior between our tests and the browser.
Expand Down Expand Up @@ -880,18 +897,21 @@ describe('parseHookNames', () => {
describe('parseHookNames worker', () => {
let inspectHooks;
let parseHookNames;
let workerizedParseHookNamesMock;
let workerizedParseSourceAndMetadataMock;

beforeEach(() => {
window.Worker = undefined;

workerizedParseHookNamesMock = jest.fn();
workerizedParseSourceAndMetadataMock = jest.fn(() => {
console.log('mock fn');
return [];
});

jest.mock('../parseHookNames/parseHookNames.worker.js', () => {
jest.mock('../parseHookNames/parseSourceAndMetadata.worker.js', () => {
return {
__esModule: true,
default: () => ({
parseHookNames: workerizedParseHookNamesMock,
parseSourceAndMetadata: workerizedParseSourceAndMetadataMock,
}),
};
});
Expand All @@ -912,11 +932,12 @@ describe('parseHookNames worker', () => {
.Component;

window.Worker = true;
// resets module so mocked worker instance can be updated

// Reset module so mocked worker instance can be updated.
jest.resetModules();
parseHookNames = require('../parseHookNames').parseHookNames;

await getHookNamesForComponent(Component);
expect(workerizedParseHookNamesMock).toHaveBeenCalledTimes(1);
expect(workerizedParseSourceAndMetadataMock).toHaveBeenCalledTimes(1);
});
});
37 changes: 29 additions & 8 deletions packages/react-devtools-extensions/src/parseHookNames/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,38 @@
* @flow
*/

// This file uses workerize to load ./parseHookNames.worker as a webworker and instanciates it,
// exposing flow typed functions that can be used on other files.
import type {HookSourceAndMetadata} from './loadSourceAndMetadata';
import type {HooksNode, HooksTree} from 'react-debug-tools/src/ReactDebugHooks';
import type {HookNames} from 'react-devtools-shared/src/types';

import WorkerizedParseHookNames from './parseHookNames.worker';
import typeof * as ParseHookNamesModule from './parseHookNames';
import WorkerizedParseSourceAndMetadata from './parseSourceAndMetadata.worker';
import typeof * as ParseSourceAndMetadataModule from './parseSourceAndMetadata';

const workerizedParseHookNames: ParseHookNamesModule = WorkerizedParseHookNames();
import loadSourceAndMetadata from './loadSourceAndMetadata';

type ParseHookNames = $PropertyType<ParseHookNamesModule, 'parseHookNames'>;
const workerizedParseHookNames: ParseSourceAndMetadataModule = WorkerizedParseSourceAndMetadata();

export const parseHookNames: ParseHookNames = hooksTree =>
workerizedParseHookNames.parseHookNames(hooksTree);
export function parseSourceAndMetadata(
hooksList: Array<HooksNode>,
locationKeyToHookSourceAndMetadata: Map<string, HookSourceAndMetadata>,
): Promise<HookNames | null> {
return workerizedParseHookNames.parseSourceAndMetadata(
hooksList,
locationKeyToHookSourceAndMetadata,
);
}

export const purgeCachedMetadata = workerizedParseHookNames.purgeCachedMetadata;

export async function parseHookNames(
hooksTree: HooksTree,
): Promise<HookNames | null> {
// Runs on the main/UI thread so it can reuse Network cache:
const [
hooksList,
locationKeyToHookSourceAndMetadata,
] = await loadSourceAndMetadata(hooksTree);

// Runs in a Worker because it's CPU intensive:
return parseSourceAndMetadata(hooksList, locationKeyToHookSourceAndMetadata);
}
Loading

0 comments on commit de091bc

Please sign in to comment.