diff --git a/.changeset/ninety-snails-study.md b/.changeset/ninety-snails-study.md new file mode 100644 index 000000000000..84bf956b76d6 --- /dev/null +++ b/.changeset/ninety-snails-study.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Support custom 404s added via `injectRoute` or as `src/pages/404.html` diff --git a/packages/astro/src/vite-plugin-astro-server/route.ts b/packages/astro/src/vite-plugin-astro-server/route.ts index 79232f10fc69..da280f7e1b4c 100644 --- a/packages/astro/src/vite-plugin-astro-server/route.ts +++ b/packages/astro/src/vite-plugin-astro-server/route.ts @@ -1,6 +1,6 @@ import type http from 'http'; import mime from 'mime'; -import type { AstroSettings, ComponentInstance, ManifestData, RouteData } from '../@types/astro'; +import type { ComponentInstance, ManifestData, RouteData } from '../@types/astro'; import type { ComponentPreload, DevelopmentEnvironment, @@ -12,12 +12,10 @@ import { call as callEndpoint } from '../core/endpoint/dev/index.js'; import { throwIfRedirectNotAllowed } from '../core/endpoint/index.js'; import { AstroErrorData } from '../core/errors/index.js'; import { warn } from '../core/logger/core.js'; -import { appendForwardSlash } from '../core/path.js'; import { preload, renderPage } from '../core/render/dev/index.js'; import { getParamsAndProps, GetParamsAndPropsError } from '../core/render/index.js'; import { createRequest } from '../core/request.js'; import { matchAllRoutes } from '../core/routing/index.js'; -import { resolvePages } from '../core/util.js'; import { log404 } from './common.js'; import { handle404Response, writeSSRResult, writeWebResponse } from './response.js'; @@ -35,11 +33,9 @@ interface MatchedRoute { mod: ComponentInstance; } -function getCustom404Route({ config }: AstroSettings, manifest: ManifestData) { - // For Windows compat, use relative page paths to match the 404 route - const relPages = resolvePages(config).href.replace(config.root.href, ''); - const pattern = new RegExp(`${appendForwardSlash(relPages)}404.(astro|md)`); - return manifest.routes.find((r) => r.component.match(pattern)); +function getCustom404Route(manifest: ManifestData): RouteData | undefined { + const route404 = /^\/404\/?$/; + return manifest.routes.find((r) => route404.test(r.route)); } export async function matchRoute( @@ -97,7 +93,7 @@ export async function matchRoute( } log404(logging, pathname); - const custom404 = getCustom404Route(settings, manifest); + const custom404 = getCustom404Route(manifest); if (custom404) { const filePath = new URL(`./${custom404.component}`, settings.config.root); diff --git a/packages/astro/test/custom-404-html.test.js b/packages/astro/test/custom-404-html.test.js new file mode 100644 index 000000000000..6c3ac6dec863 --- /dev/null +++ b/packages/astro/test/custom-404-html.test.js @@ -0,0 +1,42 @@ +import { expect } from 'chai'; +import * as cheerio from 'cheerio'; +import { loadFixture } from './test-utils.js'; + +describe('Custom 404.html', () => { + let fixture; + + before(async () => { + fixture = await loadFixture({ + root: './fixtures/custom-404-html/', + site: 'http://example.com', + }); + }); + + describe('dev', () => { + let devServer; + let $; + + before(async () => { + devServer = await fixture.startDevServer(); + }); + + after(async () => { + await devServer.stop(); + }); + + it('renders /', async () => { + const html = await fixture.fetch('/').then((res) => res.text()); + $ = cheerio.load(html); + + expect($('h1').text()).to.equal('Home'); + }); + + it('renders 404 for /a', async () => { + const html = await fixture.fetch('/a').then((res) => res.text()); + $ = cheerio.load(html); + + expect($('h1').text()).to.equal('Page not found'); + expect($('p').text()).to.equal('This 404 is a static HTML file.'); + }); + }); +}); diff --git a/packages/astro/test/custom-404-injected.test.js b/packages/astro/test/custom-404-injected.test.js new file mode 100644 index 000000000000..c8963243a616 --- /dev/null +++ b/packages/astro/test/custom-404-injected.test.js @@ -0,0 +1,42 @@ +import { expect } from 'chai'; +import * as cheerio from 'cheerio'; +import { loadFixture } from './test-utils.js'; + +describe('Custom 404 with injectRoute', () => { + let fixture; + + before(async () => { + fixture = await loadFixture({ + root: './fixtures/custom-404-injected/', + site: 'http://example.com', + }); + }); + + describe('dev', () => { + let devServer; + let $; + + before(async () => { + devServer = await fixture.startDevServer(); + }); + + after(async () => { + await devServer.stop(); + }); + + it('renders /', async () => { + const html = await fixture.fetch('/').then((res) => res.text()); + $ = cheerio.load(html); + + expect($('h1').text()).to.equal('Home'); + }); + + it('renders 404 for /a', async () => { + const html = await fixture.fetch('/a').then((res) => res.text()); + $ = cheerio.load(html); + + expect($('h1').text()).to.equal('Page not found'); + expect($('p').text()).to.equal('/a'); + }); + }); +}); diff --git a/packages/astro/test/fixtures/custom-404-html/astro.config.mjs b/packages/astro/test/fixtures/custom-404-html/astro.config.mjs new file mode 100644 index 000000000000..882e6515a67e --- /dev/null +++ b/packages/astro/test/fixtures/custom-404-html/astro.config.mjs @@ -0,0 +1,4 @@ +import { defineConfig } from 'astro/config'; + +// https://astro.build/config +export default defineConfig({}); diff --git a/packages/astro/test/fixtures/custom-404-html/package.json b/packages/astro/test/fixtures/custom-404-html/package.json new file mode 100644 index 000000000000..b137ab07695b --- /dev/null +++ b/packages/astro/test/fixtures/custom-404-html/package.json @@ -0,0 +1,8 @@ +{ + "name": "@test/custom-404-html", + "version": "0.0.0", + "private": true, + "dependencies": { + "astro": "workspace:*" + } +} diff --git a/packages/astro/test/fixtures/custom-404-html/src/pages/404.html b/packages/astro/test/fixtures/custom-404-html/src/pages/404.html new file mode 100644 index 000000000000..50051bbc0b4c --- /dev/null +++ b/packages/astro/test/fixtures/custom-404-html/src/pages/404.html @@ -0,0 +1,9 @@ + +
+This 404 is a static HTML file.
+ + diff --git a/packages/astro/test/fixtures/custom-404-html/src/pages/index.astro b/packages/astro/test/fixtures/custom-404-html/src/pages/index.astro new file mode 100644 index 000000000000..cf5ef9b586c6 --- /dev/null +++ b/packages/astro/test/fixtures/custom-404-html/src/pages/index.astro @@ -0,0 +1,11 @@ +--- +--- + + + +{canonicalURL.pathname}
+ + diff --git a/packages/astro/test/fixtures/custom-404-injected/src/pages/index.astro b/packages/astro/test/fixtures/custom-404-injected/src/pages/index.astro new file mode 100644 index 000000000000..cf5ef9b586c6 --- /dev/null +++ b/packages/astro/test/fixtures/custom-404-injected/src/pages/index.astro @@ -0,0 +1,11 @@ +--- +--- + + + +