diff --git a/.changeset/neat-papayas-brake.md b/.changeset/neat-papayas-brake.md new file mode 100644 index 0000000000..5505c7b5fa --- /dev/null +++ b/.changeset/neat-papayas-brake.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +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. diff --git a/packages/astro/src/core/build/index.ts b/packages/astro/src/core/build/index.ts index e176c5216c..ced08fecf8 100644 --- a/packages/astro/src/core/build/index.ts +++ b/packages/astro/src/core/build/index.ts @@ -211,7 +211,7 @@ class AstroBuilder { key: keyPromise, }; - const { internals, ssrOutputChunkNames, contentFileNames } = await viteBuild(opts); + const { internals, ssrOutputChunkNames, ssrOutputAssetNames, contentFileNames } = await viteBuild(opts); const hasServerIslands = this.settings.serverIslandNameMap.size > 0; // Error if there are server islands but no adapter provided. @@ -219,7 +219,7 @@ class AstroBuilder { throw new AstroError(AstroErrorData.NoAdapterInstalledServerIslands); } - await staticBuild(opts, internals, ssrOutputChunkNames, contentFileNames); + await staticBuild(opts, internals, ssrOutputChunkNames, ssrOutputAssetNames, contentFileNames); // Write any additionally generated assets to disk. this.timer.assetsStart = performance.now(); diff --git a/packages/astro/src/core/build/static-build.ts b/packages/astro/src/core/build/static-build.ts index bf1dddf56a..d4f6d368fe 100644 --- a/packages/astro/src/core/build/static-build.ts +++ b/packages/astro/src/core/build/static-build.ts @@ -104,21 +104,26 @@ export async function viteBuild(opts: StaticBuildOptions) { // For static builds, the SSR output won't be needed anymore after page generation. // We keep track of the names here so we only remove these specific files when finished. const ssrOutputChunkNames: string[] = []; + const ssrOutputAssetNames: string[] = []; for (const output of ssrOutputs) { for (const chunk of output.output) { if (chunk.type === 'chunk') { ssrOutputChunkNames.push(chunk.fileName); } + if (chunk.type === 'asset') { + ssrOutputAssetNames.push(chunk.fileName); + } } } - return { internals, ssrOutputChunkNames, contentFileNames }; + return { internals, ssrOutputChunkNames, ssrOutputAssetNames, contentFileNames }; } export async function staticBuild( opts: StaticBuildOptions, internals: BuildInternals, ssrOutputChunkNames: string[], + ssrOutputAssetNames: string[], contentFileNames?: string[], ) { const { settings } = opts; @@ -131,7 +136,7 @@ export async function staticBuild( settings.timer.start('Server generate'); await generatePages(opts, internals); await cleanStaticOutput(opts, internals); - await ssrMoveAssets(opts); + await ssrMoveAssets(opts, ssrOutputAssetNames); settings.timer.end('Server generate'); } } @@ -412,33 +417,26 @@ export async function copyFiles(fromFolder: URL, toFolder: URL, includeDotfiles ); } -async function ssrMoveAssets(opts: StaticBuildOptions) { +async function ssrMoveAssets(opts: StaticBuildOptions, ssrOutputAssetNames: string[]) { opts.logger.info('build', 'Rearranging server assets...'); const serverRoot = opts.settings.buildOutput === 'static' ? opts.settings.config.build.client : opts.settings.config.build.server; const clientRoot = opts.settings.config.build.client; - const assets = opts.settings.config.build.assets; - const serverAssets = new URL(`./${assets}/`, appendForwardSlash(serverRoot.toString())); - const clientAssets = new URL(`./${assets}/`, appendForwardSlash(clientRoot.toString())); - const files = await glob(`**/*`, { - cwd: fileURLToPath(serverAssets), - }); - - if (files.length > 0) { - await Promise.all( - files.map(async function moveAsset(filename) { - const currentUrl = new URL(filename, appendForwardSlash(serverAssets.toString())); - const clientUrl = new URL(filename, appendForwardSlash(clientAssets.toString())); - const dir = new URL(path.parse(clientUrl.href).dir); - // It can't find this file because the user defines a custom path - // that includes the folder paths in `assetFileNames - if (!fs.existsSync(dir)) await fs.promises.mkdir(dir, { recursive: true }); - return fs.promises.rename(currentUrl, clientUrl); - }), - ); - removeEmptyDirs(fileURLToPath(serverAssets)); + if (ssrOutputAssetNames.length > 0) { + await Promise.all( + ssrOutputAssetNames.map(async function moveAsset(filename) { + const currentUrl = new URL(filename, appendForwardSlash(serverRoot.toString())); + const clientUrl = new URL(filename, appendForwardSlash(clientRoot.toString())); + const dir = new URL(path.parse(clientUrl.href).dir); + // It can't find this file because the user defines a custom path + // that includes the folder paths in `assetFileNames` + if (!fs.existsSync(dir)) await fs.promises.mkdir(dir, { recursive: true }); + return fs.promises.rename(currentUrl, clientUrl); + }), + ); + removeEmptyDirs(fileURLToPath(serverRoot)); } } diff --git a/packages/astro/test/custom-assets-name.test.js b/packages/astro/test/custom-assets-name.test.js index 63cd3eb171..67fef15510 100644 --- a/packages/astro/test/custom-assets-name.test.js +++ b/packages/astro/test/custom-assets-name.test.js @@ -2,7 +2,7 @@ import assert from 'node:assert/strict'; import { before, describe, it } from 'node:test'; import { loadFixture } from './test-utils.js'; -describe('custom the assets name function', () => { +describe('custom assets name function', () => { /** @type {import('./test-utils').Fixture} */ let fixture; @@ -14,9 +14,15 @@ describe('custom the assets name function', () => { await fixture.build(); }); - it('It cant find this file cause the node throws an error if the users custom a path that includes the folder path', async () => { - const csslength = await fixture.readFile('client/assets/css/a.css'); - /** @type {Set} */ - assert.equal(!!csslength, true); + it('should load CSS file from custom client assets path', async () => { + const files = await fixture.readdir('/client/assets/css'); + const cssFile = files.find((file) => file === 'a.css'); + assert.ok(cssFile, 'Expected CSS file to exist at client/assets/css/a.css'); + }); + + it('should load image file from custom client assets path', async () => { + const files = await fixture.readdir('/client/imgAssets'); + const imgFile = files.find((file) => file === 'penguin1.jpg'); + assert.ok(imgFile, 'Expected image file to exist at client/imgAssets/penguin1.jpg'); }); }); diff --git a/packages/astro/test/fixtures/custom-assets-name/astro.config.mjs b/packages/astro/test/fixtures/custom-assets-name/astro.config.mjs index cfcddecc5b..866a05044c 100644 --- a/packages/astro/test/fixtures/custom-assets-name/astro.config.mjs +++ b/packages/astro/test/fixtures/custom-assets-name/astro.config.mjs @@ -18,14 +18,14 @@ export default defineConfig({ const { ext, dir, base } = path.parse(option.name); if (ext == ".css") return path.join(dir, "assets/css", 'a.css'); - return "assets/img/[name].[ext]"; + return "imgAssets/[name].[ext]"; } } } } }, build: { - assets: 'assets' + assets: 'assetsDir' }, output: "server", adapter: node({ diff --git a/packages/astro/test/fixtures/custom-assets-name/src/images/penguin1.jpg b/packages/astro/test/fixtures/custom-assets-name/src/images/penguin1.jpg new file mode 100644 index 0000000000..1a8986ac50 Binary files /dev/null and b/packages/astro/test/fixtures/custom-assets-name/src/images/penguin1.jpg differ diff --git a/packages/astro/test/fixtures/custom-assets-name/src/pages/index.astro b/packages/astro/test/fixtures/custom-assets-name/src/pages/index.astro index 250233e1e1..507d236d99 100644 --- a/packages/astro/test/fixtures/custom-assets-name/src/pages/index.astro +++ b/packages/astro/test/fixtures/custom-assets-name/src/pages/index.astro @@ -1,5 +1,6 @@ --- const title = 'My App'; +import p1Url from '../images/penguin1.jpg'; --- @@ -8,6 +9,7 @@ const title = 'My App';

{title}

+