Skip to content

Commit f7e57a0

Browse files
committed
chore(router): lint++
1 parent 759d040 commit f7e57a0

File tree

8 files changed

+84
-87
lines changed

8 files changed

+84
-87
lines changed

packages/qwik-router/src/buildtime/build.ts

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { addError, addWarning } from '../utils/format';
22
import { resolveSourceFiles } from './routing/resolve-source-file';
3+
import { routeSortCompare } from './routing/sort-routes';
34
import { walkRoutes } from './routing/walk-routes-dir';
45
import { walkServerPlugins } from './routing/walk-server-plugins';
56
import type { RoutingContext, BuiltRoute, RewriteRouteOption } from './types';
@@ -21,35 +22,29 @@ export async function parseRoutesDir(ctx: RoutingContext) {
2122
}
2223
}
2324

24-
export async function updateRoutingContext(ctx: RoutingContext) {
25-
if (!ctx.activeBuild) {
26-
ctx.activeBuild = new Promise<void>((resolve, reject) => {
27-
walkServerPlugins(ctx.opts)
28-
.then((serverPlugins) => {
29-
ctx.serverPlugins = serverPlugins;
30-
return walkRoutes(ctx.opts.routesDir);
31-
})
32-
.then((sourceFiles) => {
33-
const resolved = resolveSourceFiles(ctx.opts, sourceFiles);
34-
rewriteRoutes(ctx, resolved);
35-
ctx.layouts = resolved.layouts;
36-
ctx.routes = resolved.routes;
37-
ctx.entries = resolved.entries;
38-
ctx.serviceWorkers = resolved.serviceWorkers;
39-
ctx.menus = resolved.menus;
40-
resolve();
41-
}, reject)
42-
.finally(() => {
43-
ctx.activeBuild = null;
44-
});
45-
});
46-
}
25+
export function updateRoutingContext(ctx: RoutingContext) {
26+
ctx.activeBuild ||= _updateRoutingContext(ctx).finally(() => {
27+
ctx.activeBuild = null;
28+
});
4729
return ctx.activeBuild;
4830
}
4931

50-
function rewriteRoutes(ctx: RoutingContext, resolvedFiles: ReturnType<typeof resolveSourceFiles>) {
51-
if (!ctx.opts.rewriteRoutes || !resolvedFiles.routes) {
52-
return;
32+
async function _updateRoutingContext(ctx: RoutingContext) {
33+
const serverPlugins = await walkServerPlugins(ctx.opts);
34+
const sourceFiles = await walkRoutes(ctx.opts.routesDir);
35+
const resolved = resolveSourceFiles(ctx.opts, sourceFiles);
36+
resolved.routes = rewriteRoutes(ctx, resolved.routes);
37+
ctx.serverPlugins = serverPlugins;
38+
ctx.layouts = resolved.layouts;
39+
ctx.routes = resolved.routes;
40+
ctx.entries = resolved.entries;
41+
ctx.serviceWorkers = resolved.serviceWorkers;
42+
ctx.menus = resolved.menus;
43+
}
44+
45+
function rewriteRoutes(ctx: RoutingContext, routes: BuiltRoute[]) {
46+
if (!ctx.opts.rewriteRoutes) {
47+
return routes;
5348
}
5449

5550
const translatedRoutes: BuiltRoute[] = [];
@@ -60,7 +55,7 @@ function rewriteRoutes(ctx: RoutingContext, resolvedFiles: ReturnType<typeof res
6055

6156
segmentsToTranslate = Array.from(new Set(segmentsToTranslate));
6257

63-
resolvedFiles.routes.forEach((route) => {
58+
routes.forEach((route) => {
6459
// always push the original route
6560
translatedRoutes.push(route);
6661

@@ -91,7 +86,7 @@ function rewriteRoutes(ctx: RoutingContext, resolvedFiles: ReturnType<typeof res
9186
}
9287
});
9388

94-
resolvedFiles.routes = translatedRoutes;
89+
return translatedRoutes.sort(routeSortCompare);
9590
}
9691

9792
function translateRoute(

packages/qwik-router/src/buildtime/routing/sort-routes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { BuiltRoute } from '../types';
22

3+
/** Sort routes by pathname, then by extension. Longer routes are sorted first. */
34
export function routeSortCompare(a: BuiltRoute, b: BuiltRoute) {
45
const maxSegments = Math.max(a.segments.length, b.segments.length);
56

packages/qwik-router/src/middleware/request-handler/middleware.request-handler.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type { Loader as Loader_2 } from '@qwik.dev/router';
1111
import type { QwikCityPlan } from '@qwik.dev/router';
1212
import type { QwikIntrinsicElements } from '@qwik.dev/core';
1313
import type { QwikRouterConfig } from '@qwik.dev/router';
14+
import { RedirectMessage as RedirectMessage_2 } from '@qwik.dev/router/middleware/request-handler';
1415
import type { Render } from '@qwik.dev/core/server';
1516
import type { RenderOptions } from '@qwik.dev/core/server';
1617
import { RequestEvent as RequestEvent_2 } from '@qwik.dev/router/middleware/request-handler';

packages/qwik-router/src/middleware/request-handler/request-event.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,11 @@ export function createRequestEvent(
141141
return exit();
142142
};
143143

144-
const exit = () => {
144+
const exit = <T extends AbortMessage | RedirectMessage | RewriteMessage>(
145+
message: T = new AbortMessage() as T
146+
) => {
145147
routeModuleIndex = ABORT_INDEX;
146-
return new AbortMessage();
148+
return message;
147149
};
148150

149151
const loaders: Record<string, ValueOrPromise<unknown> | undefined> = {};
@@ -250,9 +252,7 @@ export function createRequestEvent(
250252
if (statusCode > 301) {
251253
headers.set('Cache-Control', 'no-store');
252254
}
253-
254-
routeModuleIndex = ABORT_INDEX;
255-
return new RedirectMessage();
255+
return exit(new RedirectMessage());
256256
},
257257

258258
rewrite: (pathname: string) => {
@@ -261,7 +261,7 @@ export function createRequestEvent(
261261
throw new Error('Rewrite does not support absolute urls');
262262
}
263263
sharedMap.set(RequestEvIsRewrite, true);
264-
return new RewriteMessage(pathname.replace(/\/+/g, '/'));
264+
return exit(new RewriteMessage(pathname.replace(/\/+/g, '/')));
265265
},
266266

267267
defer: (returnData) => {

packages/qwik-router/src/middleware/request-handler/request-handler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { getRouteMatchPathname, runQwikRouter, type QwikRouterRun } from './user
1111
*/
1212
let qwikRouterConfigActual: QwikRouterConfig;
1313
/**
14-
* The request handler for QwikRouter. Called by every integration.
14+
* The request handler for QwikRouter. Called by every adapter.
1515
*
1616
* @public
1717
*/

packages/qwik-router/src/middleware/request-handler/types.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -590,13 +590,6 @@ export interface CookieValue {
590590
number: () => number;
591591
}
592592

593-
/** @public */
594-
export interface QwikSerializer {
595-
_deserialize: typeof _deserialize;
596-
_serialize: typeof _serialize;
597-
_verifySerializable: typeof _verifySerializable;
598-
}
599-
600593
/** @public */
601594
export type HttpMethod =
602595
| 'GET'

packages/qwik-router/src/middleware/request-handler/user-response.ts

Lines changed: 47 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { getErrorHtml } from './error-handler';
99
import { createRequestEvent, getRequestMode, type RequestEventInternal } from './request-event';
1010
import { encoder } from './resolve-request-handlers';
1111
import type { ServerRequestEvent, StatusCodes } from './types';
12+
1213
// Import separately to avoid duplicate imports in the vite dev server
1314
import {
1415
AbortMessage,
@@ -18,9 +19,21 @@ import {
1819
} from '@qwik.dev/router/middleware/request-handler';
1920

2021
export interface QwikRouterRun<T> {
22+
/**
23+
* The response to the request, if any. If there is no response, there might have been an error,
24+
* or the request was aborted.
25+
*/
2126
response: Promise<T | null>;
2227
requestEv: RequestEvent;
23-
completion: Promise<unknown>;
28+
/**
29+
* Promise for the completion of the request.
30+
*
31+
* If it returns a RedirectMessage, it means the request must be redirected.
32+
*
33+
* If it returns an Error, it means there was an error, and if possible, the response already
34+
* includes the error. The error is informational only.
35+
*/
36+
completion: Promise<RedirectMessage | Error | undefined>;
2437
}
2538

2639
let asyncStore: AsyncStore | undefined;
@@ -67,7 +80,7 @@ async function runNext(
6780
requestEv: RequestEventInternal,
6881
rebuildRouteInfo: RebuildRouteInfoInternal,
6982
resolve: (value: any) => void
70-
) {
83+
): Promise<Error | RedirectMessage | undefined> {
7184
try {
7285
const isValidURL = (url: URL) => new URL(url.pathname + url.search, url);
7386
isValidURL(requestEv.originalUrl);
@@ -90,9 +103,10 @@ async function runNext(
90103
if (e instanceof RedirectMessage) {
91104
const stream = requestEv.getWritableStream();
92105
await stream.close();
106+
return e;
93107
} else if (e instanceof RewriteMessage) {
94108
if (rewriteAttempt > 50) {
95-
throw new Error(`Infinite rewrite loop`);
109+
return new Error(`Infinite rewrite loop`);
96110
}
97111

98112
rewriteAttempt += 1;
@@ -101,48 +115,47 @@ async function runNext(
101115
const { loadedRoute, requestHandlers } = await rebuildRouteInfo(url);
102116
requestEv.resetRoute(loadedRoute, requestHandlers, url);
103117
return await _runNext();
104-
} else if (e instanceof ServerError) {
105-
if (!requestEv.headersSent) {
106-
const status = e.status as StatusCodes;
107-
const accept = requestEv.request.headers.get('Accept');
108-
if (accept && !accept.includes('text/html')) {
109-
requestEv.headers.set('Content-Type', 'application/qwik-json');
110-
requestEv.send(status, await _serialize([e.data]));
111-
} else {
112-
const html = getErrorHtml(e.status, e.data);
113-
requestEv.html(status, html);
114-
}
118+
} else if (e instanceof AbortMessage) {
119+
return;
120+
} else if (e instanceof ServerError && !requestEv.headersSent) {
121+
const status = e.status as StatusCodes;
122+
const accept = requestEv.request.headers.get('Accept');
123+
if (accept && !accept.includes('text/html')) {
124+
requestEv.headers.set('Content-Type', 'application/qwik-json');
125+
requestEv.send(status, await _serialize([e.data]));
126+
} else {
127+
// TODO render the custom error route
128+
requestEv.html(status, getErrorHtml(status, e.data));
115129
}
116-
} else if (!(e instanceof AbortMessage)) {
117-
if (getRequestMode(requestEv) !== 'dev') {
118-
try {
119-
if (!requestEv.headersSent) {
120-
requestEv.headers.set('content-type', 'text/html; charset=utf-8');
121-
requestEv.cacheControl({ noCache: true });
122-
requestEv.status(500);
123-
}
124-
const stream = requestEv.getWritableStream();
125-
if (!stream.locked) {
126-
const writer = stream.getWriter();
127-
await writer.write(encoder.encode(getErrorHtml(500, 'Internal Server Error')));
128-
await writer.close();
129-
}
130-
} catch {
131-
console.error('Unable to render error page');
130+
return e;
131+
}
132+
if (getRequestMode(requestEv) !== 'dev') {
133+
try {
134+
if (!requestEv.headersSent) {
135+
requestEv.headers.set('content-type', 'text/html; charset=utf-8');
136+
requestEv.cacheControl({ noCache: true });
137+
requestEv.status(500);
132138
}
139+
const stream = requestEv.getWritableStream();
140+
if (!stream.locked) {
141+
const writer = stream.getWriter();
142+
await writer.write(encoder.encode(getErrorHtml(500, 'Internal Server Error')));
143+
await writer.close();
144+
}
145+
} catch {
146+
console.error('Unable to render error page');
133147
}
134-
135-
return e;
136148
}
137-
}
138149

139-
return undefined;
150+
return e as Error;
151+
}
140152
}
141153

142154
try {
143155
return await _runNext();
144156
} finally {
145157
if (!requestEv.isDirty()) {
158+
// The request didn't get handled, so we need to resolve with null.
146159
resolve(null);
147160
}
148161
}

packages/qwik-router/src/utils/fs.ts

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export function normalizePath(path: string) {
7979
export function normalizePathSlash(path: string) {
8080
// MIT https://github.com/sindresorhus/slash/blob/main/license
8181
// Convert Windows backslash paths to slash paths: foo\\bar ➔ foo/bar
82-
const isExtendedLengthPath = /^\\\\\?\\/.test(path);
82+
const isExtendedLengthPath = path.startsWith('\\\\?\\');
8383
const hasNonAscii = /[^\u0000-\u0080]+/.test(path); // eslint-disable-line no-control-regex
8484

8585
if (isExtendedLengthPath || hasNonAscii) {
@@ -139,17 +139,17 @@ export function createFileId(
139139
const PAGE_MODULE_EXTS: { [type: string]: boolean } = {
140140
'.tsx': true,
141141
'.jsx': true,
142-
};
142+
} as const;
143143

144144
const MODULE_EXTS: { [type: string]: boolean } = {
145145
'.ts': true,
146146
'.js': true,
147-
};
147+
} as const;
148148

149149
const MARKDOWN_EXTS: { [type: string]: boolean } = {
150150
'.md': true,
151151
'.mdx': true,
152-
};
152+
} as const;
153153

154154
export function isIndexModule(extlessName: string) {
155155
return /^index(|!|@.+)$/.test(extlessName);
@@ -192,13 +192,7 @@ export function isEntryName(extlessName: string) {
192192
}
193193

194194
export function isErrorName(extlessName: string) {
195-
try {
196-
const statusCode = parseInt(extlessName, 10);
197-
return statusCode >= 400 && statusCode <= 599;
198-
} catch (e) {
199-
//
200-
}
201-
return false;
195+
return /^[45][0-9]{2}$/.test(extlessName);
202196
}
203197

204198
export function isGroupedLayoutName(dirName: string, warn = true) {

0 commit comments

Comments
 (0)