@jslop/router 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 p-arndt
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # @jslop/router
2
+
3
+ File-system route scanning and URL matching for JSlop apps. Pure functions, no runtime dependency.
4
+
5
+ ```bash
6
+ pnpm add @jslop/router
7
+ ```
8
+
9
+ ## API
10
+
11
+ ```ts
12
+ import { scanRoutes, matchRoute, type RouteDef, type MatchResult } from "@jslop/router";
13
+
14
+ const routes = await scanRoutes("/abs/path/to/src/routes");
15
+ // → RouteDef[], sorted most-specific-first
16
+
17
+ const match = matchRoute("/posts/hello-world", routes);
18
+ // → { route, params } | null
19
+ if (match) {
20
+ console.log(match.route.pattern); // "/posts/[slug]"
21
+ console.log(match.route.relPath); // "posts/[slug].jslop"
22
+ console.log(match.params); // { slug: "hello-world" }
23
+ }
24
+ ```
25
+
26
+ ## Conventions
27
+
28
+ | File path | URL pattern |
29
+ |----------------------------------|---------------------|
30
+ | `routes/index.jslop` | `/` |
31
+ | `routes/about.jslop` | `/about` |
32
+ | `routes/dashboard/index.jslop` | `/dashboard` |
33
+ | `routes/posts/[slug].jslop` | `/posts/:slug` |
34
+
35
+ - `index` segments are stripped from the URL.
36
+ - `[bracketed]` segments are dynamic params. The name inside the brackets is the key returned in `match.params`.
37
+ - Static segments outrank dynamic ones during matching (specificity-first sort).
38
+ - Trailing slashes are normalized away.
39
+
40
+ ## Not yet supported
41
+
42
+ > [!WARNING]
43
+ > The router doesn't yet support:
44
+ >
45
+ > - Catch-all `[...slug]`
46
+ > - Optional segments
47
+ > - Layout / `<Outlet />` conventions
48
+ > - Conventional `_404.jslop` / `_error.jslop`
49
+
50
+ See [`TODO.md`](../../TODO.md) and [docs/routing.md](../../docs/routing.md).
@@ -0,0 +1,56 @@
1
+ export interface RouteDef {
2
+ /** URL pattern with [param] placeholders, e.g. "/posts/[slug]" */
3
+ pattern: string;
4
+ /** Names of bracketed params in pattern order */
5
+ paramNames: string[];
6
+ /** Path to the source .jslop file relative to the routes dir */
7
+ relPath: string;
8
+ /** Compiled JS path expected (e.g. routes/posts/[slug].compiled.mjs) */
9
+ compiledRelPath: string;
10
+ /** A safe JS identifier derived from relPath, e.g. "Posts__slug" */
11
+ identifier: string;
12
+ /**
13
+ * Layout files wrapping this route, outermost first. Each entry is a rel
14
+ * path to a `_layout.jslop` file in this route's directory chain.
15
+ */
16
+ layouts: string[];
17
+ }
18
+ export interface LayoutDef {
19
+ /** Path relative to routes dir, e.g. "dashboard/_layout.jslop" */
20
+ relPath: string;
21
+ /**
22
+ * Directory this layout owns (forward-slash, "" for routes root). Applies
23
+ * to any route whose relPath is under this directory.
24
+ */
25
+ dir: string;
26
+ identifier: string;
27
+ }
28
+ export interface NotFoundDef {
29
+ /** Path relative to routes dir, e.g. "_404.jslop" */
30
+ relPath: string;
31
+ identifier: string;
32
+ /** Layout chain (outermost first) applying to the 404 page. */
33
+ layouts: string[];
34
+ }
35
+ export interface RouteManifest {
36
+ routes: RouteDef[];
37
+ layouts: LayoutDef[];
38
+ notFound: NotFoundDef | null;
39
+ }
40
+ /**
41
+ * Walk a routes directory and produce a deterministic, more-specific-first
42
+ * list of routes plus the layout chain that wraps each one.
43
+ *
44
+ * Conventions:
45
+ * - Files named `_layout.jslop` are layouts; they apply to every route under
46
+ * the directory they live in.
47
+ * - A file named `_404.jslop` at the routes root is the catch-all page rendered
48
+ * when no route matches.
49
+ * - Any other file starting with `_` is reserved and ignored.
50
+ */
51
+ export declare function scanRoutes(routesDir: string): Promise<RouteManifest>;
52
+ export interface MatchResult {
53
+ route: RouteDef;
54
+ params: Record<string, string>;
55
+ }
56
+ export declare function matchRoute(url: string, routes: RouteDef[]): MatchResult | null;
package/dist/index.js ADDED
@@ -0,0 +1,161 @@
1
+ import { readdir, stat } from "node:fs/promises";
2
+ import { join, relative, sep, posix, extname } from "node:path";
3
+ /**
4
+ * Walk a routes directory and produce a deterministic, more-specific-first
5
+ * list of routes plus the layout chain that wraps each one.
6
+ *
7
+ * Conventions:
8
+ * - Files named `_layout.jslop` are layouts; they apply to every route under
9
+ * the directory they live in.
10
+ * - A file named `_404.jslop` at the routes root is the catch-all page rendered
11
+ * when no route matches.
12
+ * - Any other file starting with `_` is reserved and ignored.
13
+ */
14
+ export async function scanRoutes(routesDir) {
15
+ const routes = [];
16
+ const layouts = [];
17
+ let notFound = null;
18
+ await walk(routesDir, routesDir, routes, layouts, (nf) => {
19
+ notFound = nf;
20
+ });
21
+ // Resolve layout chain for each route: every layout whose `dir` is a
22
+ // prefix of the route's directory applies, outermost first.
23
+ const layoutsByDir = new Map();
24
+ for (const l of layouts)
25
+ layoutsByDir.set(l.dir, l);
26
+ const chainFor = (rel) => {
27
+ const dir = parentDir(rel);
28
+ const segs = dir === "" ? [] : dir.split("/");
29
+ const dirs = [""];
30
+ for (let i = 0; i < segs.length; i++) {
31
+ dirs.push(segs.slice(0, i + 1).join("/"));
32
+ }
33
+ const chain = [];
34
+ for (const d of dirs) {
35
+ const lay = layoutsByDir.get(d);
36
+ if (lay)
37
+ chain.push(lay.relPath);
38
+ }
39
+ return chain;
40
+ };
41
+ for (const r of routes)
42
+ r.layouts = chainFor(r.relPath);
43
+ if (notFound)
44
+ notFound.layouts = chainFor(notFound.relPath);
45
+ routes.sort((a, b) => specificity(b.pattern) - specificity(a.pattern));
46
+ layouts.sort((a, b) => a.dir.length - b.dir.length);
47
+ return { routes, layouts, notFound };
48
+ }
49
+ async function walk(rootDir, current, routes, layouts, setNotFound) {
50
+ const entries = await readdir(current);
51
+ for (const entry of entries) {
52
+ const full = join(current, entry);
53
+ const st = await stat(full);
54
+ if (st.isDirectory()) {
55
+ await walk(rootDir, full, routes, layouts, setNotFound);
56
+ continue;
57
+ }
58
+ if (extname(entry) !== ".jslop")
59
+ continue;
60
+ const rel = relative(rootDir, full).split(sep).join(posix.sep);
61
+ const noExt = rel.slice(0, -".jslop".length);
62
+ const base = basenameOf(rel);
63
+ if (base === "_layout") {
64
+ layouts.push({
65
+ relPath: rel,
66
+ dir: parentDir(rel),
67
+ identifier: identifierOf(noExt),
68
+ });
69
+ continue;
70
+ }
71
+ if (base === "_404") {
72
+ // Only honor `_404.jslop` at the routes root.
73
+ if (parentDir(rel) === "") {
74
+ setNotFound({ relPath: rel, identifier: identifierOf(noExt), layouts: [] });
75
+ }
76
+ continue;
77
+ }
78
+ if (base.startsWith("_"))
79
+ continue;
80
+ const segments = noExt.split("/");
81
+ const last = segments[segments.length - 1] ?? "";
82
+ const urlSegments = (last === "index" ? segments.slice(0, -1) : segments).map(toUrlSegment);
83
+ const pattern = "/" + urlSegments.join("/");
84
+ const cleanPattern = pattern === "" ? "/" : pattern;
85
+ const paramNames = [];
86
+ for (const seg of urlSegments) {
87
+ const m = /^\[(.+)\]$/.exec(seg);
88
+ if (m && m[1])
89
+ paramNames.push(m[1]);
90
+ }
91
+ routes.push({
92
+ pattern: cleanPattern,
93
+ paramNames,
94
+ relPath: rel,
95
+ compiledRelPath: noExt + ".compiled.mjs",
96
+ identifier: identifierOf(noExt),
97
+ layouts: [], // filled in by scanRoutes
98
+ });
99
+ }
100
+ }
101
+ function toUrlSegment(seg) {
102
+ return seg;
103
+ }
104
+ function basenameOf(rel) {
105
+ const slash = rel.lastIndexOf("/");
106
+ const file = slash === -1 ? rel : rel.slice(slash + 1);
107
+ return file.slice(0, -".jslop".length);
108
+ }
109
+ function parentDir(rel) {
110
+ const slash = rel.lastIndexOf("/");
111
+ return slash === -1 ? "" : rel.slice(0, slash);
112
+ }
113
+ function identifierOf(rel) {
114
+ let s = rel.replace(/\W+/g, "_");
115
+ if (/^\d/.test(s))
116
+ s = "_" + s;
117
+ // Capitalize first letter to make it look like a component
118
+ return s.charAt(0).toUpperCase() + s.slice(1);
119
+ }
120
+ function specificity(pattern) {
121
+ // Static segments score higher than dynamic ones.
122
+ let score = 0;
123
+ for (const seg of pattern.split("/")) {
124
+ if (!seg)
125
+ continue;
126
+ if (/^\[.+\]$/.test(seg))
127
+ score += 1;
128
+ else
129
+ score += 100;
130
+ }
131
+ return score;
132
+ }
133
+ export function matchRoute(url, routes) {
134
+ const path = url.split("?")[0] ?? "/";
135
+ const cleaned = path.length > 1 && path.endsWith("/") ? path.slice(0, -1) : path;
136
+ for (const route of routes) {
137
+ const m = patternToRegex(route.pattern).exec(cleaned);
138
+ if (!m)
139
+ continue;
140
+ const params = {};
141
+ for (let i = 0; i < route.paramNames.length; i++) {
142
+ params[route.paramNames[i]] = decodeURIComponent(m[i + 1] ?? "");
143
+ }
144
+ return { route, params };
145
+ }
146
+ return null;
147
+ }
148
+ function patternToRegex(pattern) {
149
+ const parts = pattern.split("/").map((seg) => {
150
+ if (!seg)
151
+ return "";
152
+ if (/^\[.+\]$/.test(seg))
153
+ return "([^/]+)";
154
+ return escapeRegex(seg);
155
+ });
156
+ return new RegExp("^" + parts.join("/") + "$");
157
+ }
158
+ function escapeRegex(s) {
159
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
160
+ }
161
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@jslop/router",
3
+ "version": "0.1.0",
4
+ "description": "File-based router for JSlop apps. Scans a routes directory, matches URLs with [param] segments, composes _layout.jslop chains, and orchestrates per-route load() loaders.",
5
+ "keywords": [
6
+ "jslop",
7
+ "router",
8
+ "file-based",
9
+ "layouts",
10
+ "framework"
11
+ ],
12
+ "license": "MIT",
13
+ "author": "p-arndt",
14
+ "homepage": "https://github.com/p-arndt/jslop#readme",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/p-arndt/jslop.git",
18
+ "directory": "packages/router"
19
+ },
20
+ "bugs": "https://github.com/p-arndt/jslop/issues",
21
+ "engines": {
22
+ "node": ">=20"
23
+ },
24
+ "sideEffects": false,
25
+ "type": "module",
26
+ "main": "./dist/index.js",
27
+ "types": "./dist/index.d.ts",
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.ts",
31
+ "import": "./dist/index.js"
32
+ }
33
+ },
34
+ "files": [
35
+ "dist",
36
+ "README.md"
37
+ ],
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "devDependencies": {
42
+ "@types/node": "^25.7.0",
43
+ "typescript": "^5.6.3"
44
+ },
45
+ "scripts": {
46
+ "build": "tsc -p tsconfig.json"
47
+ }
48
+ }