From 1aed597ca28e066274f5462f08d0a9059cf18b4c Mon Sep 17 00:00:00 2001
From: easy1090 <752355956@qq.com>
Date: Mon, 4 Dec 2023 21:29:50 +0800
Subject: [PATCH] feat: add overall page
feat: add overall page
---
e2e/package.json | 2 +-
examples/webpack-minimal/package.json | 35 +
examples/webpack-minimal/src/html.ts | 15 +
examples/webpack-minimal/src/index.ts | 10 +
examples/webpack-minimal/src/types.ts | 1 +
examples/webpack-minimal/src/utils.ts | 7 +
examples/webpack-minimal/src/utils/index.ts | 14 +
examples/webpack-minimal/src/utils2.ts | 5 +
examples/webpack-minimal/src/utils3.ts | 3 +
examples/webpack-minimal/src/utils4.ts | 1 +
examples/webpack-minimal/src/utils5.ts | 1 +
examples/webpack-minimal/src/utils6.ts | 1 +
examples/webpack-minimal/tsconfig.json | 9 +
examples/webpack-minimal/webpack.config.ts | 45 +
package.json | 3 +-
packages/client/CHANGELOG.md | 1 +
packages/client/LICENSE | 21 +
packages/client/README.md | 15 +
packages/client/config/constants.ts | 19 +
packages/client/config/modern.config.ts | 176 +
packages/client/package.json | 50 +
.../src/common/imgs/connection-point.svg | 1 +
packages/client/src/common/imgs/icon.svg | 21 +
.../src/common/imgs/webpack.svg | 0
.../src/common/styles/base.scss | 0
.../src/common/styles/theme.scss | 0
packages/client/src/index.tsx | 13 +
packages/client/src/main.tsx | 147 +
packages/client/src/router.tsx | 15 +
.../client/src/typings/assetsDefinition.d.ts | 16 +
packages/client/tsconfig.json | 14 +
packages/components/CHANGELOG.md | 2 +-
packages/components/modern.config.ts | 2 +-
packages/components/package.json | 57 +-
.../src/components/Alert/package-relation.tsx | 1 -
.../src/components/Configuration/index.tsx | 1 +
.../src/components/Configuration/webpack.tsx | 77 +
.../src/components/Layout/builder-select.tsx | 57 +
.../src/components/Layout/header.scss | 17 +
.../src/components/Layout/header.tsx | 127 +
.../src/components/Layout/index.tsx | 36 +
.../src/components/Layout/menus.tsx | 83 +
.../src/components/Layout/progress.tsx | 50 +
.../src/components/Manifest/data.tsx | 2 +-
.../components/src/components/Opener/code.tsx | 2 +-
.../Overall/NumberButton.module.scss | 73 +
.../src/components/Overall/NumberButton.tsx | 39 +
.../src/components/Overall/bundle.module.scss | 0
.../src/components/Overall/bundle.tsx | 184 +
.../src/components/Overall/card.module.scss | 7 +
.../components/Overall/compile.module.scss | 6 +
.../src/components/Overall/compile.tsx | 93 +
.../src/components/Overall/index.tsx | 3 +
.../src/components/Overall/list.module.scss | 10 +
.../src/components/Overall/project.tsx | 86 +
packages/components/src/components/index.ts | 5 +-
packages/components/src/config.tsx | 2 +-
packages/components/src/index.ts | 5 +-
.../components/src/pages/Overall/constants.ts | 5 +
.../components/src/pages/Overall/index.tsx | 43 +
packages/components/src/pages/index.ts | 1 +
.../src/typings/assetsDefinition.d.ts | 15 +
packages/components/src/utils/file.tsx | 7 +-
packages/components/src/utils/hooks.ts | 3 +
.../components/src/{ => utils}/i18n/cn.ts | 0
.../components/src/{ => utils}/i18n/en.ts | 0
.../components/src/{ => utils}/i18n/index.ts | 8 +-
packages/components/src/utils/manifest.tsx | 2 +-
packages/components/src/utils/worker/utils.ts | 1 -
packages/components/tsconfig.json | 2 +-
packages/core/package.json | 2 +-
.../build/module-graph/transform.ts | 4 +-
.../build/module-graph/treeShaking.ts | 2 +-
.../build-utils/build/module-graph/utils.ts | 2 +-
.../build/module-graph/webpack/transform.ts | 2 +-
.../common/chunks/assetsContent.ts | 2 +-
.../common/chunks/assetsModules.ts | 2 +-
.../common/chunks/chunkTransform.ts | 2 +-
.../common/module-graph/transform.ts | 2 +-
.../build-utils/common/module-graph/utils.ts | 2 +-
.../common/trans-utils/transStats.ts | 2 +-
.../build-utils/common/webpack/compatible.ts | 2 +-
.../core/src/inner-plugins/loaders/proxy.ts | 7 +-
.../core/src/inner-plugins/plugins/base.ts | 2 +-
.../core/src/inner-plugins/utils/plugin.ts | 2 +-
packages/core/src/inner-plugins/utils/sdk.ts | 2 +-
packages/core/src/types/plugin.ts | 4 +-
packages/core/tests/build/utils.ts | 2 +-
packages/core/tests/common/utils.ts | 2 +-
packages/graph/CHANGELOG.md | 1 +
packages/graph/LICENSE | 21 +
packages/graph/README.md | 15 +
packages/graph/modern.config.ts | 3 +
packages/graph/package.json | 42 +
.../src/graph/chunk-graph/asset.ts | 0
.../src/graph/chunk-graph/chunk.ts | 0
.../src/graph/chunk-graph/entrypoint.ts | 0
.../src/graph/chunk-graph/graph.ts | 0
.../src/graph/chunk-graph/index.ts | 0
packages/{sdk => graph}/src/graph/index.ts | 0
.../src/graph/module-graph/dependency.ts | 0
.../src/graph/module-graph/graph.ts | 0
.../src/graph/module-graph/index.ts | 0
.../src/graph/module-graph/module.ts | 0
.../src/graph/module-graph/statement.ts | 0
.../graph/module-graph/tree-shaking/export.ts | 0
.../graph/module-graph/tree-shaking/index.ts | 0
.../graph/module-graph/tree-shaking/module.ts | 0
.../module-graph/tree-shaking/sideEffect.ts | 0
.../graph/module-graph/tree-shaking/types.ts | 0
.../module-graph/tree-shaking/variable.ts | 0
.../src/graph/module-graph/types.ts | 0
.../src/graph/module-graph/utils.ts | 0
.../src/graph/package-graph/dependency.ts | 0
.../src/graph/package-graph/graph.ts | 0
.../src/graph/package-graph/index.ts | 0
.../src/graph/package-graph/package.ts | 0
.../src/graph/package-graph/types.ts | 0
.../src/graph/package-graph/utils.ts | 0
packages/graph/src/index.ts | 1 +
.../__snapshots__/module-graph.spec.ts.snap | 1253 +++
.../tests/fixture/module-graph-basic.json | 3951 ++++++++++
packages/graph/tests/module-graph.spec.ts | 95 +
packages/graph/tests/utils.spec.ts | 51 +
packages/graph/tests/utils.ts | 6 +
packages/graph/tsconfig.json | 14 +
packages/rspack-plugin/package.json | 7 +-
packages/rspack-plugin/src/plugin.ts | 4 +-
packages/sdk/package.json | 33 +-
packages/sdk/src/index.ts | 3 +-
packages/sdk/src/sdk/sdk/webpack.ts | 2 +-
packages/sdk/src/sdk/server/apis/renderer.ts | 6 +-
packages/sdk/src/sdk/server/index.ts | 4 +-
packages/webpack-plugin/package.json | 10 +-
packages/webpack-plugin/src/multiple.ts | 2 +-
packages/webpack-plugin/src/plugin.ts | 4 +-
.../webpack-plugin/src/plugins/resolver.ts | 2 +-
pnpm-lock.yaml | 6702 ++++++++++++++++-
138 files changed, 13648 insertions(+), 406 deletions(-)
create mode 100644 examples/webpack-minimal/package.json
create mode 100644 examples/webpack-minimal/src/html.ts
create mode 100644 examples/webpack-minimal/src/index.ts
create mode 100644 examples/webpack-minimal/src/types.ts
create mode 100644 examples/webpack-minimal/src/utils.ts
create mode 100644 examples/webpack-minimal/src/utils/index.ts
create mode 100644 examples/webpack-minimal/src/utils2.ts
create mode 100644 examples/webpack-minimal/src/utils3.ts
create mode 100644 examples/webpack-minimal/src/utils4.ts
create mode 100644 examples/webpack-minimal/src/utils5.ts
create mode 100644 examples/webpack-minimal/src/utils6.ts
create mode 100644 examples/webpack-minimal/tsconfig.json
create mode 100644 examples/webpack-minimal/webpack.config.ts
create mode 100644 packages/client/CHANGELOG.md
create mode 100644 packages/client/LICENSE
create mode 100644 packages/client/README.md
create mode 100644 packages/client/config/constants.ts
create mode 100644 packages/client/config/modern.config.ts
create mode 100644 packages/client/package.json
create mode 100644 packages/client/src/common/imgs/connection-point.svg
create mode 100644 packages/client/src/common/imgs/icon.svg
rename packages/{components => client}/src/common/imgs/webpack.svg (100%)
rename packages/{components => client}/src/common/styles/base.scss (100%)
rename packages/{components => client}/src/common/styles/theme.scss (100%)
create mode 100644 packages/client/src/index.tsx
create mode 100644 packages/client/src/main.tsx
create mode 100644 packages/client/src/router.tsx
create mode 100644 packages/client/src/typings/assetsDefinition.d.ts
create mode 100644 packages/client/tsconfig.json
create mode 100644 packages/components/src/components/Configuration/index.tsx
create mode 100644 packages/components/src/components/Configuration/webpack.tsx
create mode 100644 packages/components/src/components/Layout/builder-select.tsx
create mode 100644 packages/components/src/components/Layout/header.scss
create mode 100644 packages/components/src/components/Layout/header.tsx
create mode 100644 packages/components/src/components/Layout/index.tsx
create mode 100644 packages/components/src/components/Layout/menus.tsx
create mode 100644 packages/components/src/components/Layout/progress.tsx
create mode 100644 packages/components/src/components/Overall/NumberButton.module.scss
create mode 100644 packages/components/src/components/Overall/NumberButton.tsx
create mode 100644 packages/components/src/components/Overall/bundle.module.scss
create mode 100644 packages/components/src/components/Overall/bundle.tsx
create mode 100644 packages/components/src/components/Overall/card.module.scss
create mode 100644 packages/components/src/components/Overall/compile.module.scss
create mode 100644 packages/components/src/components/Overall/compile.tsx
create mode 100644 packages/components/src/components/Overall/index.tsx
create mode 100644 packages/components/src/components/Overall/list.module.scss
create mode 100644 packages/components/src/components/Overall/project.tsx
create mode 100644 packages/components/src/pages/Overall/constants.ts
create mode 100644 packages/components/src/pages/Overall/index.tsx
create mode 100644 packages/components/src/pages/index.ts
create mode 100644 packages/components/src/typings/assetsDefinition.d.ts
rename packages/components/src/{ => utils}/i18n/cn.ts (100%)
rename packages/components/src/{ => utils}/i18n/en.ts (100%)
rename packages/components/src/{ => utils}/i18n/index.ts (87%)
create mode 100644 packages/graph/CHANGELOG.md
create mode 100644 packages/graph/LICENSE
create mode 100644 packages/graph/README.md
create mode 100644 packages/graph/modern.config.ts
create mode 100644 packages/graph/package.json
rename packages/{sdk => graph}/src/graph/chunk-graph/asset.ts (100%)
rename packages/{sdk => graph}/src/graph/chunk-graph/chunk.ts (100%)
rename packages/{sdk => graph}/src/graph/chunk-graph/entrypoint.ts (100%)
rename packages/{sdk => graph}/src/graph/chunk-graph/graph.ts (100%)
rename packages/{sdk => graph}/src/graph/chunk-graph/index.ts (100%)
rename packages/{sdk => graph}/src/graph/index.ts (100%)
rename packages/{sdk => graph}/src/graph/module-graph/dependency.ts (100%)
rename packages/{sdk => graph}/src/graph/module-graph/graph.ts (100%)
rename packages/{sdk => graph}/src/graph/module-graph/index.ts (100%)
rename packages/{sdk => graph}/src/graph/module-graph/module.ts (100%)
rename packages/{sdk => graph}/src/graph/module-graph/statement.ts (100%)
rename packages/{sdk => graph}/src/graph/module-graph/tree-shaking/export.ts (100%)
rename packages/{sdk => graph}/src/graph/module-graph/tree-shaking/index.ts (100%)
rename packages/{sdk => graph}/src/graph/module-graph/tree-shaking/module.ts (100%)
rename packages/{sdk => graph}/src/graph/module-graph/tree-shaking/sideEffect.ts (100%)
rename packages/{sdk => graph}/src/graph/module-graph/tree-shaking/types.ts (100%)
rename packages/{sdk => graph}/src/graph/module-graph/tree-shaking/variable.ts (100%)
rename packages/{sdk => graph}/src/graph/module-graph/types.ts (100%)
rename packages/{sdk => graph}/src/graph/module-graph/utils.ts (100%)
rename packages/{sdk => graph}/src/graph/package-graph/dependency.ts (100%)
rename packages/{sdk => graph}/src/graph/package-graph/graph.ts (100%)
rename packages/{sdk => graph}/src/graph/package-graph/index.ts (100%)
rename packages/{sdk => graph}/src/graph/package-graph/package.ts (100%)
rename packages/{sdk => graph}/src/graph/package-graph/types.ts (100%)
rename packages/{sdk => graph}/src/graph/package-graph/utils.ts (100%)
create mode 100644 packages/graph/src/index.ts
create mode 100644 packages/graph/tests/__snapshots__/module-graph.spec.ts.snap
create mode 100644 packages/graph/tests/fixture/module-graph-basic.json
create mode 100644 packages/graph/tests/module-graph.spec.ts
create mode 100644 packages/graph/tests/utils.spec.ts
create mode 100644 packages/graph/tests/utils.ts
create mode 100644 packages/graph/tsconfig.json
diff --git a/e2e/package.json b/e2e/package.json
index 24859d3de..c8b8c5575 100644
--- a/e2e/package.json
+++ b/e2e/package.json
@@ -29,6 +29,6 @@
"@types/webpack": "5.28.0",
"fast-glob": "^3.3.1",
"playwright": "1.33.0",
- "typescript": "^5.3.0"
+ "typescript": "^5.2.2"
}
}
diff --git a/examples/webpack-minimal/package.json b/examples/webpack-minimal/package.json
new file mode 100644
index 000000000..b1310e605
--- /dev/null
+++ b/examples/webpack-minimal/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "@example/doctor-webpack-minimal",
+ "version": "0.0.1",
+ "description": "",
+ "files": [
+ "dist",
+ "src",
+ "package.json"
+ ],
+ "scripts": {
+ "compile": "node -r tsm ./node_modules/webpack/bin/webpack.js -c webpack.config.ts",
+ "build:analysis": "ENABLE_CLIENT_SERVER=true npm run compile"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/highlight": "7.18.6",
+ "chalk": "4.1.2",
+ "htmlparser2": "7.2.0"
+ },
+ "devDependencies": {
+ "@types/node": "14.18.26",
+ "@rsdoctor/types": "workspace:*",
+ "@rsdoctor/webpack-plugin": "workspace:*",
+ "bundle-stats": "4.1.7",
+ "eslint": "8.22.0",
+ "ts-loader": "9.4.2",
+ "tslib": "2.4.1",
+ "tsm": "2.3.0",
+ "typescript": "^5.2.2",
+ "webpack": "5.89.0",
+ "webpack-cli": "5.0.0"
+ }
+}
diff --git a/examples/webpack-minimal/src/html.ts b/examples/webpack-minimal/src/html.ts
new file mode 100644
index 000000000..ab1954d07
--- /dev/null
+++ b/examples/webpack-minimal/src/html.ts
@@ -0,0 +1,15 @@
+import Parser from 'htmlparser2';
+
+export function getHtmlText(code: string) {
+ let context = '';
+
+ const parser = new Parser.Parser({
+ ontext(data) {
+ context += data;
+ },
+ });
+
+ parser.write(code);
+
+ return context;
+}
diff --git a/examples/webpack-minimal/src/index.ts b/examples/webpack-minimal/src/index.ts
new file mode 100644
index 000000000..ee34841de
--- /dev/null
+++ b/examples/webpack-minimal/src/index.ts
@@ -0,0 +1,10 @@
+import { Instance } from 'chalk';
+import { highlight } from './utils';
+import { getHtmlText } from './html';
+import { key6 } from './utils2';
+
+const print = new Instance();
+
+print(key6);
+print(getHtmlText('
测试文本
'));
+print(highlight?.('const abc = 123;'));
diff --git a/examples/webpack-minimal/src/types.ts b/examples/webpack-minimal/src/types.ts
new file mode 100644
index 000000000..8f4fb3ec9
--- /dev/null
+++ b/examples/webpack-minimal/src/types.ts
@@ -0,0 +1 @@
+declare module '@babel/highlight';
diff --git a/examples/webpack-minimal/src/utils.ts b/examples/webpack-minimal/src/utils.ts
new file mode 100644
index 000000000..a9ba4c1e4
--- /dev/null
+++ b/examples/webpack-minimal/src/utils.ts
@@ -0,0 +1,7 @@
+import highlight from '@babel/highlight';
+
+export { highlight };
+export const key1 = '123';
+export const key2 = '123';
+
+console.log(key2);
diff --git a/examples/webpack-minimal/src/utils/index.ts b/examples/webpack-minimal/src/utils/index.ts
new file mode 100644
index 000000000..722f7027e
--- /dev/null
+++ b/examples/webpack-minimal/src/utils/index.ts
@@ -0,0 +1,14 @@
+import { Linter } from '@rsdoctor/types';
+
+export function toSeverity(input: Linter.SeverityInput, defaultLevel: Linter.Severity): Linter.Severity {
+ if (input === 'off') {
+ return Linter.Severity.Ignore;
+ }
+
+ if (input === 'on') {
+ return defaultLevel;
+ }
+
+ const key = `${input[0].toUpperCase()}${input.slice(1)}` as Linter.SeverityString;
+ return Linter.Severity[key] as Linter.Severity;
+}
diff --git a/examples/webpack-minimal/src/utils2.ts b/examples/webpack-minimal/src/utils2.ts
new file mode 100644
index 000000000..cdc72e09d
--- /dev/null
+++ b/examples/webpack-minimal/src/utils2.ts
@@ -0,0 +1,5 @@
+export * as utils6 from './utils6';
+
+export * from './utils';
+export * from './utils4';
+export * from './html';
diff --git a/examples/webpack-minimal/src/utils3.ts b/examples/webpack-minimal/src/utils3.ts
new file mode 100644
index 000000000..6b456a203
--- /dev/null
+++ b/examples/webpack-minimal/src/utils3.ts
@@ -0,0 +1,3 @@
+import { key1 } from './utils';
+
+export const key3 = key1 + 1;
diff --git a/examples/webpack-minimal/src/utils4.ts b/examples/webpack-minimal/src/utils4.ts
new file mode 100644
index 000000000..a2c3fd7a9
--- /dev/null
+++ b/examples/webpack-minimal/src/utils4.ts
@@ -0,0 +1 @@
+export * from './utils5';
diff --git a/examples/webpack-minimal/src/utils5.ts b/examples/webpack-minimal/src/utils5.ts
new file mode 100644
index 000000000..eb7070747
--- /dev/null
+++ b/examples/webpack-minimal/src/utils5.ts
@@ -0,0 +1 @@
+export * from './utils6';
diff --git a/examples/webpack-minimal/src/utils6.ts b/examples/webpack-minimal/src/utils6.ts
new file mode 100644
index 000000000..81095b71c
--- /dev/null
+++ b/examples/webpack-minimal/src/utils6.ts
@@ -0,0 +1 @@
+export const key6 = 'key6';
diff --git a/examples/webpack-minimal/tsconfig.json b/examples/webpack-minimal/tsconfig.json
new file mode 100644
index 000000000..d6a1f33f8
--- /dev/null
+++ b/examples/webpack-minimal/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "@rsdoctor/tsconfig/base",
+ "include": ["src", "webpack.config.ts"],
+ "compilerOptions": {
+ "outDir": "dist",
+ "baseUrl": ".",
+ "module": "ESNext"
+ }
+}
diff --git a/examples/webpack-minimal/webpack.config.ts b/examples/webpack-minimal/webpack.config.ts
new file mode 100644
index 000000000..ba9fee363
--- /dev/null
+++ b/examples/webpack-minimal/webpack.config.ts
@@ -0,0 +1,45 @@
+import { resolve } from 'path';
+import { Configuration } from 'webpack';
+import { RsdoctorWebpackPlugin } from '@rsdoctor/webpack-plugin';
+
+const data: Configuration = {
+ entry: './src/index.ts',
+ mode: 'none',
+ module: {
+ rules: [
+ {
+ test: /\.ts$/,
+ loader: 'ts-loader',
+ },
+ ],
+ },
+ resolve: {
+ mainFields: ['browser', 'module', 'main'],
+ extensions: ['.ts', '.js', '.json', '.wasm'],
+ },
+ output: {
+ path: resolve(__dirname, 'dist'),
+ filename: 'minimal.js',
+ },
+ optimization: {
+ concatenateModules: true,
+ usedExports: true,
+ mangleExports: true,
+ providedExports: true,
+ },
+ stats: {
+ assets: true,
+ warnings: true,
+ errors: true,
+ modules: true,
+ colors: true,
+ chunks: true,
+ builtAt: true,
+ hash: true,
+ ids: true,
+ },
+ devtool: 'source-map',
+ plugins: [new RsdoctorWebpackPlugin({ disableClientServer: !process.env.ENABLE_CLIENT_SERVER })],
+};
+
+export default data;
diff --git a/package.json b/package.json
index b5f2414cd..bc78b61eb 100644
--- a/package.json
+++ b/package.json
@@ -32,11 +32,12 @@
"package.json": "pnpm run check-dependency-version"
},
"devDependencies": {
+ "@modern-js/app-tools": "^2.41.0",
"@modern-js/module-tools": "^2.41.0",
"@modern-js/monorepo-tools": "^2.41.0",
"@rsbuild/test-helper": "0.1.5",
- "@rsdoctor/tsconfig": "workspace:*",
"@rsdoctor/test-helper": "workspace:*",
+ "@rsdoctor/tsconfig": "workspace:*",
"check-dependency-version-consistency": "^4.1.0",
"cross-env": "^7.0.3",
"husky": "^8.0.3",
diff --git a/packages/client/CHANGELOG.md b/packages/client/CHANGELOG.md
new file mode 100644
index 000000000..c776b3511
--- /dev/null
+++ b/packages/client/CHANGELOG.md
@@ -0,0 +1 @@
+# @rsdoctor/components
diff --git a/packages/client/LICENSE b/packages/client/LICENSE
new file mode 100644
index 000000000..82d38c25b
--- /dev/null
+++ b/packages/client/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023-present Bytedance, Inc. and its affiliates.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/client/README.md b/packages/client/README.md
new file mode 100644
index 000000000..d1c737adb
--- /dev/null
+++ b/packages/client/README.md
@@ -0,0 +1,15 @@
+# Rsdoctor
+
+Rsdoctor is a tool for diagnosing and analyzing the build process and build artifacts to help developers quickly identify and solve problems.
+
+It also supports Webpack and Rspack builders, as well as various build frameworks, such as Rsbuild.
+
+## Documentation
+
+## Contributing
+
+Please read the [Contributing Guide](https://github.com/web-infra-dev/rsdoctor/blob/main/CONTRIBUTING.md).
+
+## License
+
+Rsdoctor is [MIT licensed](https://github.com/web-infra-dev/rsdoctor/blob/main/LICENSE).
diff --git a/packages/client/config/constants.ts b/packages/client/config/constants.ts
new file mode 100644
index 000000000..548d64347
--- /dev/null
+++ b/packages/client/config/constants.ts
@@ -0,0 +1,19 @@
+import { Constants } from '@rsdoctor/types';
+
+import path from 'path';
+
+export const DistPath = path.resolve(__dirname, '../dist');
+
+export const DistResourcePath = path.resolve(__dirname, '../dist/resource');
+
+export const WebpackDoctorDirPath = path.resolve(__dirname, `../dist/${Constants.DoctorOutputFolder}`);
+
+export const WebpackStatsFilePath = path.resolve(__dirname, '../dist/stats.json');
+
+export const PortForWeb = 8681;
+
+export const PortForCLI = 8123;
+
+export const ClientEntry = path.resolve(__dirname, '../src/index.tsx');
+
+export const RsdoctorWebpackPluginMain = path.resolve(__dirname, '../../webpack-plugin/dist');
diff --git a/packages/client/config/modern.config.ts b/packages/client/config/modern.config.ts
new file mode 100644
index 000000000..4e6c6ecae
--- /dev/null
+++ b/packages/client/config/modern.config.ts
@@ -0,0 +1,176 @@
+import type { Compiler } from 'webpack';
+import { appTools, defineConfig } from '@modern-js/app-tools';
+import NodePolyfillPlugin from 'node-polyfill-webpack-plugin';
+import serve from 'serve-static';
+import path from 'path';
+import fs from 'fs';
+import glob from 'glob';
+import ip from 'ip';
+import {
+ ClientEntry,
+ DistPath,
+ PortForCLI,
+ PortForWeb,
+ RsdoctorWebpackPluginMain,
+ WebpackDoctorDirPath,
+ WebpackStatsFilePath,
+} from './constants';
+
+const { ENABLE_DEVTOOLS_PLUGIN, OFFICAL_PREVIEW_PUBLIC_PATH, OFFICAL_DEMO_MANIFEST_PATH, ENABLE_CLIENT_SERVER } =
+ process.env;
+
+
+export default defineConfig<'webpack'>((env) => {
+ const IS_PRODUCTION = env.env === 'production';
+
+ return {
+ source: {
+ entries: {
+ index: ClientEntry,
+ },
+ disableDefaultEntries: true,
+ define: {
+ 'process.env.NODE_DEBUG': JSON.stringify(false),
+ 'process.env.NODE_ENV': JSON.stringify(env.env),
+ 'process.env.OFFICAL_DEMO_MANIFEST_PATH': JSON.stringify(OFFICAL_DEMO_MANIFEST_PATH),
+ 'process.env.LOCAL_CLI_PORT': JSON.stringify(PortForCLI),
+ },
+ },
+ output: {
+ distPath: {
+ root: path.basename(DistPath),
+ html: 'template',
+ js: 'resource/js',
+ css: 'resource/css',
+ svg: 'resource/svg',
+ font: 'resource/font',
+ image: 'resource/image',
+ media: 'resource/media',
+ },
+ assetPrefix: IS_PRODUCTION
+ ? // 此处不要修改!这里 production 的 publicPath 会和 sdk serve 的路径 以及 轻服务 部署的路径联动。
+ // OFFICAL_PREVIEW_PUBLIC_PATH 是提供给 轻服务 部署后域名关系
+ // "/" 是提供给 sdk 使用的
+ OFFICAL_PREVIEW_PUBLIC_PATH?.replace(/\/resource$/, '') || '/'
+ : '/',
+ cleanDistPath: IS_PRODUCTION,
+ disableTsChecker: !IS_PRODUCTION,
+ disableSourceMap: true,
+ },
+ performance: {
+ chunkSplit: {
+ strategy: 'custom',
+ splitChunks: {
+ cacheGroups: {
+ shadow: {
+ test: /node_modules\/@byted-shadow\/*/,
+ name: 'shadow',
+ chunks: 'all',
+ },
+ react: {
+ test: /node_modules\/react-*/,
+ name: 'react',
+ chunks: 'all',
+ maxSize: 1000000,
+ minSize: 200000,
+ },
+ monaco: {
+ test: /node_modules\/monaco-editor\/*/,
+ name: 'monaco',
+ chunks: 'all',
+ maxSize: 1000000,
+ minSize: 500000,
+ },
+ },
+ }
+ },
+ },
+ tools: {
+ tsChecker: IS_PRODUCTION ? {} : undefined,
+ webpackChain: (chainConfig) => {
+ chainConfig.resolve.mainFields.merge(['browser', 'module', 'main']);
+ chainConfig.plugin('NodePolyfillPlugin').use(NodePolyfillPlugin, [{ excludeAliases: ['console'] }]);
+
+ if (ENABLE_DEVTOOLS_PLUGIN) {
+ const { RsdoctorWebpackPlugin } =
+ require(RsdoctorWebpackPluginMain) as typeof import('../../webpack-plugin/dist');
+
+ class StatsWriter {
+ apply(compiler: Compiler) {
+ compiler.hooks.done.tapPromise({ name: 'webpack-stats-json-writer', stage: 99999 }, async (stats) => {
+ const json = stats.toJson({
+ all: false,
+ assets: true,
+ chunks: true,
+ modules: true,
+ builtAt: true,
+ hash: true,
+ ids: true,
+ version: true,
+ entrypoints: true,
+ });
+ await fs.promises.writeFile(WebpackStatsFilePath, JSON.stringify(json, null, 2), 'utf-8');
+ });
+ }
+ }
+
+ chainConfig.plugin('stats-writer').use(StatsWriter);
+ chainConfig.plugin('rsdoctor').use(RsdoctorWebpackPlugin, [
+ {
+ disableClientServer: !ENABLE_CLIENT_SERVER,
+ features: {
+ loader: true,
+ plugins: true,
+ resolver: true,
+ bundle: true,
+ treeShaking: true,
+ },
+ },
+ ]);
+ }
+ },
+ devServer: {
+ historyApiFallback: true,
+ setupMiddlewares: [
+ (middlewares) => {
+ if (fs.existsSync(WebpackDoctorDirPath)) {
+ const fn = serve(WebpackDoctorDirPath, {
+ index: false,
+ setHeaders(res) {
+ res.setHeader('Content-Type', 'text/plain; charset=utf-8');
+ },
+ });
+ middlewares.push(fn);
+ }
+ },
+ ],
+ },
+ },
+ dev: {
+ startUrl: ENABLE_CLIENT_SERVER ? undefined : `http://${ip.address()}:${PortForWeb}`,
+ port: PortForWeb,
+ },
+ plugins: [
+ appTools({}),
+ {
+ // use to move html files to correct path, because of html dest path will match in TLB system.
+ name: 'MOVE_OUTPUT_HTML_FILES',
+ setup() {
+ return {
+ afterBuild() {
+ const dir = path.resolve(DistPath, './template');
+ const htmls = glob.sync('**/**.html', { cwd: dir });
+
+ htmls.forEach((html) => {
+ const prevPath = path.resolve(dir, html);
+ const prevDir = path.dirname(prevPath);
+ const curtPath = path.resolve(dir, path.basename(prevPath));
+ fs.copyFileSync(prevPath, curtPath);
+ // fs.rmSync(prevDir, { recursive: true });
+ });
+ },
+ };
+ },
+ },
+ ],
+}});
\ No newline at end of file
diff --git a/packages/client/package.json b/packages/client/package.json
new file mode 100644
index 000000000..1c115a66e
--- /dev/null
+++ b/packages/client/package.json
@@ -0,0 +1,50 @@
+{
+ "name": "@rsdoctor/client",
+ "version": "0.0.1",
+ "main": "dist/template/index.html",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/web-infra-dev/rsdoctor",
+ "directory": "packages/client"
+ },
+ "scripts": {
+ "dev": "modern dev -c './config/modern.config.ts'",
+ "start:analysis": "ENABLE_DEVTOOLS_PLUGIN=true ENABLE_CLIENT_SERVER=true modern dev -c './config/modern.config.ts'",
+ "build": "modern build -c './config/modern.config.ts'",
+ "build:analysis": "ENABLE_DEVTOOLS_PLUGIN=true DEVTOOLS_DEV=true modern build -c './config/modern.config.ts'"
+ },
+ "lint-staged": {
+ "*.{js,jsx,ts,tsx,mjs,cjs}": [
+ "node --max_old_space_size=8192 ./node_modules/eslint/bin/eslint.js --fix --color --cache --quiet"
+ ]
+ },
+ "eslintIgnore": [
+ "node_modules/",
+ "dist/"
+ ],
+ "devDependencies": {
+ "@modern-js/runtime": "^2.41.0",
+ "@rsdoctor/components": "workspace:*",
+ "@rsdoctor/types": "workspace:*",
+ "@types/node": "^16",
+ "@types/react": "^18",
+ "@types/serve-static": "1.15.0",
+ "@types/webpack": "5.28.0",
+ "antd": "5.8.3",
+ "glob": "7.2.0",
+ "ip": "1.1.8",
+ "node-polyfill-webpack-plugin": "^2.0.1",
+ "normalize.css": "8.0.1",
+ "react": "^18",
+ "react-dom": "18.2.0",
+ "react-error-boundary": "^4.0.11",
+ "react-router-dom": "6.4.3",
+ "serve-static": "1.15.0",
+ "typescript": "^5.2.2"
+ },
+ "sideEffects": [],
+ "publishConfig": {
+ "access": "public",
+ "registry": "https://registry.npmjs.org/"
+ }
+}
diff --git a/packages/client/src/common/imgs/connection-point.svg b/packages/client/src/common/imgs/connection-point.svg
new file mode 100644
index 000000000..d43d37264
--- /dev/null
+++ b/packages/client/src/common/imgs/connection-point.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/client/src/common/imgs/icon.svg b/packages/client/src/common/imgs/icon.svg
new file mode 100644
index 000000000..f12c318aa
--- /dev/null
+++ b/packages/client/src/common/imgs/icon.svg
@@ -0,0 +1,21 @@
+
diff --git a/packages/components/src/common/imgs/webpack.svg b/packages/client/src/common/imgs/webpack.svg
similarity index 100%
rename from packages/components/src/common/imgs/webpack.svg
rename to packages/client/src/common/imgs/webpack.svg
diff --git a/packages/components/src/common/styles/base.scss b/packages/client/src/common/styles/base.scss
similarity index 100%
rename from packages/components/src/common/styles/base.scss
rename to packages/client/src/common/styles/base.scss
diff --git a/packages/components/src/common/styles/theme.scss b/packages/client/src/common/styles/theme.scss
similarity index 100%
rename from packages/components/src/common/styles/theme.scss
rename to packages/client/src/common/styles/theme.scss
diff --git a/packages/client/src/index.tsx b/packages/client/src/index.tsx
new file mode 100644
index 000000000..4b957514b
--- /dev/null
+++ b/packages/client/src/index.tsx
@@ -0,0 +1,13 @@
+import '@rsdoctor/components/i18n';
+import App from './main';
+import './common/styles/base.scss';
+import icon from './common/imgs/icon.svg';
+
+const link = document.createElement('link');
+link.setAttribute('type', 'image/x-icon');
+link.setAttribute('rel', 'icon');
+link.setAttribute('href', icon);
+document.head.appendChild(link);
+
+// must export Component at the entry file in edenx.
+export default App;
diff --git a/packages/client/src/main.tsx b/packages/client/src/main.tsx
new file mode 100644
index 000000000..2be11f2b5
--- /dev/null
+++ b/packages/client/src/main.tsx
@@ -0,0 +1,147 @@
+import { Constants } from '@rsdoctor/components';
+import { Config, ConfigContext, defaultConfig } from '@rsdoctor/components/config';
+import { Layout } from '@rsdoctor/components/elements';
+import { getDemoUrl, getLocale, setThemeToStorage, setViewModeToStorage, useDetectIfCloudIdeEnv } from '@rsdoctor/components/utils';
+import type { Manifest } from '@rsdoctor/types';
+import { Alert, Button, ConfigProvider, Divider, Result, Space, Typography, theme as te } from 'antd';
+import React, { useState } from 'react';
+import { ErrorBoundary } from 'react-error-boundary';
+import { HashRouter as BrowserRouter } from 'react-router-dom';
+import Router from './router';
+// import UploaderComponent from './pages/Resources/Uploader'; TODO: uploader components
+
+const { PageState, Theme } = Constants;
+
+const Index: React.FC = (): React.ReactElement => {
+ const ifCloudIdeEnv = useDetectIfCloudIdeEnv();
+
+ const [state, setState] = useState(PageState.Success);
+ const [viewMode, setViewMode] = useState({ ...defaultConfig.viewMode });
+ const [manifest, setManifest] = useState();
+ const [theme, setTheme] = useState(defaultConfig.theme);
+
+ if (state === Constants.PageState.Fail) {
+ const demoUrl = getDemoUrl();
+ return (
+
+
+ load json file of Rsdoctor failed.
+
+
+ try to use command + r to refresh page.
+
+ {process.env.NODE_ENV === 'development' ? (
+
+ in development, you need to run emo run build:analysis to make
+ sure the mock data has been generated.
+
+ ) : null}
+
+
+
+ you can
+
+ {' '}
+ upload a file{' '}
+
+ in the area below to analyze your project.
+
+ {/* */}
+
+
+ {demoUrl ? (
+
+ or you can open the{' '}
+
+
+ demo
+
+ {' '}
+ to get started with the Web Doctor.
+
+ ) : null}
+
+ );
+ }
+
+ return (
+
+ {
+ setTheme(v);
+ setThemeToStorage(v);
+ },
+ pageState: state,
+ json: manifest!,
+ viewMode,
+ setManifest,
+ setPageState: setState,
+ setViewMode(m, saveStorage = true) {
+ const res = { ...viewMode, ...m };
+ setViewMode(res);
+ saveStorage && setViewModeToStorage(res);
+ },
+ }}
+ >
+
+ {(v) => {
+ return (
+
+
+ <>
+ {ifCloudIdeEnv && (
+ {`Please jump to:${window.location.href.replace(/https/, 'http')}`}
+ }
+ type="warning"
+ showIcon
+ closable
+ />
+ )}
+ (
+
+ Reload
+
+ }
+ >
+
+ Error Stack
+ {error.stack || error.message}
+
+
+ )}
+ onReset={() => {
+ window.location.reload();
+ }}
+ >
+
+
+ >
+
+
+ );
+ }}
+
+
+
+ );
+};
+
+export default Index;
diff --git a/packages/client/src/router.tsx b/packages/client/src/router.tsx
new file mode 100644
index 000000000..93270552c
--- /dev/null
+++ b/packages/client/src/router.tsx
@@ -0,0 +1,15 @@
+import React from 'react';
+import { Route, Routes } from 'react-router-dom';
+
+import { OverallPage } from '@rsdoctor/components/pages';
+
+
+export default function Router(): React.ReactElement {
+
+ return (
+
+ } />
+ } />
+
+ );
+}
diff --git a/packages/client/src/typings/assetsDefinition.d.ts b/packages/client/src/typings/assetsDefinition.d.ts
new file mode 100644
index 000000000..b675771f0
--- /dev/null
+++ b/packages/client/src/typings/assetsDefinition.d.ts
@@ -0,0 +1,16 @@
+declare module '*.png';
+declare module '*.jpg';
+declare module '*.svg';
+declare module '*.jpeg';
+declare module '*.gif';
+declare module '*.webp';
+declare module '*.ttf';
+declare module '*.woff';
+declare module '*.woff2';
+declare module '*.scss';
+declare module '*.less';
+declare module '*.css';
+declare module '*?__inline';
+declare module '*?__inline=true';
+declare module '*?__inline=false';
+
diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json
new file mode 100644
index 000000000..5e55b2b5d
--- /dev/null
+++ b/packages/client/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "extends": "@rsdoctor/tsconfig/base",
+ "compilerOptions": {
+ "baseUrl": "./",
+ "jsx": "react",
+ "isolatedModules": true,
+ "declaration": true,
+ "composite": true,
+ "rootDir": "src",
+ "outDir": "dist",
+ },
+ "include": ["src"],
+ "exclude": ["**/node_modules"]
+}
diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index 3808ca6e1..c776b3511 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -1 +1 @@
-# @rsbuild/doctor-components
+# @rsdoctor/components
diff --git a/packages/components/modern.config.ts b/packages/components/modern.config.ts
index 12a2d70f7..42631e31e 100644
--- a/packages/components/modern.config.ts
+++ b/packages/components/modern.config.ts
@@ -8,6 +8,6 @@ export default defineConfig({
format: 'esm',
target: 'es2019',
outDir: './dist',
- dts: false,
+ dts: {},
},
});
diff --git a/packages/components/package.json b/packages/components/package.json
index af8aab757..8f62e6260 100644
--- a/packages/components/package.json
+++ b/packages/components/package.json
@@ -1,20 +1,51 @@
{
"name": "@rsdoctor/components",
"version": "0.0.1",
- "types": "./dist/types/index.d.ts",
- "main": "./dist/lib/index.js",
- "module": "./dist/es/index.js",
+ "main": "./dist/index.js",
+ "license": "MIT",
+ "module": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/web-infra-dev/rsdoctor",
+ "directory": "packages/components"
+ },
+ "files": [
+ "dist"
+ ],
"scripts": {
- "dev": "modern dev",
"build": "modern build",
- "start": "npm run build -- -w"
+ "dev": "npm run start",
+ "start": "modern build -w",
+ "test": "vitest run"
},
- "lint-staged": {
- "*.{js,jsx,ts,tsx,mjs,cjs}": [
- "node --max_old_space_size=8192 ./node_modules/eslint/bin/eslint.js --fix --color --cache --quiet"
- ]
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ },
+ "./elements": {
+ "types": "./dist/components/index.d.ts",
+ "default": "./dist/components/index.js"
+ },
+ "./pages": {
+ "types": "./dist/pages/index.d.ts",
+ "default": "./dist/pages/index.js"
+ },
+ "./utils": {
+ "types": "./dist/utils/index.d.ts",
+ "default": "./dist/utils/index.js"
+ },
+ "./i18n": {
+ "types": "./dist/utils/i18n/index.d.ts",
+ "default": "./dist/utils/i18n/index.js"
+ },
+ "./config": {
+ "types": "./dist/config.d.ts",
+ "default": "./dist/config.js"
+ }
},
- "type": "module",
+
"eslintIgnore": [
"node_modules/",
"dist/"
@@ -22,7 +53,7 @@
"devDependencies": {
"@ant-design/icons": "4.8.0",
"@monaco-editor/react": "4.4.6",
- "@rsdoctor/sdk": "workspace:*",
+ "@rsdoctor/graph": "workspace:*",
"@rsdoctor/types": "workspace:*",
"@rsdoctor/utils": "workspace:*",
"@types/lodash-es": "4.17.6",
@@ -32,14 +63,14 @@
"ansi-to-react": "6.1.6",
"antd": "5.8.3",
"axios": "^1.6.1",
- "glob": "7.2.0",
"i18next": "22.0.4",
+ "lodash-es": "4.17.21",
"monaco-editor": "0.34.1",
- "normalize.css": "8.0.1",
"rc-dialog": "9.1.0",
"rc-tree": "5.7.2",
"react": "^18",
"react-i18next": "12.0.0",
+ "react-json-view": "1.21.3",
"react-router-dom": "6.4.3",
"socket.io-client": "4.6.1",
"typescript": "^5.2.2",
diff --git a/packages/components/src/components/Alert/package-relation.tsx b/packages/components/src/components/Alert/package-relation.tsx
index 5dba2f658..5ffd34896 100644
--- a/packages/components/src/components/Alert/package-relation.tsx
+++ b/packages/components/src/components/Alert/package-relation.tsx
@@ -92,7 +92,6 @@ export const PackageRelationReasonsWithServer = withServerAPI({
export const PackageRelationAlert: React.FC = ({
data,
- cwd,
getPackageRelationContentComponent,
}) => {
const { level, code, packages } = data;
diff --git a/packages/components/src/components/Configuration/index.tsx b/packages/components/src/components/Configuration/index.tsx
new file mode 100644
index 000000000..9db5c0f39
--- /dev/null
+++ b/packages/components/src/components/Configuration/index.tsx
@@ -0,0 +1 @@
+export * from './webpack';
diff --git a/packages/components/src/components/Configuration/webpack.tsx b/packages/components/src/components/Configuration/webpack.tsx
new file mode 100644
index 000000000..f8f1f1aa3
--- /dev/null
+++ b/packages/components/src/components/Configuration/webpack.tsx
@@ -0,0 +1,77 @@
+import { SDK } from '@rsdoctor/types';
+import { Divider, Row, Select, Space, Typography } from 'antd';
+import { pick } from 'lodash-es';
+import React, { useState } from 'react';
+import ReactJson from 'react-json-view';
+import { useWebpackConfigurationByConfigs } from '../../utils';
+import { withServerAPI } from '../Manifest';
+import { TextDrawer } from '../TextDrawer';
+import { Title } from '../Title';
+
+interface WebpackConfigurationViewerBaseProps {
+ defaultKeys?: string[];
+ configs: SDK.ConfigData;
+}
+
+export const WebpackConfigurationViewerBase: React.FC = ({
+ defaultKeys,
+ configs,
+}) => {
+ const webpackData = useWebpackConfigurationByConfigs(configs || []);
+
+ if (!webpackData) return null;
+
+ const { config: webpack, version } = webpackData;
+ const keys = Object.keys(webpack);
+ const [selectKeys, setSelectKeys] = useState(defaultKeys || keys);
+
+ return (
+
+
+
+
+
+ Properties:
+
+
+
+
+
+
+ );
+};
+
+export const WebpackConfigurationViewer = withServerAPI({
+ Component: WebpackConfigurationViewerBase,
+ api: SDK.ServerAPI.API.LoadDataByKey,
+ responsePropName: 'configs',
+ body: {
+ key: 'configs',
+ },
+ showSkeleton: false,
+});
diff --git a/packages/components/src/components/Layout/builder-select.tsx b/packages/components/src/components/Layout/builder-select.tsx
new file mode 100644
index 000000000..58f2eb44a
--- /dev/null
+++ b/packages/components/src/components/Layout/builder-select.tsx
@@ -0,0 +1,57 @@
+import { Select, Divider, Typography } from 'antd';
+import React, { useState, useEffect } from 'react';
+import { Manifest } from '@rsdoctor/types';
+import Icon from '../../common/imgs/connection-point.svg';
+import { fetchManifest, changeOrigin } from '../../utils';
+
+export const BuilderSelect: React.FC = () => {
+ const [buildName, setBuildName] = useState('');
+ const [series, setSeries] = useState([]);
+
+ useEffect(() => {
+ fetchManifest().then(({ name, series }) => {
+ if (name) {
+ setBuildName(name);
+ }
+
+ if (series && series.length > 0) {
+ setSeries(series);
+ }
+ });
+ }, []);
+
+ if (buildName.length <= 0 || series.length <= 0) {
+ return <>>;
+ }
+
+ return (
+ <>
+
+
+ Compiler
+
+ >
+ );
+};
diff --git a/packages/components/src/components/Layout/header.scss b/packages/components/src/components/Layout/header.scss
new file mode 100644
index 000000000..998f4a787
--- /dev/null
+++ b/packages/components/src/components/Layout/header.scss
@@ -0,0 +1,17 @@
+.header-switch {
+ .ant-switch-handle {
+ top: 1px !important;
+ }
+}
+
+.header-icon {
+ user-select: none;
+
+ &:hover {
+ color: #1668dc;
+ }
+ &:active {
+ color: #40a9ff;
+ }
+}
+
diff --git a/packages/components/src/components/Layout/header.tsx b/packages/components/src/components/Layout/header.tsx
new file mode 100644
index 000000000..d8a47a003
--- /dev/null
+++ b/packages/components/src/components/Layout/header.tsx
@@ -0,0 +1,127 @@
+import { TranslationOutlined, UserOutlined } from '@ant-design/icons';
+import { Avatar, Button, Col, Dropdown, Input, Layout, Row, Select, Switch, Typography } from 'antd';
+import React from 'react';
+import { APILoaderMode4Dev, Language, Size, Theme } from '../../constants';
+import {
+ getAPILoaderModeFromStorage,
+ setAPILoaderModeToStorage,
+ useI18n,
+ useTheme
+} from '../../utils';
+import { OverlayAlertsWithButton } from '../Alerts';
+import { BuilderSelect } from './builder-select';
+import { Menus } from './menus';
+
+import icon from '../../common/imgs/icon.svg';
+import './header.sass';
+
+export const Header: React.FC = () => {
+ const { i18n } = useI18n();
+
+ const { setTheme, isLight, isDark } = useTheme();
+ const iconStyle: React.CSSProperties = {
+ display: 'inline-block',
+ fontSize: 20,
+ textAlign: 'center',
+ verticalAlign: 'middle',
+ cursor: 'pointer',
+ width: 30,
+ transition: 'all 0.3s ease',
+ };
+ const languages = [
+ { value: Language.Cn, label: '中文' },
+ { value: Language.En, label: 'English' },
+ ];
+
+ return (
+
+
+
+
+
+
Web Doctor
+
+
+
+
+
+ {process.env.NODE_ENV === 'development' ? (
+
+
+
+
+
+
+ ) : null}
+
+
+
+
+
+ {
+ setTheme(checked ? Theme.Dark : Theme.Light);
+ }}
+ style={{ border: `1px solid ${isLight ? '#ddd' : '#fff'}`, background: isLight ? '#eee' : '#141414' }}
+ />
+
+
+ ({
+ label: e.label,
+ key: e.value,
+ onClick() {
+ i18n.changeLanguage(e.value);
+ },
+ })),
+ selectedKeys: [i18n.language],
+ }}
+ >
+
+
+ *
+ } />
+
+
+
+
+ );
+};
diff --git a/packages/components/src/components/Layout/index.tsx b/packages/components/src/components/Layout/index.tsx
new file mode 100644
index 000000000..252c0ad8c
--- /dev/null
+++ b/packages/components/src/components/Layout/index.tsx
@@ -0,0 +1,36 @@
+import { PropsWithChildren, useEffect } from 'react';
+import { FloatButton, Layout as L } from 'antd';
+import { Size } from '../../constants';
+import { Header } from './header';
+import { useLocale, useI18n } from '../../utils';
+import { Progress } from './progress';
+
+export interface LayoutProps {
+ children: JSX.Element;
+}
+
+export const Layout = (props: PropsWithChildren): JSX.Element => {
+ const locale = useLocale();
+ const { i18n } = useI18n();
+ const { children } = props;
+
+
+ useEffect(() => {
+ if (i18n.language !== locale) {
+ i18n.changeLanguage(locale);
+ }
+ }, [locale]);
+
+ return (
+
+
+
+
+ {children}
+
+
+
+ );
+};
diff --git a/packages/components/src/components/Layout/menus.tsx b/packages/components/src/components/Layout/menus.tsx
new file mode 100644
index 000000000..db86a7f94
--- /dev/null
+++ b/packages/components/src/components/Layout/menus.tsx
@@ -0,0 +1,83 @@
+import {
+ BarChartOutlined,
+ MenuOutlined
+} from '@ant-design/icons';
+import { Manifest, SDK } from '@rsdoctor/types';
+import { Col, Grid, Menu, MenuProps } from 'antd';
+import { includes } from 'lodash-es';
+import React from 'react';
+import { useLocation, useNavigate } from 'react-router-dom';
+import { Size } from '../../constants';
+import * as OverallConstants from '../../pages/Overall/constants';
+import { useI18n } from '../../utils';
+import { withServerAPI } from '../Manifest';
+
+const BuilderSwitchName = 'builder-switcher';
+
+const MenusBase: React.FC<{ style?: React.CSSProperties; routes: Manifest.DoctorManifestClientRoutes[] }> = (
+ props,
+) => {
+ const { t } = useI18n();
+ const { pathname } = useLocation();
+ const navigate = useNavigate();
+ const { routes: enableRoutes } = props;
+ const iconStyle: React.CSSProperties = {
+ fontSize: 16,
+ };
+
+ const items: MenuProps['items'] = [];
+
+ const { xxl } = Grid.useBreakpoint();
+
+ console.log('enableRoutes: ', enableRoutes);
+
+ if (includes(enableRoutes, Manifest.DoctorManifestClientRoutes.Overall)) {
+ items.push({
+ label: t(OverallConstants.name),
+ key: OverallConstants.route,
+ icon: ,
+ children: [],
+ onTitleClick(e) {
+ navigate(e.key);
+ },
+ });
+ }
+
+ const MenuComponent = (
+