Skip to content

Commit be79ad2

Browse files
authored
Merge pull request #7988 from QwikDev/qc-fix-qdata-redirects
fix(qwik-city): q-data.json redirect handling
2 parents 2657dcc + 9fa4290 commit be79ad2

File tree

15 files changed

+320
-152
lines changed

15 files changed

+320
-152
lines changed

.changeset/lemon-pandas-turn.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@builder.io/qwik-city': patch
3+
---
4+
5+
fix: redirecting internal q-data.json requests will keep the q-data.json suffix so that the client can still fetch the correct one

.changeset/shaky-parks-hope.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@builder.io/qwik-city': patch
3+
---
4+
5+
fix: while prefetching Link data, don't navigate to captive portals

packages/docs/public/_redirects

Lines changed: 53 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
1+
# https://developers.cloudflare.com/pages/configuration/redirects/
2+
3+
# Keep in sync with `src/routes/[email protected]`
4+
5+
# Discord
6+
17
/chat https://discord.gg/TsNCMd6uGW 307
28
/chat/ https://discord.gg/TsNCMd6uGW 307
39

10+
# Cute redirects
11+
412
/examples /examples/introduction/hello-world/ 307
513
/examples/ /examples/introduction/hello-world/ 307
614
/guide /docs/ 307
@@ -10,63 +18,67 @@
1018
/tutorials /tutorial/welcome/overview/ 307
1119
/tutorials/ /tutorial/welcome/overview/ 307
1220

13-
/tutorial/hooks/use-client-effect/ /tutorial/hooks/use-browser-visible-task/ 307
21+
# Old URLs
1422

15-
/qwikcity/routing/pathless/ /docs/layout/grouped/ 308
23+
/tutorial/hooks/use-client-effect/ /tutorial/hooks/use-visible-task/ 308
1624

17-
/qwikcity/middleware/azure-swa/ /deployments/azure-swa/ 308
18-
/qwikcity/middleware/cloudflare-pages/ /deployments/cloudflare-pages/ 308
19-
/qwikcity/middleware/netlify-edge/ /deployments/netlify-edge/ 308
20-
/qwikcity/middleware/node/ /deployments/node/ 308
21-
/qwikcity/middleware/express/ /deployments/node/ 308
2225
/integrations/deployments/azure-swa/ /deployments/azure-swa/ 308
2326
/integrations/deployments/cloudflare-pages/ /deployments/cloudflare-pages/ 308
24-
/integrations/deployments/netlify-edge/ /deployments/netlify-edge/ 308
2527
/integrations/deployments/express/ /deployments/express/ 308
28+
/integrations/deployments/netlify-edge/ /deployments/netlify-edge/ 308
2629
/integrations/deployments/vercel-edge/ /deployments/vercel-edge/ 308
2730

