@epicabdou/linkr 0.0.2 → 0.0.4

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # linkr.js
1
+ # @epicabdou/linkr
2
2
 
3
- File-based routing for **React Router v7** in Vite + React SPA apps. Generates a route tree from a `src/pages/` directory using `import.meta.glob` (the consumer passes the glob result; the library does not call it).
3
+ File-based routing for **React Router v7** in Vite + React SPA apps. Generates a route tree from a `src/pages/` directory using `import.meta.glob` the consumer passes the glob result; the library does not call it.
4
4
 
5
5
  ## Install
6
6
 
@@ -8,10 +8,12 @@ File-based routing for **React Router v7** in Vite + React SPA apps. Generates a
8
8
  pnpm add @epicabdou/linkr react react-dom react-router
9
9
  ```
10
10
 
11
- Peer deps: `react`, `react-dom`, `react-router` (v7+).
11
+ **Peer dependencies:** `react` (≥18), `react-dom` (≥18), `react-router` (≥7).
12
12
 
13
13
  ## Usage
14
14
 
15
+ ### Basic setup
16
+
15
17
  ```tsx
16
18
  import { createRoutes } from "@epicabdou/linkr";
17
19
  import { createBrowserRouter, RouterProvider } from "react-router";
@@ -23,36 +25,140 @@ const router = createBrowserRouter(routes);
23
25
  // Then render <RouterProvider router={router} />
24
26
  ```
25
27
 
26
- - **`pagesGlob`** (required): result of `import.meta.glob("...")` from your app.
27
- - **`pagesDir`** (optional, default `"src/pages"`): base path used to normalize file paths. Must match the prefix you strip from glob keys (e.g. use `"pages"` when glob is `./pages/**/*.tsx` from `src/`).
28
- - **`layoutFileName`** (optional, default `"_layout"`).
29
- - **`notFoundFileName`** (optional, default `"404"`).
30
- - **`routeExtensions`** (optional, default `[".tsx", ".ts", ".jsx", ".js"]`).
31
- - **`defaultRedirectTo`** (optional, default `"/"`): default redirect path when a route's `protect` check fails and no `redirectTo` is set.
32
-
33
- ## Conventions
34
-
35
- | File / pattern | Route |
36
- |-----------------------|---------------------------------|
37
- | `index.tsx` | `/` (index route) |
38
- | `about.tsx` | `/about` |
39
- | `blog/index.tsx` | `/blog` (index under blog) |
40
- | `blog/[id].tsx` | `/blog/:id` |
41
- | `docs/[...slug].tsx` | `/docs/*` (splat) |
42
- | `404.tsx` | `path: "*"` (last) |
43
- | `_layout.tsx` (any folder) | Layout route; children render in `<Outlet />` |
44
-
45
- - **Nested routes**: folder nesting = nested routes.
46
- - **Layouts**: `_layout.tsx` in a folder becomes the parent route for that segment; all siblings (index, segment files, nested folders) are its children.
47
- - **Sort order**: among siblings, static segments first, then dynamic (`:id`), then splat (`*`).
48
- - **Lazy loading**: every route uses React Router's `lazy()` for code splitting.
28
+ ### One-line app bootstrap
29
+
30
+ Use `createRootWithLinkr` to create the root, build routes, and render in one call:
31
+
32
+ ```tsx
33
+ import { createRootWithLinkr } from "@epicabdou/linkr";
34
+
35
+ createRootWithLinkr(document.getElementById("root")!, {
36
+ pagesGlob: import.meta.glob("./pages/**/*.tsx"),
37
+ pagesDir: "pages",
38
+ });
39
+ ```
40
+
41
+ This uses `createBrowserRouter`, `StrictMode`, and `RouterProvider` under the hood. No need to import React Router or call `createRoutes` yourself.
42
+
43
+ ### Using `<LinkrApp>` (with custom providers)
44
+
45
+ When you need to wrap the app with your own providers, use the `<LinkrApp>` component and pass the same options as `createRoutes`:
46
+
47
+ ```tsx
48
+ import { LinkrApp } from "@epicabdou/linkr";
49
+
50
+ const pages = import.meta.glob("./pages/**/*.tsx");
51
+
52
+ function App() {
53
+ return (
54
+ <YourProvider>
55
+ <LinkrApp
56
+ pagesGlob={pages}
57
+ pagesDir="pages"
58
+ defaultRedirectTo="/login"
59
+ />
60
+ </YourProvider>
61
+ );
62
+ }
63
+
64
+ createRoot(document.getElementById("root")!).render(<App />);
65
+ ```
66
+
67
+ Options are read once on mount; the router is created with `useMemo`.
68
+
69
+ ---
70
+
71
+ ## `createRoutes` options
72
+
73
+ | Option | Type | Default | Description |
74
+ |--------|------|---------|-------------|
75
+ | **`pagesGlob`** | `Record<string, () => Promise<unknown>>` | *required* | Result of `import.meta.glob("...")` from your app. |
76
+ | **`pagesDir`** | `string` | `"src/pages"` | Base path used to normalize file paths. Must match the prefix you strip from glob keys (e.g. `"pages"` when glob is `./pages/**/*.tsx` from `src/`). |
77
+ | **`layoutFileName`** | `string` | `"_layout"` | Filename (without extension) for layout routes. |
78
+ | **`notFoundFileName`** | `string` | `"404"` | Filename for the catch-all not-found route. |
79
+ | **`routeExtensions`** | `string[]` | `[".tsx", ".ts", ".jsx", ".js"]` | Extensions that define route files. |
80
+ | **`defaultRedirectTo`** | `string` | `"/"` | Default redirect path when a route's `protect` check fails and no `redirectTo` is set. |
81
+ | **`layoutsGlob`** | `Record<string, () => Promise<unknown>>` | — | When set with **`layoutMap`**, layouts are loaded from this glob and `_layout` files in pages are ignored. Example: `import.meta.glob("./layouts/**/*.tsx")`. |
82
+ | **`layoutsDir`** | `string` | `"src/layouts"` | Base path for layout files when using **`layoutsGlob`**. |
83
+ | **`layoutMap`** | `Record<string, string>` | — | Path prefix → layout name. Use `""` or `"/"` for root; segment name (e.g. `blog`) for nested. Requires **`layoutsGlob`**. |
84
+
85
+ ---
86
+
87
+ ## File conventions
88
+
89
+ | File / pattern | Route |
90
+ |----------------|-------|
91
+ | `index.tsx` | `/` (index route) |
92
+ | `about.tsx` | `/about` |
93
+ | `blog/index.tsx` | `/blog` (index under blog) |
94
+ | `blog/[id].tsx` | `/blog/:id` |
95
+ | `docs/[...slug].tsx` | `/docs/*` (splat) |
96
+ | `404.tsx` | `path: "*"` (catch-all, last) |
97
+ | `_layout.tsx` (in any folder) | Layout route; children render in `<Outlet />`. Ignored when using **layoutsGlob** + **layoutMap**. |
98
+
99
+ - **Nested routes:** folder nesting = nested routes.
100
+ - **Layouts:** Either put `_layout.tsx` in a folder (folder-based), or use a **separate layouts folder** with **layoutsGlob** and **layoutMap** so layouts are reusable components.
101
+ - **Sort order:** among siblings — static segments first, then dynamic (`:id`), then splat (`*`).
102
+ - **Lazy loading:** every route uses React Router's `lazy()` for code splitting.
103
+
104
+ ---
49
105
 
50
106
  ## Page module exports
51
107
 
52
- - **`default`**: React component used as the route element (required).
53
- - **`ErrorBoundary`**: optional; used as `errorElement`.
54
- - **`handle`**: optional; attached to the route's `handle`.
55
- - **`protect`**: optional; run a condition before rendering the route (or layout). When the check returns `false`, the user is redirected. See [Route protection](#route-protection).
108
+ | Export | Description |
109
+ |--------|-------------|
110
+ | **`default`** | React component used as the route element (required). |
111
+ | **`ErrorBoundary`** | Optional; used as the route's `errorElement`. |
112
+ | **`handle`** | Optional; attached to the route's `handle`. |
113
+ | **`protect`** | Optional; runs before rendering. If it returns `false`, the user is redirected. See [Route protection](#route-protection). |
114
+
115
+ ---
116
+
117
+ ## Layouts in a separate folder
118
+
119
+ Keep layouts as reusable components in a dedicated folder and wire them by path with **layoutMap** (no `_layout.tsx` in pages).
120
+
121
+ **Example layouts:**
122
+
123
+ ```tsx
124
+ // src/layouts/Root.tsx
125
+ import { Link, Outlet } from "react-router";
126
+ export default function RootLayout() {
127
+ return (
128
+ <div>
129
+ <nav><Link to="/">Home</Link> <Link to="/blog">Blog</Link></nav>
130
+ <Outlet />
131
+ </div>
132
+ );
133
+ }
134
+ ```
135
+
136
+ ```tsx
137
+ // src/layouts/Blog.tsx
138
+ import { Outlet } from "react-router";
139
+ export default function BlogLayout() {
140
+ return <div><h2>Blog</h2><Outlet /></div>;
141
+ }
142
+ ```
143
+
144
+ **Wire in router setup:**
145
+
146
+ ```tsx
147
+ const pages = import.meta.glob("./pages/**/*.tsx");
148
+ const layouts = import.meta.glob("./layouts/**/*.tsx");
149
+ const routes = createRoutes({
150
+ pagesGlob: pages,
151
+ pagesDir: "pages",
152
+ layoutsGlob: layouts,
153
+ layoutsDir: "layouts",
154
+ layoutMap: { "/": "Root", "": "Root", blog: "Blog" },
155
+ });
156
+ ```
157
+
158
+ - **layoutMap keys:** `""` or `"/"` = root layout; `"blog"` = layout for `/blog` and its children. Value = layout filename without extension (e.g. `Root`, `Blog`).
159
+ - The same layout component can be reused by mapping multiple keys to the same name.
160
+
161
+ ---
56
162
 
57
163
  ## Route protection
58
164
 
@@ -92,7 +198,7 @@ export default function SettingsLayout() {
92
198
  }
93
199
  ```
