Repository: withastro/astro
File: packages/astro/src/core/app/index.ts (TypeScript)

Code before:
	#redirectTrailingSlash(pathname: string): string | undefined {
		const { trailingSlash } = this.#manifest;

		// Ignore root and internal paths
		if (pathname === '/' || pathname.startsWith('/_')) {
			return;
		}

		// Redirect multiple trailing slashes to collapsed path
		if (MANY_SLASHES.test(pathname)) {
			const path = pathname.replace(MANY_SLASHES, trailingSlash === 'never' ? '' : '/');
			return path ? path : '/';
		}

		if (trailingSlash === 'ignore') {
			return;
		}

		// Add a trailing slash if it's not a file
		const hasTrailingSlash = pathname.endsWith('/');
		if (trailingSlash === 'always' && !hasTrailingSlash && !hasFileExtension(pathname)) {
			return `${pathname}/`;
		}
		if (trailingSlash === 'never' && hasTrailingSlash) {
			return pathname.slice(0, -1);
		}
	}

	async render(request: Request, renderOptions?: RenderOptions): Promise<Response> {
		let routeData: RouteData | undefined;
		let locals: object | undefined;
		let clientAddress: string | undefined;
		let addCookieHeader: boolean | undefined;

		const url = new URL(request.url);
		const redirect = this.#redirectTrailingSlash(url.pathname);

		if (redirect) {
			const status = request.method === 'GET' ? 301 : 308;
			return new Response(redirectTemplate({ status, location: redirect, from: request.url }), {
				status,
				headers: {
					location: redirect + url.search,
				},
			});
		}

		addCookieHeader = renderOptions?.addCookieHeader;
		clientAddress = renderOptions?.clientAddress ?? Reflect.get(request, clientAddressSymbol);
		routeData = renderOptions?.routeData;
		locals = renderOptions?.locals;

Review comment (suggestion):
We already have a bunch of methods to handle trailing slashes, let's use them instead of re-inventing the wheel: 
- `prependForwardSlash`
- `removeTrailingForwardSlash`

Code after:
		const { trailingSlash } = this.#manifest;

		// Ignore root and internal paths
		if (pathname === '/' || pathname.startsWith('/_')) {
			return pathname;
		}

		// Redirect multiple trailing slashes to collapsed path
		const path = collapseDuplicateTrailingSlashes(pathname, trailingSlash !== 'never');
		if (path !== pathname) {
			return path;
		}

		if (trailingSlash === 'ignore') {
			return pathname;
		}

		if (trailingSlash === 'always' && !hasFileExtension(pathname)) {
			return appendForwardSlash(pathname);
		}
		if (trailingSlash === 'never') {
			return removeTrailingForwardSlash(pathname);
		}

		return pathname;
	}

	async render(request: Request, renderOptions?: RenderOptions): Promise<Response> {
		let routeData: RouteData | undefined;
		let locals: object | undefined;
		let clientAddress: string | undefined;
		let addCookieHeader: boolean | undefined;
		const url = new URL(request.url);
		const redirect = this.#redirectTrailingSlash(url.pathname);

		if (redirect !== url.pathname) {
			const status = request.method === 'GET' ? 301 : 308;
			return new Response(redirectTemplate({ status, location: redirect, from: request.url }), {
				status,
				headers: {
					location: redirect + url.search,
				},
			});
		}

		addCookieHeader = renderOptions?.addCookieHeader;
		clientAddress = renderOptions?.clientAddress ?? Reflect.get(request, clientAddressSymbol);
		routeData = renderOptions?.routeData;
		locals = renderOptions?.locals;

		if (routeData) {