From 03003890b23860e95b7e7c9edbfac8552db86e8f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Oct 2025 15:04:23 +0000 Subject: [PATCH 1/2] Initial plan From 3a6c743fa9e168fb4abc00deba8b8d403429f06d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Oct 2025 15:26:04 +0000 Subject: [PATCH 2/2] fix!: only serve dev static assets from publish dir as fallback When using `@netlify/vite-plugin`, `vite dev` (or e.g. `astro dev`) was incorrectly serving files from the Netlify publish dir (e.g. `dist/`) instead of the source directory during vite dev. I can't think of any reason to ever augment the directories passed to `@netlify/dev`'s options with the default directory. In every known use case (`@netlify/vite-plugin`, `@netlify/nuxt`...), we pass a known set of directories that should be used to serve static assets in dev. It seems reasonable to use some reasonable defaults here for future use cases. This commit makes this change. Co-authored-by: serhalp <1377702+serhalp@users.noreply.github.com> --- packages/dev/src/main.test.ts | 90 +++++++++++++++++++++++++++++++++++ packages/dev/src/main.ts | 17 +++---- 2 files changed, 99 insertions(+), 8 deletions(-) diff --git a/packages/dev/src/main.test.ts b/packages/dev/src/main.test.ts index 2d0129d5..5fd13a89 100644 --- a/packages/dev/src/main.test.ts +++ b/packages/dev/src/main.test.ts @@ -1062,4 +1062,94 @@ describe('Handling requests', () => { await fixture.destroy() }) }) + + describe('Static file serving', () => { + test('Serves static files from given `staticFiles.directories` only', async () => { + const fixture = new Fixture() + .withFile( + 'netlify.toml', + `[build] + publish = "dist" + `, + ) + .withFile('dist/index.html', `from dist`) + .withFile('custom-static/app.css', `from custom-static`) + .withFile('another-static/script.js', `from another-static`) + const directory = await fixture.create() + + const dev = new NetlifyDev({ + projectRoot: directory, + edgeFunctions: {}, + geolocation: { + enabled: false, + }, + staticFiles: { + directories: [`${directory}/custom-static`, `${directory}/another-static`], + }, + }) + await dev.start() + + const customStaticRes = await dev.handle(new Request('https://site.netlify/app.css')) + expect(await customStaticRes?.text()).toBe('from custom-static') + const anotherStaticRes = await dev.handle(new Request('https://site.netlify/script.js')) + expect(await anotherStaticRes?.text()).toBe('from another-static') + const publishDirRes = await dev.handle(new Request('https://site.netlify/index.html')) + expect(publishDirRes).toBeUndefined() + + await dev.stop() + await fixture.destroy() + }) + + test('Falls back to publish directory when no custom directories provided', async () => { + const fixture = new Fixture() + .withFile( + 'netlify.toml', + `[build] + publish = "public" + `, + ) + .withFile('public/index.html', `from public`) + .withFile('public/style.css', `from public css`) + const directory = await fixture.create() + + const dev = new NetlifyDev({ + projectRoot: directory, + edgeFunctions: {}, + geolocation: { + enabled: false, + }, + }) + await dev.start() + + const publishDirRes1 = await dev.handle(new Request('https://site.netlify/index.html')) + expect(await publishDirRes1?.text()).toBe('from public') + const publishDirRes2 = await dev.handle(new Request('https://site.netlify/style.css')) + expect(await publishDirRes2?.text()).toBe('from public css') + + await dev.stop() + await fixture.destroy() + }) + + test('Uses project root when no publish directory configured and no custom directories provided', async () => { + const fixture = new Fixture().withFile('index.html', `from root`).withFile('app.js', `from root js`) + const directory = await fixture.create() + + const dev = new NetlifyDev({ + projectRoot: directory, + edgeFunctions: {}, + geolocation: { + enabled: false, + }, + }) + await dev.start() + + const projectRootRes1 = await dev.handle(new Request('https://site.netlify/index.html')) + expect(await projectRootRes1?.text()).toBe('from root') + const projectRootRes2 = await dev.handle(new Request('https://site.netlify/app.js')) + expect(await projectRootRes2?.text()).toBe('from root js') + + await dev.stop() + await fixture.destroy() + }) + }) }) diff --git a/packages/dev/src/main.ts b/packages/dev/src/main.ts index f85a8504..f86675da 100644 --- a/packages/dev/src/main.ts +++ b/packages/dev/src/main.ts @@ -123,8 +123,8 @@ export interface Features { enabled?: boolean /** - * Additional list of directories where static files can be found. The - * `publish` directory configured on your site will be used automatically. + * List of directories where static files can be found. If not provided, + * the `publish` directory configured on your Netlify project will be used automatically. */ directories?: string[] } @@ -196,7 +196,7 @@ export class NetlifyDev { #serverAddress?: string | null #siteID?: string #staticHandler?: StaticHandler - #staticHandlerAdditionalDirectories: string[] + #staticHandlerDirectories?: string[] constructor(options: NetlifyDevOptions) { if (options.apiURL) { @@ -227,7 +227,7 @@ export class NetlifyDev { this.#logger = options.logger ?? globalThis.console this.#serverAddress = options.serverAddress this.#projectRoot = projectRoot - this.#staticHandlerAdditionalDirectories = options.staticFiles?.directories ?? [] + this.#staticHandlerDirectories = options.staticFiles?.directories ?? undefined } private getServerAddress(requestServerAddress?: string) { @@ -582,11 +582,12 @@ export class NetlifyDev { } if (this.#features.static) { + // If custom static directories are provided (e.g., by `@netlify/vite-plugin` or `@netlify/nuxt`), + // use those directories. Otherwise, use the build.publish directory from config. + const directories = this.#staticHandlerDirectories ?? [this.#config?.config.build.publish ?? this.#projectRoot] + this.#staticHandler = new StaticHandler({ - directory: [ - this.#config?.config.build.publish ?? this.#projectRoot, - ...this.#staticHandlerAdditionalDirectories, - ], + directory: directories, }) }