94
200
 
95
- Pass a default redirect when creating routes:
201
+ Set a default redirect when creating routes:
96
202
 
97
203
  ```tsx
98
204
  createRoutes({
@@ -102,14 +208,14 @@ createRoutes({
102
208
  });
103
209
  ```
104
210
 
105
- ### 2. `<Protect>` component (wrap a route or layout content)
211
+ ### 2. `<Protect>` component
106
212
 
107
- Use the `<Protect>` component when you want to guard content inside a layout (e.g. wrap `<Outlet />`) or need a stable condition with a fallback UI.
213
+ Use `<Protect>` when you want to guard content inside a layout (e.g. wrap `<Outlet />`) or need a stable condition with a fallback UI.
108
214
 
109
- **Predefined config (recommended):** define `condition`, `redirectTo`, and `fallback` once in a config file, then reuse with `<Protect {...config}>`.
215
+ **Predefined config (recommended):** define `condition`, `redirectTo`, and `fallback` once, then reuse with `<Protect {...config}>`.
110
216
 
111
217
  ```tsx
112
- // src/config/protect.tsx (or src/lib/auth.tsx)
218
+ // src/config/protect.tsx
113
219
  import type { ProtectConfig } from "@epicabdou/linkr";
114
220
 
115
221
  export const authProtect: ProtectConfig = {
@@ -134,13 +240,31 @@ export default function DashboardLayout() {
134
240
  }
135
241
  ```
136
242
 
137
- You can define multiple configs (e.g. `authProtect`, `adminProtect`) in the same file and import the one you need.
243
+ **Props:**
138
244
 
139
245
  - **`condition`**: sync or async function; return `true` to allow, `false` to redirect.
140
246
  - **`redirectTo`**: path to redirect to when the condition fails.
141
247
  - **`fallback`**: optional React node shown while an async condition is pending.
142
248
  - **`children`**: content to render when the condition is true.
143
249
 
250
+ You can define multiple configs (e.g. `authProtect`, `adminProtect`) and import the one you need.
251
+
252
+ ---
253
+
254
+ ## API exports
255
+
256
+ | Export | Description |
257
+ |--------|-------------|
258
+ | `createRoutes` | Builds React Router route array from pages glob and options. |
259
+ | `LinkrApp` | Component that renders `RouterProvider` with routes from options. |
260
+ | `createRootWithLinkr` | One-shot: `createRoot`, `createRoutes`, `StrictMode` + `RouterProvider`. |
261
+ | `Protect` | Component for conditional redirect with fallback UI. |
262
+ | `normalizePath`, `parseSegment`, `compareSegments` | Path utilities (for advanced use). |
263
+
264
+ **Types:** `CreateRoutesOptions`, `LayoutMap`, `LayoutsGlob`, `PagesGlob`, `RouteObject`, `RouteProtect`, `ProtectProps`, `ProtectConfig`, `LinkrAppOptions`, `ParsedSegment`, `SegmentKind`.
265
+
266
+ ---
267
+
144
268
  ## Quick start with CLI
145
269
 
146
270
  ```bash
@@ -150,7 +274,16 @@ cd my-app && pnpm dev
150
274
 
151
275
  ## Build & test
152
276
 
277
+ From the monorepo root:
278
+
279
+ ```bash
280
+ pnpm --filter @epicabdou/linkr build
281
+ pnpm --filter @epicabdou/linkr test
282
+ ```
283
+
284
+ Or from `packages/linkr`:
285
+
153
286
  ```bash