28-
/qwikcity/directory-layout/ /docs/project-structure/ 308
29-
/qwikcity/static-assets/ /docs/advanced/static-assets/ 308
30-
/qwikcity/routing/overview/ /docs/routing/ 308
31-
/qwikcity/routing/route-parameters/ /docs/routing/ 308
32-
/qwikcity/routing/error-responses/ /docs/advanced/routing/ 308
33-
/qwikcity/loader/ /docs/route-loader/ 308
34-
/qwikcity/layout/overview/ /docs/layout/ 308
35-
/qwikcity/layout/nested/ /docs/advanced/routing/ 308
36-
/qwikcity/layout/grouped/ /docs/advanced/routing/ 308
37-
/qwikcity/layout/named/ /docs/advanced/routing/ 308
38-
/qwikcity/data/overview/ /docs/routing/ 308
39-
/qwikcity/data/retrieve/ /docs/routing/ 308
40-
/qwikcity/data/modify/ /docs/endpoints/ 308
41-
/qwikcity/data/endpoints/ /docs/endpoints/ 308
42-
/qwikcity/data/redirects/ /docs/guides/redirects/ 308
31+
/qwikcity/advanced/prefetching/ /docs/advanced/modules-prefetching/
4332
/qwikcity/content/component/ /docs/pages/ 308
33+
/qwikcity/content/head/ /docs/pages/ 308
4434
/qwikcity/content/mdx/ /docs/guides/mdx/ 308
4535
/qwikcity/content/menu/ /docs/advanced/menu/ 308
46-
/qwikcity/content/head/ /docs/pages/ 308
47-
/qwikcity/static-site-generation/overview/ /docs/guides/static-site-generation/ 308
48-
/qwikcity/static-site-generation/static-site-config/ /docs/guides/static-site-generation/ 308
49-
/qwikcity/static-site-generation/dynamic-routes/ /docs/guides/static-site-generation/ 308
50-
/qwikcity/advanced/prefetching/ /docs/advanced/modules-prefetching/
36+
/qwikcity/data/endpoints/ /docs/endpoints/ 308
37+
/qwikcity/data/modify/ /docs/endpoints/ 308
38+
/qwikcity/data/overview/ /docs/routing/ 308
39+
/qwikcity/data/redirects/ /docs/guides/redirects/ 308
40+
/qwikcity/data/retrieve/ /docs/routing/ 308
41+
/qwikcity/directory-layout/ /docs/project-structure/ 308
42+
/qwikcity/layout/grouped/ /docs/advanced/routing/ 308
43+
/qwikcity/layout/named/ /docs/advanced/routing/ 308
44+
/qwikcity/layout/nested/ /docs/advanced/routing/ 308
45+
/qwikcity/layout/overview/ /docs/layout/ 308
46+
/qwikcity/loader/ /docs/route-loader/ 308
47+
/qwikcity/middleware/azure-swa/ /deployments/azure-swa/ 308
48+
/qwikcity/middleware/cloudflare-pages/ /deployments/cloudflare-pages/ 308
49+
/qwikcity/middleware/express/ /deployments/node/ 308
50+
/qwikcity/middleware/netlify-edge/ /deployments/netlify-edge/ 308
51+
/qwikcity/middleware/node/ /deployments/node/ 308
5152
/qwikcity/prefetching/overview/ /docs/advanced/speculative-module-fetching/ 308
52-
/qwikcity/prefetching/service-worker-prefetching/ /docs/advanced/speculative-module-fetching/ 308
53-
/qwikcity/prefetching/request-response-cache/ /docs/advanced/speculative-module-fetching/ 308
5453
/qwikcity/prefetching/parallelizing-network-requests/ /docs/advanced/speculative-module-fetching/ 308
55-
/docs/overview /docs/ 308
56-
/docs/overview/ /docs/ 308
57-
/docs/cheat/qwik-react/ /docs/integrations/react/ 308
54+
/qwikcity/prefetching/request-response-cache/ /docs/advanced/speculative-module-fetching/ 308
55+
/qwikcity/prefetching/service-worker-prefetching/ /docs/advanced/speculative-module-fetching/ 308
56+
/qwikcity/routing/error-responses/ /docs/advanced/routing/ 308
57+
/qwikcity/routing/overview/ /docs/routing/ 308
58+
/qwikcity/routing/pathless/ /docs/layout/grouped/ 308
59+
/qwikcity/routing/route-parameters/ /docs/routing/ 308
60+
/qwikcity/static-assets/ /docs/advanced/static-assets/ 308
61+
/qwikcity/static-site-generation/dynamic-routes/ /docs/guides/static-site-generation/ 308
62+
/qwikcity/static-site-generation/overview/ /docs/guides/static-site-generation/ 308
63+
/qwikcity/static-site-generation/static-site-config/ /docs/guides/static-site-generation/ 308
64+
65+
/docs/advanced/i18n/ /docs/integrations/i18n/ 308
5866
/docs/cheat/best-practices/ /docs/guides/best-practices/ 308
67+
/docs/cheat/qwik-react/ /docs/integrations/react/ 308
5968
/docs/cheat/serialization/ /docs/guides/serialization/ 308
69+
/docs/components/inline-components/ /docs/core/overview/ 308
6070
/docs/components/lifecycle/ /docs/core/tasks/ 308
6171
/docs/components/projection/ /docs/core/slots/ 308
6272
/docs/components/resource/ /docs/core/state/ 308
6373
/docs/cookbook/re-exporting-loaders/ /docs/re-exporting-loaders/ 308
64-
65-
/qwikcity/* /docs/:splat 308
66-
/integrations/* /docs/integrations/:splat 308
67-
/deployments/* /docs/deployments/:splat 308
68-
/docs/advanced/i18n/ /docs/integrations/i18n/ 308
69-
/docs/components/inline-components/ /docs/core/overview/ 308
70-
/docs/think-qwik/ /docs/concepts/think-qwik/ 308
7174
/docs/env-variables/ /docs/guides/env-variables/ 308
72-
/docs/components/* /docs/core/:splat 308
75+
/docs/overview /docs/ 308
76+
/docs/overview/ /docs/ 308
77+
/docs/think-qwik/ /docs/concepts/think-qwik/ 308
78+
79+
# All wildcards (and only wildcards) must be below here
80+
81+
/deployments/_ /docs/deployments/:splat 308
82+
/docs/components/_ /docs/core/:splat 308
83+
/integrations/_ /docs/integrations/:splat 308
84+
/qwikcity/_ /docs/:splat 308

packages/docs/src/routes/api/qwik/api.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1774,7 +1774,7 @@
17741774
}
17751775
],
17761776
"kind": "Function",
1777-
"content": "> This API is provided as an alpha preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.\n> \n\n> Warning: This API is now obsolete.\n> \n> This is no longer needed as the preloading happens automatically in qrl-class.ts. Leave this in your app for a while so it uninstalls existing service workers, but don't use it for new projects.\n> \n\n\n```typescript\nPrefetchServiceWorker: (opts: {\n base?: string;\n scope?: string;\n path?: string;\n verbose?: boolean;\n fetchBundleGraph?: boolean;\n nonce?: string;\n}) => JSXNode<'script'>\n```\n\n\n<table><thead><tr><th>\n\nParameter\n\n\n</th><th>\n\nType\n\n\n</th><th>\n\nDescription\n\n\n</th></tr></thead>\n<tbody><tr><td>\n\nopts\n\n\n</td><td>\n\n{ base?: string; scope?: string; path?: string; verbose?: boolean; fetchBundleGraph?: boolean; nonce?: string; }\n\n\n</td><td>\n\n\n</td></tr>\n</tbody></table>\n\n**Returns:**\n\nJSXNode&lt;'script'&gt;",
1777+
"content": "> This API is provided as an alpha preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.\n> \n\n> Warning: This API is now obsolete.\n> \n> This is no longer needed as the preloading happens automatically in qrl-class.ts. Leave this in your app for a while so it uninstalls existing service workers, but don't use it for new projects.\n> \n\n\n```typescript\nPrefetchServiceWorker: (opts: {\n base?: string;\n scope?: string;\n path?: string;\n verbose?: boolean;\n fetchBundleGraph?: boolean;\n nonce?: string;\n}) => JSXNode<'script'>\n```\n\n\n<table><thead><tr><th>\n\nParameter\n\n\n</th><th>\n\nType\n\n\n</th><th>\n\nDescription\n\n\n</th></tr></thead>\n<tbody><tr><td>\n\nopts\n\n\n</td><td>\n\n{ base?: string; scope?: string; path?: string; verbose?: boolean; fetchBundleGraph?: boolean; nonce?: string; }\n\n\n</td><td>\n\n\n</td></tr>\n</tbody></table>\n\n**Returns:**\n\n[JSXNode](#jsxnode)<!-- -->&lt;'script'&gt;",
17781778
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/components/prefetch.ts",
17791779
"mdFile": "qwik.prefetchserviceworker.md"
17801780
},

packages/docs/src/routes/api/qwik/index.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3667,7 +3667,7 @@ opts
36673667
36683668
**Returns:**
36693669
3670-
JSXNode&lt;'script'&gt;
3670+
[JSXNode](#jsxnode)&lt;'script'&gt;
36713671
36723672
[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/components/prefetch.ts)
36733673
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import type { RequestEvent } from '@builder.io/qwik-city';
2+
3+
// Keep in sync with `public/_redirects`
4+
5+
export const onGet = ({ url, redirect }: RequestEvent) => {
6+
const { pathname } = url;
7+
const tempRedirect = tempRedirects[pathname];
8+
if (tempRedirect) {
9+
throw redirect(307, tempRedirect);
10+
}
11+
const redirectUrl = redirects[pathname];
12+
if (redirectUrl) {
13+
throw redirect(308, redirectUrl);
14+
}
15+
const rewritePrefix = (path: string, replacement: string) => {
16+
if (pathname.startsWith(path)) {
17+
throw redirect(308, pathname.replace(path, replacement));
18+
}
19+
};
20+
rewritePrefix('/docs/components/', '/docs/core/');
21+
rewritePrefix('/deployments/', '/docs/deployments/');
22+
rewritePrefix('/integrations/', '/docs/integrations/');
23+
rewritePrefix('/qwikcity/', '/docs/');
24+
};
25+
26+
const tempRedirects: Record<string, string> = {
27+
'/chat': ' https://discord.gg/TsNCMd6uGW',
28+
'/chat/': ' https://discord.gg/TsNCMd6uGW',
29+
30+
'/examples ': '/examples/introduction/hello-world/',
31+
'/examples/ ': '/examples/introduction/hello-world/',
32+
'/guide ': '/docs/',
33+
'/guide/ ': '/docs/',
34+
'/tutorial ': '/tutorial/welcome/overview/',
35+
'/tutorial/ ': '/tutorial/welcome/overview/',
36+
'/tutorials ': '/tutorial/welcome/overview/',
37+
'/tutorials/ ': '/tutorial/welcome/overview/',
38+
};
39+
40+
const redirects: Record<string, string> = {
41+
'/tutorial/hooks/use-client-effect/': '/tutorial/hooks/use-visible-task/',
42+
43+
'/integrations/deployments/azure-swa/': '/deployments/azure-swa/',
44+
'/integrations/deployments/cloudflare-pages/': '/deployments/cloudflare-pages/',
45+
'/integrations/deployments/express/': '/deployments/express/',
46+
'/integrations/deployments/netlify-edge/': '/deployments/netlify-edge/',
47+
'/integrations/deployments/vercel-edge/': '/deployments/vercel-edge/',
48+
49+
'/qwikcity/advanced/prefetching/': '/docs/advanced/modules-prefetching/',
50+
'/qwikcity/content/component/': '/docs/pages/',
51+
'/qwikcity/content/head/': '/docs/pages/',
52+
'/qwikcity/content/mdx/': '/docs/guides/mdx/',
53+
'/qwikcity/content/menu/': '/docs/advanced/menu/',
54+
'/qwikcity/data/endpoints/': '/docs/endpoints/',
55+
'/qwikcity/data/modify/': '/docs/endpoints/',
56+
'/qwikcity/data/overview/': '/docs/routing/',
57+
'/qwikcity/data/redirects/': '/docs/guides/redirects/',
58+
'/qwikcity/data/retrieve/': '/docs/routing/',
59+
'/qwikcity/directory-layout/': '/docs/project-structure/',
60+
'/qwikcity/layout/grouped/': '/docs/advanced/routing/',
61+
'/qwikcity/layout/named/': '/docs/advanced/routing/',
62+
'/qwikcity/layout/nested/': '/docs/advanced/routing/',
63+
'/qwikcity/layout/overview/': '/docs/layout/',
64+
'/qwikcity/loader/': '/docs/route-loader/',
65+
'/qwikcity/middleware/azure-swa/': '/deployments/azure-swa/',
66+
'/qwikcity/middleware/cloudflare-pages/': '/deployments/cloudflare-pages/',
67+
'/qwikcity/middleware/express/': '/deployments/node/',
68+
'/qwikcity/middleware/netlify-edge/': '/deployments/netlify-edge/',
69+
'/qwikcity/middleware/node/': '/deployments/node/',
70+
'/qwikcity/prefetching/overview/': '/docs/advanced/speculative-module-fetching/',
71+
'/qwikcity/prefetching/parallelizing-network-requests/':
72+
'/docs/advanced/speculative-module-fetching/',
73+
'/qwikcity/prefetching/request-response-cache/': '/docs/advanced/speculative-module-fetching/',
74+
'/qwikcity/prefetching/service-worker-prefetching/':
75+
'/docs/advanced/speculative-module-fetching/',
76+
'/qwikcity/routing/error-responses/': '/docs/advanced/routing/',
77+
'/qwikcity/routing/overview/': '/docs/routing/',
78+
'/qwikcity/routing/pathless/': '/docs/layout/grouped/',
79+
'/qwikcity/routing/route-parameters/': '/docs/routing/',
80+
'/qwikcity/static-assets/': '/docs/advanced/static-assets/',
81+
'/qwikcity/static-site-generation/dynamic-routes/': '/docs/guides/static-site-generation/',
82+
'/qwikcity/static-site-generation/overview/': '/docs/guides/static-site-generation/',
83+
'/qwikcity/static-site-generation/static-site-config/': '/docs/guides/static-site-generation/',
84+
85+
'/docs/advanced/i18n/': '/docs/integrations/i18n/',
86+
'/docs/cheat/best-practices/': '/docs/guides/best-practices/',
87+
'/docs/cheat/qwik-react/': '/docs/integrations/react/',
88+
'/docs/cheat/serialization/': '/docs/guides/serialization/',
89+
'/docs/components/inline-components/': '/docs/core/overview/',
90+
'/docs/components/lifecycle/': '/docs/core/tasks/',
91+
'/docs/components/projection/': '/docs/core/slots/',
92+
'/docs/components/resource/': '/docs/core/state/',
93+
'/docs/cookbook/re-exporting-loaders/': '/docs/re-exporting-loaders/',
94+
'/docs/env-variables/': '/docs/guides/env-variables/',
95+
'/docs/overview': '/docs/',
96+
'/docs/overview/': '/docs/',
97+
'/docs/think-qwik/': '/docs/concepts/think-qwik/',
98+
};

packages/qwik-city/src/buildtime/vite/dev-server.ts

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,19 @@ import {
99
resolveRequestHandlers,
1010
} from '../../middleware/request-handler/resolve-request-handlers';
1111
import { getQwikCityServerData } from '../../middleware/request-handler/response-page';
12-
import {
13-
QDATA_JSON,
14-
getRouteMatchPathname,
15-
runQwikCity,
16-
} from '../../middleware/request-handler/user-response';
12+
import { getRouteMatchPathname, runQwikCity } from '../../middleware/request-handler/user-response';
1713
import { matchRoute } from '../../runtime/src/route-matcher';
1814
import { getMenuLoader } from '../../runtime/src/routing';
1915
import type {
2016
ActionInternal,
21-
RebuildRouteInfoInternal,
2217
ContentMenu,
2318
LoadedRoute,
2419
LoaderInternal,
2520
MenuData,
2621
MenuModule,
2722
MenuModuleLoader,
2823
PathParams,
24+
RebuildRouteInfoInternal,
2925
RequestEvent,
3026
RouteModule,
3127
} from '../../runtime/src/types';
@@ -126,16 +122,18 @@ export function ssrDevMiddleware(ctx: BuildContext, server: ViteDevServer) {
126122
] satisfies LoadedRoute;
127123
return { serverPlugins, loadedRoute };
128124
};
129-
const resolveRoute = (routeModulePaths: WeakMap<RouteModule<unknown>, string>, url: URL) => {
130-
const matchPathname = getRouteMatchPathname(url.pathname, ctx.opts.trailingSlash);
131-
routePs[matchPathname] ||= _resolveRoute(routeModulePaths, matchPathname).finally(() => {
132-
delete routePs[matchPathname];
125+
const resolveRoute = (
126+
routeModulePaths: WeakMap<RouteModule<unknown>, string>,
127+
pathname: string
128+
) => {
129+
routePs[pathname] ||= _resolveRoute(routeModulePaths, pathname).finally(() => {
130+
delete routePs[pathname];
133131
});
134-
return routePs[matchPathname];
132+
return routePs[pathname];
135133
};
136134

137135
// Preload the modules needed to handle /, so that they load faster on first request.
138-
resolveRoute(new WeakMap(), new URL('/', 'http://localhost')).catch((e: unknown) => {
136+
resolveRoute(new WeakMap(), '/').catch((e: unknown) => {
139137
if (e instanceof Error) {
140138
server.ssrFixStacktrace(e);
141139
formatError(e);
@@ -150,32 +148,34 @@ export function ssrDevMiddleware(ctx: BuildContext, server: ViteDevServer) {
150148
next();
151149
return;
152150
}
151+
const { pathname, isInternal } = getRouteMatchPathname(url.pathname, ctx.opts.trailingSlash);
152+
153+
if (!isInternal) {
154+
// Normally, entries are served statically, so in dev mode we need to handle them here.
155+
const matchRouteName = url.pathname.slice(1);
156+
const entry = ctx.entries.find((e) => e.routeName === matchRouteName);
157+
if (entry) {
158+
const entryContents = await server.transformRequest(
159+
`/@fs${entry.filePath.startsWith('/') ? '' : '/'}${entry.filePath}`
160+
);
153161

154-
// Normally, entries are served statically, so in dev mode we need to handle them here.
155-
const matchRouteName = url.pathname.slice(1);
156-
const entry = ctx.entries.find((e) => e.routeName === matchRouteName);
157-
if (entry) {
158-
const entryContents = await server.transformRequest(
159-
`/@fs${entry.filePath.startsWith('/') ? '' : '/'}${entry.filePath}`
160-
);
161-
162-
if (entryContents) {
163-
res.setHeader('Content-Type', 'text/javascript');
164-
res.end(entryContents.code);
165-
} else {
166-
next();
162+
if (entryContents) {
163+
res.setHeader('Content-Type', 'text/javascript');
164+
res.end(entryContents.code);
165+
} else {
166+
next();
167+
}
168+
return;
167169
}
168-
return;
169170
}
170171

171172
const routeModulePaths = new WeakMap<RouteModule, string>();
172173
try {
173-
const { serverPlugins, loadedRoute } = await resolveRoute(routeModulePaths, url);
174+
const { serverPlugins, loadedRoute } = await resolveRoute(routeModulePaths, pathname);
174175

175176
const renderFn = async (requestEv: RequestEvent) => {
176177
// routeResult && requestEv.sharedMap.set('@routeName', routeResult.route.pathname);
177-
const isPageDataReq = requestEv.pathname.endsWith(QDATA_JSON);
178-
if (!isPageDataReq) {
178+
if (!isInternal) {
179179
const serverData = getQwikCityServerData(requestEv);
180180

181181
res.statusCode = requestEv.status();
@@ -217,7 +217,8 @@ export function ssrDevMiddleware(ctx: BuildContext, server: ViteDevServer) {
217217
loadedRoute,
218218
req.method ?? 'GET',
219219
false,
220-
renderFn
220+
renderFn,
221+
isInternal
221222
);
222223

223224
if (requestHandlers.length > 0) {
@@ -229,13 +230,15 @@ export function ssrDevMiddleware(ctx: BuildContext, server: ViteDevServer) {
229230
const qwikSerializer = { _deserializeData, _serializeData, _verifySerializable };
230231

231232
const rebuildRouteInfo: RebuildRouteInfoInternal = async (url: URL) => {
232-
const { serverPlugins, loadedRoute } = await resolveRoute(routeModulePaths, url);
233+
const { pathname } = getRouteMatchPathname(url.pathname, ctx.opts.trailingSlash);
234+
const { serverPlugins, loadedRoute } = await resolveRoute(routeModulePaths, pathname);
233235
const requestHandlers = resolveRequestHandlers(
234236
serverPlugins,
235237
loadedRoute,
236238
req.method ?? 'GET',
237239
false,
238-
renderFn
240+
renderFn,
241+
isInternal
239242
);
240243

241244
return {

0 commit comments

Comments
 (0)