diff --git a/.changeset/honest-snakes-draw.md b/.changeset/honest-snakes-draw.md new file mode 100644 index 0000000000..f7a5ffe830 --- /dev/null +++ b/.changeset/honest-snakes-draw.md @@ -0,0 +1,23 @@ +--- +'@backstage/cli': minor +--- + +**BREAKING**: The new app build based on [Rspack](https://rspack.dev/) is now the default, and the `EXPERIMENTAL_RSPACK` flag has been removed. To revert to the old behavior, set the `LEGACY_WEBPACK_BUILD` environment flag and install the following optional dependencies: + +```json +{ + "dependencies": { + "@module-federation/enhanced": "^0.9.0", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.7", + "esbuild-loader": "^4.0.0", + "eslint-webpack-plugin": "^4.2.0", + "fork-ts-checker-webpack-plugin": "^9.0.0", + "mini-css-extract-plugin": "^2.4.2", + "terser-webpack-plugin": "^5.1.3", + "webpack": "^5.96.0", + "webpack-dev-server": "^5.0.0" + } +} +``` + +If you do encounter a blocking issue that forces you to use the old WebPack build, please [open an issue](https://github.com/backstage/backstage/issues) explaining the problem. The WebPack build will be removed in a future release. diff --git a/packages/app/e2e-tests/HomePage.test.ts b/packages/app/e2e-tests/HomePage.test.ts index 0b1459935f..f495fb08d9 100644 --- a/packages/app/e2e-tests/HomePage.test.ts +++ b/packages/app/e2e-tests/HomePage.test.ts @@ -35,7 +35,7 @@ test('Should not throw `ResizeObserver loop completed with undelivered notificat ).not.toBeVisible(); }); -test('Should resize widgets vertically and horizontally', async ({ page }) => { +test('Should render some home page widgets', async ({ page }) => { await page.goto('/'); const enterButton = page.getByRole('button', { name: 'Enter' }); @@ -43,32 +43,6 @@ test('Should resize widgets vertically and horizontally', async ({ page }) => { await enterButton.click(); await page.goto('/home'); - await expect(page.getByText('Backstage Example App')).toBeVisible(); - - // Start editing mode - await page.getByRole('button', { name: /Edit/ }).click(); - await expect(page.getByRole('button', { name: /Save/ })).toBeVisible(); - - // Resize the last installed widget - const widgetElement = await page.locator('.react-grid-item:nth-child(3)'); - const defaultWidgetBox = { x: 1, y: 1, width: 1, height: 1 }; - const widgetBoxBefore = - (await widgetElement.boundingBox()) ?? defaultWidgetBox; - const widgetResizeHandle = await widgetElement.locator( - '.react-resizable-handle', - ); - await widgetResizeHandle.hover(); - await page.mouse.down(); - await page.mouse.move(widgetBoxBefore.width / 2, widgetBoxBefore.height / 2); - await page.mouse.up(); - const widgetBoxAfter = - (await widgetElement.boundingBox()) ?? defaultWidgetBox; - - // Ensure that both height and width was reduced - expect(widgetBoxAfter.width).toBeLessThan(widgetBoxBefore.width); - expect(widgetBoxAfter.height).toBeLessThan(widgetBoxBefore.height); - - // Exit editing mode - await page.getByRole('button', { name: 'Save' }).click(); - await expect(page.getByRole('button', { name: /Edit/ })).toBeVisible(); + await expect(page.getByText('Top Visited')).toBeVisible(); + await expect(page.getByText('Recently Visited')).toBeVisible(); }); diff --git a/packages/cli/package.json b/packages/cli/package.json index 8b77fb5fab..89a78020aa 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -63,15 +63,16 @@ "@octokit/graphql-schema": "^13.7.0", "@octokit/oauth-app": "^4.2.0", "@octokit/request": "^8.0.0", - "@pmmmwh/react-refresh-webpack-plugin": "^0.5.7", "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.0.0", "@rollup/plugin-node-resolve": "^15.0.0", "@rollup/plugin-yaml": "^4.0.0", + "@rspack/core": "^1.4.11", + "@rspack/dev-server": "^1.1.4", + "@rspack/plugin-react-refresh": "^1.4.3", "@spotify/eslint-config-base": "^15.0.0", "@spotify/eslint-config-react": "^15.0.0", "@spotify/eslint-config-typescript": "^15.0.0", - "@sucrase/webpack-loader": "^2.0.0", "@swc/core": "^1.3.46", "@swc/helpers": "^0.5.0", "@swc/jest": "^0.2.22", @@ -91,7 +92,6 @@ "css-loader": "^6.5.1", "ctrlc-windows": "^2.1.0", "esbuild": "^0.25.0", - "esbuild-loader": "^4.0.0", "eslint": "^8.6.0", "eslint-config-prettier": "^9.0.0", "eslint-formatter-friendly": "^7.0.0", @@ -102,9 +102,8 @@ "eslint-plugin-react": "^7.37.2", "eslint-plugin-react-hooks": "^5.0.0", "eslint-plugin-unused-imports": "^4.1.4", - "eslint-webpack-plugin": "^4.2.0", + "eslint-rspack-plugin": "^4.2.1", "express": "^4.17.1", - "fork-ts-checker-webpack-plugin": "^9.0.0", "fs-extra": "^11.2.0", "git-url-parse": "^15.0.0", "glob": "^7.1.7", @@ -120,7 +119,6 @@ "jest-runtime": "^29.0.2", "json-schema": "^0.4.0", "lodash": "^4.17.21", - "mini-css-extract-plugin": "^2.4.2", "minimatch": "^9.0.0", "node-stdlib-browser": "^1.3.1", "npm-packlist": "^5.0.0", @@ -144,12 +142,10 @@ "sucrase": "^3.20.2", "swc-loader": "^0.2.3", "tar": "^6.1.12", - "terser-webpack-plugin": "^5.1.3", + "ts-checker-rspack-plugin": "^1.1.5", "ts-morph": "^24.0.0", "undici": "^7.2.3", "util": "^0.12.3", - "webpack": "^5.94.0", - "webpack-dev-server": "^5.0.0", "yaml": "^2.0.0", "yargs": "^16.2.0", "yml-loader": "^2.1.0", @@ -174,9 +170,7 @@ "@backstage/plugin-scaffolder-node-test-utils": "workspace:^", "@backstage/test-utils": "workspace:^", "@backstage/theme": "workspace:^", - "@rspack/core": "^1.3.9", - "@rspack/dev-server": "^1.1.1", - "@rspack/plugin-react-refresh": "^1.4.2", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.7", "@types/cross-spawn": "^6.0.2", "@types/ejs": "^3.1.3", "@types/express": "^4.17.6", @@ -194,22 +188,53 @@ "@types/webpack-sources": "^3.2.3", "@types/yarnpkg__lockfile": "^1.1.4", "del": "^8.0.0", + "esbuild-loader": "^4.0.0", + "eslint-webpack-plugin": "^4.2.0", + "fork-ts-checker-webpack-plugin": "^9.0.0", + "mini-css-extract-plugin": "^2.4.2", "msw": "^1.0.0", - "nodemon": "^3.0.1" + "nodemon": "^3.0.1", + "terser-webpack-plugin": "^5.1.3", + "webpack": "~5.96.0", + "webpack-dev-server": "^5.0.0" }, "peerDependencies": { - "@rspack/core": "^1.2.8", - "@rspack/dev-server": "^1.0.9", - "@rspack/plugin-react-refresh": "^1.0.0" + "@module-federation/enhanced": "^0.9.0", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.7", + "esbuild-loader": "^4.0.0", + "eslint-webpack-plugin": "^4.2.0", + "fork-ts-checker-webpack-plugin": "^9.0.0", + "mini-css-extract-plugin": "^2.4.2", + "terser-webpack-plugin": "^5.1.3", + "webpack": "~5.96.0", + "webpack-dev-server": "^5.0.0" }, "peerDependenciesMeta": { - "@rspack/core": { + "@module-federation/enhanced": { "optional": true }, - "@rspack/dev-server": { + "@pmmmwh/react-refresh-webpack-plugin": { "optional": true }, - "@rspack/plugin-react-refresh": { + "esbuild-loader": { + "optional": true + }, + "eslint-webpack-plugin": { + "optional": true + }, + "fork-ts-checker-webpack-plugin": { + "optional": true + }, + "mini-css-extract-plugin": { + "optional": true + }, + "terser-webpack-plugin": { + "optional": true + }, + "webpack": { + "optional": true + }, + "webpack-dev-server": { "optional": true } }, diff --git a/packages/cli/src/modules/build/commands/package/build/command.ts b/packages/cli/src/modules/build/commands/package/build/command.ts index d6a71b6fc1..378fd31896 100644 --- a/packages/cli/src/modules/build/commands/package/build/command.ts +++ b/packages/cli/src/modules/build/commands/package/build/command.ts @@ -25,8 +25,8 @@ import { isValidUrl } from '../../../lib/urls'; import chalk from 'chalk'; export async function command(opts: OptionValues): Promise { - const rspack = process.env.EXPERIMENTAL_RSPACK - ? (require('@rspack/core') as typeof import('@rspack/core').rspack) + const webpack = process.env.LEGACY_WEBPACK_BUILD + ? (require('webpack') as typeof import('webpack')) : undefined; const role = await findRoleFromCommand(opts); @@ -44,7 +44,7 @@ export async function command(opts: OptionValues): Promise { targetDir: paths.targetDir, configPaths, writeStats: Boolean(opts.stats), - rspack, + webpack, }); } return buildBackend({ @@ -67,7 +67,7 @@ export async function command(opts: OptionValues): Promise { configPaths: [], writeStats: Boolean(opts.stats), isModuleFederationRemote: true, - rspack, + webpack, }); } diff --git a/packages/cli/src/modules/build/commands/repo/build.ts b/packages/cli/src/modules/build/commands/repo/build.ts index d0b9de8867..beb19afb32 100644 --- a/packages/cli/src/modules/build/commands/repo/build.ts +++ b/packages/cli/src/modules/build/commands/repo/build.ts @@ -31,10 +31,9 @@ import { createScriptOptionsParser } from '../../../../lib/optionsParser'; export async function command(opts: OptionValues, cmd: Command): Promise { let packages = await PackageGraph.listTargetPackages(); - const shouldUseRspack = Boolean(process.env.EXPERIMENTAL_RSPACK); - const rspack = shouldUseRspack - ? (require('@rspack/core') as typeof import('@rspack/core').rspack) + const webpack = process.env.LEGACY_WEBPACK_BUILD + ? (require('webpack') as typeof import('webpack')) : undefined; if (opts.since) { @@ -116,7 +115,7 @@ export async function command(opts: OptionValues, cmd: Command): Promise { targetDir: pkg.dir, configPaths: (buildOptions.config as string[]) ?? [], writeStats: Boolean(buildOptions.stats), - rspack, + webpack, }); }, }); diff --git a/packages/cli/src/modules/build/lib/buildFrontend.ts b/packages/cli/src/modules/build/lib/buildFrontend.ts index 6ff04cdb53..fd6ce9c420 100644 --- a/packages/cli/src/modules/build/lib/buildFrontend.ts +++ b/packages/cli/src/modules/build/lib/buildFrontend.ts @@ -26,11 +26,11 @@ interface BuildAppOptions { writeStats: boolean; configPaths: string[]; isModuleFederationRemote?: true; - rspack?: typeof import('@rspack/core').rspack; + webpack?: typeof import('webpack'); } export async function buildFrontend(options: BuildAppOptions) { - const { targetDir, writeStats, configPaths, rspack } = options; + const { targetDir, writeStats, configPaths, webpack } = options; const packageJson = (await fs.readJson( resolvePath(targetDir, 'package.json'), )) as BackstagePackageJson; @@ -48,6 +48,6 @@ export async function buildFrontend(options: BuildAppOptions) { args: configPaths, fromPackage: packageJson.name, })), - rspack, + webpack, }); } diff --git a/packages/cli/src/modules/build/lib/bundler/bundle.ts b/packages/cli/src/modules/build/lib/bundler/bundle.ts index 7d88a2f97f..0bdec2d1c3 100644 --- a/packages/cli/src/modules/build/lib/bundler/bundle.ts +++ b/packages/cli/src/modules/build/lib/bundler/bundle.ts @@ -17,7 +17,7 @@ import yn from 'yn'; import fs from 'fs-extra'; import { resolve as resolvePath } from 'path'; -import webpack from 'webpack'; +import { rspack, Configuration, MultiStats } from '@rspack/core'; import { measureFileSizesBeforeBuild, printFileSizesAfterBuild, @@ -38,7 +38,7 @@ function applyContextToError(error: string, moduleName: string): string { } export async function buildBundle(options: BuildOptions) { - const { statsJsonEnabled, schema: configSchema, rspack } = options; + const { statsJsonEnabled, schema: configSchema, webpack } = options; const paths = resolveBundlingPaths(options); const publicPaths = await resolveOptionalBundlingPaths({ @@ -54,7 +54,7 @@ export async function buildBundle(options: BuildOptions) { getFrontendAppConfigs: () => options.frontendAppConfigs, }; - const configs: webpack.Configuration[] = []; + const configs: Configuration[] = []; if (options.moduleFederation?.mode === 'remote') { // Package detection is disabled for remote bundles @@ -119,13 +119,11 @@ export async function buildBundle(options: BuildOptions) { ); } - if (rspack) { - console.log( - chalk.yellow(`⚠️ WARNING: Using experimental RSPack bundler.`), - ); + if (webpack) { + console.log(chalk.yellow(`⚠️ WARNING: Using legacy WebPack bundler`)); } - const { stats } = await build(configs, isCi, rspack); + const { stats } = await build(configs, isCi, webpack); if (!stats) { throw new Error('No stats returned'); @@ -159,34 +157,32 @@ export async function buildBundle(options: BuildOptions) { } async function build( - configs: webpack.Configuration[], + configs: Configuration[], isCi: boolean, - rspack?: typeof import('@rspack/core').rspack, + webpack?: typeof import('webpack'), ) { - const bundler = (rspack ?? webpack) as typeof webpack; + const bundler = (webpack ?? rspack) as typeof rspack; - const stats = await new Promise( - (resolve, reject) => { - bundler(configs, (err, buildStats) => { - if (err) { - if (err.message) { - const { errors } = formatWebpackMessages({ - errors: [err.message], - warnings: new Array(), - _showErrors: true, - _showWarnings: true, - }); + const stats = await new Promise((resolve, reject) => { + bundler(configs, (err, buildStats) => { + if (err) { + if (err.message) { + const { errors } = formatWebpackMessages({ + errors: [err.message], + warnings: new Array(), + _showErrors: true, + _showWarnings: true, + }); - throw new Error(errors[0]); - } else { - reject(err); - } + throw new Error(errors[0]); } else { - resolve(buildStats); + reject(err); } - }); - }, - ); + } else { + resolve(buildStats); + } + }); + }); if (!stats) { throw new Error('Failed to compile: No stats provided'); diff --git a/packages/cli/src/modules/build/lib/bundler/config.ts b/packages/cli/src/modules/build/lib/bundler/config.ts index 655ce4fd8b..c4011ede48 100644 --- a/packages/cli/src/modules/build/lib/bundler/config.ts +++ b/packages/cli/src/modules/build/lib/bundler/config.ts @@ -16,16 +16,15 @@ import { BundlingOptions, ModuleFederationOptions } from './types'; import { resolve as resolvePath } from 'path'; -import webpack from 'webpack'; +import { rspack, Configuration } from '@rspack/core'; import { BundlingPaths } from './paths'; import { Config } from '@backstage/config'; -import ESLintPlugin from 'eslint-webpack-plugin'; -import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'; +import ESLintRspackPlugin from 'eslint-rspack-plugin'; +import { TsCheckerRspackPlugin } from 'ts-checker-rspack-plugin'; import HtmlWebpackPlugin from 'html-webpack-plugin'; -import { ModuleFederationPlugin } from '@module-federation/enhanced/webpack'; import ModuleScopePlugin from 'react-dev-utils/ModuleScopePlugin'; -import ReactRefreshPlugin from '@pmmmwh/react-refresh-webpack-plugin'; +import { ModuleFederationPlugin } from '@module-federation/enhanced/rspack'; import { paths as cliPaths } from '../../../../lib/paths'; import fs from 'fs-extra'; import { optimization as optimizationConfig } from './optimization'; @@ -113,14 +112,14 @@ async function readBuildInfo() { export async function createConfig( paths: BundlingPaths, options: BundlingOptions, -): Promise { +): Promise { const { checksEnabled, isDev, frontendConfig, moduleFederation, publicSubPath = '', - rspack, + webpack, } = options; const { plugins, loaders } = transforms(options); @@ -147,27 +146,35 @@ export async function createConfig( }, } as const; - if (rspack) { + if (webpack) { + const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); + plugins.push(new ReactRefreshPlugin(refreshOptions)); + } else { const RspackReactRefreshPlugin = require('@rspack/plugin-react-refresh'); plugins.push(new RspackReactRefreshPlugin(refreshOptions)); - } else { - plugins.push(new ReactRefreshPlugin(refreshOptions)); } } if (checksEnabled) { + const TsCheckerPlugin = webpack + ? (require('fork-ts-checker-webpack-plugin') as typeof import('fork-ts-checker-webpack-plugin')) + : TsCheckerRspackPlugin; + const ESLintPlugin = webpack + ? (require('eslint-webpack-plugin') as typeof import('eslint-webpack-plugin')) + : ESLintRspackPlugin; plugins.push( - new ForkTsCheckerWebpackPlugin({ - typescript: { configFile: paths.targetTsConfig, memoryLimit: 4096 }, + new TsCheckerPlugin({ + typescript: { configFile: paths.targetTsConfig, memoryLimit: 8192 }, }), new ESLintPlugin({ + cache: false, // Cache seems broken context: paths.targetPath, files: ['**/*.(ts|tsx|mts|cts|js|jsx|mjs|cjs)'], }), ); } - const bundler = rspack ? (rspack as unknown as typeof webpack) : webpack; + const bundler = webpack ? (webpack as unknown as typeof rspack) : rspack; // TODO(blam): process is no longer auto polyfilled by webpack in v5. // we use the provide plugin to provide this polyfill, but lets look @@ -190,7 +197,11 @@ export async function createConfig( config: frontendConfig, }, }; - if (rspack) { + if (webpack) { + // Config injection via index.html doesn't work across reloads with + // WebPack, so we rely on the APP_CONFIG injection instead + plugins.push(new HtmlWebpackPlugin(templateOptions)); + } else { // With Rspack we inject config via index.html, this is both because we // can't use APP_CONFIG due to the lack of support for runtime values, but // also because we are able to do it and it lines up better with what the @@ -204,10 +215,6 @@ export async function createConfig( options.getFrontendAppConfigs, ), ); - } else { - // Config injection via index.html doesn't work across reloads with - // WebPack, so we rely on the APP_CONFIG injection instead - plugins.push(new HtmlWebpackPlugin(templateOptions)); } plugins.push( new HtmlWebpackPlugin({ @@ -227,9 +234,9 @@ export async function createConfig( if (options.moduleFederation) { const isRemote = options.moduleFederation?.mode === 'remote'; - const AdaptedModuleFederationPlugin = rspack - ? (require('@module-federation/enhanced/rspack') - .ModuleFederationPlugin as typeof ModuleFederationPlugin) + const AdaptedModuleFederationPlugin = webpack + ? (require('@module-federation/enhanced/webpack') + .ModuleFederationPlugin as unknown as typeof ModuleFederationPlugin) : ModuleFederationPlugin; const exposes = options.moduleFederation?.exposes @@ -304,18 +311,28 @@ export async function createConfig( const buildInfo = await readBuildInfo(); plugins.push( - new bundler.DefinePlugin({ - 'process.env.BUILD_INFO': JSON.stringify(buildInfo), - 'process.env.APP_CONFIG': rspack - ? JSON.stringify([]) // Inject via index.html instead - : bundler.DefinePlugin.runtimeValue( + webpack + ? new webpack.DefinePlugin({ + 'process.env.BUILD_INFO': JSON.stringify(buildInfo), + 'process.env.APP_CONFIG': webpack.DefinePlugin.runtimeValue( () => JSON.stringify(options.getFrontendAppConfigs()), true, ), - // This allows for conditional imports of react-dom/client, since there's no way - // to check for presence of it in source code without module resolution errors. - 'process.env.HAS_REACT_DOM_CLIENT': JSON.stringify(hasReactDomClient()), - }), + // This allows for conditional imports of react-dom/client, since there's no way + // to check for presence of it in source code without module resolution errors. + 'process.env.HAS_REACT_DOM_CLIENT': JSON.stringify( + hasReactDomClient(), + ), + }) + : new bundler.DefinePlugin({ + 'process.env.BUILD_INFO': JSON.stringify(buildInfo), + 'process.env.APP_CONFIG': JSON.stringify([]), // Inject via index.html instead + // This allows for conditional imports of react-dom/client, since there's no way + // to check for presence of it in source code without module resolution errors. + 'process.env.HAS_REACT_DOM_CLIENT': JSON.stringify( + hasReactDomClient(), + ), + }), ); if (options.linkedWorkspace) { @@ -330,9 +347,8 @@ export async function createConfig( // These files are required by the transpiled code when using React Refresh. // They need to be excluded to the module scope plugin which ensures that files // that exist in the package are required. - const reactRefreshFiles = rspack - ? [] - : [ + const reactRefreshFiles = webpack + ? [ require.resolve( '@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils.js', ), @@ -340,7 +356,8 @@ export async function createConfig( '@pmmmwh/react-refresh-webpack-plugin/overlay/index.js', ), require.resolve('react-refresh'), - ]; + ] + : []; const mode = isDev ? 'development' : 'production'; const optimization = optimizationConfig(options); @@ -388,7 +405,7 @@ export async function createConfig( util: require.resolve('util/'), }, // FIXME: see also https://github.com/web-infra-dev/rspack/issues/3408 - ...(!rspack && { + ...(webpack && { plugins: [ new ModuleScopePlugin( [paths.targetSrc, paths.targetDev], @@ -421,7 +438,7 @@ export async function createConfig( }, experiments: { lazyCompilation: yn(process.env.EXPERIMENTAL_LAZY_COMPILATION), - ...(rspack && { + ...(!webpack && { // We're still using `style-loader` for custom `insert` option css: false, }), diff --git a/packages/cli/src/modules/build/lib/bundler/linkWorkspaces.ts b/packages/cli/src/modules/build/lib/bundler/linkWorkspaces.ts index a2fe6cd25a..e2a9e7b6b3 100644 --- a/packages/cli/src/modules/build/lib/bundler/linkWorkspaces.ts +++ b/packages/cli/src/modules/build/lib/bundler/linkWorkspaces.ts @@ -16,7 +16,7 @@ import { relative as relativePath } from 'path'; import { getPackages } from '@manypkg/get-packages'; -import webpack from 'webpack'; +import { rspack } from '@rspack/core'; import { paths } from '../../../../lib/paths'; /** @@ -26,7 +26,7 @@ import { paths } from '../../../../lib/paths'; * of react and react-dom which are always resolved from the target workspace. */ export async function createWorkspaceLinkingPlugins( - bundler: typeof webpack, + bundler: typeof rspack, workspace: string, ) { const { packages: linkedPackages, root: linkedRoot } = await getPackages( diff --git a/packages/cli/src/modules/build/lib/bundler/optimization.ts b/packages/cli/src/modules/build/lib/bundler/optimization.ts index 9f61043910..5d763ea4ac 100644 --- a/packages/cli/src/modules/build/lib/bundler/optimization.ts +++ b/packages/cli/src/modules/build/lib/bundler/optimization.ts @@ -14,19 +14,21 @@ * limitations under the License. */ -import { WebpackOptionsNormalized } from 'webpack'; import { BundlingOptions } from './types'; - -const { EsbuildPlugin } = require('esbuild-loader'); +import { + SwcJsMinimizerRspackPlugin, + LightningCssMinimizerRspackPlugin, + RspackOptionsNormalized, +} from '@rspack/core'; export const optimization = ( options: BundlingOptions, -): WebpackOptionsNormalized['optimization'] => { - const { isDev, rspack } = options; +): RspackOptionsNormalized['optimization'] => { + const { isDev, webpack } = options; - const MinifyPlugin = rspack - ? rspack.SwcJsMinimizerRspackPlugin - : EsbuildPlugin; + const MinifyPlugin = webpack + ? require('esbuild-loader').EsbuildPlugin + : SwcJsMinimizerRspackPlugin; return { minimize: !isDev, @@ -42,7 +44,7 @@ export const optimization = ( format: undefined, include: 'remoteEntry.js', }), - rspack && new rspack.LightningCssMinimizerRspackPlugin(), + webpack ? undefined : new LightningCssMinimizerRspackPlugin(), ], runtimeChunk: 'single', splitChunks: { @@ -74,7 +76,7 @@ export const optimization = ( priority: 10, minSize: 100000, minChunks: 1, - ...(!rspack && { + ...(webpack && { maxAsyncRequests: Infinity, maxInitialRequests: Infinity, }), diff --git a/packages/cli/src/modules/build/lib/bundler/server.ts b/packages/cli/src/modules/build/lib/bundler/server.ts index 9d4af5c0fe..76043e67c6 100644 --- a/packages/cli/src/modules/build/lib/bundler/server.ts +++ b/packages/cli/src/modules/build/lib/bundler/server.ts @@ -19,8 +19,8 @@ import chalk from 'chalk'; import fs from 'fs-extra'; import { resolve as resolvePath } from 'path'; import openBrowser from 'react-dev-utils/openBrowser'; -import webpack from 'webpack'; -import WebpackDevServer from 'webpack-dev-server'; +import { rspack } from '@rspack/core'; +import { RspackDevServer } from '@rspack/dev-server'; import { paths as libPaths } from '../../../../lib/paths'; import { loadCliConfig } from '../../../config/lib/config'; @@ -55,20 +55,20 @@ DEPRECATION WARNING: React Router Beta is deprecated and support for it will be resolvePath(options.targetDir ?? libPaths.targetDir, 'package.json'), ); - let webpackServer: WebpackDevServer | undefined = undefined; + let devServer: RspackDevServer | undefined = undefined; let latestFrontendAppConfigs: AppConfig[] = []; /** Triggers a full reload of all clients */ const triggerReload = () => { - if (webpackServer) { - webpackServer.invalidate(); + if (devServer) { + devServer.invalidate(); // For the Rspack server it's not enough to invalidate, we also need to // tell the browser to reload, which we do with a 'static-changed' message - if (process.env.EXPERIMENTAL_RSPACK) { - webpackServer.sendMessage( - webpackServer.webSocketServer?.clients ?? [], + if (!process.env.LEGACY_WEBPACK_BUILD) { + devServer.sendMessage( + devServer.webSocketServer?.clients ?? [], 'static-changed', ); } @@ -122,8 +122,8 @@ DEPRECATION WARNING: React Router Beta is deprecated and support for it will be }, }); - const rspack = process.env.EXPERIMENTAL_RSPACK - ? (require('@rspack/core') as typeof import('@rspack/core').rspack) + const webpack = process.env.LEGACY_WEBPACK_BUILD + ? (require('webpack') as typeof import('webpack')) : undefined; const commonConfigOptions = { @@ -132,7 +132,7 @@ DEPRECATION WARNING: React Router Beta is deprecated and support for it will be isDev: true, baseUrl: url, frontendConfig, - rspack, + webpack, getFrontendAppConfigs: () => { return latestFrontendAppConfigs; }, @@ -144,15 +144,13 @@ DEPRECATION WARNING: React Router Beta is deprecated and support for it will be moduleFederation: options.moduleFederation, }); - const bundler = (rspack ?? webpack) as typeof webpack; - const DevServer: typeof WebpackDevServer = rspack - ? require('@rspack/dev-server').RspackDevServer - : WebpackDevServer; + const bundler = (webpack ?? rspack) as typeof rspack; + const DevServer: typeof RspackDevServer = webpack + ? require('webpack-dev-server') + : RspackDevServer; - if (rspack) { - console.log( - chalk.yellow(`⚠️ WARNING: Using experimental RSPack dev server.`), - ); + if (webpack) { + console.log(chalk.yellow(`⚠️ WARNING: Using legacy WebPack dev server.`)); } const publicPaths = await resolveOptionalBundlingPaths({ @@ -170,7 +168,7 @@ DEPRECATION WARNING: React Router Beta is deprecated and support for it will be ? bundler([config, await createConfig(publicPaths, commonConfigOptions)]) : bundler(config); - webpackServer = new DevServer( + devServer = new DevServer( { hot: !process.env.CI, devMiddleware: { @@ -225,8 +223,8 @@ DEPRECATION WARNING: React Router Beta is deprecated and support for it will be ); await new Promise(async (resolve, reject) => { - if (webpackServer) { - webpackServer.startCallback((err?: Error) => { + if (devServer) { + devServer.startCallback((err?: Error) => { if (err) { reject(err); return; @@ -245,7 +243,7 @@ DEPRECATION WARNING: React Router Beta is deprecated and support for it will be const waitForExit = async () => { for (const signal of ['SIGINT', 'SIGTERM'] as const) { process.on(signal, () => { - webpackServer?.stop(); + devServer?.stop(); // exit instead of resolve. The process is shutting down and resolving a promise here logs an error process.exit(); }); diff --git a/packages/cli/src/modules/build/lib/bundler/transforms.ts b/packages/cli/src/modules/build/lib/bundler/transforms.ts index ec78311ee2..64e33b220e 100644 --- a/packages/cli/src/modules/build/lib/bundler/transforms.ts +++ b/packages/cli/src/modules/build/lib/bundler/transforms.ts @@ -14,26 +14,30 @@ * limitations under the License. */ -import { RuleSetRule, WebpackPluginInstance } from 'webpack'; -import MiniCssExtractPlugin from 'mini-css-extract-plugin'; +import { + RuleSetRule, + RspackPluginInstance, + CssExtractRspackPlugin, + WebpackPluginInstance, +} from '@rspack/core'; type Transforms = { loaders: RuleSetRule[]; - plugins: WebpackPluginInstance[]; + plugins: Array; }; type TransformOptions = { isDev: boolean; isBackend?: boolean; - rspack?: typeof import('@rspack/core').rspack; + webpack?: typeof import('webpack').webpack; }; export const transforms = (options: TransformOptions): Transforms => { - const { isDev, isBackend, rspack } = options; + const { isDev, isBackend, webpack } = options; - const CssExtractRspackPlugin: typeof MiniCssExtractPlugin = rspack - ? (rspack.CssExtractRspackPlugin as unknown as typeof MiniCssExtractPlugin) - : MiniCssExtractPlugin; + const CssExtractPlugin: typeof CssExtractRspackPlugin = webpack + ? (require('mini-css-extract-plugin') as unknown as typeof CssExtractRspackPlugin) + : CssExtractRspackPlugin; // This ensures that styles inserted from the style-loader and any // async style chunks are always given lower priority than JSS styles. @@ -58,7 +62,9 @@ export const transforms = (options: TransformOptions): Transforms => { exclude: /node_modules/, use: [ { - loader: rspack ? 'builtin:swc-loader' : require.resolve('swc-loader'), + loader: webpack + ? require.resolve('swc-loader') + : 'builtin:swc-loader', options: { jsc: { target: 'es2022', @@ -86,7 +92,9 @@ export const transforms = (options: TransformOptions): Transforms => { exclude: /node_modules/, use: [ { - loader: rspack ? 'builtin:swc-loader' : require.resolve('swc-loader'), + loader: webpack + ? require.resolve('swc-loader') + : 'builtin:swc-loader', options: { jsc: { target: 'es2022', @@ -161,7 +169,7 @@ export const transforms = (options: TransformOptions): Transforms => { insert: insertBeforeJssStyles, }, } - : CssExtractRspackPlugin.loader, + : CssExtractPlugin.loader, { loader: require.resolve('css-loader'), options: { @@ -172,11 +180,11 @@ export const transforms = (options: TransformOptions): Transforms => { }, ]; - const plugins = new Array(); + const plugins = new Array(); if (!isDev) { plugins.push( - new CssExtractRspackPlugin({ + new CssExtractPlugin({ filename: 'static/[name].[contenthash:8].css', chunkFilename: 'static/[name].[id].[contenthash:8].css', insert: insertBeforeJssStyles, // Only applies to async chunks diff --git a/packages/cli/src/modules/build/lib/bundler/types.ts b/packages/cli/src/modules/build/lib/bundler/types.ts index a018ccad93..59b0dcb70d 100644 --- a/packages/cli/src/modules/build/lib/bundler/types.ts +++ b/packages/cli/src/modules/build/lib/bundler/types.ts @@ -45,7 +45,7 @@ export type BundlingOptions = { // An external linked workspace to include in the bundling linkedWorkspace?: string; moduleFederation?: ModuleFederationOptions; - rspack?: typeof import('@rspack/core').rspack; + webpack?: typeof import('webpack'); }; export type ServeOptions = BundlingPathsOptions & { @@ -69,7 +69,7 @@ export type BuildOptions = BundlingPathsOptions & { frontendAppConfigs: AppConfig[]; fullConfig: Config; moduleFederation?: ModuleFederationOptions; - rspack?: typeof import('@rspack/core').rspack; + webpack?: typeof import('webpack'); }; export type BackendBundlingOptions = { @@ -79,5 +79,5 @@ export type BackendBundlingOptions = { inspectEnabled: boolean; inspectBrkEnabled: boolean; require?: string; - rspack?: typeof import('@rspack/core').rspack; + webpack?: typeof import('webpack'); }; diff --git a/packages/cli/src/types.d.ts b/packages/cli/src/types.d.ts index 10a2490439..3ddef88689 100644 --- a/packages/cli/src/types.d.ts +++ b/packages/cli/src/types.d.ts @@ -58,8 +58,6 @@ declare module 'react-dev-utils/ModuleScopePlugin' { } declare module 'react-dev-utils/FileSizeReporter' { - import webpack = require('webpack'); - export interface OpaqueFileSizes { root: string; sizes: Record; @@ -81,7 +79,7 @@ declare module 'react-dev-utils/FileSizeReporter' { * the main bundle or a chunk exceeds the specified size (in bytes). */ export function printFileSizesAfterBuild( - webpackStats: webpack.Stats, + stats: import('@rspack/core').Stats, previousFileSizes: OpaqueFileSizes, buildFolder: string, maxBundleGzipSize?: number, diff --git a/packages/create-app/templates/default-app/playwright.config.ts b/packages/create-app/templates/default-app/playwright.config.ts index 37c7fb14c7..0dc740b466 100644 --- a/packages/create-app/templates/default-app/playwright.config.ts +++ b/packages/create-app/templates/default-app/playwright.config.ts @@ -32,11 +32,17 @@ export default defineConfig({ ? [] : [ { - command: 'yarn start', + command: 'yarn start app', port: 3000, reuseExistingServer: true, timeout: 60_000, }, + { + command: 'yarn start backend', + port: 7007, + reuseExistingServer: true, + timeout: 60_000, + }, ], forbidOnly: !!process.env.CI, diff --git a/packages/create-app/templates/next-app/playwright.config.ts b/packages/create-app/templates/next-app/playwright.config.ts index 37c7fb14c7..0dc740b466 100644 --- a/packages/create-app/templates/next-app/playwright.config.ts +++ b/packages/create-app/templates/next-app/playwright.config.ts @@ -32,11 +32,17 @@ export default defineConfig({ ? [] : [ { - command: 'yarn start', + command: 'yarn start app', port: 3000, reuseExistingServer: true, timeout: 60_000, }, + { + command: 'yarn start backend', + port: 7007, + reuseExistingServer: true, + timeout: 60_000, + }, ], forbidOnly: !!process.env.CI, diff --git a/packages/e2e-test/src/commands/run.ts b/packages/e2e-test/src/commands/run.ts index 796bd43fb0..6df58349df 100644 --- a/packages/e2e-test/src/commands/run.ts +++ b/packages/e2e-test/src/commands/run.ts @@ -221,7 +221,7 @@ async function buildDistWorkspace(workspaceName: string, rootDir: string) { */ async function pinYarnVersion(dir: string) { const yarnRc = await fs.readFile(paths.resolveOwnRoot('.yarnrc.yml'), 'utf8'); - const yarnRcLines = yarnRc.split('\n'); + const yarnRcLines = yarnRc.split(/\r?\n/); const yarnPathLine = yarnRcLines.find(line => line.startsWith('yarnPath:')); if (!yarnPathLine) { throw new Error(`Unable to find 'yarnPath' in ${yarnRc}`); diff --git a/playwright.config.ts b/playwright.config.ts index ee8228e413..a5867ce217 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -32,18 +32,17 @@ export default defineConfig({ ? [] : [ { - command: 'yarn start', + command: 'yarn start example-app', port: 3000, reuseExistingServer: true, timeout: 60_000, }, - // TODO: Before encouraging e2e tests for backend we'll want to provide better utilities for mocking auth - // { - // command: 'yarn start-backend', - // port: 7007, - // reuseExistingServer: true, - // timeout: 60_000, - // }, + { + command: 'yarn start example-backend', + port: 7007, + reuseExistingServer: true, + timeout: 60_000, + }, ], forbidOnly: !!process.env.CI, diff --git a/yarn.lock b/yarn.lock index cdbc99a4c7..32e694979c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1896,14 +1896,14 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.16.7, @babel/code-frame@npm:^7.24.2, @babel/code-frame@npm:^7.25.9, @babel/code-frame@npm:^7.26.0, @babel/code-frame@npm:^7.26.2, @babel/code-frame@npm:^7.8.3": - version: 7.26.2 - resolution: "@babel/code-frame@npm:7.26.2" +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.16.7, @babel/code-frame@npm:^7.24.2, @babel/code-frame@npm:^7.25.9, @babel/code-frame@npm:^7.26.0, @babel/code-frame@npm:^7.26.2, @babel/code-frame@npm:^7.27.1, @babel/code-frame@npm:^7.8.3": + version: 7.27.1 + resolution: "@babel/code-frame@npm:7.27.1" dependencies: - "@babel/helper-validator-identifier": "npm:^7.25.9" + "@babel/helper-validator-identifier": "npm:^7.27.1" js-tokens: "npm:^4.0.0" - picocolors: "npm:^1.0.0" - checksum: 10/db2c2122af79d31ca916755331bb4bac96feb2b334cdaca5097a6b467fdd41963b89b14b6836a14f083de7ff887fc78fa1b3c10b14e743d33e12dbfe5ee3d223 + picocolors: "npm:^1.1.1" + checksum: 10/721b8a6e360a1fa0f1c9fe7351ae6c874828e119183688b533c477aa378f1010f37cc9afbfc4722c686d1f5cdd00da02eab4ba7278a0c504fa0d7a321dcd4fdf languageName: node linkType: hard @@ -2976,13 +2976,12 @@ __metadata: "@rollup/plugin-json": "npm:^6.0.0" "@rollup/plugin-node-resolve": "npm:^15.0.0" "@rollup/plugin-yaml": "npm:^4.0.0" - "@rspack/core": "npm:^1.3.9" - "@rspack/dev-server": "npm:^1.1.1" - "@rspack/plugin-react-refresh": "npm:^1.4.2" + "@rspack/core": "npm:^1.4.11" + "@rspack/dev-server": "npm:^1.1.4" + "@rspack/plugin-react-refresh": "npm:^1.4.3" "@spotify/eslint-config-base": "npm:^15.0.0" "@spotify/eslint-config-react": "npm:^15.0.0" "@spotify/eslint-config-typescript": "npm:^15.0.0" - "@sucrase/webpack-loader": "npm:^2.0.0" "@swc/core": "npm:^1.3.46" "@swc/helpers": "npm:^0.5.0" "@swc/jest": "npm:^0.2.22" @@ -3030,6 +3029,7 @@ __metadata: eslint-plugin-react: "npm:^7.37.2" eslint-plugin-react-hooks: "npm:^5.0.0" eslint-plugin-unused-imports: "npm:^4.1.4" + eslint-rspack-plugin: "npm:^4.2.1" eslint-webpack-plugin: "npm:^4.2.0" express: "npm:^4.17.1" fork-ts-checker-webpack-plugin: "npm:^9.0.0" @@ -3075,10 +3075,11 @@ __metadata: swc-loader: "npm:^0.2.3" tar: "npm:^6.1.12" terser-webpack-plugin: "npm:^5.1.3" + ts-checker-rspack-plugin: "npm:^1.1.5" ts-morph: "npm:^24.0.0" undici: "npm:^7.2.3" util: "npm:^0.12.3" - webpack: "npm:^5.94.0" + webpack: "npm:~5.96.0" webpack-dev-server: "npm:^5.0.0" yaml: "npm:^2.0.0" yargs: "npm:^16.2.0" @@ -3087,15 +3088,33 @@ __metadata: zod: "npm:^3.22.4" zod-validation-error: "npm:^3.4.0" peerDependencies: - "@rspack/core": ^1.2.8 - "@rspack/dev-server": ^1.0.9 - "@rspack/plugin-react-refresh": ^1.0.0 + "@module-federation/enhanced": ^0.9.0 + "@pmmmwh/react-refresh-webpack-plugin": ^0.5.7 + esbuild-loader: ^4.0.0 + eslint-webpack-plugin: ^4.2.0 + fork-ts-checker-webpack-plugin: ^9.0.0 + mini-css-extract-plugin: ^2.4.2 + terser-webpack-plugin: ^5.1.3 + webpack: ~5.96.0 + webpack-dev-server: ^5.0.0 peerDependenciesMeta: - "@rspack/core": + "@module-federation/enhanced": optional: true - "@rspack/dev-server": + "@pmmmwh/react-refresh-webpack-plugin": optional: true - "@rspack/plugin-react-refresh": + esbuild-loader: + optional: true + eslint-webpack-plugin: + optional: true + fork-ts-checker-webpack-plugin: + optional: true + mini-css-extract-plugin: + optional: true + terser-webpack-plugin: + optional: true + webpack: + optional: true + webpack-dev-server: optional: true bin: backstage-cli: bin/backstage-cli @@ -10293,6 +10312,73 @@ __metadata: languageName: node linkType: hard +"@jsonjoy.com/base64@npm:^1.1.2": + version: 1.1.2 + resolution: "@jsonjoy.com/base64@npm:1.1.2" + peerDependencies: + tslib: 2 + checksum: 10/d76bb58eff841c090d9bf69a073611ffa73c40a664ccbcea689f65961f57d7b24051269d06b437e4f6204285d6ba92f50f587c5e95c5f9e4f10b36a2ed4cd0c8 + languageName: node + linkType: hard + +"@jsonjoy.com/buffers@npm:^1.0.0": + version: 1.0.0 + resolution: "@jsonjoy.com/buffers@npm:1.0.0" + peerDependencies: + tslib: 2 + checksum: 10/3347a16a555398c19265203877b8e325be5facc1d875c0e85db0cc20b53418302257ed5af15bf53b84ff8337bd024a10be25ff3a02ebc54ae8e643577ca29e63 + languageName: node + linkType: hard + +"@jsonjoy.com/codegen@npm:^1.0.0": + version: 1.0.0 + resolution: "@jsonjoy.com/codegen@npm:1.0.0" + peerDependencies: + tslib: 2 + checksum: 10/a0afb03d2af4fbc1377c547e507f5db99a25f515d8c4b6b2cef1ff28145ac59fff12b6e1f41f9734cb62ea5619e7f9be1acd0908305d6f4176898ee534ee9a64 + languageName: node + linkType: hard + +"@jsonjoy.com/json-pack@npm:^1.0.3": + version: 1.10.0 + resolution: "@jsonjoy.com/json-pack@npm:1.10.0" + dependencies: + "@jsonjoy.com/base64": "npm:^1.1.2" + "@jsonjoy.com/buffers": "npm:^1.0.0" + "@jsonjoy.com/codegen": "npm:^1.0.0" + "@jsonjoy.com/json-pointer": "npm:^1.0.1" + "@jsonjoy.com/util": "npm:^1.9.0" + hyperdyperid: "npm:^1.2.0" + thingies: "npm:^2.5.0" + peerDependencies: + tslib: 2 + checksum: 10/5ad1eb008cf5f681dbc1301f5a7c33ca3a101d22773dbd2a9db4b02e5847c2f8b0c5f074c69f4ced5c24b3dc1dd85ddbc1688f0002fee9416869c579c34c4a8e + languageName: node + linkType: hard + +"@jsonjoy.com/json-pointer@npm:^1.0.1": + version: 1.0.1 + resolution: "@jsonjoy.com/json-pointer@npm:1.0.1" + dependencies: + "@jsonjoy.com/util": "npm:^1.3.0" + peerDependencies: + tslib: 2 + checksum: 10/ff15df95df38677a09fe2155cf4521844c9f0653c77a7090bb6e726d275fb78ab6c3543c4ebdc14aff07e44da08b1545631a63e42e217c18059194957e060ed9 + languageName: node + linkType: hard + +"@jsonjoy.com/util@npm:^1.3.0, @jsonjoy.com/util@npm:^1.9.0": + version: 1.9.0 + resolution: "@jsonjoy.com/util@npm:1.9.0" + dependencies: + "@jsonjoy.com/buffers": "npm:^1.0.0" + "@jsonjoy.com/codegen": "npm:^1.0.0" + peerDependencies: + tslib: 2 + checksum: 10/1a6e5301d725a7161b93ff707eb1a954bf4552a2fa96eee9a960d3ae3ed5f993d18b56dcff29e98036341a5968c5d1b2dfe21f76695390e7f0d89b81f24c85e0 + languageName: node + linkType: hard + "@juggle/resize-observer@npm:^3.3.1": version: 3.3.1 resolution: "@juggle/resize-observer@npm:3.3.1" @@ -16831,7 +16917,7 @@ __metadata: languageName: node linkType: hard -"@rspack/core@npm:^1.3.9": +"@rspack/core@npm:^1.4.11": version: 1.4.11 resolution: "@rspack/core@npm:1.4.11" dependencies: @@ -16847,7 +16933,7 @@ __metadata: languageName: node linkType: hard -"@rspack/dev-server@npm:^1.1.1": +"@rspack/dev-server@npm:^1.1.4": version: 1.1.4 resolution: "@rspack/dev-server@npm:1.1.4" dependencies: @@ -16862,14 +16948,14 @@ __metadata: languageName: node linkType: hard -"@rspack/lite-tapable@npm:1.0.1": +"@rspack/lite-tapable@npm:1.0.1, @rspack/lite-tapable@npm:^1.0.1": version: 1.0.1 resolution: "@rspack/lite-tapable@npm:1.0.1" checksum: 10/240b7832965bca5a52d1f03a8539dab5810958ce24b5a670405b2505d81350f10d668f4055648f5918bc18ac033e637bcb7f92189345f0f2f671b546019c2f9e languageName: node linkType: hard -"@rspack/plugin-react-refresh@npm:^1.4.2": +"@rspack/plugin-react-refresh@npm:^1.4.3": version: 1.4.3 resolution: "@rspack/plugin-react-refresh@npm:1.4.3" dependencies: @@ -18870,17 +18956,6 @@ __metadata: languageName: node linkType: hard -"@sucrase/webpack-loader@npm:^2.0.0": - version: 2.0.0 - resolution: "@sucrase/webpack-loader@npm:2.0.0" - dependencies: - loader-utils: "npm:^1.1.0" - peerDependencies: - sucrase: ^3 - checksum: 10/16578991b1b888ac5bec5628bd24db9e21651bbbe30de076aece8787f115d8971ac87a20bc75446187c73c3185851ec2233d5b6f18c4a2dd53fbbb1ed4e488b4 - languageName: node - linkType: hard - "@swagger-api/apidom-ast@npm:^1.0.0-beta.11": version: 1.0.0-beta.11 resolution: "@swagger-api/apidom-ast@npm:1.0.0-beta.11" @@ -29853,6 +29928,21 @@ __metadata: languageName: node linkType: hard +"eslint-rspack-plugin@npm:^4.2.1": + version: 4.2.1 + resolution: "eslint-rspack-plugin@npm:4.2.1" + dependencies: + "@types/eslint": "npm:^8.56.10" + jest-worker: "npm:^29.7.0" + micromatch: "npm:^4.0.8" + normalize-path: "npm:^3.0.0" + schema-utils: "npm:^4.2.0" + peerDependencies: + eslint: ^8.0.0 || ^9.0.0 + checksum: 10/c45639e660d13af63c58f8f80103a033f607b9d635d9315d7d859408134cc35e2e3f981805f7abdd6061ec9d8207cfc7c0d5a097df9ac8c7ccd707ca5aa14583 + languageName: node + linkType: hard + "eslint-scope@npm:5.1.1, eslint-scope@npm:^5.1.1": version: 5.1.1 resolution: "eslint-scope@npm:5.1.1" @@ -33252,6 +33342,13 @@ __metadata: languageName: node linkType: hard +"hyperdyperid@npm:^1.2.0": + version: 1.2.0 + resolution: "hyperdyperid@npm:1.2.0" + checksum: 10/64abb5568ff17aa08ac0175ae55e46e22831c5552be98acdd1692081db0209f36fff58b31432017b4e1772c178962676a2cc3c54e4d5d7f020d7710cec7ad7a6 + languageName: node + linkType: hard + "hyphenate-style-name@npm:^1.0.3": version: 1.0.3 resolution: "hyphenate-style-name@npm:1.0.3" @@ -37840,12 +37937,15 @@ __metadata: languageName: node linkType: hard -"memfs@npm:^4.6.0": - version: 4.7.7 - resolution: "memfs@npm:4.7.7" +"memfs@npm:^4.28.0, memfs@npm:^4.6.0": + version: 4.36.0 + resolution: "memfs@npm:4.36.0" dependencies: + "@jsonjoy.com/json-pack": "npm:^1.0.3" + "@jsonjoy.com/util": "npm:^1.3.0" + tree-dump: "npm:^1.0.1" tslib: "npm:^2.0.0" - checksum: 10/311633e5857c91f41021b43f00eda8d540fed2c2d9e02c780fe78de720cfb55d15ab2d5b5ce9f2576637589b82e84488f1b9ff503563e817ed65200ad24617fb + checksum: 10/4898187c3278bd127340ec3d6c51d1fa77faa50aad71a8a1e4b7c02abbccd8485dccc7f565291b386b0cc96e5ac06902e4645108fad44e458fe8c1dfee3caf42 languageName: node linkType: hard @@ -47372,6 +47472,15 @@ __metadata: languageName: node linkType: hard +"thingies@npm:^2.5.0": + version: 2.5.0 + resolution: "thingies@npm:2.5.0" + peerDependencies: + tslib: ^2 + checksum: 10/b8b028a474aab798ff12ad5a5648306059976d3e23870327ae4ef640012953314b1d7f208dd5a8e9ebaeee6dd1cdb05503bb829699ee8f36514c37f771f2f035 + languageName: node + linkType: hard + "throttle-debounce@npm:^3.0.1": version: 3.0.1 resolution: "throttle-debounce@npm:3.0.1" @@ -47671,6 +47780,15 @@ __metadata: languageName: node linkType: hard +"tree-dump@npm:^1.0.1": + version: 1.0.3 + resolution: "tree-dump@npm:1.0.3" + peerDependencies: + tslib: 2 + checksum: 10/cf382e61cfb5e3ff8f03425b5bc1923e8f0e385b3a02f43d9d0a32d09da9984477e0f2a7698628662263d1d3f1af17e33486c77ff454978f0f9f07fb5d1fe9a2 + languageName: node + linkType: hard + "tree-kill@npm:^1.2.2": version: 1.2.2 resolution: "tree-kill@npm:1.2.2" @@ -47767,6 +47885,27 @@ __metadata: languageName: node linkType: hard +"ts-checker-rspack-plugin@npm:^1.1.5": + version: 1.1.5 + resolution: "ts-checker-rspack-plugin@npm:1.1.5" + dependencies: + "@babel/code-frame": "npm:^7.27.1" + "@rspack/lite-tapable": "npm:^1.0.1" + chokidar: "npm:^3.6.0" + is-glob: "npm:^4.0.3" + memfs: "npm:^4.28.0" + minimatch: "npm:^9.0.5" + picocolors: "npm:^1.1.1" + peerDependencies: + "@rspack/core": ^1.0.0 + typescript: ">=3.8.0" + peerDependenciesMeta: + "@rspack/core": + optional: true + checksum: 10/d43f87a5860f3b8f528bf8f43c78c2d958a18fa484d791a204047fc5672134fd689665021ee25b07f9f9d8c807cd291978f58b789bf531b4704a9a4e3ad84735 + languageName: node + linkType: hard + "ts-dedent@npm:^2.0.0, ts-dedent@npm:^2.2.0": version: 2.2.0 resolution: "ts-dedent@npm:2.2.0" @@ -49468,7 +49607,7 @@ __metadata: languageName: node linkType: hard -"webpack@npm:5, webpack@npm:^5, webpack@npm:^5.94.0": +"webpack@npm:5, webpack@npm:^5, webpack@npm:~5.96.0": version: 5.96.1 resolution: "webpack@npm:5.96.1" dependencies: