Skip to content

Commit e6b8017

Browse files
authored
fix custom assetFileNames issue (#12449)
* fix custom assetFileNames issue * fix error * fix asset name * handle edge cases fo multiple asset dirs * add tests * format * add changeset * improve changeset * add missing files for tests * Update neat-papayas-brake.md improve changeset
1 parent 350b3da commit e6b8017

File tree

7 files changed

+43
-32
lines changed

7 files changed

+43
-32
lines changed

.changeset/neat-papayas-brake.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'astro': patch
3+
---
4+
5+
Fixes an issue where the custom `assetFileNames` configuration caused assets to be incorrectly moved to the server directory instead of the client directory, resulting in 404 errors when accessed from the client side.

packages/astro/src/core/build/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -211,15 +211,15 @@ class AstroBuilder {
211211
key: keyPromise,
212212
};
213213

214-
const { internals, ssrOutputChunkNames, contentFileNames } = await viteBuild(opts);
214+
const { internals, ssrOutputChunkNames, ssrOutputAssetNames, contentFileNames } = await viteBuild(opts);
215215

216216
const hasServerIslands = this.settings.serverIslandNameMap.size > 0;
217217
// Error if there are server islands but no adapter provided.
218218
if (hasServerIslands && this.settings.buildOutput !== 'server') {
219219
throw new AstroError(AstroErrorData.NoAdapterInstalledServerIslands);
220220
}
221221

222-
await staticBuild(opts, internals, ssrOutputChunkNames, contentFileNames);
222+
await staticBuild(opts, internals, ssrOutputChunkNames, ssrOutputAssetNames, contentFileNames);
223223

224224
// Write any additionally generated assets to disk.
225225
this.timer.assetsStart = performance.now();

packages/astro/src/core/build/static-build.ts

+21-23
Original file line numberDiff line numberDiff line change
@@ -104,21 +104,26 @@ export async function viteBuild(opts: StaticBuildOptions) {
104104
// For static builds, the SSR output won't be needed anymore after page generation.
105105
// We keep track of the names here so we only remove these specific files when finished.
106106
const ssrOutputChunkNames: string[] = [];
107+
const ssrOutputAssetNames: string[] = [];
107108
for (const output of ssrOutputs) {
108109
for (const chunk of output.output) {
109110
if (chunk.type === 'chunk') {
110111
ssrOutputChunkNames.push(chunk.fileName);
111112
}
113+
if (chunk.type === 'asset') {
114+
ssrOutputAssetNames.push(chunk.fileName);
115+
}
112116
}
113117
}
114118

115-
return { internals, ssrOutputChunkNames, contentFileNames };
119+
return { internals, ssrOutputChunkNames, ssrOutputAssetNames, contentFileNames };
116120
}
117121

118122
export async function staticBuild(
119123
opts: StaticBuildOptions,
120124
internals: BuildInternals,
121125
ssrOutputChunkNames: string[],
126+
ssrOutputAssetNames: string[],
122127
contentFileNames?: string[],
123128
) {
124129
const { settings } = opts;
@@ -131,7 +136,7 @@ export async function staticBuild(
131136
settings.timer.start('Server generate');
132137
await generatePages(opts, internals);
133138
await cleanStaticOutput(opts, internals);
134-
await ssrMoveAssets(opts);
139+
await ssrMoveAssets(opts, ssrOutputAssetNames);
135140
settings.timer.end('Server generate');
136141
}
137142
}
@@ -412,33 +417,26 @@ export async function copyFiles(fromFolder: URL, toFolder: URL, includeDotfiles
412417
);
413418
}
414419

415-
async function ssrMoveAssets(opts: StaticBuildOptions) {
420+
async function ssrMoveAssets(opts: StaticBuildOptions, ssrOutputAssetNames: string[]) {
416421
opts.logger.info('build', 'Rearranging server assets...');
417422
const serverRoot =
418423
opts.settings.buildOutput === 'static'
419424
? opts.settings.config.build.client
420425
: opts.settings.config.build.server;
421426
const clientRoot = opts.settings.config.build.client;
422-
const assets = opts.settings.config.build.assets;
423-
const serverAssets = new URL(`./${assets}/`, appendForwardSlash(serverRoot.toString()));
424-
const clientAssets = new URL(`./${assets}/`, appendForwardSlash(clientRoot.toString()));
425-
const files = await glob(`**/*`, {
426-
cwd: fileURLToPath(serverAssets),
427-
});
428-
429-
if (files.length > 0) {
430-
await Promise.all(
431-
files.map(async function moveAsset(filename) {
432-
const currentUrl = new URL(filename, appendForwardSlash(serverAssets.toString()));
433-
const clientUrl = new URL(filename, appendForwardSlash(clientAssets.toString()));
434-
const dir = new URL(path.parse(clientUrl.href).dir);
435-
// It can't find this file because the user defines a custom path
436-
// that includes the folder paths in `assetFileNames
437-
if (!fs.existsSync(dir)) await fs.promises.mkdir(dir, { recursive: true });
438-
return fs.promises.rename(currentUrl, clientUrl);
439-
}),
440-
);
441-
removeEmptyDirs(fileURLToPath(serverAssets));
427+
if (ssrOutputAssetNames.length > 0) {
428+
await Promise.all(
429+
ssrOutputAssetNames.map(async function moveAsset(filename) {
430+
const currentUrl = new URL(filename, appendForwardSlash(serverRoot.toString()));
431+
const clientUrl = new URL(filename, appendForwardSlash(clientRoot.toString()));
432+
const dir = new URL(path.parse(clientUrl.href).dir);
433+
// It can't find this file because the user defines a custom path
434+
// that includes the folder paths in `assetFileNames`
435+
if (!fs.existsSync(dir)) await fs.promises.mkdir(dir, { recursive: true });
436+
return fs.promises.rename(currentUrl, clientUrl);
437+
}),
438+
);
439+
removeEmptyDirs(fileURLToPath(serverRoot));
442440
}
443441
}
444442

packages/astro/test/custom-assets-name.test.js

+11-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import assert from 'node:assert/strict';
22
import { before, describe, it } from 'node:test';
33
import { loadFixture } from './test-utils.js';
44

5-
describe('custom the assets name function', () => {
5+
describe('custom assets name function', () => {
66
/** @type {import('./test-utils').Fixture} */
77
let fixture;
88

@@ -14,9 +14,15 @@ describe('custom the assets name function', () => {
1414
await fixture.build();
1515
});
1616

17-
it('It cant find this file cause the node throws an error if the users custom a path that includes the folder path', async () => {
18-
const csslength = await fixture.readFile('client/assets/css/a.css');
19-
/** @type {Set<string>} */
20-
assert.equal(!!csslength, true);
17+
it('should load CSS file from custom client assets path', async () => {
18+
const files = await fixture.readdir('/client/assets/css');
19+
const cssFile = files.find((file) => file === 'a.css');
20+
assert.ok(cssFile, 'Expected CSS file to exist at client/assets/css/a.css');
21+
});
22+
23+
it('should load image file from custom client assets path', async () => {
24+
const files = await fixture.readdir('/client/imgAssets');
25+
const imgFile = files.find((file) => file === 'penguin1.jpg');
26+
assert.ok(imgFile, 'Expected image file to exist at client/imgAssets/penguin1.jpg');
2127
});
2228
});

packages/astro/test/fixtures/custom-assets-name/astro.config.mjs

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ export default defineConfig({
1818
const { ext, dir, base } = path.parse(option.name);
1919

2020
if (ext == ".css") return path.join(dir, "assets/css", 'a.css');
21-
return "assets/img/[name].[ext]";
21+
return "imgAssets/[name].[ext]";
2222
}
2323
}
2424
}
2525
}
2626
},
2727
build: {
28-
assets: 'assets'
28+
assets: 'assetsDir'
2929
},
3030
output: "server",
3131
adapter: node({
Loading

packages/astro/test/fixtures/custom-assets-name/src/pages/index.astro

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
const title = 'My App';
3+
import p1Url from '../images/penguin1.jpg';
34
---
45

56
<html>
@@ -8,6 +9,7 @@ const title = 'My App';
89
</head>
910
<body>
1011
<h1>{title}</h1>
12+
<img src={p1Url.src}/>
1113
</body>
1214
</html>
1315

0 commit comments

Comments
 (0)