Skip to content

Commit f4e8889

Browse files
authored
fix: skip trailing slash redirection for more internal paths (#14169)
* fix: skip trailing slash redirection for more internal paths * Changes from review
1 parent 2fa34e4 commit f4e8889

File tree

7 files changed

+44
-4
lines changed

7 files changed

+44
-4
lines changed

.changeset/common-ducks-trade.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@astrojs/internal-helpers': minor
3+
---
4+
5+
Adds `isInternalPath` helper

.changeset/flat-shirts-shake.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'astro': patch
3+
---
4+
5+
Skips trailing slash handling for paths that start with `/.`.

.changeset/thirty-ears-guess.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'astro': patch
3+
---
4+
5+
Fixes a bug that prevented images from being displayed in dev when using the Netlify adapter with `trailingSlash` set to `always`

packages/astro/src/core/app/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { collapseDuplicateTrailingSlashes, hasFileExtension } from '@astrojs/internal-helpers/path';
1+
import { collapseDuplicateTrailingSlashes, hasFileExtension, isInternalPath } from '@astrojs/internal-helpers/path';
22
import { normalizeTheLocale } from '../../i18n/index.js';
33
import type { RoutesList } from '../../types/astro.js';
44
import type { RouteData, SSRManifest } from '../../types/public/internal.js';
@@ -292,7 +292,7 @@ export class App {
292292
const { trailingSlash } = this.#manifest;
293293

294294
// Ignore root and internal paths
295-
if (pathname === '/' || pathname.startsWith('/_')) {
295+
if (pathname === '/' || isInternalPath(pathname)) {
296296
return pathname;
297297
}
298298

packages/astro/src/vite-plugin-astro-server/trailing-slash.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { collapseDuplicateTrailingSlashes, hasFileExtension } from '@astrojs/internal-helpers/path';
1+
import { collapseDuplicateTrailingSlashes, hasFileExtension, isInternalPath } from '@astrojs/internal-helpers/path';
22
import type * as vite from 'vite';
33
import { trailingSlashMismatchTemplate } from '../template/4xx.js';
44
import type { AstroSettings } from '../types/astro.js';
@@ -16,7 +16,7 @@ export function trailingSlashMiddleware(settings: AstroSettings): vite.Connect.N
1616
/* malformed uri */
1717
return next(e);
1818
}
19-
if (pathname.startsWith('/_') || pathname.startsWith('/@')) {
19+
if (isInternalPath(pathname)) {
2020
return next();
2121
}
2222

packages/astro/test/ssr-trailing-slash.test.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ describe('Redirecting trailing slashes in SSR', () => {
115115
'/_image?url=http://example.com/foo.jpg',
116116
'/_server-islands/foo',
117117
'/_actions/foo',
118+
'/.netlify/image?url=http://example.com/foo.jpg',
118119
]) {
119120
const request = new Request(`http://example.com${path}`);
120121
const response = await app.render(request);
@@ -211,6 +212,7 @@ describe('Redirecting trailing slashes in SSR', () => {
211212
'/_image/?url=http://example.com/foo.jpg',
212213
'/_server-islands/foo/',
213214
'/_actions/foo/',
215+
'/.netlify/image/?url=http://example.com/foo.jpg',
214216
]) {
215217
const request = new Request(`http://example.com${path}/`);
216218
const response = await app.render(request);
@@ -259,5 +261,21 @@ describe('Redirecting trailing slashes in SSR', () => {
259261
const response = await app.render(request);
260262
assert.equal(response.status, 200);
261263
});
264+
265+
it('Does not redirect internal paths', async () => {
266+
const app = await fixture.loadTestAdapterApp();
267+
268+
for (const path of [
269+
'/_astro/something//',
270+
'/_image//?url=http://example.com/foo.jpg',
271+
'/_server-islands/foo//',
272+
'/_actions/foo//',
273+
'/.netlify/image//?url=http://example.com/foo.jpg',
274+
]) {
275+
const request = new Request(`http://example.com${path}/`);
276+
const response = await app.render(request);
277+
assert.notEqual(response.status, 301);
278+
}
279+
});
262280
});
263281
});

packages/internal-helpers/src/path.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ function isString(path: unknown): path is string {
6969
return typeof path === 'string' || path instanceof String;
7070
}
7171

72+
const INTERNAL_PREFIXES = new Set(['/_', '/@', '/.']);
73+
const JUST_SLASHES = /^\/{2,}$/;
74+
75+
export function isInternalPath(path: string) {
76+
return INTERNAL_PREFIXES.has(path.slice(0, 2)) && !JUST_SLASHES.test(path);
77+
}
78+
7279
export function joinPaths(...paths: (string | undefined)[]) {
7380
return paths
7481
.filter(isString)

0 commit comments

Comments
 (0)