From 20da681544418edf5508a7bc6ae343e92a3c35f6 Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Thu, 16 Jan 2025 16:37:54 +0000 Subject: [PATCH] Refactor into separate middleware --- packages/astro/src/template/4xx.ts | 9 +++++ .../src/vite-plugin-astro-server/base.ts | 33 +++--------------- .../src/vite-plugin-astro-server/plugin.ts | 5 +++ .../trailing-slash.ts | 34 +++++++++++++++++++ 4 files changed, 52 insertions(+), 29 deletions(-) create mode 100644 packages/astro/src/vite-plugin-astro-server/trailing-slash.ts diff --git a/packages/astro/src/template/4xx.ts b/packages/astro/src/template/4xx.ts index 38621d966f23..a7e0f1a91ef0 100644 --- a/packages/astro/src/template/4xx.ts +++ b/packages/astro/src/template/4xx.ts @@ -144,3 +144,12 @@ export function trailingSlashMismatchTemplate(pathname: string, trailingSlash: '

Come to our Discord if you need help.

`, }); } + +export function notFoundTemplate(pathname: string, message = 'Not found') { + return template({ + pathname, + statusCode: 404, + title: message, + tabTitle: `404: ${message}`, + }); +} diff --git a/packages/astro/src/vite-plugin-astro-server/base.ts b/packages/astro/src/vite-plugin-astro-server/base.ts index 0bd4b26ac3ad..7f4bd1e8a827 100644 --- a/packages/astro/src/vite-plugin-astro-server/base.ts +++ b/packages/astro/src/vite-plugin-astro-server/base.ts @@ -3,18 +3,11 @@ import type { AstroSettings } from '../types/astro.js'; import * as fs from 'node:fs'; import path from 'node:path'; -import { - appendForwardSlash, - collapseDuplicateTrailingSlashes, - hasFileExtension, -} from '@astrojs/internal-helpers/path'; import { bold } from 'kleur/colors'; import type { Logger } from '../core/logger/core.js'; -import notFoundTemplate, { - subpathNotUsedTemplate, - trailingSlashMismatchTemplate, -} from '../template/4xx.js'; -import { writeHtmlResponse, writeRedirectResponse } from './response.js'; +import { notFoundTemplate, subpathNotUsedTemplate } from '../template/4xx.js'; +import { writeHtmlResponse } from './response.js'; +import { appendForwardSlash } from '@astrojs/internal-helpers/path'; export function baseMiddleware( settings: AstroSettings, @@ -25,14 +18,9 @@ export function baseMiddleware( const devRootURL = new URL(config.base, 'http://localhost'); const devRoot = site ? site.pathname : devRootURL.pathname; const devRootReplacement = devRoot.endsWith('/') ? '/' : ''; - const { trailingSlash } = settings.config; return function devBaseMiddleware(req, res, next) { const url = req.url!; - const destination = collapseDuplicateTrailingSlashes(url, true); - if (url && destination !== url) { - return writeRedirectResponse(res, 301, destination); - } let pathname: string; try { pathname = decodeURI(new URL(url, 'http://localhost').pathname); @@ -41,14 +29,6 @@ export function baseMiddleware( return next(e); } - if ( - (trailingSlash === 'never' && pathname.endsWith('/')) || - (trailingSlash === 'always' && !pathname.endsWith('/') && !hasFileExtension(pathname)) - ) { - const html = trailingSlashMismatchTemplate(pathname, trailingSlash); - return writeHtmlResponse(res, 404, html); - } - if (pathname.startsWith(devRoot)) { req.url = url.replace(devRoot, devRootReplacement); return next(); @@ -60,12 +40,7 @@ export function baseMiddleware( } if (req.headers.accept?.includes('text/html')) { - const html = notFoundTemplate({ - statusCode: 404, - title: 'Not found', - tabTitle: '404: Not Found', - pathname, - }); + const html = notFoundTemplate(pathname, 'Not Found'); return writeHtmlResponse(res, 404, html); } diff --git a/packages/astro/src/vite-plugin-astro-server/plugin.ts b/packages/astro/src/vite-plugin-astro-server/plugin.ts index 5e53cd466646..d7eaa0075108 100644 --- a/packages/astro/src/vite-plugin-astro-server/plugin.ts +++ b/packages/astro/src/vite-plugin-astro-server/plugin.ts @@ -24,6 +24,7 @@ import { recordServerError } from './error.js'; import { DevPipeline } from './pipeline.js'; import { handleRequest } from './request.js'; import { setRouteError } from './server-state.js'; +import { trailingSlashMiddleware } from './trailing-slash.js'; export interface AstroPluginOptions { settings: AstroSettings; @@ -119,6 +120,10 @@ export default function createVitePluginAstroServer({ route: '', handle: baseMiddleware(settings, logger), }); + viteServer.middlewares.stack.unshift({ + route: '', + handle: trailingSlashMiddleware(settings), + }); // Note that this function has a name so other middleware can find it. viteServer.middlewares.use(async function astroDevHandler(request, response) { if (request.url === undefined || !request.method) { diff --git a/packages/astro/src/vite-plugin-astro-server/trailing-slash.ts b/packages/astro/src/vite-plugin-astro-server/trailing-slash.ts new file mode 100644 index 000000000000..cc737392370b --- /dev/null +++ b/packages/astro/src/vite-plugin-astro-server/trailing-slash.ts @@ -0,0 +1,34 @@ +import type * as vite from 'vite'; +import type { AstroSettings } from '../types/astro.js'; + +import { collapseDuplicateTrailingSlashes, hasFileExtension } from '@astrojs/internal-helpers/path'; +import { trailingSlashMismatchTemplate } from '../template/4xx.js'; +import { writeHtmlResponse, writeRedirectResponse } from './response.js'; + +export function trailingSlashMiddleware(settings: AstroSettings): vite.Connect.NextHandleFunction { + const { trailingSlash } = settings.config; + + return function devTrailingSlash(req, res, next) { + const url = req.url!; + const destination = collapseDuplicateTrailingSlashes(url, true); + if (url && destination !== url) { + console.log({url, destination}); + return writeRedirectResponse(res, 301, destination); + } + let pathname: string; + try { + pathname = decodeURI(new URL(url, 'http://localhost').pathname); + } catch (e) { + /* malformed uri */ + return next(e); + } + if ( + (trailingSlash === 'never' && pathname.endsWith('/') && pathname !== '/') || + (trailingSlash === 'always' && !pathname.endsWith('/') && !hasFileExtension(pathname)) + ) { + const html = trailingSlashMismatchTemplate(pathname, trailingSlash); + return writeHtmlResponse(res, 404, html); + } + return next(); + }; +}