From 6b1c88987499ffa736ff025c3334a460568416a7 Mon Sep 17 00:00:00 2001 From: neverland Date: Wed, 15 Oct 2025 13:45:44 +0800 Subject: [PATCH 1/5] refactor: simplify public directory path formatting --- packages/core/src/defaultConfig.ts | 39 ++++++++++++++-------- packages/core/src/plugins/html.ts | 9 +---- packages/core/src/plugins/server.ts | 15 +++------ packages/core/src/provider/initConfigs.ts | 7 ++-- packages/core/src/server/devMiddlewares.ts | 8 +---- packages/core/src/server/devServer.ts | 1 - 6 files changed, 37 insertions(+), 42 deletions(-) diff --git a/packages/core/src/defaultConfig.ts b/packages/core/src/defaultConfig.ts index e147c68be2..6e00b94cad 100644 --- a/packages/core/src/defaultConfig.ts +++ b/packages/core/src/defaultConfig.ts @@ -1,5 +1,5 @@ import { createRequire } from 'node:module'; -import { dirname, join } from 'node:path'; +import { dirname, isAbsolute, join } from 'node:path'; import { ASSETS_DIST_DIR, CSS_DIST_DIR, @@ -301,11 +301,15 @@ export const withDefaultConfig = async ( */ export const normalizeConfigStructure = ( config: RsbuildConfig, + rootPath: string, ): RsbuildConfig => { const { dev, output } = config; config.server ||= {}; - config.server.publicDir = normalizePublicDirs(config.server.publicDir); + config.server.publicDir = normalizePublicDirs( + rootPath, + config.server.publicDir, + ); if (typeof output?.distPath === 'string') { config = { @@ -334,7 +338,10 @@ export const normalizeConfigStructure = ( * Normalizes the user configuration by merging it with defaults and ensuring * consistent structure. */ -export const normalizeConfig = (config: RsbuildConfig): NormalizedConfig => { +export const normalizeConfig = ( + config: RsbuildConfig, + rootPath: string, +): NormalizedConfig => { const getMode = (): RsbuildMode => { if (config.mode) { return config.mode; @@ -350,13 +357,14 @@ export const normalizeConfig = (config: RsbuildConfig): NormalizedConfig => { ...createDefaultConfig(), mode: getMode(), }, - normalizeConfigStructure(config), + normalizeConfigStructure(config, rootPath), ) as Required; return mergedConfig as unknown as NormalizedConfig; }; const normalizePublicDirs = ( + rootPath: string, publicDir?: PublicDir, ): Required[] => { if (publicDir === false) { @@ -364,7 +372,7 @@ const normalizePublicDirs = ( } const defaultConfig: Required = { - name: 'public', + name: join(rootPath, 'public'), copyOnBuild: 'auto', watch: false, }; @@ -374,17 +382,20 @@ const normalizePublicDirs = ( return [defaultConfig]; } - if (Array.isArray(publicDir)) { - return publicDir.map((options) => ({ + const mergeWithDefault = (options: PublicDirOptions) => { + const merged = { ...defaultConfig, ...options, - })); + }; + if (!isAbsolute(merged.name)) { + merged.name = join(rootPath, merged.name); + } + return merged; + }; + + if (Array.isArray(publicDir)) { + return publicDir.map((options) => mergeWithDefault(options)); } - return [ - { - ...defaultConfig, - ...publicDir, - }, - ]; + return [mergeWithDefault(publicDir)]; }; diff --git a/packages/core/src/plugins/html.ts b/packages/core/src/plugins/html.ts index 9ba4ca88e7..5e1a9c918e 100644 --- a/packages/core/src/plugins/html.ts +++ b/packages/core/src/plugins/html.ts @@ -220,16 +220,9 @@ export const pluginHtml = (context: InternalContext): RsbuildPlugin => ({ return defaultFavicon; } - const { rootPath } = api.context; const { publicDir } = api.getNormalizedConfig().server; const extensions = ['ico', 'png', 'svg']; - const publicDirs = Array.from( - new Set( - publicDir.map(({ name }) => - isAbsolute(name) ? name : path.join(rootPath, name), - ), - ), - ); + const publicDirs = Array.from(new Set(publicDir.map(({ name }) => name))); const faviconPaths: string[] = []; for (const publicDir of publicDirs) { diff --git a/packages/core/src/plugins/server.ts b/packages/core/src/plugins/server.ts index 97706a745e..f46a2fd14e 100644 --- a/packages/core/src/plugins/server.ts +++ b/packages/core/src/plugins/server.ts @@ -1,5 +1,4 @@ import fs from 'node:fs'; -import { isAbsolute, join } from 'node:path'; import { isDeno } from '../constants'; import { color } from '../helpers'; import { dedupeNestedPaths } from '../helpers/path'; @@ -31,16 +30,12 @@ export const pluginServer = (): RsbuildPlugin => ({ } const config = api.getNormalizedConfig(); - for (const { name, copyOnBuild } of config.server.publicDir) { - if (copyOnBuild === false || !name) { + for (const { name: publicDir, copyOnBuild } of config.server.publicDir) { + if (copyOnBuild === false) { continue; } - const normalizedPath = isAbsolute(name) - ? name - : join(api.context.rootPath, name); - - if (!fs.existsSync(normalizedPath)) { + if (!fs.existsSync(publicDir)) { continue; } @@ -67,7 +62,7 @@ export const pluginServer = (): RsbuildPlugin => ({ }); } - return fs.promises.cp(normalizedPath, distPath, { + return fs.promises.cp(publicDir, distPath, { recursive: true, // dereference symlinks dereference: true, @@ -76,7 +71,7 @@ export const pluginServer = (): RsbuildPlugin => ({ ); } catch (err) { if (err instanceof Error) { - err.message = `Failed to copy public directory '${color.yellow(normalizedPath)}' to output directory. To disable public directory copying, set \`${color.cyan('server.publicDir: false')}\` in your config.\n${err.message}`; + err.message = `Failed to copy public directory '${color.yellow(publicDir)}' to output directory. To disable public directory copying, set \`${color.cyan('server.publicDir: false')}\` in your config.\n${err.message}`; } throw err; diff --git a/packages/core/src/provider/initConfigs.ts b/packages/core/src/provider/initConfigs.ts index 4ec6d5d7e8..c742a1f839 100644 --- a/packages/core/src/provider/initConfigs.ts +++ b/packages/core/src/provider/initConfigs.ts @@ -151,7 +151,7 @@ const initEnvironmentConfigs = ( ...baseConfig, dev: pick(dev, allowedEnvironmentDevKeys), }, - normalizeConfigStructure(config), + normalizeConfigStructure(config, rootPath), ), } as unknown as MergedEnvironmentConfig; @@ -247,7 +247,10 @@ export async function initRsbuildConfig({ await modifyRsbuildConfig(context); - const normalizedBaseConfig = normalizeConfig(context.config); + const normalizedBaseConfig = normalizeConfig( + context.config, + context.rootPath, + ); const environments: Record = {}; const mergedEnvironments = initEnvironmentConfigs( diff --git a/packages/core/src/server/devMiddlewares.ts b/packages/core/src/server/devMiddlewares.ts index 479994ab34..af08f4c49f 100644 --- a/packages/core/src/server/devMiddlewares.ts +++ b/packages/core/src/server/devMiddlewares.ts @@ -1,4 +1,3 @@ -import { isAbsolute, join } from 'node:path'; import { castArray, pick } from '../helpers'; import { isMultiCompiler } from '../helpers/compiler'; import { requireCompiledPackage } from '../helpers/vendors'; @@ -34,7 +33,6 @@ export type RsbuildDevMiddlewareOptions = { * Callbacks returned by the `onBeforeStartDevServer` hook. */ postCallbacks: (() => void)[]; - pwd: string; }; const applySetupMiddlewares = ( @@ -72,7 +70,6 @@ const applyDefaultMiddlewares = ({ context, devServerAPI, middlewares, - pwd, postCallbacks, }: RsbuildDevMiddlewareOptions & { middlewares: Middlewares; @@ -202,13 +199,10 @@ const applyDefaultMiddlewares = ({ for (const { name } of server.publicDir) { const sirv = requireCompiledPackage('sirv'); - const normalizedPath = isAbsolute(name) ? name : join(pwd, name); - - const servePublicDirMiddleware = sirv(normalizedPath, { + const servePublicDirMiddleware = sirv(name, { etag: true, dev: true, }); - middlewares.push(servePublicDirMiddleware); } diff --git a/packages/core/src/server/devServer.ts b/packages/core/src/server/devServer.ts index b9f3ffa624..874385401a 100644 --- a/packages/core/src/server/devServer.ts +++ b/packages/core/src/server/devServer.ts @@ -476,7 +476,6 @@ export async function createDevServer< }); devMiddlewares = getDevMiddlewares({ - pwd: root, buildManager, config, devServerAPI, From 6691cf3f0a7f83514f03400a2a212cb3abd44eba Mon Sep 17 00:00:00 2001 From: neverland Date: Wed, 15 Oct 2025 13:49:48 +0800 Subject: [PATCH 2/5] fix --- packages/core/src/defaultConfig.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/core/src/defaultConfig.ts b/packages/core/src/defaultConfig.ts index 6e00b94cad..c882e01af7 100644 --- a/packages/core/src/defaultConfig.ts +++ b/packages/core/src/defaultConfig.ts @@ -383,6 +383,12 @@ const normalizePublicDirs = ( } const mergeWithDefault = (options: PublicDirOptions) => { + if (options.name === '') { + throw new Error( + '[rsbuild:config] publicDir.name cannot be empty string.', + ); + } + const merged = { ...defaultConfig, ...options, From ab642744d6f1386ce9630faadf5d54b68fbdb84a Mon Sep 17 00:00:00 2001 From: neverland Date: Wed, 15 Oct 2025 13:50:12 +0800 Subject: [PATCH 3/5] fix --- packages/core/src/defaultConfig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/defaultConfig.ts b/packages/core/src/defaultConfig.ts index c882e01af7..a01a339157 100644 --- a/packages/core/src/defaultConfig.ts +++ b/packages/core/src/defaultConfig.ts @@ -385,7 +385,7 @@ const normalizePublicDirs = ( const mergeWithDefault = (options: PublicDirOptions) => { if (options.name === '') { throw new Error( - '[rsbuild:config] publicDir.name cannot be empty string.', + '[rsbuild:config] `publicDir.name` cannot be empty string.', ); } From 356b89630c80e42f8a42e1f1fb6ad9266639c03c Mon Sep 17 00:00:00 2001 From: neverland Date: Wed, 15 Oct 2025 13:50:56 +0800 Subject: [PATCH 4/5] fix --- e2e/cases/html/favicon/index.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/cases/html/favicon/index.test.ts b/e2e/cases/html/favicon/index.test.ts index 858950e70c..26e265dd24 100644 --- a/e2e/cases/html/favicon/index.test.ts +++ b/e2e/cases/html/favicon/index.test.ts @@ -185,7 +185,7 @@ for (const filename of ['favicon.ico', 'favicon.png', 'favicon.svg']) { const rsbuild = await build({ config: { server: { - publicDir: [{ name: publicPath }], + publicDir: { name: publicPath }, }, }, }); From 11f9f7d2495e25e75c472bf9318774d7c7b1340c Mon Sep 17 00:00:00 2001 From: neverland Date: Wed, 15 Oct 2025 13:57:37 +0800 Subject: [PATCH 5/5] fix --- .../__snapshots__/environments.test.ts.snap | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/core/tests/__snapshots__/environments.test.ts.snap b/packages/core/tests/__snapshots__/environments.test.ts.snap index aefbf12aef..4ed67856a9 100644 --- a/packages/core/tests/__snapshots__/environments.test.ts.snap +++ b/packages/core/tests/__snapshots__/environments.test.ts.snap @@ -127,7 +127,7 @@ exports[`environment config > should normalize environment config correctly 1`] "publicDir": [ { "copyOnBuild": "auto", - "name": "public", + "name": "/public", "watch": false, }, ], @@ -281,7 +281,7 @@ exports[`environment config > should normalize environment config correctly 2`] "publicDir": [ { "copyOnBuild": "auto", - "name": "public", + "name": "/public", "watch": false, }, ], @@ -439,7 +439,7 @@ exports[`environment config > should print environment config when inspect confi "publicDir": [ { "copyOnBuild": "auto", - "name": "public", + "name": "/public", "watch": false, }, ], @@ -593,7 +593,7 @@ exports[`environment config > should print environment config when inspect confi "publicDir": [ { "copyOnBuild": "auto", - "name": "public", + "name": "/public", "watch": false, }, ], @@ -767,7 +767,7 @@ exports[`environment config > should support modify environment config by api.mo "publicDir": [ { "copyOnBuild": "auto", - "name": "public", + "name": "/public", "watch": false, }, ], @@ -922,7 +922,7 @@ exports[`environment config > should support modify environment config by api.mo "publicDir": [ { "copyOnBuild": "auto", - "name": "public", + "name": "/public", "watch": false, }, ], @@ -1077,7 +1077,7 @@ exports[`environment config > should support modify environment config by api.mo "publicDir": [ { "copyOnBuild": "auto", - "name": "public", + "name": "/public", "watch": false, }, ], @@ -1234,7 +1234,7 @@ exports[`environment config > should support modify single environment config by "publicDir": [ { "copyOnBuild": "auto", - "name": "public", + "name": "/public", "watch": false, }, ], @@ -1389,7 +1389,7 @@ exports[`environment config > should support modify single environment config by "publicDir": [ { "copyOnBuild": "auto", - "name": "public", + "name": "/public", "watch": false, }, ],