fs-router-dom 0.0.1 → 0.0.3

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,29 +1,35 @@
1
- # react-file-router
1
+ # fs-router-dom
2
2
 
3
3
  Lightweight file-based router for Vite + React Router DOM, like Next.js.
4
4
 
5
+ ## What's New in 0.0.3
6
+
7
+ - **Nested Routes**: Routes are now automatically nested, providing better support for `react-router-dom` features and improving route management.
8
+ - **Nested Layout Support**: Create complex and reusable UI structures with nested layouts that apply to specific sections of your application.
9
+ - **Custom 404 and Suspense Fallback**: You can now provide your own custom components for 404 (Not Found) pages and suspense fallbacks, allowing for a more consistent and branded user experience.
10
+
5
11
  ## Installation
6
12
  ```
7
- npm install react-file-router
13
+ npm install fs-router-dom
8
14
  ```
9
15
 
10
16
  ## Usage
11
17
 
12
- ### `<FileRouter />`
18
+ ### `<FSRouter />`
13
19
 
14
- The `FileRouter` component dynamically creates `react-router-dom` routes from a list of file-based modules. It requires being a child of a `BrowserRouter` (or another `react-router` router).
20
+ The `FSRouter` component dynamically creates `react-router-dom` routes from a list of file-based modules. It requires being a child of a `BrowserRouter` (or another `react-router` router).
15
21
 
16
22
  ```jsx
17
23
  // App.tsx
18
24
  import { BrowserRouter } from 'react-router-dom';
19
- import { FileRouter } from 'react-file-router';
25
+ import { FSRouter } from 'fs-router-dom';
20
26
 
21
27
  const routes = import.meta.glob('./pages/**/*.tsx');
22
28
 
23
29
  function App() {
24
30
  return (
25
31
  <BrowserRouter basename="/app">
26
- <FileRouter routes={routes} />
32
+ <FSRouter routes={routes} />
27
33
  </BrowserRouter>
28
34
  );
29
35
  }
@@ -31,22 +37,22 @@ function App() {
31
37
  export default App;
32
38
  ```
33
39
 
34
- ### `<FullFileRouter />`
40
+ ### `<FullFSRouter />`
35
41
 
36
- The `FullFileRouter` component is a "batteries-included" component for users who want to quickly set up file-based routing without configuring `react-router-dom`'s `BrowserRouter` separately. It instantiates a `BrowserRouter` and places the `FileRouter` inside it, handling the setup in one step.
42
+ The `FullFSRouter` component is a "batteries-included" component for users who want to quickly set up file-based routing without configuring `react-router-dom`'s `BrowserRouter` separately. It instantiates a `BrowserRouter` and places the `FSRouter` inside it, handling the setup in one step.
37
43
 
38
- `FullFileRouter` accepts two types of props:
39
- - `routes`: The same `routes` object required by `FileRouter`.
44
+ `FullFSRouter` accepts two types of props:
45
+ - `routes`: The same `routes` object required by `FSRouter`.
40
46
  - All props accepted by `react-router-dom`'s `BrowserRouter` (e.g., `basename`, `window`).
41
47
 
