Skip to content

Commit 6fbe0dd

Browse files
authored
fix(vite): basePath handling for static assets (freshframework#3394)
closes freshframework#3391 --- Generated PR message: Fix base path handling for assets in plugin-vite Fixes issue where Fresh apps mounted at non-root paths (e.g., /ui) in frameworks like Hono would fail to load assets correctly. Problem When mounting a Fresh app with app.mount('/ui', UI.fetch), the HTML would load but all assets (CSS, JS, images) would return 404 because asset paths were hardcoded to start with /assets/ instead of respecting the Vite base configuration. Solution - Modified packages/plugin-vite/src/plugins/server_entry.ts to read Vite's base config and apply it to asset paths - Added getAssetPath() helper function to construct proper asset URLs with base path - Updated both CSS and asset file registration to use the base path Testing Added comprehensive test case "vite build - base path asset handling" that: - Builds demo with custom base path /my-app/ - Verifies generated server.js contains correctly prefixed asset paths - Follows existing test patterns and utilities Usage Users can now properly mount Fresh apps by configuring both: ```ts // vite.config.ts export default defineConfig({ base: "/ui/", plugins: [fresh()], }); // main.ts const app = new App({ basePath: "/ui" }); // hono-app.ts app.mount('/ui', UI.fetch); // Assets now work correctly ``` Backward compatible - no changes needed for apps mounted at root path.
1 parent 554644b commit 6fbe0dd

3 files changed

Lines changed: 38 additions & 3 deletions

File tree

packages/plugin-vite/src/plugins/server_entry.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,19 @@ export function serverEntryPlugin(
1616
let serverOutDir = "";
1717
let clientOutDir = "";
1818
let root = "";
19+
let basePath = "";
1920

2021
let isDev = false;
2122

23+
const getAssetPath = (id: string): string => {
24+
if (basePath === "/") {
25+
return `/${id}`;
26+
}
27+
// Ensure basePath ends with / and construct the path manually to avoid platform-specific path issues
28+
const normalizedBase = basePath.endsWith("/") ? basePath : basePath + "/";
29+
return normalizedBase + id;
30+
};
31+
2232
return {
2333
name: "fresh:server_entry",
2434
applyToEnvironment(env) {
@@ -29,6 +39,10 @@ export function serverEntryPlugin(
2939
},
3040
configResolved(config) {
3141
root = config.root;
42+
basePath = config.base || "/";
43+
if (basePath !== "/" && !basePath.endsWith("/")) {
44+
basePath += "/";
45+
}
3246
serverEntry = pathWithRoot(options.serverEntry, config.root);
3347
serverOutDir = pathWithRoot(
3448
config.environments.ssr.build.outDir,
@@ -98,7 +112,7 @@ if (import.meta.hot) import.meta.hot.accept();`;
98112
staticFiles.push({
99113
filePath: path.join(serverOutDir, id),
100114
hash: null,
101-
pathname: `/${id}`,
115+
pathname: getAssetPath(id),
102116
});
103117
}
104118
}
@@ -110,7 +124,7 @@ if (import.meta.hot) import.meta.hot.accept();`;
110124
staticFiles.push({
111125
filePath: path.join(serverOutDir, id),
112126
hash: null,
113-
pathname: `/${id}`,
127+
pathname: getAssetPath(id),
114128
});
115129
}
116130
}

packages/plugin-vite/tests/build_test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,3 +447,20 @@ Deno.test({
447447
sanitizeOps: false,
448448
sanitizeResources: false,
449449
});
450+
451+
Deno.test({
452+
name: "vite build - base path asset handling",
453+
fn: async () => {
454+
await using res = await buildVite(DEMO_DIR, { base: "/my-app/" });
455+
456+
// Read the generated server.js to check asset paths
457+
const serverJs = await Deno.readTextFile(
458+
path.join(res.tmp, "_fresh", "server.js"),
459+
);
460+
461+
// Asset paths should include the base path /my-app/
462+
expect(serverJs).toContain('"/my-app/assets/');
463+
},
464+
sanitizeOps: false,
465+
sanitizeResources: false,
466+
});

packages/plugin-vite/tests/test_utils.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,10 @@ export async function withDevServer(
131131
await launchDevServer(tmp.dir, fn, env);
132132
}
133133

134-
export async function buildVite(fixtureDir: string) {
134+
export async function buildVite(
135+
fixtureDir: string,
136+
options?: { base?: string },
137+
) {
135138
const tmp = await withTmpDir({
136139
dir: path.join(import.meta.dirname!, ".."),
137140
prefix: "tmp_vite_",
@@ -140,6 +143,7 @@ export async function buildVite(fixtureDir: string) {
140143
const builder = await createBuilder({
141144
logLevel: "error",
142145
root: fixtureDir,
146+
base: options?.base,
143147
build: {
144148
emptyOutDir: true,
145149
},

0 commit comments

Comments
 (0)