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 }, }, }, }); diff --git a/packages/core/src/defaultConfig.ts b/packages/core/src/defaultConfig.ts index e147c68be2..a01a339157 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,26 @@ const normalizePublicDirs = ( return [defaultConfig]; } - if (Array.isArray(publicDir)) { - return publicDir.map((options) => ({ + const mergeWithDefault = (options: PublicDirOptions) => { + if (options.name === '') { + throw new Error( + '[rsbuild:config] `publicDir.name` cannot be empty string.', + ); + } + + 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, 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, }, ],