stx-router 0.2.97 → 0.2.98
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.js +1 -8
- package/dist/codegen.js +8 -22
- package/dist/file-router.js +27 -159
- package/dist/index.js +22 -1508
- package/dist/layout-metadata.js +4 -18
- package/dist/matcher.js +7 -19
- package/dist/middleware.js +44 -80
- package/dist/named-routes.js +12 -26
- package/dist/nested-layouts.js +4 -12
- package/dist/types.js +0 -3
- package/package.json +1 -1
- package/dist/client.js.map +0 -10
- package/dist/codegen.js.map +0 -10
- package/dist/file-router.js.map +0 -13
- package/dist/index.js.map +0 -17
- package/dist/layout-metadata.js.map +0 -10
- package/dist/matcher.js.map +0 -10
- package/dist/middleware.js.map +0 -10
- package/dist/named-routes.js.map +0 -10
- package/dist/nested-layouts.js.map +0 -10
- package/dist/types.js.map +0 -9
package/dist/client.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
// src/client.ts
|
|
3
|
-
function getRouterScript() {
|
|
1
|
+
export function getRouterScript() {
|
|
4
2
|
return `
|
|
5
3
|
;(function(){
|
|
6
4
|
'use strict';
|
|
@@ -876,8 +874,3 @@ else {
|
|
|
876
874
|
})();
|
|
877
875
|
`;
|
|
878
876
|
}
|
|
879
|
-
export {
|
|
880
|
-
getRouterScript
|
|
881
|
-
};
|
|
882
|
-
|
|
883
|
-
//# debugId=557E80E21BC18D0D64756E2164756E21
|
package/dist/codegen.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import path from "path";
|
|
5
|
-
function generateRouteTypes(routes, outputDir) {
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export function generateRouteTypes(routes, outputDir) {
|
|
6
4
|
const lines = [
|
|
7
5
|
"// Auto-generated by STX - do not edit",
|
|
8
6
|
'declare module "stx/routes" {',
|
|
@@ -18,36 +16,24 @@ function generateRouteTypes(routes, outputDir) {
|
|
|
18
16
|
lines.push(` '${route.pattern}': { ${paramsStr} }`);
|
|
19
17
|
}
|
|
20
18
|
lines.push(" }", "}", "");
|
|
21
|
-
fs.mkdirSync(outputDir, { recursive:
|
|
19
|
+
fs.mkdirSync(outputDir, { recursive: !0 });
|
|
22
20
|
fs.writeFileSync(path.join(outputDir, "route-types.d.ts"), lines.join(`
|
|
23
21
|
`));
|
|
24
22
|
}
|
|
25
|
-
function generateRouteManifest(routes, outputDir, pagesDir) {
|
|
26
|
-
const cwd = process.cwd()
|
|
27
|
-
const relativePages = path.relative(cwd, pagesDir);
|
|
28
|
-
const excludeDirs = ["components", "layouts", "partials"];
|
|
29
|
-
const pageRoutes = routes.filter((r) => !excludeDirs.some((dir) => r.pattern.startsWith(`/${dir}/`) || r.pattern === `/${dir}`));
|
|
30
|
-
const lines = [
|
|
23
|
+
export function generateRouteManifest(routes, outputDir, pagesDir) {
|
|
24
|
+
const cwd = process.cwd(), relativePages = path.relative(cwd, pagesDir), excludeDirs = ["components", "layouts", "partials"], pageRoutes = routes.filter((r) => !excludeDirs.some((dir) => r.pattern.startsWith(`/${dir}/`) || r.pattern === `/${dir}`)), lines = [
|
|
31
25
|
"// Auto-generated by stx \u2014 do not edit",
|
|
32
26
|
`// Source: ${relativePages || "pages"}/`,
|
|
33
27
|
"",
|
|
34
28
|
"export const routes = ["
|
|
35
29
|
];
|
|
36
30
|
for (const route of pageRoutes) {
|
|
37
|
-
const relFile = path.relative(cwd, route.filePath).replace(/\\/g, "/");
|
|
38
|
-
const params = JSON.stringify(route.params);
|
|
39
|
-
const layoutStr = route.layout ? `, layout: '${path.relative(cwd, route.layout).replace(/\\/g, "/")}'` : "";
|
|
31
|
+
const relFile = path.relative(cwd, route.filePath).replace(/\\/g, "/"), params = JSON.stringify(route.params), layoutStr = route.layout ? `, layout: '${path.relative(cwd, route.layout).replace(/\\/g, "/")}'` : "";
|
|
40
32
|
lines.push(` { pattern: '${route.pattern}', filePath: '${relFile}', isDynamic: ${route.isDynamic}, params: ${params}${layoutStr} },`);
|
|
41
33
|
}
|
|
42
34
|
lines.push("] as const", "");
|
|
43
35
|
lines.push("export type RouteName = typeof routes[number]['pattern']", "");
|
|
44
|
-
fs.mkdirSync(outputDir, { recursive:
|
|
36
|
+
fs.mkdirSync(outputDir, { recursive: !0 });
|
|
45
37
|
fs.writeFileSync(path.join(outputDir, "routes.ts"), lines.join(`
|
|
46
38
|
`));
|
|
47
39
|
}
|
|
48
|
-
export {
|
|
49
|
-
generateRouteTypes,
|
|
50
|
-
generateRouteManifest
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
//# debugId=C5615717ED79810164756E2164756E21
|
package/dist/file-router.js
CHANGED
|
@@ -1,144 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
const lines = [
|
|
7
|
-
"// Auto-generated by STX - do not edit",
|
|
8
|
-
'declare module "stx/routes" {',
|
|
9
|
-
" interface RouteMap {"
|
|
10
|
-
];
|
|
11
|
-
for (const route of routes) {
|
|
12
|
-
const paramEntries = [];
|
|
13
|
-
for (const p of route.params) {
|
|
14
|
-
const type = route.pattern.includes(`:${p}*`) ? "string[]" : "string";
|
|
15
|
-
paramEntries.push(`${p}: ${type}`);
|
|
16
|
-
}
|
|
17
|
-
const paramsStr = paramEntries.join("; ");
|
|
18
|
-
lines.push(` '${route.pattern}': { ${paramsStr} }`);
|
|
19
|
-
}
|
|
20
|
-
lines.push(" }", "}", "");
|
|
21
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
22
|
-
fs.writeFileSync(path.join(outputDir, "route-types.d.ts"), lines.join(`
|
|
23
|
-
`));
|
|
24
|
-
}
|
|
25
|
-
function generateRouteManifest(routes, outputDir, pagesDir) {
|
|
26
|
-
const cwd = process.cwd();
|
|
27
|
-
const relativePages = path.relative(cwd, pagesDir);
|
|
28
|
-
const excludeDirs = ["components", "layouts", "partials"];
|
|
29
|
-
const pageRoutes = routes.filter((r) => !excludeDirs.some((dir) => r.pattern.startsWith(`/${dir}/`) || r.pattern === `/${dir}`));
|
|
30
|
-
const lines = [
|
|
31
|
-
"// Auto-generated by stx \u2014 do not edit",
|
|
32
|
-
`// Source: ${relativePages || "pages"}/`,
|
|
33
|
-
"",
|
|
34
|
-
"export const routes = ["
|
|
35
|
-
];
|
|
36
|
-
for (const route of pageRoutes) {
|
|
37
|
-
const relFile = path.relative(cwd, route.filePath).replace(/\\/g, "/");
|
|
38
|
-
const params = JSON.stringify(route.params);
|
|
39
|
-
const layoutStr = route.layout ? `, layout: '${path.relative(cwd, route.layout).replace(/\\/g, "/")}'` : "";
|
|
40
|
-
lines.push(` { pattern: '${route.pattern}', filePath: '${relFile}', isDynamic: ${route.isDynamic}, params: ${params}${layoutStr} },`);
|
|
41
|
-
}
|
|
42
|
-
lines.push("] as const", "");
|
|
43
|
-
lines.push("export type RouteName = typeof routes[number]['pattern']", "");
|
|
44
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
45
|
-
fs.writeFileSync(path.join(outputDir, "routes.ts"), lines.join(`
|
|
46
|
-
`));
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// src/matcher.ts
|
|
50
|
-
import path2 from "path";
|
|
51
|
-
function filePathToPattern(filePath, pagesDir) {
|
|
52
|
-
const relativePath = path2.relative(pagesDir, filePath);
|
|
53
|
-
let route = relativePath.replace(/\.(stx|md)$/, "");
|
|
54
|
-
route = route.split(path2.sep).join("/");
|
|
55
|
-
if (route === "index") {
|
|
56
|
-
return "/";
|
|
57
|
-
}
|
|
58
|
-
if (route.endsWith("/index")) {
|
|
59
|
-
route = route.slice(0, -6);
|
|
60
|
-
}
|
|
61
|
-
route = route.replace(/\[\[([^\]]+)\]\]/g, ":$1?");
|
|
62
|
-
route = route.replace(/\[([^\]]+)\]/g, ":$1");
|
|
63
|
-
route = route.replace(/:\.\.\.([^/]+)/g, ":$1*");
|
|
64
|
-
return `/${route}`;
|
|
65
|
-
}
|
|
66
|
-
function patternToRegex(pattern) {
|
|
67
|
-
const params = [];
|
|
68
|
-
let regexStr = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
69
|
-
regexStr = regexStr.replace(/\/:([^/]+)\?/g, (_, name) => {
|
|
70
|
-
params.push(name);
|
|
71
|
-
return "(?:/([^/]+))?";
|
|
72
|
-
});
|
|
73
|
-
regexStr = regexStr.replace(/:([^/]+)\*/g, (_, name) => {
|
|
74
|
-
params.push(name);
|
|
75
|
-
return "(.+)";
|
|
76
|
-
});
|
|
77
|
-
regexStr = regexStr.replace(/:([^/]+)/g, (_, name) => {
|
|
78
|
-
params.push(name);
|
|
79
|
-
return "([^/]+)";
|
|
80
|
-
});
|
|
81
|
-
regexStr = `^${regexStr}$`;
|
|
82
|
-
return { regex: new RegExp(regexStr), params };
|
|
83
|
-
}
|
|
84
|
-
function matchRoute(pathname, routes) {
|
|
85
|
-
const normalizedPath = pathname === "" ? "/" : pathname;
|
|
86
|
-
for (const route of routes) {
|
|
87
|
-
const match = normalizedPath.match(route.regex);
|
|
88
|
-
if (match) {
|
|
89
|
-
const params = {};
|
|
90
|
-
route.params.forEach((name, index) => {
|
|
91
|
-
params[name] = match[index + 1];
|
|
92
|
-
});
|
|
93
|
-
return { route, params };
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// src/nested-layouts.ts
|
|
100
|
-
import fs2 from "fs";
|
|
101
|
-
import path3 from "path";
|
|
102
|
-
function resolveLayoutChain(routeFilePath, pagesDir) {
|
|
103
|
-
const layouts = [];
|
|
104
|
-
let dir = path3.dirname(routeFilePath);
|
|
105
|
-
while (dir.startsWith(pagesDir) || dir === pagesDir) {
|
|
106
|
-
const layoutFile = path3.join(dir, "_layout.stx");
|
|
107
|
-
if (fs2.existsSync(layoutFile)) {
|
|
108
|
-
layouts.unshift(layoutFile);
|
|
109
|
-
}
|
|
110
|
-
if (dir === pagesDir)
|
|
111
|
-
break;
|
|
112
|
-
dir = path3.dirname(dir);
|
|
113
|
-
}
|
|
114
|
-
return layouts;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// src/file-router.ts
|
|
118
|
-
import fs3 from "fs";
|
|
119
|
-
import path4 from "path";
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { generateRouteManifest, generateRouteTypes } from "./codegen.js";
|
|
4
|
+
import { filePathToPattern, patternToRegex } from "./matcher.js";
|
|
5
|
+
import { resolveLayoutChain } from "./nested-layouts.js";
|
|
120
6
|
function scanDirectory(dir, extensions) {
|
|
121
7
|
const files = [];
|
|
122
|
-
if (!
|
|
8
|
+
if (!fs.existsSync(dir))
|
|
123
9
|
return files;
|
|
124
|
-
}
|
|
125
|
-
const entries = fs3.readdirSync(dir, { withFileTypes: true });
|
|
10
|
+
const entries = fs.readdirSync(dir, { withFileTypes: !0 });
|
|
126
11
|
for (const entry of entries) {
|
|
127
|
-
const fullPath =
|
|
12
|
+
const fullPath = path.join(dir, entry.name);
|
|
128
13
|
if (entry.isDirectory()) {
|
|
129
|
-
if (entry.name.startsWith("."))
|
|
14
|
+
if (entry.name.startsWith("."))
|
|
130
15
|
continue;
|
|
131
|
-
}
|
|
132
16
|
files.push(...scanDirectory(fullPath, extensions));
|
|
133
17
|
} else if (entry.isFile()) {
|
|
134
|
-
const ext =
|
|
18
|
+
const ext = path.extname(entry.name);
|
|
135
19
|
if (extensions.includes(ext)) {
|
|
136
|
-
if (entry.name.startsWith("_") && entry.name !== "_layout.stx")
|
|
20
|
+
if (entry.name.startsWith("_") && entry.name !== "_layout.stx")
|
|
137
21
|
continue;
|
|
138
|
-
|
|
139
|
-
if (entry.name === "_layout.stx") {
|
|
22
|
+
if (entry.name === "_layout.stx")
|
|
140
23
|
continue;
|
|
141
|
-
}
|
|
142
24
|
files.push(fullPath);
|
|
143
25
|
}
|
|
144
26
|
}
|
|
@@ -146,7 +28,7 @@ function scanDirectory(dir, extensions) {
|
|
|
146
28
|
return files;
|
|
147
29
|
}
|
|
148
30
|
|
|
149
|
-
class Router {
|
|
31
|
+
export class Router {
|
|
150
32
|
routes;
|
|
151
33
|
pagesDir;
|
|
152
34
|
pagesDirs;
|
|
@@ -154,10 +36,9 @@ class Router {
|
|
|
154
36
|
constructor(baseDir, config = {}) {
|
|
155
37
|
this.config = config;
|
|
156
38
|
const rawDirs = config.pagesDirs && config.pagesDirs.length > 0 ? config.pagesDirs : [config.pagesDir || "pages"];
|
|
157
|
-
this.pagesDirs = rawDirs.map((d) =>
|
|
39
|
+
this.pagesDirs = rawDirs.map((d) => path.resolve(baseDir, d));
|
|
158
40
|
this.pagesDir = this.pagesDirs[0];
|
|
159
|
-
const extensions = config.extensions || [".stx"];
|
|
160
|
-
const seenPatterns = new Set;
|
|
41
|
+
const extensions = config.extensions || [".stx"], seenPatterns = new Set;
|
|
161
42
|
this.routes = [];
|
|
162
43
|
for (const dir of this.pagesDirs) {
|
|
163
44
|
const files = scanDirectory(dir, extensions);
|
|
@@ -166,15 +47,14 @@ class Router {
|
|
|
166
47
|
if (seenPatterns.has(pattern))
|
|
167
48
|
continue;
|
|
168
49
|
seenPatterns.add(pattern);
|
|
169
|
-
const { regex, params } = patternToRegex(pattern);
|
|
170
|
-
const layouts = config.layouts !== false ? resolveLayoutChain(filePath, dir) : [];
|
|
50
|
+
const { regex, params } = patternToRegex(pattern), layouts = config.layouts !== !1 ? resolveLayoutChain(filePath, dir) : [];
|
|
171
51
|
this.routes.push({
|
|
172
52
|
pattern,
|
|
173
53
|
regex,
|
|
174
54
|
params,
|
|
175
55
|
filePath,
|
|
176
56
|
isDynamic: params.length > 0,
|
|
177
|
-
layout: layouts.length > 0 ? layouts[layouts.length - 1] :
|
|
57
|
+
layout: layouts.length > 0 ? layouts[layouts.length - 1] : void 0
|
|
178
58
|
});
|
|
179
59
|
}
|
|
180
60
|
}
|
|
@@ -183,13 +63,12 @@ class Router {
|
|
|
183
63
|
return -1;
|
|
184
64
|
if (a.isDynamic && !b.isDynamic)
|
|
185
65
|
return 1;
|
|
186
|
-
const aSegments = a.pattern.split("/").length;
|
|
187
|
-
const bSegments = b.pattern.split("/").length;
|
|
66
|
+
const aSegments = a.pattern.split("/").length, bSegments = b.pattern.split("/").length;
|
|
188
67
|
if (aSegments !== bSegments)
|
|
189
68
|
return bSegments - aSegments;
|
|
190
69
|
return a.pattern.localeCompare(b.pattern);
|
|
191
70
|
});
|
|
192
|
-
const stxDir =
|
|
71
|
+
const stxDir = path.join(baseDir, ".stx");
|
|
193
72
|
generateRouteTypes(this.routes, stxDir);
|
|
194
73
|
generateRouteManifest(this.routes, stxDir, this.pagesDir);
|
|
195
74
|
}
|
|
@@ -212,11 +91,9 @@ class Router {
|
|
|
212
91
|
if (!route)
|
|
213
92
|
return "#not-found";
|
|
214
93
|
let resolved = route.pattern;
|
|
215
|
-
if (params)
|
|
216
|
-
for (const [key, value] of Object.entries(params))
|
|
94
|
+
if (params)
|
|
95
|
+
for (const [key, value] of Object.entries(params))
|
|
217
96
|
resolved = resolved.replace(`:${key}`, encodeURIComponent(value));
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
97
|
return resolved;
|
|
221
98
|
}
|
|
222
99
|
addRoute(route) {
|
|
@@ -233,30 +110,21 @@ class Router {
|
|
|
233
110
|
return route.layout ?? null;
|
|
234
111
|
}
|
|
235
112
|
}
|
|
236
|
-
function createRouter(baseDir, config) {
|
|
113
|
+
export function createRouter(baseDir, config) {
|
|
237
114
|
return new Router(baseDir, config);
|
|
238
115
|
}
|
|
239
|
-
function findErrorPage(pagesDir, statusCode) {
|
|
116
|
+
export function findErrorPage(pagesDir, statusCode) {
|
|
240
117
|
const candidates = [`${statusCode}.stx`, "error.stx"];
|
|
241
118
|
for (const candidate of candidates) {
|
|
242
|
-
const filePath =
|
|
243
|
-
if (
|
|
119
|
+
const filePath = path.join(pagesDir, candidate);
|
|
120
|
+
if (fs.existsSync(filePath))
|
|
244
121
|
return filePath;
|
|
245
122
|
}
|
|
246
123
|
return null;
|
|
247
124
|
}
|
|
248
|
-
function formatRoutes(routes, baseDir) {
|
|
125
|
+
export function formatRoutes(routes, baseDir) {
|
|
249
126
|
return routes.map((route) => {
|
|
250
|
-
const relativePath =
|
|
251
|
-
const dynamicLabel = route.isDynamic ? " (dynamic)" : "";
|
|
127
|
+
const relativePath = path.relative(baseDir, route.filePath), dynamicLabel = route.isDynamic ? " (dynamic)" : "";
|
|
252
128
|
return `${route.pattern} \u2192 ${relativePath}${dynamicLabel}`;
|
|
253
129
|
});
|
|
254
130
|
}
|
|
255
|
-
export {
|
|
256
|
-
formatRoutes,
|
|
257
|
-
findErrorPage,
|
|
258
|
-
createRouter,
|
|
259
|
-
Router
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
//# debugId=288E1FC95396709A64756E2164756E21
|