Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(devtools): built-in doctor tab for rsdoctor #5368

Merged
merged 16 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/wicked-toes-beg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@integration-test/devtools': minor
'@modern-js/devtools-client': minor
'@modern-js/plugin-devtools': minor
'@modern-js/devtools-kit': minor
---

feat(devtools): add new doctor tab to show overview of rsdoctor
feat(devtools): 新增 doctor 页面展示 Rsdoctor 概览信息
16 changes: 16 additions & 0 deletions packages/devtools/client/modern.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,22 @@ export default defineConfig<'rspack'>({
disableInlineRuntimeChunk: true,
disableSourceMap: process.env.NODE_ENV === 'production',
},
performance: {
chunkSplit: {
strategy: 'split-by-experience',
override: {
cacheGroups: {
components: {
test: /\/src\/components\/.*\.(scss|css)$/,
chunks: 'all',
name: 'components',
enforce: true,
priority: 9999,
},
},
},
},
},
tools: {
postcss: (config, { addPlugins }) => {
addPlugins(require('postcss-custom-media'));
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.indicate-card {
border-radius: var(--radius-5);
box-shadow: var(--shadow-4);
padding: var(--space-5);
:global(.dark) & {
background-color: var(--gray-1);
}
}
35 changes: 35 additions & 0 deletions packages/devtools/client/src/components/Card/Indicate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { FC } from 'react';
import clsx from 'clsx';
import { Box, Flex } from '@radix-ui/themes';
import type { BoxProps } from '@radix-ui/themes/dist/cjs/components/box';
import type { FlexProps } from '@radix-ui/themes/dist/cjs/components/flex';
import styles from './Indicate.module.scss';

export type IndicateCardProps = BoxProps & React.RefAttributes<HTMLDivElement>;

const Card: FC<IndicateCardProps> = ({ children, className, ...props }) => {
return (
<Box {...props} className={clsx(styles.indicateCard, className)}>
{children}
</Box>
);
};

Card.displayName = 'IndicateCard';

const Column: FC<FlexProps> = props => (
<Flex
gap="2"
height="100%"
justify="center"
direction="column"
align="start"
{...props}
/>
);

Column.displayName = 'IndicateCard.Column';

export const IndicateCard = Object.assign(Card, {
Column,
});
1 change: 1 addition & 0 deletions packages/devtools/client/src/components/Card/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './Indicate';
24 changes: 21 additions & 3 deletions packages/devtools/client/src/components/Devtools/Capsule.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { SetupClientParams } from '@modern-js/devtools-kit/runtime';
import { Flex, Theme } from '@radix-ui/themes';
import React, { useEffect, useState } from 'react';
import { useEvent, useToggle } from 'react-use';
import { useAsync, useEvent, useToggle } from 'react-use';
import { HiMiniCursorArrowRipple } from 'react-icons/hi2';
import { withQuery } from 'ufo';
import Visible from '../Visible';
Expand All @@ -13,9 +13,21 @@ import { $client, wallAgent } from '@/entries/mount/state';
import { pTimeout } from '@/utils/promise';
import { ReactDevtoolsWallListener } from '@/utils/react-devtools';

const parseDeepLink = (url = window.location) => {
// Expected: #/__devtools/doctor
const { hash } = url;
// Parse pathname from hash.
const pathname = hash.match(/^#\/__devtools(.*)/)?.[1];
// Check if match the expected pattern.
if (typeof pathname !== 'string') return null;
if (pathname === '') return '/';
return pathname;
};

export const DevtoolsCapsule: React.FC<SetupClientParams> = props => {
const logoSrc = props.def.assets.logo;
const [showDevtools, toggleDevtools] = useToggle(false);
const deepLink = parseDeepLink();
const [showDevtools, toggleDevtools] = useToggle(Boolean(deepLink));
const [loadDevtools, setLoadDevtools] = useState(false);

const src = withQuery(props.endpoint, { src: props.dataSource });
Expand Down Expand Up @@ -64,13 +76,19 @@ export const DevtoolsCapsule: React.FC<SetupClientParams> = props => {
setLoadDevtools(true);
try {
const client = await pTimeout($client, 10_000);
client.remote.pullUpReactInspector();
client.remote.pullUp('/react/components#inspecting');
} catch (e) {
console.error(e);
document.documentElement.style.removeProperty('cursor');
}
};

useAsync(async () => {
if (!deepLink) return;
const client = await pTimeout($client, 10_000);
client.remote.pullUp(deepLink);
}, []);

return (
<Theme appearance={appearance} className={appearance}>
<Visible when={showDevtools} keepAlive={true} load={loadDevtools}>
Expand Down
25 changes: 8 additions & 17 deletions packages/devtools/client/src/components/Devtools/Puller.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,23 @@ import React, { useEffect } from 'react';
import { useNavigate } from '@modern-js/runtime/router';
import { useThrowable } from '@/utils';
import { $mountPoint } from '@/entries/client/routes/state';
import { wallAgent } from '@/entries/client/routes/react/state';

let _intendPullUp = false;
let _intendPullUp = '';

$mountPoint.then(({ hooks }) => {
hooks.hookOnce('pullUpReactInspector', async () => {
_intendPullUp = true;
hooks.hookOnce('pullUp', async target => {
_intendPullUp = target;
});
});

export const DevtoolsPuller: React.FC = () => {
export const Puller: React.FC = () => {
const navigate = useNavigate();
const mountPoint = useThrowable($mountPoint);
const handlePullUp = async () => {
navigate('/react');
if (wallAgent.status === 'active') {
wallAgent.send('startInspectingNative', null);
} else {
wallAgent.hookOnce('active', () => {
wallAgent.send('startInspectingNative', null);
});
}
};
useEffect(() => {
_intendPullUp && handlePullUp();
mountPoint.hooks.hook('pullUpReactInspector', handlePullUp);
_intendPullUp && navigate(_intendPullUp);
mountPoint.hooks.hook('pullUp', async target => {
navigate(target);
});
}, []);
return null;
};
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,12 @@ export const ErrorFallback: React.FC<ErrorFallbackProps> = props => {
<Heading as="h4" mb="4">
<Text as="span">{title}</Text>
</Heading>
<TextArea disabled style={{ height: '50vh' }}>
{description}
</TextArea>
<TextArea disabled style={{ height: '50vh' }} value={description} />
</Box>
);
};

export const ErrorRouteHandler: React.FC<ErrorFallbackProps> = props => {
export const ErrorRouteHandler: React.FC = () => {
const error = useRouteError();
return <ErrorFallback {...props} error={error} />;
return <ErrorFallback resetErrorBoundary={() => null} error={error} />;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.container {
width: max-content;
min-width: var(--space-9);
color: var(--gray-11);
}

.children {
color: var(--gray-9);
}
25 changes: 25 additions & 0 deletions packages/devtools/client/src/components/Error/FeatureDisabled.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React, { PropsWithChildren } from 'react';
import { Box, Flex, Heading } from '@radix-ui/themes';
import { HiExclamationCircle } from 'react-icons/hi2';
import styles from './FeatureDisabled.module.scss';

export interface FeatureDisabledProps extends PropsWithChildren {
title: string;
}

export const FeatureDisabled: React.FC<FeatureDisabledProps> = props => {
const { title, children } = props;
return (
<Flex className={styles.container} mt="9" mx="auto" gap="2">
<Flex height="9" align="center">
<HiExclamationCircle size="36" />
</Flex>
<Box>
<Flex height="9" align="center">
<Heading>{title}</Heading>
</Flex>
<Box className={styles.children}>{children}</Box>
</Box>
</Flex>
);
};
Empty file.
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { ErrorRouteHandler } from '@/components/ErrorFallback/ErrorFallback';
import { ErrorRouteHandler } from '@/components/Error/Fallback';

export default ErrorRouteHandler;
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { ErrorRouteHandler } from '@/components/ErrorFallback/ErrorFallback';
import { ErrorRouteHandler } from '@/components/Error/Fallback';

export default ErrorRouteHandler;
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { FC } from 'react';
import { useRouteError } from '@modern-js/runtime/router';
import { Box, Link } from '@radix-ui/themes';
import { useSnapshot } from 'valtio';
import { parseURL } from 'ufo';
import { $definition } from '../state';
import {
ErrorFallbackProps,
ErrorRouteHandler,
} from '@/components/Error/Fallback';
import { FeatureDisabled } from '@/components/Error/FeatureDisabled';

const Handler: FC<ErrorFallbackProps> = () => {
const error = useRouteError();
const def = useSnapshot($definition);
const isStateError =
error && typeof error === 'object' && Object.keys(error).length === 0;
if (isStateError) {
const websiteDisplay = parseURL(def.doctor.quickStart).host;
const websiteLink = (
<Link
href={def.doctor.quickStart}
target="_blank"
rel="noopener noreferrer"
>
🔗{websiteDisplay}
</Link>
);
return (
<Box width="100%">
<FeatureDisabled title="Doctor is not working">
Visit the website of {websiteLink} to learn more.
</FeatureDisabled>
</Box>
);
} else {
return <ErrorRouteHandler />;
}
};

export default Handler;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const handle = {
breadcrumb: {
title: 'Doctor',
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
.container {
max-width: 40rem;
width: 100%;
}

.primary-card {
flex: 4;
min-width: max-content;
background-color: var(--amber-9);
:global(.dark) & {
background-color: var(--amber-9);
}
button {
border: none;
border-radius: var(--radius-thumb);
color: var(--black-a8);
font-size: var(--font-size-1);
font-weight: var(--font-weight-regular);
padding: var(--space-1) var(--space-2);
overflow: hidden;
background-color: var(--black-a2);
&:hover,
&:active {
background-color: var(--black-a1);
}
}
}

.info-card {
flex: 2;
min-width: max-content;
}

.count-card {
flex: 1;
min-width: max-content;
}

.compile-cost-card {
flex: 4;
}

.logo {
width: var(--space-8);
opacity: 0.5;
}

.count-text {
display: flex;
align-items: center;
gap: var(--space-1);
}

.error-item {
display: flex;
align-items: center;
width: 100%;
gap: var(--space-1);
color: var(--gray-11);
}

.text-truncation {
width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

.cost-box {
min-width: var(--space-2);

.cost-bar {
height: var(--space-1);
width: 100%;
border-radius: var(--radius-thumb);
}

&:nth-child(odd) {
.cost-bar {
background-color: var(--red-9);
}
}

&:nth-child(even) {
.cost-bar {
background-color: var(--orange-9);
}
}

.cost-label {
word-break: keep-all;
display: block;
margin-bottom: var(--space-2);
}
}

.heading {
font-family: 'NotoSans';
font-weight: 900;
font-size: var(--font-size-7);
}
Loading
Loading