Repository: withastro/astro
File: packages/astro/src/vite-plugin-astro-server/base.ts (TypeScript)

Code before:
import * as fs from 'node:fs';
import path from 'node:path';
import { appendForwardSlash } from '@astrojs/internal-helpers/path';
import colors from 'piccolore';
import type * as vite from 'vite';
import type { Logger } from '../core/logger/core.js';
import { notFoundTemplate, subpathNotUsedTemplate } from '../template/4xx.js';
import type { AstroSettings } from '../types/astro.js';
import { writeHtmlResponse } from './response.js';

export function baseMiddleware(
	settings: AstroSettings,
	logger: Logger,
): vite.Connect.NextHandleFunction {
	const { config } = settings;
	const base = config.base || '/';
	const site = config.site ? new URL(base, config.site) : undefined;
	const devRootURL = new URL(base, 'http://localhost');
	const devRoot = site ? site.pathname : devRootURL.pathname;
	const devRootReplacement = devRoot.endsWith('/') ? '/' : '';

	return function devBaseMiddleware(req, res, next) {
		const url = req.url!;
		let pathname: string;
		try {
			pathname = decodeURI(new URL(url, 'http://localhost').pathname);
		} catch (e) {
			/* malformed uri */
			return next(e);
		}

		if (pathname.startsWith(devRoot)) {
			req.url = url.replace(devRoot, devRootReplacement);
			return next();
		}

		if (pathname === '/' || pathname === '/index.html') {
			const html = subpathNotUsedTemplate(devRoot, pathname);
			return writeHtmlResponse(res, 404, html);
		}

		if (req.headers.accept?.includes('text/html')) {

Review comment (bug):
```suggestion
	// The base may be an empty string by now, causing the URL creation to fail. We provide a default instead
	const base = config.base || '/';
```

Code after:
import * as fs from 'node:fs';
import path from 'node:path';
import { appendForwardSlash } from '@astrojs/internal-helpers/path';
import colors from 'piccolore';
import type * as vite from 'vite';
import type { Logger } from '../core/logger/core.js';
import { notFoundTemplate, subpathNotUsedTemplate } from '../template/4xx.js';
import type { AstroSettings } from '../types/astro.js';
import { writeHtmlResponse } from './response.js';

export function baseMiddleware(
	settings: AstroSettings,
	logger: Logger,
): vite.Connect.NextHandleFunction {
	const { config } = settings;
	// The base may be an empty string by now, causing the URL creation to fail. We provide a default instead
	const base = config.base || '/';
	const site = config.site ? new URL(base, config.site) : undefined;
	const devRootURL = new URL(base, 'http://localhost');
	const devRoot = site ? site.pathname : devRootURL.pathname;
	const devRootReplacement = devRoot.endsWith('/') ? '/' : '';

	return function devBaseMiddleware(req, res, next) {
		const url = req.url!;
		let pathname: string;
		try {
			pathname = decodeURI(new URL(url, 'http://localhost').pathname);
		} catch (e) {
			/* malformed uri */
			return next(e);
		}

		if (pathname.startsWith(devRoot)) {
			req.url = url.replace(devRoot, devRootReplacement);
			return next();
		}

		if (pathname === '/' || pathname === '/index.html') {
			const html = subpathNotUsedTemplate(devRoot, pathname);
			return writeHtmlResponse(res, 404, html);
		}