154
- pnpm --filter linkr build
155
- pnpm --filter linkr test
287
+ pnpm build
288
+ pnpm test
156
289
  ```
package/dist/index.d.ts CHANGED
@@ -2,11 +2,19 @@ import { RouteObject } from 'react-router';
2
2
  export { RouteObject } from 'react-router';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import { ReactNode } from 'react';
5
+ import { createRoot } from 'react-dom/client';
5
6
 
6
7
  /**
7
8
  * Result of import.meta.glob("...") - keys are file paths, values are import functions.
8
9
  */
9
10
  type PagesGlob = Record<string, () => Promise<unknown>>;
11
+ /** Same shape as PagesGlob; use for layouts, e.g. import.meta.glob from your layouts directory. */
12
+ type LayoutsGlob = Record<string, () => Promise<unknown>>;
13
+ /**
14
+ * Maps a path prefix to a layout name (filename without ext from layouts dir).
15
+ * Use "" or "/" for root layout; use segment name for nested (e.g. "blog" for /blog).
16
+ */
17
+ type LayoutMap = Record<string, string>;
10
18
  /**
11
19
  * Protection check: sync or async function that returns true to allow access, false to redirect.
12
20
  * Can be a plain function or an object with check, redirectTo, and optional fallback.
@@ -35,6 +43,18 @@ interface CreateRoutesOptions {
35
43
  routeExtensions?: string[];
36
44
  /** Default redirect path when a route's protect check fails and redirectTo is not set (default: "/"). */
37
45
  defaultRedirectTo?: string;
46
+ /**
47
+ * When set, layouts are loaded from this glob and assigned by layoutMap.
48
+ * _layout files in pages are ignored. Example: import.meta.glob("./layouts/*.tsx")
49
+ */
50
+ layoutsGlob?: LayoutsGlob;
51
+ /** Base directory for layout files, used to derive layout names (default: "src/layouts"). */
52
+ layoutsDir?: string;
53
+ /**
54
+ * Path prefix -> layout name. "" or "/" = root layout; "blog" = layout for /blog.
55
+ * Layout name = filename without extension in layoutsDir.
56
+ */
57
+ layoutMap?: LayoutMap;
38
58
  }
39
59
 
40
60
  declare function createRoutes(options: CreateRoutesOptions): RouteObject[];
@@ -82,4 +102,31 @@ type ProtectConfig = Pick<ProtectProps, "condition" | "redirectTo" | "fallback">
82
102
  */
83
103
  declare function Protect({ condition, redirectTo, fallback, children }: ProtectProps): react_jsx_runtime.JSX.Element | null;
84
104
 
85
- export { type CreateRoutesOptions, type PagesGlob, type ParsedSegment, Protect, type ProtectConfig, type ProtectProps, type RouteProtect, type SegmentKind, compareSegments, createRoutes, normalizePath, parseSegment };
105
+ /**
106
+ * Options for LinkrApp and createRootWithLinkr.
107
+ * Same as CreateRoutesOptions (pagesGlob required; rest optional).
108
+ */
109
+ type LinkrAppOptions = {
110
+ pagesGlob: PagesGlob;
111
+ } & Omit<CreateRoutesOptions, "pagesGlob">;
112
+ /**
113
+ * Renders the file-based router. Options are read once on mount.
114
+ * Use this when you need to wrap the app with your own providers.
115
+ */
116
+ declare function LinkrApp(options: LinkrAppOptions): react_jsx_runtime.JSX.Element;
117
+ /**
118
+ * One-shot setup: creates the root, builds routes from pagesGlob, and renders
119
+ * with StrictMode + RouterProvider. Minimizes app entry to a single call.
120
+ *
121
+ * @example
122
+ * ```tsx
123
+ * import { createRootWithLinkr } from "@epicabdou/linkr";
124
+ * createRootWithLinkr(document.getElementById("root")!, {
125
+ * pagesGlob: import.meta.glob("./pages/**\/*.tsx"),
126
+ * pagesDir: "pages",
127
+ * });
128
+ * ```
129
+ */
130
+ declare function createRootWithLinkr(rootElement: HTMLElement, options: LinkrAppOptions): ReturnType<typeof createRoot>;
131
+
132
+ export { type CreateRoutesOptions, type LayoutMap, type LayoutsGlob, LinkrApp, type LinkrAppOptions, type PagesGlob, type ParsedSegment, Protect, type ProtectConfig, type ProtectProps, type RouteProtect, type SegmentKind, compareSegments, createRootWithLinkr, createRoutes, normalizePath, parseSegment };
package/dist/index.js CHANGED
@@ -32,9 +32,28 @@ function compareSegments(a, b) {
32
32
 
33
33
  // src/createRoutes.ts
34
34
  var DEFAULT_PAGES_DIR = "src/pages";
35
+ var DEFAULT_LAYOUTS_DIR = "src/layouts";
35
36
  var DEFAULT_LAYOUT_FILE = "_layout";
36
37
  var DEFAULT_NOT_FOUND_FILE = "404";
37
38
  var DEFAULT_EXTENSIONS = [".tsx", ".ts", ".jsx", ".js"];
39
+ function parseLayoutsGlob(layoutsGlob, layoutsDir, extensions) {
40
+ const map = /* @__PURE__ */ new Map();
41
+ const dir = normalizePath(layoutsDir).replace(/\/$/, "");
42
+ for (const key of Object.keys(layoutsGlob)) {
43
+ const normalized = normalizePath(key).replace(/^\.\//, "");
44
+ let relative = normalized;
45
+ if (relative.startsWith(dir + "/")) relative = relative.slice(dir.length + 1);
46
+ else if (relative.startsWith(dir)) relative = relative.slice(dir.length).replace(/^\//, "");
47
+ const ext = getRouteExtension(relative, extensions);
48
+ if (!ext) continue;
49
+ const withoutExt = relative.slice(0, -ext.length);
50
+ const parts = withoutExt.split("/").filter(Boolean);
51
+ if (parts.length === 0) continue;
52
+ const name = parts[parts.length - 1];
53
+ map.set(name, layoutsGlob[key]);
54
+ }
55
+ return map;
56
+ }
38
57
  function stripExtension(filename, extensions) {
39
58
  const lower = filename.toLowerCase();
40
59
  for (const ext of extensions) {
@@ -204,11 +223,12 @@ function makeLazyRoute(loader, filePath, defaultRedirectTo) {
204
223
  if (!ok) throw redirect(redirectTo);
205
224
  return null;
206
225
  };
226
+ result.HydrateFallback = () => null;
207
227
  }
208
228
  return result;
209
229
  };
210
230
  }
211
- function nodeToRoutes(node, pathPrefix, pagesGlob, defaultRedirectTo) {
231
+ function nodeToRoutes(node, pathPrefix, pagesGlob, defaultRedirectTo, layoutMap, layoutLoaders) {
212
232
  const childRouteObjects = [];
213
233
  if (node.indexRoute) {
214
234
  const loader = pagesGlob[node.indexRoute.path];
@@ -246,30 +266,41 @@ function nodeToRoutes(node, pathPrefix, pagesGlob, defaultRedirectTo) {
246
266
  const firstSeg = key.split("/")[0];
247
267
  const seg = parseSegment(firstSeg);
248
268
  const pathSeg = segmentToPath(seg);
249
- const nested = nodeToRoutes(childNode, pathPrefix ? pathPrefix + "/" + pathSeg : pathSeg, pagesGlob, defaultRedirectTo);
250
- if (childNode.layout) {
251
- if (childNode.layout.path in pagesGlob) {
252
- childRouteObjects.push(...nested);
253
- } else {
254
- childRouteObjects.push({ path: pathSeg, children: nested });
255
- }
269
+ const nested = nodeToRoutes(
270
+ childNode,
271
+ pathPrefix ? pathPrefix + "/" + pathSeg : pathSeg,
272
+ pagesGlob,
273
+ defaultRedirectTo,
274
+ layoutMap,
275
+ layoutLoaders
276
+ );
277
+ const layoutName = layoutMap && layoutLoaders ? layoutMap[key] ?? layoutMap["/" + key] : void 0;
278
+ const layoutLoader = layoutName ? layoutLoaders.get(layoutName) : void 0;
279
+ if (layoutLoader) {
280
+ childRouteObjects.push({
281
+ path: pathSeg,
282
+ lazy: makeLazyRoute(layoutLoader, "layout:" + layoutName, defaultRedirectTo),
283
+ children: nested
284
+ });
285
+ } else if (childNode.layout && childNode.layout.path in pagesGlob) {
286
+ childRouteObjects.push(...nested);
287
+ } else if (childNode.layout) {
288
+ childRouteObjects.push({ path: pathSeg, children: nested });
256
289
  } else if (nested.length > 0) {
257
290
  childRouteObjects.push({ path: pathSeg, children: nested });
258
291
  }
259
292
  }
260
- if (node.layout) {
261
- if (node.layout.path in pagesGlob) {
262
- const layoutLoader = pagesGlob[node.layout.path];
263
- return [{
264
- path: pathPrefix === "" ? "/" : pathPrefix,
265
- lazy: makeLazyRoute(layoutLoader, node.layout.path, defaultRedirectTo),
266
- children: childRouteObjects.length ? childRouteObjects : void 0
267
- }];
268
- }
293
+ if (node.layout && node.layout.path in pagesGlob) {
294
+ const layoutLoader = pagesGlob[node.layout.path];
295
+ return [{
296
+ path: pathPrefix === "" ? "/" : pathPrefix,
297
+ lazy: makeLazyRoute(layoutLoader, node.layout.path, defaultRedirectTo),
298
+ children: childRouteObjects.length ? childRouteObjects : void 0
299
+ }];
269
300
  }
270
301
  return childRouteObjects;
271
302
  }
272
- function flattenRootChildren(root, pagesGlob, defaultRedirectTo) {
303
+ function flattenRootChildren(root, pagesGlob, defaultRedirectTo, layoutMap, layoutLoaders) {
273
304
  const result = [];
274
305
  if (root.indexRoute) {
275
306
  const loader = pagesGlob[root.indexRoute.path];
@@ -307,17 +338,39 @@ function flattenRootChildren(root, pagesGlob, defaultRedirectTo) {
307
338
  const firstSeg = key.split("/")[0];
308
339
  const seg = parseSegment(firstSeg);
309
340
  const pathSeg = segmentToPath(seg);
310
- const nested = nodeToRoutes(childNode, pathSeg, pagesGlob, defaultRedirectTo);
311
- if (childNode.layout) {
312
- if (childNode.layout.path in pagesGlob) {
313
- result.push(...nested);
314
- } else {
315
- result.push({ path: pathSeg, children: nested });
316
- }
341
+ const nested = nodeToRoutes(
342
+ childNode,
343
+ pathSeg,
344
+ pagesGlob,
345
+ defaultRedirectTo,
346
+ layoutMap,
347
+ layoutLoaders
348
+ );
349
+ const layoutName = layoutMap && layoutLoaders ? layoutMap[key] ?? layoutMap["/" + key] : void 0;
350
+ const layoutLoader = layoutName ? layoutLoaders.get(layoutName) : void 0;
351
+ if (layoutLoader) {
352
+ result.push({
353
+ path: pathSeg,
354
+ lazy: makeLazyRoute(layoutLoader, "layout:" + layoutName, defaultRedirectTo),
355
+ children: nested
356
+ });
357
+ } else if (childNode.layout && childNode.layout.path in pagesGlob) {
358
+ result.push(...nested);
359
+ } else if (childNode.layout) {
360
+ result.push({ path: pathSeg, children: nested });
317
361
  } else if (nested.length > 0) {
318
362
  result.push({ path: pathSeg, children: nested });
319
363
  }
320
364
  }
365
+ const rootLayoutName = layoutMap && layoutLoaders ? layoutMap[""] ?? layoutMap["/"] : void 0;
366
+ const rootLayoutLoader = rootLayoutName ? layoutLoaders.get(rootLayoutName) : void 0;
367
+ if (rootLayoutLoader && result.length > 0) {
368
+ return [{
369
+ path: "/",
370
+ lazy: makeLazyRoute(rootLayoutLoader, "layout:" + rootLayoutName, defaultRedirectTo),
371
+ children: result
372
+ }];
373
+ }
321
374
  return result;
322
375
  }
323
376
  function createRoutes(options) {
@@ -334,10 +387,24 @@ function createRoutes(options) {
334
387
  if (entry) entries.push(entry);
335
388
  }
336
389
  const notFoundEntry = entries.find((e) => e.is404);
337
- const rest = entries.filter((e) => !e.is404);
390
+ const useLayoutsFolder = options.layoutsGlob != null && options.layoutMap != null;
391
+ const rest = useLayoutsFolder ? entries.filter((e) => !e.is404 && !e.isLayout) : entries.filter((e) => !e.is404);
338
392
  const root = buildTree(rest);
393
+ const layoutLoaders = options.layoutsGlob && options.layoutMap ? parseLayoutsGlob(
394
+ options.layoutsGlob,
395
+ options.layoutsDir ?? DEFAULT_LAYOUTS_DIR,
396
+ opts.routeExtensions
397
+ ) : void 0;
339
398
  let topLevelRoutes;
340
- if (root.layout && root.layout.path in options.pagesGlob) {
399
+ if (layoutLoaders && options.layoutMap) {
400
+ topLevelRoutes = flattenRootChildren(
401
+ root,
402
+ options.pagesGlob,
403
+ options.defaultRedirectTo,
404
+ options.layoutMap,
405
+ layoutLoaders
406
+ );
407
+ } else if (root.layout && root.layout.path in options.pagesGlob) {
341
408
  const layoutLoader = options.pagesGlob[root.layout.path];
342
409
  topLevelRoutes = [{
343
410
  path: "/",
@@ -384,9 +451,34 @@ function Protect({ condition, redirectTo, fallback = null, children }) {
384
451
  if (!allowed) return null;
385
452
  return /* @__PURE__ */ jsx(Fragment, { children });
386
453
  }
454
+
455
+ // src/LinkrApp.tsx
456
+ import { StrictMode, useMemo, useRef } from "react";
457
+ import { createRoot } from "react-dom/client";
458
+ import { createBrowserRouter, RouterProvider } from "react-router";
459
+ import { jsx as jsx2 } from "react/jsx-runtime";
460
+ function LinkrApp(options) {
461
+ const optionsRef = useRef(options);
462
+ optionsRef.current = options;
463
+ const router = useMemo(
464
+ () => createBrowserRouter(createRoutes(optionsRef.current)),
465
+ []
466
+ );
467
+ return /* @__PURE__ */ jsx2(RouterProvider, { router });
468
+ }
469
+ function createRootWithLinkr(rootElement, options) {
470
+ const router = createBrowserRouter(createRoutes(options));
471
+ const root = createRoot(rootElement);
472
+ root.render(
473
+ /* @__PURE__ */ jsx2(StrictMode, { children: /* @__PURE__ */ jsx2(RouterProvider, { router }) })
474
+ );
475
+ return root;
476
+ }
387
477
  export {
478
+ LinkrApp,
388
479
  Protect,
389
480
  compareSegments,
481
+ createRootWithLinkr,
390
482
  createRoutes,
391
483
  normalizePath,
392
484
  parseSegment
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/createRoutes.ts","../src/pathUtils.ts","../src/Protect.tsx"],"sourcesContent":["import type { RouteObject } from \"react-router\";\r\nimport { redirect } from \"react-router\";\r\nimport type { CreateRoutesOptions, PagesGlob, RouteProtect } from \"./types\";\r\nimport {\r\n normalizePath,\r\n getRouteExtension,\r\n parseSegment,\r\n compareSegments,\r\n type ParsedSegment,\r\n} from \"./pathUtils\";\r\n\r\nconst DEFAULT_PAGES_DIR = \"src/pages\";\r\nconst DEFAULT_LAYOUT_FILE = \"_layout\";\r\nconst DEFAULT_NOT_FOUND_FILE = \"404\";\r\nconst DEFAULT_EXTENSIONS = [\".tsx\", \".ts\", \".jsx\", \".js\"];\r\n\r\ntype LazyModule = {\r\n default?: React.ComponentType;\r\n ErrorBoundary?: React.ComponentType;\r\n handle?: unknown;\r\n protect?: RouteProtect;\r\n};\r\n\r\n/**\r\n * Strip extension from filename.\r\n */\r\nfunction stripExtension(filename: string, extensions: string[]): string {\r\n const lower = filename.toLowerCase();\r\n for (const ext of extensions) {\r\n if (lower.endsWith(ext)) return filename.slice(0, -ext.length);\r\n }\r\n return filename;\r\n}\r\n\r\n/**\r\n * Check if segment is a valid splat (exactly \"[...name]\").\r\n */\r\nfunction isValidSplatSegment(segment: string): boolean {\r\n return /^\\[\\.\\.\\.[^\\]]*\\]$/.test(segment);\r\n}\r\n\r\n/**\r\n * Warn in dev when a module has no default export (called from lazy).\r\n */\r\nfunction warnNoDefault(path: string): void {\r\n if (typeof process !== \"undefined\" && process.env?.NODE_ENV !== \"production\") {\r\n console.warn(`[linkr] Module has no default export, skipping route: ${path}`);\r\n }\r\n}\r\n\r\n/**\r\n * Warn when [...slug] is used with other segments in the same filename.\r\n */\r\nfunction warnInvalidSplat(path: string): void {\r\n if (typeof process !== \"undefined\" && process.env?.NODE_ENV !== \"production\") {\r\n console.warn(\r\n `[linkr] Invalid splat usage (e.g. foo[...slug].tsx); use only [...slug].tsx in filename. Skipping: ${path}`\r\n );\r\n }\r\n}\r\n\r\ninterface FileEntry {\r\n path: string;\r\n segments: string[];\r\n isLayout: boolean;\r\n is404: boolean;\r\n isIndex: boolean;\r\n segmentKinds: ReturnType<typeof parseSegment>[];\r\n}\r\n\r\nfunction parseGlobKey(\r\n key: string,\r\n opts: Required<Pick<CreateRoutesOptions, \"pagesDir\" | \"layoutFileName\" | \"notFoundFileName\" | \"routeExtensions\">>\r\n): FileEntry | null {\r\n const normalized = normalizePath(key);\r\n const withoutLeadingSlash = normalized.replace(/^\\.\\//, \"\");\r\n const dir = normalizePath(opts.pagesDir).replace(/\\/$/, \"\");\r\n let relative = withoutLeadingSlash;\r\n if (relative.startsWith(dir + \"/\")) {\r\n relative = relative.slice(dir.length + 1);\r\n } else if (relative.startsWith(dir)) {\r\n relative = relative.slice(dir.length).replace(/^\\//, \"\");\r\n } else if (dir.includes(\"/\")) {\r\n const lastDirSegment = dir.split(\"/\").pop()!;\r\n if (relative.startsWith(lastDirSegment + \"/\")) {\r\n relative = relative.slice(lastDirSegment.length + 1);\r\n }\r\n }\r\n\r\n const ext = getRouteExtension(relative, opts.routeExtensions);\r\n if (!ext) return null;\r\n\r\n const withoutExt = relative.slice(0, -ext.length);\r\n const parts = withoutExt.split(\"/\").filter(Boolean);\r\n if (parts.length === 0) return null;\r\n\r\n const lastPart = parts[parts.length - 1];\r\n const isLayout = stripExtension(lastPart, opts.routeExtensions) === opts.layoutFileName;\r\n const is404 = stripExtension(lastPart, opts.routeExtensions) === opts.notFoundFileName;\r\n const isIndex = lastPart.toLowerCase().replace(new RegExp(\"\\\\\" + ext + \"$\", \"i\"), \"\") === \"index\";\r\n const segmentParts = isIndex ? parts.slice(0, -1) : parts;\r\n const segmentStrings = segmentParts.map((p, i) => {\r\n const isLast = i === segmentParts.length - 1;\r\n return isLast ? stripExtension(p, opts.routeExtensions) : p;\r\n });\r\n\r\n const segmentKinds = segmentStrings.map(parseSegment);\r\n for (const s of segmentStrings) {\r\n if (s.includes(\"[...\") && !isValidSplatSegment(s)) {\r\n warnInvalidSplat(key);\r\n return null;\r\n }\r\n }\r\n\r\n return {\r\n path: key,\r\n segments: segmentStrings,\r\n isLayout,\r\n is404,\r\n isIndex,\r\n segmentKinds,\r\n };\r\n}\r\n\r\ninterface RouteNode {\r\n path: string;\r\n layout: FileEntry | null;\r\n indexRoute: FileEntry | null;\r\n children: Map<string, { entry: FileEntry }>;\r\n childLayouts: Map<string, RouteNode>;\r\n}\r\n\r\nfunction buildTree(entries: FileEntry[]): RouteNode {\r\n const root: RouteNode = {\r\n path: \"\",\r\n layout: null,\r\n indexRoute: null,\r\n children: new Map(),\r\n childLayouts: new Map(),\r\n };\r\n\r\n for (const entry of entries) {\r\n if (entry.is404) continue;\r\n if (entry.isLayout) {\r\n const layoutSegments = entry.segments.slice(0, -1);\r\n let node = root;\r\n for (let i = 0; i < layoutSegments.length; i++) {\r\n const key = layoutSegments.slice(0, i + 1).join(\"/\");\r\n if (!node.childLayouts.has(key)) {\r\n node.childLayouts.set(key, {\r\n path: key,\r\n layout: null,\r\n indexRoute: null,\r\n children: new Map(),\r\n childLayouts: new Map(),\r\n });\r\n }\r\n node = node.childLayouts.get(key)!;\r\n }\r\n node.layout = entry;\r\n continue;\r\n }\r\n\r\n if (entry.isIndex && entry.segments.length === 0) {\r\n root.indexRoute = entry;\r\n continue;\r\n }\r\n\r\n if (entry.isIndex) {\r\n let node = root;\r\n const segs = entry.segments;\r\n for (let i = 0; i < segs.length; i++) {\r\n const key = segs.slice(0, i + 1).join(\"/\");\r\n if (node.childLayouts.has(key)) {\r\n node = node.childLayouts.get(key)!;\r\n } else {\r\n const next: RouteNode = {\r\n path: key,\r\n layout: null,\r\n indexRoute: null,\r\n children: new Map(),\r\n childLayouts: new Map(),\r\n };\r\n node.childLayouts.set(key, next);\r\n node = next;\r\n }\r\n }\r\n node.indexRoute = entry;\r\n continue;\r\n }\r\n\r\n const segs = entry.segments;\r\n let node = root;\r\n for (let i = 0; i < segs.length - 1; i++) {\r\n const key = segs.slice(0, i + 1).join(\"/\");\r\n if (node.childLayouts.has(key)) {\r\n node = node.childLayouts.get(key)!;\r\n } else {\r\n const next: RouteNode = {\r\n path: key,\r\n layout: null,\r\n indexRoute: null,\r\n children: new Map(),\r\n childLayouts: new Map(),\r\n };\r\n node.childLayouts.set(key, next);\r\n node = next;\r\n }\r\n }\r\n const lastSeg = segs[segs.length - 1];\r\n node.children.set(lastSeg, { entry });\r\n }\r\n\r\n return root;\r\n}\r\n\r\nfunction segmentToPath(segment: ParsedSegment): string {\r\n if (segment.kind === \"static\") return segment.raw;\r\n if (segment.kind === \"dynamic\") return \":\" + (segment.paramName ?? \"param\");\r\n if (segment.kind === \"splat\") return \"*\";\r\n return segment.raw;\r\n}\r\n\r\nfunction makeLazyRoute(\r\n loader: () => Promise<unknown>,\r\n filePath: string,\r\n defaultRedirectTo?: string\r\n): RouteObject[\"lazy\"] {\r\n return async () => {\r\n const mod = (await loader()) as LazyModule;\r\n if (!mod.default) {\r\n warnNoDefault(filePath);\r\n return { Component: () => null };\r\n }\r\n const result: Record<string, unknown> = { Component: mod.default };\r\n if (mod.ErrorBoundary) result.ErrorBoundary = mod.ErrorBoundary;\r\n if (mod.handle !== undefined) result.handle = mod.handle;\r\n\r\n if (mod.protect) {\r\n const check = typeof mod.protect === \"function\" ? mod.protect : mod.protect.check;\r\n const redirectTo =\r\n (typeof mod.protect === \"object\" && mod.protect.redirectTo != null\r\n ? mod.protect.redirectTo\r\n : defaultRedirectTo) ?? \"/\";\r\n result.loader = async () => {\r\n const ok = await Promise.resolve(check());\r\n if (!ok) throw redirect(redirectTo);\r\n return null;\r\n };\r\n }\r\n\r\n return result as {\r\n Component: React.ComponentType;\r\n ErrorBoundary?: React.ComponentType;\r\n handle?: unknown;\r\n loader?: RouteObject[\"loader\"];\r\n };\r\n };\r\n}\r\n\r\nfunction nodeToRoutes(\r\n node: RouteNode,\r\n pathPrefix: string,\r\n pagesGlob: PagesGlob,\r\n defaultRedirectTo?: string\r\n): RouteObject[] {\r\n const childRouteObjects: RouteObject[] = [];\r\n\r\n if (node.indexRoute) {\r\n const loader = pagesGlob[node.indexRoute.path];\r\n if (loader) {\r\n childRouteObjects.push({\r\n index: true,\r\n lazy: makeLazyRoute(loader, node.indexRoute.path, defaultRedirectTo),\r\n });\r\n }\r\n }\r\n\r\n const childSegments = Array.from(node.children.entries());\r\n childSegments.sort(([a], [b]) => compareSegments(parseSegment(a), parseSegment(b)));\r\n\r\n for (const [segStr, { entry }] of childSegments) {\r\n const seg = parseSegment(segStr);\r\n const pathSeg = segmentToPath(seg);\r\n const loader = pagesGlob[entry.path];\r\n if (!loader) continue;\r\n childRouteObjects.push({\r\n path: pathSeg,\r\n lazy: makeLazyRoute(loader, entry.path, defaultRedirectTo),\r\n });\r\n }\r\n\r\n const sortedLayoutKeys = Array.from(node.childLayouts.keys()).sort((a, b) => {\r\n const segsA = a.split(\"/\").map(parseSegment);\r\n const segsB = b.split(\"/\").map(parseSegment);\r\n const len = Math.min(segsA.length, segsB.length);\r\n for (let i = 0; i < len; i++) {\r\n const c = compareSegments(segsA[i], segsB[i]);\r\n if (c !== 0) return c;\r\n }\r\n return segsA.length - segsB.length;\r\n });\r\n\r\n for (const key of sortedLayoutKeys) {\r\n const childNode = node.childLayouts.get(key)!;\r\n const firstSeg = key.split(\"/\")[0];\r\n const seg = parseSegment(firstSeg);\r\n const pathSeg = segmentToPath(seg);\r\n const nested = nodeToRoutes(childNode, pathPrefix ? pathPrefix + \"/\" + pathSeg : pathSeg, pagesGlob, defaultRedirectTo);\r\n if (childNode.layout) {\r\n if (childNode.layout.path in pagesGlob) {\r\n childRouteObjects.push(...nested);\r\n } else {\r\n childRouteObjects.push({ path: pathSeg, children: nested });\r\n }\r\n } else if (nested.length > 0) {\r\n childRouteObjects.push({ path: pathSeg, children: nested });\r\n }\r\n }\r\n\r\n if (node.layout) {\r\n if (node.layout.path in pagesGlob) {\r\n const layoutLoader = pagesGlob[node.layout.path];\r\n return [{\r\n path: pathPrefix === \"\" ? \"/\" : pathPrefix,\r\n lazy: makeLazyRoute(layoutLoader, node.layout.path, defaultRedirectTo),\r\n children: childRouteObjects.length ? childRouteObjects : undefined,\r\n }];\r\n }\r\n }\r\n\r\n return childRouteObjects;\r\n}\r\n\r\nfunction flattenRootChildren(root: RouteNode, pagesGlob: PagesGlob, defaultRedirectTo?: string): RouteObject[] {\r\n const result: RouteObject[] = [];\r\n\r\n if (root.indexRoute) {\r\n const loader = pagesGlob[root.indexRoute.path];\r\n if (loader) {\r\n result.push({\r\n index: true,\r\n lazy: makeLazyRoute(loader, root.indexRoute.path, defaultRedirectTo),\r\n });\r\n }\r\n }\r\n\r\n const childSegments = Array.from(root.children.entries());\r\n childSegments.sort(([a], [b]) => compareSegments(parseSegment(a), parseSegment(b)));\r\n\r\n for (const [segStr, { entry }] of childSegments) {\r\n const seg = parseSegment(segStr);\r\n const pathSeg = segmentToPath(seg);\r\n const loader = pagesGlob[entry.path];\r\n if (!loader) continue;\r\n result.push({\r\n path: pathSeg,\r\n lazy: makeLazyRoute(loader, entry.path, defaultRedirectTo),\r\n });\r\n }\r\n\r\n const layoutKeys = Array.from(root.childLayouts.keys()).sort((a, b) => {\r\n const segsA = a.split(\"/\").map(parseSegment);\r\n const segsB = b.split(\"/\").map(parseSegment);\r\n const len = Math.min(segsA.length, segsB.length);\r\n for (let i = 0; i < len; i++) {\r\n const c = compareSegments(segsA[i], segsB[i]);\r\n if (c !== 0) return c;\r\n }\r\n return segsA.length - segsB.length;\r\n });\r\n\r\n for (const key of layoutKeys) {\r\n const childNode = root.childLayouts.get(key)!;\r\n const firstSeg = key.split(\"/\")[0];\r\n const seg = parseSegment(firstSeg);\r\n const pathSeg = segmentToPath(seg);\r\n const nested = nodeToRoutes(childNode, pathSeg, pagesGlob, defaultRedirectTo);\r\n if (childNode.layout) {\r\n if (childNode.layout.path in pagesGlob) {\r\n result.push(...nested);\r\n } else {\r\n result.push({ path: pathSeg, children: nested });\r\n }\r\n } else if (nested.length > 0) {\r\n result.push({ path: pathSeg, children: nested });\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\nexport function createRoutes(options: CreateRoutesOptions): RouteObject[] {\r\n const pagesDir = normalizePath(options.pagesDir ?? DEFAULT_PAGES_DIR).replace(/\\/$/, \"\");\r\n const opts = {\r\n pagesDir,\r\n layoutFileName: options.layoutFileName ?? DEFAULT_LAYOUT_FILE,\r\n notFoundFileName: options.notFoundFileName ?? DEFAULT_NOT_FOUND_FILE,\r\n routeExtensions: options.routeExtensions ?? DEFAULT_EXTENSIONS,\r\n };\r\n\r\n const entries: FileEntry[] = [];\r\n for (const key of Object.keys(options.pagesGlob)) {\r\n const entry = parseGlobKey(key, opts);\r\n if (entry) entries.push(entry);\r\n }\r\n\r\n const notFoundEntry = entries.find((e) => e.is404);\r\n const rest = entries.filter((e) => !e.is404);\r\n const root = buildTree(rest);\r\n\r\n let topLevelRoutes: RouteObject[];\r\n\r\n if (root.layout && root.layout.path in options.pagesGlob) {\r\n const layoutLoader = options.pagesGlob[root.layout.path];\r\n topLevelRoutes = [{\r\n path: \"/\",\r\n lazy: makeLazyRoute(layoutLoader, root.layout.path, options.defaultRedirectTo),\r\n children: flattenRootChildren(root, options.pagesGlob, options.defaultRedirectTo),\r\n }];\r\n } else {\r\n topLevelRoutes = flattenRootChildren(root, options.pagesGlob, options.defaultRedirectTo);\r\n }\r\n\r\n if (notFoundEntry && notFoundEntry.path in options.pagesGlob) {\r\n const loader = options.pagesGlob[notFoundEntry.path];\r\n topLevelRoutes.push({\r\n path: \"*\",\r\n lazy: makeLazyRoute(loader, notFoundEntry.path, options.defaultRedirectTo),\r\n });\r\n }\r\n\r\n return topLevelRoutes;\r\n}\r\n","/**\r\n * Normalize path to use forward slashes (Windows-safe).\r\n */\r\nexport function normalizePath(p: string): string {\r\n return p.replace(/\\\\/g, \"/\");\r\n}\r\n\r\n/**\r\n * Get route extension from filename (e.g. \".tsx\") or null if not a route file.\r\n */\r\nexport function getRouteExtension(\r\n filename: string,\r\n extensions: string[]\r\n): string | null {\r\n const lower = filename.toLowerCase();\r\n for (const ext of extensions) {\r\n if (lower.endsWith(ext)) return ext;\r\n }\r\n return null;\r\n}\r\n\r\nexport type SegmentKind = \"static\" | \"dynamic\" | \"splat\";\r\n\r\nexport interface ParsedSegment {\r\n /** Original segment string, e.g. \"blog\", \"[id]\", \"[...slug]\" */\r\n raw: string;\r\n kind: SegmentKind;\r\n /** For dynamic: \"id\"; for splat: \"slug\"; for static: same as raw */\r\n paramName?: string;\r\n}\r\n\r\n/**\r\n * Parse a single path segment into kind and param name.\r\n */\r\nexport function parseSegment(segment: string): ParsedSegment {\r\n if (segment.startsWith(\"[...\") && segment.endsWith(\"]\")) {\r\n const param = segment.slice(4, -1);\r\n return { raw: segment, kind: \"splat\", paramName: param || \"splat\" };\r\n }\r\n if (segment.startsWith(\"[\") && segment.endsWith(\"]\")) {\r\n const param = segment.slice(1, -1);\r\n return { raw: segment, kind: \"dynamic\", paramName: param || \"param\" };\r\n }\r\n return { raw: segment, kind: \"static\" };\r\n}\r\n\r\n/**\r\n * Compare two segments for sort order: static < dynamic < splat.\r\n */\r\nexport function compareSegments(a: ParsedSegment, b: ParsedSegment): number {\r\n const order = { static: 0, dynamic: 1, splat: 2 };\r\n const diff = order[a.kind] - order[b.kind];\r\n if (diff !== 0) return diff;\r\n return a.raw.localeCompare(b.raw);\r\n}\r\n","import { useEffect, useState, type ReactNode } from \"react\";\r\nimport { useNavigate } from \"react-router\";\r\n\r\nexport interface ProtectProps {\r\n /** Sync or async function that returns true to allow access, false to redirect. */\r\n condition: () => boolean | Promise<boolean>;\r\n /** Where to redirect when condition returns false. */\r\n redirectTo: string;\r\n /** Optional content to show while an async condition is pending (e.g. a spinner). */\r\n fallback?: ReactNode;\r\n /** Content to render when condition is true. */\r\n children: ReactNode;\r\n}\r\n\r\n/**\r\n * Reusable protection config: condition, redirectTo, and optional fallback.\r\n * Define once in a config file and spread into <Protect {...config}>.\r\n */\r\nexport type ProtectConfig = Pick<ProtectProps, \"condition\" | \"redirectTo\" | \"fallback\">;\r\n\r\n/**\r\n * Protects content by checking a condition before rendering. If the condition\r\n * returns false (sync or async), redirects to redirectTo. Use this to guard\r\n * a single route's content or an entire layout's <Outlet />.\r\n */\r\nexport function Protect({ condition, redirectTo, fallback = null, children }: ProtectProps) {\r\n const navigate = useNavigate();\r\n const [allowed, setAllowed] = useState<boolean | null>(null);\r\n\r\n useEffect(() => {\r\n let cancelled = false;\r\n const result = condition();\r\n if (typeof result === \"boolean\") {\r\n if (!cancelled) setAllowed(result);\r\n if (!result) navigate(redirectTo, { replace: true });\r\n return;\r\n }\r\n result.then((ok) => {\r\n if (!cancelled) setAllowed(ok);\r\n if (!ok) navigate(redirectTo, { replace: true });\r\n });\r\n return () => {\r\n cancelled = true;\r\n };\r\n }, [condition, redirectTo, navigate]);\r\n\r\n if (allowed === null) return <>{fallback}</>;\r\n if (!allowed) return null;\r\n return <>{children}</>;\r\n}\r\n"],"mappings":"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","names":["node","segs"]}
1
+ {"version":3,"sources":["../src/createRoutes.ts","../src/pathUtils.ts","../src/Protect.tsx","../src/LinkrApp.tsx"],"sourcesContent":["import type { RouteObject } from \"react-router\";\r\nimport { redirect } from \"react-router\";\r\nimport type { CreateRoutesOptions, LayoutMap, LayoutsGlob, PagesGlob, RouteProtect } from \"./types\";\r\nimport {\r\n normalizePath,\r\n getRouteExtension,\r\n parseSegment,\r\n compareSegments,\r\n type ParsedSegment,\r\n} from \"./pathUtils\";\r\n\r\nconst DEFAULT_PAGES_DIR = \"src/pages\";\r\nconst DEFAULT_LAYOUTS_DIR = \"src/layouts\";\r\nconst DEFAULT_LAYOUT_FILE = \"_layout\";\r\nconst DEFAULT_NOT_FOUND_FILE = \"404\";\r\nconst DEFAULT_EXTENSIONS = [\".tsx\", \".ts\", \".jsx\", \".js\"];\r\n\r\n/** Build layout name -> loader from layoutsGlob. Layout name = filename without ext (one level). */\r\nfunction parseLayoutsGlob(\r\n layoutsGlob: LayoutsGlob,\r\n layoutsDir: string,\r\n extensions: string[]\r\n): Map<string, () => Promise<unknown>> {\r\n const map = new Map<string, () => Promise<unknown>>();\r\n const dir = normalizePath(layoutsDir).replace(/\\/$/, \"\");\r\n for (const key of Object.keys(layoutsGlob)) {\r\n const normalized = normalizePath(key).replace(/^\\.\\//, \"\");\r\n let relative = normalized;\r\n if (relative.startsWith(dir + \"/\")) relative = relative.slice(dir.length + 1);\r\n else if (relative.startsWith(dir)) relative = relative.slice(dir.length).replace(/^\\//, \"\");\r\n const ext = getRouteExtension(relative, extensions);\r\n if (!ext) continue;\r\n const withoutExt = relative.slice(0, -ext.length);\r\n const parts = withoutExt.split(\"/\").filter(Boolean);\r\n if (parts.length === 0) continue;\r\n const name = parts[parts.length - 1];\r\n map.set(name, layoutsGlob[key]);\r\n }\r\n return map;\r\n}\r\n\r\ntype LazyModule = {\r\n default?: React.ComponentType;\r\n ErrorBoundary?: React.ComponentType;\r\n handle?: unknown;\r\n protect?: RouteProtect;\r\n};\r\n\r\n/**\r\n * Strip extension from filename.\r\n */\r\nfunction stripExtension(filename: string, extensions: string[]): string {\r\n const lower = filename.toLowerCase();\r\n for (const ext of extensions) {\r\n if (lower.endsWith(ext)) return filename.slice(0, -ext.length);\r\n }\r\n return filename;\r\n}\r\n\r\n/**\r\n * Check if segment is a valid splat (exactly \"[...name]\").\r\n */\r\nfunction isValidSplatSegment(segment: string): boolean {\r\n return /^\\[\\.\\.\\.[^\\]]*\\]$/.test(segment);\r\n}\r\n\r\n/**\r\n * Warn in dev when a module has no default export (called from lazy).\r\n */\r\nfunction warnNoDefault(path: string): void {\r\n if (typeof process !== \"undefined\" && process.env?.NODE_ENV !== \"production\") {\r\n console.warn(`[linkr] Module has no default export, skipping route: ${path}`);\r\n }\r\n}\r\n\r\n/**\r\n * Warn when [...slug] is used with other segments in the same filename.\r\n */\r\nfunction warnInvalidSplat(path: string): void {\r\n if (typeof process !== \"undefined\" && process.env?.NODE_ENV !== \"production\") {\r\n console.warn(\r\n `[linkr] Invalid splat usage (e.g. foo[...slug].tsx); use only [...slug].tsx in filename. Skipping: ${path}`\r\n );\r\n }\r\n}\r\n\r\ninterface FileEntry {\r\n path: string;\r\n segments: string[];\r\n isLayout: boolean;\r\n is404: boolean;\r\n isIndex: boolean;\r\n segmentKinds: ReturnType<typeof parseSegment>[];\r\n}\r\n\r\nfunction parseGlobKey(\r\n key: string,\r\n opts: Required<Pick<CreateRoutesOptions, \"pagesDir\" | \"layoutFileName\" | \"notFoundFileName\" | \"routeExtensions\">>\r\n): FileEntry | null {\r\n const normalized = normalizePath(key);\r\n const withoutLeadingSlash = normalized.replace(/^\\.\\//, \"\");\r\n const dir = normalizePath(opts.pagesDir).replace(/\\/$/, \"\");\r\n let relative = withoutLeadingSlash;\r\n if (relative.startsWith(dir + \"/\")) {\r\n relative = relative.slice(dir.length + 1);\r\n } else if (relative.startsWith(dir)) {\r\n relative = relative.slice(dir.length).replace(/^\\//, \"\");\r\n } else if (dir.includes(\"/\")) {\r\n const lastDirSegment = dir.split(\"/\").pop()!;\r\n if (relative.startsWith(lastDirSegment + \"/\")) {\r\n relative = relative.slice(lastDirSegment.length + 1);\r\n }\r\n }\r\n\r\n const ext = getRouteExtension(relative, opts.routeExtensions);\r\n if (!ext) return null;\r\n\r\n const withoutExt = relative.slice(0, -ext.length);\r\n const parts = withoutExt.split(\"/\").filter(Boolean);\r\n if (parts.length === 0) return null;\r\n\r\n const lastPart = parts[parts.length - 1];\r\n const isLayout = stripExtension(lastPart, opts.routeExtensions) === opts.layoutFileName;\r\n const is404 = stripExtension(lastPart, opts.routeExtensions) === opts.notFoundFileName;\r\n const isIndex = lastPart.toLowerCase().replace(new RegExp(\"\\\\\" + ext + \"$\", \"i\"), \"\") === \"index\";\r\n const segmentParts = isIndex ? parts.slice(0, -1) : parts;\r\n const segmentStrings = segmentParts.map((p, i) => {\r\n const isLast = i === segmentParts.length - 1;\r\n return isLast ? stripExtension(p, opts.routeExtensions) : p;\r\n });\r\n\r\n const segmentKinds = segmentStrings.map(parseSegment);\r\n for (const s of segmentStrings) {\r\n if (s.includes(\"[...\") && !isValidSplatSegment(s)) {\r\n warnInvalidSplat(key);\r\n return null;\r\n }\r\n }\r\n\r\n return {\r\n path: key,\r\n segments: segmentStrings,\r\n isLayout,\r\n is404,\r\n isIndex,\r\n segmentKinds,\r\n };\r\n}\r\n\r\ninterface RouteNode {\r\n path: string;\r\n layout: FileEntry | null;\r\n indexRoute: FileEntry | null;\r\n children: Map<string, { entry: FileEntry }>;\r\n childLayouts: Map<string, RouteNode>;\r\n}\r\n\r\nfunction buildTree(entries: FileEntry[]): RouteNode {\r\n const root: RouteNode = {\r\n path: \"\",\r\n layout: null,\r\n indexRoute: null,\r\n children: new Map(),\r\n childLayouts: new Map(),\r\n };\r\n\r\n for (const entry of entries) {\r\n if (entry.is404) continue;\r\n if (entry.isLayout) {\r\n const layoutSegments = entry.segments.slice(0, -1);\r\n let node = root;\r\n for (let i = 0; i < layoutSegments.length; i++) {\r\n const key = layoutSegments.slice(0, i + 1).join(\"/\");\r\n if (!node.childLayouts.has(key)) {\r\n node.childLayouts.set(key, {\r\n path: key,\r\n layout: null,\r\n indexRoute: null,\r\n children: new Map(),\r\n childLayouts: new Map(),\r\n });\r\n }\r\n node = node.childLayouts.get(key)!;\r\n }\r\n node.layout = entry;\r\n continue;\r\n }\r\n\r\n if (entry.isIndex && entry.segments.length === 0) {\r\n root.indexRoute = entry;\r\n continue;\r\n }\r\n\r\n if (entry.isIndex) {\r\n let node = root;\r\n const segs = entry.segments;\r\n for (let i = 0; i < segs.length; i++) {\r\n const key = segs.slice(0, i + 1).join(\"/\");\r\n if (node.childLayouts.has(key)) {\r\n node = node.childLayouts.get(key)!;\r\n } else {\r\n const next: RouteNode = {\r\n path: key,\r\n layout: null,\r\n indexRoute: null,\r\n children: new Map(),\r\n childLayouts: new Map(),\r\n };\r\n node.childLayouts.set(key, next);\r\n node = next;\r\n }\r\n }\r\n node.indexRoute = entry;\r\n continue;\r\n }\r\n\r\n const segs = entry.segments;\r\n let node = root;\r\n for (let i = 0; i < segs.length - 1; i++) {\r\n const key = segs.slice(0, i + 1).join(\"/\");\r\n if (node.childLayouts.has(key)) {\r\n node = node.childLayouts.get(key)!;\r\n } else {\r\n const next: RouteNode = {\r\n path: key,\r\n layout: null,\r\n indexRoute: null,\r\n children: new Map(),\r\n childLayouts: new Map(),\r\n };\r\n node.childLayouts.set(key, next);\r\n node = next;\r\n }\r\n }\r\n const lastSeg = segs[segs.length - 1];\r\n node.children.set(lastSeg, { entry });\r\n }\r\n\r\n return root;\r\n}\r\n\r\nfunction segmentToPath(segment: ParsedSegment): string {\r\n if (segment.kind === \"static\") return segment.raw;\r\n if (segment.kind === \"dynamic\") return \":\" + (segment.paramName ?? \"param\");\r\n if (segment.kind === \"splat\") return \"*\";\r\n return segment.raw;\r\n}\r\n\r\nfunction makeLazyRoute(\r\n loader: () => Promise<unknown>,\r\n filePath: string,\r\n defaultRedirectTo?: string\r\n): RouteObject[\"lazy\"] {\r\n return async () => {\r\n const mod = (await loader()) as LazyModule;\r\n if (!mod.default) {\r\n warnNoDefault(filePath);\r\n return { Component: () => null };\r\n }\r\n const result: Record<string, unknown> = { Component: mod.default };\r\n if (mod.ErrorBoundary) result.ErrorBoundary = mod.ErrorBoundary;\r\n if (mod.handle !== undefined) result.handle = mod.handle;\r\n\r\n if (mod.protect) {\r\n const check = typeof mod.protect === \"function\" ? mod.protect : mod.protect.check;\r\n const redirectTo =\r\n (typeof mod.protect === \"object\" && mod.protect.redirectTo != null\r\n ? mod.protect.redirectTo\r\n : defaultRedirectTo) ?? \"/\";\r\n result.loader = async () => {\r\n const ok = await Promise.resolve(check());\r\n if (!ok) throw redirect(redirectTo);\r\n return null;\r\n };\r\n // React Router v7 requires HydrateFallback when a route has a loader and no hydration data.\r\n result.HydrateFallback = () => null;\r\n }\r\n\r\n return result as {\r\n Component: React.ComponentType;\r\n ErrorBoundary?: React.ComponentType;\r\n handle?: unknown;\r\n loader?: RouteObject[\"loader\"];\r\n HydrateFallback?: React.ComponentType;\r\n };\r\n };\r\n}\r\n\r\nfunction nodeToRoutes(\r\n node: RouteNode,\r\n pathPrefix: string,\r\n pagesGlob: PagesGlob,\r\n defaultRedirectTo?: string,\r\n layoutMap?: LayoutMap,\r\n layoutLoaders?: Map<string, () => Promise<unknown>>\r\n): RouteObject[] {\r\n const childRouteObjects: RouteObject[] = [];\r\n\r\n if (node.indexRoute) {\r\n const loader = pagesGlob[node.indexRoute.path];\r\n if (loader) {\r\n childRouteObjects.push({\r\n index: true,\r\n lazy: makeLazyRoute(loader, node.indexRoute.path, defaultRedirectTo),\r\n });\r\n }\r\n }\r\n\r\n const childSegments = Array.from(node.children.entries());\r\n childSegments.sort(([a], [b]) => compareSegments(parseSegment(a), parseSegment(b)));\r\n\r\n for (const [segStr, { entry }] of childSegments) {\r\n const seg = parseSegment(segStr);\r\n const pathSeg = segmentToPath(seg);\r\n const loader = pagesGlob[entry.path];\r\n if (!loader) continue;\r\n childRouteObjects.push({\r\n path: pathSeg,\r\n lazy: makeLazyRoute(loader, entry.path, defaultRedirectTo),\r\n });\r\n }\r\n\r\n const sortedLayoutKeys = Array.from(node.childLayouts.keys()).sort((a, b) => {\r\n const segsA = a.split(\"/\").map(parseSegment);\r\n const segsB = b.split(\"/\").map(parseSegment);\r\n const len = Math.min(segsA.length, segsB.length);\r\n for (let i = 0; i < len; i++) {\r\n const c = compareSegments(segsA[i], segsB[i]);\r\n if (c !== 0) return c;\r\n }\r\n return segsA.length - segsB.length;\r\n });\r\n\r\n for (const key of sortedLayoutKeys) {\r\n const childNode = node.childLayouts.get(key)!;\r\n const firstSeg = key.split(\"/\")[0];\r\n const seg = parseSegment(firstSeg);\r\n const pathSeg = segmentToPath(seg);\r\n const nested = nodeToRoutes(\r\n childNode,\r\n pathPrefix ? pathPrefix + \"/\" + pathSeg : pathSeg,\r\n pagesGlob,\r\n defaultRedirectTo,\r\n layoutMap,\r\n layoutLoaders\r\n );\r\n const layoutName = layoutMap && layoutLoaders ? layoutMap[key] ?? layoutMap[\"/\" + key] : undefined;\r\n const layoutLoader = layoutName ? layoutLoaders!.get(layoutName) : undefined;\r\n if (layoutLoader) {\r\n childRouteObjects.push({\r\n path: pathSeg,\r\n lazy: makeLazyRoute(layoutLoader, \"layout:\" + layoutName, defaultRedirectTo),\r\n children: nested,\r\n });\r\n } else if (childNode.layout && childNode.layout.path in pagesGlob) {\r\n childRouteObjects.push(...nested);\r\n } else if (childNode.layout) {\r\n childRouteObjects.push({ path: pathSeg, children: nested });\r\n } else if (nested.length > 0) {\r\n childRouteObjects.push({ path: pathSeg, children: nested });\r\n }\r\n }\r\n\r\n if (node.layout && node.layout.path in pagesGlob) {\r\n const layoutLoader = pagesGlob[node.layout.path];\r\n return [{\r\n path: pathPrefix === \"\" ? \"/\" : pathPrefix,\r\n lazy: makeLazyRoute(layoutLoader, node.layout.path, defaultRedirectTo),\r\n children: childRouteObjects.length ? childRouteObjects : undefined,\r\n }];\r\n }\r\n\r\n return childRouteObjects;\r\n}\r\n\r\nfunction flattenRootChildren(\r\n root: RouteNode,\r\n pagesGlob: PagesGlob,\r\n defaultRedirectTo?: string,\r\n layoutMap?: LayoutMap,\r\n layoutLoaders?: Map<string, () => Promise<unknown>>\r\n): RouteObject[] {\r\n const result: RouteObject[] = [];\r\n\r\n if (root.indexRoute) {\r\n const loader = pagesGlob[root.indexRoute.path];\r\n if (loader) {\r\n result.push({\r\n index: true,\r\n lazy: makeLazyRoute(loader, root.indexRoute.path, defaultRedirectTo),\r\n });\r\n }\r\n }\r\n\r\n const childSegments = Array.from(root.children.entries());\r\n childSegments.sort(([a], [b]) => compareSegments(parseSegment(a), parseSegment(b)));\r\n\r\n for (const [segStr, { entry }] of childSegments) {\r\n const seg = parseSegment(segStr);\r\n const pathSeg = segmentToPath(seg);\r\n const loader = pagesGlob[entry.path];\r\n if (!loader) continue;\r\n result.push({\r\n path: pathSeg,\r\n lazy: makeLazyRoute(loader, entry.path, defaultRedirectTo),\r\n });\r\n }\r\n\r\n const layoutKeys = Array.from(root.childLayouts.keys()).sort((a, b) => {\r\n const segsA = a.split(\"/\").map(parseSegment);\r\n const segsB = b.split(\"/\").map(parseSegment);\r\n const len = Math.min(segsA.length, segsB.length);\r\n for (let i = 0; i < len; i++) {\r\n const c = compareSegments(segsA[i], segsB[i]);\r\n if (c !== 0) return c;\r\n }\r\n return segsA.length - segsB.length;\r\n });\r\n\r\n for (const key of layoutKeys) {\r\n const childNode = root.childLayouts.get(key)!;\r\n const firstSeg = key.split(\"/\")[0];\r\n const seg = parseSegment(firstSeg);\r\n const pathSeg = segmentToPath(seg);\r\n const nested = nodeToRoutes(\r\n childNode,\r\n pathSeg,\r\n pagesGlob,\r\n defaultRedirectTo,\r\n layoutMap,\r\n layoutLoaders\r\n );\r\n const layoutName = layoutMap && layoutLoaders ? layoutMap[key] ?? layoutMap[\"/\" + key] : undefined;\r\n const layoutLoader = layoutName ? layoutLoaders!.get(layoutName) : undefined;\r\n if (layoutLoader) {\r\n result.push({\r\n path: pathSeg,\r\n lazy: makeLazyRoute(layoutLoader, \"layout:\" + layoutName, defaultRedirectTo),\r\n children: nested,\r\n });\r\n } else if (childNode.layout && childNode.layout.path in pagesGlob) {\r\n result.push(...nested);\r\n } else if (childNode.layout) {\r\n result.push({ path: pathSeg, children: nested });\r\n } else if (nested.length > 0) {\r\n result.push({ path: pathSeg, children: nested });\r\n }\r\n }\r\n\r\n const rootLayoutName = layoutMap && layoutLoaders ? layoutMap[\"\"] ?? layoutMap[\"/\"] : undefined;\r\n const rootLayoutLoader = rootLayoutName ? layoutLoaders!.get(rootLayoutName) : undefined;\r\n if (rootLayoutLoader && result.length > 0) {\r\n return [{\r\n path: \"/\",\r\n lazy: makeLazyRoute(rootLayoutLoader, \"layout:\" + rootLayoutName, defaultRedirectTo),\r\n children: result,\r\n }];\r\n }\r\n return result;\r\n}\r\n\r\nexport function createRoutes(options: CreateRoutesOptions): RouteObject[] {\r\n const pagesDir = normalizePath(options.pagesDir ?? DEFAULT_PAGES_DIR).replace(/\\/$/, \"\");\r\n const opts = {\r\n pagesDir,\r\n layoutFileName: options.layoutFileName ?? DEFAULT_LAYOUT_FILE,\r\n notFoundFileName: options.notFoundFileName ?? DEFAULT_NOT_FOUND_FILE,\r\n routeExtensions: options.routeExtensions ?? DEFAULT_EXTENSIONS,\r\n };\r\n\r\n const entries: FileEntry[] = [];\r\n for (const key of Object.keys(options.pagesGlob)) {\r\n const entry = parseGlobKey(key, opts);\r\n if (entry) entries.push(entry);\r\n }\r\n\r\n const notFoundEntry = entries.find((e) => e.is404);\r\n const useLayoutsFolder = options.layoutsGlob != null && options.layoutMap != null;\r\n const rest = useLayoutsFolder\r\n ? entries.filter((e) => !e.is404 && !e.isLayout)\r\n : entries.filter((e) => !e.is404);\r\n const root = buildTree(rest);\r\n\r\n const layoutLoaders =\r\n options.layoutsGlob && options.layoutMap\r\n ? parseLayoutsGlob(\r\n options.layoutsGlob,\r\n options.layoutsDir ?? DEFAULT_LAYOUTS_DIR,\r\n opts.routeExtensions\r\n )\r\n : undefined;\r\n\r\n let topLevelRoutes: RouteObject[];\r\n\r\n if (layoutLoaders && options.layoutMap) {\r\n topLevelRoutes = flattenRootChildren(\r\n root,\r\n options.pagesGlob,\r\n options.defaultRedirectTo,\r\n options.layoutMap,\r\n layoutLoaders\r\n );\r\n } else if (root.layout && root.layout.path in options.pagesGlob) {\r\n const layoutLoader = options.pagesGlob[root.layout.path];\r\n topLevelRoutes = [{\r\n path: \"/\",\r\n lazy: makeLazyRoute(layoutLoader, root.layout.path, options.defaultRedirectTo),\r\n children: flattenRootChildren(root, options.pagesGlob, options.defaultRedirectTo),\r\n }];\r\n } else {\r\n topLevelRoutes = flattenRootChildren(root, options.pagesGlob, options.defaultRedirectTo);\r\n }\r\n\r\n if (notFoundEntry && notFoundEntry.path in options.pagesGlob) {\r\n const loader = options.pagesGlob[notFoundEntry.path];\r\n topLevelRoutes.push({\r\n path: \"*\",\r\n lazy: makeLazyRoute(loader, notFoundEntry.path, options.defaultRedirectTo),\r\n });\r\n }\r\n\r\n return topLevelRoutes;\r\n}\r\n","/**\r\n * Normalize path to use forward slashes (Windows-safe).\r\n */\r\nexport function normalizePath(p: string): string {\r\n return p.replace(/\\\\/g, \"/\");\r\n}\r\n\r\n/**\r\n * Get route extension from filename (e.g. \".tsx\") or null if not a route file.\r\n */\r\nexport function getRouteExtension(\r\n filename: string,\r\n extensions: string[]\r\n): string | null {\r\n const lower = filename.toLowerCase();\r\n for (const ext of extensions) {\r\n if (lower.endsWith(ext)) return ext;\r\n }\r\n return null;\r\n}\r\n\r\nexport type SegmentKind = \"static\" | \"dynamic\" | \"splat\";\r\n\r\nexport interface ParsedSegment {\r\n /** Original segment string, e.g. \"blog\", \"[id]\", \"[...slug]\" */\r\n raw: string;\r\n kind: SegmentKind;\r\n /** For dynamic: \"id\"; for splat: \"slug\"; for static: same as raw */\r\n paramName?: string;\r\n}\r\n\r\n/**\r\n * Parse a single path segment into kind and param name.\r\n */\r\nexport function parseSegment(segment: string): ParsedSegment {\r\n if (segment.startsWith(\"[...\") && segment.endsWith(\"]\")) {\r\n const param = segment.slice(4, -1);\r\n return { raw: segment, kind: \"splat\", paramName: param || \"splat\" };\r\n }\r\n if (segment.startsWith(\"[\") && segment.endsWith(\"]\")) {\r\n const param = segment.slice(1, -1);\r\n return { raw: segment, kind: \"dynamic\", paramName: param || \"param\" };\r\n }\r\n return { raw: segment, kind: \"static\" };\r\n}\r\n\r\n/**\r\n * Compare two segments for sort order: static < dynamic < splat.\r\n */\r\nexport function compareSegments(a: ParsedSegment, b: ParsedSegment): number {\r\n const order = { static: 0, dynamic: 1, splat: 2 };\r\n const diff = order[a.kind] - order[b.kind];\r\n if (diff !== 0) return diff;\r\n return a.raw.localeCompare(b.raw);\r\n}\r\n","import { useEffect, useState, type ReactNode } from \"react\";\r\nimport { useNavigate } from \"react-router\";\r\n\r\nexport interface ProtectProps {\r\n /** Sync or async function that returns true to allow access, false to redirect. */\r\n condition: () => boolean | Promise<boolean>;\r\n /** Where to redirect when condition returns false. */\r\n redirectTo: string;\r\n /** Optional content to show while an async condition is pending (e.g. a spinner). */\r\n fallback?: ReactNode;\r\n /** Content to render when condition is true. */\r\n children: ReactNode;\r\n}\r\n\r\n/**\r\n * Reusable protection config: condition, redirectTo, and optional fallback.\r\n * Define once in a config file and spread into <Protect {...config}>.\r\n */\r\nexport type ProtectConfig = Pick<ProtectProps, \"condition\" | \"redirectTo\" | \"fallback\">;\r\n\r\n/**\r\n * Protects content by checking a condition before rendering. If the condition\r\n * returns false (sync or async), redirects to redirectTo. Use this to guard\r\n * a single route's content or an entire layout's <Outlet />.\r\n */\r\nexport function Protect({ condition, redirectTo, fallback = null, children }: ProtectProps) {\r\n const navigate = useNavigate();\r\n const [allowed, setAllowed] = useState<boolean | null>(null);\r\n\r\n useEffect(() => {\r\n let cancelled = false;\r\n const result = condition();\r\n if (typeof result === \"boolean\") {\r\n if (!cancelled) setAllowed(result);\r\n if (!result) navigate(redirectTo, { replace: true });\r\n return;\r\n }\r\n result.then((ok) => {\r\n if (!cancelled) setAllowed(ok);\r\n if (!ok) navigate(redirectTo, { replace: true });\r\n });\r\n return () => {\r\n cancelled = true;\r\n };\r\n }, [condition, redirectTo, navigate]);\r\n\r\n if (allowed === null) return <>{fallback}</>;\r\n if (!allowed) return null;\r\n return <>{children}</>;\r\n}\r\n","import { StrictMode, useMemo, useRef } from \"react\";\r\nimport { createRoot } from \"react-dom/client\";\r\nimport { createBrowserRouter, RouterProvider } from \"react-router\";\r\nimport { createRoutes } from \"./createRoutes\";\r\nimport type { CreateRoutesOptions, PagesGlob } from \"./types\";\r\n\r\n/**\r\n * Options for LinkrApp and createRootWithLinkr.\r\n * Same as CreateRoutesOptions (pagesGlob required; rest optional).\r\n */\r\nexport type LinkrAppOptions = { pagesGlob: PagesGlob } & Omit<CreateRoutesOptions, \"pagesGlob\">;\r\n\r\n/**\r\n * Renders the file-based router. Options are read once on mount.\r\n * Use this when you need to wrap the app with your own providers.\r\n */\r\nexport function LinkrApp(options: LinkrAppOptions) {\r\n const optionsRef = useRef(options);\r\n optionsRef.current = options;\r\n const router = useMemo(\r\n () => createBrowserRouter(createRoutes(optionsRef.current)),\r\n []\r\n );\r\n return <RouterProvider router={router} />;\r\n}\r\n\r\n/**\r\n * One-shot setup: creates the root, builds routes from pagesGlob, and renders\r\n * with StrictMode + RouterProvider. Minimizes app entry to a single call.\r\n *\r\n * @example\r\n * ```tsx\r\n * import { createRootWithLinkr } from \"@epicabdou/linkr\";\r\n * createRootWithLinkr(document.getElementById(\"root\")!, {\r\n * pagesGlob: import.meta.glob(\"./pages/**\\/*.tsx\"),\r\n * pagesDir: \"pages\",\r\n * });\r\n * ```\r\n */\r\nexport function createRootWithLinkr(\r\n rootElement: HTMLElement,\r\n options: LinkrAppOptions\r\n): ReturnType<typeof createRoot> {\r\n const router = createBrowserRouter(createRoutes(options));\r\n const root = createRoot(rootElement);\r\n root.render(\r\n <StrictMode>\r\n <RouterProvider router={router} />\r\n </StrictMode>\r\n );\r\n return root;\r\n}\r\n"],"mappings":"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","names":["node","segs","jsx"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epicabdou/linkr",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "linkr.js — File-based routing for React Router (v7) with Vite",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",