Skip to content

Commit bbcf69e

Browse files
authored
Fix redirects in dev mode (#7342)
* Fix redirects in dev mode * Adding a changeset
1 parent 3d9a392 commit bbcf69e

File tree

4 files changed

+109
-54
lines changed

4 files changed

+109
-54
lines changed

.changeset/swift-flies-press.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'astro': patch
3+
---
4+
5+
Fix for experimental redirects in dev mode

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ function isRedirect(statusCode: number) {
161161
}
162162

163163
export function throwIfRedirectNotAllowed(response: Response, config: AstroConfig) {
164-
if (!isServerLikeOutput(config) && isRedirect(response.status)) {
164+
if (!isServerLikeOutput(config) && isRedirect(response.status) && !config.experimental.redirects) {
165165
throw new AstroError(AstroErrorData.StaticRedirectNotAvailable);
166166
}
167167
}

packages/astro/src/prerender/routing.ts

+21-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { AstroSettings, RouteData } from '../@types/astro';
2-
import { preload, type DevelopmentEnvironment } from '../core/render/dev/index.js';
2+
import { preload, type DevelopmentEnvironment, type ComponentPreload } from '../core/render/dev/index.js';
33
import { getPrerenderStatus } from './metadata.js';
4+
import { routeIsRedirect, RedirectComponentInstance } from '../core/redirects/index.js';
45

56
type GetSortedPreloadedMatchesParams = {
67
env: DevelopmentEnvironment;
@@ -26,14 +27,31 @@ type PreloadAndSetPrerenderStatusParams = {
2627
matches: RouteData[];
2728
settings: AstroSettings;
2829
};
30+
31+
type PreloadAndSetPrerenderStatusResult = {
32+
filePath: URL;
33+
route: RouteData;
34+
preloadedComponent: ComponentPreload;
35+
};
36+
2937
async function preloadAndSetPrerenderStatus({
3038
env,
3139
matches,
3240
settings,
33-
}: PreloadAndSetPrerenderStatusParams) {
41+
}: PreloadAndSetPrerenderStatusParams): Promise<PreloadAndSetPrerenderStatusResult[]> {
3442
const preloaded = await Promise.all(
3543
matches.map(async (route) => {
3644
const filePath = new URL(`./${route.component}`, settings.config.root);
45+
46+
if(routeIsRedirect(route)) {
47+
const preloadedComponent: ComponentPreload = [[], RedirectComponentInstance];
48+
return {
49+
preloadedComponent,
50+
route,
51+
filePath
52+
};
53+
}
54+
3755
const preloadedComponent = await preload({ env, filePath });
3856

3957
// gets the prerender metadata set by the `astro:scanner` vite plugin
@@ -46,7 +64,7 @@ async function preloadAndSetPrerenderStatus({
4664
route.prerender = prerenderStatus;
4765
}
4866

49-
return { preloadedComponent, route, filePath } as const;
67+
return { preloadedComponent, route, filePath };
5068
})
5169
);
5270
return preloaded;

packages/astro/test/redirects.test.js

+82-50
Original file line numberDiff line numberDiff line change
@@ -65,61 +65,93 @@ describe('Astro.redirect', () => {
6565
});
6666

6767
describe('output: "static"', () => {
68-
before(async () => {
69-
process.env.STATIC_MODE = true;
70-
fixture = await loadFixture({
71-
root: './fixtures/ssr-redirect/',
72-
output: 'static',
73-
experimental: {
74-
redirects: true,
75-
},
76-
redirects: {
77-
'/one': '/',
78-
'/two': '/',
79-
'/blog/[...slug]': '/articles/[...slug]',
80-
'/three': {
81-
status: 302,
82-
destination: '/',
68+
describe('build', () => {
69+
before(async () => {
70+
process.env.STATIC_MODE = true;
71+
fixture = await loadFixture({
72+
root: './fixtures/ssr-redirect/',
73+
output: 'static',
74+
experimental: {
75+
redirects: true,
8376
},
84-
},
77+
redirects: {
78+
'/one': '/',
79+
'/two': '/',
80+
'/blog/[...slug]': '/articles/[...slug]',
81+
'/three': {
82+
status: 302,
83+
destination: '/',
84+
},
85+
},
86+
});
87+
await fixture.build();
88+
});
89+
90+
it('Includes the meta refresh tag in Astro.redirect pages', async () => {
91+
const html = await fixture.readFile('/secret/index.html');
92+
expect(html).to.include('http-equiv="refresh');
93+
expect(html).to.include('url=/login');
94+
});
95+
96+
it('Includes the meta refresh tag in `redirect` config pages', async () => {
97+
let html = await fixture.readFile('/one/index.html');
98+
expect(html).to.include('http-equiv="refresh');
99+
expect(html).to.include('url=/');
100+
101+
html = await fixture.readFile('/two/index.html');
102+
expect(html).to.include('http-equiv="refresh');
103+
expect(html).to.include('url=/');
104+
105+
html = await fixture.readFile('/three/index.html');
106+
expect(html).to.include('http-equiv="refresh');
107+
expect(html).to.include('url=/');
108+
});
109+
110+
it('Generates page for dynamic routes', async () => {
111+
let html = await fixture.readFile('/blog/one/index.html');
112+
expect(html).to.include('http-equiv="refresh');
113+
expect(html).to.include('url=/articles/one');
114+
115+
html = await fixture.readFile('/blog/two/index.html');
116+
expect(html).to.include('http-equiv="refresh');
117+
expect(html).to.include('url=/articles/two');
118+
});
119+
120+
it('Generates redirect pages for redirects created by middleware', async () => {
121+
let html = await fixture.readFile('/middleware-redirect/index.html');
122+
expect(html).to.include('http-equiv="refresh');
123+
expect(html).to.include('url=/');
85124
});
86-
await fixture.build();
87-
});
88-
89-
it('Includes the meta refresh tag in Astro.redirect pages', async () => {
90-
const html = await fixture.readFile('/secret/index.html');
91-
expect(html).to.include('http-equiv="refresh');
92-
expect(html).to.include('url=/login');
93-
});
94-
95-
it('Includes the meta refresh tag in `redirect` config pages', async () => {
96-
let html = await fixture.readFile('/one/index.html');
97-
expect(html).to.include('http-equiv="refresh');
98-
expect(html).to.include('url=/');
99-
100-
html = await fixture.readFile('/two/index.html');
101-
expect(html).to.include('http-equiv="refresh');
102-
expect(html).to.include('url=/');
103-
104-
html = await fixture.readFile('/three/index.html');
105-
expect(html).to.include('http-equiv="refresh');
106-
expect(html).to.include('url=/');
107125
});
108126

109-
it('Generates page for dynamic routes', async () => {
110-
let html = await fixture.readFile('/blog/one/index.html');
111-
expect(html).to.include('http-equiv="refresh');
112-
expect(html).to.include('url=/articles/one');
113-
114-
html = await fixture.readFile('/blog/two/index.html');
115-
expect(html).to.include('http-equiv="refresh');
116-
expect(html).to.include('url=/articles/two');
117-
});
127+
describe('dev', () => {
128+
/** @type {import('./test-utils.js').DevServer} */
129+
let devServer;
130+
before(async () => {
131+
process.env.STATIC_MODE = true;
132+
fixture = await loadFixture({
133+
root: './fixtures/ssr-redirect/',
134+
output: 'static',
135+
experimental: {
136+
redirects: true,
137+
},
138+
redirects: {
139+
'/one': '/',
140+
},
141+
});
142+
devServer = await fixture.startDevServer();
143+
});
118144

119-
it('Generates redirect pages for redirects created by middleware', async () => {
120-
let html = await fixture.readFile('/middleware-redirect/index.html');
121-
expect(html).to.include('http-equiv="refresh');
122-
expect(html).to.include('url=/');
145+
after(async () => {
146+
await devServer.stop();
147+
});
148+
149+
it('Returns 301', async () => {
150+
let res = await fixture.fetch('/one', {
151+
redirect: 'manual'
152+
});
153+
expect(res.status).to.equal(301);
154+
});
123155
});
124156
});
125157

0 commit comments

Comments
 (0)