orpc-file-based-router 0.0.8 → 0.0.9

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,10 +1,14 @@
1
1
  # orpc-file-based-router
2
2
 
3
- A plugin for [oRPC](https://orpc.unnoq.com) that automatically creates an oRPC router configuration based on your file structure.
3
+ A plugin for [oRPC](https://orpc.unnoq.com) that automatically creates an oRPC
4
+ router configuration based on your file structure.
4
5
 
5
- > ⚠️ **IMPORTANT:** At this time, the plugin's functionality is only guaranteed in nodejs runtime
6
+ > ⚠️ **IMPORTANT:** At this time, the plugin's functionality is only guaranteed
7
+ > in nodejs runtime
6
8
 
7
- ## Installation
9
+ ## Get started
10
+
11
+ 1. Install package
8
12
 
9
13
  ```bash
10
14
  npm install orpc-file-based-router
@@ -12,22 +16,9 @@ npm install orpc-file-based-router
12
16
  yarn add orpc-file-based-router
13
17
  ```
14
18
 
15
- ## Usage
16
-
17
- ```typescript
18
- import { RPCHandler } from '@orpc/server/node'
19
- import { generateRouter } from 'orpc-file-based-router'
20
-
21
- const routesDir = new URL('./routes', import.meta.url).pathname
22
- const outputFile = new URL('./router.ts', import.meta.url).pathname
23
- const router = await generateRouter(routesDir, outputFile)
19
+ 2. Create a routes directory structure (for example):
24
20
 
25
- const handler = new RPCHandler(router)
26
21
  ```
27
-
28
- ### File Structure Example
29
-
30
- ```
31
22
  src/routes
32
23
  ├── auth
33
24
  │ ├── me.ts
@@ -43,37 +34,69 @@ src/routes
43
34
  │ ├── index.ts
44
35
  │ └── list.ts
45
36
 
46
- └── sse.ts
37
+ └── sse.ts
47
38
  ```
48
39
 
49
- ### Generated result
40
+ 3. Each file should export a oRPC handler function
41
+
42
+ 4. Simply replace router in your handlers with the result of the `createRouter`
43
+ function:
50
44
 
51
45
  ```typescript
52
- import { me } from './routes/auth/me'
53
- import { signin } from './routes/auth/signin'
54
- import { signup } from './routes/auth/signup'
55
- import { createPlanet } from './routes/planets/create'
56
- import { indexRoute } from './routes/planets'
57
- import { listPlanets } from './routes/planets/list'
58
- import { findPlanet } from './routes/planets/{id}/find'
59
- import { updatePlanet } from './routes/planets/{id}/update'
60
- import { sse } from './routes/sse'
46
+ import { RPCHandler } from "@orpc/server/node";
47
+ import { createRouter } from "orpc-file-based-router";
48
+
49
+ const routesDir = new URL("./routes", import.meta.url).pathname;
50
+ const router = await createRouter(routesDir);
51
+
52
+ const handler = new RPCHandler(router);
53
+ ```
54
+
55
+ ## How to generate configuration file
56
+
57
+ 1. Call `generateRouter`
58
+
59
+ ```typescript
60
+ import { generateRouter } from "orpc-file-based-router";
61
+
62
+ const routesDir = new URL("./routes", import.meta.url).pathname;
63
+ const outputFile = new URL("./router.ts", import.meta.url).pathname;
64
+ generateRouter(routesDir, outputFile);
65
+ ```
66
+
67
+ 2. Generated configuration is ready to use in router client:
68
+
69
+ ```typescript
70
+ // router.ts
71
+ import { me } from "./routes/auth/me";
72
+ import { signin } from "./routes/auth/signin";
73
+ import { signup } from "./routes/auth/signup";
74
+ import { createPlanet } from "./routes/planets/create";
75
+ import { indexRoute } from "./routes/planets";
76
+ import { listPlanets } from "./routes/planets/list";
77
+ import { findPlanet } from "./routes/planets/{id}/find";
78
+ import { updatePlanet } from "./routes/planets/{id}/update";
79
+ import { sse } from "./routes/sse";
61
80
 
62
81
  export const router = {
63
82
  auth: {
64
- me: me.route({ path: '/auth/me' }),
65
- signin: signin.route({ path: '/auth/signin' }),
66
- signup: signup.route({ path: '/auth/signup' }),
83
+ me: me.route({ path: "/auth/me" }),
84
+ signin: signin.route({ path: "/auth/signin" }),
85
+ signup: signup.route({ path: "/auth/signup" }),
67
86
  },
68
87
  planets: {
69
- create: createPlanet.route({ path: '/planets/create' }),
70
- indexRoute: indexRoute.route({ path: '/planets' }),
71
- list: listPlanets.route({ path: '/planets/list' }),
72
- find: findPlanet.route({ path: '/planets/{id}/find' }),
73
- update: updatePlanet.route({ path: '/planets/{id}/update' }),
88
+ create: createPlanet.route({ path: "/planets/create" }),
89
+ indexRoute: indexRoute.route({ path: "/planets" }),
90
+ list: listPlanets.route({ path: "/planets/list" }),
91
+ find: findPlanet.route({ path: "/planets/{id}/find" }),
92
+ update: updatePlanet.route({ path: "/planets/{id}/update" }),
74
93
  },
75
- sse: sse.route({ path: '/sse' }),
76
- }
94
+ sse: sse.route({ path: "/sse" }),
95
+ };
96
+
97
+ // lib/orpc.ts
98
+ const client: RouterClient<typeof router> = createORPCClient(link)
99
+
77
100
  ```
78
101
 
79
102
  ## License
package/dist/index.cjs CHANGED
@@ -27,6 +27,15 @@ function walkTree(directory, tree = []) {
27
27
  function mergePaths(...paths) {
28
28
  return `/${paths.map((path2) => path2.replace(/^\/|\/$/g, "")).filter((path2) => path2 !== "").join("/")}`;
29
29
  }
30
+ async function createRouter(routesDir) {
31
+ const files = walkTree(
32
+ routesDir
33
+ );
34
+ const exports = await generateRoutes(files);
35
+ return buildRouter(exports, (r, e) => {
36
+ return r.exports[e].route({ path: `${r.path}` });
37
+ });
38
+ }
30
39
  async function generateRouter(routesDir, outputFile) {
31
40
  const files = walkTree(
32
41
  routesDir
@@ -43,10 +52,6 @@ async function generateRouter(routesDir, outputFile) {
43
52
  routerContent += "\n\nexport const router = ";
44
53
  routerContent += JSON.stringify(content, null, 2).replace(/"/g, "");
45
54
  node_fs.writeFileSync(path.join(outputFile), routerContent);
46
- const router = buildRouter(exports, (r, e) => {
47
- return r.exports[e].route({ path: `${r.path}` });
48
- });
49
- return router;
50
55
  }
51
56
  function buildRoutePath(parsedFile) {
52
57
  const directory = parsedFile.dir === parsedFile.root ? "" : parsedFile.dir;
@@ -78,12 +83,11 @@ function simplifyRouter(router) {
78
83
  for (const key in router) {
79
84
  if (isLeaf(router[key])) {
80
85
  simplifiedRouter[key] = router[key];
86
+ } else if (hasSingleLeaf(router[key])) {
87
+ const childKey = Object.keys(router[key])[0];
88
+ simplifiedRouter[key] = router[key][childKey];
81
89
  } else {
82
90
  simplifiedRouter[key] = simplifyRouter(router[key]);
83
- if (isSingleChild(simplifiedRouter[key])) {
84
- const childKey = Object.keys(simplifiedRouter[key])[0];
85
- simplifiedRouter[key] = simplifiedRouter[key][childKey];
86
- }
87
91
  }
88
92
  }
89
93
  return simplifiedRouter;
@@ -94,9 +98,9 @@ function isORPCProcedure(obj) {
94
98
  function isLeaf(obj) {
95
99
  return typeof obj === "string" || isORPCProcedure(obj);
96
100
  }
97
- function isSingleChild(obj) {
101
+ function hasSingleLeaf(obj) {
98
102
  const keys = Object.keys(obj);
99
- return keys.length === 1;
103
+ return keys.length === 1 && isLeaf(obj[keys[0]]);
100
104
  }
101
105
  const isCjs = () => typeof module !== "undefined" && !!module?.exports;
102
106
  const IS_ESM = !isCjs();
@@ -115,4 +119,5 @@ async function generateRoutes(files) {
115
119
  return routes;
116
120
  }
117
121
 
122
+ exports.createRouter = createRouter;
118
123
  exports.generateRouter = generateRouter;
package/dist/index.d.cts CHANGED
@@ -1,4 +1,5 @@
1
- declare function generateRouter(routesDir: string, outputFile: string): Promise<Router>;
1
+ declare function createRouter(routesDir: string): Promise<Router>;
2
+ declare function generateRouter(routesDir: string, outputFile: string): Promise<void>;
2
3
  type Router = Record<string, any>;
3
4
 
4
- export { generateRouter };
5
+ export { createRouter, generateRouter };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,5 @@
1
- declare function generateRouter(routesDir: string, outputFile: string): Promise<Router>;
1
+ declare function createRouter(routesDir: string): Promise<Router>;
2
+ declare function generateRouter(routesDir: string, outputFile: string): Promise<void>;
2
3
  type Router = Record<string, any>;
3
4
 
4
- export { generateRouter };
5
+ export { createRouter, generateRouter };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- declare function generateRouter(routesDir: string, outputFile: string): Promise<Router>;
1
+ declare function createRouter(routesDir: string): Promise<Router>;
2
+ declare function generateRouter(routesDir: string, outputFile: string): Promise<void>;
2
3
  type Router = Record<string, any>;
3
4
 
4
- export { generateRouter };
5
+ export { createRouter, generateRouter };
package/dist/index.mjs CHANGED
@@ -21,6 +21,15 @@ function walkTree(directory, tree = []) {
21
21
  function mergePaths(...paths) {
22
22
  return `/${paths.map((path2) => path2.replace(/^\/|\/$/g, "")).filter((path2) => path2 !== "").join("/")}`;
23
23
  }
24
+ async function createRouter(routesDir) {
25
+ const files = walkTree(
26
+ routesDir
27
+ );
28
+ const exports = await generateRoutes(files);
29
+ return buildRouter(exports, (r, e) => {
30
+ return r.exports[e].route({ path: `${r.path}` });
31
+ });
32
+ }
24
33
  async function generateRouter(routesDir, outputFile) {
25
34
  const files = walkTree(
26
35
  routesDir
@@ -37,10 +46,6 @@ async function generateRouter(routesDir, outputFile) {
37
46
  routerContent += "\n\nexport const router = ";
38
47
  routerContent += JSON.stringify(content, null, 2).replace(/"/g, "");
39
48
  writeFileSync(join(outputFile), routerContent);
40
- const router = buildRouter(exports, (r, e) => {
41
- return r.exports[e].route({ path: `${r.path}` });
42
- });
43
- return router;
44
49
  }
45
50
  function buildRoutePath(parsedFile) {
46
51
  const directory = parsedFile.dir === parsedFile.root ? "" : parsedFile.dir;
@@ -72,12 +77,11 @@ function simplifyRouter(router) {
72
77
  for (const key in router) {
73
78
  if (isLeaf(router[key])) {
74
79
  simplifiedRouter[key] = router[key];
80
+ } else if (hasSingleLeaf(router[key])) {
81
+ const childKey = Object.keys(router[key])[0];
82
+ simplifiedRouter[key] = router[key][childKey];
75
83
  } else {
76
84
  simplifiedRouter[key] = simplifyRouter(router[key]);
77
- if (isSingleChild(simplifiedRouter[key])) {
78
- const childKey = Object.keys(simplifiedRouter[key])[0];
79
- simplifiedRouter[key] = simplifiedRouter[key][childKey];
80
- }
81
85
  }
82
86
  }
83
87
  return simplifiedRouter;
@@ -88,9 +92,9 @@ function isORPCProcedure(obj) {
88
92
  function isLeaf(obj) {
89
93
  return typeof obj === "string" || isORPCProcedure(obj);
90
94
  }
91
- function isSingleChild(obj) {
95
+ function hasSingleLeaf(obj) {
92
96
  const keys = Object.keys(obj);
93
- return keys.length === 1;
97
+ return keys.length === 1 && isLeaf(obj[keys[0]]);
94
98
  }
95
99
  const isCjs = () => typeof module !== "undefined" && !!module?.exports;
96
100
  const IS_ESM = !isCjs();
@@ -109,4 +113,4 @@ async function generateRoutes(files) {
109
113
  return routes;
110
114
  }
111
115
 
112
- export { generateRouter };
116
+ export { createRouter, generateRouter };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orpc-file-based-router",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "description": "File-based router plugin for oRPC - automatically generate oRPC router from your file structure",
5
5
  "author": "zeeeeby",
6
6
  "license": "MIT",