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

Code before:
			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;

		if (routeData) {
			this.#logger.debug(
				'router',
				'The adapter ' + this.#manifest.adapterName + ' provided a custom RouteData for ',
				request.url,
			);
			this.#logger.debug('router', 'RouteData:\n' + routeData);
		}
		if (locals) {
			if (typeof locals !== 'object') {

Review comment (suggestion):
The changeset doesn't mention anything regarding the status code (`301` and `308`). I think we should document it.

Code after:
		}

		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) {
			this.#logger.debug(
				'router',
				'The adapter ' + this.#manifest.adapterName + ' provided a custom RouteData for ',
				request.url,
			);
			this.#logger.debug('router', 'RouteData:\n' + routeData);
		}
		if (locals) {
			if (typeof locals !== 'object') {
				const error = new AstroError(AstroErrorData.LocalsNotAnObject);
				this.#logger.error(null, error.stack!);