diff --git a/.changeset/plenty-eyes-develop.md b/.changeset/plenty-eyes-develop.md new file mode 100644 index 000000000..74073721b --- /dev/null +++ b/.changeset/plenty-eyes-develop.md @@ -0,0 +1,7 @@ +--- +'astro': patch +--- + +404 when not using subpath for items in public in dev + +Previously if using a base like `base: '/subpath/` you could load things from the root, which would break in prod. Now you must include the subpath. diff --git a/packages/astro/src/vite-plugin-astro-server/base.ts b/packages/astro/src/vite-plugin-astro-server/base.ts index 2618749db..dcab634b7 100644 --- a/packages/astro/src/vite-plugin-astro-server/base.ts +++ b/packages/astro/src/vite-plugin-astro-server/base.ts @@ -5,6 +5,7 @@ import { LogOptions } from '../core/logger/core.js'; import notFoundTemplate, { subpathNotUsedTemplate } from '../template/4xx.js'; import { log404 } from './common.js'; import { writeHtmlResponse } from './response.js'; +import * as fs from 'fs'; export function baseMiddleware( settings: AstroSettings, @@ -12,12 +13,13 @@ export function baseMiddleware( ): vite.Connect.NextHandleFunction { const { config } = settings; const site = config.site ? new URL(config.base, config.site) : undefined; - const devRoot = site ? site.pathname : '/'; + const devRoot = site ? site.pathname : new URL(config.base, 'http://localhost').pathname; return function devBaseMiddleware(req, res, next) { const url = req.url!; - const pathname = decodeURI(new URL(url, 'http://vitejs.dev').pathname); + const pathname = decodeURI(new URL(url, 'http://localhost').pathname); + if (pathname.startsWith(devRoot)) { req.url = url.replace(devRoot, '/'); @@ -41,6 +43,16 @@ export function baseMiddleware( return writeHtmlResponse(res, 404, html); } - next(); + // Check to see if it's in public and if so 404 + const publicPath = new URL('.' + req.url, config.publicDir); + fs.stat(publicPath, (_err, stats) => { + if(stats) { + log404(logging, pathname); + const html = subpathNotUsedTemplate(devRoot, pathname); + return writeHtmlResponse(res, 404, html); + } else { + next(); + } + }); }; } diff --git a/packages/astro/test/fixtures/alias/public/test.txt b/packages/astro/test/fixtures/alias/public/test.txt new file mode 100644 index 000000000..90bfcb510 --- /dev/null +++ b/packages/astro/test/fixtures/alias/public/test.txt @@ -0,0 +1 @@ +this is a test diff --git a/packages/astro/test/units/dev/dev.test.js b/packages/astro/test/units/dev/dev.test.js index 88fbaac45..062fcba23 100644 --- a/packages/astro/test/units/dev/dev.test.js +++ b/packages/astro/test/units/dev/dev.test.js @@ -157,4 +157,50 @@ describe('dev container', () => { } ); }); + + it('items in public/ are not available from root when using a base', async () => { + await runInContainer({ + root, + userConfig: { + base: '/sub/' + } + }, async (container) => { + // First try the subpath + let r = createRequestAndResponse({ + method: 'GET', + url: '/sub/test.txt', + }); + + container.handle(r.req, r.res); + await r.done; + + expect(r.res.statusCode).to.equal(200); + + // Next try the root path + r = createRequestAndResponse({ + method: 'GET', + url: '/test.txt', + }); + + container.handle(r.req, r.res); + await r.done; + + expect(r.res.statusCode).to.equal(404); + }); + }); + + it('items in public/ are available from root when not using a base', async () => { + await runInContainer({ root }, async (container) => { + // Try the root path + let r = createRequestAndResponse({ + method: 'GET', + url: '/test.txt', + }); + + container.handle(r.req, r.res); + await r.done; + + expect(r.res.statusCode).to.equal(200); + }); + }); });