From d4abb8b22d3f15888cd897186d43ebd3cd005c4a Mon Sep 17 00:00:00 2001 From: Chris Swithinbank Date: Sat, 29 Apr 2023 00:20:07 +0200 Subject: [PATCH 01/10] Add unit test for injected 404 routes --- packages/astro/test/units/dev/dev.test.js | 62 +++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/packages/astro/test/units/dev/dev.test.js b/packages/astro/test/units/dev/dev.test.js index eddc14c5dd5b..5c19af635a25 100644 --- a/packages/astro/test/units/dev/dev.test.js +++ b/packages/astro/test/units/dev/dev.test.js @@ -158,6 +158,68 @@ describe('dev container', () => { ); }); + it('Serves injected 404 route for any 404', async () => { + const fs = createFs( + { + '/src/components/404.astro': `

Custom 404

`, + '/src/pages/page.astro': `

Regular page

`, + }, + root + ); + + await runInContainer( + { + fs, + root, + userConfig: { + output: 'server', + integrations: [ + { + name: '@astrojs/test-integration', + hooks: { + 'astro:config:setup': ({ injectRoute }) => { + injectRoute({ + pattern: '/404', + entryPoint: './src/components/404.astro', + }); + }, + }, + }, + ], + }, + }, + async (container) => { + { + // Regular pages are served as expected. + const r = createRequestAndResponse({ method: 'GET', url: '/page' }); + container.handle(r.req, r.res); + await r.done; + const doc = await r.text(); + expect(doc).to.match(/

Regular page<\/h1>/); + expect(r.res.statusCode).to.equal(200); + } + { + // `/404` serves the custom 404 page as expected. + const r = createRequestAndResponse({ method: 'GET', url: '/404' }); + container.handle(r.req, r.res); + await r.done; + const doc = await r.text(); + expect(doc).to.match(/

Custom 404<\/h1>/); + expect(r.res.statusCode).to.equal(200); + } + { + // A non-existent page also serves the custom 404 page. + const r = createRequestAndResponse({ method: 'GET', url: '/other-page' }); + container.handle(r.req, r.res); + await r.done; + const doc = await r.text(); + expect(doc).to.match(/

Custom 404<\/h1>/); + expect(r.res.statusCode).to.equal(200); + } + } + ); + }); + it('items in public/ are not available from root when using a base', async () => { await runInContainer( { From e81a8dd0a0cc1f8a297dadf17fc214b15bc5ac45 Mon Sep 17 00:00:00 2001 From: Chris Swithinbank Date: Sat, 29 Apr 2023 00:21:37 +0200 Subject: [PATCH 02/10] Add fixture test for injected 404 routes --- .../astro/test/custom-404-injected.test.js | 42 +++++++++++++++++++ .../custom-404-injected/astro.config.mjs | 18 ++++++++ .../fixtures/custom-404-injected/package.json | 8 ++++ .../custom-404-injected/src/404.astro | 13 ++++++ .../custom-404-injected/src/pages/index.astro | 11 +++++ 5 files changed, 92 insertions(+) create mode 100644 packages/astro/test/custom-404-injected.test.js create mode 100644 packages/astro/test/fixtures/custom-404-injected/astro.config.mjs create mode 100644 packages/astro/test/fixtures/custom-404-injected/package.json create mode 100644 packages/astro/test/fixtures/custom-404-injected/src/404.astro create mode 100644 packages/astro/test/fixtures/custom-404-injected/src/pages/index.astro 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-injected/astro.config.mjs b/packages/astro/test/fixtures/custom-404-injected/astro.config.mjs new file mode 100644 index 000000000000..af687172055f --- /dev/null +++ b/packages/astro/test/fixtures/custom-404-injected/astro.config.mjs @@ -0,0 +1,18 @@ +import { defineConfig } from 'astro/config'; + +// https://astro.build/config +export default defineConfig({ + integrations: [ + { + name: '404-integration', + hooks: { + 'astro:config:setup': ({ injectRoute }) => { + injectRoute({ + pattern: '404', + entryPoint: '/src/404.astro', + }); + }, + }, + }, + ], +}); diff --git a/packages/astro/test/fixtures/custom-404-injected/package.json b/packages/astro/test/fixtures/custom-404-injected/package.json new file mode 100644 index 000000000000..855088a9f754 --- /dev/null +++ b/packages/astro/test/fixtures/custom-404-injected/package.json @@ -0,0 +1,8 @@ +{ + "name": "@test/custom-404-injected", + "version": "0.0.0", + "private": true, + "dependencies": { + "astro": "workspace:*" + } +} diff --git a/packages/astro/test/fixtures/custom-404-injected/src/404.astro b/packages/astro/test/fixtures/custom-404-injected/src/404.astro new file mode 100644 index 000000000000..63d560b0fb91 --- /dev/null +++ b/packages/astro/test/fixtures/custom-404-injected/src/404.astro @@ -0,0 +1,13 @@ +--- +const canonicalURL = new URL(Astro.url.pathname, Astro.site); +--- + + + + Not Found - Custom 404 + + +

Page not found

+

{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 @@ +--- +--- + + + + Custom 404 + + +

Home

+ + From ed61d8399aaa1284adcc18d7a37df88183324718 Mon Sep 17 00:00:00 2001 From: Chris Swithinbank Date: Sat, 29 Apr 2023 00:22:46 +0200 Subject: [PATCH 03/10] Use any route pattern to find custom 404 --- packages/astro/src/vite-plugin-astro-server/route.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/astro/src/vite-plugin-astro-server/route.ts b/packages/astro/src/vite-plugin-astro-server/route.ts index 79232f10fc69..44321d00c176 100644 --- a/packages/astro/src/vite-plugin-astro-server/route.ts +++ b/packages/astro/src/vite-plugin-astro-server/route.ts @@ -35,11 +35,12 @@ 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( + { config }: AstroSettings, + manifest: ManifestData +): RouteData | undefined { + const pathname = config.trailingSlash === 'always' ? '/404/' : '/404'; + return manifest.routes.find((r) => r.pattern.test(pathname)); } export async function matchRoute( From 9015e5da69bf3492384da8d3c85e6ea9614e1143 Mon Sep 17 00:00:00 2001 From: Chris Swithinbank Date: Sat, 29 Apr 2023 00:28:31 +0200 Subject: [PATCH 04/10] Fix frozen lockfile error --- pnpm-lock.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5d3f593608fc..c6aec2ec3065 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2438,6 +2438,12 @@ importers: specifier: workspace:* version: link:../../.. + packages/astro/test/fixtures/custom-404-injected: + dependencies: + astro: + specifier: workspace:* + version: link:../../.. + packages/astro/test/fixtures/custom-404-md: dependencies: astro: From 7e73502eb62338e1e3e554d107e3023772a957d7 Mon Sep 17 00:00:00 2001 From: Chris Swithinbank Date: Sat, 29 Apr 2023 11:19:23 +0200 Subject: [PATCH 05/10] Use `route` instead of `pattern` to match custom 404 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dynamic catch-all routes can match against `/404/` but will then error because they’re not actually designed to handle a param of `'404'`. Testing `route` instead excludes dynamic routes (because they’ll contain dynamic segments inside square brackets). Not sure if someone might _want_ to render the 404 via a dynamic route, but we already don’t support that, so this doesn’t change anything. --- .../astro/src/vite-plugin-astro-server/route.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/packages/astro/src/vite-plugin-astro-server/route.ts b/packages/astro/src/vite-plugin-astro-server/route.ts index 44321d00c176..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,12 +33,9 @@ interface MatchedRoute { mod: ComponentInstance; } -function getCustom404Route( - { config }: AstroSettings, - manifest: ManifestData -): RouteData | undefined { - const pathname = config.trailingSlash === 'always' ? '/404/' : '/404'; - return manifest.routes.find((r) => r.pattern.test(pathname)); +function getCustom404Route(manifest: ManifestData): RouteData | undefined { + const route404 = /^\/404\/?$/; + return manifest.routes.find((r) => route404.test(r.route)); } export async function matchRoute( @@ -98,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); From 79d579aba893296275e8b17b4dfdf14ef4b7f248 Mon Sep 17 00:00:00 2001 From: Chris Swithinbank Date: Sat, 29 Apr 2023 12:15:22 +0200 Subject: [PATCH 06/10] Fix injected 404 fixture --- .../astro/test/fixtures/custom-404-injected/astro.config.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/astro/test/fixtures/custom-404-injected/astro.config.mjs b/packages/astro/test/fixtures/custom-404-injected/astro.config.mjs index af687172055f..d46ce7eb77c9 100644 --- a/packages/astro/test/fixtures/custom-404-injected/astro.config.mjs +++ b/packages/astro/test/fixtures/custom-404-injected/astro.config.mjs @@ -9,7 +9,7 @@ export default defineConfig({ 'astro:config:setup': ({ injectRoute }) => { injectRoute({ pattern: '404', - entryPoint: '/src/404.astro', + entryPoint: 'src/404.astro', }); }, }, From b88b4787ccfb26d933fcc7d3835cf364e619e10f Mon Sep 17 00:00:00 2001 From: Chris Swithinbank Date: Sat, 29 Apr 2023 12:51:36 +0200 Subject: [PATCH 07/10] Add tests for `src/pages/404.html` --- packages/astro/test/custom-404-html.test.js | 42 +++++++++++++++++++ .../fixtures/custom-404-html/astro.config.mjs | 4 ++ .../fixtures/custom-404-html/package.json | 8 ++++ .../custom-404-html/src/pages/404.html | 9 ++++ .../custom-404-html/src/pages/index.astro | 11 +++++ 5 files changed, 74 insertions(+) create mode 100644 packages/astro/test/custom-404-html.test.js create mode 100644 packages/astro/test/fixtures/custom-404-html/astro.config.mjs create mode 100644 packages/astro/test/fixtures/custom-404-html/package.json create mode 100644 packages/astro/test/fixtures/custom-404-html/src/pages/404.html create mode 100644 packages/astro/test/fixtures/custom-404-html/src/pages/index.astro 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/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..73ef4171b3a9 --- /dev/null +++ b/packages/astro/test/fixtures/custom-404-html/package.json @@ -0,0 +1,8 @@ +{ + "name": "@test/custom-404", + "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 @@ + + + Not Found - Custom 404 + + +

Page not found

+

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 @@ +--- +--- + + + + Custom 404 + + +

Home

+ + From 1645d28c9a06c12a6bb98b5973b07ea7d1f8d1fc Mon Sep 17 00:00:00 2001 From: Chris Swithinbank Date: Sat, 29 Apr 2023 12:51:41 +0200 Subject: [PATCH 08/10] Add changeset --- .changeset/ninety-snails-study.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/ninety-snails-study.md 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` From dcd53a51b1f214866cef915c40cf538178a62757 Mon Sep 17 00:00:00 2001 From: Chris Swithinbank Date: Sat, 29 Apr 2023 12:53:38 +0200 Subject: [PATCH 09/10] Fix lockfile --- pnpm-lock.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c6aec2ec3065..395dccc7659b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2438,6 +2438,12 @@ importers: specifier: workspace:* version: link:../../.. + packages/astro/test/fixtures/custom-404-html: + dependencies: + astro: + specifier: workspace:* + version: link:../../.. + packages/astro/test/fixtures/custom-404-injected: dependencies: astro: From 80ca78088d7398bd426200bfc9c75db5cff6fae0 Mon Sep 17 00:00:00 2001 From: Chris Swithinbank Date: Sat, 29 Apr 2023 12:56:56 +0200 Subject: [PATCH 10/10] And again --- packages/astro/test/fixtures/custom-404-html/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/astro/test/fixtures/custom-404-html/package.json b/packages/astro/test/fixtures/custom-404-html/package.json index 73ef4171b3a9..b137ab07695b 100644 --- a/packages/astro/test/fixtures/custom-404-html/package.json +++ b/packages/astro/test/fixtures/custom-404-html/package.json @@ -1,5 +1,5 @@ { - "name": "@test/custom-404", + "name": "@test/custom-404-html", "version": "0.0.0", "private": true, "dependencies": {