Skip to content

Commit

Permalink
Added hookNames entrypoint to react-devtools-inline
Browse files Browse the repository at this point in the history
And changed dynamic import() code to be passed in rather than embedded in the package
  • Loading branch information
Brian Vaughn committed Sep 8, 2021
1 parent d5dd630 commit 407574e
Show file tree
Hide file tree
Showing 10 changed files with 153 additions and 90 deletions.
4 changes: 4 additions & 0 deletions packages/react-devtools-extensions/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,9 @@ function createPanelIfReactLoaded() {
};
}

const hookNamesModuleLoaderFunction = () =>
import('react-devtools-shared/src/hooks/parseHookNames');

root = createRoot(document.createElement('div'));

render = (overrideTab = mostRecentOverrideTab) => {
Expand All @@ -298,6 +301,7 @@ function createPanelIfReactLoaded() {
componentsPortalContainer,
enabledInspectedElementContextMenu: true,
fetchFileWithCaching,
hookNamesModuleLoaderFunction,
overrideTab,
profilerPortalContainer,
showTabBar: false,
Expand Down
1 change: 1 addition & 0 deletions packages/react-devtools-inline/hookNames.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('./dist/hookNames');
15 changes: 15 additions & 0 deletions packages/react-devtools-inline/src/hookNames.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/** @flow */

import {
parseHookNames,
parseSourceAndMetadata,
prefetchSourceFiles,
purgeCachedMetadata,
} from 'react-devtools-shared/src/hooks/parseHookNames';

export {
parseHookNames,
parseSourceAndMetadata,
prefetchSourceFiles,
purgeCachedMetadata,
};
1 change: 1 addition & 0 deletions packages/react-devtools-inline/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ module.exports = {
entry: {
backend: './src/backend.js',
frontend: './src/frontend.js',
hookNames: './src/hookNames.js',
},
output: {
path: __dirname + '/dist',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
// @flow
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import {createContext} from 'react';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import type {Thenable} from 'shared/ReactTypes';

import {createContext} from 'react';
import typeof * as ParseHookNamesModule from 'react-devtools-shared/src/hooks/parseHookNames';

export type HookNamesModuleLoaderFunction = () => Thenable<ParseHookNamesModule>;
export type Context = HookNamesModuleLoaderFunction | null;

const HookNamesModuleLoaderContext = createContext<Context>(null);
HookNamesModuleLoaderContext.displayName = 'HookNamesModuleLoaderContext';

export default HookNamesModuleLoaderContext;
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
} from 'react-devtools-shared/src/hookNamesCache';
import {loadModule} from 'react-devtools-shared/src/dynamicImportCache';
import FetchFileWithCachingContext from 'react-devtools-shared/src/devtools/views/Components/FetchFileWithCachingContext';
import HookNamesModuleLoaderContext from 'react-devtools-shared/src/devtools/views/Components/HookNamesModuleLoaderContext';
import {SettingsContext} from '../Settings/SettingsContext';
import {enableNamedHooksFeature} from 'react-devtools-feature-flags';

Expand Down Expand Up @@ -60,13 +61,6 @@ export const InspectedElementContext = createContext<Context>(

const POLL_INTERVAL = 1000;

// parseHookNames has a lot of code.
// Embedding it into a build makes the build large.
// This component uses Suspense to lazily import() it only if the feature will be used.
function loadHookNamesModuleLoaderFunction() {
return import('react-devtools-shared/src/hooks/parseHookNames');
}

export type Props = {|
children: ReactNodeList,
|};
Expand All @@ -78,6 +72,11 @@ export function InspectedElementContextController({children}: Props) {
const store = useContext(StoreContext);
const {parseHookNames: parseHookNamesByDefault} = useContext(SettingsContext);

// parseHookNames has a lot of code.
// Embedding it into a build makes the build large.
// This function enables DevTools to make use of Suspense to lazily import() it only if the feature will be used.
const hookNamesModuleLoader = useContext(HookNamesModuleLoaderContext);

const refresh = useCacheRefresh();

// Temporarily stores most recently-inspected (hydrated) path.
Expand Down Expand Up @@ -127,31 +126,31 @@ export function InspectedElementContextController({children}: Props) {
inspectedElement = inspectElement(element, state.path, store, bridge);

if (enableNamedHooksFeature) {
if (parseHookNames || alreadyLoadedHookNames) {
const loadHookNamesModule = loadModule(
loadHookNamesModuleLoaderFunction,
);
if (loadHookNamesModule !== null) {
const {
parseHookNames: loadHookNamesFunction,
prefetchSourceFiles,
purgeCachedMetadata,
} = loadHookNamesModule;
if (typeof hookNamesModuleLoader === 'function') {
if (parseHookNames || alreadyLoadedHookNames) {
const hookNamesModule = loadModule(hookNamesModuleLoader);
if (hookNamesModule !== null) {
const {
parseHookNames: loadHookNamesFunction,
prefetchSourceFiles,
purgeCachedMetadata,
} = hookNamesModule;

purgeCachedMetadataRef.current = purgeCachedMetadata;
prefetchSourceFilesRef.current = prefetchSourceFiles;
purgeCachedMetadataRef.current = purgeCachedMetadata;
prefetchSourceFilesRef.current = prefetchSourceFiles;

if (
inspectedElement !== null &&
inspectedElement.hooks !== null &&
loadHookNamesFunction !== null
) {
hookNames = loadHookNames(
element,
inspectedElement.hooks,
loadHookNamesFunction,
fetchFileWithCaching,
);
if (
inspectedElement !== null &&
inspectedElement.hooks !== null &&
loadHookNamesFunction !== null
) {
hookNames = loadHookNames(
element,
inspectedElement.hooks,
loadHookNamesFunction,
fetchFileWithCaching,
);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
enableNamedHooksFeature,
enableProfilerChangedHookIndices,
} from 'react-devtools-feature-flags';
import HookNamesModuleLoaderContext from 'react-devtools-shared/src/devtools/views/Components/HookNamesModuleLoaderContext';

import type {InspectedElement} from './types';
import type {HooksNode, HooksTree} from 'react-debug-tools/src/ReactDebugHooks';
Expand Down Expand Up @@ -65,6 +66,8 @@ export function InspectedElementHooksTree({
toggleParseHookNames();
};

const hookNamesModuleLoader = useContext(HookNamesModuleLoaderContext);

const hookParsingFailed = parseHookNames && hookNames === null;

let toggleTitle;
Expand All @@ -85,16 +88,18 @@ export function InspectedElementHooksTree({
<div className={styles.HooksTreeView}>
<div className={styles.HeaderRow}>
<div className={styles.Header}>hooks</div>
{enableNamedHooksFeature && (!parseHookNames || hookParsingFailed) && (
<Toggle
className={hookParsingFailed ? styles.ToggleError : null}
isChecked={parseHookNamesOptimistic}
isDisabled={parseHookNamesOptimistic || hookParsingFailed}
onChange={handleChange}
title={toggleTitle}>
<ButtonIcon type="parse-hook-names" />
</Toggle>
)}
{enableNamedHooksFeature &&
typeof hookNamesModuleLoader === 'function' &&
(!parseHookNames || hookParsingFailed) && (
<Toggle
className={hookParsingFailed ? styles.ToggleError : null}
isChecked={parseHookNamesOptimistic}
isDisabled={parseHookNamesOptimistic || hookParsingFailed}
onChange={handleChange}
title={toggleTitle}>
<ButtonIcon type="parse-hook-names" />
</Toggle>
)}
<Button onClick={handleCopy} title="Copy to clipboard">
<ButtonIcon type="copy" />
</Button>
Expand Down
99 changes: 50 additions & 49 deletions packages/react-devtools-shared/src/devtools/views/DevTools.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {SettingsContextController} from './Settings/SettingsContext';
import {TreeContextController} from './Components/TreeContext';
import ViewElementSourceContext from './Components/ViewElementSourceContext';
import FetchFileWithCachingContext from './Components/FetchFileWithCachingContext';
import HookNamesModuleLoaderContext from 'react-devtools-shared/src/devtools/views/Components/HookNamesModuleLoaderContext';
import {ProfilerContextController} from './Profiler/ProfilerContext';
import {SchedulingProfilerContextController} from 'react-devtools-scheduling-profiler/src/SchedulingProfilerContext';
import {ModalDialogContextController} from './ModalDialog';
Expand All @@ -42,12 +43,10 @@ import styles from './DevTools.css';

import './root.css';

import type {HooksTree} from 'react-debug-tools/src/ReactDebugHooks';
import type {InspectedElement} from 'react-devtools-shared/src/devtools/views/Components/types';
import type {FetchFileWithCaching} from './Components/FetchFileWithCachingContext';
import type {HookNamesModuleLoaderFunction} from 'react-devtools-shared/src/devtools/views/Components/HookNamesModuleLoaderContext';
import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
import type {HookNames} from 'react-devtools-shared/src/types';
import type {Thenable} from '../cache';

export type BrowserTheme = 'dark' | 'light';
export type TabID = 'components' | 'profiler';
Expand All @@ -56,9 +55,6 @@ export type ViewElementSource = (
id: number,
inspectedElement: InspectedElement,
) => void;
export type LoadHookNamesFunction = (
hooksTree: HooksTree,
) => Thenable<HookNames>;
export type ViewAttributeSource = (
id: number,
path: Array<string | number>,
Expand Down Expand Up @@ -102,6 +98,7 @@ export type Props = {|
// and extracts hook "names" based on the variables the hook return values get assigned to.
// Not every DevTools build can load source maps, so this property is optional.
fetchFileWithCaching?: ?FetchFileWithCaching,
hookNamesModuleLoaderFunction?: ?HookNamesModuleLoaderFunction,
|};

const componentsTab = {
Expand All @@ -127,6 +124,7 @@ export default function DevTools({
defaultTab = 'components',
enabledInspectedElementContextMenu = false,
fetchFileWithCaching,
hookNamesModuleLoaderFunction,
overrideTab,
profilerPortalContainer,
showTabBar = false,
Expand Down Expand Up @@ -244,52 +242,55 @@ export default function DevTools({
componentsPortalContainer={componentsPortalContainer}
profilerPortalContainer={profilerPortalContainer}>
<ViewElementSourceContext.Provider value={viewElementSource}>
<FetchFileWithCachingContext.Provider
value={fetchFileWithCaching || null}>
<TreeContextController>
<ProfilerContextController>
<SchedulingProfilerContextController>
<ThemeProvider>
<div
className={styles.DevTools}
ref={devToolsRef}
data-react-devtools-portal-root={true}>
{showTabBar && (
<div className={styles.TabBar}>
<ReactLogo />
<span className={styles.DevToolsVersion}>
{process.env.DEVTOOLS_VERSION}
</span>
<div className={styles.Spacer} />
<TabBar
currentTab={tab}
id="DevTools"
selectTab={setTab}
tabs={tabs}
type="navigation"
<HookNamesModuleLoaderContext.Provider
value={hookNamesModuleLoaderFunction || null}>
<FetchFileWithCachingContext.Provider
value={fetchFileWithCaching || null}>
<TreeContextController>
<ProfilerContextController>
<SchedulingProfilerContextController>
<ThemeProvider>
<div
className={styles.DevTools}
ref={devToolsRef}
data-react-devtools-portal-root={true}>
{showTabBar && (
<div className={styles.TabBar}>
<ReactLogo />
<span className={styles.DevToolsVersion}>
{process.env.DEVTOOLS_VERSION}
</span>
<div className={styles.Spacer} />
<TabBar
currentTab={tab}
id="DevTools"
selectTab={setTab}
tabs={tabs}
type="navigation"
/>
</div>
)}
<div
className={styles.TabContent}
hidden={tab !== 'components'}>
<Components
portalContainer={componentsPortalContainer}
/>
</div>
<div
className={styles.TabContent}
hidden={tab !== 'profiler'}>
<Profiler
portalContainer={profilerPortalContainer}
/>
</div>
)}
<div
className={styles.TabContent}
hidden={tab !== 'components'}>
<Components
portalContainer={componentsPortalContainer}
/>
</div>
<div
className={styles.TabContent}
hidden={tab !== 'profiler'}>
<Profiler
portalContainer={profilerPortalContainer}
/>
</div>
</div>
</ThemeProvider>
</SchedulingProfilerContextController>
</ProfilerContextController>
</TreeContextController>
</FetchFileWithCachingContext.Provider>
</ThemeProvider>
</SchedulingProfilerContextController>
</ProfilerContextController>
</TreeContextController>
</FetchFileWithCachingContext.Provider>
</HookNamesModuleLoaderContext.Provider>
</ViewElementSourceContext.Provider>
</SettingsContextController>
<UnsupportedBridgeProtocolDialog />
Expand Down
9 changes: 9 additions & 0 deletions packages/react-devtools-shell/src/devtools.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import {
import {initialize as initializeFrontend} from 'react-devtools-inline/frontend';
import {initDevTools} from 'react-devtools-shared/src/devtools';

// This is a pretty gross hack to make the runtime loaded named-hooks-code work.
// $FlowFixMe
__webpack_public_path__ = '/dist/'; // eslint-disable-line no-undef

const iframe = ((document.getElementById('target'): any): HTMLIFrameElement);

const {contentDocument, contentWindow} = iframe;
Expand Down Expand Up @@ -50,6 +54,10 @@ mountButton.addEventListener('click', function() {
}
});

function hookNamesModuleLoaderFunction() {
return import('react-devtools-inline/hookNames');
}

inject('dist/app.js', () => {
initDevTools({
connect(cb) {
Expand All @@ -58,6 +66,7 @@ inject('dist/app.js', () => {
createElement(DevTools, {
browserTheme: 'light',
enabledInspectedElementContextMenu: true,
hookNamesModuleLoaderFunction,
showTabBar: true,
warnIfLegacyBackendDetected: true,
warnIfUnsupportedVersionDetected: true,
Expand Down

0 comments on commit 407574e

Please sign in to comment.