11import fs from 'node:fs' ;
22import os from 'node:os' ;
3+ import { fileURLToPath } from 'node:url' ;
34import PLimit from 'p-limit' ;
45import PQueue from 'p-queue' ;
56import colors from 'piccolore' ;
@@ -35,6 +36,8 @@ import type { BuildApp } from './app.js';
3536import { getOutFile , getOutFolder } from './common.js' ;
3637import { type BuildInternals , hasPrerenderedPages } from './internal.js' ;
3738import type { StaticBuildOptions } from './types.js' ;
39+ import type { AstroSettings } from '../../types/astro.js' ;
40+ import type { Logger } from '../logger/core.js' ;
3841import { getTimeStat , shouldAppendForwardSlash } from './util.js' ;
3942
4043export async function generatePages (
@@ -617,6 +620,9 @@ async function generatePath(
617620 routeToHeaders . set ( pathname , { headers : responseHeaders , route : integrationRoute } ) ;
618621 }
619622
623+ // Public files take priority over generated routes
624+ if ( checkPublicConflict ( outFile , route , settings , logger ) ) return false ;
625+
620626 await fs . promises . mkdir ( outFolder , { recursive : true } ) ;
621627 await fs . promises . writeFile ( outFile , body ) ;
622628
@@ -634,3 +640,29 @@ function getPrettyRouteName(route: RouteData): string {
634640 }
635641 return route . component ;
636642}
643+
644+ /**
645+ * Check if a file exists in the public directory that would conflict with the output file.
646+ * Public files take priority over generated routes. Returns true if there's a conflict.
647+ */
648+ function checkPublicConflict (
649+ outFile : URL ,
650+ route : RouteData ,
651+ settings : AstroSettings ,
652+ logger : Logger ,
653+ ) : boolean {
654+ const outFilePath = fileURLToPath ( outFile ) ;
655+ const outRoot = fileURLToPath (
656+ settings . buildOutput === 'static' ? settings . config . outDir : settings . config . build . client ,
657+ ) ;
658+ const relativePath = outFilePath . slice ( outRoot . length ) ;
659+ const publicFilePath = new URL ( relativePath , settings . config . publicDir ) ;
660+ if ( fs . existsSync ( publicFilePath ) ) {
661+ logger . warn (
662+ 'build' ,
663+ `Skipping ${ route . component } because a file with the same name exists in the public folder: ${ relativePath } ` ,
664+ ) ;
665+ return true ;
666+ }
667+ return false ;
668+ }
0 commit comments