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,
},
],