diff --git a/.github/workflows/test-ubuntu-doctor.yml b/.github/workflows/test-ubuntu-doctor.yml new file mode 100644 index 0000000000..91fde5b9e9 --- /dev/null +++ b/.github/workflows/test-ubuntu-doctor.yml @@ -0,0 +1,62 @@ +name: Test (Ubuntu) + +# Controls when the action will run. +on: + # Triggers the workflow on pull request events but only for the main branch + pull_request: + branches: [main] + + push: + branches: [main] + paths: + - "packages/doctor-*" + paths-ignore: + - "*.md" + - "*.mdx" + + merge_group: + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # ===== e2e for doctor ======= + e2e-ubuntu-doctor: + runs-on: ubuntu-latest + strategy: + matrix: + # Rsbuild need to compat Node.js 16 + node-version: [16.x] + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 10 + + - name: Install Pnpm + run: corepack enable + + - name: Check skip CI + run: echo "RESULT=$(node ./scripts/skipCI.js)" >> "$GITHUB_OUTPUT" + id: skip-ci + + - name: Log skip CI result + run: echo "${{steps.skip-ci.outputs.RESULT}}" + + - name: Setup Node.js ${{ matrix.node-version }} + if: ${{steps.skip-ci.outputs.RESULT != 'true'}} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + cache: 'pnpm' + + - name: Install Dependencies + if: ${{steps.skip-ci.outputs.RESULT != 'true'}} + run: pnpm install && cd ./e2e && npx playwright install + + - name: E2E Test + if: ${{steps.skip-ci.outputs.RESULT != 'true'}} + run: pnpm run e2e:doctor \ No newline at end of file diff --git a/.github/workflows/test-windows-doctor.yml b/.github/workflows/test-windows-doctor.yml new file mode 100644 index 0000000000..d2928937fe --- /dev/null +++ b/.github/workflows/test-windows-doctor.yml @@ -0,0 +1,67 @@ +name: Test (Windows) + +# Controls when the action will run. +on: + # Triggers the workflow on pull request events but only for the main branch + pull_request: + branches: [main] + + push: + branches: [main] + paths: + - "packages/doctor-*" + paths-ignore: + - "*.md" + - "*.mdx" + + merge_group: + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # ======== e2e for doctor======== + e2e-windows-doctor: + runs-on: windows-latest + strategy: + matrix: + node-version: [18.x] + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + - name: Git config + shell: bash + run: | + git config --system core.longpaths true + + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 10 + + - name: Install Pnpm + run: corepack enable + + - name: Check skip CI + shell: bash + run: echo "RESULT=$(node ./scripts/skipCI.js)" >> "$GITHUB_OUTPUT" + id: skip-ci + + - name: Log skip CI result + run: echo "${{steps.skip-ci.outputs.RESULT}}" + + - name: Setup Node.js ${{ matrix.node-version }} + if: ${{steps.skip-ci.outputs.RESULT != 'true'}} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + cache: 'pnpm' + + - name: Install Dependencies + if: ${{steps.skip-ci.outputs.RESULT != 'true'}} + run: pnpm install && cd ./e2e && npx playwright install + + - name: E2E Test + if: ${{steps.skip-ci.outputs.RESULT != 'true'}} + run: pnpm run e2e:doctor diff --git a/packages/doctor-rspack-plugin/tests/fixtures/a.js b/e2e/cases/doctor-rspack/tests/fixtures/a.js similarity index 100% rename from packages/doctor-rspack-plugin/tests/fixtures/a.js rename to e2e/cases/doctor-rspack/tests/fixtures/a.js diff --git a/packages/doctor-rspack-plugin/tests/fixtures/b.js b/e2e/cases/doctor-rspack/tests/fixtures/b.js similarity index 100% rename from packages/doctor-rspack-plugin/tests/fixtures/b.js rename to e2e/cases/doctor-rspack/tests/fixtures/b.js diff --git a/packages/doctor-rspack-plugin/tests/fixtures/loaders/comment.js b/e2e/cases/doctor-rspack/tests/fixtures/loaders/comment.js similarity index 100% rename from packages/doctor-rspack-plugin/tests/fixtures/loaders/comment.js rename to e2e/cases/doctor-rspack/tests/fixtures/loaders/comment.js diff --git a/packages/doctor-rspack-plugin/tests/fixtures/loaders/serialize-query-to-comment.js b/e2e/cases/doctor-rspack/tests/fixtures/loaders/serialize-query-to-comment.js similarity index 100% rename from packages/doctor-rspack-plugin/tests/fixtures/loaders/serialize-query-to-comment.js rename to e2e/cases/doctor-rspack/tests/fixtures/loaders/serialize-query-to-comment.js diff --git a/packages/doctor-rspack-plugin/tests/fixtures/port.js b/e2e/cases/doctor-rspack/tests/fixtures/port.js similarity index 100% rename from packages/doctor-rspack-plugin/tests/fixtures/port.js rename to e2e/cases/doctor-rspack/tests/fixtures/port.js diff --git a/e2e/cases/doctor-rspack/tests/plugin.test.ts b/e2e/cases/doctor-rspack/tests/plugin.test.ts new file mode 100644 index 0000000000..265362f3e3 --- /dev/null +++ b/e2e/cases/doctor-rspack/tests/plugin.test.ts @@ -0,0 +1,82 @@ +import { expect, test } from '@playwright/test'; +import { getSDK } from '@rsbuild/doctor-core/plugins'; +import { compileByRspack } from '@rsbuild/test-helper'; +import { Compiler } from '@rspack/core'; +import os from 'os'; +import path from 'path'; +import { createDoctorPlugin } from './test-utils'; + +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 res = await compile(file, { + module: { + rules: [ + { + test: /\.js/, + use: loader, + }, + ], + }, + plugins: [ + // @ts-ignore + createDoctorPlugin({}), + { + name: tapName, + apply(compiler: Compiler) { + compiler.hooks.done.tapPromise(tapName, async () => { + // nothing + }); + compiler.hooks.thisCompilation.tap(tapName, (compilation) => { + compilation.hooks.processAssets.tap(tapName, () => { + return 'processAssets end'; + }); + }); + }, + }, + ], + }); + + return res; +} + +test('rspack plugin intercept', async () => { + const tapName = 'XXX'; + await rspackCompile(tapName, compileByRspack); + const sdk = getSDK(); + const { done, thisCompilation } = sdk.getStoreData().plugin; + const doneData = done.filter((e) => e.tapName === tapName); + expect(doneData).toHaveLength(1); + expect(doneData[0].type).toEqual('promise'); + expect(doneData[0].result).toBeNull(); + + const sealData = thisCompilation.filter((e) => e.tapName === tapName); + expect(sealData).toHaveLength(1); + expect(sealData[0].type).toEqual('sync'); + expect(sealData[0].result).toBeNull(); +}); + +test('rspack data store', async () => { + const tapName = 'XXX'; + await rspackCompile(tapName, compileByRspack); + const sdk = getSDK(); + const datas = sdk.getStoreData(); + expect(datas.errors.length).toBe(0); + const graphData = datas.moduleGraph; + + os.EOL === '\n' + ? expect( + graphData.modules[0].webpackId.indexOf('tests/fixtures/a.js'), + ).toBeGreaterThan(0) + : expect( + graphData.modules[0].webpackId.indexOf('\\tests\\fixtures\\a.js'), + ).toBeGreaterThan(0); + + graphData.modules.forEach((mod) => (mod.webpackId = '')); + expect(graphData.modules[0].size).toEqual({ + sourceSize: 33, + transformedSize: 33, + parsedSize: 0, + }); + expect(graphData.modules[0].path).toMatch('tests/fixtures/a.js'); +}); diff --git a/packages/doctor-rspack-plugin/tests/test-utils.ts b/e2e/cases/doctor-rspack/tests/test-utils.ts similarity index 77% rename from packages/doctor-rspack-plugin/tests/test-utils.ts rename to e2e/cases/doctor-rspack/tests/test-utils.ts index 662d1a3752..19b7672f0b 100644 --- a/packages/doctor-rspack-plugin/tests/test-utils.ts +++ b/e2e/cases/doctor-rspack/tests/test-utils.ts @@ -1,9 +1,9 @@ import { DoctorRspackPluginOptions } from '@rsbuild/doctor-core/types'; +import { RsbuildDoctorRspackPlugin } from '@rsbuild/doctor-rspack-plugin'; import { Linter } from '@rsbuild/doctor-types'; import { File } from '@rsbuild/doctor-utils/build'; import { tmpdir } from 'os'; import path from 'path'; -import { RsbuildDoctorRspackPlugin } from '../src'; export function createDoctorPlugin( options: DoctorRspackPluginOptions, @@ -20,10 +20,14 @@ export function createDoctorPlugin( tmpdir(), `./${Date.now()}/rsbuild_doctor_rspack_plugin_test`, ); - plugin.sdk.setOutputDir(outdir); plugin.sdk.hooks.afterSaveManifest.tapPromise('REMOVE_TMP_DIR', async () => { - await File.fse.remove(plugin.sdk.outputDir); + plugin.sdk.setOutputDir(outdir); + try { + await File.fse.remove(plugin.sdk.outputDir); + } catch (e) { + console.error(e); + } }); return plugin; diff --git a/packages/doctor-webpack-plugin/tests/fixtures/a.js b/e2e/cases/doctor-webpack/fixtures/a.js similarity index 100% rename from packages/doctor-webpack-plugin/tests/fixtures/a.js rename to e2e/cases/doctor-webpack/fixtures/a.js diff --git a/packages/doctor-webpack-plugin/tests/fixtures/b.js b/e2e/cases/doctor-webpack/fixtures/b.js similarity index 100% rename from packages/doctor-webpack-plugin/tests/fixtures/b.js rename to e2e/cases/doctor-webpack/fixtures/b.js diff --git a/packages/doctor-webpack-plugin/tests/fixtures/loaders/comment.js b/e2e/cases/doctor-webpack/fixtures/loaders/comment.js similarity index 100% rename from packages/doctor-webpack-plugin/tests/fixtures/loaders/comment.js rename to e2e/cases/doctor-webpack/fixtures/loaders/comment.js diff --git a/packages/doctor-webpack-plugin/tests/fixtures/loaders/serialize-query-to-comment.js b/e2e/cases/doctor-webpack/fixtures/loaders/serialize-query-to-comment.js similarity index 100% rename from packages/doctor-webpack-plugin/tests/fixtures/loaders/serialize-query-to-comment.js rename to e2e/cases/doctor-webpack/fixtures/loaders/serialize-query-to-comment.js diff --git a/packages/doctor-webpack-plugin/tests/fixtures/port.js b/e2e/cases/doctor-webpack/fixtures/port.js similarity index 100% rename from packages/doctor-webpack-plugin/tests/fixtures/port.js rename to e2e/cases/doctor-webpack/fixtures/port.js diff --git a/e2e/cases/doctor-webpack/loaders/proxy.test.ts b/e2e/cases/doctor-webpack/loaders/proxy.test.ts new file mode 100644 index 0000000000..4054c0da1d --- /dev/null +++ b/e2e/cases/doctor-webpack/loaders/proxy.test.ts @@ -0,0 +1,227 @@ +import { expect, test } from '@playwright/test'; +import { getSDK, setSDK } from '@rsbuild/doctor-core/plugins'; +import { DoctorWebpackSDK } from '@rsbuild/doctor-sdk/sdk'; +import { SDK } from '@rsbuild/doctor-types'; +import { compileByWebpack5 } from '@rsbuild/test-helper'; +import os from 'os'; +import path from 'path'; +import { Compiler } from 'webpack'; +import { createRsbuildDoctorPlugin } from '../test-utils'; + +const file = path.resolve(__dirname, '../fixtures/a.js'); +const loaderPath = path.resolve(__dirname, '../fixtures/loaders/comment.js'); +const codeTransformed = `console.log('a');\n\n// hello world`; + +async function webpack5(mode: 'async' | 'callback' | 'sync', pitchResult = '') { + const res = await compileByWebpack5(file, { + module: { + rules: [ + { + test: /\.js$/, + use: [ + { + loader: loaderPath, + options: { + mode, + pitchResult, + }, + }, + ], + }, + ], + }, + // @ts-ignore + plugins: [createRsbuildDoctorPlugin()], + }); + return res; +} + +test('webpack5', async () => { + const mode = 'sync'; + const { modules } = await webpack5(mode); + + expect(modules!.length).toEqual(1); + expect(getSDK()).toBeInstanceOf(DoctorWebpackSDK); + + const { loader } = getSDK().getStoreData(); + + expect(loader).toHaveLength(1); + expect(loader[0].resource).toStrictEqual({ + path: file, + query: {}, + queryRaw: '', + ext: 'js', + } as SDK.ResourceData); + expect(loader[0].loaders[0].isPitch).toBeFalsy(); + expect(loader[0].loaders[0].loaderIndex).toEqual(0); + expect(loader[0].loaders[0].path).toEqual(loaderPath); + os.EOL === '\n' && + expect(loader[0].loaders[0].result).toEqual(codeTransformed); + expect(loader[0].loaders[0].options).toStrictEqual({ + mode, + pitchResult: '', + }); + expect(loader[0].loaders[0].errors).toHaveLength(0); +}); + +test('test async', async () => { + const mode = 'async'; + const { modules } = await webpack5(mode); + + expect(modules!.length).toEqual(1); + expect(getSDK()).toBeInstanceOf(DoctorWebpackSDK); + + const { loader } = getSDK().getStoreData(); + + expect(loader).toHaveLength(1); + expect(loader[0].resource).toStrictEqual({ + path: file, + query: {}, + queryRaw: '', + ext: 'js', + } as SDK.ResourceData); + expect(loader[0].loaders[0].isPitch).toBeFalsy(); + expect(loader[0].loaders[0].loaderIndex).toEqual(0); + expect(loader[0].loaders[0].path).toEqual(loaderPath); + os.EOL === '\n' && + expect(loader[0].loaders[0].result).toEqual(codeTransformed); + expect(loader[0].loaders[0].options).toStrictEqual({ + mode, + pitchResult: '', + }); + expect(loader[0].loaders[0].errors).toHaveLength(0); +}); + +test('test callback', async () => { + const mode = 'callback'; + const { modules } = await webpack5(mode); + + expect(modules!.length).toEqual(1); + expect(getSDK()).toBeInstanceOf(DoctorWebpackSDK); + + const { loader } = getSDK().getStoreData(); + + expect(loader).toHaveLength(1); + expect(loader[0].resource).toStrictEqual({ + path: file, + query: {}, + queryRaw: '', + ext: 'js', + } as SDK.ResourceData); + expect(loader[0].loaders[0].isPitch).toBeFalsy(); + expect(loader[0].loaders[0].loaderIndex).toEqual(0); + expect(loader[0].loaders[0].path).toEqual(loaderPath); + os.EOL === '\n' && + expect(loader[0].loaders[0].result).toEqual(codeTransformed); + expect(loader[0].loaders[0].options).toStrictEqual({ + mode, + pitchResult: '', + }); + expect(loader[0].loaders[0].errors).toHaveLength(0); +}); + +test('test pitch', async () => { + const mode = 'callback'; + const pitchResult = '// pitch success'; + const { modules } = await webpack5(mode, pitchResult); + + expect(modules!.length).toEqual(1); + expect(getSDK()).toBeInstanceOf(DoctorWebpackSDK); + + const { loader } = getSDK().getStoreData(); + + expect(loader).toHaveLength(1); + expect(loader[0].resource).toStrictEqual({ + path: file, + query: {}, + queryRaw: '', + ext: 'js', + } as SDK.ResourceData); + expect(loader[0].loaders[0].isPitch).toBeTruthy(); + expect(loader[0].loaders[0].loaderIndex).toEqual(0); + expect(loader[0].loaders[0].path).toEqual(loaderPath); + expect(loader[0].loaders[0].result).toEqual(pitchResult); + expect(loader[0].loaders[0].options).toStrictEqual({ mode, pitchResult }); + expect(loader[0].loaders[0].errors).toHaveLength(0); +}); + +test('set sdk.reportLoader as null to mock this scene', async () => { + const mode = 'sync'; + const plugin = createRsbuildDoctorPlugin(); + const { modules } = await compileByWebpack5(file, { + module: { + rules: [ + { + test: /\.js$/, + use: [ + { + loader: loaderPath, + options: { + mode, + pitchResult: '', + }, + }, + ], + }, + ], + }, + plugins: [ + // @ts-ignore + plugin, + // @ts-ignore + { + name: 'XXX', + apply(compiler: Compiler) { + compiler.hooks.beforeRun.tapPromise( + { name: 'XXX', stage: 99999 }, + async () => { + const sdk = getSDK(); + setSDK( + new Proxy(sdk, { + get(target, key, receiver) { + switch (key) { + case 'reportLoader': + return null; + 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); + }, + }), + ); + }, + ); + }, + }, + ], + }); + + expect(modules!.length).toEqual(1); + expect(getSDK().reportLoader).toEqual(null); + + // @ts-ignore + const { loader } = plugin.sdk.getStoreData(); + + expect(loader).toHaveLength(1); + expect(loader[0].resource).toStrictEqual({ + path: file, + query: {}, + queryRaw: '', + ext: 'js', + } as SDK.ResourceData); + expect(loader[0].loaders[0].isPitch).toBeFalsy(); + expect(loader[0].loaders[0].loaderIndex).toEqual(0); + expect(loader[0].loaders[0].path).toEqual(loaderPath); + os.EOL === '\n' && + expect(loader[0].loaders[0].result).toEqual(codeTransformed); + expect(loader[0].loaders[0].options).toStrictEqual({ + mode, + pitchResult: '', + }); + expect(loader[0].loaders[0].errors).toHaveLength(0); +}); diff --git a/e2e/cases/doctor-webpack/loaders/query.test.ts b/e2e/cases/doctor-webpack/loaders/query.test.ts new file mode 100644 index 0000000000..41a0e61fda --- /dev/null +++ b/e2e/cases/doctor-webpack/loaders/query.test.ts @@ -0,0 +1,67 @@ +import { expect, test } from '@playwright/test'; +import { getSDK } from '@rsbuild/doctor-core/plugins'; +import { compileByWebpack5 } from '@rsbuild/test-helper'; +import os from 'os'; +import path from 'path'; +import qs from 'querystring'; +import { createRsbuildDoctorPlugin } from '../test-utils'; + +const file = path.resolve(__dirname, '../fixtures/a.js'); +const loaderPath = path.resolve( + __dirname, + '../fixtures/loaders/serialize-query-to-comment.js', +); + +async function webpack5(query?: string) { + const res = await compileByWebpack5(query ? `${file}${query}` : file, { + module: { + rules: [ + { + test: /\.js$/, + use: [ + { + loader: loaderPath, + }, + ], + }, + ], + }, + // @ts-ignore + plugins: [createRsbuildDoctorPlugin()], + }); + return res; +} + +test('webpack5', async () => { + const codeTransformed = + os.EOL === '\n' + ? `console.log('a');\n\n// ${JSON.stringify('')}` + : `console.log('a');\r\n\n// ${JSON.stringify('')}`; + + await webpack5(); + + const { loader } = getSDK().getStoreData(); + expect(loader).toHaveLength(1); + os.EOL === '\n' && + expect(loader[0].loaders[0].result).toEqual(codeTransformed); +}); + +test('query exists', async () => { + // number are not parsed: https://github.com/webpack/loader-utils/tree/v2.0.0-branch#parsequery + const query = { test: '111' }; + const querystring = `?${qs.stringify(query)}`; + const codeTransformed = + os.EOL === '\n' + ? `console.log('a');\n\n// ${JSON.stringify( + querystring, + )}\n// ${JSON.stringify(query)}` + : `console.log('a');\r\n\n// ${JSON.stringify( + querystring, + )}\n// ${JSON.stringify(query)}`; + + await webpack5(querystring); + + const { loader } = getSDK().getStoreData(); + expect(loader).toHaveLength(1); + expect(loader[0].loaders[0].result).toEqual(codeTransformed); +}); diff --git a/e2e/cases/doctor-webpack/plugins/loader.test.ts b/e2e/cases/doctor-webpack/plugins/loader.test.ts new file mode 100644 index 0000000000..74a46b1e8a --- /dev/null +++ b/e2e/cases/doctor-webpack/plugins/loader.test.ts @@ -0,0 +1,202 @@ +import { Common } from '@rsbuild/doctor-types'; +import { compileByWebpack5 } from '@rsbuild/test-helper'; +import { cloneDeep } from 'lodash'; +import path from 'path'; +import { test, expect } from '@playwright/test'; +import type { NormalModule, WebpackPluginInstance } from 'webpack'; +import { createRsbuildDoctorPlugin } from '../test-utils'; + +const testLoaderPath = path.resolve( + __dirname, + '../fixtures/loaders/comment.js', +); + +async function webpack( + compile: typeof compileByWebpack5, + transformer: (module: NormalModule) => void, +) { + const file = path.resolve(__dirname, '../fixtures/b.js'); + + const beforeTransform = (data: any) => data; + let beforeTransformRes; + const afterTransform = (data: any) => data; + let afterTransformRes; + + /** + * Based on https://github.com/arco-design/arco-plugins/blob/main/packages/plugin-webpack-react/src/arco-design-plugin/utils/index.ts#L37 + */ + const arcoDesignPluginForked: WebpackPluginInstance = { + apply(compiler) { + const pluginName = 'arco-design-plugin-forked'; + const mapper = (module: NormalModule) => + module.loaders.map((e) => ({ + loader: e.loader, + options: cloneDeep(e.options), + })); + const hookHandler = ( + context: Common.PlainObject, + module: NormalModule, + ) => { + beforeTransformRes = beforeTransform(mapper(module)); + transformer(module); + afterTransformRes = afterTransform(mapper(module)); + }; + // @ts-ignore + compiler.hooks.compilation.tap(pluginName, (compilation) => { + // for webpack 5 + if ( + compiler.webpack && + compiler.webpack.NormalModule && + compiler.webpack.NormalModule.getCompilationHooks + ) { + compiler.webpack.NormalModule.getCompilationHooks( + compilation, + ).loader.tap(pluginName, hookHandler); + } else if (compilation.hooks) { + // for webpack 4 + compilation.hooks.normalModuleLoader.tap(pluginName, hookHandler); + } + }); + }, + }; + + const RsbuildDoctorPlugin = createRsbuildDoctorPlugin({}); + + const result = await compile(file, { + optimization: { + minimize: true, + }, + module: { + rules: [ + { + test: /\.js$/, + use: { + loader: testLoaderPath, + options: { + mode: 'callback', + }, + }, + }, + ], + }, + plugins: [ + // @ts-ignore + RsbuildDoctorPlugin, + // @ts-ignore + arcoDesignPluginForked, + ], + }); + + return { + RsbuildDoctorPlugin, + loaderData: RsbuildDoctorPlugin.sdk.getStoreData().loader, + afterTransformRes, + beforeTransformRes, + }; +} + +function createTests(title: string, compile: typeof compileByWebpack5) { + test(`${title} basic usage`, async () => { + const { loaderData, beforeTransformRes, afterTransformRes } = await webpack( + compile, + () => {}, + ); + + expect(beforeTransformRes).toStrictEqual([ + { loader: testLoaderPath, options: { mode: 'callback' } }, + ]); + + expect(afterTransformRes).toStrictEqual([ + { loader: testLoaderPath, options: { mode: 'callback' } }, + ]); + + // test the data from sdk + const { options, loader } = loaderData[0].loaders[0]; + expect(loader).toEqual(testLoaderPath); + expect(options).toStrictEqual({ mode: 'callback' }); + }); + + test(`${title} overwrite loader options`, async () => { + const { loaderData, beforeTransformRes, afterTransformRes } = await webpack( + compile, + (module) => { + module.loaders[0].options.mode = 'async'; + }, + ); + + expect(beforeTransformRes).toStrictEqual([ + { loader: testLoaderPath, options: { mode: 'callback' } }, + ]); + + expect(afterTransformRes).toStrictEqual([ + { loader: testLoaderPath, options: { mode: 'async' } }, + ]); + + // test the data from sdk + const { options, loader } = loaderData[0].loaders[0]; + expect(loader).toEqual(testLoaderPath); + expect(options).toStrictEqual({ mode: 'async' }); + }); + + test(`${title} add loader and overwrite options`, async () => { + const { loaderData, beforeTransformRes, afterTransformRes } = await webpack( + compile, + (module) => { + const originLoaders = cloneDeep(module.loaders); + + originLoaders[0].options.mode = 'async'; + + module.loaders = [ + ...originLoaders, + { + loader: testLoaderPath, + options: { pitchResult: '// hello world' }, + ident: null, + type: null, + }, + ]; + }, + ); + + expect(beforeTransformRes).toStrictEqual([ + { loader: testLoaderPath, options: { mode: 'callback' } }, + ]); + + expect(afterTransformRes).toStrictEqual([ + { loader: testLoaderPath, options: { mode: 'async' } }, + { + loader: testLoaderPath, + options: { pitchResult: '// hello world' }, + }, + ]); + + // test the data from sdk + expect(loaderData[0].loaders).toHaveLength(2); + expect(loaderData[0].loaders[0].options).toStrictEqual({ + pitchResult: '// hello world', + }); + expect(loaderData[0].loaders[1].options).toStrictEqual({ + mode: 'async', + }); + }); + + test(`${title} remove all loaders`, async () => { + const { loaderData, beforeTransformRes, afterTransformRes } = await webpack( + compile, + (module) => { + module.loaders.length = 0; + }, + ); + + expect(beforeTransformRes).toStrictEqual([ + { loader: testLoaderPath, options: { mode: 'callback' } }, + ]); + + expect(afterTransformRes).toStrictEqual([]); + + // test the data from sdk + expect(loaderData).toHaveLength(0); + }); +} + +createTests('[webpack5]', compileByWebpack5); diff --git a/e2e/cases/doctor-webpack/plugins/plugin.test.ts b/e2e/cases/doctor-webpack/plugins/plugin.test.ts new file mode 100644 index 0000000000..d71e6ed878 --- /dev/null +++ b/e2e/cases/doctor-webpack/plugins/plugin.test.ts @@ -0,0 +1,70 @@ +import { expect, test } from '@playwright/test'; +import { getSDK } from '@rsbuild/doctor-core/plugins'; +import { compileByWebpack5 } from '@rsbuild/test-helper'; +import os from 'os'; +import path from 'path'; +import { Compiler } from 'webpack'; +import { createRsbuildDoctorPlugin } from '../test-utils'; + +async function webpack(tapName: string, compile: typeof compileByWebpack5) { + const file = path.resolve(__dirname, '../fixtures/a.js'); + const loader = path.resolve(__dirname, '../fixtures/loaders/comment.js'); + const res = await compile(file, { + module: { + rules: [ + { + test: /\.js/, + use: loader, + }, + ], + }, + plugins: [ + // @ts-ignore + createRsbuildDoctorPlugin({}), + // @ts-ignore + { + name: tapName, + // @ts-ignore + apply(compiler: Compiler) { + compiler.hooks.done.tapPromise(tapName, async () => { + // nothing + }); + compiler.hooks.thisCompilation.tap(tapName, (compilation) => { + compilation.hooks.seal.tap(tapName, () => { + return 'seal end'; + }); + }); + }, + }, + ], + }); + return res; +} + +test('webpack5', async () => { + const tapName = 'XXX'; + await webpack(tapName, compileByWebpack5); + const sdk = getSDK(); + const { done, seal } = sdk.getStoreData().plugin; + + const doneData = done.filter((e) => e.tapName === tapName); + expect(doneData).toHaveLength(1); + expect(doneData[0].type).toEqual('promise'); + expect(doneData[0].result).toBeNull(); + + const sealData = seal.filter((e) => e.tapName === tapName); + expect(sealData).toHaveLength(1); + expect(sealData[0].type).toEqual('sync'); + expect(sealData[0].result).toBeNull(); + + const { assets, chunks } = sdk.getStoreData().chunkGraph; + + expect(assets.length).toBe(1); + expect(assets[0].chunks.length).toBeGreaterThan(0); + expect(assets[0].content.length).toBeGreaterThan(10); + os.EOL === '\n' && expect(assets[0].content).toMatchSnapshot(); + + expect(chunks.length).toBe(1); + os.EOL === '\n' && + expect(Buffer.from(JSON.stringify(chunks))).toMatchSnapshot(); +}); diff --git a/e2e/cases/doctor-webpack/plugins/plugin.test.ts-snapshots/webpack5-1-darwin.txt b/e2e/cases/doctor-webpack/plugins/plugin.test.ts-snapshots/webpack5-1-darwin.txt new file mode 100644 index 0000000000..394d911acd --- /dev/null +++ b/e2e/cases/doctor-webpack/plugins/plugin.test.ts-snapshots/webpack5-1-darwin.txt @@ -0,0 +1,7 @@ +/******/ (() => { // webpackBootstrap +var __webpack_exports__ = {}; +console.log('a'); + +// hello world +/******/ })() +; \ No newline at end of file diff --git a/e2e/cases/doctor-webpack/plugins/plugin.test.ts-snapshots/webpack5-2-darwin.dat b/e2e/cases/doctor-webpack/plugins/plugin.test.ts-snapshots/webpack5-2-darwin.dat new file mode 100644 index 0000000000..4c382a0be1 --- /dev/null +++ b/e2e/cases/doctor-webpack/plugins/plugin.test.ts-snapshots/webpack5-2-darwin.dat @@ -0,0 +1 @@ +[{"id":"0","name":"main","initial":true,"size":33,"parsedSize":0,"entry":true,"assets":["bundle.js"],"modules":[1],"dependencies":[],"imported":[]}] \ No newline at end of file diff --git a/e2e/cases/doctor-webpack/plugins/rules.test.ts b/e2e/cases/doctor-webpack/plugins/rules.test.ts new file mode 100644 index 0000000000..c104dfad93 --- /dev/null +++ b/e2e/cases/doctor-webpack/plugins/rules.test.ts @@ -0,0 +1,59 @@ +import { defineRule } from '@rsbuild/doctor-core/rules'; +import { Rule } from '@rsbuild/doctor-types'; +import { compileByWebpack5 } from '@rsbuild/test-helper'; +import path from 'path'; +import { test, expect } from '@playwright/test'; +import { createRsbuildDoctorPlugin } from '../test-utils'; + +async function webpack(compile: typeof compileByWebpack5) { + let checkFnTimes = 0; + let checkEndFnTimes = 0; + let checkEndAfterManifestFnTimes = 0; + const file = path.resolve(__dirname, '../fixtures/b.js'); + const result = await compile(file, { + optimization: { + minimize: true, + }, + plugins: [ + // @ts-ignore + createRsbuildDoctorPlugin({ + linter: { + extends: [ + defineRule(() => ({ + meta: { + code: Rule.RuleMessageCodeEnumerated.Extend, + title: 'test' as const, + category: 'bundle', + severity: 'Warn', + }, + check() { + checkFnTimes += 1; + }, + async onCheckEnd({ hooks }) { + hooks.afterSaveManifest.tapPromise('CCC', async () => { + checkEndAfterManifestFnTimes += 1; + }); + checkEndFnTimes += 1; + }, + })), + ], + }, + }), + ], + }); + + return { + result, + checkFnTimes, + checkEndFnTimes, + checkEndAfterManifestFnTimes, + }; +} + +test('webpack5', async () => { + const { checkEndAfterManifestFnTimes, checkEndFnTimes, checkFnTimes } = + await webpack(compileByWebpack5); + expect(checkFnTimes).toBe(1); + expect(checkEndFnTimes).toBe(1); + expect(checkEndAfterManifestFnTimes).toBe(1); +}); diff --git a/e2e/cases/doctor-webpack/summary.test.ts b/e2e/cases/doctor-webpack/summary.test.ts new file mode 100644 index 0000000000..c09bb33060 --- /dev/null +++ b/e2e/cases/doctor-webpack/summary.test.ts @@ -0,0 +1,53 @@ +import { expect, test } from '@playwright/test'; +import { getSDK } from '@rsbuild/doctor-core/plugins'; +import { Summary } from '@rsbuild/doctor-utils'; +import path from 'path'; +import { compileByWebpack5 } from '@rsbuild/test-helper'; +import { createRsbuildDoctorPlugin } from './test-utils'; + +async function webpack(compile: typeof compileByWebpack5) { + const file = path.resolve(__dirname, './fixtures/b.js'); + const loader = path.resolve(__dirname, './fixtures/loaders/comment.js'); + const res = await compile(file, { + module: { + rules: [ + { + test: /\.js/, + use: loader, + }, + ], + }, + optimization: { + minimize: true, + }, + plugins: [createRsbuildDoctorPlugin({})], + }); + return res; +} + +const costsNames = [ + Summary.SummaryCostsDataName.Bootstrap, + Summary.SummaryCostsDataName.Compile, + Summary.SummaryCostsDataName.Done, + Summary.SummaryCostsDataName.Minify, +]; + +test('webpack5', async () => { + await webpack(compileByWebpack5); + const sdk = getSDK(); + const { configs } = sdk.getStoreData(); + const { costs } = sdk.getStoreData().summary; + + expect(configs[0]).toBeInstanceOf(Object); + expect(configs[0].name).toEqual('webpack'); + + costsNames.forEach((costsName) => { + expect({ + name: costsName, + count: costs.filter((e) => e.name === costsName).length, + }).toStrictEqual({ + name: costsName, + count: 1, + }); + }); +}); diff --git a/e2e/cases/doctor-webpack/test-utils.ts b/e2e/cases/doctor-webpack/test-utils.ts new file mode 100644 index 0000000000..ae31de7b53 --- /dev/null +++ b/e2e/cases/doctor-webpack/test-utils.ts @@ -0,0 +1,37 @@ +import { DoctorWebpackPluginOptions } from '@rsbuild/doctor-core/types'; +import { RsbuildDoctorWebpackPlugin } from '@rsbuild/doctor-webpack-plugin'; +import { Linter } from '@rsbuild/doctor-types'; +import { File } from '@rsbuild/doctor-utils/build'; +import { tmpdir } from 'os'; +import path from 'path'; + +export function createRsbuildDoctorPlugin( + options: DoctorWebpackPluginOptions = {}, +) { + const plugin = new RsbuildDoctorWebpackPlugin({ + ...options, + disableClientServer: + typeof options.disableClientServer === 'boolean' + ? options.disableClientServer + : true, + }); + + const outdir = path.resolve( + tmpdir(), + `./${Date.now()}/web_doctor_webpack_plugin_test`, + ); + + plugin.sdk.hooks.afterSaveManifest.tapPromise( + { name: 'REMOVE_TMP_DIR', stage: -9999 }, + async () => { + plugin.sdk.setOutputDir(outdir); + try { + await File.fse.remove(plugin.sdk.outputDir); + } catch (e) { + console.error(e); + } + }, + ); + + return plugin; +} diff --git a/e2e/package.json b/e2e/package.json index 9069f9e5ae..4850eb067e 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -6,7 +6,8 @@ "test": "pnpm run test:rspack && pnpm run test:webpack", "test:webpack": "PROVIDE_TYPE=webpack playwright test", "test:rspack": "playwright test", - "test:webpack-swc": "PROVIDE_TYPE=webpack playwright test swc" + "test:webpack-swc": "PROVIDE_TYPE=webpack playwright test swc", + "test:doctor": "playwright test -c playwright.config.doctor.ts" }, "dependencies": { "devcert": "1.2.2", @@ -19,10 +20,16 @@ "devDependencies": { "@playwright/test": "1.33.0", "@rsbuild/core": "workspace:*", + "@rsbuild/doctor-core": "workspace:*", + "@rsbuild/doctor-rspack-plugin": "workspace:*", + "@rsbuild/doctor-sdk": "workspace:*", + "@rsbuild/doctor-types": "workspace:*", + "@rsbuild/doctor-utils": "workspace:*", + "@rsbuild/doctor-webpack-plugin": "workspace:*", "@rsbuild/plugin-assets-retry": "workspace:*", "@rsbuild/plugin-babel": "workspace:*", - "@rsbuild/plugin-css-minimizer": "workspace:*", "@rsbuild/plugin-check-syntax": "workspace:*", + "@rsbuild/plugin-css-minimizer": "workspace:*", "@rsbuild/plugin-image-compress": "workspace:*", "@rsbuild/plugin-node-polyfill": "workspace:*", "@rsbuild/plugin-pug": "workspace:*", @@ -40,11 +47,14 @@ "@rsbuild/plugin-vue2": "workspace:*", "@rsbuild/plugin-vue2-jsx": "workspace:*", "@rsbuild/shared": "workspace:*", + "@rsbuild/test-helper": "workspace:*", "@rsbuild/webpack": "workspace:*", + "@rspack/core": "0.3.14", "@types/lodash": "^4.14.200", "@types/node": "^16", "@types/react": "^18", "@types/react-dom": "^18", + "@types/webpack": "5.28.0", "fast-glob": "^3.3.1", "playwright": "1.33.0", "typescript": "^5.3.0" diff --git a/e2e/playwright.config.doctor.ts b/e2e/playwright.config.doctor.ts new file mode 100644 index 0000000000..327abd347c --- /dev/null +++ b/e2e/playwright.config.doctor.ts @@ -0,0 +1,5 @@ +import { defineConfig } from '@playwright/test'; + +export default defineConfig({ + testMatch: ['**/cases/doctor-*/**/**.test.ts'], +}); diff --git a/packages/doctor-rspack-plugin/package.json b/packages/doctor-rspack-plugin/package.json index 6a753c3532..4a89656049 100644 --- a/packages/doctor-rspack-plugin/package.json +++ b/packages/doctor-rspack-plugin/package.json @@ -15,8 +15,7 @@ "scripts": { "dev": "npm run start", "start": "modern build -w", - "build": "modern build", - "test": "vitest run" + "build": "modern build" }, "dependencies": { "@rsbuild/doctor-core": "workspace:*", diff --git a/packages/doctor-rspack-plugin/tests/__snapshots__/plugin.test.ts.snap b/packages/doctor-rspack-plugin/tests/__snapshots__/plugin.test.ts.snap deleted file mode 100644 index 39a1be085b..0000000000 --- a/packages/doctor-rspack-plugin/tests/__snapshots__/plugin.test.ts.snap +++ /dev/null @@ -1,30 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`test rspack-plugin src/plugin.ts > test plugin interceptor > rspack data store 1`] = ` -{ - "dependencies": [], - "exports": [], - "moduleGraphModules": [], - "modules": [ - { - "chunks": [ - "main", - ], - "dependencies": [], - "id": 1, - "imported": [], - "isPreferSource": false, - "kind": 0, - "path": "/packages/doctor-rspack-plugin/tests/fixtures/a.js", - "size": { - "parsedSize": 0, - "sourceSize": 33, - "transformedSize": 33, - }, - "webpackId": "", - }, - ], - "sideEffects": [], - "variables": [], -} -`; diff --git a/packages/doctor-rspack-plugin/tests/plugin.test.ts b/packages/doctor-rspack-plugin/tests/plugin.test.ts deleted file mode 100644 index c6021a08a2..0000000000 --- a/packages/doctor-rspack-plugin/tests/plugin.test.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { getSDK } from '@rsbuild/doctor-core/plugins'; -import { compileByRspack } from '@rsbuild/test-helper'; -import { Compiler } from '@rspack/core'; -import path from 'path'; -import os from 'os'; -import { describe, expect, it } from 'vitest'; -import { createDoctorPlugin } from './test-utils'; -// TODO: migrate to e2e -describe.skip('test rspack-plugin src/plugin.ts', () => { - describe('test plugin interceptor', () => { - 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 res = await compile(file, { - module: { - rules: [ - { - test: /\.js/, - use: loader, - }, - ], - }, - plugins: [ - // @ts-ignore - createDoctorPlugin({}), - { - name: tapName, - apply(compiler: Compiler) { - compiler.hooks.done.tapPromise(tapName, async () => { - // nothing - }); - compiler.hooks.thisCompilation.tap(tapName, (compilation) => { - compilation.hooks.processAssets.tap(tapName, () => { - return 'processAssets end'; - }); - }); - }, - }, - ], - }); - - return res; - } - - it('rspack plugin intercept', async () => { - const tapName = 'XXX'; - await rspackCompile(tapName, compileByRspack); - const sdk = getSDK(); - const { done, thisCompilation } = sdk.getStoreData().plugin; - const doneData = done.filter((e) => e.tapName === tapName); - expect(doneData).toHaveLength(1); - expect(doneData[0].type).toEqual('promise'); - expect(doneData[0].result).toBeNull(); - - const sealData = thisCompilation.filter((e) => e.tapName === tapName); - expect(sealData).toHaveLength(1); - expect(sealData[0].type).toEqual('sync'); - expect(sealData[0].result).toBeNull(); - }); - - it('rspack data store', async () => { - const tapName = 'XXX'; - await rspackCompile(tapName, compileByRspack); - const sdk = getSDK(); - const datas = sdk.getStoreData(); - expect(datas.errors.length).toBe(0); - const graphData = datas.moduleGraph; - - os.EOL === '\n' - ? expect( - graphData.modules[0].webpackId.indexOf('tests/fixtures/a.js'), - ).toBeGreaterThan(0) - : expect( - graphData.modules[0].webpackId.indexOf('\\tests\\fixtures\\a.js'), - ).toBeGreaterThan(0); - - graphData.modules.forEach((mod) => (mod.webpackId = '')); - expect(graphData).toMatchSnapshot(); - }); - }); -}); diff --git a/packages/doctor-webpack-plugin/tests/loaders/proxy.test.ts b/packages/doctor-webpack-plugin/tests/loaders/proxy.test.ts deleted file mode 100644 index 48a43f8f23..0000000000 --- a/packages/doctor-webpack-plugin/tests/loaders/proxy.test.ts +++ /dev/null @@ -1,236 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { SDK } from '@rsbuild/doctor-types'; -import path from 'path'; -import { Compiler } from 'webpack'; -import os from 'os'; -import { getSDK, setSDK } from '@rsbuild/doctor-core/plugins'; -import { createRsbuildDoctorPlugin } from '../test-utils'; -import { DoctorWebpackSDK } from '@rsbuild/doctor-sdk/sdk'; -import { compileByWebpack5 } from '@rsbuild/test-helper'; -// TODO: migrate to e2e -describe.skip('test src/loaders/proxy.ts', () => { - const file = path.resolve(__dirname, '../fixtures/a.js'); - const loaderPath = path.resolve(__dirname, '../fixtures/loaders/comment.js'); - const codeTransformed = `console.log('a');\n\n// hello world`; - - async function webpack5( - mode: 'async' | 'callback' | 'sync', - pitchResult = '', - ) { - const res = await compileByWebpack5(file, { - module: { - rules: [ - { - test: /\.js$/, - use: [ - { - loader: loaderPath, - options: { - mode, - pitchResult, - }, - }, - ], - }, - ], - }, - // @ts-ignore - plugins: [createRsbuildDoctorPlugin()], - }); - return res; - } - - describe('webpack5', () => { - it('test sync', async () => { - const mode = 'sync'; - const { modules } = await webpack5(mode); - - expect(modules!.length).toEqual(1); - expect(getSDK()).toBeInstanceOf(DoctorWebpackSDK); - - const { loader } = getSDK().getStoreData(); - - expect(loader).toHaveLength(1); - expect(loader[0].resource).toStrictEqual({ - path: file, - query: {}, - queryRaw: '', - ext: 'js', - } as SDK.ResourceData); - expect(loader[0].loaders[0].isPitch).toBeFalsy(); - expect(loader[0].loaders[0].loaderIndex).toEqual(0); - expect(loader[0].loaders[0].path).toEqual(loaderPath); - os.EOL === '\n' && - expect(loader[0].loaders[0].result).toEqual(codeTransformed); - expect(loader[0].loaders[0].options).toStrictEqual({ - mode, - pitchResult: '', - }); - expect(loader[0].loaders[0].errors).toHaveLength(0); - }); - - it('test async', async () => { - const mode = 'async'; - const { modules } = await webpack5(mode); - - expect(modules!.length).toEqual(1); - expect(getSDK()).toBeInstanceOf(DoctorWebpackSDK); - - const { loader } = getSDK().getStoreData(); - - expect(loader).toHaveLength(1); - expect(loader[0].resource).toStrictEqual({ - path: file, - query: {}, - queryRaw: '', - ext: 'js', - } as SDK.ResourceData); - expect(loader[0].loaders[0].isPitch).toBeFalsy(); - expect(loader[0].loaders[0].loaderIndex).toEqual(0); - expect(loader[0].loaders[0].path).toEqual(loaderPath); - os.EOL === '\n' && - expect(loader[0].loaders[0].result).toEqual(codeTransformed); - expect(loader[0].loaders[0].options).toStrictEqual({ - mode, - pitchResult: '', - }); - expect(loader[0].loaders[0].errors).toHaveLength(0); - }); - - it('test callback', async () => { - const mode = 'callback'; - const { modules } = await webpack5(mode); - - expect(modules!.length).toEqual(1); - expect(getSDK()).toBeInstanceOf(DoctorWebpackSDK); - - const { loader } = getSDK().getStoreData(); - - expect(loader).toHaveLength(1); - expect(loader[0].resource).toStrictEqual({ - path: file, - query: {}, - queryRaw: '', - ext: 'js', - } as SDK.ResourceData); - expect(loader[0].loaders[0].isPitch).toBeFalsy(); - expect(loader[0].loaders[0].loaderIndex).toEqual(0); - expect(loader[0].loaders[0].path).toEqual(loaderPath); - os.EOL === '\n' && - expect(loader[0].loaders[0].result).toEqual(codeTransformed); - expect(loader[0].loaders[0].options).toStrictEqual({ - mode, - pitchResult: '', - }); - expect(loader[0].loaders[0].errors).toHaveLength(0); - }); - - it('test pitch', async () => { - const mode = 'callback'; - const pitchResult = '// pitch success'; - const { modules } = await webpack5(mode, pitchResult); - - expect(modules!.length).toEqual(1); - expect(getSDK()).toBeInstanceOf(DoctorWebpackSDK); - - const { loader } = getSDK().getStoreData(); - - expect(loader).toHaveLength(1); - expect(loader[0].resource).toStrictEqual({ - path: file, - query: {}, - queryRaw: '', - ext: 'js', - } as SDK.ResourceData); - expect(loader[0].loaders[0].isPitch).toBeTruthy(); - expect(loader[0].loaders[0].loaderIndex).toEqual(0); - expect(loader[0].loaders[0].path).toEqual(loaderPath); - expect(loader[0].loaders[0].result).toEqual(pitchResult); - expect(loader[0].loaders[0].options).toStrictEqual({ mode, pitchResult }); - expect(loader[0].loaders[0].errors).toHaveLength(0); - }); - }); - - describe('report loader data by http request', () => { - it('set sdk.reportLoader as null to mock this scene', async () => { - const mode = 'sync'; - const plugin = createRsbuildDoctorPlugin(); - const { modules } = await compileByWebpack5(file, { - module: { - rules: [ - { - test: /\.js$/, - use: [ - { - loader: loaderPath, - options: { - mode, - pitchResult: '', - }, - }, - ], - }, - ], - }, - plugins: [ - // @ts-ignore - plugin, - // @ts-ignore - { - name: 'XXX', - apply(compiler: Compiler) { - compiler.hooks.beforeRun.tapPromise( - { name: 'XXX', stage: 99999 }, - async () => { - const sdk = getSDK(); - setSDK( - new Proxy(sdk, { - get(target, key, receiver) { - switch (key) { - case 'reportLoader': - return null; - 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); - }, - }), - ); - }, - ); - }, - }, - ], - }); - - expect(modules!.length).toEqual(1); - expect(getSDK().reportLoader).toEqual(null); - - // @ts-ignore - const { loader } = plugin.sdk.getStoreData(); - - expect(loader).toHaveLength(1); - expect(loader[0].resource).toStrictEqual({ - path: file, - query: {}, - queryRaw: '', - ext: 'js', - } as SDK.ResourceData); - expect(loader[0].loaders[0].isPitch).toBeFalsy(); - expect(loader[0].loaders[0].loaderIndex).toEqual(0); - expect(loader[0].loaders[0].path).toEqual(loaderPath); - os.EOL === '\n' && - expect(loader[0].loaders[0].result).toEqual(codeTransformed); - expect(loader[0].loaders[0].options).toStrictEqual({ - mode, - pitchResult: '', - }); - expect(loader[0].loaders[0].errors).toHaveLength(0); - }); - }); -}); diff --git a/packages/doctor-webpack-plugin/tests/loaders/query.test.ts b/packages/doctor-webpack-plugin/tests/loaders/query.test.ts deleted file mode 100644 index 2df28c1d3e..0000000000 --- a/packages/doctor-webpack-plugin/tests/loaders/query.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import path from 'path'; -import os from 'os'; -import qs from 'querystring'; -import { getSDK } from '@rsbuild/doctor-core/plugins'; -import { createRsbuildDoctorPlugin } from '../test-utils'; -import { compileByWebpack5 } from '@rsbuild/test-helper'; -// TODO: migrate to e2e -describe('test src/loaders/proxy.ts', () => { - const file = path.resolve(__dirname, '../fixtures/a.js'); - const loaderPath = path.resolve( - __dirname, - '../fixtures/loaders/serialize-query-to-comment.js', - ); - - async function webpack5(query?: string) { - const res = await compileByWebpack5(query ? `${file}${query}` : file, { - module: { - rules: [ - { - test: /\.js$/, - use: [ - { - loader: loaderPath, - }, - ], - }, - ], - }, - // @ts-ignore - plugins: [createRsbuildDoctorPlugin()], - }); - return res; - } - - describe('webpack5', () => { - it('query is undefined', async () => { - const codeTransformed = - os.EOL === '\n' - ? `console.log('a');\n\n// ${JSON.stringify('')}` - : `console.log('a');\r\n\n// ${JSON.stringify('')}`; - - await webpack5(); - - const { loader } = getSDK().getStoreData(); - expect(loader).toHaveLength(1); - os.EOL === '\n' && - expect(loader[0].loaders[0].result).toEqual(codeTransformed); - }); - - it('query exists', async () => { - // number are not parsed: https://github.com/webpack/loader-utils/tree/v2.0.0-branch#parsequery - const query = { test: '111' }; - const querystring = `?${qs.stringify(query)}`; - const codeTransformed = - os.EOL === '\n' - ? `console.log('a');\n\n// ${JSON.stringify( - querystring, - )}\n// ${JSON.stringify(query)}` - : `console.log('a');\r\n\n// ${JSON.stringify( - querystring, - )}\n// ${JSON.stringify(query)}`; - - await webpack5(querystring); - - const { loader } = getSDK().getStoreData(); - expect(loader).toHaveLength(1); - expect(loader[0].loaders[0].result).toEqual(codeTransformed); - }); - }); -}); diff --git a/packages/doctor-webpack-plugin/tests/multiple.test.ts b/packages/doctor-webpack-plugin/tests/multiple.test.ts index d0329b67cc..15c8b3a60a 100644 --- a/packages/doctor-webpack-plugin/tests/multiple.test.ts +++ b/packages/doctor-webpack-plugin/tests/multiple.test.ts @@ -1,5 +1,4 @@ import { describe, expect, it } from 'vitest'; -import path from 'path'; /** * create sandbox to load src/multiple.ts to avoid sdk save in global variable between different test cases. diff --git a/packages/doctor-webpack-plugin/tests/plugins/__snapshots__/plugin.test.ts.snap b/packages/doctor-webpack-plugin/tests/plugins/__snapshots__/plugin.test.ts.snap deleted file mode 100644 index 1c8b10aee0..0000000000 --- a/packages/doctor-webpack-plugin/tests/plugins/__snapshots__/plugin.test.ts.snap +++ /dev/null @@ -1,32 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`test src/utils/plugin.ts > test plugin interceptor > webpack5 1`] = ` -"/******/ (() => { / webpackBootstrap -var __webpack_exports__ = {}; -console.log('a'); - -/ hello world -/******/ })() -;" -`; - -exports[`test src/utils/plugin.ts > test plugin interceptor > webpack5 2`] = ` -[ - { - "assets": [ - "bundle.js", - ], - "dependencies": [], - "entry": true, - "id": "0", - "imported": [], - "initial": true, - "modules": [ - 1, - ], - "name": "main", - "parsedSize": 0, - "size": 33, - }, -] -`; diff --git a/packages/doctor-webpack-plugin/tests/plugins/loader.test.ts b/packages/doctor-webpack-plugin/tests/plugins/loader.test.ts deleted file mode 100644 index e5c39ca65e..0000000000 --- a/packages/doctor-webpack-plugin/tests/plugins/loader.test.ts +++ /dev/null @@ -1,212 +0,0 @@ -import { Common } from '@rsbuild/doctor-types'; -import { compileByWebpack5 } from '@rsbuild/test-helper'; -import { cloneDeep } from 'lodash'; -import path from 'path'; -import { describe, expect, it, vi } from 'vitest'; -import type { NormalModule, WebpackPluginInstance } from 'webpack'; -import { createRsbuildDoctorPlugin } from '../test-utils'; -// TODO: migrate to e2e -describe('test src/plugins/loader.ts', () => { - const testLoaderPath = path.resolve( - __dirname, - '../fixtures/loaders/comment.js', - ); - - async function webpack( - compile: typeof compileByWebpack5, - transformer: (module: NormalModule) => void, - ) { - const file = path.resolve(__dirname, '../fixtures/b.js'); - - const beforeTransform = vi.fn(); - const afterTransform = vi.fn(); - - /** - * Based on https://github.com/arco-design/arco-plugins/blob/main/packages/plugin-webpack-react/src/arco-design-plugin/utils/index.ts#L37 - */ - const arcoDesignPluginForked: WebpackPluginInstance = { - apply(compiler) { - const pluginName = 'arco-design-plugin-forked'; - const mapper = (module: NormalModule) => - module.loaders.map((e) => ({ - loader: e.loader, - options: cloneDeep(e.options), - })); - const hookHandler = ( - context: Common.PlainObject, - module: NormalModule, - ) => { - beforeTransform(mapper(module)); - transformer(module); - afterTransform(mapper(module)); - }; - // @ts-ignore - compiler.hooks.compilation.tap(pluginName, (compilation) => { - // for webpack 5 - if ( - compiler.webpack && - compiler.webpack.NormalModule && - compiler.webpack.NormalModule.getCompilationHooks - ) { - compiler.webpack.NormalModule.getCompilationHooks( - compilation, - ).loader.tap(pluginName, hookHandler); - } else if (compilation.hooks) { - // for webpack 4 - compilation.hooks.normalModuleLoader.tap(pluginName, hookHandler); - } - }); - }, - }; - - const RsbuildDoctorPlugin = createRsbuildDoctorPlugin({}); - - const result = await compile(file, { - optimization: { - minimize: true, - }, - module: { - rules: [ - { - test: /\.js$/, - use: { - loader: testLoaderPath, - options: { - mode: 'callback', - }, - }, - }, - ], - }, - plugins: [ - // @ts-ignore - RsbuildDoctorPlugin, - // @ts-ignore - arcoDesignPluginForked, - ], - }); - - return { - RsbuildDoctorPlugin, - loaderData: RsbuildDoctorPlugin.sdk.getStoreData().loader, - beforeTransform, - afterTransform, - }; - } - - function createTests(title: string, compile: typeof compileByWebpack5) { - describe(title, () => { - it.skip(`${title} basic usage`, async () => { - const { loaderData, beforeTransform, afterTransform } = await webpack( - compile, - () => {}, - ); - - expect(beforeTransform).toBeCalledTimes(1); - expect(beforeTransform).toBeCalledWith([ - { loader: testLoaderPath, options: { mode: 'callback' } }, - ]); - - expect(afterTransform).toBeCalledTimes(1); - expect(afterTransform).toBeCalledWith([ - { loader: testLoaderPath, options: { mode: 'callback' } }, - ]); - - // test the data from sdk - const { options, loader } = loaderData[0].loaders[0]; - expect(loader).toEqual(testLoaderPath); - expect(options).toStrictEqual({ mode: 'callback' }); - }); - - it.skip(`${title} overwrite loader options`, async () => { - const { loaderData, beforeTransform, afterTransform } = await webpack( - compile, - (module) => { - module.loaders[0].options.mode = 'async'; - }, - ); - - expect(beforeTransform).toBeCalledTimes(1); - expect(beforeTransform).toBeCalledWith([ - { loader: testLoaderPath, options: { mode: 'callback' } }, - ]); - - expect(afterTransform).toBeCalledTimes(1); - expect(afterTransform).toBeCalledWith([ - { loader: testLoaderPath, options: { mode: 'async' } }, - ]); - - // test the data from sdk - const { options, loader } = loaderData[0].loaders[0]; - expect(loader).toEqual(testLoaderPath); - expect(options).toStrictEqual({ mode: 'async' }); - }); - - it.skip(`${title} add loader and overwrite options`, async () => { - const { loaderData, beforeTransform, afterTransform } = await webpack( - compile, - (module) => { - const originLoaders = cloneDeep(module.loaders); - - originLoaders[0].options.mode = 'async'; - - module.loaders = [ - ...originLoaders, - { - loader: testLoaderPath, - options: { pitchResult: '// hello world' }, - ident: null, - type: null, - }, - ]; - }, - ); - - expect(beforeTransform).toBeCalledTimes(1); - expect(beforeTransform).toBeCalledWith([ - { loader: testLoaderPath, options: { mode: 'callback' } }, - ]); - - expect(afterTransform).toBeCalledTimes(1); - expect(afterTransform).toBeCalledWith([ - { loader: testLoaderPath, options: { mode: 'async' } }, - { - loader: testLoaderPath, - options: { pitchResult: '// hello world' }, - }, - ]); - - // test the data from sdk - expect(loaderData[0].loaders).toHaveLength(2); - expect(loaderData[0].loaders[0].options).toStrictEqual({ - pitchResult: '// hello world', - }); - expect(loaderData[0].loaders[1].options).toStrictEqual({ - mode: 'async', - }); - }); - - it.skip(`${title} remove all loaders`, async () => { - const { loaderData, beforeTransform, afterTransform } = await webpack( - compile, - (module) => { - module.loaders.length = 0; - }, - ); - - expect(beforeTransform).toBeCalledTimes(1); - expect(beforeTransform).toBeCalledWith([ - { loader: testLoaderPath, options: { mode: 'callback' } }, - ]); - - expect(afterTransform).toBeCalledTimes(1); - expect(afterTransform).toBeCalledWith([]); - - // test the data from sdk - expect(loaderData).toHaveLength(0); - }); - }); - } - - createTests('[webpack5]', compileByWebpack5); -}); diff --git a/packages/doctor-webpack-plugin/tests/plugins/plugin.test.ts b/packages/doctor-webpack-plugin/tests/plugins/plugin.test.ts deleted file mode 100644 index 1cae0b24e3..0000000000 --- a/packages/doctor-webpack-plugin/tests/plugins/plugin.test.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import os from 'os'; -import path from 'path'; -import { Compiler } from 'webpack'; -import { getSDK } from '@rsbuild/doctor-core/plugins'; -import { createRsbuildDoctorPlugin } from '../test-utils'; -import { compileByWebpack5 } from '@rsbuild/test-helper'; -// TODO: migrate to e2e -describe('test src/utils/plugin.ts', () => { - describe('test plugin interceptor', () => { - async function webpack(tapName: string, compile: typeof compileByWebpack5) { - const file = path.resolve(__dirname, '../fixtures/a.js'); - const loader = path.resolve(__dirname, '../fixtures/loaders/comment.js'); - const res = await compile(file, { - module: { - rules: [ - { - test: /\.js/, - use: loader, - }, - ], - }, - plugins: [ - // @ts-ignore - createRsbuildDoctorPlugin({}), - // @ts-ignore - { - name: tapName, - // @ts-ignore - apply(compiler: Compiler) { - compiler.hooks.done.tapPromise(tapName, async () => { - // nothing - }); - compiler.hooks.thisCompilation.tap(tapName, (compilation) => { - compilation.hooks.seal.tap(tapName, () => { - return 'seal end'; - }); - }); - }, - }, - ], - }); - return res; - } - - it.skip('webpack5', async () => { - const tapName = 'XXX'; - await webpack(tapName, compileByWebpack5); - const sdk = getSDK(); - const { done, seal } = sdk.getStoreData().plugin; - - const doneData = done.filter((e) => e.tapName === tapName); - expect(doneData).toHaveLength(1); - expect(doneData[0].type).toEqual('promise'); - expect(doneData[0].result).toBeNull(); - - const sealData = seal.filter((e) => e.tapName === tapName); - expect(sealData).toHaveLength(1); - expect(sealData[0].type).toEqual('sync'); - expect(sealData[0].result).toBeNull(); - - const { assets, chunks } = sdk.getStoreData().chunkGraph; - - expect(assets.length).toBe(1); - expect(assets[0].chunks.length).toBeGreaterThan(0); - expect(assets[0].content.length).toBeGreaterThan(10); - os.EOL === '\n' && expect(assets[0].content).toMatchSnapshot(); - - expect(chunks.length).toBe(1); - os.EOL === '\n' && expect(chunks).toMatchSnapshot(); - }); - }); -}); diff --git a/packages/doctor-webpack-plugin/tests/plugins/rules.test.ts b/packages/doctor-webpack-plugin/tests/plugins/rules.test.ts deleted file mode 100644 index 5e2d639c08..0000000000 --- a/packages/doctor-webpack-plugin/tests/plugins/rules.test.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { defineRule } from '@rsbuild/doctor-core/rules'; -import { Rule } from '@rsbuild/doctor-types'; -import { compileByWebpack5 } from '@rsbuild/test-helper'; -import path from 'path'; -import { describe, expect, it, vi } from 'vitest'; -import { createRsbuildDoctorPlugin } from '../test-utils'; -// TODO: migrate to e2e -describe('test src/plugins/rules.ts', () => { - async function webpack(compile: typeof compileByWebpack5) { - const check = vi.fn(); - const onCheckEnd = vi.fn(); - const onCheckEndAfterManifest = vi.fn(); - - const file = path.resolve(__dirname, '../fixtures/b.js'); - const result = await compile(file, { - optimization: { - minimize: true, - }, - plugins: [ - // @ts-ignore - createRsbuildDoctorPlugin({ - linter: { - extends: [ - defineRule(() => ({ - meta: { - code: Rule.RuleMessageCodeEnumerated.Extend, - title: 'test' as const, - category: 'bundle', - severity: 'Warn', - }, - check() { - check('check'); - }, - async onCheckEnd({ hooks }) { - hooks.afterSaveManifest.tapPromise('aaa', async () => { - onCheckEndAfterManifest('onCheckEndAfterManifest'); - }); - onCheckEnd('onCheckEnd'); - }, - })), - ], - }, - }), - ], - }); - - return { - result, - check, - onCheckEnd, - onCheckEndAfterManifest, - }; - } - - it.skip('webpack5', async () => { - const { check, onCheckEnd, onCheckEndAfterManifest } = - await webpack(compileByWebpack5); - - expect(check).toBeCalledTimes(1); - expect(check).toHaveBeenNthCalledWith(1, 'check'); - expect(onCheckEnd).toBeCalledTimes(1); - expect(onCheckEnd).toHaveBeenNthCalledWith(1, 'onCheckEnd'); - // expect(onCheckEndAfterManifest).toBeCalledTimes(1); // TODO: check this - expect(onCheckEndAfterManifest).toHaveBeenNthCalledWith( - 1, - 'onCheckEndAfterManifest', - ); - }); -}); diff --git a/packages/doctor-webpack-plugin/tests/summary.test.ts b/packages/doctor-webpack-plugin/tests/summary.test.ts deleted file mode 100644 index e8045a707b..0000000000 --- a/packages/doctor-webpack-plugin/tests/summary.test.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { getSDK } from '@rsbuild/doctor-core/plugins'; -import { Summary } from '@rsbuild/doctor-utils'; -import path from 'path'; -import { describe, expect, it } from 'vitest'; - -import { createRsbuildDoctorPlugin } from './test-utils'; -import { compileByWebpack5 } from '@rsbuild/test-helper'; -// TODO: migrate to e2e -describe('test src/plugin.ts summary data reporter', () => { - async function webpack(compile: typeof compileByWebpack5) { - const file = path.resolve(__dirname, './fixtures/b.js'); - const loader = path.resolve(__dirname, './fixtures/loaders/comment.js'); - const res = await compile(file, { - module: { - rules: [ - { - test: /\.js/, - use: loader, - }, - ], - }, - optimization: { - minimize: true, - }, - plugins: [createRsbuildDoctorPlugin({})], - }); - return res; - } - - const costsNames = [ - Summary.SummaryCostsDataName.Bootstrap, - Summary.SummaryCostsDataName.Compile, - Summary.SummaryCostsDataName.Done, - Summary.SummaryCostsDataName.Minify, - ]; - - it.skip('webpack5', async () => { - await webpack(compileByWebpack5); - const sdk = getSDK(); - const { configs } = sdk.getStoreData(); - const { costs } = sdk.getStoreData().summary; - - expect(configs[0]).toBeInstanceOf(Object); - expect(configs[0].name).toEqual('webpack'); - - costsNames.forEach((costsName) => { - expect({ - name: costsName, - count: costs.filter((e) => e.name === costsName).length, - }).toStrictEqual({ - name: costsName, - count: 1, - }); - }); - }); -}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e7e0920f50..cfbd774e9e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -75,6 +75,24 @@ importers: '@rsbuild/core': specifier: workspace:* version: link:../packages/core + '@rsbuild/doctor-core': + specifier: workspace:* + version: link:../packages/doctor-core + '@rsbuild/doctor-rspack-plugin': + specifier: workspace:* + version: link:../packages/doctor-rspack-plugin + '@rsbuild/doctor-sdk': + specifier: workspace:* + version: link:../packages/doctor-sdk + '@rsbuild/doctor-types': + specifier: workspace:* + version: link:../packages/doctor-types + '@rsbuild/doctor-utils': + specifier: workspace:* + version: link:../packages/doctor-utils + '@rsbuild/doctor-webpack-plugin': + specifier: workspace:* + version: link:../packages/doctor-webpack-plugin '@rsbuild/plugin-assets-retry': specifier: workspace:* version: link:../packages/plugin-assets-retry @@ -138,9 +156,15 @@ importers: '@rsbuild/shared': specifier: workspace:* version: link:../packages/shared + '@rsbuild/test-helper': + specifier: workspace:* + version: link:../packages/test-helper '@rsbuild/webpack': specifier: workspace:* version: link:../packages/compat/webpack + '@rspack/core': + specifier: 0.3.14 + version: 0.3.14 '@types/lodash': specifier: ^4.14.200 version: 4.14.200 @@ -153,6 +177,9 @@ importers: '@types/react-dom': specifier: ^18 version: 18.2.14 + '@types/webpack': + specifier: 5.28.0 + version: 5.28.0 fast-glob: specifier: ^3.3.1 version: 3.3.1 @@ -5244,7 +5271,6 @@ packages: cpu: [arm64] os: [darwin] requiresBuild: true - dev: false optional: true /@rspack/binding-darwin-x64@0.3.14: @@ -5252,7 +5278,6 @@ packages: cpu: [x64] os: [darwin] requiresBuild: true - dev: false optional: true /@rspack/binding-linux-arm64-gnu@0.3.14: @@ -5260,7 +5285,6 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true - dev: false optional: true /@rspack/binding-linux-arm64-musl@0.3.14: @@ -5268,7 +5292,6 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true - dev: false optional: true /@rspack/binding-linux-x64-gnu@0.3.14: @@ -5276,7 +5299,6 @@ packages: cpu: [x64] os: [linux] requiresBuild: true - dev: false optional: true /@rspack/binding-linux-x64-musl@0.3.14: @@ -5284,7 +5306,6 @@ packages: cpu: [x64] os: [linux] requiresBuild: true - dev: false optional: true /@rspack/binding-win32-arm64-msvc@0.3.14: @@ -5292,7 +5313,6 @@ packages: cpu: [arm64] os: [win32] requiresBuild: true - dev: false optional: true /@rspack/binding-win32-ia32-msvc@0.3.14: @@ -5300,7 +5320,6 @@ packages: cpu: [ia32] os: [win32] requiresBuild: true - dev: false optional: true /@rspack/binding-win32-x64-msvc@0.3.14: @@ -5308,7 +5327,6 @@ packages: cpu: [x64] os: [win32] requiresBuild: true - dev: false optional: true /@rspack/binding@0.3.14: @@ -5323,7 +5341,6 @@ packages: '@rspack/binding-win32-arm64-msvc': 0.3.14 '@rspack/binding-win32-ia32-msvc': 0.3.14 '@rspack/binding-win32-x64-msvc': 0.3.14 - dev: false /@rspack/core@0.3.14: resolution: {integrity: sha512-B76vRdbKXMi/Xjd8Q0f/4xrlqp4mFELH2w+6ROoAscGVgS8eDsWmJHa7ddIB3FELizzDvI3BjGfBeGftZjSg3g==} @@ -5344,7 +5361,6 @@ packages: webpack-sources: 3.2.3 zod: 3.22.4 zod-validation-error: 1.2.0(zod@3.22.4) - dev: false /@rspack/plugin-react-refresh@0.3.14(react-refresh@0.14.0)(webpack@5.89.0): resolution: {integrity: sha512-xHMDOHpQdI7aTJHdVUJMDyE5KM+5bGNcDCY+kptITTrIfiL4RtpIQYa0w0kSZR+/JqgFgviZgqJLW8gnnS1b8Q==} @@ -7866,7 +7882,6 @@ packages: /compare-versions@6.0.0-rc.1: resolution: {integrity: sha512-cFhkjbGY1jLFWIV7KegECbfuyYPxSGvgGkdkfM+ibboQDoPwg2FRHm5BSNTOApiauRBzJIQH7qvOJs2sW5ueKQ==} - dev: false /compressible@2.0.18: resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} @@ -9335,7 +9350,6 @@ packages: /fast-decode-uri-component@1.0.1: resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} - dev: false /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -9357,7 +9371,6 @@ packages: resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} dependencies: fast-decode-uri-component: 1.0.1 - dev: false /fastq@1.15.0: resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} @@ -9777,7 +9790,6 @@ packages: /graceful-fs@4.2.10: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} - dev: false /graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -10745,7 +10757,6 @@ packages: /json-parse-even-better-errors@3.0.0: resolution: {integrity: sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: false /json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -13225,7 +13236,6 @@ packages: /react-refresh@0.14.0: resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==} engines: {node: '>=0.10.0'} - dev: false /react-router-dom@6.17.0(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-qWHkkbXQX+6li0COUUPKAUkxjNNqPJuiBd27dVwQGDNsuFBdMbrS6UZ0CLYc4CsbdLYTckn4oB4tGDuPZpPhaQ==} @@ -14317,7 +14327,6 @@ packages: dependencies: has-flag: 4.0.0 supports-color: 7.2.0 - dev: false /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} @@ -14519,7 +14528,6 @@ packages: dependencies: ansi-escapes: 4.3.2 supports-hyperlinks: 2.3.0 - dev: false /terser-webpack-plugin@5.3.9(@swc/core@1.3.42)(webpack@5.89.0): resolution: {integrity: sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==} @@ -15923,11 +15931,9 @@ packages: zod: ^3.18.0 dependencies: zod: 3.22.4 - dev: false /zod@3.22.4: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} - dev: false /zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}