From 96023a92501fce7395637c8f4e79520ce85b352e Mon Sep 17 00:00:00 2001 From: Asuka109 Date: Mon, 25 Sep 2023 21:18:51 +0800 Subject: [PATCH] feat(devtools): improve draggable action button & route tester (#4721) * feat: re-organize exports * build: update pnpm lock file * fix: can't resolve main * feat: auto configure devtools data source hostname * feat: sizing logo image * fix: disable rspack to fix resolve issues * feat: remove settings button * feat: make action button draggable * feat: add box shadow for framebox * feat: use environment variable `HASH_SUFFIXED_VERSION` control whether use hash suffix for version * build: update pnpm lock file * feat: re-export type `Options` * fix: property `devtools` is missing in return type * build: update pnpm lock file * feat: hosting client in local * build: remove plugin devtools from the dependencies of app tools * fix: not found export `RPC_SERVER_PATHNAME` * feat: disable dynamic prefix * build: update pnpm lock file * build: update pnpm lock file * feat: extract devtools basename as `ROUTE_BASENAME` * fix: can't resolve typings * fix: `pages` tab content overflow * fix: client endpoint not found * feat: better draggable ux * fix: route url path * fix: can't resolve client routes within a '/' server route * fix: cannot find module './types' * feat: rewrite integration test app * feat: remove chinese text --- packages/devtools/client/modern.config.ts | 12 +- .../ClientRouteStats/RemixRoute.tsx | 10 +- .../ClientRouteStats/RemixRouteStats.tsx | 5 +- .../routes/pages/EntryRoute/EntryRoute.tsx | 6 +- .../client/src/routes/pages/layout.tsx | 13 -- .../devtools/client/src/routes/pages/page.tsx | 20 ++- .../devtools/kit/src/{types => }/client.ts | 14 ++ packages/devtools/kit/src/constants.ts | 1 + packages/devtools/kit/src/index.ts | 5 +- packages/devtools/kit/src/{types => }/logo.ts | 0 .../kit/src/{types => }/mount-point.ts | 2 - .../devtools/kit/src/{types => }/server.ts | 0 packages/devtools/kit/src/types/index.ts | 17 -- packages/devtools/kit/src/utils.ts | 2 +- .../mount/src/components/Devtools/Action.tsx | 83 ++-------- .../devtools/mount/src/utils/draggable.ts | 145 ++++++++++++++++++ packages/devtools/plugin/package.json | 2 +- packages/devtools/plugin/src/cli.ts | 14 +- packages/devtools/plugin/src/config.ts | 2 - tests/integration/devtools/modern.config.ts | 4 + .../devtools/src/admin/routes/index.css | 115 -------------- .../devtools/src/admin/routes/layout.tsx | 7 +- .../devtools/src/admin/routes/page.loader.ts | 5 + .../devtools/src/admin/routes/page.tsx | 93 +---------- .../integration/devtools/src/devtools/App.tsx | 2 +- .../devtools/src/main/routes/layout.tsx | 9 ++ .../devtools/src/main/routes/page.tsx | 14 ++ .../src/main/routes/post/[id]/page.tsx | 18 +++ .../devtools/src/main/routes/user/page.tsx | 10 ++ .../src/main/routes/user/profile/page.tsx | 12 ++ tests/integration/devtools/src/prelude.css | 4 + .../integration/devtools/tests/index.test.ts | 2 +- 32 files changed, 305 insertions(+), 343 deletions(-) delete mode 100644 packages/devtools/client/src/routes/pages/layout.tsx rename packages/devtools/kit/src/{types => }/client.ts (80%) create mode 100644 packages/devtools/kit/src/constants.ts rename packages/devtools/kit/src/{types => }/logo.ts (100%) rename packages/devtools/kit/src/{types => }/mount-point.ts (81%) rename packages/devtools/kit/src/{types => }/server.ts (100%) delete mode 100644 packages/devtools/kit/src/types/index.ts create mode 100644 packages/devtools/mount/src/utils/draggable.ts delete mode 100644 tests/integration/devtools/src/admin/routes/index.css create mode 100644 tests/integration/devtools/src/admin/routes/page.loader.ts create mode 100644 tests/integration/devtools/src/main/routes/layout.tsx create mode 100644 tests/integration/devtools/src/main/routes/page.tsx create mode 100644 tests/integration/devtools/src/main/routes/post/[id]/page.tsx create mode 100644 tests/integration/devtools/src/main/routes/user/page.tsx create mode 100644 tests/integration/devtools/src/main/routes/user/profile/page.tsx create mode 100644 tests/integration/devtools/src/prelude.css diff --git a/packages/devtools/client/modern.config.ts b/packages/devtools/client/modern.config.ts index 37fdf57c037d..5d3dcd01eeac 100644 --- a/packages/devtools/client/modern.config.ts +++ b/packages/devtools/client/modern.config.ts @@ -1,16 +1,16 @@ import { appTools, defineConfig } from '@modern-js/app-tools'; -import { proxyPlugin } from '@modern-js/plugin-proxy'; +import { ROUTE_BASENAME } from '@modern-js/devtools-kit'; import { version } from './package.json'; // https://modernjs.dev/en/configure/app/usage export default defineConfig<'rspack'>({ runtime: { router: { - basename: '/_modern_js/devtools', + basename: ROUTE_BASENAME, }, }, dev: { - assetPrefix: '/_modern_js/devtools', + assetPrefix: ROUTE_BASENAME, port: 8780, }, source: { @@ -20,10 +20,8 @@ export default defineConfig<'rspack'>({ }, }, output: { - assetPrefix: '/_modern_js/devtools', + assetPrefix: ROUTE_BASENAME, enableCssModuleTSDeclaration: true, }, - tools: {}, - html: {}, - plugins: [appTools({ bundler: 'experimental-rspack' }), proxyPlugin()], + plugins: [appTools({ bundler: 'experimental-rspack' })], }); diff --git a/packages/devtools/client/src/routes/pages/EntryRoute/ClientRouteStats/RemixRoute.tsx b/packages/devtools/client/src/routes/pages/EntryRoute/ClientRouteStats/RemixRoute.tsx index 557f04c492c5..82779b48c9b5 100644 --- a/packages/devtools/client/src/routes/pages/EntryRoute/ClientRouteStats/RemixRoute.tsx +++ b/packages/devtools/client/src/routes/pages/EntryRoute/ClientRouteStats/RemixRoute.tsx @@ -3,7 +3,7 @@ import { RouteObject } from '@modern-js/runtime/router'; import { Box, Flex, Link, Code } from '@radix-ui/themes'; import styled from '@emotion/styled'; import _ from 'lodash'; -import { withoutTrailingSlash } from 'ufo'; +import { resolveURL } from 'ufo'; import { useHoverDirty } from 'react-use'; import { MatchRemixRouteContext } from '../MatchRemixRouteContext'; @@ -18,10 +18,8 @@ export const RemixRoute: React.FC = ({ route }) => { '_component' in curr && _.isString(curr._component) ? curr._component : null; - const displayPath = routes - .map(r => r.path && withoutTrailingSlash(r.path)) - .filter(_.isString) - .join('/'); + const displayPath = + resolveURL('/', ...routes.map(r => r.path).filter(_.isString)) || '/'; const isIndex = curr.index ?? false; const isRoot = displayPath === '/'; const matched = useContext(MatchRemixRouteContext); @@ -35,7 +33,7 @@ export const RemixRoute: React.FC = ({ route }) => { - {!isRoot && ( + {(isIndex && isRoot) || ( {displayPath} diff --git a/packages/devtools/client/src/routes/pages/EntryRoute/ClientRouteStats/RemixRouteStats.tsx b/packages/devtools/client/src/routes/pages/EntryRoute/ClientRouteStats/RemixRouteStats.tsx index e694f8700757..6413b0e3e743 100644 --- a/packages/devtools/client/src/routes/pages/EntryRoute/ClientRouteStats/RemixRouteStats.tsx +++ b/packages/devtools/client/src/routes/pages/EntryRoute/ClientRouteStats/RemixRouteStats.tsx @@ -6,6 +6,7 @@ import { import type { ServerRoute } from '@modern-js/types'; import { Flex } from '@radix-ui/themes'; import React, { useContext, useMemo } from 'react'; +import { resolveURL, cleanDoubleSlashes } from 'ufo'; import { MatchUrlContext } from '../../MatchUrl'; import { MatchRemixRouteContext } from '../MatchRemixRouteContext'; import { RemixRoute } from './RemixRoute'; @@ -22,7 +23,7 @@ export const RemixRouteStats: React.FC = ({ const testingUrl = useContext(MatchUrlContext); const matchedRoutes = useMemo(() => { if (!testingUrl || !remixRoutes) return []; - const location = testingUrl.replace(route.urlPath, ''); + const location = cleanDoubleSlashes(testingUrl.replace(route.urlPath, '/')); const matched = matchRemixRoutes(remixRoutes, location) ?? []; return matched as RouteMatch[]; }, [remixRoutes, testingUrl]); @@ -35,7 +36,7 @@ export const RemixRouteStats: React.FC = ({ {remixRoutes.map(r => ( ))} diff --git a/packages/devtools/client/src/routes/pages/EntryRoute/EntryRoute.tsx b/packages/devtools/client/src/routes/pages/EntryRoute/EntryRoute.tsx index 12031b194bef..d1a1cafd84e5 100644 --- a/packages/devtools/client/src/routes/pages/EntryRoute/EntryRoute.tsx +++ b/packages/devtools/client/src/routes/pages/EntryRoute/EntryRoute.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Badge, Flex } from '@radix-ui/themes'; +import { Badge, Flex, Box } from '@radix-ui/themes'; import { ServerRoute } from '@modern-js/types'; import { BaseRoute } from '../BaseRoute'; import { EntryStats } from './EntryStats'; @@ -14,7 +14,9 @@ const EntryRoute: React.FC = ({ route }) => { - + + + ); diff --git a/packages/devtools/client/src/routes/pages/layout.tsx b/packages/devtools/client/src/routes/pages/layout.tsx deleted file mode 100644 index db225a501c40..000000000000 --- a/packages/devtools/client/src/routes/pages/layout.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { Outlet } from '@modern-js/runtime/router'; -import { ScrollArea } from '@radix-ui/themes'; -import { Suspense } from 'react'; - -export default function Layout() { - return ( - - - - - - ); -} diff --git a/packages/devtools/client/src/routes/pages/page.tsx b/packages/devtools/client/src/routes/pages/page.tsx index ad4551ef66e2..1468f7811f84 100644 --- a/packages/devtools/client/src/routes/pages/page.tsx +++ b/packages/devtools/client/src/routes/pages/page.tsx @@ -32,11 +32,13 @@ const Page: React.FC = () => { - - {serverRoutes.map(route => ( - - ))} - + + + {serverRoutes.map(route => ( + + ))} + + ); @@ -44,11 +46,17 @@ const Page: React.FC = () => { export default Page; -const Container = styled(Box)({}); +const Container = styled(Box)({ + display: 'flex', + flexDirection: 'column', + height: '100%', +}); const RoutesContainer = styled(Box)({ display: 'flex', + flex: '0', flexDirection: 'column', + alignItems: 'stretch', gap: 'var(--space-2)', justifyContent: 'space-between', }); diff --git a/packages/devtools/kit/src/types/client.ts b/packages/devtools/kit/src/client.ts similarity index 80% rename from packages/devtools/kit/src/types/client.ts rename to packages/devtools/kit/src/client.ts index c35f63b4c304..e53c5f761282 100644 --- a/packages/devtools/kit/src/types/client.ts +++ b/packages/devtools/kit/src/client.ts @@ -39,3 +39,17 @@ export class ClientDefinition { assets: AssetDefinition = new AssetDefinition(); } + +export interface IframeTabView { + type: 'iframe'; + src: string; +} + +export type CustomTabView = IframeTabView; + +export interface CustomTab { + name: string; + title: string; + view: CustomTabView; + icon?: string; +} diff --git a/packages/devtools/kit/src/constants.ts b/packages/devtools/kit/src/constants.ts new file mode 100644 index 000000000000..cf2676563f9a --- /dev/null +++ b/packages/devtools/kit/src/constants.ts @@ -0,0 +1 @@ +export const ROUTE_BASENAME = '/__devtools'; diff --git a/packages/devtools/kit/src/index.ts b/packages/devtools/kit/src/index.ts index 6d5a6ef495dd..df883e2c05ba 100644 --- a/packages/devtools/kit/src/index.ts +++ b/packages/devtools/kit/src/index.ts @@ -1,2 +1,5 @@ -export * from './types'; +export * from './server'; +export * from './client'; +export * from './mount-point'; export * from './utils'; +export * from './constants'; diff --git a/packages/devtools/kit/src/types/logo.ts b/packages/devtools/kit/src/logo.ts similarity index 100% rename from packages/devtools/kit/src/types/logo.ts rename to packages/devtools/kit/src/logo.ts diff --git a/packages/devtools/kit/src/types/mount-point.ts b/packages/devtools/kit/src/mount-point.ts similarity index 81% rename from packages/devtools/kit/src/types/mount-point.ts rename to packages/devtools/kit/src/mount-point.ts index 86e69583f51d..1fe636bcd31c 100644 --- a/packages/devtools/kit/src/types/mount-point.ts +++ b/packages/devtools/kit/src/mount-point.ts @@ -1,5 +1,3 @@ -import _ from '@modern-js/utils/lodash'; - export interface MountPointFunctions { getLocation: () => string; } diff --git a/packages/devtools/kit/src/types/server.ts b/packages/devtools/kit/src/server.ts similarity index 100% rename from packages/devtools/kit/src/types/server.ts rename to packages/devtools/kit/src/server.ts diff --git a/packages/devtools/kit/src/types/index.ts b/packages/devtools/kit/src/types/index.ts deleted file mode 100644 index 06524e81fab4..000000000000 --- a/packages/devtools/kit/src/types/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -export interface IframeTabView { - type: 'iframe'; - src: string; -} - -export type CustomTabView = IframeTabView; - -export interface CustomTab { - name: string; - title: string; - view: CustomTabView; - icon?: string; -} - -export * from './server'; -export * from './client'; -export * from './mount-point'; diff --git a/packages/devtools/kit/src/utils.ts b/packages/devtools/kit/src/utils.ts index f90121ba2d2e..0433ed9ad0a3 100644 --- a/packages/devtools/kit/src/utils.ts +++ b/packages/devtools/kit/src/utils.ts @@ -1,4 +1,4 @@ -import { ShortenAlias } from './types'; +import { ShortenAlias } from './client'; export function applyShortenAliases( resource: string, diff --git a/packages/devtools/mount/src/components/Devtools/Action.tsx b/packages/devtools/mount/src/components/Devtools/Action.tsx index d04ccfac88b2..ee151e5f7f74 100644 --- a/packages/devtools/mount/src/components/Devtools/Action.tsx +++ b/packages/devtools/mount/src/components/Devtools/Action.tsx @@ -1,10 +1,11 @@ +import { ROUTE_BASENAME, SetupClientOptions } from '@modern-js/devtools-kit'; import React from 'react'; -import { useGetSet, useToggle } from 'react-use'; -import { withQuery, stringifyParsedURL, parseURL } from 'ufo'; -import { SetupClientOptions } from '@modern-js/devtools-kit'; +import { useToggle } from 'react-use'; +import { parseURL, stringifyParsedURL, withQuery } from 'ufo'; import Visible from '../Visible'; import styles from './Action.module.scss'; import FrameBox from './FrameBox'; +import { useStickyDraggable } from '@/utils/draggable'; const parseDataSource = (url: string) => { const newSrc = parseURL(url); @@ -12,62 +13,10 @@ const parseDataSource = (url: string) => { protocol: location.protocol === 'https:' ? 'wss:' : 'ws:', host: location.host, ...newSrc, - pathname: newSrc.pathname || '/_modern_js/devtools/rpc', + pathname: newSrc.pathname || `${ROUTE_BASENAME}/rpc`, }); }; -const useStickyDraggable = () => { - const [isDragging, setIsDragging] = useGetSet(false); - const handleMouseDown = (e: React.MouseEvent) => { - const target = e.currentTarget; - if (!(target instanceof HTMLElement)) { - return; - } - const { offsetX, offsetY } = e.nativeEvent; - const handleMousemove = (e: MouseEvent) => { - if (e.movementX + e.movementY > 1) { - setIsDragging(true); - } - - const x = e.clientX - offsetX; - const y = e.clientY - offsetY; - const distances = [ - { prop: 'top', value: e.clientY } as const, - { prop: 'bottom', value: window.innerHeight - e.clientY } as const, - { prop: 'left', value: e.clientX } as const, - { prop: 'right', value: window.innerWidth - e.clientX } as const, - ]; - const [primary, ...rest] = distances.sort((a, b) => a.value - b.value); - target.style[primary.prop] = '10px'; - for (const unset of rest) { - target.style.removeProperty(unset.prop); - } - if (['top', 'bottom'].includes(primary.prop)) { - target.style.left = `${x}px`; - } else { - target.style.top = `${y}px`; - } - }; - window.addEventListener('mousemove', handleMousemove); - window.addEventListener('blur', () => { - setTimeout(() => setIsDragging(false), 0); - window.removeEventListener('mousemove', handleMousemove); - }); - window.addEventListener( - 'mouseup', - () => { - setTimeout(() => setIsDragging(false), 0); - window.removeEventListener('mousemove', handleMousemove); - }, - { once: true }, - ); - }; - return { - onMouseDown: handleMouseDown, - isDragging: isDragging(), - }; -}; - const DevtoolsAction: React.FC = props => { const logoSrc = process.env._MODERN_DEVTOOLS_LOGO_SRC!; const opts: Required = { @@ -80,28 +29,26 @@ const DevtoolsAction: React.FC = props => { let src = opts.endpoint; src = withQuery(src, { src: opts.dataSource }); - const { isDragging, onMouseDown } = useStickyDraggable(); + const draggable = useStickyDraggable({ clamp: true }); return ( <> -
+ ); }; diff --git a/packages/devtools/mount/src/utils/draggable.ts b/packages/devtools/mount/src/utils/draggable.ts new file mode 100644 index 000000000000..ad0e4d8d516d --- /dev/null +++ b/packages/devtools/mount/src/utils/draggable.ts @@ -0,0 +1,145 @@ +import React, { useState } from 'react'; +import { useEvent } from 'react-use'; + +export interface StickyDraggableOptions { + margin?: string | number; + clamp?: boolean; +} + +const isCrossMargin = (a: string, b: string) => + ({ + top: ['left', 'right'], + bottom: ['left', 'right'], + left: ['top', 'bottom'], + right: ['top', 'bottom'], + }[a]?.includes(b) ?? false); + +interface DraggingState { + el: HTMLElement; + clickable: boolean; + moveable: boolean; + offsetX: number; + offsetY: number; + startX: number; + startY: number; +} + +const RECT_SIDES = ['top', 'bottom', 'left', 'right'] as const; + +type RectSide = (typeof RECT_SIDES)[number]; + +interface OrderedRectSide { + key: RectSide; + value: number; +} + +const sortRectSides = (sides: Record) => { + const arr: OrderedRectSide[] = [ + { key: 'top', value: sides.top }, + { key: 'bottom', value: sides.bottom }, + { key: 'left', value: sides.left }, + { key: 'right', value: sides.right }, + ]; + const [primary, ...rest] = arr.sort((a, b) => a.value - b.value); + const _secondaryIndex = rest.findIndex(({ key }) => + isCrossMargin(primary.key, key), + ); + const [secondary] = rest.splice(_secondaryIndex, 1); + return [primary, secondary, rest[0], rest[1]] as const; +}; + +const getElementRectSides = (el: HTMLElement): Record => { + const { top, left, height, width } = el.getBoundingClientRect(); + return { + top, + left, + bottom: window.innerHeight - top - height, + right: window.innerWidth - left - width, + }; +}; + +export const useStickyDraggable = (options?: StickyDraggableOptions) => { + const rawMargin = options?.margin ?? '10px'; + const margin = typeof rawMargin === 'number' ? `${rawMargin}px` : rawMargin; + const [state, setState] = useState(); + + const handleRelease = () => { + if (!state) { + return; + } + state.moveable = false; + const { el } = state; + const [primary] = sortRectSides(getElementRectSides(el)); + el.style.transition = `${primary.key} 200ms`; + requestIdleCallback(() => { + setState(undefined); + el.style[primary.key] = margin; + }); + }; + + const handleMouseMove = (e: MouseEvent) => { + if (!state?.moveable) { + return; + } + const { startX, startY } = state; + if ((startX - e.clientX) ** 2 + (startY - e.clientY) ** 2 > 9) { + state.clickable = false; + } + const { width, height } = state.el.getBoundingClientRect(); + const top = e.clientY - state.offsetY; + const left = e.clientX - state.offsetX; + const sides: Record = { + top, + left, + bottom: window.innerHeight - top - height, + right: window.innerWidth - left - width, + }; + const ordered = sortRectSides(sides).map(({ key, value }) => ({ + key, + value: `${value}px`, + })); + if (options?.clamp) { + for (const side of ordered) { + const maxValue = ['top', 'bottom'].includes(side.key) + ? window.innerHeight - height + : window.innerWidth - width; + side.value = `clamp(${margin}, ${side.value}, ${maxValue}px)`; + } + } + const [primary, secondary, ...rest] = ordered; + const { style } = state.el; + style.transition = ''; + style[primary.key] = primary.value; + style[secondary.key] = secondary.value; + style[rest[0].key] = 'auto'; + style[rest[1].key] = 'auto'; + }; + + useEvent('blur', handleRelease); + useEvent('mouseup', handleRelease); + useEvent('mousemove', handleMouseMove); + + const onMouseDown = (e: React.MouseEvent) => { + setState({ + el: e.currentTarget, + clickable: true, + moveable: true, + offsetX: e.nativeEvent.offsetX, + offsetY: e.nativeEvent.offsetY, + startX: e.clientX, + startY: e.clientY, + }); + }; + + const onClickCapture = (e: React.MouseEvent) => { + state?.clickable || e.stopPropagation(); + }; + + return { + isDragging: Boolean(state), + props: { + onMouseDown, + onClickCapture, + }, + }; +}; diff --git a/packages/devtools/plugin/package.json b/packages/devtools/plugin/package.json index a2bb763eee67..91e8009ba781 100644 --- a/packages/devtools/plugin/package.json +++ b/packages/devtools/plugin/package.json @@ -85,6 +85,6 @@ "registry": "https://registry.npmjs.org/", "access": "public", "provenance": true, - "types": "./dist/types/index.d.ts" + "types": "./dist/index.d.ts" } } diff --git a/packages/devtools/plugin/src/cli.ts b/packages/devtools/plugin/src/cli.ts index 8e56f3f3a815..1e3a052d1a1f 100644 --- a/packages/devtools/plugin/src/cli.ts +++ b/packages/devtools/plugin/src/cli.ts @@ -4,7 +4,11 @@ import { ProxyDetail } from '@modern-js/types'; import { getPort } from '@modern-js/utils'; import createServeMiddleware from 'serve-static'; import type { AppTools, CliPlugin } from '@modern-js/app-tools'; -import { SetupClientOptions, ClientDefinition } from '@modern-js/devtools-kit'; +import { + SetupClientOptions, + ClientDefinition, + ROUTE_BASENAME, +} from '@modern-js/devtools-kit'; import { withQuery } from 'ufo'; import { Options, resolveOptions } from './config'; import { setupClientConnection } from './rpc'; @@ -60,8 +64,8 @@ export const devtoolsPlugin = (options?: Options): CliPlugin => ({ opts.def && rpc.setDefinition(opts.def); const mountOpts = { - dataSource: `/_modern_js/devtools/rpc`, - endpoint: `/_modern_js/devtools`, + dataSource: `${ROUTE_BASENAME}/rpc`, + endpoint: ROUTE_BASENAME, __keep: true, } as SetupClientOptions; let runtimeEntry = require.resolve( @@ -81,10 +85,10 @@ export const devtoolsPlugin = (options?: Options): CliPlugin => ({ tools: { devServer: { proxy: { - '/_modern_js/devtools': { + [ROUTE_BASENAME]: { target: `http://localhost:${port}`, pathRewrite: { - '^/_modern_js/devtools': '', + [`^${ROUTE_BASENAME}`]: '', }, ws: true, }, diff --git a/packages/devtools/plugin/src/config.ts b/packages/devtools/plugin/src/config.ts index 3381069df488..594dc5c34726 100644 --- a/packages/devtools/plugin/src/config.ts +++ b/packages/devtools/plugin/src/config.ts @@ -4,13 +4,11 @@ import { PartialDeep } from 'type-fest'; import { CliPluginAPI } from './types'; export interface Options { - // prefix?: string; def?: PartialDeep; } export const getDefaultOptions = () => ({ - // prefix: '/_modern_js/devtools', def: new ClientDefinition(), } satisfies Options); diff --git a/tests/integration/devtools/modern.config.ts b/tests/integration/devtools/modern.config.ts index 9fad1c9e8c12..92a3353d3f19 100644 --- a/tests/integration/devtools/modern.config.ts +++ b/tests/integration/devtools/modern.config.ts @@ -5,6 +5,10 @@ export default defineConfig({ runtime: { router: true, }, + source: { + mainEntryName: 'main', + preEntry: ['./src/prelude.css'], + }, output: { // disable polyfill and ts checker to make test faster polyfill: 'off', diff --git a/tests/integration/devtools/src/admin/routes/index.css b/tests/integration/devtools/src/admin/routes/index.css deleted file mode 100644 index f17d4b7272d9..000000000000 --- a/tests/integration/devtools/src/admin/routes/index.css +++ /dev/null @@ -1,115 +0,0 @@ -html, -body { - padding: 0; - margin: 0; - font-family: PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; - background: #181a1b; -} - -p { - margin: 0; -} - -* { - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - box-sizing: border-box; -} - -.container-box { - min-height: 100vh; - max-width: 100%; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - padding-top: 10px; -} - -main { - flex: 1; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; -} - -.title { - display: flex; - margin: 4rem 0 4rem; - align-items: center; - font-size: 4rem; - font-weight: 600; -} - -.logo { - width: 6rem; - margin: 7px 0 0 1rem; -} - -.name { - color: #4ecaff; -} - -.description { - text-align: center; - line-height: 1.5; - font-size: 1.3rem; - color: #1b3a42; - margin-bottom: 5rem; -} - -.code { - background: #fafafa; - border-radius: 12px; - padding: 0.6rem 0.9rem; - font-size: 1.05rem; - font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, - Bitstream Vera Sans Mono, Courier New, monospace; -} - -.container-box .grid { - display: flex; - align-items: center; - justify-content: center; - width: 1100px; - margin-top: 3rem; -} - -.card { - padding: 1.5rem; - display: flex; - flex-direction: column; - justify-content: center; - height: 100px; - color: inherit; - text-decoration: none; - transition: 0.15s ease; - width: 45%; -} - -.card:hover, -.card:focus { - transform: scale(1.05); -} - -.card h2 { - display: flex; - align-items: center; - font-size: 1.5rem; - margin: 0; - padding: 0; -} - -.card p { - opacity: 0.6; - font-size: 0.9rem; - line-height: 1.5; - margin-top: 1rem; -} - -.arrow-right { - width: 1.3rem; - margin-left: 0.5rem; - margin-top: 3px; -} diff --git a/tests/integration/devtools/src/admin/routes/layout.tsx b/tests/integration/devtools/src/admin/routes/layout.tsx index 6433ea79e92b..5afcbb0cfb95 100644 --- a/tests/integration/devtools/src/admin/routes/layout.tsx +++ b/tests/integration/devtools/src/admin/routes/layout.tsx @@ -1,8 +1,13 @@ -import { Outlet } from '@modern-js/runtime/router'; +import { Outlet, Link } from '@modern-js/runtime/router'; export default function Layout() { return (
+
+ foo + bar + bar/baz +
); diff --git a/tests/integration/devtools/src/admin/routes/page.loader.ts b/tests/integration/devtools/src/admin/routes/page.loader.ts new file mode 100644 index 000000000000..789359c59b8e --- /dev/null +++ b/tests/integration/devtools/src/admin/routes/page.loader.ts @@ -0,0 +1,5 @@ +import { redirect } from '@modern-js/runtime/router'; + +export default () => { + return redirect('./foo'); +}; diff --git a/tests/integration/devtools/src/admin/routes/page.tsx b/tests/integration/devtools/src/admin/routes/page.tsx index 7e067a93bfbc..7646bbd17d04 100644 --- a/tests/integration/devtools/src/admin/routes/page.tsx +++ b/tests/integration/devtools/src/admin/routes/page.tsx @@ -1,92 +1 @@ -import { Helmet } from '@modern-js/runtime/head'; -import './index.css'; - -const Index = () => ( - -); - -export default Index; +export default null; diff --git a/tests/integration/devtools/src/devtools/App.tsx b/tests/integration/devtools/src/devtools/App.tsx index 309a39b182c6..763397b6b00d 100644 --- a/tests/integration/devtools/src/devtools/App.tsx +++ b/tests/integration/devtools/src/devtools/App.tsx @@ -1,5 +1,5 @@ function App() { - return
Hello Modern.js!
; + return
Hello, Modern.js DevTools!
; } export default App; diff --git a/tests/integration/devtools/src/main/routes/layout.tsx b/tests/integration/devtools/src/main/routes/layout.tsx new file mode 100644 index 000000000000..d15bcf44793c --- /dev/null +++ b/tests/integration/devtools/src/main/routes/layout.tsx @@ -0,0 +1,9 @@ +import { Outlet } from '@modern-js/runtime/router'; + +const Layout = (): JSX.Element => ( +
+ +
+); + +export default Layout; diff --git a/tests/integration/devtools/src/main/routes/page.tsx b/tests/integration/devtools/src/main/routes/page.tsx new file mode 100644 index 000000000000..adf2ebed6c9e --- /dev/null +++ b/tests/integration/devtools/src/main/routes/page.tsx @@ -0,0 +1,14 @@ +import { Link } from '@modern-js/runtime/router'; + +const Index = (): JSX.Element => ( +
+

Hello, Modern.js!

+
+ User + DevTools + Admin +
+
+); + +export default Index; diff --git a/tests/integration/devtools/src/main/routes/post/[id]/page.tsx b/tests/integration/devtools/src/main/routes/post/[id]/page.tsx new file mode 100644 index 000000000000..bd338eb60b9d --- /dev/null +++ b/tests/integration/devtools/src/main/routes/post/[id]/page.tsx @@ -0,0 +1,18 @@ +import React from 'react'; + +const Page: React.FC = () => ( +
+

Conventional Routing

+

+ With routes/ as the convention for entry points, Modern.js automatically + generates the corresponding routing structure based on the file system. +

+

+ Modern.js supports the popular conventional routing mode in the industry: + Nested Routing. When using nested routing, the page's routing corresponds + to the UI structure, and we will introduce this routing mode in detail. +

+
+); + +export default Page; diff --git a/tests/integration/devtools/src/main/routes/user/page.tsx b/tests/integration/devtools/src/main/routes/user/page.tsx new file mode 100644 index 000000000000..b07473bfc7e1 --- /dev/null +++ b/tests/integration/devtools/src/main/routes/user/page.tsx @@ -0,0 +1,10 @@ +import { Link } from '@modern-js/runtime/router'; + +const Page: React.FC = () => ( +
+

Hello Bob!

+ Edit Profile +
+); + +export default Page; diff --git a/tests/integration/devtools/src/main/routes/user/profile/page.tsx b/tests/integration/devtools/src/main/routes/user/profile/page.tsx new file mode 100644 index 000000000000..e4ae6ceeb021 --- /dev/null +++ b/tests/integration/devtools/src/main/routes/user/profile/page.tsx @@ -0,0 +1,12 @@ +const Page: React.FC = () => ( +
+
+ + + + +
+
+); + +export default Page; diff --git a/tests/integration/devtools/src/prelude.css b/tests/integration/devtools/src/prelude.css new file mode 100644 index 000000000000..6771161e4076 --- /dev/null +++ b/tests/integration/devtools/src/prelude.css @@ -0,0 +1,4 @@ +html, :root { + background-color: #121212; + color: #fafafa; +} \ No newline at end of file diff --git a/tests/integration/devtools/tests/index.test.ts b/tests/integration/devtools/tests/index.test.ts index b61912bf8b95..c4d60402e643 100644 --- a/tests/integration/devtools/tests/index.test.ts +++ b/tests/integration/devtools/tests/index.test.ts @@ -47,7 +47,7 @@ describe.skip('devtools dev', () => { const root = await page.$('#root'); const targetText = await page.evaluate(el => el?.textContent, root); - expect(targetText?.trim()).toEqual('Hello Modern.js!'); + expect(targetText?.trim()).toEqual('Hello, Modern.js!'); expect(errors.length).toEqual(0); await browser.close();