@jay-framework/stack-route-scanner 0.25.1 → 0.25.2

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/index.d.mts CHANGED
@@ -33,6 +33,24 @@ type JayRoute = {
33
33
  devOnly?: boolean;
34
34
  };
35
35
  type JayRoutes = JayRoute[];
36
+ /**
37
+ * A reserved top-level error-page folder (Design Log #218). `pages/404/`, `pages/5xx/`, etc. are
38
+ * not routable; they are collected here instead of in {@link JayRoutes}.
39
+ */
40
+ type JayErrorPage = {
41
+ /** HTTP status token: exact (`404`, `502`) or family (`4xx`, `5xx`). */
42
+ token: string;
43
+ jayHtmlPath: string;
44
+ compPath: string;
45
+ };
46
+ type JayErrorPages = JayErrorPage[];
47
+ /** Result of a route scan: navigable routes plus reserved error pages (DL#218). */
48
+ interface ScanResult {
49
+ routes: JayRoutes;
50
+ errorPages: JayErrorPages;
51
+ }
52
+ /** True when a top-level folder name is a reserved HTTP status token (DL#218). */
53
+ declare function isStatusToken(name: string): boolean;
36
54
  interface ScanFilesOptions {
37
55
  jayHtmlFilename: string;
38
56
  compFilename: string;
@@ -55,8 +73,13 @@ declare function createRoute(routePath: string, jayHtmlPath: string, compPath: s
55
73
  devOnly?: boolean;
56
74
  clientCompPath?: string;
57
75
  }): JayRoute;
58
- declare function scanRoutes(baseDir: string, options: ScanFilesOptions): Promise<JayRoutes>;
76
+ /**
77
+ * Scan a pages directory for navigable routes and reserved error pages (DL#218).
78
+ *
79
+ * Returns `{ routes, errorPages }`. Callers that only need routes destructure `{ routes }`.
80
+ */
81
+ declare function scanRoutes(baseDir: string, options: ScanFilesOptions): Promise<ScanResult>;
59
82
 
60
83
  declare function routeToExpressRoute(route: JayRoute): string;
61
84
 
62
- export { type JayRoute, type JayRouteParam, JayRouteParamType, type JayRouteSegment, type JayRoutes, type ScanFilesOptions, createRoute, parseRouteSegments, routeToExpressRoute, scanRoutes, sortRoutesByPriority };
85
+ export { type JayErrorPage, type JayErrorPages, type JayRoute, type JayRouteParam, JayRouteParamType, type JayRouteSegment, type JayRoutes, type ScanFilesOptions, type ScanResult, createRoute, isStatusToken, parseRouteSegments, routeToExpressRoute, scanRoutes, sortRoutesByPriority };
package/dist/index.mjs CHANGED
@@ -9,6 +9,10 @@ var JayRouteParamType = /* @__PURE__ */ ((JayRouteParamType2) => {
9
9
  JayRouteParamType2[JayRouteParamType2["optional"] = 2] = "optional";
10
10
  return JayRouteParamType2;
11
11
  })(JayRouteParamType || {});
12
+ const STATUS_TOKEN = /^([1-5][0-9]{2}|[1-5]xx)$/;
13
+ function isStatusToken(name) {
14
+ return STATUS_TOKEN.test(name);
15
+ }
12
16
  const PARSE_PARAM = /^\[(\[)?(\.\.\.)?([^\]]+)\]?\]$/;
