diff --git a/packages/client/rsbuild.config.ts b/packages/client/rsbuild.config.ts index 47d5d1a4..8c7ac8ae 100644 --- a/packages/client/rsbuild.config.ts +++ b/packages/client/rsbuild.config.ts @@ -7,6 +7,7 @@ import { pluginSass } from '@rsbuild/plugin-sass'; import serve from 'serve-static'; import path from 'path'; import fs from 'fs'; + import { ClientEntry, DistPath, diff --git a/packages/components/modern.config.ts b/packages/components/modern.config.ts index d3a0c82a..9a9f3d58 100644 --- a/packages/components/modern.config.ts +++ b/packages/components/modern.config.ts @@ -2,12 +2,15 @@ import { moduleTools, defineConfig } from '@modern-js/module-tools'; export default defineConfig({ plugins: [moduleTools()], - buildConfig: - { - buildType: 'bundleless', - format: 'esm', - target: "es2020", - outDir: './dist', - dts: {}, + buildConfig: { + buildType: 'bundleless', + format: 'esm', + target: 'es2020', + outDir: './dist', + asset: { + svgr: { + include: /\.svg$/, + }, }, + }, }); diff --git a/packages/components/src/common/svg/bundle-size.svg b/packages/components/src/common/svg/bundle-size.svg new file mode 100644 index 00000000..51cf09a0 --- /dev/null +++ b/packages/components/src/common/svg/bundle-size.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/components/src/common/svg/error.svg b/packages/components/src/common/svg/error.svg new file mode 100644 index 00000000..b46bc3eb --- /dev/null +++ b/packages/components/src/common/svg/error.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/components/src/common/svg/file-css.svg b/packages/components/src/common/svg/file-css.svg new file mode 100644 index 00000000..c21e8446 --- /dev/null +++ b/packages/components/src/common/svg/file-css.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/components/src/common/svg/file-html.svg b/packages/components/src/common/svg/file-html.svg new file mode 100644 index 00000000..076a6899 --- /dev/null +++ b/packages/components/src/common/svg/file-html.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/packages/components/src/common/svg/file-image.svg b/packages/components/src/common/svg/file-image.svg new file mode 100644 index 00000000..eadd6847 --- /dev/null +++ b/packages/components/src/common/svg/file-image.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/components/src/common/svg/file-js.svg b/packages/components/src/common/svg/file-js.svg new file mode 100644 index 00000000..f4b44673 --- /dev/null +++ b/packages/components/src/common/svg/file-js.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/components/src/common/svg/file-unknown.svg b/packages/components/src/common/svg/file-unknown.svg new file mode 100644 index 00000000..347c59f1 --- /dev/null +++ b/packages/components/src/common/svg/file-unknown.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/components/src/common/svg/file.svg b/packages/components/src/common/svg/file.svg new file mode 100644 index 00000000..6ca1d301 --- /dev/null +++ b/packages/components/src/common/svg/file.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/components/src/common/svg/output.svg b/packages/components/src/common/svg/output.svg new file mode 100644 index 00000000..420c875d --- /dev/null +++ b/packages/components/src/common/svg/output.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/components/src/common/svg/source-size.svg b/packages/components/src/common/svg/source-size.svg new file mode 100644 index 00000000..794ac639 --- /dev/null +++ b/packages/components/src/common/svg/source-size.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/components/src/common/svg/source.svg b/packages/components/src/common/svg/source.svg new file mode 100644 index 00000000..ec7b2a81 --- /dev/null +++ b/packages/components/src/common/svg/source.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/components/src/common/svg/total-size.svg b/packages/components/src/common/svg/total-size.svg new file mode 100644 index 00000000..d5e2431c --- /dev/null +++ b/packages/components/src/common/svg/total-size.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/packages/components/src/common/svg/version.svg b/packages/components/src/common/svg/version.svg new file mode 100644 index 00000000..4263d8f9 --- /dev/null +++ b/packages/components/src/common/svg/version.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/packages/components/src/components/Alert/ecma-version-check.module.scss b/packages/components/src/components/Alert/ecma-version-check.module.scss new file mode 100644 index 00000000..ef500581 --- /dev/null +++ b/packages/components/src/components/Alert/ecma-version-check.module.scss @@ -0,0 +1,27 @@ +.container { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 20px; + font-family: var(--font-family-code); + + .box { + max-width: 300px; + display: flex; + } + + .title { + font-size: 12px; + font-weight: 400; + line-height: 16px; + margin-bottom: 5px; + color: #00000073; + } + + .content { + font-size: 14px; + font-weight: 400; + line-height: 16px; + margin-left: 5px; + } +} diff --git a/packages/components/src/components/Alert/ecma-version-check.tsx b/packages/components/src/components/Alert/ecma-version-check.tsx new file mode 100644 index 00000000..6a2f97c8 --- /dev/null +++ b/packages/components/src/components/Alert/ecma-version-check.tsx @@ -0,0 +1,63 @@ +import { Button, Typography } from 'antd'; +import Icon from '@ant-design/icons'; + +import SourceSvg from '../../common/svg/source.svg'; +import OutputSvg from '../../common/svg/output.svg'; +import ErrorSvg from '../../common/svg/error.svg'; +import { useRuleIndexNavigate } from '../../utils'; + +import { LinkAlertProps } from './types'; + +import styles from './ecma-version-check.module.scss'; + +const { Text } = Typography; + +export const LinkRuleAlert: React.FC = ({ data }) => { + return data.map((d) => { + const { code, link } = d; + const navigate = useRuleIndexNavigate(code, link); + return ( +
+
+
Source
+
+ + + source placeholder + +
+
+
+
Output
+
+ + + output placeholder + +
+
+
+
Error
+
+ + + {d.description} + +
+
+ +
+ ); + }); +}; diff --git a/packages/components/src/components/Alert/index.tsx b/packages/components/src/components/Alert/index.tsx deleted file mode 100644 index f8f3dcc6..00000000 --- a/packages/components/src/components/Alert/index.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; -import { LinkRuleAlert } from './link'; -import { FileRelationAlert } from './file-relation'; -import { PackageRelationAlert } from './package-relation'; -import { CodeChangeAlert } from './change'; -import { CodeViewAlert } from './view'; - -import { AlertProps } from './types'; - -import './index.sass'; - -export const Alert: React.FC = (props) => { - const { data, cwd = '' } = props; - - if (data.type === 'file-relation') { - return ; - } - - if (data.type === 'code-view') { - return ; - } - - if (data.type === 'code-change') { - return ; - } - - if (data.type === 'package-relation') { - const { getPackageRelationContentComponent } = props; - return ( - - ); - } - - return ; -}; diff --git a/packages/components/src/components/Alert/link.tsx b/packages/components/src/components/Alert/link.tsx deleted file mode 100644 index 4b2273a7..00000000 --- a/packages/components/src/components/Alert/link.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react'; -import { Alert, Button, Space, Typography } from 'antd'; -import { InfoCircleOutlined } from '@ant-design/icons'; -import { useRuleIndexNavigate } from '../../utils'; -import { LinkAlertProps } from './types'; - -export const LinkRuleAlert: React.FC = ({ data }) => { - const { title, description = '', level, code } = data; - const navigate = useRuleIndexNavigate(code, data.link); - - return ( - - - {code} - - {title} - - } - description={
} - type={level === 'warn' ? 'info' : level} - action={ - - } - /> - ); -}; diff --git a/packages/components/src/components/Alert/package-relation.module.scss b/packages/components/src/components/Alert/package-relation.module.scss new file mode 100644 index 00000000..50bef7ba --- /dev/null +++ b/packages/components/src/components/Alert/package-relation.module.scss @@ -0,0 +1,23 @@ +.dot { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.filePath { + display: flex; + justify-content: space-between; + align-items: center; + border: 1px solid #eaedf1; + padding: 5px 10px; + border-radius: 10px; +} + +.arrow { + position: relative; + top: 8px; + left: 50%; + transform: translateX(-50%) rotate(90deg); + color: #2994ff; +} diff --git a/packages/components/src/components/Alert/package-relation.tsx b/packages/components/src/components/Alert/package-relation.tsx index fe54d755..13a55b5e 100644 --- a/packages/components/src/components/Alert/package-relation.tsx +++ b/packages/components/src/components/Alert/package-relation.tsx @@ -1,5 +1,4 @@ /* eslint-disable financial/no-float-calculation */ -import React, { useState } from 'react'; import { Space, Alert, @@ -10,101 +9,76 @@ import { Row, Col, Timeline, - Card, Tag, Empty, - Popover, Grid, } from 'antd'; import { sumBy } from 'lodash-es'; -import { Rule, SDK } from '@rsdoctor/types'; -import { ExpandAltOutlined, InfoCircleOutlined } from '@ant-design/icons'; -import { useRuleIndexNavigate, formatSize, useI18n } from '../../utils'; +import { + ExpandAltOutlined, + InfoCircleOutlined, + DoubleRightOutlined, +} from '@ant-design/icons'; + +import { useRuleIndexNavigate, formatSize } from '../../utils'; import { TextDrawer } from '../TextDrawer'; import { Title } from '../Title'; import { Size, Color } from '../../constants'; import { Badge as Bdg } from '../Badge'; -import { PackageRelationAlertProps } from './types'; import { withServerAPI } from '../Manifest'; +import { Rule, SDK } from '@rsdoctor/types'; + +import { PackageRelationAlertProps } from './types'; + +import styles from './package-relation.module.scss'; + const TextDrawerWidth = '60%'; export const PackageRelationReasons: React.FC<{ data: SDK.ServerAPI.InferResponseType; cwd: string; }> = ({ data }) => { - const [index, setIndex] = useState(0); - const { t } = useI18n(); - return ( - - - {t('DuplicatePakCodeExplain')} - - } - > - Explain - - } - bodyStyle={{ overflow: 'scroll' }} - > - {data.length ? ( - -
- - Click the file path below to show the reason in code viewer. - -
- - {data.map((e, i) => { - const { dependency, module, relativePath } = e!; - const { statements } = dependency; - const { start } = statements?.[0]?.position - ? module.isPreferSource - ? statements[0].position.source! - : statements[0].position.transformed - : { start: { line: 0, column: 0 } }; - const text = `${relativePath}:${start.line}:${ - start.column || 1 - }`; + + {data.length ? ( + <> + + {data.map((e, i) => { + const { dependency, module, relativePath } = e!; + const { statements } = dependency; + const { start } = statements?.[0]?.position + ? module.isPreferSource + ? statements[0].position.source! + : statements[0].position.transformed + : { start: { line: 0, column: 0 } }; + const text = `${relativePath}:${start.line}:${ + start.column || 1 + }`; - return ( - - { - e.preventDefault(); - e.stopPropagation(); - setIndex(i); - }} - strong={i === index} - style={{ - color: i === index ? Color.Blue : 'inherit', - display: 'block', - }} - > + return ( + + +
{text} - - - ); - })} - - - ) : ( - - )} - + +
+ {i !== data.length - 1 ? ( + + ) : null} +
+
+ ); + })} +
+ + ) : ( + + )}
); @@ -237,7 +211,7 @@ export const PackageRelationAlert: React.FC = ({ } type={level === 'warn' ? 'info' : level} action={ - + <> {packages && packages.length > 0 ? ( = ({ size="small" icon={} /> - + } /> ); diff --git a/packages/components/src/components/Alert/types.ts b/packages/components/src/components/Alert/types.ts index b2ab7467..e0456769 100644 --- a/packages/components/src/components/Alert/types.ts +++ b/packages/components/src/components/Alert/types.ts @@ -30,5 +30,5 @@ export interface CodeViewAlertProps { } export interface LinkAlertProps { - data: Rule.LinkRuleStoreData; + data: Rule.RuleStoreDataItem[]; } diff --git a/packages/components/src/components/Alerts/bundle-alert.module.scss b/packages/components/src/components/Alerts/bundle-alert.module.scss new file mode 100644 index 00000000..1f464ba6 --- /dev/null +++ b/packages/components/src/components/Alerts/bundle-alert.module.scss @@ -0,0 +1,16 @@ +.card { + :global { + .ant-card-body { + padding: 0; + } + + .ant-collapse { + border-radius: 0; + border: 0; + } + + .ant-collapse-content { + border-radius: 0 !important; + } + } +} diff --git a/packages/components/src/components/Alerts/bundle-alert.tsx b/packages/components/src/components/Alerts/bundle-alert.tsx new file mode 100644 index 00000000..3839df4e --- /dev/null +++ b/packages/components/src/components/Alerts/bundle-alert.tsx @@ -0,0 +1,113 @@ +import { Typography, Tabs, Empty } from 'antd'; + +import { Card } from '../Card'; +import { LinkRuleAlert } from '../Alert/ecma-version-check'; +import { Overview } from '../Overall/overview'; +import { AlertCollapse } from './collapse'; +import { CommonList } from './list'; +import { ViewMode } from '../../constants'; + +import { AlertProps } from '../Alert/types'; + +import type { Rule } from '@rsdoctor/types'; + +import styles from './bundle-alert.module.scss'; + +interface BundleAlertProps { + title: string; + cwd: string; + dataSource: Rule.RuleStoreDataItem[]; + extraData: Omit; + viewMode: ViewMode; + setViewMode(mode: ViewMode): void; + extraCom?: JSX.Element | undefined; +} + +export const BundleAlert: React.FC = ({ + title, + dataSource, + extraData, +}) => { + if (!dataSource.length) return null; + + const tabData: Array<{ + key: string; + label: string; + data: Array; + }> = [ + { + key: 'E1001', + label: 'Duplicate Packages', + data: [], + }, + { + key: 'E1002', + label: 'Default Import Check', + data: [], + }, + { + key: 'E1003', + label: 'Loader Performance Optimization', + data: [], + }, + { + key: 'E1004', + label: 'ECMA Version Check', + data: [], + }, + ]; + + dataSource.forEach((data) => { + const target = tabData.find((td) => td.key === data.code)?.data; + target?.push(data); + }); + + const tabItems = tabData.map((td) => { + const LabelComponent = () => ( + {td.key}} + /> + ); + + let children; + switch (td.key) { + case 'E1001': + children = ; + break; + case 'E1002': + children = ; + break; + case 'E1003': + children = ; + break; + case 'E1004': + children = ; + break; + default: + children = null; + break; + } + + if (!td.data.length) { + children = ; + } + + return { + key: td.key, + label: , + children: ( + + {children} + + ), + }; + }); + + return ( + + + + ); +}; diff --git a/packages/components/src/components/Alerts/bundle.tsx b/packages/components/src/components/Alerts/bundle.tsx index 8c51c82a..4547ebe4 100644 --- a/packages/components/src/components/Alerts/bundle.tsx +++ b/packages/components/src/components/Alerts/bundle.tsx @@ -1,7 +1,12 @@ import React, { useEffect } from 'react'; import { Rule, SDK } from '@rsdoctor/types'; -import { hasViewModeFromStorage, useBundleAlertsByErrors, useViewMode } from '../../utils'; -import { CommonAlertsContainer } from './common'; +import { + hasViewModeFromStorage, + useBundleAlertsByErrors, + useViewMode, + useI18n, +} from '../../utils'; +import { BundleAlert } from './bundle-alert'; import { withServerAPI } from '../Manifest'; import { ViewMode } from '../../constants'; import { PackageRelationReasonsWithServer } from '../Alert/package-relation'; @@ -12,10 +17,14 @@ interface BundleAlertsProps { project: SDK.ServerAPI.InferResponseType; } -export const BundleAlertsBase: React.FC = ({ filter, project }) => { +export const BundleAlertsBase: React.FC = ({ + filter, + project, +}) => { const { errors, root: cwd } = project; const bundleAlerts = useBundleAlertsByErrors(errors); const { setBundleAlertsViewMode, viewMode, setViewMode } = useViewMode(); + const { t } = useI18n(); const dataSource = filter ? bundleAlerts.filter(filter) : bundleAlerts; @@ -23,7 +32,8 @@ export const BundleAlertsBase: React.FC = ({ filter, project if (!hasViewModeFromStorage()) { setViewMode( { - bundleAlerts: bundleAlerts.length >= 5 ? ViewMode.Group : ViewMode.List, + bundleAlerts: + bundleAlerts.length >= 5 ? ViewMode.Group : ViewMode.List, }, false, ); @@ -37,17 +47,20 @@ export const BundleAlertsBase: React.FC = ({ filter, project Alert: { colorInfoBg: '#e6f4ff57', colorInfoBorder: 'none', - } - } + }, + }, }} > - ( - + ), }} viewMode={viewMode.bundleAlerts} diff --git a/packages/components/src/components/Alerts/collapse.module.scss b/packages/components/src/components/Alerts/collapse.module.scss new file mode 100644 index 00000000..6765c453 --- /dev/null +++ b/packages/components/src/components/Alerts/collapse.module.scss @@ -0,0 +1,78 @@ +.label { + display: flex; + justify-content: space-between; + + :global { + .ant-collapse-header { + background-color: white; + } + } + + .labelContent { + display: flex; + + & > * { + margin-right: 10px; + } + } +} + +.collapseContainer { + .collapseChild { + display: flex; + font-family: var(--font-family-code); + font-size: 14px; + font-weight: 400; + line-height: 20px; + text-align: left; + + & > * { + margin-right: 10px; + } + + .attribute { + font-family: var(--font-family-code); + font-size: 12px; + font-weight: 400; + line-height: 16px; + text-align: left; + color: #00000073; + margin: 5px 0; + } + + .copy { + position: relative; + left: 100%; + top: -50%; + } + } +} + +.drawerLabelTitle { + min-width: 120px; + display: flex; + justify-content: space-evenly; + align-items: center; + + .drawerLabelSize { + font-family: var(--font-family-code); + font-size: 12px; + font-weight: 400; + } +} + +.extraContainer { + display: flex; + align-items: center; +} + +.iconContainer { + display: flex; + margin-right: 20px; + justify-content: center; + align-items: center; + + .data { + margin-left: 5px; + } +} diff --git a/packages/components/src/components/Alerts/collapse.tsx b/packages/components/src/components/Alerts/collapse.tsx new file mode 100644 index 00000000..5da5c441 --- /dev/null +++ b/packages/components/src/components/Alerts/collapse.tsx @@ -0,0 +1,198 @@ +import { ReactNode } from 'react'; +import { Collapse, Typography, Divider, Space, Tabs, Tag, Tooltip } from 'antd'; +import Icon from '@ant-design/icons'; +import { sumBy } from 'lodash-es'; + +import Overview from '../Overall/overview'; +import { TextDrawer } from '../TextDrawer'; +import { Title } from '../Title'; +import { Size } from '../../constants'; +import { formatSize } from '../../utils'; +import BundleSizeSvg from '../../common/svg/bundle-size.svg'; +import SourceSizeSvg from '../../common/svg/source-size.svg'; +import TotalSizeSvg from '../../common/svg/total-size.svg'; +import VersionSvg from '../../common/svg/version.svg'; + +import type { Rule } from '@rsdoctor/types'; + +import type { AlertProps } from '../Alert/types'; + +import styles from './collapse.module.scss'; + +const { Paragraph } = Typography; + +const LabelComponent = (props: { + title: string; + description: string; + extra: ReactNode; +}) => { + const { title, description, extra } = props; + return ( +
+
+
{title}
+
{description}
+
+
{extra}
+
+ ); +}; + +export const AlertCollapse = (props: { + data: Array; + extraData: Omit; +}) => { + const { data, extraData } = props; + + const items = data.map((d) => { + const { packages } = d as Rule.PackageRelationDiffRuleStoreData; + const totalSize = sumBy(packages, (e) => e.targetSize.sourceSize); + const totalSizeStr = formatSize(totalSize); + const { name } = packages.find((e) => !!e.target.name)!.target; + const versions = packages.map((item) => item.target.version); + + const ChildComponent = () => { + return packages.map((pkg, idx) => { + const version = pkg.target.version; + const root = pkg.target.root; + const sizeStr = formatSize(pkg.targetSize.sourceSize); + const parsedSizeStr = pkg.targetSize.parsedSize + ? formatSize(pkg.targetSize.parsedSize) + : null; + + return ( +
+ +
+
Version
+
+ + v.{version} +
+
+
+
Source size
+
+ + {sizeStr} +
+
+
+
Bundle size
+
+ + + + {parsedSizeStr || 'CONCATENATED'} + + +
+
+
+ } + icon={ + + } + /> + {idx !== packages.length - 1 ? ( + + ) : null} +
+ ); + }); + }; + + return { + key: d.code, + label: ( + +
+ + {totalSizeStr} +
+ {packages && packages.length > 0 ? ( + + + + + {name} + </Tag> + } + upperFirst={false} + /> + <Typography.Text strong> + {versions.length} + </Typography.Text> + <Typography.Text> versions was found</Typography.Text> + </Space> + <Tabs + size="middle" + items={ + packages.map((pkg) => { + const { target, targetSize } = pkg; + return { + label: ( + <Space className={styles.drawerLabelTitle}> + <div>v.{target.version}</div> + <Tag className={styles.drawerLabelSize}> + {formatSize(targetSize.sourceSize)} + </Tag> + </Space> + ), + key: `${target.root}${target.name}${target.version}`, + children: + extraData.getPackageRelationContentComponent({ + data: d as Rule.PackageRelationDiffRuleStoreData, + package: pkg, + }), + }; + })! + } + /> + </Space> + </TextDrawer> + ) : null} + </div> + } + /> + ), + children: <ChildComponent />, + }; + }); + + return ( + <Collapse + style={{ width: '100%' }} + defaultActiveKey={['E1001']} + items={items} + /> + ); +}; diff --git a/packages/components/src/components/Alerts/common.tsx b/packages/components/src/components/Alerts/common.tsx deleted file mode 100644 index 950b701d..00000000 --- a/packages/components/src/components/Alerts/common.tsx +++ /dev/null @@ -1,168 +0,0 @@ -import { Collapse, Space, Radio, Typography, Tooltip } from 'antd'; -import React, { useMemo } from 'react'; -import { AppstoreOutlined, UnorderedListOutlined } from '@ant-design/icons'; -import { Rule } from '@rsdoctor/types'; -import { groupBy, sumBy, values } from 'lodash-es'; -import { Size, ViewMode } from '../../constants'; -import { Alert } from '../Alert'; -import { Card } from '../Card'; -import { Badge as Bdg } from '../Badge'; -import { AlertProps } from '../Alert/types'; -import { RuleErrorCodes } from '@rsdoctor/types/dist/rule'; - -interface CommonAlertsContainerProps { - title: string; - cwd: string; - dataSource: Rule.RuleStoreDataItem[]; - extraData: Omit<AlertProps, 'data'>; - viewMode: ViewMode; - setViewMode(mode: ViewMode): void; - extraCom?: JSX.Element | undefined; -} - -interface CommonAlertsContentProps - extends Pick<CommonAlertsContainerProps, 'dataSource' | 'extraData'> {} - -const LevelMap = { - warn: 1, - error: 2, -}; - -const CommonAlertsList: React.FC<CommonAlertsContentProps> = ({ - dataSource, - extraData, -}) => { - const _dataSource = useMemo( - () => - dataSource.slice().sort((a, b) => { - return LevelMap[b.level] - LevelMap[a.level]; - }), - [dataSource], - ); - - return ( - <Space - direction="vertical" - style={{ wordBreak: 'break-all', width: '100%' }} - > - {_dataSource.map((err, i) => { - return <Alert data={err} key={i} {...extraData} />; - })} - </Space> - ); -}; - -const CommonAlertsGroup: React.FC<CommonAlertsContentProps> = ({ - dataSource, - extraData, -}) => { - const _dataSource = useMemo(() => { - const sortedDataSource = dataSource.toSorted((a, b) => { - let sizeA = 0; - let sizeB = 0; - - if (a.type === 'package-relation') { - sizeA = sumBy(a.packages, (e) => e.targetSize.sourceSize); - } - - if (b.type === 'package-relation') { - sizeB = sumBy(b.packages, (e) => e.targetSize.sourceSize); - } - - return sizeB - sizeA; - }); - const groups = groupBy(sortedDataSource, (e) => e.code); - return values(groups); - }, [dataSource]); - - return ( - <Space - direction="vertical" - style={{ wordBreak: 'break-all', width: '100%' }} - > - <Collapse> - {_dataSource.map((el) => { - const [first] = el; - return ( - <Collapse.Panel - header={ - <Space> - <Typography.Text code strong style={{ cursor: 'pointer' }}> - {first.code} - </Typography.Text> - <Typography.Text strong> - {Rule.RuleErrorMap[first.code as keyof RuleErrorCodes] - ?.title || first.title} - </Typography.Text> - <Bdg label={'count'} value={el.length} type="error" /> - </Space> - } - key={first.code} - > - <Space direction="vertical" size={16} style={{ width: '100%' }}> - {el.map((err, i) => { - return <Alert data={err} key={i} {...extraData} />; - })} - </Space> - </Collapse.Panel> - ); - })} - </Collapse> - </Space> - ); -}; - -export const CommonAlertsContainer: React.FC<CommonAlertsContainerProps> = ({ - title, - dataSource, - extraData, - viewMode, - setViewMode, - extraCom, -}) => { - if (!dataSource.length) return null; - - return ( - <Card - title={title} - style={{ marginTop: Size.BasePadding }} - collapsable - extra={ - extraCom || ( - <Radio.Group - options={[ - { - label: ( - <Tooltip title="display by list"> - <UnorderedListOutlined /> - </Tooltip> - ), - value: ViewMode.List, - }, - { - label: ( - <Tooltip title="display by group"> - <AppstoreOutlined /> - </Tooltip> - ), - value: ViewMode.Group, - }, - ]} - value={viewMode} - optionType="button" - size="small" - onChange={(v) => { - setViewMode(v.target.value as ViewMode); - }} - /> - ) - } - > - {viewMode === ViewMode.List ? ( - <CommonAlertsList dataSource={dataSource} extraData={extraData} /> - ) : ( - <CommonAlertsGroup dataSource={dataSource} extraData={extraData} /> - )} - </Card> - ); -}; diff --git a/packages/components/src/components/Alerts/compile.tsx b/packages/components/src/components/Alerts/compile.tsx index 9ebae6d4..f6bab790 100644 --- a/packages/components/src/components/Alerts/compile.tsx +++ b/packages/components/src/components/Alerts/compile.tsx @@ -1,7 +1,11 @@ import React, { useEffect } from 'react'; import { Rule, SDK } from '@rsdoctor/types'; -import { hasViewModeFromStorage, useCompileAlertsByErrors, useViewMode } from '../../utils'; -import { CommonAlertsContainer } from './common'; +import { + hasViewModeFromStorage, + useCompileAlertsByErrors, + useViewMode, +} from '../../utils'; +import { BundleAlert } from './bundle-alert'; import { withServerAPI } from '../Manifest'; import { ViewMode } from '../../constants'; import { PackageRelationReasonsWithServer } from '../Alert/package-relation'; @@ -11,7 +15,10 @@ interface CompileAlertsProps { project: SDK.ServerAPI.InferResponseType<SDK.ServerAPI.API.GetProjectInfo>; } -const CompileAlertsBase: React.FC<CompileAlertsProps> = ({ filter, project }) => { +const CompileAlertsBase: React.FC<CompileAlertsProps> = ({ + filter, + project, +}) => { const { root: cwd, errors } = project; const compileAlerts = useCompileAlertsByErrors(errors); const { setCompileAlertsViewMode, viewMode, setViewMode } = useViewMode(); @@ -22,7 +29,8 @@ const CompileAlertsBase: React.FC<CompileAlertsProps> = ({ filter, project }) => if (!hasViewModeFromStorage()) { setViewMode( { - compileAlerts: compileAlerts.length >= 5 ? ViewMode.Group : ViewMode.List, + compileAlerts: + compileAlerts.length >= 5 ? ViewMode.Group : ViewMode.List, }, false, ); @@ -30,13 +38,16 @@ const CompileAlertsBase: React.FC<CompileAlertsProps> = ({ filter, project }) => }, []); return ( - <CommonAlertsContainer + <BundleAlert title="Compile Alerts" dataSource={dataSource} extraData={{ cwd, getPackageRelationContentComponent: (res) => ( - <PackageRelationReasonsWithServer body={{ id: res.data.id, target: res.package.target }} cwd={cwd} /> + <PackageRelationReasonsWithServer + body={{ id: res.data.id, target: res.package.target }} + cwd={cwd} + /> ), }} viewMode={viewMode.compileAlerts} diff --git a/packages/components/src/components/Alerts/list.module.scss b/packages/components/src/components/Alerts/list.module.scss new file mode 100644 index 00000000..a961a97c --- /dev/null +++ b/packages/components/src/components/Alerts/list.module.scss @@ -0,0 +1,6 @@ +.description { + font-family: var(--font-family-code); + font-size: 14px; + font-weight: 400; + line-height: 16px; +} diff --git a/packages/components/src/components/Alerts/list.tsx b/packages/components/src/components/Alerts/list.tsx new file mode 100644 index 00000000..20288352 --- /dev/null +++ b/packages/components/src/components/Alerts/list.tsx @@ -0,0 +1,30 @@ +import { Button } from 'antd'; + +import { Overview } from '../Overall/overview'; +import { useRuleIndexNavigate } from '../../utils'; + +import type { Rule } from '@rsdoctor/types'; + +import styles from './list.module.scss'; + +export const CommonList = (props: { data: Array<Rule.RuleStoreDataItem> }) => { + const { data } = props; + return data.map((d) => { + const { code, link, description } = d; + const navigate = useRuleIndexNavigate(code, link); + + return ( + <Overview + style={{ + background: '#fff', + }} + description={<span className={styles.description}>{description}</span>} + icon={ + <Button onClick={() => navigate} type="link"> + more + </Button> + } + /> + ); + }); +}; diff --git a/packages/components/src/components/Alerts/overlay.tsx b/packages/components/src/components/Alerts/overlay.tsx index 1996ef9c..585e2775 100644 --- a/packages/components/src/components/Alerts/overlay.tsx +++ b/packages/components/src/components/Alerts/overlay.tsx @@ -1,15 +1,32 @@ import React, { useState } from 'react'; import { SDK } from '@rsdoctor/types'; -import { Alert, Button, Col, Collapse, Row, Tag, Typography } from 'antd'; -import { BugOutlined, CloseCircleOutlined, DownOutlined, RightOutlined } from '@ant-design/icons'; +import { + Alert, + Button, + Col, + Collapse, + Row, + Tag, + Typography, + Badge, +} from 'antd'; +import { + BugOutlined, + CloseCircleOutlined, + DownOutlined, + RightOutlined, +} from '@ant-design/icons'; import Dialog from 'rc-dialog'; import Ansi from 'ansi-to-react'; + import { withServerAPI } from '../Manifest'; import { Size, Color } from '../../constants'; import 'rc-dialog/assets/index.css'; -function getOverlayAlertsMessage(alerts: SDK.ServerAPI.InferResponseType<SDK.ServerAPI.API.GetOverlayAlerts>) { +function getOverlayAlertsMessage( + alerts: SDK.ServerAPI.InferResponseType<SDK.ServerAPI.API.GetOverlayAlerts>, +) { let warns = 0; let errors = 0; @@ -21,7 +38,8 @@ function getOverlayAlertsMessage(alerts: SDK.ServerAPI.InferResponseType<SDK.Ser } }); - const suffixText = warns !== 0 && errors !== 0 ? 'problems' : warns === 0 ? 'errors' : 'warns'; + const suffixText = + warns !== 0 && errors !== 0 ? 'problems' : warns === 0 ? 'errors' : 'warns'; const fontSize = 16; @@ -29,11 +47,16 @@ function getOverlayAlertsMessage(alerts: SDK.ServerAPI.InferResponseType<SDK.Ser title: `${alerts.length} compiled ${suffixText}`, detail: ( <Typography.Text strong style={{ color: '#fff', marginBottom: 0 }}> - <Typography.Text style={{ color: 'inherit', fontSize }}>Compiled with </Typography.Text> + <Typography.Text style={{ color: 'inherit', fontSize }}> + Compiled with{' '} + </Typography.Text> <Typography.Text strong style={{ color: Color.Red, fontSize }}> {errors} errors </Typography.Text> - <Typography.Text style={{ color: 'inherit', fontSize }}> and </Typography.Text> + <Typography.Text style={{ color: 'inherit', fontSize }}> + {' '} + and{' '} + </Typography.Text> <Typography.Text strong style={{ color: Color.Yellow, fontSize }}> {warns} warnings </Typography.Text> @@ -82,7 +105,15 @@ export const OverlayAlertsModal: React.FC<{ <Row gutter={[0, Size.BasePadding]}> {alerts.map((e) => { return ( - <Col span={24} key={e.id} style={{ whiteSpace: 'pre-wrap', lineHeight: `16px`, fontSize: 14 }}> + <Col + span={24} + key={e.id} + style={{ + whiteSpace: 'pre-wrap', + lineHeight: `16px`, + fontSize: 14, + }} + > <Collapse ghost style={{ background: '#000' }} @@ -145,7 +176,11 @@ export const OverlayAlertsTips: React.FC<{ </Button> } ></Alert> - <OverlayAlertsModal alerts={alerts} open={open} onClose={() => setOpen(false)} /> + <OverlayAlertsModal + alerts={alerts} + open={open} + onClose={() => setOpen(false)} + /> </React.Fragment> ); }; @@ -155,17 +190,19 @@ export const OverlayAlertsButton: React.FC<{ }> = ({ alerts = [] }) => { if (!alerts.length) return null; - const { errors } = getOverlayAlertsMessage(alerts); - const defaultOpen = errors > 0; // open modal when has compiled error. - const [open, setOpen] = useState(defaultOpen); + const [open, setOpen] = useState(false); return ( - <React.Fragment> - <Button icon={<BugOutlined />} danger size="small" onClick={() => setOpen(!open)}> - {getOverlayAlertsMessage(alerts).title} - </Button> - <OverlayAlertsModal alerts={alerts} open={open} onClose={() => setOpen(false)} /> - </React.Fragment> + <div style={{ position: 'relative', top: '4px', marginRight: '3px' }}> + <Badge count={alerts.length} size="small"> + <BugOutlined style={{ fontSize: 15 }} onClick={() => setOpen(!open)} /> + </Badge> + <OverlayAlertsModal + alerts={alerts} + open={open} + onClose={() => setOpen(false)} + /> + </div> ); }; diff --git a/packages/components/src/components/Card/index.tsx b/packages/components/src/components/Card/index.tsx index a92328b7..149456bf 100644 --- a/packages/components/src/components/Card/index.tsx +++ b/packages/components/src/components/Card/index.tsx @@ -1,9 +1,9 @@ +import React, { useState, CSSProperties } from 'react'; import { ColumnHeightOutlined, VerticalAlignMiddleOutlined, } from '@ant-design/icons'; import { Card as C, CardProps as CProps, Space, Button, Divider } from 'antd'; -import React, { useState, CSSProperties } from 'react'; export * from './diff'; export interface CardProps extends CProps { collapsable?: boolean; diff --git a/packages/components/src/components/Charts/bootstrap.tsx b/packages/components/src/components/Charts/bootstrap.tsx index 807c44ca..00e74156 100644 --- a/packages/components/src/components/Charts/bootstrap.tsx +++ b/packages/components/src/components/Charts/bootstrap.tsx @@ -6,13 +6,21 @@ import { TextDrawer } from '../TextDrawer'; import './loader.scss'; import './tooltips.scss'; -import { CommonChartProps, CommonExecutionEmptyTips, CommonExecutionsChart } from './common'; +import { + CommonChartProps, + CommonExecutionEmptyTips, + CommonExecutionsChart, +} from './common'; import { ServerAPIProvider } from '../Manifest'; import { Summary } from '@rsdoctor/utils/common'; -export const BootstrapChartContainer: React.FC<CommonChartProps> = ({ summary }) => { +export const BootstrapChartContainer: React.FC<CommonChartProps> = ({ + summary, +}) => { const { costs = [] } = summary || {}; - const target = costs.find((e) => e.name === Summary.SummaryCostsDataName.Bootstrap); + const target = costs.find( + (e) => e.name === Summary.SummaryCostsDataName.Bootstrap, + ); const hooks: string[] = [ 'environment', @@ -33,6 +41,7 @@ export const BootstrapChartContainer: React.FC<CommonChartProps> = ({ summary }) return ( <TextDrawer + containerProps={{ style: { display: 'inline' } }} drawerProps={{ title: 'Chart of the "Bootstrap -> BeforeCompile" stage' }} text={ <Space> @@ -42,7 +51,13 @@ export const BootstrapChartContainer: React.FC<CommonChartProps> = ({ summary }) } > <ServerAPIProvider api={SDK.ServerAPI.API.GetPluginData} body={{ hooks }}> - {(res) => (res && res.length ? <CommonExecutionsChart plugins={res} /> : <CommonExecutionEmptyTips />)} + {(res) => + res && res.length ? ( + <CommonExecutionsChart plugins={res} /> + ) : ( + <CommonExecutionEmptyTips /> + ) + } </ServerAPIProvider> </TextDrawer> ); diff --git a/packages/components/src/components/Charts/done.tsx b/packages/components/src/components/Charts/done.tsx index a8c703e0..32f9aaa4 100644 --- a/packages/components/src/components/Charts/done.tsx +++ b/packages/components/src/components/Charts/done.tsx @@ -5,17 +5,24 @@ import { SDK } from '@rsdoctor/types'; import { Summary } from '@rsdoctor/utils/common'; import { TextDrawer } from '../TextDrawer'; import { Card } from '../Card'; -import { CommonChartProps, CommonExecutionsChart, CommonExecutionEmptyTips } from './common'; +import { + CommonChartProps, + CommonExecutionsChart, + CommonExecutionEmptyTips, +} from './common'; import { WebpackPluginsDataTable } from '../Plugins/webpack'; import { ServerAPIProvider } from '../Manifest'; import './loader.scss'; import './tooltips.scss'; - -export const DoneChartContainer: React.FC<CommonChartProps> = ({ summary }): JSX.Element | null => { +export const DoneChartContainer: React.FC<CommonChartProps> = ({ + summary, +}): JSX.Element | null => { const { costs = [] } = summary || {}; - const target = costs.find((e) => e.name === Summary.SummaryCostsDataName.Done); + const target = costs.find( + (e) => e.name === Summary.SummaryCostsDataName.Done, + ); const hooks: Array<string> = [ 'afterCompile', @@ -32,6 +39,7 @@ export const DoneChartContainer: React.FC<CommonChartProps> = ({ summary }): JSX return ( <TextDrawer + containerProps={{ style: { display: 'inline' } }} drawerProps={{ title: `Details ${suffix}` }} text={ <Space> diff --git a/packages/components/src/components/Charts/minify.tsx b/packages/components/src/components/Charts/minify.tsx index b3a4dc5a..e4e2df1c 100644 --- a/packages/components/src/components/Charts/minify.tsx +++ b/packages/components/src/components/Charts/minify.tsx @@ -7,19 +7,28 @@ import { Summary } from '@rsdoctor/utils/common'; import { Card } from '../Card'; import { ServerAPIProvider } from '../Manifest'; import { WebpackPluginsDataTable } from '../Plugins/webpack'; -import { CommonChartProps, CommonExecutionEmptyTips, CommonExecutionsChart } from './common'; +import { + CommonChartProps, + CommonExecutionEmptyTips, + CommonExecutionsChart, +} from './common'; import './loader.scss'; import './tooltips.scss'; -export const MinifyChartContainer: React.FC<CommonChartProps> = ({ summary }) => { +export const MinifyChartContainer: React.FC<CommonChartProps> = ({ + summary, +}) => { const hooks: Array<string> = ['processAssets', 'optimizeChunkAssets']; const { costs = [] } = summary || {}; - const target = costs.find((e) => e.name === Summary.SummaryCostsDataName.Minify); + const target = costs.find( + (e) => e.name === Summary.SummaryCostsDataName.Minify, + ); if (!target) return null; return ( <TextDrawer + containerProps={{ style: { display: 'inline' } }} drawerProps={{ title: 'Details of the "Minify" stage' }} text={ <Space> diff --git a/packages/components/src/components/Configuration/builder.tsx b/packages/components/src/components/Configuration/builder.tsx index d39486c0..bf5f0217 100644 --- a/packages/components/src/components/Configuration/builder.tsx +++ b/packages/components/src/components/Configuration/builder.tsx @@ -63,6 +63,7 @@ export const WebpackConfigurationViewerBase: React.FC< src={selectKeys.length === 0 ? config : pick(config, selectKeys)} displayDataTypes={false} displayObjectSize={false} + style={{ width: '100%' }} /> </Row> </TextDrawer> diff --git a/packages/components/src/components/Layout/header.tsx b/packages/components/src/components/Layout/header.tsx index a2209330..e45539fa 100644 --- a/packages/components/src/components/Layout/header.tsx +++ b/packages/components/src/components/Layout/header.tsx @@ -81,6 +81,8 @@ export const Header: React.FC = () => { <BuilderSelect /> </div> </Col> + <Menus style={{ transition: 'none' }} /> + <Col flex={1}> <Row align="middle" @@ -92,7 +94,6 @@ export const Header: React.FC = () => { <Col> <OverlayAlertsWithButton /> </Col> - <Menus style={{ transition: 'none' }} /> {/* <Col> TODO: dark mode has some error need fix. <Switch diff --git a/packages/components/src/components/Layout/menus.tsx b/packages/components/src/components/Layout/menus.tsx index efd38a78..8aa95348 100644 --- a/packages/components/src/components/Layout/menus.tsx +++ b/packages/components/src/components/Layout/menus.tsx @@ -8,7 +8,7 @@ import { ToolOutlined, } from '@ant-design/icons'; import { Manifest, SDK } from '@rsdoctor/types'; -import { Col, Menu, MenuProps, Typography } from 'antd'; +import { Menu, MenuProps, Typography } from 'antd'; import { includes } from 'lodash-es'; import React from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; @@ -48,8 +48,6 @@ const MenusBase: React.FC<{ const items: MenuProps['items'] = []; - console.log('enableRoutes: ', enableRoutes); - if (includes(enableRoutes, Manifest.RsdoctorManifestClientRoutes.Overall)) { items.push({ label: t(OverallConstants.name), @@ -162,17 +160,7 @@ const MenusBase: React.FC<{ /> ); - return ( - <Col - xs={{ span: 3 }} - md={{ span: 6 }} - lg={{ span: 10 }} - xl={{ span: 11 }} - xxl={{ span: 12 }} - > - {MenuComponent} - </Col> - ); + return <div style={{ marginLeft: '30px' }}>{MenuComponent}</div>; }; export const Menus = withServerAPI({ diff --git a/packages/components/src/components/Overall/NumberButton.module.scss b/packages/components/src/components/Overall/DataSummary.module.scss similarity index 63% rename from packages/components/src/components/Overall/NumberButton.module.scss rename to packages/components/src/components/Overall/DataSummary.module.scss index 1623b0bb..e1c96036 100644 --- a/packages/components/src/components/Overall/NumberButton.module.scss +++ b/packages/components/src/components/Overall/DataSummary.module.scss @@ -1,9 +1,10 @@ .errorNumber, .warningNumber, .successNumber { - font-size: 36px; + font-family: var(--font-family-code); + font-size: 14px; font-weight: 500; - line-height: 54px; + line-height: 32px; } .errorNumber, @@ -67,7 +68,36 @@ .container { display: flex; - flex-direction: column; - justify-content: center; - margin-bottom: 16px; + align-items: center; + padding: 10px 20px 10px 5px; + border-radius: 10px; + margin: 10px 0; + + .icon { + margin: 0 5px 0 10px; + } + + .description { + margin-right: 5px; + font-family: var(--font-family-code); + font-size: 14px; + font-weight: 400; + line-height: 20px; + color: #000000a6; + } + + .data { + font-family: var(--font-family-code); + font-size: 14px; + font-weight: 500; + line-height: 32px; + } +} + +.common { + background: #f0f5ff; +} + +.warning { + background: #fff7e6; } diff --git a/packages/components/src/components/Overall/DataSummary.tsx b/packages/components/src/components/Overall/DataSummary.tsx new file mode 100644 index 00000000..3dc33f68 --- /dev/null +++ b/packages/components/src/components/Overall/DataSummary.tsx @@ -0,0 +1,31 @@ +import Icon from '@ant-design/icons'; + +import TotalSizeSvg from '../../common/svg/total-size.svg'; +import FileSvg from '../../common/svg/file.svg'; + +import styles from './DataSummary.module.scss'; + +export interface DataSummaryProps { + theme: 'common' | 'warning'; + number: string | number; + onClick?: () => void; + description: string; + numberFontSize?: string; +} + +export const DataSummary = ({ + theme, + number, + description, +}: DataSummaryProps) => { + return ( + <div className={`${styles.container} ${styles[theme]}`}> + <Icon + style={{ fontSize: '18px', margin: '0 5px' }} + component={theme === 'common' ? FileSvg : TotalSizeSvg} + /> + <span className={styles.description}>{description}</span> + <span className={styles.data}>{number}</span> + </div> + ); +}; diff --git a/packages/components/src/components/Overall/NumberButton.tsx b/packages/components/src/components/Overall/NumberButton.tsx deleted file mode 100644 index 77fe4db5..00000000 --- a/packages/components/src/components/Overall/NumberButton.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { Button, Typography } from 'antd'; -import styles from './NumberButton.module.scss'; - -export interface NumberButtonProps { - theme: 'success' | 'error' | 'warning'; - number: string | number; - onClick?: () => void; - description: string; - numberFontSize?: string; -} - -export const NumberButton = ({ theme, number, onClick, description, numberFontSize }: NumberButtonProps) => { - const themeClass = { - success: { - number: styles.successNumber, - button: styles.successButton, - description: styles.successText, - }, - error: { - number: styles.errorNumber, - button: styles.errorButton, - description: styles.errorText, - }, - warning: { - number: styles.warningNumber, - button: styles.warningButton, - description: styles.warningText, - }, - }; - - return ( - <Button type="text" className={themeClass[theme].button} onClick={onClick}> - <Typography.Text className={themeClass[theme].number} style={{ fontSize: numberFontSize }}> - {number} - </Typography.Text> - <div className={themeClass[theme].description}>{description}</div> - </Button> - ); -}; diff --git a/packages/components/src/components/Overall/bundle.module.scss b/packages/components/src/components/Overall/bundle.module.scss index e69de29b..c5a6b8c2 100644 --- a/packages/components/src/components/Overall/bundle.module.scss +++ b/packages/components/src/components/Overall/bundle.module.scss @@ -0,0 +1,44 @@ +.title { + display: flex; + justify-content: space-between; +} + +.description { + font-family: var(--font-family-code); + font-size: 20px; + font-weight: 500; + line-height: 32px; + margin-right: 5px; +} + +.unit { + font-family: var(--font-family-code); + font-size: 12px; + font-weight: 400; + line-height: 32px; +} + +.treeContainer { + display: flex; + + .line { + border-bottom: 1px dashed #e5e6eb; + flex: 1; + transform: translateY(-45%); + margin: 0 15px 0 10px; + } + + .treeTitle { + word-wrap: break-word; + word-break: break-all; + flex-wrap: wrap; + } + + .icon { + font-size: 16px; + } + + .tag { + height: 23px; + } +} diff --git a/packages/components/src/components/Overall/bundle.tsx b/packages/components/src/components/Overall/bundle.tsx index 19335bb3..90b1e1fb 100644 --- a/packages/components/src/components/Overall/bundle.tsx +++ b/packages/components/src/components/Overall/bundle.tsx @@ -1,37 +1,102 @@ -import { Space, Typography, Tag, Row, Col, Descriptions, DescriptionsProps } from 'antd'; -import React, { useMemo } from 'react'; -import { Client, SDK } from '@rsdoctor/types'; -import { ExceptionOutlined } from '@ant-design/icons'; +import React, { useState } from 'react'; +import { + Descriptions, + DescriptionsProps, + Radio, + RadioChangeEvent, + Button, + Tree, + Tag, +} from 'antd'; +import Icon, { FolderOpenTwoTone } from '@ant-design/icons'; import { useNavigate } from 'react-router-dom'; -import { createFileStructures, formatSize, useDuplicatePackagesByErrors, useI18n } from '../../utils'; -import { DuplicatePackageDrawerWithServer, TextDrawer } from '../TextDrawer'; + +import { formatSize, useI18n } from '../../utils'; +import { TextDrawer } from '../TextDrawer'; import { Card } from '../Card'; import { ServerAPIProvider } from '../Manifest'; -import { FileTree } from '../FileTree'; import listStyles from './list.module.scss'; import cardStyles from './card.module.scss'; -import numberButtonStyles from './NumberButton.module.scss'; -import { NumberButton } from './NumberButton'; +import { DataSummary } from './DataSummary'; +import JsSvg from '../../common/svg/file-js.svg'; +import CssSvg from '../../common/svg/file-css.svg'; +import HtmlSvg from '../../common/svg/file-html.svg'; +import ImageSvg from '../../common/svg/file-image.svg'; +import UnknownSvg from '../../common/svg/file-unknown.svg'; -const getFilesWithDrawer = (data: Client.RsdoctorClientAssetsSummary['all']['total']): JSX.Element => { - const fileStructures = useMemo(() => { - if (!data.files.length) return []; - return createFileStructures({ - files: data.files.map((e) => e.path), - fileTitle(file, basename) { - const { size, initial } = data.files.find((e) => e.path === file)!; - return ( - <Space> - <Typography.Text>{basename}</Typography.Text> - <Tag color="success" style={{ marginRight: 0 }}> - {formatSize(size)} +import { Client, SDK } from '@rsdoctor/types'; +import type { TreeDataNode } from 'antd'; + +import styles from './bundle.module.scss'; + +type viewType = 'files' | 'size'; + +const { DirectoryTree } = Tree; +const getFilesWithDrawer = ( + data: Client.RsdoctorClientAssetsSummary['all']['total'], + fileType: 'js' | 'css' | 'image' | 'html' | 'unknown', +): JSX.Element => { + let files: Array<{ + fileName: string; + defaultDir: string; + size: number; + initial: boolean; + }> = []; + if (data.files.length) { + files = data.files.map((fileMessage) => { + const filePath = fileMessage.path; + const pathArray = filePath.split('/'); + const fileName = pathArray.pop()!; + const defaultDir = pathArray.join('/') || 'output'; + + return { + fileName, + defaultDir, + size: fileMessage.size, + initial: fileMessage.initial, + }; + }); + } + + const iconMap = { + js: <Icon className={styles.icon} component={JsSvg} />, + css: <Icon className={styles.icon} component={CssSvg} />, + image: <Icon className={styles.icon} component={ImageSvg} />, + html: <Icon className={styles.icon} component={HtmlSvg} />, + unknown: <Icon className={styles.icon} component={UnknownSvg} />, + }; + const treeData: TreeDataNode[] = []; + files.forEach((file) => { + const target = treeData.find((data) => data.title === file.defaultDir); + const parent: TreeDataNode = target || { + title: file.defaultDir, + key: file.defaultDir, + icon: <FolderOpenTwoTone />, + children: [], + }; + if (!target) { + treeData.push(parent); + } + parent.children!.push({ + title: ( + <div className={styles.treeContainer}> + <div className={styles.treeTitle}>{file.fileName}</div> + <div className={styles.line} /> + <Tag className={styles.tag} color="green"> + {formatSize(file.size)} + </Tag> + {file.initial ? ( + <Tag className={styles.tag} color="cyan"> + initial </Tag> - {initial ? <Tag color="cyan">Initial</Tag> : null} - </Space> - ); - }, + ) : null} + </div> + ), + key: file.fileName, + isLeaf: true, + icon: iconMap[fileType], }); - }, [data.files]); + }); return ( <> @@ -43,13 +108,20 @@ const getFilesWithDrawer = (data: Client.RsdoctorClientAssetsSummary['all']['tot buttonStyle={{ fontSize: 'inherit', }} - text={ - <> - {data.count} <ExceptionOutlined /> - </> - } + drawerProps={{ + title: 'Files', + }} + text={<span>{data.count}</span>} > - <FileTree treeData={fileStructures} defaultExpandAll /> + <DirectoryTree + defaultExpandAll + selectable={false} + treeData={treeData} + rootStyle={{ + minHeight: '800px', + border: '1px solid rgba(235, 237, 241)', + }} + /> </TextDrawer> ) : ( data.count @@ -58,124 +130,205 @@ const getFilesWithDrawer = (data: Client.RsdoctorClientAssetsSummary['all']['tot ); }; -const BundleDescriptions = ({ res }: { res: Client.RsdoctorClientAssetsSummary }) => { - const items: DescriptionsProps['items'] = [ +const BundleDescriptions = ({ + res, + view, +}: { + res: Client.RsdoctorClientAssetsSummary; + view: viewType; +}) => { + const fileItems: DescriptionsProps['items'] = [ { - key: 'total-files-count', - label: 'Total files', - children: res.all.total.count, + key: 'js-files-count', + label: 'JS files', + children: ( + <span className={styles.description}> + {getFilesWithDrawer(res.js.total, 'js')} + </span> + ), }, { - key: 'total-files-size', - label: '', - children: '', + key: 'css-files-count', + label: 'CSS files', + children: ( + <span className={styles.description}> + {getFilesWithDrawer(res.css.total, 'css')} + </span> + ), }, { - key: 'js-files-count', - label: 'JS files', - children: getFilesWithDrawer(res.js.total), + key: 'font-files-count', + label: 'Font files', + children: ( + <span className={styles.description}> + {getFilesWithDrawer(res.fonts.total, 'unknown')} + </span> + ), }, { - key: 'js-files-size', - label: 'JS size', - children: formatSize(res.js.total.size), + key: 'html-files-count', + label: 'HTML files', + children: ( + <span className={styles.description}> + {getFilesWithDrawer(res.html.total, 'html')} + </span> + ), }, { - key: 'css-files-count', - label: 'CSS files', - children: getFilesWithDrawer(res.css.total), + key: 'image-files-count', + label: 'Image files', + children: ( + <span className={styles.description}> + {getFilesWithDrawer(res.imgs.total, 'image')} + </span> + ), }, { - key: 'css-files-size', - label: 'CSS size', - children: formatSize(res.css.total.size), + key: 'media-files-count', + label: 'Media files', + children: ( + <span className={styles.description}> + {getFilesWithDrawer(res.media.total, 'unknown')} + </span> + ), }, + ]; + + const [jsSize, jsSizeUnit] = formatSize(res.js.total.size).split(' '); + const [cssSize, cssSizeUnit] = formatSize(res.css.total.size).split(' '); + const [fontSize, fontSizeUnit] = formatSize(res.fonts.total.size).split(' '); + const [htmlSize, htmlSizeUnit] = formatSize(res.html.total.size).split(' '); + const [imgSize, imgSizeUnit] = formatSize(res.imgs.total.size).split(' '); + const [mediaSize, mediaSizeUnit] = formatSize(res.media.total.size).split( + ' ', + ); + + const sizeItems: DescriptionsProps['items'] = [ { - key: 'font-files-count', - label: 'Font files', - children: getFilesWithDrawer(res.fonts.total), + key: 'js-files-size', + label: 'JS size', + children: ( + <> + <span className={styles.description}>{jsSize}</span> + <span className={styles.unit}>{jsSizeUnit}</span> + </> + ), }, { - key: 'font-files-size', - label: 'Font size', - children: formatSize(res.fonts.total.size), + key: 'css-files-size', + label: 'CSS size', + children: ( + <> + <span className={styles.description}>{cssSize}</span> + <span className={styles.unit}>{cssSizeUnit}</span> + </> + ), }, { - key: 'html-files-count', - label: 'HTML files', - children: getFilesWithDrawer(res.html.total), + key: 'font-files-size', + label: 'Font size', + children: ( + <> + <span className={styles.description}>{fontSize}</span> + <span className={styles.unit}>{fontSizeUnit}</span> + </> + ), }, { key: 'html-files-size', label: 'HTML size', - children: formatSize(res.html.total.size), - }, - { - key: 'image-files-count', - label: 'Image files', - children: getFilesWithDrawer(res.imgs.total), + children: ( + <> + <span className={styles.description}>{htmlSize}</span> + <span className={styles.unit}>{htmlSizeUnit}</span> + </> + ), }, { key: 'image-files-size', label: 'Image size', - children: formatSize(res.imgs.total.size), - }, - { - key: 'media-files-count', - label: 'Media files', - children: getFilesWithDrawer(res.media.total), + children: ( + <> + <span className={styles.description}>{imgSize}</span> + <span className={styles.unit}>{imgSizeUnit}</span> + </> + ), }, { key: 'media-files-size', label: 'Media size', - children: formatSize(res.media.total.size), + children: ( + <> + <span className={styles.description}>{mediaSize}</span> + <span className={styles.unit}>{mediaSizeUnit}</span> + </> + ), }, ]; - return <Descriptions className={listStyles.root} size="small" column={2} items={items} />; + return ( + <Descriptions + layout={'vertical'} + className={listStyles.root} + size="small" + column={3} + colon={false} + items={view === 'files' ? fileItems : sizeItems} + /> + ); }; export const BundleOverall: React.FC<{ errors: SDK.ErrorsData; cwd: string; -}> = ({ errors, cwd }): JSX.Element | null => { - const { t } = useI18n(); +}> = (): JSX.Element | null => { + const [view, setView] = useState<viewType>('size'); const navigate = useNavigate(); - const duplicatePackages = useDuplicatePackagesByErrors(errors); + const { t } = useI18n(); + + const handleViewChange = (e: RadioChangeEvent) => { + setView(e.target.value); + }; return ( - <ServerAPIProvider api={SDK.ServerAPI.API.GetAssetsSummary} body={{ withFileContent: false }}> + <ServerAPIProvider + api={SDK.ServerAPI.API.GetAssetsSummary} + body={{ withFileContent: false }} + > {(res) => { const totalSizeStr = formatSize(res.all.total.size); return ( - <Card title={t('Bundle Overall')} className={cardStyles.card}> - <Row gutter={16}> - <Col span={12} className={numberButtonStyles.container}> - <NumberButton - theme="success" - number={totalSizeStr} - description="Total Size" - numberFontSize="30px" + <Card + title={ + <div className={styles.title}> + <span>{t('Bundle Overall')}</span> + <Button + type="link" onClick={() => { navigate(Client.RsdoctorClientRoutes.BundleSize); }} - /> - </Col> - <Col span={12} className={numberButtonStyles.container}> - <DuplicatePackageDrawerWithServer - cwd={cwd} - duplicatePackages={duplicatePackages} - button={ - <NumberButton - theme={duplicatePackages.length === 0 ? 'success' : 'warning'} - number={duplicatePackages.length} - description="Duplicate Packages" - /> - } - /> - </Col> - </Row> - <BundleDescriptions res={res} /> + > + View Bundler Size + </Button> + </div> + } + className={cardStyles.card} + > + <Radio.Group + onChange={handleViewChange} + value={view} + defaultValue={view} + style={{ marginBottom: 8 }} + > + <Radio.Button value="size">Size</Radio.Button> + <Radio.Button value="files">Files</Radio.Button> + </Radio.Group> + <DataSummary + theme={view === 'files' ? 'common' : 'warning'} + number={view === 'files' ? res.all.total.count : totalSizeStr} + description={`Total ${view}`} + /> + <BundleDescriptions view={view} res={res} /> </Card> ); }} diff --git a/packages/components/src/components/Overall/card.module.scss b/packages/components/src/components/Overall/card.module.scss index 6dccc4e3..cabcc936 100644 --- a/packages/components/src/components/Overall/card.module.scss +++ b/packages/components/src/components/Overall/card.module.scss @@ -1,7 +1,8 @@ .card { - min-height: 440px; + width: 100%; + padding-bottom: 10px; :global(.ant-card-body) { padding-bottom: 16px; } -} \ No newline at end of file +} diff --git a/packages/components/src/components/Overall/compile.tsx b/packages/components/src/components/Overall/compile.tsx index ca60a170..230dc38b 100644 --- a/packages/components/src/components/Overall/compile.tsx +++ b/packages/components/src/components/Overall/compile.tsx @@ -25,14 +25,9 @@ const Stage: React.FC< ); }; -const getProgressColor = (percent: number) => { - if (percent < 33.3) { - return { '0%': '#95de64', '100%': '#5cdbd3' }; - } - return { '0%': '#ff9c6e', '100%': '#ff7875' }; -}; - -export const CompileOverall: React.FC<{ summary: SDK.SummaryData }> = ({ summary }) => { +export const CompileOverall: React.FC<{ summary: SDK.SummaryData }> = ({ + summary, +}) => { const { t } = useI18n(); if (!summary?.costs?.length) return null; @@ -41,7 +36,11 @@ export const CompileOverall: React.FC<{ summary: SDK.SummaryData }> = ({ summary return ( <Card title={t('Compile Overall')} className={cardStyles.card}> - <Space style={{ wordBreak: 'break-all', width: '100%' }} size={20} direction="vertical"> + <Space + style={{ wordBreak: 'break-all', width: '100%' }} + size={20} + direction="vertical" + > {summary.costs.map((e) => { const { name, costs } = e; const percent = (costs * 100) / maxCosts; @@ -51,7 +50,6 @@ export const CompileOverall: React.FC<{ summary: SDK.SummaryData }> = ({ summary className={styles.progress} percent={percent} status="normal" - strokeColor={getProgressColor(percent)} format={() => formatCosts(costs)} /> ); diff --git a/packages/components/src/components/Overall/help-center.module.scss b/packages/components/src/components/Overall/help-center.module.scss new file mode 100644 index 00000000..9e0637ff --- /dev/null +++ b/packages/components/src/components/Overall/help-center.module.scss @@ -0,0 +1,23 @@ +.title { + display: flex; + justify-content: space-between; +} + +.container { + display: flex; + justify-content: space-around; + flex-wrap: wrap; + + .content { + cursor: pointer; + font-size: 16px; + width: 48%; + margin-bottom: 13px; + padding: 0 10px; + transition: color 0.5s ease; + } + + .content:hover { + color: #1677ff; + } +} diff --git a/packages/components/src/components/Overall/help-center.tsx b/packages/components/src/components/Overall/help-center.tsx new file mode 100644 index 00000000..d1e0174c --- /dev/null +++ b/packages/components/src/components/Overall/help-center.tsx @@ -0,0 +1,50 @@ +import { Button } from 'antd'; + +import { Card } from '../Card'; +import { useI18n } from '../../utils'; + +import styles from './help-center.module.scss'; + +interface HelpCenterProps { + data: Array<{ + title: string; + link: string; + }>; +} + +export const HelpCenter = (props: HelpCenterProps) => { + const { data } = props; + const { t } = useI18n(); + + return ( + <Card + style={{ width: '100%' }} + title={ + <div className={styles.title}> + <span>{t('Help Center')}</span> + <Button + type="link" + onClick={() => { + window.open('https://rsdoctor.dev/index', '_blank'); + }} + > + More + </Button> + </div> + } + > + <div className={styles.container}> + {data.map(({ title, link }) => { + return ( + <div + className={styles.content} + onClick={() => window.open(link, '_blank')} + > + {t(title)} + </div> + ); + })} + </div> + </Card> + ); +}; diff --git a/packages/components/src/components/Overall/list.module.scss b/packages/components/src/components/Overall/list.module.scss index dd0d9934..0b29356a 100644 --- a/packages/components/src/components/Overall/list.module.scss +++ b/packages/components/src/components/Overall/list.module.scss @@ -2,6 +2,7 @@ :global(.ant-descriptions-item-label), :global(.ant-descriptions-item-content) { font-size: 13px; + color: #000000a6; } :global(.ant-descriptions-item) { diff --git a/packages/components/src/components/Overall/overview.module.scss b/packages/components/src/components/Overall/overview.module.scss new file mode 100644 index 00000000..86d086ec --- /dev/null +++ b/packages/components/src/components/Overall/overview.module.scss @@ -0,0 +1,26 @@ +.container { + width: 100%; + display: flex; + background: #f6f8fa; + align-items: center; + justify-content: space-between; + min-width: 280px; + padding: 10px 20px; + border-radius: 8px; + margin-right: 10px; + + .title { + font-family: var(--font-family-code); + font-size: 12px; + font-weight: 400; + line-height: 20px; + color: #000000a6; + } + + .description { + font-family: var(--font-family-code); + font-size: 24px; + font-weight: 500; + line-height: 32px; + } +} diff --git a/packages/components/src/components/Overall/overview.tsx b/packages/components/src/components/Overall/overview.tsx new file mode 100644 index 00000000..7c08463f --- /dev/null +++ b/packages/components/src/components/Overall/overview.tsx @@ -0,0 +1,23 @@ +import styles from './overview.module.scss'; + +interface OverviewProps { + title?: string | React.ReactNode; + description?: string | React.ReactNode; + icon?: React.ReactNode; + style?: React.CSSProperties; +} + +export const Overview = (props: OverviewProps) => { + const { title, description, icon, style } = props; + return ( + <div className={styles.container} style={style}> + <div> + <div className={styles.title}>{title}</div> + <div className={styles.description}>{description}</div> + </div> + {icon} + </div> + ); +}; + +export default Overview; diff --git a/packages/components/src/components/Overall/project.module.scss b/packages/components/src/components/Overall/project.module.scss new file mode 100644 index 00000000..b54d67bb --- /dev/null +++ b/packages/components/src/components/Overall/project.module.scss @@ -0,0 +1,4 @@ +.overview { + display: flex; + margin-bottom: 20px; +} diff --git a/packages/components/src/components/Overall/project.tsx b/packages/components/src/components/Overall/project.tsx index a8fde67a..e772ab5c 100644 --- a/packages/components/src/components/Overall/project.tsx +++ b/packages/components/src/components/Overall/project.tsx @@ -1,17 +1,23 @@ -import { Space, Row, Col, Descriptions, DescriptionsProps } from 'antd'; -import React from 'react'; +import { Descriptions, DescriptionsProps, Avatar } from 'antd'; +import { + CloseCircleFilled, + WarningFilled, + FileFilled, + ExperimentFilled, +} from '@ant-design/icons'; import { filter } from 'lodash-es'; -import { SDK } from '@rsdoctor/types'; -import { useI18n } from '../../utils'; + +import { ServerAPIProvider } from '../Manifest'; +import { useI18n, formatSize } from '../../utils'; import { WebpackConfigurationViewer } from '../Configuration'; import { Card } from '../Card'; +import { Overview } from './overview'; import listStyles from './list.module.scss'; import cardStyles from './card.module.scss'; -import numberButtonStyles from './NumberButton.module.scss'; -import { TextDrawer } from '../TextDrawer'; -import { BundleAlerts, CompileAlerts, OverlayAlertsWithTips } from '../Alerts'; -import { NumberButton } from './NumberButton'; +import projectStyles from './project.module.scss'; + +import { SDK } from '@rsdoctor/types'; export const ProjectOverall: React.FC<{ configs: SDK.ConfigData; @@ -53,34 +59,88 @@ export const ProjectOverall: React.FC<{ ]; return ( - <Card title={t('Project Overall')} extra={<WebpackConfigurationViewer />} className={cardStyles.card}> - <Row gutter={16}> - <Col span={12} className={numberButtonStyles.container}> - <TextDrawer - button={<NumberButton theme={errors === 0 ? 'success' : 'error'} number={errors} description="Errors" />} - drawerProps={{ title: 'Errors List' }} - > - <Space direction="vertical" style={{ width: '100%' }}> - <OverlayAlertsWithTips defaultOpen={false} /> - <BundleAlerts filter={(r) => r.level === 'error'} /> - <CompileAlerts filter={(r) => r.level === 'error'} /> - </Space> - </TextDrawer> - </Col> - <Col span={12} className={numberButtonStyles.container}> - <TextDrawer - button={<NumberButton theme={warns === 0 ? 'success' : 'warning'} number={warns} description="Warnings" />} - drawerProps={{ title: 'Warnings List' }} + <ServerAPIProvider + api={SDK.ServerAPI.API.GetAssetsSummary} + body={{ withFileContent: false }} + > + {(res) => { + const totalSizeStr = formatSize(res.all.total.size); + const totalFiles = res.all.total.count; + const [size, unit] = totalSizeStr.split(' '); + const overViewData = [ + { + title: 'Errors', + description: <span style={{ color: '#FF4D4F' }}>{errors}</span>, + icon: ( + <Avatar + style={{ background: '#FF4D4F' }} + shape="circle" + icon={<CloseCircleFilled style={{ fontSize: '18px' }} />} + /> + ), + }, + { + title: 'Warnings', + description: <span style={{ color: '#FAAD14' }}>{warns}</span>, + icon: ( + <Avatar + style={{ background: '#FAAD14' }} + shape="circle" + icon={<WarningFilled style={{ fontSize: '18px' }} />} + /> + ), + }, + { + title: 'Total Files', + description: <span>{totalFiles}</span>, + icon: ( + <Avatar + style={{ background: '#3874F6' }} + shape="circle" + icon={<FileFilled style={{ fontSize: '18px' }} />} + /> + ), + }, + { + title: 'Total Size', + description: ( + <> + <span style={{ fontSize: '20px' }}>{size}</span> + <span style={{ fontSize: '13px', marginLeft: '5px' }}> + {unit} + </span> + </> + ), + icon: ( + <Avatar + style={{ background: '#FF4D4F' }} + shape="circle" + icon={<ExperimentFilled style={{ fontSize: '18px' }} />} + /> + ), + }, + ]; + + return ( + <Card + title={t('Project Overall')} + extra={<WebpackConfigurationViewer />} + className={cardStyles.card} > - <Space direction="vertical" style={{ width: '100%' }}> - <OverlayAlertsWithTips defaultOpen={false} /> - <BundleAlerts filter={(r) => r.level === 'warn'} /> - <CompileAlerts filter={(r) => r.level === 'warn'} /> - </Space> - </TextDrawer> - </Col> - </Row> - <Descriptions className={listStyles.root} items={items} size="small" column={1} /> - </Card> + <div className={projectStyles.overview}> + {overViewData.map((data, idx) => ( + <Overview + key={idx} + title={data.title} + description={<span>{data.description}</span>} + icon={data.icon} + /> + ))} + </div> + <Descriptions className={listStyles.root} items={items} /> + </Card> + ); + }} + </ServerAPIProvider> ); }; diff --git a/packages/components/src/components/TextDrawer/index.tsx b/packages/components/src/components/TextDrawer/index.tsx index 84d4d083..8337d3f8 100644 --- a/packages/components/src/components/TextDrawer/index.tsx +++ b/packages/components/src/components/TextDrawer/index.tsx @@ -1,6 +1,10 @@ import { Button, Drawer, ButtonProps, DrawerProps } from 'antd'; -import React, { CSSProperties, PropsWithChildren, useState } from 'react'; -import { drawerWidth } from '../../constants'; +import React, { + CSSProperties, + HTMLAttributes, + PropsWithChildren, + useState, +} from 'react'; export interface TextDrawerProps { text?: string | React.ReactNode; @@ -8,13 +12,17 @@ export interface TextDrawerProps { buttonProps?: ButtonProps; buttonStyle?: CSSProperties; drawerProps?: DrawerProps; + containerProps?: HTMLAttributes<HTMLDivElement>; } -export const TextDrawer = (props: PropsWithChildren<TextDrawerProps>): JSX.Element => { +export const TextDrawer = ( + props: PropsWithChildren<TextDrawerProps>, +): JSX.Element => { const [visible, setVisible] = useState(false); return ( - <React.Fragment> + // avoid propagation event affect collapse component + <div onClick={(e) => e.stopPropagation()} {...props.containerProps}> {props.button ? ( <div onClick={() => setVisible(!visible)}>{props.button}</div> ) : ( @@ -29,8 +37,8 @@ export const TextDrawer = (props: PropsWithChildren<TextDrawerProps>): JSX.Eleme )} <Drawer maskClosable - width={drawerWidth} zIndex={999} + width={'60%'} {...props.drawerProps} open={visible} onClose={() => setVisible(false)} @@ -38,7 +46,7 @@ export const TextDrawer = (props: PropsWithChildren<TextDrawerProps>): JSX.Eleme > {props.children} </Drawer> - </React.Fragment> + </div> ); }; diff --git a/packages/components/src/components/index.ts b/packages/components/src/components/index.ts index 7b8ff3f1..dd786745 100644 --- a/packages/components/src/components/index.ts +++ b/packages/components/src/components/index.ts @@ -1,4 +1,3 @@ -export * from './Alert'; export * from './Alerts'; export * from './Badge'; export * from './Card'; diff --git a/packages/components/src/pages/Overall/index.module.scss b/packages/components/src/pages/Overall/index.module.scss new file mode 100644 index 00000000..d7352a96 --- /dev/null +++ b/packages/components/src/pages/Overall/index.module.scss @@ -0,0 +1,35 @@ +:root { + --font-family-code: Roboto, Roboto Mono, 'Segoe UI', 'Segoe UI Symbol', + 'Noto Color Emoji'; +} + +:global { + .ant-tabs .ant-tabs-tab + .ant-tabs-tab { + margin-left: 7px; + } + + .rc-tree .rc-tree-treenode .rc-tree-node-content-wrapper { + cursor: default; + } + + // for bundle overall + .ant-tree-title { + flex: 1; + } + .ant-tree-node-content-wrapper-normal { + display: flex; + } +} + +.overall { + display: flex; + justify-content: space-between; +} + +.layout { + margin-right: 20px; + + .row { + margin-bottom: 20px; + } +} diff --git a/packages/components/src/pages/Overall/index.tsx b/packages/components/src/pages/Overall/index.tsx index 8957fbe5..e0cc7c97 100644 --- a/packages/components/src/pages/Overall/index.tsx +++ b/packages/components/src/pages/Overall/index.tsx @@ -1,37 +1,88 @@ import { SDK } from '@rsdoctor/types'; import React from 'react'; -import { BundleAlerts, CompileAlerts } from '../../components/Alerts'; + +import { HelpCenter } from '../../components/Overall/help-center'; +import { BundleAlerts } from '../../components/Alerts'; import { withServerAPI } from '../../components/Manifest'; import { BundleOverall, CompileOverall, ProjectOverall, } from '../../components/Overall'; -import { ResponsiveGridLayout } from './responsiveGridList'; +import { ResponsiveLayout } from './responsiveLayout'; + +import style from './index.module.scss'; interface Props { project: SDK.ServerAPI.InferResponseType<SDK.ServerAPI.API.GetProjectInfo>; } +const helpCenterData = [ + { + title: 'FAQ', + link: 'https://rsdoctor.dev/guide/more/faq', + }, + { + title: 'Introduction', + link: 'https://rsdoctor.dev/guide/start/intro', + }, + { + title: 'Bundle Alerts', + link: 'https://rsdoctor.dev/guide/usage/bundle-alerts', + }, + { + title: 'Bundle Overall', + link: 'https://rsdoctor.dev/guide/usage/bundle-overall', + }, + { + title: 'Bundle Analysis', + link: 'https://rsdoctor.dev/guide/usage/bundle-size', + }, + { + title: 'Compilation Alerts', + link: 'https://rsdoctor.dev/guide/usage/compile-alerts', + }, + { + title: 'Compile Overall', + link: 'https://rsdoctor.dev/guide/usage/compile-overall', + }, + { + title: 'Loaders Analysis', + link: 'https://rsdoctor.dev/guide/usage/loaders-analysis', + }, + { + title: 'Loaders Timeline', + link: 'https://rsdoctor.dev/guide/usage/loaders-timeline', + }, + { + title: 'Plugin Analysis', + link: 'https://rsdoctor.dev/guide/usage/plugins-analysis', + }, +]; const Component: React.FC<Props> = ({ project }) => { const { summary, configs, root: cwd, envinfo, errors } = project; return ( - <div> - <ResponsiveGridLayout> - <ProjectOverall - configs={configs} - cwd={cwd} - envinfo={envinfo} - alerts={errors} - /> - <BundleOverall errors={errors} cwd={cwd} /> - <CompileOverall summary={summary} /> - </ResponsiveGridLayout> - - <CompileAlerts /> - - <BundleAlerts /> + <div className={style.overall}> + <div> + <ResponsiveLayout> + <ProjectOverall + configs={configs} + cwd={cwd} + envinfo={envinfo} + alerts={errors} + /> + <BundleAlerts /> + </ResponsiveLayout> + </div> + + <div> + <ResponsiveLayout> + <BundleOverall errors={errors} cwd={cwd} /> + <CompileOverall summary={summary} /> + <HelpCenter data={helpCenterData} /> + </ResponsiveLayout> + </div> </div> ); }; diff --git a/packages/components/src/pages/Overall/responsiveGridList.tsx b/packages/components/src/pages/Overall/responsiveGridList.tsx deleted file mode 100644 index ac8ebaca..00000000 --- a/packages/components/src/pages/Overall/responsiveGridList.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { Col, Grid, Row } from 'antd'; -import React, { useMemo } from 'react'; -import { Size } from '../../constants'; - -interface Props { - children: React.ReactNode[]; -} - -export const ResponsiveGridLayout = ({ children }: Props) => { - const { sm, xxl } = Grid.useBreakpoint(); - - const gutters = useMemo<[number, number]>(() => { - if (xxl) { - return [Size.BasePadding * 2, Size.BasePadding]; - } else { - return [Size.BasePadding, Size.BasePadding]; - } - }, [sm, xxl]); - - return ( - <Row - gutter={gutters} - wrap - style={{ - marginBottom: Size.BasePadding, - }} - > - {children.map((e, i) => ( - <Col key={i} xs={24} sm={24} md={12} lg={8}> - {e} - </Col> - ))} - </Row> - ); -}; diff --git a/packages/components/src/pages/Overall/responsiveLayout.tsx b/packages/components/src/pages/Overall/responsiveLayout.tsx new file mode 100644 index 00000000..5b083c06 --- /dev/null +++ b/packages/components/src/pages/Overall/responsiveLayout.tsx @@ -0,0 +1,20 @@ +import { Col, Row } from 'antd'; +import React from 'react'; + +import style from './index.module.scss'; + +interface Props { + children: React.ReactNode[]; +} + +export const ResponsiveLayout = ({ children }: Props) => { + return ( + <Col className={style.layout}> + {children.map((e, i) => ( + <Row className={style.row} key={i} wrap> + {e} + </Row> + ))} + </Col> + ); +}; diff --git a/packages/components/src/utils/i18n/cn.ts b/packages/components/src/utils/i18n/cn.ts index afb237fa..76bb9e93 100644 --- a/packages/components/src/utils/i18n/cn.ts +++ b/packages/components/src/utils/i18n/cn.ts @@ -3,6 +3,11 @@ import en from './en'; const cn: typeof en = { Overall: '概览', 'Compile Analysis': '编译分析', + 'Bundle Alerts': '编译预警', + 'Help Center': '帮助中心', + Introduction: '介绍', + 'Compilation Alerts': '编译预警', + 'Plugin Analysis': 'Plugins 分析', LoadersAnalysis: 'Loaders 分析', 'Loaders Timeline': 'Loaders 概览', 'Loaders Analysis': 'Loaders 分析', diff --git a/packages/components/src/utils/i18n/en.ts b/packages/components/src/utils/i18n/en.ts index cf91d505..6648245f 100644 --- a/packages/components/src/utils/i18n/en.ts +++ b/packages/components/src/utils/i18n/en.ts @@ -2,9 +2,14 @@ const en = { Overall: 'Overall', 'Compile Analysis': 'Compile Analysis', + 'Bundle Alerts': 'Bundle Alerts', + 'Help Center': 'Help Center', LoadersAnalysis: 'Loaders Analysis', 'Loaders Timeline': 'Loaders Timeline', 'Loaders Analysis': 'Loaders Analysis', + Introduction: 'Introduction', + 'Compilation Alerts': 'Compilation Alerts', + 'Plugin Analysis': 'Plugin Analysis', PluginsAnalysis: 'Plugins Analysis', ModuleResolve: 'Module Resolve', 'Bundle Analysis': 'Bundle Analysis', @@ -33,7 +38,8 @@ const en = { 'After Compile': 'After Compile', 'After Bundle': 'After Bundle', CodeModeExplain: 'In lite mode or pure stats.json upload, only source code.', - DuplicatePakCodeExplain: 'There is no code, when in the case of pure stats.json.', + DuplicatePakCodeExplain: + 'There is no code, when in the case of pure stats.json.', }; export default en; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 62511158..d2ae4c04 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1364,7 +1364,7 @@ packages: dependencies: chalk: 4.1.2 lodash: 4.17.21 - micromatch: 4.0.8 + micromatch: 4.0.7 dev: true /@arco-plugins/webpack-react@1.4.9-beta.2(webpack@5.94.0): @@ -1537,6 +1537,15 @@ packages: jsesc: 2.5.2 dev: true + /@babel/generator@7.25.7: + resolution: {integrity: sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.26.0 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 3.0.2 + /@babel/generator@7.26.2: resolution: {integrity: sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==} engines: {node: '>=6.9.0'} @@ -1705,6 +1714,15 @@ packages: - supports-color dev: true + /@babel/helper-module-imports@7.24.7: + resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/traverse': 7.25.7 + '@babel/types': 7.26.0 + transitivePeerDependencies: + - supports-color + /@babel/helper-module-imports@7.25.9(supports-color@5.5.0): resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} engines: {node: '>=6.9.0'} @@ -1876,6 +1894,10 @@ packages: resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} engines: {node: '>=6.9.0'} + /@babel/helper-validator-option@7.24.8: + resolution: {integrity: sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==} + engines: {node: '>=6.9.0'} + /@babel/helper-validator-option@7.25.9: resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} engines: {node: '>=6.9.0'} @@ -3573,7 +3595,7 @@ packages: dependencies: '@babel/core': 7.25.2 '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-module-imports': 7.25.9(supports-color@5.5.0) + '@babel/helper-module-imports': 7.24.7 '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.25.2) '@babel/types': 7.26.0 @@ -3588,7 +3610,7 @@ packages: dependencies: '@babel/core': 7.26.0 '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-module-imports': 7.25.9(supports-color@5.5.0) + '@babel/helper-module-imports': 7.24.7 '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.26.0) '@babel/types': 7.26.0 @@ -4133,7 +4155,7 @@ packages: dependencies: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-validator-option': 7.25.9 + '@babel/helper-validator-option': 7.24.8 '@babel/plugin-transform-react-display-name': 7.24.7(@babel/core@7.25.2) '@babel/plugin-transform-react-jsx': 7.25.2(@babel/core@7.25.2) '@babel/plugin-transform-react-jsx-development': 7.24.7(@babel/core@7.25.2) @@ -4149,7 +4171,7 @@ packages: dependencies: '@babel/core': 7.26.0 '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-validator-option': 7.25.9 + '@babel/helper-validator-option': 7.24.8 '@babel/plugin-transform-react-display-name': 7.24.7(@babel/core@7.26.0) '@babel/plugin-transform-react-jsx': 7.25.2(@babel/core@7.26.0) '@babel/plugin-transform-react-jsx-development': 7.24.7(@babel/core@7.26.0) @@ -4166,7 +4188,7 @@ packages: dependencies: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-validator-option': 7.25.9 + '@babel/helper-validator-option': 7.24.8 '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.25.2) '@babel/plugin-transform-modules-commonjs': 7.24.8(@babel/core@7.25.2) '@babel/plugin-transform-typescript': 7.25.2(@babel/core@7.25.2) @@ -4181,7 +4203,7 @@ packages: dependencies: '@babel/core': 7.26.0 '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-validator-option': 7.25.9 + '@babel/helper-validator-option': 7.24.8 '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.26.0) '@babel/plugin-transform-modules-commonjs': 7.24.8(@babel/core@7.26.0) '@babel/plugin-transform-typescript': 7.25.2(@babel/core@7.26.0) @@ -4228,6 +4250,14 @@ packages: dependencies: regenerator-runtime: 0.14.1 + /@babel/template@7.25.7: + resolution: {integrity: sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.25.7 + '@babel/parser': 7.26.2 + '@babel/types': 7.26.0 + /@babel/template@7.25.9: resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} engines: {node: '>=6.9.0'} @@ -4251,6 +4281,20 @@ packages: - supports-color dev: true + /@babel/traverse@7.25.7: + resolution: {integrity: sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.25.7 + '@babel/generator': 7.25.7 + '@babel/parser': 7.26.2 + '@babel/template': 7.25.7 + '@babel/types': 7.26.0 + debug: 4.3.7(supports-color@5.5.0) + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + /@babel/traverse@7.25.9(supports-color@5.5.0): resolution: {integrity: sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==} engines: {node: '>=6.9.0'} @@ -6565,7 +6609,7 @@ packages: '@rsbuild/plugin-sass': 1.0.4(@rsbuild/core@1.0.19) '@rsbuild/plugin-source-build': 1.0.1(@rsbuild/core@1.0.19) '@rsbuild/plugin-styled-components': 1.0.1(@rsbuild/core@1.0.19) - '@rsbuild/plugin-svgr': 1.0.4(@rsbuild/core@1.0.19)(typescript@5.5.4) + '@rsbuild/plugin-svgr': 1.0.6(@rsbuild/core@1.0.19)(typescript@5.5.4) '@rsbuild/plugin-toml': 1.0.1(@rsbuild/core@1.0.19) '@rsbuild/plugin-type-check': 1.0.1(@rsbuild/core@1.0.19)(esbuild@0.17.19)(typescript@5.5.4) '@rsbuild/plugin-typed-css-modules': 1.0.2(@rsbuild/core@1.0.19) @@ -7271,7 +7315,7 @@ packages: dependencies: '@rsbuild/core': 1.1.12 deepmerge: 4.3.1 - reduce-configs: 1.1.0 + reduce-configs: 1.0.0 /@rsbuild/plugin-node-polyfill@1.0.4(@rsbuild/core@1.0.14): resolution: {integrity: sha512-WuYnMmbRpRPGsHn1maLLa4aHY4qSlEI5wbVhf4vcYlz4Zi+F+RgM/cerFZ0zSgNW/7zEHRORoSWFEyOUff8RvQ==} @@ -7355,16 +7399,6 @@ packages: reduce-configs: 1.0.0 dev: true - /@rsbuild/plugin-react@1.0.4(@rsbuild/core@1.0.19): - resolution: {integrity: sha512-lZQPl2Ocw3mxdR8dGZNTx70iLILt/p1B4oAStDNnDCVK9mzeCzpG67IYP82KaAJ5KowXTPLRqEkF9fKr5lWPPA==} - peerDependencies: - '@rsbuild/core': 1.x || ^1.0.1-rc.0 - dependencies: - '@rsbuild/core': 1.0.19 - '@rspack/plugin-react-refresh': 1.0.0(react-refresh@0.14.2) - react-refresh: 0.14.2 - dev: true - /@rsbuild/plugin-react@1.0.7(@rsbuild/core@1.0.19): resolution: {integrity: sha512-t7T/GqDwodusTAnxGpqVRnQ/G+HYh98zk71qIg19WkjVJJGv57AC1Ppx0/6zzbZAbxU60bfK8TeEEXjhXCdSxA==} peerDependencies: @@ -7385,6 +7419,16 @@ packages: react-refresh: 0.14.2 dev: true + /@rsbuild/plugin-react@1.1.0(@rsbuild/core@1.0.19): + resolution: {integrity: sha512-uqdRoV2V91G1XIA14dAmxqYTlTDVf0ktpE7TgwG29oQ2j+DerF1kh29WPHK9HvGE34JTfaBrsme2Zmb6bGD0cw==} + peerDependencies: + '@rsbuild/core': 1.x + dependencies: + '@rsbuild/core': 1.0.19 + '@rspack/plugin-react-refresh': 1.0.0(react-refresh@0.16.0) + react-refresh: 0.16.0 + dev: true + /@rsbuild/plugin-react@1.1.0(@rsbuild/core@1.1.12): resolution: {integrity: sha512-uqdRoV2V91G1XIA14dAmxqYTlTDVf0ktpE7TgwG29oQ2j+DerF1kh29WPHK9HvGE34JTfaBrsme2Zmb6bGD0cw==} peerDependencies: @@ -7430,7 +7474,7 @@ packages: loader-utils: 2.0.4 postcss: 8.4.49 reduce-configs: 1.1.0 - sass-embedded: 1.82.0 + sass-embedded: 1.83.0 /@rsbuild/plugin-source-build@1.0.1(@rsbuild/core@1.0.19): resolution: {integrity: sha512-GA9Uapy4cTOOa0jkwf4/L4m6rPieWWOmeeEygVnJAHRdB5nW45conwlV9g1ZQC14ITHsZlai8FiZotWGPNJlwA==} @@ -7459,13 +7503,13 @@ packages: reduce-configs: 1.0.0 dev: true - /@rsbuild/plugin-svgr@1.0.4(@rsbuild/core@1.0.19)(typescript@5.5.4): - resolution: {integrity: sha512-j2BXjNxsIEwRghsw3hagljbCYhe5uuw9qOMuN9Lgrb9PzNt44IMgIgrIdsS+3TR05CioMU/LHhA+Xjl+bhTMxA==} + /@rsbuild/plugin-svgr@1.0.6(@rsbuild/core@1.0.19)(typescript@5.5.4): + resolution: {integrity: sha512-znLFk2fumNObMntkjrpZhO3guXmaQZbqv0JjhqBVng63bNdsJAmfZGXX5He8Avp4VDlv6EJI6BC5SIEs8XspTg==} peerDependencies: - '@rsbuild/core': 1.x || ^1.0.1-rc.0 + '@rsbuild/core': 1.x dependencies: '@rsbuild/core': 1.0.19 - '@rsbuild/plugin-react': 1.0.4(@rsbuild/core@1.0.19) + '@rsbuild/plugin-react': 1.1.0(@rsbuild/core@1.0.19) '@svgr/core': 8.1.0(typescript@5.5.4) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0) '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0)(typescript@5.5.4) @@ -8962,7 +9006,7 @@ packages: estree-walker: 2.0.2 glob: 7.2.3 graceful-fs: 4.2.11 - micromatch: 4.0.8 + micromatch: 4.0.7 node-gyp-build: 4.8.1 resolve-from: 5.0.0 transitivePeerDependencies: @@ -8989,7 +9033,7 @@ packages: /@vitest/snapshot@1.6.0: resolution: {integrity: sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==} dependencies: - magic-string: 0.30.14 + magic-string: 0.30.12 pathe: 1.1.2 pretty-format: 29.7.0 dev: true @@ -9035,7 +9079,7 @@ packages: '@vue/compiler-ssr': 3.5.13 '@vue/shared': 3.5.13 estree-walker: 2.0.2 - magic-string: 0.30.14 + magic-string: 0.30.12 postcss: 8.4.49 source-map-js: 1.2.1 dev: false @@ -9668,16 +9712,6 @@ packages: possible-typed-array-names: 1.0.0 dev: true - /axios@1.7.7: - resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==} - dependencies: - follow-redirects: 1.15.9(debug@4.3.7) - form-data: 4.0.0 - proxy-from-env: 1.1.0 - transitivePeerDependencies: - - debug - dev: true - /axios@1.7.9(debug@4.3.7): resolution: {integrity: sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==} dependencies: @@ -9715,7 +9749,7 @@ packages: /babel-plugin-import@1.13.8: resolution: {integrity: sha512-36babpjra5m3gca44V6tSTomeBlPA7cHUynrE2WiQIm3rEGD9xy28MKsx5IdO45EbnpJY7Jrgd00C6Dwt/l/2Q==} dependencies: - '@babel/helper-module-imports': 7.25.9(supports-color@5.5.0) + '@babel/helper-module-imports': 7.24.7 transitivePeerDependencies: - supports-color dev: true @@ -10287,7 +10321,7 @@ packages: js-yaml: 4.1.0 semver: 7.6.3 table: 6.8.2 - type-fest: 4.30.0 + type-fest: 4.30.2 dev: true /check-error@1.0.3: @@ -13941,7 +13975,6 @@ packages: /jsesc@2.5.2: resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} engines: {node: '>=4'} - hasBin: true dev: true /jsesc@3.0.2: @@ -14332,8 +14365,8 @@ packages: '@jridgewell/sourcemap-codec': 1.5.0 dev: true - /magic-string@0.30.14: - resolution: {integrity: sha512-5c99P1WKTed11ZC0HMJOj6CDIue6F8ySu+bJL+85q1zBEIY8IklrJ1eiKC2NDRh3Ct3FcvmJPyQHb9erXMTJNw==} + /magic-string@0.30.12: + resolution: {integrity: sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==} dependencies: '@jridgewell/sourcemap-codec': 1.5.0 @@ -15359,7 +15392,6 @@ packages: /nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -15510,7 +15542,7 @@ packages: '@yarnpkg/lockfile': 1.1.0 '@yarnpkg/parsers': 3.0.0-rc.46 '@zkochan/js-yaml': 0.0.6 - axios: 1.7.7 + axios: 1.7.9(debug@4.3.7) chalk: 4.1.2 cli-cursor: 3.1.0 cli-spinners: 2.6.1 @@ -17818,7 +17850,6 @@ packages: resolution: {integrity: sha512-/JCYSgL/QeXXsq0Lv/7kOZfqvof7vyzHWfyNQPt3c6vc73mU4WRyT8RJ6ZH5Ci08vUOqXwk7jkZy6BycHTDD9w==} dependencies: browserslist: 4.24.0 - dev: true /reduce-configs@1.1.0: resolution: {integrity: sha512-DQxy6liNadHfrLahZR7lMdc227NYVaQZhY5FMsxLEjX8X0SCuH+ESHSLCoz2yDZFq1/CLMDOAHdsEHwOEXKtvg==} @@ -18230,8 +18261,8 @@ packages: dev: true optional: true - /sass-embedded-android-arm64@1.82.0: - resolution: {integrity: sha512-bldHMs02QQWXsgHUZRgolNnZdMjN6XHvmUYoRkzmFq7lsvtLU6SJg2S1Wa9IZJs9jRWdTmOgA6YibSf3pROyFQ==} + /sass-embedded-android-arm64@1.83.0: + resolution: {integrity: sha512-GBiCvM4a2rkWBLdYDxI6XYnprfk5U5c81g69RC2X6kqPuzxzx8qTArQ9M6keFK4+iDQ5N9QTwFCr0KbZTn+ZNQ==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [android] @@ -18247,8 +18278,8 @@ packages: dev: true optional: true - /sass-embedded-android-arm@1.82.0: - resolution: {integrity: sha512-ttGMvWnA/5TYdZTjr5fWHDbb9nZgKipHKCc9zZQRF5HjUydOYWKNqmAJHQtbFWaq35kd5qn6yiE73IJN6eJ6wA==} + /sass-embedded-android-arm@1.83.0: + resolution: {integrity: sha512-uwFSXzJlfbd4Px189xE5l+cxN8+TQpXdQgJec7TIrb4HEY7imabtpYufpVdqUVwT1/uiis5V4+qIEC4Vl5XObQ==} engines: {node: '>=14.0.0'} cpu: [arm] os: [android] @@ -18264,8 +18295,8 @@ packages: dev: true optional: true - /sass-embedded-android-ia32@1.82.0: - resolution: {integrity: sha512-FUJOnxw8IYKuYuxxiOkk6QXle8/yQFtKjnuSAJuZ5ZpLVMcSZzLc3SWOtuEXYx5iSAfJCO075o2ZoG/pPrJ9aw==} + /sass-embedded-android-ia32@1.83.0: + resolution: {integrity: sha512-5ATPdGo2SICqAhiJl/Z8KQ23zH4sGgobGgux0TnrNtt83uHZ+r+To/ubVJ7xTkZxed+KJZnIpolGD8dQyQqoTg==} engines: {node: '>=14.0.0'} cpu: [ia32] os: [android] @@ -18281,8 +18312,8 @@ packages: dev: true optional: true - /sass-embedded-android-riscv64@1.82.0: - resolution: {integrity: sha512-rd+vc+sxJxNnbhaubiIJmnb1b3FvC9wxCIq8spstopbO7o1uufvBBDeRoFSJaN+7oNhamzjlYGdu6aQoQNs3+A==} + /sass-embedded-android-riscv64@1.83.0: + resolution: {integrity: sha512-aveknUOB8GZewOzVn2Uwk+DKcncTR50Q6vtzslNMGbYnxtgQNHzy8A1qVEviNUruex+pHofppeMK4iMPFAbiEQ==} engines: {node: '>=14.0.0'} cpu: [riscv64] os: [android] @@ -18298,8 +18329,8 @@ packages: dev: true optional: true - /sass-embedded-android-x64@1.82.0: - resolution: {integrity: sha512-EVlybGTgJ8wNLyWj8RUatPXSnmIcvCsx3EfsRfBfhGihLbn4NNpavYO9QsvZzI2XWbJqHLBCd+CvkTcDw/TaSQ==} + /sass-embedded-android-x64@1.83.0: + resolution: {integrity: sha512-WqIay/72ncyf9Ph4vS742J3a73wZihWmzFUwpn1OD6lme1Aj4eWzWIve5IVnlTEJgcZcDHu6ECID9IZgehJKoA==} engines: {node: '>=14.0.0'} cpu: [x64] os: [android] @@ -18315,8 +18346,8 @@ packages: dev: true optional: true - /sass-embedded-darwin-arm64@1.82.0: - resolution: {integrity: sha512-LvdJPojjKlNGYOB0nSUR/ZtMDuAF4puspHlwK42aA/qK292bfSkMUKZPPapB2aSRwccc/ieBq5fI7n/WHrOCVw==} + /sass-embedded-darwin-arm64@1.83.0: + resolution: {integrity: sha512-XQl9QqgxFFIPm/CzHhmppse5o9ocxrbaAdC2/DAnlAqvYWBBtgFqPjGoYlej13h9SzfvNoogx+y9r+Ap+e+hYg==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [darwin] @@ -18332,8 +18363,8 @@ packages: dev: true optional: true - /sass-embedded-darwin-x64@1.82.0: - resolution: {integrity: sha512-6LfnD6YmG1aBfd3ReqMOJDb6Pg2Z/hmlJB7nU+Lb3E+hCNjAZAgeUHQxU/Pm1eIqJJTU/h4ib5QP0Pt9O8yVnw==} + /sass-embedded-darwin-x64@1.83.0: + resolution: {integrity: sha512-ERQ7Tvp1kFOW3ux4VDFIxb7tkYXHYc+zJpcrbs0hzcIO5ilIRU2tIOK1OrNwrFO6Qxyf7AUuBwYKLAtIU/Nz7g==} engines: {node: '>=14.0.0'} cpu: [x64] os: [darwin] @@ -18349,8 +18380,8 @@ packages: dev: true optional: true - /sass-embedded-linux-arm64@1.82.0: - resolution: {integrity: sha512-590/y0HJr/JiyxaqgR7Xf9P20BIhJ+zhB/afAnVuZe/4lEfCpTyM5xMe2+sKLsqtrVyzs9Zm/M4S4ASUOPCggA==} + /sass-embedded-linux-arm64@1.83.0: + resolution: {integrity: sha512-syEAVTJt4qhaMLxrSwOWa46zdqHJdnqJkLUK+t9aCr8xqBZLPxSUeIGji76uOehQZ1C+KGFj6n9xstHN6wzOJw==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [linux] @@ -18366,8 +18397,8 @@ packages: dev: true optional: true - /sass-embedded-linux-arm@1.82.0: - resolution: {integrity: sha512-ozjdC5rWzyi5Vo300I4tVZzneXOTQUiaxOr7DjtN26HuFaGAGCGmvThh2BRV4RvySg++5H9rdFu+VgyUQ5iukw==} + /sass-embedded-linux-arm@1.83.0: + resolution: {integrity: sha512-baG9RYBJxUFmqwDNC9h9ZFElgJoyO3jgHGjzEZ1wHhIS9anpG+zZQvO8bHx3dBpKEImX+DBeLX+CxsFR9n81gQ==} engines: {node: '>=14.0.0'} cpu: [arm] os: [linux] @@ -18383,8 +18414,8 @@ packages: dev: true optional: true - /sass-embedded-linux-ia32@1.82.0: - resolution: {integrity: sha512-hpc4acZ3UTjjJ3Q/GUXqQOCSml6AFKaku0HMawra9bKyRmOpxn8V5hqgXeOWVjK2oQzCmCnJvwKoQUP+S/SIYQ==} + /sass-embedded-linux-ia32@1.83.0: + resolution: {integrity: sha512-RRBxQxMpoxu5+XcSSc6QR/o9asEwUzR8AbCS83RaXcdTIHTa/CccQsiAoDDoPlRsMTLqnzs0LKL4CfOsf7zBbA==} engines: {node: '>=14.0.0'} cpu: [ia32] os: [linux] @@ -18400,8 +18431,8 @@ packages: dev: true optional: true - /sass-embedded-linux-musl-arm64@1.82.0: - resolution: {integrity: sha512-bc2MUSMv/jabnNGEyKP2jQAYZoEzTT/c633W6QoeSEWETGCuTNjaHvWWE6qSI6/UfRg1EpuV1LQA2jPMzZfv/w==} + /sass-embedded-linux-musl-arm64@1.83.0: + resolution: {integrity: sha512-Y7juhPHClUO2H5O+u+StRy6SEAcwZ+hTEk5WJdEmo1Bb1gDtfHvJaWB/iFZJ2tW0W1e865AZeUrC4OcOFjyAQA==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [linux] @@ -18417,8 +18448,8 @@ packages: dev: true optional: true - /sass-embedded-linux-musl-arm@1.82.0: - resolution: {integrity: sha512-R5PQmY/I+GSoMtfLo8GgHkvF/q6x6y8VNM7yu/Ac1mJj86n48VFi29W1HfY2496+Q6cpAq7toobDj7YfldIdVA==} + /sass-embedded-linux-musl-arm@1.83.0: + resolution: {integrity: sha512-Yc7u2TelCfBab+PRob9/MNJFh3EooMiz4urvhejXkihTiKSHGCv5YqDdtWzvyb9tY2Jb7YtYREVuHwfdVn3dTQ==} engines: {node: '>=14.0.0'} cpu: [arm] os: [linux] @@ -18434,8 +18465,8 @@ packages: dev: true optional: true - /sass-embedded-linux-musl-ia32@1.82.0: - resolution: {integrity: sha512-ZQKCFKm5TBcJ19UG6uUQmIKfVCJIWMb7e1a93lGeujSb9gyKF5Fb6MN3tuExoT7iFK8zU0Z9iyHqh93F58lcCw==} + /sass-embedded-linux-musl-ia32@1.83.0: + resolution: {integrity: sha512-arQeYwGmwXV8byx5G1PtSzZWW1jbkfR5qrIHMEbTFSAvAxpqjgSvCvrHMOFd73FcMxVaYh4BX9LQNbKinkbEdg==} engines: {node: '>=14.0.0'} cpu: [ia32] os: [linux] @@ -18451,8 +18482,8 @@ packages: dev: true optional: true - /sass-embedded-linux-musl-riscv64@1.82.0: - resolution: {integrity: sha512-5meSU8BHFeaT09RWfkuUrikRlC+WZcYb9To7MpfV1d9nlD7CZ2xydPExK+mj3DqRuQvTbvhMPcr7f+pHlgHINQ==} + /sass-embedded-linux-musl-riscv64@1.83.0: + resolution: {integrity: sha512-E6uzlIWz59rut+Z3XR6mLG915zNzv07ISvj3GUNZENdHM7dF8GQ//ANoIpl5PljMQKp89GnYdvo6kj2gnaBf/g==} engines: {node: '>=14.0.0'} cpu: [riscv64] os: [linux] @@ -18468,8 +18499,8 @@ packages: dev: true optional: true - /sass-embedded-linux-musl-x64@1.82.0: - resolution: {integrity: sha512-ASLAMfjWv7YEPBvEOVlb3zzHq8l4Y9Eh4x3m7B1dNauGVbO11Yng5cPCX/XbwGVf30BtE75pwqvV7oXxBtN15w==} + /sass-embedded-linux-musl-x64@1.83.0: + resolution: {integrity: sha512-eAMK6tyGqvqr21r9g8BnR3fQc1rYFj85RGduSQ3xkITZ6jOAnOhuU94N5fwRS852Hpws0lXhET+7JHXgg3U18w==} engines: {node: '>=14.0.0'} cpu: [x64] os: [linux] @@ -18485,8 +18516,8 @@ packages: dev: true optional: true - /sass-embedded-linux-riscv64@1.82.0: - resolution: {integrity: sha512-qWvRDXCXH3GzD8OcP0ntd8gBTK3kZyUeyXmxQDZyEtMAM4STC2Tn7+5+2JYYHlppzqWnZPFBNESvpKeOtHaBBw==} + /sass-embedded-linux-riscv64@1.83.0: + resolution: {integrity: sha512-Ojpi78pTv02sy2fUYirRGXHLY3fPnV/bvwuC2i5LwPQw2LpCcFyFTtN0c5h4LJDk9P6wr+/ZB/JXU8tHIOlK+Q==} engines: {node: '>=14.0.0'} cpu: [riscv64] os: [linux] @@ -18502,8 +18533,8 @@ packages: dev: true optional: true - /sass-embedded-linux-x64@1.82.0: - resolution: {integrity: sha512-AmRaHqShztwfep+M4NagdGaY7fTyWGSOM3k4Z/dd7q4nZclXbALLqNJtKx8xOM7A41LHYJ9zDpIBVRkrh0PzTA==} + /sass-embedded-linux-x64@1.83.0: + resolution: {integrity: sha512-3iLjlXdoPfgZRtX4odhRvka1BQs5mAXqfCtDIQBgh/o0JnGPzJIWWl9bYLpHxK8qb+uyVBxXYgXpI0sCzArBOw==} engines: {node: '>=14.0.0'} cpu: [x64] os: [linux] @@ -18519,8 +18550,8 @@ packages: dev: true optional: true - /sass-embedded-win32-arm64@1.82.0: - resolution: {integrity: sha512-zL9JDQZHXHSGAZe5DqSrR86wMHbm9QPziU4/3hoIG+99StuS74CuV42+hw/+FXXBkXMWbjKWsyF/HZt+I/wJuw==} + /sass-embedded-win32-arm64@1.83.0: + resolution: {integrity: sha512-iOHw/8/t2dlTW3lOFwG5eUbiwhEyGWawivlKWJ8lkXH7fjMpVx2VO9zCFAm8RvY9xOHJ9sf1L7g5bx3EnNP9BQ==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [win32] @@ -18536,8 +18567,8 @@ packages: dev: true optional: true - /sass-embedded-win32-ia32@1.82.0: - resolution: {integrity: sha512-xE+AzLquCkFPnnpo0NHjQdLRIhG1bVs42xIKx42aUbVLYKkBDvbBGpw6EtTscRMyvcjoOqGH5saRvSFComUQcw==} + /sass-embedded-win32-ia32@1.83.0: + resolution: {integrity: sha512-2PxNXJ8Pad4geVcTXY4rkyTr5AwbF8nfrCTDv0ulbTvPhzX2mMKEGcBZUXWn5BeHZTBc6whNMfS7d5fQXR9dDQ==} engines: {node: '>=14.0.0'} cpu: [ia32] os: [win32] @@ -18553,8 +18584,8 @@ packages: dev: true optional: true - /sass-embedded-win32-x64@1.82.0: - resolution: {integrity: sha512-cEgfOQG5womOzzk16ReTv2dxPq5BG16LgLUold/LH9IZH86u4E/MN7Fspf4RWeEJ2EcLdew9QYSC2YWs1l98dQ==} + /sass-embedded-win32-x64@1.83.0: + resolution: {integrity: sha512-muBXkFngM6eLTNqOV0FQi7Dv9s+YRQ42Yem26mosdan/GmJQc81deto6uDTgrYn+bzFNmiXcOdfm+0MkTWK3OQ==} engines: {node: '>=14.0.0'} cpu: [x64] os: [win32] @@ -18597,8 +18628,8 @@ packages: sass-embedded-win32-x64: 1.81.0 dev: true - /sass-embedded@1.82.0: - resolution: {integrity: sha512-v13sRVVZtWAQLpAGTz5D8hy+oyNKRHao5tKVc/P6AMqSP+jDM8X6GkEpL0jfbu3MaN2/hAQsd4Qx14GG1u0prQ==} + /sass-embedded@1.83.0: + resolution: {integrity: sha512-/8cYZeL39evUqe0o//193na51Q1VWZ61qhxioQvLJwOtWIrX+PgNhCyD8RSuTtmzc4+6+waFZf899bfp/MCUwA==} engines: {node: '>=16.0.0'} hasBin: true dependencies: @@ -18611,26 +18642,26 @@ packages: sync-child-process: 1.0.2 varint: 6.0.0 optionalDependencies: - sass-embedded-android-arm: 1.82.0 - sass-embedded-android-arm64: 1.82.0 - sass-embedded-android-ia32: 1.82.0 - sass-embedded-android-riscv64: 1.82.0 - sass-embedded-android-x64: 1.82.0 - sass-embedded-darwin-arm64: 1.82.0 - sass-embedded-darwin-x64: 1.82.0 - sass-embedded-linux-arm: 1.82.0 - sass-embedded-linux-arm64: 1.82.0 - sass-embedded-linux-ia32: 1.82.0 - sass-embedded-linux-musl-arm: 1.82.0 - sass-embedded-linux-musl-arm64: 1.82.0 - sass-embedded-linux-musl-ia32: 1.82.0 - sass-embedded-linux-musl-riscv64: 1.82.0 - sass-embedded-linux-musl-x64: 1.82.0 - sass-embedded-linux-riscv64: 1.82.0 - sass-embedded-linux-x64: 1.82.0 - sass-embedded-win32-arm64: 1.82.0 - sass-embedded-win32-ia32: 1.82.0 - sass-embedded-win32-x64: 1.82.0 + sass-embedded-android-arm: 1.83.0 + sass-embedded-android-arm64: 1.83.0 + sass-embedded-android-ia32: 1.83.0 + sass-embedded-android-riscv64: 1.83.0 + sass-embedded-android-x64: 1.83.0 + sass-embedded-darwin-arm64: 1.83.0 + sass-embedded-darwin-x64: 1.83.0 + sass-embedded-linux-arm: 1.83.0 + sass-embedded-linux-arm64: 1.83.0 + sass-embedded-linux-ia32: 1.83.0 + sass-embedded-linux-musl-arm: 1.83.0 + sass-embedded-linux-musl-arm64: 1.83.0 + sass-embedded-linux-musl-ia32: 1.83.0 + sass-embedded-linux-musl-riscv64: 1.83.0 + sass-embedded-linux-musl-x64: 1.83.0 + sass-embedded-linux-riscv64: 1.83.0 + sass-embedded-linux-x64: 1.83.0 + sass-embedded-win32-arm64: 1.83.0 + sass-embedded-win32-ia32: 1.83.0 + sass-embedded-win32-x64: 1.83.0 /sax@1.4.1: resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} @@ -19705,7 +19736,7 @@ packages: dependencies: chalk: 4.1.2 enhanced-resolve: 5.12.0 - micromatch: 4.0.8 + micromatch: 4.0.7 semver: 7.6.3 typescript: 5.5.4 webpack: 5.95.0(esbuild@0.17.19) @@ -19816,8 +19847,8 @@ packages: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} - /type-fest@4.30.0: - resolution: {integrity: sha512-G6zXWS1dLj6eagy6sVhOMQiLtJdxQBHIA9Z6HFUNLOlr6MFOgzV8wvmidtPONfPtEUv0uZsy77XJNzTAfwPDaA==} + /type-fest@4.30.2: + resolution: {integrity: sha512-UJShLPYi1aWqCdq9HycOL/gwsuqda1OISdBO3t8RlXQC4QvtuIz4b5FCfe2dQIWEpmlRExKmcTBfP1r9bhY7ig==} engines: {node: '>=16'} dev: true