stx-router 0.2.97 → 0.2.99

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 CHANGED
@@ -1,6 +1,4 @@
1
- // @bun
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
- // @bun
2
- // src/codegen.ts
3
- import fs from "fs";
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: true });
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: true });
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
@@ -1,144 +1,26 @@
1
- // @bun
2
- // src/codegen.ts
3
- import fs from "fs";
4
- import path from "path";
5
- function generateRouteTypes(routes, outputDir) {
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 (!fs3.existsSync(dir)) {
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 = path4.join(dir, entry.name);
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 = path4.extname(entry.name);
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) => path4.resolve(baseDir, 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] : undefined
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 = path4.join(baseDir, ".stx");
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 = path4.join(pagesDir, candidate);
243
- if (fs3.existsSync(filePath))
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 = path4.relative(baseDir, route.filePath);
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