13
17
  function convertToRoutePath(BASE_DIR, jayHtmlPath, { jayHtmlFilename, compFilename }) {
14
18
  let rawRoute = jayHtmlPath.replace(BASE_DIR, "").replace(`/${jayHtmlFilename}`, "").replace("\\", "/");
@@ -29,14 +33,22 @@ function convertToRoutePath(BASE_DIR, jayHtmlPath, { jayHtmlFilename, compFilena
29
33
  return { segments, jayHtmlPath, compPath, rawRoute };
30
34
  }
31
35
  const IGNORED_DIRS = /* @__PURE__ */ new Set(["build", "node_modules", "dist", ".git"]);
32
- async function scanDirectory(BASE_DIR, directory, options) {
36
+ async function scanDirectory(BASE_DIR, directory, options, depth = 0) {
33
37
  let routes = [];
38
+ let errorPages = [];
34
39
  const items = await promises.readdir(directory, { withFileTypes: true });
35
40
  for (const item of items) {
36
41
  const fullPath = path.join(directory, item.name);
37
42
  if (item.isDirectory()) {
38
43
  if (IGNORED_DIRS.has(item.name)) continue;
39
- routes = [...routes, ...await scanDirectory(BASE_DIR, fullPath, options)];
44
+ if (depth === 0 && isStatusToken(item.name)) {
45
+ const errorPage = toErrorPage(item.name, fullPath, options);
46
+ if (errorPage) errorPages.push(errorPage);
47
+ continue;
48
+ }
49
+ const nested = await scanDirectory(BASE_DIR, fullPath, options, depth + 1);
50
+ routes = [...routes, ...nested.routes];
51
+ errorPages = [...errorPages, ...nested.errorPages];
40
52
  } else if (item.name === options.jayHtmlFilename) {
41
53
  const route = convertToRoutePath(BASE_DIR, fullPath, options);
42
54
  const { params, validations } = await parseHeadlessProps(fullPath);
@@ -69,7 +81,14 @@ async function scanDirectory(BASE_DIR, directory, options) {
69
81
  routes.push(route);
70
82
  }
71
83
  }
72
- return routes;
84
+ return { routes, errorPages };
85
+ }
86
+ function toErrorPage(token, folderPath, { jayHtmlFilename, compFilename }) {
87
+ const jayHtmlPath = path.join(folderPath, jayHtmlFilename);
88
+ if (!existsSync(jayHtmlPath)) return void 0;
89
+ const candidateCompPath = path.join(folderPath, compFilename);
90
+ const compPath = existsSync(candidateCompPath) ? candidateCompPath : "";
91
+ return { token, jayHtmlPath, compPath };
73
92
  }
74
93
  function getSegmentPriority(segment) {
75
94
  if (typeof segment === "string") return 0;
@@ -174,7 +193,7 @@ function createRoute(routePath, jayHtmlPath, compPath, componentExport, options)
174
193
  }
175
194
  async function scanRoutes(baseDir, options) {
176
195
  const BASE_DIR = path.resolve(baseDir);
177
- const routes = await scanDirectory(BASE_DIR, BASE_DIR, options);
196
+ const { routes, errorPages } = await scanDirectory(BASE_DIR, BASE_DIR, options);
178
197
  const sortedRoutes = sortRoutesByPriority(routes);
179
198
  const routesWithParams = sortedRoutes.filter((r) => r.inferredParams);
180
199
  if (routesWithParams.length > 0) {
@@ -183,7 +202,7 @@ async function scanRoutes(baseDir, options) {
183
202
  getLogger().info(` ${route.rawRoute} → ${JSON.stringify(route.inferredParams)}`);
184
203
  }
185
204
  }
186
- return sortedRoutes;
205
+ return { routes: sortedRoutes, errorPages };
187
206
  }
188
207
  function routeToExpressRoute(route) {
189
208
  const parts = [];
@@ -203,6 +222,7 @@ function routeToExpressRoute(route) {
203
222
  export {
204
223
  JayRouteParamType,
205
224
  createRoute,
225
+ isStatusToken,
206
226
  parseRouteSegments,
207
227
  routeToExpressRoute,
208
228
  scanRoutes,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/stack-route-scanner",
3
- "version": "0.25.1",
3
+ "version": "0.25.2",
4
4
  "license": "Apache-2.0",
5
5
  "main": "dist/index.mjs",
6
6
  "types": "dist/index.d.mts",
@@ -9,7 +9,7 @@
9
9
  "readme.md"
10
10
  ],
11
11
  "dependencies": {
12
- "@jay-framework/logger": "^0.25.1",
12
+ "@jay-framework/logger": "^0.25.2",
13
13
  "node-html-parser": "^6.1.12",
14
14
  "yaml": "^2.3.4"
15
15
  },
@@ -25,7 +25,7 @@
25
25
  "test:watch": "vitest"
26
26
  },
27
27
  "devDependencies": {
28
- "@jay-framework/dev-environment": "^0.25.1",
28
+ "@jay-framework/dev-environment": "^0.25.2",
29
29
  "@types/node": "^20.11.5",
30
30
  "nodemon": "^3.0.3",
31
31
  "replace-in-file": "^7.1.0",