diff options
Diffstat (limited to 'packages/astro/src/vite-plugin-astro-server/trailing-slash.ts')
-rw-r--r-- | packages/astro/src/vite-plugin-astro-server/trailing-slash.ts | 34 |
1 files changed, 34 insertions, 0 deletions
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 000000000..fff8d3117 --- /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) { + 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(); + }; +} |