diff --git a/.github/workflows/test-ubuntu.yml b/.github/workflows/test-ubuntu.yml index 6bff3036..ab928585 100644 --- a/.github/workflows/test-ubuntu.yml +++ b/.github/workflows/test-ubuntu.yml @@ -53,7 +53,7 @@ jobs: - name: Install Dependencies if: ${{steps.skip-ci.outputs.RESULT != 'true'}} - run: pnpm install + run: pnpm install && cd ./e2e && npx playwright install chromium - name: Unit Test if: ${{steps.skip-ci.outputs.RESULT != 'true'}} diff --git a/README.md b/README.md index a67f1b96..80451580 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ Come and chat with us on [Discord](https://discord.gg/wrBPBT6rkM)! The Rsdoctor - [Rspack](https://github.com/web-infra-dev/rspack): A fast Rust-based web bundler. - [Rsbuild](https://github.com/web-infra-dev/rsbuild): An Rspack-based build tool for the web, rebranded from Modern.js Builder. -- [Rslib](https://github.com/web-infra-dev/rslib): A library build tool powered by Rsbuild. +- [Rslib](https://github.com/web-infra-dev/rslib): A library development tool powered by Rsbuild. - [Rspress](https://github.com/web-infra-dev/rspress): A fast Rspack-based static site generator. - [Modern.js](https://github.com/web-infra-dev/modern.js): A progressive React framework based on Rsbuild. diff --git a/README.zh-CN.md b/README.zh-CN.md index 788f5ddc..7e7696fc 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -62,7 +62,7 @@ https://github.com/user-attachments/assets/3400e0a2-a1dc-4a14-9466-6283af9dd9ed - [Rspack](https://github.com/web-infra-dev/rspack): 基于 rust 的 web 构建器。 - [Rsbuild](https://github.com/web-infra-dev/rsbuild): 基于 Rspack 的 web 构建工具。 -- [Rslib](https://github.com/web-infra-dev/rslib): 基于 Rsbuild 的 library 构建工具。 +- [Rslib](https://github.com/web-infra-dev/rslib): 基于 Rsbuild 的 library 开发工具。 - [Rspress](https://github.com/web-infra-dev/rspress): 基于 Rsbuild 的静态站点生成器。 - [Modern.js](https://github.com/web-infra-dev/modern.js): 基于 Rsbuild 的渐进式 React 框架。 diff --git a/e2e/README.md b/e2e/README.md index c5f01e1c..0c23a3f0 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -9,6 +9,9 @@ This folder contains the e2e test cases of Rsdoctor. ## Commands ```bash +# Install playwright browser +npx playwright install chromium + # Run all test cases, including Rspack and webpack pnpm run test ``` diff --git a/e2e/cases/doctor-rspack/banner-plugin.test.ts b/e2e/cases/doctor-rspack/banner-plugin.test.ts index dc77733d..1f8460d8 100644 --- a/e2e/cases/doctor-rspack/banner-plugin.test.ts +++ b/e2e/cases/doctor-rspack/banner-plugin.test.ts @@ -1,10 +1,10 @@ import { expect, test } from '@playwright/test'; +import { Utils } from '@rsdoctor/core/build-utils'; import { getSDK, setSDK } from '@rsdoctor/core/plugins'; -import { compileByRspack } from '@scripts/test-helper'; import { BannerPlugin, Compiler } from '@rspack/core'; +import { compileByRspack } from '@scripts/test-helper'; import path from 'path'; import { createRsdoctorPlugin } from './test-utils'; -import { parseBundle } from '../../node_modules/@rsdoctor/core/dist/build-utils/build/utils/parseBundle'; let reportLoaderStartOrEndTimes = 0; @@ -118,7 +118,7 @@ test('rspack banner plugin', async () => { const sdk = getSDK(); // @ts-ignore - const bundle = parseBundle( + const bundle = Utils.parseBundle( path.join(__dirname, './fixtures/rspack-banner-plugin.js'), // @ts-ignore sdk.getStoreData().moduleGraph.modules, diff --git a/e2e/cases/doctor-rspack/brief.test.ts b/e2e/cases/doctor-rspack/brief.test.ts index 65db019b..c189fbab 100644 --- a/e2e/cases/doctor-rspack/brief.test.ts +++ b/e2e/cases/doctor-rspack/brief.test.ts @@ -1,4 +1,4 @@ -import { expect, test, webkit } from '@playwright/test'; +import { expect, test, chromium } from '@playwright/test'; import { getSDK, setSDK } from '@rsdoctor/core/plugins'; import { compileByRspack } from '@scripts/test-helper'; import { Compiler } from '@rspack/core'; @@ -25,7 +25,7 @@ async function rspackCompile( extensions: ['.ts', '.js'], }, output: { - path: path.join(__dirname, '../doctor-rspack/dist'), + path: path.join(__dirname, '../doctor-rspack/dist/brief'), }, module: { rules: [ @@ -106,12 +106,12 @@ test('rspack brief mode', async () => { const reportPath = path.join( __dirname, - './dist/.rsdoctor/rsdoctor-report.html', + './dist/brief/.rsdoctor/rsdoctor-report.html', ); fileExists(reportPath); - const browser = await webkit.launch(); + const browser = await chromium.launch(); // Create a new browser context const context = await browser.newContext(); diff --git a/e2e/cases/doctor-rspack/fixtures/c.js b/e2e/cases/doctor-rspack/fixtures/c.js new file mode 100644 index 00000000..492f10d3 --- /dev/null +++ b/e2e/cases/doctor-rspack/fixtures/c.js @@ -0,0 +1 @@ +const c = 1; diff --git a/e2e/cases/doctor-rspack/linter-rule-render.test.ts b/e2e/cases/doctor-rspack/linter-rule-render.test.ts new file mode 100644 index 00000000..71ec89e8 --- /dev/null +++ b/e2e/cases/doctor-rspack/linter-rule-render.test.ts @@ -0,0 +1,153 @@ +import { expect, test, chromium } from '@playwright/test'; +import { getSDK, setSDK } from '@rsdoctor/core/plugins'; +import { compileByRspack } from '@scripts/test-helper'; +import { Compiler } from '@rspack/core'; +import path from 'path'; +import fs from 'fs'; +import { createRsdoctorPlugin } from './test-utils'; + +let reportLoaderStartOrEndTimes = 0; +const ecmaVersion = 3; + +async function rspackCompile( + _tapName: string, + compile: typeof compileByRspack, +) { + const file = path.resolve(__dirname, './fixtures/c.js'); + + const res = await compile(file, { + resolve: { + extensions: ['.ts', '.js'], + }, + output: { + path: path.join(__dirname, '../doctor-rspack/dist/linter-rule-render'), + }, + module: { + rules: [ + { + test: /\.[jt]s$/, + use: { + loader: 'builtin:swc-loader', + options: { + sourceMap: true, + jsc: { + parser: { + syntax: 'typescript', + }, + externalHelpers: true, + preserveAllComments: false, + }, + }, + }, + type: 'javascript/auto', + }, + ], + }, + plugins: [ + // @ts-ignore + createRsdoctorPlugin({ + mode: 'brief', + linter: { + rules: { + 'ecma-version-check': [ + 'Warn', + { + ecmaVersion, + }, + ], + }, + }, + }), + { + name: 'Foo', + apply(compiler: Compiler) { + compiler.hooks.beforeRun.tapPromise( + { name: 'Foo', stage: 99999 }, + async () => { + const sdk = getSDK(); + setSDK( + new Proxy(sdk, { + get(target, key, receiver) { + switch (key) { + case 'reportLoader': + return null; + case 'reportLoaderStartOrEnd': + return (_data: any) => { + reportLoaderStartOrEndTimes += 1; + }; + default: + return Reflect.get(target, key, receiver); + } + }, + set(target, key, value, receiver) { + return Reflect.set(target, key, value, receiver); + }, + defineProperty(target, p, attrs) { + return Reflect.defineProperty(target, p, attrs); + }, + }), + ); + }, + ); + }, + }, + ], + }); + + return res; +} + +test('linter rule render check', async () => { + const tapName = 'Foo'; + await rspackCompile(tapName, compileByRspack); + + const reportPath = path.join( + __dirname, + './dist/linter-rule-render/.rsdoctor/rsdoctor-report.html', + ); + + fileExists(reportPath); + + const browser = await chromium.launch(); + + // Create a new browser context + const context = await browser.newContext(); + + // Open a new page + const page = await context.newPage(); + + // Navigate to a URL + await page.goto(`file:///${reportPath}`); + + const ecmaVersionButton = await page.$('[data-node-key="E1004"]'); + await ecmaVersionButton?.click(); + // ignore output text check because there's no .map file for track the source code + const source = await page.$('.e2e-ecma-source'); + const error = await page.$('.e2e-ecma-error'); + const sourceText = await source?.textContent(); + const errorText = await error?.textContent(); + expect(sourceText).toBe( + '/cases/doctor-rspack/dist/linter-rule-render/main.js:1:2', + ); + expect(errorText).toBe( + `Find some syntax that does not match "ecmaVersion <= ${ecmaVersion}"`, + ); + + // Close the page + await page.close(); + + // Close the browser context + await context.close(); + + // Close the browser + await browser.close(); +}); + +async function fileExists(filePath: string) { + try { + await fs.existsSync(filePath); + return true; + } catch { + return false; + } +} diff --git a/e2e/cases/doctor-rspack/tag-plugin-without-banner.test.ts b/e2e/cases/doctor-rspack/tag-plugin-without-banner.test.ts new file mode 100644 index 00000000..86e7e067 --- /dev/null +++ b/e2e/cases/doctor-rspack/tag-plugin-without-banner.test.ts @@ -0,0 +1,123 @@ +import { expect, test } from '@playwright/test'; +import { getSDK, setSDK } from '@rsdoctor/core/plugins'; +import { compileByRspack } from '@scripts/test-helper'; +import { Utils } from '@rsdoctor/core/build-utils'; +import { Compiler } from '@rspack/core'; +import path from 'path'; +import { createRsdoctorPlugin } from './test-utils'; + +let reportLoaderStartOrEndTimes = 0; + +async function rspackCompile( + _tapName: string, + compile: typeof compileByRspack, +) { + const file = path.resolve(__dirname, './fixtures/a.js'); + const loader = path.resolve(__dirname, './fixtures/loaders/comment.js'); + + const esmLoader = path.resolve( + __dirname, + './fixtures/loaders/esm-serialize-query-to-comment.mjs', + ); + + const res = await compile(file, { + resolve: { + extensions: ['.ts', '.js'], + }, + output: { + path: path.join(__dirname, '../doctor-rspack/dist'), + }, + module: { + rules: [ + { + test: /\.js/, + use: loader, + }, + { + test: /\.js/, + use: esmLoader, + }, + { + test: /\.[jt]s$/, + use: { + loader: 'builtin:swc-loader', + options: { + sourceMap: true, + jsc: { + parser: { + syntax: 'typescript', + }, + externalHelpers: true, + preserveAllComments: false, + }, + }, + }, + type: 'javascript/auto', + }, + ], + }, + plugins: [ + // @ts-ignore + createRsdoctorPlugin({ + supports: { + banner: true, + }, + }), + { + name: 'Foo', + apply(compiler: Compiler) { + compiler.hooks.beforeRun.tapPromise( + { name: 'Foo', stage: 99999 }, + async () => { + const sdk = getSDK(); + setSDK( + new Proxy(sdk, { + get(target, key, receiver) { + switch (key) { + case 'reportLoader': + return null; + case 'reportLoaderStartOrEnd': + return (_data: any) => { + reportLoaderStartOrEndTimes += 1; + }; + default: + return Reflect.get(target, key, receiver); + } + }, + set(target, key, value, receiver) { + return Reflect.set(target, key, value, receiver); + }, + defineProperty(target, p, attrs) { + return Reflect.defineProperty(target, p, attrs); + }, + }), + ); + }, + ); + }, + }, + ], + }); + + return res; +} + +test('rspack banner plugin', async () => { + const tapName = 'Foo'; + await rspackCompile(tapName, compileByRspack); + const sdk = getSDK(); + + // @ts-ignore + const bundle = Utils.parseBundle( + path.join(__dirname, './fixtures/rspack-banner-plugin.js'), + // @ts-ignore + sdk.getStoreData().moduleGraph.modules, + ); + + expect(JSON.stringify(bundle.modules)).toBe( + '{"":{"size":313,"sizeConvert":"313 B","content":"function (\\n __unused_webpack_module,\\n __webpack_exports__,\\n __webpack_require__,\\n ) {\\n \'use strict\';\\n __webpack_require__.r(__webpack_exports__);\\n __webpack_require__.d(__webpack_exports__, {\\n a: function () {\\n return a;\\n },\\n });\\n var a = 1;\\n }"}}', + ); + const res = sdk.getStoreData().chunkGraph; + expect(res.assets[0].content).toContain('RSDOCTOR_START'); + expect(res.assets[0].content).toContain('RSDOCTOR_END'); +}); diff --git a/e2e/package.json b/e2e/package.json index d4ff59b8..c311ba90 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -3,8 +3,7 @@ "name": "@rsdoctor/e2e", "version": "0.0.1", "scripts": { - "test": "pnpm run i:webkit && playwright test", - "i:webkit": "playwright install-deps" + "test": "playwright test" }, "dependencies": { "devcert": "1.2.2", @@ -15,7 +14,7 @@ "vue": "^3.5.13" }, "devDependencies": { - "@playwright/test": "1.33.0", + "@playwright/test": "1.44.1", "@rsdoctor/cli": "workspace:*", "@rsdoctor/core": "workspace:*", "@rsdoctor/rspack-plugin": "workspace:*", @@ -23,15 +22,15 @@ "@rsdoctor/types": "workspace:*", "@rsdoctor/utils": "workspace:*", "@rsdoctor/webpack-plugin": "workspace:*", - "@rspack/core": "^1.1.8", + "@rspack/core": "^1.2.2", "@types/lodash": "^4.17.14", "@types/node": "^16", "@types/react": "^18.3.18", "@types/react-dom": "^18.3.5", "fast-glob": "^3.3.3", "loader-utils": "^2.0.4", - "playwright": "1.33.0", + "playwright": "1.44.1", "typescript": "^5.2.2", - "webpack": "^5.95.0" + "webpack": "^5.97.1" } } diff --git a/e2e/test-kit/launch.ts b/e2e/test-kit/launch.ts index 4dc961b7..9190e0e4 100644 --- a/e2e/test-kit/launch.ts +++ b/e2e/test-kit/launch.ts @@ -1,7 +1,7 @@ -import { webkit } from '@playwright/test'; +import { chromium } from '@playwright/test'; export async function launchPlaywright() { - const browser = await webkit.launch(); + const browser = await chromium.launch(); const page = await browser.newPage(); diff --git a/examples/multiple-minimal/package.json b/examples/multiple-minimal/package.json index aff5a4c9..533cbd4c 100644 --- a/examples/multiple-minimal/package.json +++ b/examples/multiple-minimal/package.json @@ -23,7 +23,6 @@ "@types/react": "^18", "@types/react-dom": "18.0.8", "css-loader": "6.7.3", - "eslint": "8.22.0", "less-loader": "11.1.0", "react": "18.2.0", "react-dom": "18.2.0", diff --git a/examples/rsbuild-minimal/package.json b/examples/rsbuild-minimal/package.json index 4be46288..d0a961f8 100644 --- a/examples/rsbuild-minimal/package.json +++ b/examples/rsbuild-minimal/package.json @@ -22,8 +22,8 @@ "semver7": "npm:semver@7.5.4" }, "devDependencies": { - "@rsbuild/core": "^1.1.3", - "@rsbuild/plugin-react": "^1.0.7", + "@rsbuild/core": "^1.2.2", + "@rsbuild/plugin-react": "^1.1.0", "@rsdoctor/core": "workspace:*", "@rsdoctor/rspack-plugin": "workspace:*", "@types/react": "^18", diff --git a/examples/rspack-layers-minimal/build.ts b/examples/rspack-layers-minimal/build.ts index c9c95b10..eb502abe 100644 --- a/examples/rspack-layers-minimal/build.ts +++ b/examples/rspack-layers-minimal/build.ts @@ -13,8 +13,6 @@ function rspackBuild(config: rspack.Configuration) { throw err; } - console.log(); - if (stats) { console.log( stats.toString({ diff --git a/examples/rspack-minimal/rspack.config.js b/examples/rspack-minimal/rspack.config.js index 7fbfb8a7..6aec073e 100644 --- a/examples/rspack-minimal/rspack.config.js +++ b/examples/rspack-minimal/rspack.config.js @@ -91,6 +91,16 @@ const config = { disableClientServer: process.env.ENABLE_CLIENT_SERVER === 'false', features: ['bundle', 'plugins', 'loader'], mode: 'brief', + linter: { + rules: { + 'ecma-version-check': [ + 'Warn', + { + ecmaVersion: 3, + }, + ], + }, + }, }), new rspack.HtmlRspackPlugin({ template: './index.html', diff --git a/examples/webpack-minimal/package.json b/examples/webpack-minimal/package.json index cc9f767a..cd68e5cf 100644 --- a/examples/webpack-minimal/package.json +++ b/examples/webpack-minimal/package.json @@ -9,7 +9,7 @@ "package.json" ], "scripts": { - "compile": "node -r tsm ./node_modules/webpack/bin/webpack.js -c webpack.config.ts", + "compile": "node -r tsm ./node_modules/webpack/bin/webpack.js -c webpack-c.config.ts", "build:analysis": "ENABLE_CLIENT_SERVER=true npm run compile" }, "keywords": [], @@ -27,7 +27,6 @@ "@rsdoctor/webpack-plugin": "workspace:*", "@types/node": "14.18.26", "bundle-stats": "4.1.7", - "eslint": "8.22.0", "ts-loader": "9.4.2", "tslib": "2.4.1", "tsm": "2.3.0", diff --git a/examples/webpack-minimal/src/deps/a.js b/examples/webpack-minimal/src/deps/a.js new file mode 100644 index 00000000..f9dc7e62 --- /dev/null +++ b/examples/webpack-minimal/src/deps/a.js @@ -0,0 +1 @@ +require('./b'); diff --git a/examples/webpack-minimal/src/deps/b.js b/examples/webpack-minimal/src/deps/b.js new file mode 100644 index 00000000..bb0e0b81 --- /dev/null +++ b/examples/webpack-minimal/src/deps/b.js @@ -0,0 +1 @@ +require('./c'); diff --git a/examples/webpack-minimal/src/deps/c.js b/examples/webpack-minimal/src/deps/c.js new file mode 100644 index 00000000..f9dc7e62 --- /dev/null +++ b/examples/webpack-minimal/src/deps/c.js @@ -0,0 +1 @@ +require('./b'); diff --git a/examples/webpack-minimal/src/deps/context/a.js b/examples/webpack-minimal/src/deps/context/a.js new file mode 100644 index 00000000..e774602b --- /dev/null +++ b/examples/webpack-minimal/src/deps/context/a.js @@ -0,0 +1 @@ +require('../i'); diff --git a/examples/webpack-minimal/src/deps/d.js b/examples/webpack-minimal/src/deps/d.js new file mode 100644 index 00000000..bc1d248d --- /dev/null +++ b/examples/webpack-minimal/src/deps/d.js @@ -0,0 +1 @@ +require('./e'); diff --git a/examples/webpack-minimal/src/deps/e.js b/examples/webpack-minimal/src/deps/e.js new file mode 100644 index 00000000..4ad28537 --- /dev/null +++ b/examples/webpack-minimal/src/deps/e.js @@ -0,0 +1 @@ +require('./f'); diff --git a/examples/webpack-minimal/src/deps/f.js b/examples/webpack-minimal/src/deps/f.js new file mode 100644 index 00000000..5c8e13fa --- /dev/null +++ b/examples/webpack-minimal/src/deps/f.js @@ -0,0 +1 @@ +require('./g'); diff --git a/examples/webpack-minimal/src/deps/g.js b/examples/webpack-minimal/src/deps/g.js new file mode 100644 index 00000000..bc1d248d --- /dev/null +++ b/examples/webpack-minimal/src/deps/g.js @@ -0,0 +1 @@ +require('./e'); diff --git a/examples/webpack-minimal/src/deps/h.js b/examples/webpack-minimal/src/deps/h.js new file mode 100644 index 00000000..49852d5e --- /dev/null +++ b/examples/webpack-minimal/src/deps/h.js @@ -0,0 +1 @@ +require('./i'); diff --git a/examples/webpack-minimal/src/deps/i.js b/examples/webpack-minimal/src/deps/i.js new file mode 100644 index 00000000..05ae483e --- /dev/null +++ b/examples/webpack-minimal/src/deps/i.js @@ -0,0 +1 @@ +require.context('./context/', false, /.*/); diff --git a/examples/webpack-minimal/src/deps/module-concat-plugin-compat/a.js b/examples/webpack-minimal/src/deps/module-concat-plugin-compat/a.js new file mode 100644 index 00000000..750ca113 --- /dev/null +++ b/examples/webpack-minimal/src/deps/module-concat-plugin-compat/a.js @@ -0,0 +1,5 @@ +import b from './b'; + +var a = { name: 'a', dep: b && b.name }; + +export default a; diff --git a/examples/webpack-minimal/src/deps/module-concat-plugin-compat/b.js b/examples/webpack-minimal/src/deps/module-concat-plugin-compat/b.js new file mode 100644 index 00000000..6c26fd69 --- /dev/null +++ b/examples/webpack-minimal/src/deps/module-concat-plugin-compat/b.js @@ -0,0 +1,5 @@ +import a from './a'; + +var b = { name: 'b', dep: a && a.name }; + +export default b; diff --git a/examples/webpack-minimal/src/deps/module-concat-plugin-compat/index.js b/examples/webpack-minimal/src/deps/module-concat-plugin-compat/index.js new file mode 100644 index 00000000..d7b465a5 --- /dev/null +++ b/examples/webpack-minimal/src/deps/module-concat-plugin-compat/index.js @@ -0,0 +1,2 @@ +import a from './a'; +import b from './b'; diff --git a/examples/webpack-minimal/src/deps/nocycle.js b/examples/webpack-minimal/src/deps/nocycle.js new file mode 100644 index 00000000..e69de29b diff --git a/examples/webpack-minimal/src/deps/self-referencing/imports-self.js b/examples/webpack-minimal/src/deps/self-referencing/imports-self.js new file mode 100644 index 00000000..e8ad4225 --- /dev/null +++ b/examples/webpack-minimal/src/deps/self-referencing/imports-self.js @@ -0,0 +1,4 @@ +let own = require('./imports-self'); +let a = 1; +// console.log(a, own); +module.exports = a; diff --git a/examples/webpack-minimal/src/deps/self-referencing/uses-exports.js b/examples/webpack-minimal/src/deps/self-referencing/uses-exports.js new file mode 100644 index 00000000..bd816eab --- /dev/null +++ b/examples/webpack-minimal/src/deps/self-referencing/uses-exports.js @@ -0,0 +1 @@ +module.exports = 1; diff --git a/examples/webpack-minimal/src/deps/self-referencing/uses-this.js b/examples/webpack-minimal/src/deps/self-referencing/uses-this.js new file mode 100644 index 00000000..b251f26b --- /dev/null +++ b/examples/webpack-minimal/src/deps/self-referencing/uses-this.js @@ -0,0 +1 @@ +this; diff --git a/examples/webpack-minimal/src/deps/ts/a.tsx b/examples/webpack-minimal/src/deps/ts/a.tsx new file mode 100644 index 00000000..5862328d --- /dev/null +++ b/examples/webpack-minimal/src/deps/ts/a.tsx @@ -0,0 +1,4 @@ +import b from './b'; + +let a = 1; +module.exports = a; diff --git a/examples/webpack-minimal/src/deps/ts/b.tsx b/examples/webpack-minimal/src/deps/ts/b.tsx new file mode 100644 index 00000000..8731c94d --- /dev/null +++ b/examples/webpack-minimal/src/deps/ts/b.tsx @@ -0,0 +1,2 @@ +let b = 1; +export default b; diff --git a/examples/webpack-minimal/src/deps/ts/tsconfig.json b/examples/webpack-minimal/src/deps/ts/tsconfig.json new file mode 100644 index 00000000..18eb3b0b --- /dev/null +++ b/examples/webpack-minimal/src/deps/ts/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "outDir": "./dist/", + "noImplicitAny": true, + "module": "es6", + "target": "es5", + "jsx": "react", + "allowJs": true + } +} diff --git a/examples/webpack-minimal/webpack-c.config.ts b/examples/webpack-minimal/webpack-c.config.ts new file mode 100644 index 00000000..af42d805 --- /dev/null +++ b/examples/webpack-minimal/webpack-c.config.ts @@ -0,0 +1,27 @@ +import { resolve } from 'path'; +import { Configuration } from 'webpack'; +import { RsdoctorWebpackPlugin } from '@rsdoctor/webpack-plugin'; +import svgToMiniDataURI from 'mini-svg-data-uri'; + +const data: Configuration = { + entry: './src/deps/a.js', + mode: 'none', + + resolve: { + mainFields: ['browser', 'module', 'main'], + extensions: ['.ts', '.js', '.json', '.wasm'], + }, + output: { + path: resolve(__dirname, 'dist'), + filename: 'deps.js', + }, + devtool: 'source-map', + plugins: [ + new RsdoctorWebpackPlugin({ + disableClientServer: !process.env.ENABLE_CLIENT_SERVER, + features: ['bundle'], + }), + ], +}; + +export default data; diff --git a/package.json b/package.json index ce56f866..8ec4f469 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "test": "cross-env NODE_OPTIONS=--max-old-space-size=8192 pnpm run ut", "test:all": "pnpm run test && pnpm run e2e", "sort-package-json": "npx sort-package-json \"packages/*/package.json\"", - "ut": "vitest run", + "ut": "vitest run --pool=forks", "ut:watch": "vitest" }, "nano-staged": { @@ -35,10 +35,10 @@ "devDependencies": { "@biomejs/biome": "1.5.1", "@changesets/cli": "^2.27.11", - "@modern-js/module-tools": "^2.63.5", + "@modern-js/module-tools": "^2.63.7", "@rsdoctor/tsconfig": "workspace:*", "@scripts/test-helper": "workspace:*", - "check-dependency-version-consistency": "^4.1.1", + "check-dependency-version-consistency": "^5.0.0", "cross-env": "^7.0.3", "cspell-ban-words": "^0.0.4", "husky": "^9.1.7", diff --git a/packages/cli/package.json b/packages/cli/package.json index 09b649e2..f15aac8a 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@rsdoctor/cli", - "version": "1.0.0-alpha.0", + "version": "1.0.0-alpha.3", "repository": { "type": "git", "url": "https://github.com/web-infra-dev/rsdoctor", diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 837a304d..4788449b 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -67,7 +67,6 @@ export async function execute( await action(args); } catch (error) { const { message, stack } = error as Error; - console.log(''); console.error(red(stack || message)); process.exit(1); } diff --git a/packages/client/package.json b/packages/client/package.json index 9877b6c3..f11628a8 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@rsdoctor/client", - "version": "1.0.0-alpha.0", + "version": "1.0.0-alpha.3", "main": "dist/index.html", "repository": { "type": "git", @@ -18,21 +18,12 @@ "build:analysis": "ENABLE_DEVTOOLS_PLUGIN=true DEVTOOLS_DEV=true rsbuild build", "preview": "rsbuild preview" }, - "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": { - "@rsbuild/core": "^1.1.13", + "@rsbuild/core": "^1.2.2", "@rsbuild/plugin-node-polyfill": "^1.2.0", "@rsbuild/plugin-react": "^1.1.0", - "@rsbuild/plugin-sass": "^1.1.2", - "@rsbuild/plugin-type-check": "^1.2.0", + "@rsbuild/plugin-sass": "^1.2.0", + "@rsbuild/plugin-type-check": "^1.2.1", "@rsdoctor/components": "workspace:*", "@rsdoctor/types": "workspace:*", "@types/node": "^16", diff --git a/packages/components/package.json b/packages/components/package.json index 7828da3a..1bab61d7 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@rsdoctor/components", - "version": "1.0.0-alpha.0", + "version": "1.0.0-alpha.3", "main": "./dist/index.js", "license": "MIT", "module": "dist/index.js", @@ -45,13 +45,9 @@ "default": "./dist/config.js" } }, - "eslintIgnore": [ - "node_modules/", - "dist/" - ], "devDependencies": { "@ant-design/icons": "5.5.2", - "@rsbuild/plugin-check-syntax": "1.2.0", + "@rsbuild/plugin-check-syntax": "1.2.1", "@monaco-editor/react": "4.6.0", "@rsdoctor/graph": "workspace:*", "@rsdoctor/types": "workspace:*", @@ -79,7 +75,7 @@ "react-hyper-tree": "0.3.12", "react-i18next": "12.0.0", "react-json-view": "1.21.3", - "react-markdown": "^9.0.1", + "react-markdown": "^9.0.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/ecma-version-check.tsx b/packages/components/src/components/Alert/ecma-version-check.tsx index 091f3e30..c758051b 100644 --- a/packages/components/src/components/Alert/ecma-version-check.tsx +++ b/packages/components/src/components/Alert/ecma-version-check.tsx @@ -35,7 +35,7 @@ export const ECMAVersionCheck: React.FC = ({ data }) => { {sourceMessage} @@ -47,7 +47,7 @@ export const ECMAVersionCheck: React.FC = ({ data }) => { {outputMessage} @@ -59,7 +59,7 @@ export const ECMAVersionCheck: React.FC = ({ data }) => { {d.description} diff --git a/packages/components/src/components/Layout/header.scss b/packages/components/src/components/Layout/header.scss index 998f4a78..b85e6383 100644 --- a/packages/components/src/components/Layout/header.scss +++ b/packages/components/src/components/Layout/header.scss @@ -15,3 +15,11 @@ } } +.rsdoctor-logo { + cursor: pointer; + height: 30px; + + &:active { + opacity: 0.8; + } +} diff --git a/packages/components/src/components/Layout/header.tsx b/packages/components/src/components/Layout/header.tsx index cb9704bd..d63d438f 100644 --- a/packages/components/src/components/Layout/header.tsx +++ b/packages/components/src/components/Layout/header.tsx @@ -7,10 +7,13 @@ import { OverlayAlertsWithButton } from '../Alerts'; import { BuilderSelect } from './builder-select'; import { Menus } from './menus'; import './header.sass'; +import { Client } from '@rsdoctor/types'; +import { useNavigate } from 'react-router-dom'; export const Header: React.FC = () => { const { i18n } = useI18n(); + const navigate = useNavigate(); const { isLight } = useTheme(); const iconStyle: React.CSSProperties = { display: 'inline-block', @@ -61,10 +64,12 @@ export const Header: React.FC = () => { }} > logo { + navigate(Client.RsdoctorClientRoutes.Home); + }} /> diff --git a/packages/components/src/components/Loader/Analysis/style.module.scss b/packages/components/src/components/Loader/Analysis/style.module.scss index 5dd67318..d071d6cf 100644 --- a/packages/components/src/components/Loader/Analysis/style.module.scss +++ b/packages/components/src/components/Loader/Analysis/style.module.scss @@ -42,7 +42,6 @@ .text { font-size: 12px; font-weight: 400; - font-family: 'Roboto'; } } diff --git a/packages/components/src/utils/file.tsx b/packages/components/src/utils/file.tsx index 0d3521e0..b0debedd 100644 --- a/packages/components/src/utils/file.tsx +++ b/packages/components/src/utils/file.tsx @@ -191,7 +191,6 @@ export function readJSONByFileReader( const reader = new FileReader(); reader.onloadend = () => { const { result } = reader; - console.log('reader result: ', result); try { const json = JSON.parse(result!.toString()); resolve(json); diff --git a/packages/core/package.json b/packages/core/package.json index e1c9c89f..0fe28933 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@rsdoctor/core", - "version": "1.0.0-alpha.0", + "version": "1.0.0-alpha.3", "repository": { "type": "git", "url": "https://github.com/web-infra-dev/rsdoctor", @@ -72,7 +72,9 @@ "@rsdoctor/sdk": "workspace:*", "@rsdoctor/types": "workspace:*", "@rsdoctor/utils": "workspace:*", + "@types/circular-dependency-plugin": "^5.0.8", "axios": "^1.7.9", + "circular-dependency-plugin": "^5.2.2", "enhanced-resolve": "5.12.0", "filesize": "^10.1.6", "fs-extra": "^11.1.1", @@ -81,11 +83,11 @@ "semver": "^7.6.3", "source-map": "^0.7.4", "webpack-bundle-analyzer": "^4.10.2", - "@rsbuild/plugin-check-syntax": "1.2.0", + "@rsbuild/plugin-check-syntax": "1.2.1", "browserslist-load-config": "^1.0.0" }, "devDependencies": { - "@rspack/core": "^1.1.8", + "@rspack/core": "^1.2.2", "@scripts/test-helper": "workspace:*", "@types/fs-extra": "^11.0.4", "@types/lodash": "^4.17.14", @@ -96,10 +98,10 @@ "@types/tapable": "2.2.7", "babel-loader": "9.1.3", "string-loader": "0.0.1", - "ts-loader": "^9.5.1", + "ts-loader": "^9.5.2", "tslib": "2.8.1", "typescript": "^5.2.2", - "webpack": "^5.95.0" + "webpack": "^5.97.1" }, "publishConfig": { "access": "public", diff --git a/packages/core/src/build-utils/build/module-graph/webpack/transform.ts b/packages/core/src/build-utils/build/module-graph/webpack/transform.ts index 8b0bb1a2..b1e88f43 100644 --- a/packages/core/src/build-utils/build/module-graph/webpack/transform.ts +++ b/packages/core/src/build-utils/build/module-graph/webpack/transform.ts @@ -1,5 +1,6 @@ import type { SourceMapConsumer } from 'source-map'; import * as Webpack from 'webpack'; +import * as Rspack from '@rspack/core'; import { File } from '@rsdoctor/utils/build'; import { Node } from '@rsdoctor/utils/ruleUtils'; import { Plugin, SDK } from '@rsdoctor/types'; @@ -52,6 +53,24 @@ async function readFile(target: string, wbFs: WebpackFs) { return File.fse.readFile(target).catch(() => {}); } +/** + * Get the type of dependencies between modules. + * This property can determine what runtime webpack has added to the modules. + */ +export function getModuleExportsType( + module: Webpack.NormalModule | Rspack.NormalModule, + moduleGraph?: Webpack.ModuleGraph, + strict = false, +): SDK.DependencyBuildMeta['exportsType'] { + // webpack 5 + // https://github.com/webpack/webpack/blob/v5.75.0/lib/RuntimeTemplate.js#L771 + if (moduleGraph && 'getExportsType' in module) { + return module.getExportsType(moduleGraph, strict); + } + // Rspack does not support `getExportsType` yet. + return strict ? 'default-with-named' : 'dynamic'; +} + function appendDependency( webpackDep: Webpack.Dependency, module: SDK.ModuleInstance, @@ -94,7 +113,8 @@ function appendDependency( if (dependency) { dependency.setBuildMeta({ - exportsType: resolvedWebpackModule.getExportsType( + exportsType: getModuleExportsType( + resolvedWebpackModule, webpackGraph, module.meta.strictHarmonyModule, ), diff --git a/packages/core/src/build-utils/common/webpack/compatible.ts b/packages/core/src/build-utils/common/webpack/compatible.ts index a05caaaa..635be31b 100644 --- a/packages/core/src/build-utils/common/webpack/compatible.ts +++ b/packages/core/src/build-utils/common/webpack/compatible.ts @@ -61,7 +61,7 @@ export function getDependencyPosition( ): SDK.StatementInstance | undefined { const { loc: depLoc } = dep; - if (!('start' in depLoc)) { + if (depLoc === undefined || !('start' in depLoc)) { return; } diff --git a/packages/core/src/inner-plugins/plugins/bundleTagPlugin.ts b/packages/core/src/inner-plugins/plugins/bundleTagPlugin.ts index ea231a63..e0b4cd3e 100644 --- a/packages/core/src/inner-plugins/plugins/bundleTagPlugin.ts +++ b/packages/core/src/inner-plugins/plugins/bundleTagPlugin.ts @@ -20,9 +20,10 @@ export class InternalBundleTagPlugin< }, async () => { if ( - !compilation.options.plugins + (!compilation.options.plugins .map((p) => p && p.constructor.name) - .includes('BannerPlugin') || + .includes('BannerPlugin') && + supportBannerPlugin !== true) || supportBannerPlugin === false ) { return; diff --git a/packages/core/src/inner-plugins/plugins/ensureModulesChunkGraph.ts b/packages/core/src/inner-plugins/plugins/ensureModulesChunkGraph.ts index 98529540..1e612b27 100644 --- a/packages/core/src/inner-plugins/plugins/ensureModulesChunkGraph.ts +++ b/packages/core/src/inner-plugins/plugins/ensureModulesChunkGraph.ts @@ -104,7 +104,7 @@ export const ensureModulesChunksGraphFn = ( ChunksBuildUtils.TileGraphReportName, ), reportTitle: 'bundle-analyzer', - reportDir: _this.options.reportDir, + reportDir: _this.options.output.reportDir, }, compiler.outputPath, ); diff --git a/packages/core/src/inner-plugins/plugins/rule-plugins/circular-dependency/index.ts b/packages/core/src/inner-plugins/plugins/rule-plugins/circular-dependency/index.ts new file mode 100644 index 00000000..741d7a9c --- /dev/null +++ b/packages/core/src/inner-plugins/plugins/rule-plugins/circular-dependency/index.ts @@ -0,0 +1,156 @@ +import path from 'path'; +let extend = require('util')._extend; + +let BASE_ERROR = 'Circular dependency detected:\r\n' +let PluginTitle = 'CircularDependencyPlugin' + + +/** + * The following code is based on + * https://github.com/aackerman/circular-dependency-plugin/blob/master/index.js + * + * + * ISC Licensed + * Author aackerman + * Copyright (c) 2016, Aaron Ackerman . + * https://github.com/aackerman/circular-dependency-plugin/blob/master/LICENSE + */ +export class CircularDependencyPlugin { + options: { + exclude: RegExp; + include: RegExp; + failOnError: boolean; + allowAsyncCycles: boolean; + onDetected?: (data: { module: any; paths: string[]; compilation: any }) => void; + onStart?: (data: { compilation: any }) => void; + onEnd?: (data: { compilation: any }) => void; + cwd: string; + }; + + constructor(options: Partial) { + this.options = extend({ + exclude: new RegExp('$^'), + include: new RegExp('.*'), + failOnError: false, + allowAsyncCycles: false, + onDetected: false, + cwd: process.cwd() + }, options); + } + + apply(compiler: any) { + let plugin = this; + + compiler.hooks.compilation.tap(PluginTitle, (compilation: any) => { + compilation.hooks.optimizeModules.tap(PluginTitle, (modules: any[]) => { + if (plugin.options.onStart) { + plugin.options.onStart({ compilation }); + } + for (let module of modules) { + const shouldSkip = ( + module.resource == null || + plugin.options.exclude.test(module.resource) || + !plugin.options.include.test(module.resource) + ); + // skip the module if it matches the exclude pattern + if (shouldSkip) { + continue; + } + + let maybeCyclicalPathsList = this.isCyclic(module, module, {}, compilation); + if (maybeCyclicalPathsList) { + // allow consumers to override all behavior with onDetected + if (plugin.options.onDetected) { + try { + if (Array.isArray(maybeCyclicalPathsList)) { + plugin.options.onDetected({ + module: module, + paths: maybeCyclicalPathsList, + compilation: compilation + }); + } + } catch(err) { + compilation.errors.push(err); + } + continue; + } + + // mark warnings or errors on webpack compilation + let error = maybeCyclicalPathsList && typeof maybeCyclicalPathsList === 'object' && maybeCyclicalPathsList.length && new Error(BASE_ERROR.concat(maybeCyclicalPathsList.join(' -> '))); + if (plugin.options.failOnError) { + compilation.errors.push(error); + } else { + compilation.warnings.push(error); + } + } + } + if (plugin.options.onEnd) { + plugin.options.onEnd({ compilation }); + } + }); + }); + } + + isCyclic(initialModule: any, currentModule: any, seenModules: { [key: string]: boolean }, compilation: any): string[] | undefined | boolean { + let cwd = this.options.cwd; + + // Add the current module to the seen modules cache + seenModules[currentModule.debugId] = true; + + // If the modules aren't associated to resources + // it's not possible to display how they are cyclical + if (!currentModule.resource || !initialModule.resource) { + return false; + } + + // Iterate over the current modules dependencies + for (let dependency of currentModule.dependencies) { + if ( + dependency.constructor && + dependency.constructor.name === 'CommonJsSelfReferenceDependency' + ) { + continue; + } + + let depModule: any = null; + if (compilation.moduleGraph) { + // handle getting a module for webpack 5 + depModule = compilation.moduleGraph.getModule(dependency); + } else { + // handle getting a module for webpack 4 + depModule = dependency.module; + } + + if (!depModule) { continue; } + // ignore dependencies that don't have an associated resource + if (!depModule.resource) { continue; } + // ignore dependencies that are resolved asynchronously + if (this.options.allowAsyncCycles && dependency.weak) { continue; } + // the dependency was resolved to the current module due to how webpack internals + // setup dependencies like CommonJsSelfReferenceDependency and ModuleDecoratorDependency + if (currentModule === depModule) { + continue; + } + + if (depModule.debugId in seenModules) { + if (depModule.debugId === initialModule.debugId) { + // Initial module has a circular dependency + return [ + path.relative(cwd, currentModule.resource), + path.relative(cwd, depModule.resource) + ]; + } + // Found a cycle, but not for this module + continue; + } + + let maybeCyclicalPathsList: any = this.isCyclic(initialModule, depModule, seenModules, compilation); + if (maybeCyclicalPathsList) { + maybeCyclicalPathsList.unshift(path.relative(cwd, currentModule.resource)); + return maybeCyclicalPathsList; + } + } + + return false; + } +} \ No newline at end of file diff --git a/packages/core/src/inner-plugins/plugins/rule-plugins/index.ts b/packages/core/src/inner-plugins/plugins/rule-plugins/index.ts new file mode 100644 index 00000000..ec76fa62 --- /dev/null +++ b/packages/core/src/inner-plugins/plugins/rule-plugins/index.ts @@ -0,0 +1,10 @@ +import { CircularDependencyPlugin } from "./circular-dependency"; + +export const circularDependencyPlugin = new CircularDependencyPlugin({ + // 在这里添加所需的选项 + exclude: /node_modules/, + include: /src/, + failOnError: true, + allowAsyncCycles: false, + cwd: process.cwd(), +}); \ No newline at end of file diff --git a/packages/core/src/inner-plugins/plugins/rules.ts b/packages/core/src/inner-plugins/plugins/rules.ts index 0a3f4f3a..78cff2d2 100644 --- a/packages/core/src/inner-plugins/plugins/rules.ts +++ b/packages/core/src/inner-plugins/plugins/rules.ts @@ -4,12 +4,16 @@ import { DevToolError } from '@rsdoctor/utils/error'; import { isArray, pull } from 'lodash'; import { Plugin } from '@rsdoctor/types'; import { WebpackError } from 'webpack'; +import { circularDependencyPlugin } from './rule-plugins'; export class InternalRulesPlugin extends InternalBasePlugin { public readonly name = 'rules'; public apply(compiler: Plugin.BaseCompiler) { compiler.hooks.done.tapPromise(this.tapPreOptions, this.done); + + // Execute rules plugins. + circularDependencyPlugin.apply(compiler); } public done = async (stats: Plugin.BaseStats): Promise => { diff --git a/packages/core/src/inner-plugins/utils/config.ts b/packages/core/src/inner-plugins/utils/config.ts index c67d2c84..b0d78ed4 100644 --- a/packages/core/src/inner-plugins/utils/config.ts +++ b/packages/core/src/inner-plugins/utils/config.ts @@ -14,6 +14,7 @@ import { RsdoctorPluginOptionsNormalized, IReportCodeType, } from '@/types'; +import { chalk, logger } from '@rsdoctor/utils/logger'; function defaultBoolean(v: unknown, dft: boolean): boolean { return typeof v === 'boolean' ? v : dft; @@ -28,15 +29,17 @@ export function normalizeUserConfig( loaderInterceptorOptions = {}, disableClientServer = false, sdkInstance, - reportCodeType = { - noModuleSource: false, - noAssetsAndModuleSource: false, - noCode: false, - sourceCode: true, - assetsCode: true, - }, disableTOSUpload = false, innerClientPath = '', + output = { + reportCodeType: { + noModuleSource: false, + noAssetsAndModuleSource: false, + noCode: false, + }, + reportDir: '', + compressData: true, + }, supports = { parseBundle: true, banner: undefined, @@ -50,7 +53,6 @@ export function normalizeUserConfig( reportHtmlName: undefined, writeDataJson: false, }, - reportDir = '', } = config; assert(linter && typeof linter === 'object'); @@ -99,14 +101,28 @@ export function normalizeUserConfig( }, disableClientServer, sdkInstance, - /** - * Data storage is divided into three types: - * The first type: normal mode, all codes are saved. - * The second type: lite is the same as reportCodeType.noModuleSource, which lacks module source code. - * The third type: reportCodeType.noAssetsAndModuleSource means there is no module source code nor the packaged product code. - * - * */ - reportCodeType: normalizeReportType(reportCodeType, mode), + output: { + /** + * Data storage is divided into three types: + * The first type: normal mode, all codes are saved. + * The second type: lite is the same as reportCodeType.noModuleSource, which lacks module source code. + * The third type: reportCodeType.noAssetsAndModuleSource means there is no module source code nor the packaged product code. + * + * */ + reportCodeType: output.reportCodeType + ? normalizeReportType(output.reportCodeType, mode) + : normalizeReportType( + { + noModuleSource: false, + noAssetsAndModuleSource: false, + noCode: false, + }, + mode, + ), + reportDir: output.reportDir || '', + compressData: + output.compressData !== undefined ? output.compressData : true, + }, disableTOSUpload, innerClientPath, supports, @@ -114,9 +130,19 @@ export function normalizeUserConfig( printLog, mode, brief, - reportDir, }; - + if ( + !output.compressData && + ((output.reportCodeType && + !output.reportCodeType?.noAssetsAndModuleSource) || + !output.reportCodeType) + ) { + logger.info( + chalk.yellow( + `[RSDOCTOR]: When you use compressData: false, it is recommended to set output.reportCodeType to { noAssetsAndModuleSource: true }.`, + ), + ); + } return res; } @@ -198,10 +224,10 @@ export const normalizeReportType = ( default: { if (reportCodeType.noCode) { globalReportCodeType = SDK.ToDataType.NoCode; - } else if (reportCodeType.noModuleSource) { - globalReportCodeType = SDK.ToDataType.NoSource; } else if (reportCodeType.noAssetsAndModuleSource) { globalReportCodeType = SDK.ToDataType.NoSourceAndAssets; + } else if (reportCodeType.noModuleSource) { + globalReportCodeType = SDK.ToDataType.NoSource; } else { globalReportCodeType = SDK.ToDataType.Normal; } diff --git a/packages/core/src/types/plugin.ts b/packages/core/src/types/plugin.ts index 67b5c665..8ef066d4 100644 --- a/packages/core/src/types/plugin.ts +++ b/packages/core/src/types/plugin.ts @@ -40,7 +40,7 @@ export interface RsdoctorWebpackPluginOptions< mode?: keyof typeof SDK.IMode; /** - * configuration of the interceptor for webpack loaders. + * configuration of the interceptor for webpack loaders. TODO: delete this options. * @description worked when the `features.loader === true`. */ loaderInterceptorOptions?: { @@ -54,8 +54,9 @@ export interface RsdoctorWebpackPluginOptions< * @default false */ disableClientServer?: boolean; + /** - * sdk instance of outside + * sdk instance of outside. TODO: delete this options */ sdkInstance?: RsdoctorSDK; @@ -64,16 +65,6 @@ export interface RsdoctorWebpackPluginOptions< */ supports?: ISupport; - /** - * The directory where the report files will be output. - */ - reportDir?: string; - - /** - * Control the Rsdoctor reporter codes records. - */ - reportCodeType?: IReportCodeType | undefined; - /** * The port of the Rsdoctor server. */ @@ -90,16 +81,34 @@ export interface RsdoctorWebpackPluginOptions< brief?: SDK.BriefConfig; /** - * control the Rsdoctor upload data to TOS, used by inner-rsdoctor. + * control the Rsdoctor upload data to TOS, used by inner-rsdoctor. TODO: delete this options * @default false */ disableTOSUpload?: boolean; /** - * The name of inner rsdoctor's client package, used by inner-rsdoctor. + * The name of inner rsdoctor's client package, used by inner-rsdoctor. TODO: delete this options * @default false */ innerClientPath?: string; + + output?: { + /** + * The directory where the report files will be output. + */ + reportDir?: string; + + /** + * Control the Rsdoctor reporter codes records. + */ + reportCodeType?: IReportCodeType | undefined; + + /** + * Configure whether to compress data. + * @default false + */ + compressData?: boolean; + }; } export interface RsdoctorMultiplePluginOptions< @@ -123,19 +132,18 @@ export interface RsdoctorPluginOptionsNormalized< > extends Common.DeepRequired< Omit< RsdoctorWebpackPluginOptions, - | 'sdkInstance' - | 'linter' - | 'reportCodeType' - | 'supports' - | 'port' - | 'brief' + 'sdkInstance' | 'linter' | 'output' | 'supports' | 'port' | 'brief' > > { features: Common.DeepRequired; linter: Required>; sdkInstance?: RsdoctorSDK; + output: { + reportCodeType: SDK.ToDataType; + reportDir: string; + compressData: boolean; + }; port?: number; - reportCodeType: SDK.ToDataType; supports: ISupport; brief: SDK.BriefConfig; } diff --git a/packages/document/package.json b/packages/document/package.json index d68c5668..719df22d 100644 --- a/packages/document/package.json +++ b/packages/document/package.json @@ -1,6 +1,6 @@ { "name": "@rsdoctor/docs", - "version": "1.0.0-alpha.0", + "version": "1.0.0-alpha.3", "scripts": { "dev": "rspress dev", "build": "rspress build", @@ -39,7 +39,7 @@ }, "dependencies": { "@rstack-dev/doc-ui": "1.5.4", - "react-markdown": "^9.0.1", + "react-markdown": "^9.0.3", "rspress": "^1.39.4" } } diff --git a/packages/graph/package.json b/packages/graph/package.json index 271d1376..91abbf30 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -1,6 +1,6 @@ { "name": "@rsdoctor/graph", - "version": "1.0.0-alpha.0", + "version": "1.0.0-alpha.3", "repository": { "type": "git", "url": "https://github.com/web-infra-dev/rsdoctor", diff --git a/packages/rspack-plugin/package.json b/packages/rspack-plugin/package.json index 4abac267..51266751 100644 --- a/packages/rspack-plugin/package.json +++ b/packages/rspack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@rsdoctor/rspack-plugin", - "version": "1.0.0-alpha.0", + "version": "1.0.0-alpha.3", "repository": { "type": "git", "url": "https://github.com/web-infra-dev/rsdoctor", @@ -27,7 +27,7 @@ "lodash": "^4.17.21" }, "devDependencies": { - "@rspack/core": "^1.1.8", + "@rspack/core": "^1.2.2", "@types/lodash": "^4.17.14", "@types/node": "^16", "@types/tapable": "2.2.7", diff --git a/packages/rspack-plugin/src/multiple.ts b/packages/rspack-plugin/src/multiple.ts index e2872afc..b67090d5 100644 --- a/packages/rspack-plugin/src/multiple.ts +++ b/packages/rspack-plugin/src/multiple.ts @@ -36,7 +36,7 @@ export class RsdoctorRspackMultiplePlugin< mode: normallizedOptions.mode ? normallizedOptions.mode : undefined, brief: normallizedOptions.brief, }, - type: normallizedOptions.reportCodeType, + type: normallizedOptions.output.reportCodeType, }); super({ diff --git a/packages/rspack-plugin/src/plugin.ts b/packages/rspack-plugin/src/plugin.ts index ca1efafe..503cc0df 100644 --- a/packages/rspack-plugin/src/plugin.ts +++ b/packages/rspack-plugin/src/plugin.ts @@ -72,13 +72,14 @@ export class RsdoctorRspackPlugin port: this.options.port, name: pluginTapName, root: process.cwd(), - type: this.options.reportCodeType, + type: this.options.output.reportCodeType, config: { disableTOSUpload: this.options.disableTOSUpload, innerClientPath: this.options.innerClientPath, printLog: this.options.printLog, mode: this.options.mode ? this.options.mode : undefined, brief: this.options.brief, + compressData: this.options.output.compressData, }, }); this.outsideInstance = Boolean(this.options.sdkInstance); @@ -191,7 +192,7 @@ export class RsdoctorRspackPlugin if (this.outsideInstance && 'parent' in this.sdk) { this.sdk.parent.master.setOutputDir( path.resolve( - this.options.reportDir || compiler.outputPath, + this.options.output.reportDir || compiler.outputPath, `./${Constants.RsdoctorOutputFolder}`, ), ); @@ -199,7 +200,7 @@ export class RsdoctorRspackPlugin this.sdk.setOutputDir( path.resolve( - this.options.reportDir || compiler.outputPath, + this.options.output.reportDir || compiler.outputPath, `./${Constants.RsdoctorOutputFolder}`, ), ); @@ -251,7 +252,7 @@ export class RsdoctorRspackPlugin this.sdk.setOutputDir( path.resolve( - this.options.reportDir || compiler.outputPath, + this.options.output.reportDir || compiler.outputPath, `./${Constants.RsdoctorOutputFolder}`, ), ); diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 07f6fd5c..9b669891 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@rsdoctor/sdk", - "version": "1.0.0-alpha.0", + "version": "1.0.0-alpha.3", "repository": { "type": "git", "url": "https://github.com/web-infra-dev/rsdoctor", diff --git a/packages/sdk/src/sdk/sdk/core.ts b/packages/sdk/src/sdk/sdk/core.ts index 65447f78..22bfdaa3 100644 --- a/packages/sdk/src/sdk/sdk/core.ts +++ b/packages/sdk/src/sdk/sdk/core.ts @@ -17,7 +17,7 @@ export abstract class SDKCore protected hash!: string; - extraConfig: SDK.SDKOptionsType | undefined; + public extraConfig: SDK.SDKOptionsType | undefined; public readonly root: string; @@ -155,11 +155,19 @@ export abstract class SDKCore if (Array.isArray(jsonStr)) { const urls = jsonStr.map((str, index) => { - return this.writeToFolder(str, outputDir, key, index + 1); + return this.writeToFolder( + str, + outputDir, + key, + this.extraConfig, + index + 1, + ); }); urlsPromiseList.push(...urls); } else { - urlsPromiseList.push(this.writeToFolder(jsonStr, outputDir, key)); + urlsPromiseList.push( + this.writeToFolder(jsonStr, outputDir, key, this.extraConfig), + ); } } @@ -212,9 +220,13 @@ export abstract class SDKCore jsonStr: string, dir: string, key: string, + extraConfig: SDK.SDKOptionsType | undefined, index?: number, ): Promise { - const sharding = new File.FileSharding(Algorithm.compressText(jsonStr)); + const { compressData } = extraConfig || { compressData: true }; + const sharding = compressData + ? new File.FileSharding(Algorithm.compressText(jsonStr)) + : new File.FileSharding(jsonStr); const folder = path.resolve(dir, key); const writer = sharding.writeStringToFolder(folder, '', index); return writer.then((item) => { diff --git a/packages/types/package.json b/packages/types/package.json index 829589b2..eddaae8b 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@rsdoctor/types", - "version": "1.0.0-alpha.0", + "version": "1.0.0-alpha.3", "repository": { "type": "git", "url": "https://github.com/web-infra-dev/rsdoctor", @@ -24,7 +24,7 @@ "source-map": "^0.7.4" }, "devDependencies": { - "@rspack/core": "^1.1.8", + "@rspack/core": "^1.2.2", "@types/node": "^16", "@types/react": "^18.3.18", "tslib": "2.8.1", diff --git a/packages/types/src/client.ts b/packages/types/src/client.ts index 03ad175b..2cf41983 100644 --- a/packages/types/src/client.ts +++ b/packages/types/src/client.ts @@ -4,6 +4,7 @@ export enum RsdoctorClientUrlQuery { } export enum RsdoctorClientRoutes { + Home = '/', Overall = '/overall', WebpackLoaderOverall = '/webpack/loaders/overall', WebpackLoaderAnalysis = '/webpack/loaders/analysis', diff --git a/packages/types/src/sdk/instance.ts b/packages/types/src/sdk/instance.ts index 39d7d9d1..c2d5ecb6 100644 --- a/packages/types/src/sdk/instance.ts +++ b/packages/types/src/sdk/instance.ts @@ -128,6 +128,7 @@ export type SDKOptionsType = { printLog?: IPrintLog; mode?: keyof typeof IMode; brief?: BriefConfig; + compressData?: boolean; }; /** diff --git a/packages/utils/package.json b/packages/utils/package.json index 7f031243..4328860d 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@rsdoctor/utils", - "version": "1.0.0-alpha.0", + "version": "1.0.0-alpha.3", "repository": { "type": "git", "url": "https://github.com/web-infra-dev/rsdoctor", diff --git a/packages/webpack-plugin/package.json b/packages/webpack-plugin/package.json index a7f3a3bc..13d5631c 100644 --- a/packages/webpack-plugin/package.json +++ b/packages/webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@rsdoctor/webpack-plugin", - "version": "1.0.0-alpha.0", + "version": "1.0.0-alpha.3", "repository": { "type": "git", "url": "https://github.com/web-infra-dev/rsdoctor", @@ -34,7 +34,7 @@ "@types/tapable": "2.2.7", "tslib": "2.8.1", "typescript": "^5.2.2", - "webpack": "^5.95.0" + "webpack": "^5.97.1" }, "peerDependencies": { "webpack": "5.x" diff --git a/packages/webpack-plugin/src/multiple.ts b/packages/webpack-plugin/src/multiple.ts index 6d41734f..a17bd075 100644 --- a/packages/webpack-plugin/src/multiple.ts +++ b/packages/webpack-plugin/src/multiple.ts @@ -35,8 +35,9 @@ export class RsdoctorWebpackMultiplePlugin< printLog: normallizedOptions.printLog, mode: normallizedOptions.mode ? normallizedOptions.mode : undefined, brief: normallizedOptions.brief, + compressData: normallizedOptions.output.compressData, }, - type: normallizedOptions.reportCodeType, + type: normallizedOptions.output.reportCodeType, }); super({ diff --git a/packages/webpack-plugin/src/plugin.ts b/packages/webpack-plugin/src/plugin.ts index ab368bdf..2d8e346a 100644 --- a/packages/webpack-plugin/src/plugin.ts +++ b/packages/webpack-plugin/src/plugin.ts @@ -58,15 +58,17 @@ export class RsdoctorWebpackPlugin port: this.options.port, name: pluginTapName, root: process.cwd(), - type: this.options.reportCodeType, + type: this.options.output.reportCodeType, config: { disableTOSUpload: this.options.disableTOSUpload, innerClientPath: this.options.innerClientPath, printLog: this.options.printLog, - mode: this.options.mode ? this.options.mode : undefined, + mode: this.options.mode, brief: this.options.brief, + compressData: this.options.output.compressData, }, }); + this.modulesGraph = new ModuleGraph(); this.chunkGraph = new ChunkGraph(); this.isRsdoctorPlugin = true; @@ -163,7 +165,7 @@ export class RsdoctorWebpackPlugin this.sdk.setOutputDir( path.resolve( - this.options.reportDir || compiler.outputPath, + this.options.output.reportDir || compiler.outputPath, `./${Constants.RsdoctorOutputFolder}`, ), ); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 90d1bfe3..52cf9eb8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -696,12 +696,18 @@ importers: '@rsdoctor/utils': specifier: workspace:* version: link:../utils + '@types/circular-dependency-plugin': + specifier: ^5.0.8 + version: 5.0.8 axios: specifier: ^1.7.9 version: 1.7.9(debug@4.3.7) browserslist-load-config: specifier: ^1.0.0 version: 1.0.0 + circular-dependency-plugin: + specifier: ^5.2.2 + version: 5.2.2(webpack@5.97.1) enhanced-resolve: specifier: 5.12.0 version: 5.12.0 @@ -6495,6 +6501,18 @@ packages: '@types/node': 16.18.123 dev: true + /@types/circular-dependency-plugin@5.0.8: + resolution: {integrity: sha512-P9nspqZxy+9NsoS4KOG5p1JFss0n0pv8UHD8OAP7bz4lAkgtdAnOS0J0ZXlcqzekWdUtmCFwpIuW+AtOhFp8VA==} + dependencies: + '@types/node': 16.18.123 + webpack: 5.97.1(webpack-cli@5.1.4) + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + - webpack-cli + dev: false + /@types/configstore@2.1.1: resolution: {integrity: sha512-YY+hm3afkDHeSM2rsFXxeZtu0garnusBWNG1+7MknmDWQHqcH2w21/xOU9arJUi8ch4qyFklidANLCu3ihhVwQ==} dev: false @@ -8232,6 +8250,15 @@ packages: safe-buffer: 5.2.1 dev: true + /circular-dependency-plugin@5.2.2(webpack@5.97.1): + resolution: {integrity: sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==} + engines: {node: '>=6.0.0'} + peerDependencies: + webpack: '>=4.0.1' + dependencies: + webpack: 5.97.1(webpack-cli@5.1.4) + dev: false + /classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} diff --git a/scripts/test-helper/package.json b/scripts/test-helper/package.json index 5c0dde22..39c912a5 100644 --- a/scripts/test-helper/package.json +++ b/scripts/test-helper/package.json @@ -25,14 +25,14 @@ "dev": "modern build --watch" }, "dependencies": { - "@rspack/core": "^1.1.8", + "@rspack/core": "^1.2.2", "@types/lodash": "^4.17.14", "@types/node": "^16", "lodash": "^4.17.21", - "memfs": "4.15.3", + "memfs": "4.17.0", "typescript": "^5.2.2", "upath": "2.0.1", - "webpack": "^5.95.0" + "webpack": "^5.97.1" }, "publishConfig": { "access": "public", diff --git a/scripts/update-packages/package.json b/scripts/update-packages/package.json index 6e1a4612..530a7b30 100644 --- a/scripts/update-packages/package.json +++ b/scripts/update-packages/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "@manypkg/get-packages": "^2.2.2", - "@modern-js/generator-utils": "^3.7.11", + "@modern-js/generator-utils": "^3.7.13", "fs-extra": "^11.1.1" }, "devDependencies": {