42
48
  ```jsx
43
49
  // App.tsx
44
- import { FullFileRouter } from 'react-file-router';
50
+ import { FullFSRouter } from 'fs-router-dom';
45
51
 
46
52
  const routes = import.meta.glob('./pages/**/*.tsx');
47
53
 
48
54
  function App() {
49
- return <FullFileRouter routes={routes} basename="/app" />;
55
+ return <FullFSRouter routes={routes} basename="/app" />;
50
56
  }
51
57
 
52
58
  export default App;
@@ -0,0 +1,14 @@
1
+ import React, { ComponentType } from "react";
2
+ import { BrowserRouterProps } from "react-router-dom";
3
+ interface FSRouterProps {
4
+ routes: Record<string, () => Promise<{
5
+ default: ComponentType;
6
+ }>>;
7
+ notFoundComponent?: React.ReactNode;
8
+ suspenseFallback?: React.ReactNode;
9
+ }
10
+ export declare function FSRouter({ routes, notFoundComponent, suspenseFallback, }: FSRouterProps): import("react/jsx-runtime").JSX.Element;
11
+ interface FullFSRouterProps extends FSRouterProps, Omit<BrowserRouterProps, "children"> {
12
+ }
13
+ export declare function FullFSRouter({ routes, notFoundComponent, suspenseFallback, ...browserProps }: FullFSRouterProps): import("react/jsx-runtime").JSX.Element;
14
+ export {};
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.FSRouter = FSRouter;
37
+ exports.FullFSRouter = FullFSRouter;
38
+ const jsx_runtime_1 = require("react/jsx-runtime");
39
+ const react_1 = __importStar(require("react"));
40
+ const react_router_dom_1 = require("react-router-dom");
41
+ const build_route_tree_1 = require("./utils/build-route-tree");
42
+ function FSRouter({ routes, notFoundComponent = (0, jsx_runtime_1.jsx)("div", { children: "404 - Not Found" }), suspenseFallback = (0, jsx_runtime_1.jsx)("div", { children: "Loading..." }), }) {
43
+ const routeTree = (0, build_route_tree_1.buildRouteTree)(routes);
44
+ function renderNode(node) {
45
+ const Component = node.component ? react_1.default.lazy(node.component) : null;
46
+ let element;
47
+ if (node.isLayout && Component) {
48
+ element = ((0, jsx_runtime_1.jsx)(react_1.Suspense, { fallback: suspenseFallback, children: react_1.default.createElement(Component, null, (0, jsx_runtime_1.jsx)(react_router_dom_1.Outlet, {})) }));
49
+ }
50
+ else if (Component) {
51
+ element = ((0, jsx_runtime_1.jsx)(react_1.Suspense, { fallback: suspenseFallback, children: (0, jsx_runtime_1.jsx)(Component, {}) }));
52
+ }
53
+ else if (node.children.length > 0) {
54
+ element = (0, jsx_runtime_1.jsx)(react_router_dom_1.Outlet, {});
55
+ }
56
+ else {
57
+ element = null; // Fallback for groups
58
+ }
59
+ return ((0, jsx_runtime_1.jsx)(react_router_dom_1.Route, { path: node.segment, element: element, children: node.children.map((child) => renderNode(child)) }));
60
+ }
61
+ const rootComponent = routeTree.component
62
+ ? react_1.default.lazy(routeTree.component)
63
+ : null;
64
+ return ((0, jsx_runtime_1.jsxs)(react_router_dom_1.Routes, { children: [rootComponent && ((0, jsx_runtime_1.jsx)(react_router_dom_1.Route, { path: "/", element: (0, jsx_runtime_1.jsx)(react_1.Suspense, { fallback: suspenseFallback, children: react_1.default.createElement(rootComponent) }) })), routeTree.children.map((child) => renderNode(child)), (0, jsx_runtime_1.jsx)(react_router_dom_1.Route, { path: "*", element: notFoundComponent })] }));
65
+ }
66
+ function FullFSRouter({ routes, notFoundComponent, suspenseFallback, ...browserProps }) {
67
+ return ((0, jsx_runtime_1.jsx)(react_router_dom_1.BrowserRouter, { ...browserProps, children: (0, jsx_runtime_1.jsx)(FSRouter, { routes: routes, notFoundComponent: notFoundComponent, suspenseFallback: suspenseFallback }) }));
68
+ }
69
+ //# sourceMappingURL=FSRouter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FSRouter.js","sourceRoot":"","sources":["../src/FSRouter.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgBA,4BAuDC;AAKD,oCAeC;;AA3FD,+CAAuD;AACvD,uDAM0B;AAC1B,+DAAqE;AAQrE,SAAgB,QAAQ,CAAC,EACvB,MAAM,EACN,iBAAiB,GAAG,8DAA0B,EAC9C,gBAAgB,GAAG,yDAAqB,GAC1B;IACd,MAAM,SAAS,GAAG,IAAA,iCAAc,EAAC,MAAM,CAAC,CAAC;IAEzC,SAAS,UAAU,CAAC,IAAe;QACjC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,eAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAErE,IAAI,OAAO,CAAC;QACZ,IAAI,IAAI,CAAC,QAAQ,IAAI,SAAS,EAAE,CAAC;YAC/B,OAAO,GAAG,CACR,uBAAC,gBAAQ,IAAC,QAAQ,EAAE,gBAAgB,YACjC,eAAK,CAAC,aAAa,CAAC,SAAS,EAAE,IAAI,EAAE,uBAAC,yBAAM,KAAG,CAAC,GACxC,CACZ,CAAC;QACJ,CAAC;aAAM,IAAI,SAAS,EAAE,CAAC;YACrB,OAAO,GAAG,CACR,uBAAC,gBAAQ,IAAC,QAAQ,EAAE,gBAAgB,YAClC,uBAAC,SAAS,KAAG,GACJ,CACZ,CAAC;QACJ,CAAC;aAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpC,OAAO,GAAG,uBAAC,yBAAM,KAAG,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,OAAO,GAAG,IAAI,CAAC,CAAC,sBAAsB;QACxC,CAAC;QAED,OAAO,CACL,uBAAC,wBAAK,IAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,YACxC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,GAC1C,CACT,CAAC;IACJ,CAAC;IACD,MAAM,aAAa,GAAG,SAAS,CAAC,SAAS;QACvC,CAAC,CAAC,eAAK,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;QACjC,CAAC,CAAC,IAAI,CAAC;IAET,OAAO,CACL,wBAAC,yBAAM,eACJ,aAAa,IAAI,CAChB,uBAAC,wBAAK,IACJ,IAAI,EAAC,GAAG,EACR,OAAO,EACL,uBAAC,gBAAQ,IAAC,QAAQ,EAAE,gBAAgB,YACjC,eAAK,CAAC,aAAa,CAAC,aAAa,CAAC,GAC1B,GAEb,CACH,EACA,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EACrD,uBAAC,wBAAK,IAAC,IAAI,EAAC,GAAG,EAAC,OAAO,EAAE,iBAAiB,GAAI,IACvC,CACV,CAAC;AACJ,CAAC;AAKD,SAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,iBAAiB,EACjB,gBAAgB,EAChB,GAAG,YAAY,EACG;IAClB,OAAO,CACL,uBAAC,gCAAa,OAAK,YAAY,YAC7B,uBAAC,QAAQ,IACP,MAAM,EAAE,MAAM,EACd,iBAAiB,EAAE,iBAAiB,EACpC,gBAAgB,EAAE,gBAAgB,GAClC,GACY,CACjB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const jsx_runtime_1 = require("react/jsx-runtime");
4
+ const react_1 = require("@testing-library/react");
5
+ const react_router_dom_1 = require("react-router-dom");
6
+ const FSRouter_1 = require("./FSRouter");
7
+ // Mock components
8
+ const Home = () => (0, jsx_runtime_1.jsx)("div", { children: "Home" });
9
+ const About = () => (0, jsx_runtime_1.jsx)("div", { children: "About" });
10
+ const User = () => (0, jsx_runtime_1.jsx)("div", { children: "User" });
11
+ const routes = {
12
+ './pages/index.tsx': () => Promise.resolve({ default: Home }),
13
+ './pages/about.tsx': () => Promise.resolve({ default: About }),
14
+ './pages/users/[id].tsx': () => Promise.resolve({ default: User }),
15
+ };
16
+ describe('FSRouter', () => {
17
+ it('should render the correct component for a static route', async () => {
18
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(react_router_dom_1.MemoryRouter, { initialEntries: ['/about'], children: (0, jsx_runtime_1.jsx)(FSRouter_1.FSRouter, { routes: routes }) }));
19
+ expect(await react_1.screen.findByText('About')).toBeInTheDocument();
20
+ });
21
+ it('should render the correct component for the root route', async () => {
22
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(react_router_dom_1.MemoryRouter, { initialEntries: ['/'], children: (0, jsx_runtime_1.jsx)(FSRouter_1.FSRouter, { routes: routes }) }));
23
+ expect(await react_1.screen.findByText('Home')).toBeInTheDocument();
24
+ });
25
+ it('should render the correct component for a dynamic route', async () => {
26
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(react_router_dom_1.MemoryRouter, { initialEntries: ['/users/123'], children: (0, jsx_runtime_1.jsx)(FSRouter_1.FSRouter, { routes: routes }) }));
27
+ expect(await react_1.screen.findByText('User')).toBeInTheDocument();
28
+ });
29
+ it('should render the default 404 component if route does not exist', async () => {
30
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(react_router_dom_1.MemoryRouter, { initialEntries: ['/this-route-does-not-exist'], children: (0, jsx_runtime_1.jsx)(FSRouter_1.FSRouter, { routes: routes }) }));
31
+ expect(await react_1.screen.findByText('404 - Not Found')).toBeInTheDocument();
32
+ });
33
+ it('should render a custom 404 component if provided', async () => {
34
+ const CustomNotFound = () => (0, jsx_runtime_1.jsx)("div", { children: "Custom Not Found" });
35
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(react_router_dom_1.MemoryRouter, { initialEntries: ['/this-route-does-not-exist'], children: (0, jsx_runtime_1.jsx)(FSRouter_1.FSRouter, { routes: routes, notFoundComponent: (0, jsx_runtime_1.jsx)(CustomNotFound, {}) }) }));
36
+ expect(await react_1.screen.findByText('Custom Not Found')).toBeInTheDocument();
37
+ });
38
+ it('should render a custom suspense fallback if provided', async () => {
39
+ const CustomSuspenseFallback = () => (0, jsx_runtime_1.jsx)("div", { children: "Custom Loading..." });
40
+ const routesWithDelay = {
41
+ './pages/index.tsx': () => new Promise((resolve) => setTimeout(() => resolve({ default: Home }), 100)),
42
+ };
43
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(react_router_dom_1.MemoryRouter, { initialEntries: ['/'], children: (0, jsx_runtime_1.jsx)(FSRouter_1.FSRouter, { routes: routesWithDelay, suspenseFallback: (0, jsx_runtime_1.jsx)(CustomSuspenseFallback, {}) }) }));
44
+ expect(await react_1.screen.findByText('Custom Loading...')).toBeInTheDocument();
45
+ expect(await react_1.screen.findByText('Home')).toBeInTheDocument();
46
+ });
47
+ });
48
+ //# sourceMappingURL=FSRouter.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FSRouter.test.js","sourceRoot":"","sources":["../src/FSRouter.test.tsx"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1 +1 @@
1
- export { FileRouter, FullFileRouter } from './FileRouter';
1
+ export { FSRouter, FullFSRouter } from './FSRouter';
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FullFileRouter = exports.FileRouter = void 0;
4
- var FileRouter_1 = require("./FileRouter");
5
- Object.defineProperty(exports, "FileRouter", { enumerable: true, get: function () { return FileRouter_1.FileRouter; } });
6
- Object.defineProperty(exports, "FullFileRouter", { enumerable: true, get: function () { return FileRouter_1.FullFileRouter; } });
3
+ exports.FullFSRouter = exports.FSRouter = void 0;
4
+ var FSRouter_1 = require("./FSRouter");
5
+ Object.defineProperty(exports, "FSRouter", { enumerable: true, get: function () { return FSRouter_1.FSRouter; } });
6
+ Object.defineProperty(exports, "FullFSRouter", { enumerable: true, get: function () { return FSRouter_1.FullFSRouter; } });
7
7
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,2CAA0D;AAAjD,wGAAA,UAAU,OAAA;AAAE,4GAAA,cAAc,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,uCAAoD;AAA3C,oGAAA,QAAQ,OAAA;AAAE,wGAAA,YAAY,OAAA"}
@@ -0,0 +1,12 @@
1
+ export interface RouteNode {
2
+ segment: string;
3
+ component?: () => Promise<{
4
+ default: React.ComponentType;
5
+ }>;
6
+ isLayout?: boolean;
7
+ isRoot: boolean;
8
+ children: RouteNode[];
9
+ }
10
+ export declare function buildRouteTree(routes: Record<string, () => Promise<{
11
+ default: React.ComponentType;
12
+ }>>): RouteNode;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildRouteTree = buildRouteTree;
4
+ function buildRouteTree(routes) {
5
+ const root = { segment: "", children: [], isRoot: true };
6
+ Object.entries(routes).forEach(([fileRoute, importFn]) => {
7
+ let path = fileRoute.replace(/.*?\/pages\//, "").replace(/\.[jt]sx?$/, ""); // Strip prefix and extension
8
+ const segments = path.split("/").filter(Boolean); // Split into array, remove empty
9
+ let currentNode = root;
10
+ segments.forEach((seg, index) => {
11
+ if (seg === "index" && segments.length === 1) {
12
+ currentNode.segment = "";
13
+ currentNode.component = importFn;
14
+ return;
15
+ }
16
+ let cleanSeg = seg.replace(/\[(.*?)\]/g, ":$1"); // Handle [param] -> :param
17
+ // Find or create child node for this segment
18
+ let child = currentNode.children.find((c) => c.segment === cleanSeg);
19
+ if (!child) {
20
+ child = {
21
+ segment: cleanSeg,
22
+ isRoot: false,
23
+ children: [],
24
+ };
25
+ currentNode.children.push(child);
26
+ }
27
+ // If last segment, assign component
28
+ if (index === segments.length - 1) {
29
+ if (seg === "_layout") {
30
+ child.isLayout = true;
31
+ }
32
+ child.component = importFn;
33
+ }
34
+ currentNode = child;
35
+ });
36
+ });
37
+ return root;
38
+ }
39
+ //# sourceMappingURL=build-route-tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-route-tree.js","sourceRoot":"","sources":["../../src/utils/build-route-tree.tsx"],"names":[],"mappings":";;AAQA,wCA2CC;AA3CD,SAAgB,cAAc,CAC5B,MAAuE;IAEvE,MAAM,IAAI,GAAc,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAEpE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,EAAE;QACvD,IAAI,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,CAAC,6BAA6B;QAEzG,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,iCAAiC;QAEnF,IAAI,WAAW,GAAG,IAAI,CAAC;QACvB,QAAQ,CAAC,OAAO,CAAC,CAAC,GAAW,EAAE,KAAa,EAAE,EAAE;YAC9C,IAAI,GAAG,KAAK,OAAO,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC7C,WAAW,CAAC,OAAO,GAAG,EAAE,CAAC;gBACzB,WAAW,CAAC,SAAS,GAAG,QAAQ,CAAC;gBACjC,OAAO;YACT,CAAC;YACD,IAAI,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC,CAAC,2BAA2B;YAE5E,6CAA6C;YAC7C,IAAI,KAAK,GAAG,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC;YACrE,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,KAAK,GAAG;oBACN,OAAO,EAAE,QAAQ;oBACjB,MAAM,EAAE,KAAK;oBACb,QAAQ,EAAE,EAAE;iBACb,CAAC;gBACF,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnC,CAAC;YAED,oCAAoC;YACpC,IAAI,KAAK,KAAK,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAClC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;oBACtB,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC;gBACxB,CAAC;gBACD,KAAK,CAAC,SAAS,GAAG,QAAQ,CAAC;YAC7B,CAAC;YAED,WAAW,GAAG,KAAK,CAAC;QACtB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,IAAI,CAAC;AACd,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fs-router-dom",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Lightweight file-based router for Vite + react-router-dom, inspired by Next.js",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,9 +0,0 @@
1
- import { BrowserRouterProps } from 'react-router-dom';
2
- interface FileRouterProps {
3
- routes: Record<string, () => Promise<unknown>>;
4
- }
5
- export declare function FileRouter({ routes }: FileRouterProps): import("react/jsx-runtime").JSX.Element;
6
- interface FullFileRouterProps extends FileRouterProps, Omit<BrowserRouterProps, 'children'> {
7
- }
8
- export declare function FullFileRouter({ routes, ...browserProps }: FullFileRouterProps): import("react/jsx-runtime").JSX.Element;
9
- export {};
@@ -1,77 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.FileRouter = FileRouter;
37
- exports.FullFileRouter = FullFileRouter;
38
- const jsx_runtime_1 = require("react/jsx-runtime");
39
- const react_1 = __importStar(require("react"));
40
- const react_router_dom_1 = require("react-router-dom");
41
- /**
42
- * Transforms a file path from the format './pages/users/[id].tsx'
43
- * to the format '/users/:id' for react-router-dom.
44
- * @param filePath The file path to transform.
45
- * @returns The transformed route path.
46
- */
47
- function transformFilePathToRoutePath(filePath) {
48
- // Remove everything up to and including /pages/
49
- let path = filePath.replace(/.*\/pages\//, '/');
50
- // Remove .js, .jsx, .ts, .tsx extension
51
- path = path.replace(/\.[jt]sx?$/, '');
52
- // Handle index routes
53
- if (path.endsWith('/index')) {
54
- path = path.slice(0, -6); // Remove /index
55
- }
56
- // Handle root route
57
- if (path === '') {
58
- path = '/';
59
- }
60
- // Convert [param] to :param for dynamic routes
61
- path = path.replace(/\[(.*?)\]/g, ':$1');
62
- return path;
63
- }
64
- function FileRouter({ routes }) {
65
- return ((0, jsx_runtime_1.jsxs)(react_router_dom_1.Routes, { children: [Object.entries(routes).map(([filePath, importFn]) => {
66
- const path = transformFilePathToRoutePath(filePath);
67
- const Component = react_1.default.lazy(async () => {
68
- const module = await importFn();
69
- return { default: module.default };
70
- });
71
- return ((0, jsx_runtime_1.jsx)(react_router_dom_1.Route, { path: path, element: (0, jsx_runtime_1.jsx)(react_1.Suspense, { fallback: (0, jsx_runtime_1.jsx)("div", { children: "Loading..." }), children: (0, jsx_runtime_1.jsx)(Component, {}) }) }, path));
72
- }), (0, jsx_runtime_1.jsx)(react_router_dom_1.Route, { path: "*", element: (0, jsx_runtime_1.jsx)("div", { children: "404 - Not Found" }) })] }));
73
- }
74
- function FullFileRouter({ routes, ...browserProps }) {
75
- return ((0, jsx_runtime_1.jsx)(react_router_dom_1.BrowserRouter, { ...browserProps, children: (0, jsx_runtime_1.jsx)(FileRouter, { routes: routes }) }));
76
- }
77
- //# sourceMappingURL=FileRouter.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"FileRouter.js","sourceRoot":"","sources":["../src/FileRouter.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCA,gCA0BC;AAID,wCAMC;;AAxED,+CAAwC;AACxC,uDAAoF;AAMpF;;;;;GAKG;AACH,SAAS,4BAA4B,CAAC,QAAgB;IACpD,gDAAgD;IAChD,IAAI,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;IAEhD,wCAAwC;IACxC,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IAEtC,sBAAsB;IACtB,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,gBAAgB;IAC5C,CAAC;IAED,oBAAoB;IACpB,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;QAChB,IAAI,GAAG,GAAG,CAAC;IACb,CAAC;IAED,+CAA+C;IAC/C,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;IAEzC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAgB,UAAU,CAAC,EAAE,MAAM,EAAmB;IACpD,OAAO,CACL,wBAAC,yBAAM,eACJ,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,EAAE;gBACnD,MAAM,IAAI,GAAG,4BAA4B,CAAC,QAAQ,CAAC,CAAC;gBAEpD,MAAM,SAAS,GAAG,eAAK,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;oBACtC,MAAM,MAAM,GAAG,MAAM,QAAQ,EAAE,CAAC;oBAChC,OAAO,EAAE,OAAO,EAAG,MAAgD,CAAC,OAAO,EAAE,CAAC;gBAChF,CAAC,CAAC,CAAC;gBACH,OAAO,CACL,uBAAC,wBAAK,IAEJ,IAAI,EAAE,IAAI,EACV,OAAO,EACL,uBAAC,gBAAQ,IAAC,QAAQ,EAAE,yDAAqB,YACvC,uBAAC,SAAS,KAAG,GACJ,IALR,IAAI,CAOT,CACH,CAAC;YACJ,CAAC,CAAC,EAEF,uBAAC,wBAAK,IAAC,IAAI,EAAC,GAAG,EAAC,OAAO,EAAE,8DAA0B,GAAI,IAChD,CACV,CAAC;AACJ,CAAC;AAID,SAAgB,cAAc,CAAC,EAAE,MAAM,EAAE,GAAG,YAAY,EAAuB;IAC7E,OAAO,CACL,uBAAC,gCAAa,OAAK,YAAY,YAC7B,uBAAC,UAAU,IAAC,MAAM,EAAE,MAAM,GAAI,GAChB,CACjB,CAAC;AACJ,CAAC"}
@@ -1,30 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const jsx_runtime_1 = require("react/jsx-runtime");
4
- const react_1 = require("@testing-library/react");
5
- const react_router_dom_1 = require("react-router-dom");
6
- const FileRouter_1 = require("./FileRouter");
7
- // Mock components
8
- const Home = () => (0, jsx_runtime_1.jsx)("div", { children: "Home" });
9
- const About = () => (0, jsx_runtime_1.jsx)("div", { children: "About" });
10
- const User = () => (0, jsx_runtime_1.jsx)("div", { children: "User" });
11
- const routes = {
12
- './pages/index.tsx': () => Promise.resolve({ default: Home }),
13
- './pages/about.tsx': () => Promise.resolve({ default: About }),
14
- './pages/users/[id].tsx': () => Promise.resolve({ default: User }),
15
- };
16
- describe('FileRouter', () => {
17
- it('should render the correct component for a static route', async () => {
18
- (0, react_1.render)((0, jsx_runtime_1.jsx)(react_router_dom_1.MemoryRouter, { initialEntries: ['/about'], children: (0, jsx_runtime_1.jsx)(FileRouter_1.FileRouter, { routes: routes }) }));
19
- expect(await react_1.screen.findByText('About')).toBeInTheDocument();
20
- });
21
- it('should render the correct component for the root route', async () => {
22
- (0, react_1.render)((0, jsx_runtime_1.jsx)(react_router_dom_1.MemoryRouter, { initialEntries: ['/'], children: (0, jsx_runtime_1.jsx)(FileRouter_1.FileRouter, { routes: routes }) }));
23
- expect(await react_1.screen.findByText('Home')).toBeInTheDocument();
24
- });
25
- it('should render the correct component for a dynamic route', async () => {
26
- (0, react_1.render)((0, jsx_runtime_1.jsx)(react_router_dom_1.MemoryRouter, { initialEntries: ['/users/123'], children: (0, jsx_runtime_1.jsx)(FileRouter_1.FileRouter, { routes: routes }) }));
27
- expect(await react_1.screen.findByText('User')).toBeInTheDocument();
28
- });
29
- });
30
- //# sourceMappingURL=FileRouter.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"FileRouter.test.js","sourceRoot":"","sources":["../src/FileRouter.test.tsx"],"names":[],"mappings":";;;AACA,kDAAwD;AACxD,uDAAgD;AAChD,6CAA0C;AAE1C,kBAAkB;AAClB,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,mDAAe,CAAC;AACnC,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,oDAAgB,CAAC;AACrC,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,mDAAe,CAAC;AAEnC,MAAM,MAAM,GAAG;IACb,mBAAmB,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC7D,mBAAmB,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC9D,wBAAwB,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;CACnE,CAAC;AAEF,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACtE,IAAA,cAAM,EACJ,uBAAC,+BAAY,IAAC,cAAc,EAAE,CAAC,QAAQ,CAAC,YACtC,uBAAC,uBAAU,IAAC,MAAM,EAAE,MAAM,GAAI,GACjB,CAChB,CAAC;QACF,MAAM,CAAC,MAAM,cAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,iBAAiB,EAAE,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACtE,IAAA,cAAM,EACJ,uBAAC,+BAAY,IAAC,cAAc,EAAE,CAAC,GAAG,CAAC,YACjC,uBAAC,uBAAU,IAAC,MAAM,EAAE,MAAM,GAAI,GACjB,CAChB,CAAC;QACF,MAAM,CAAC,MAAM,cAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,iBAAiB,EAAE,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,KAAK,IAAI,EAAE;QACvE,IAAA,cAAM,EACJ,uBAAC,+BAAY,IAAC,cAAc,EAAE,CAAC,YAAY,CAAC,YAC1C,uBAAC,uBAAU,IAAC,MAAM,EAAE,MAAM,GAAI,GACjB,CAChB,CAAC;QACF,MAAM,CAAC,MAAM,cAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,iBAAiB,EAAE,CAAC;IAC9D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}