orpc-file-based-router 0.1.6 → 0.1.7

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
@@ -43,97 +43,40 @@ src/routes
43
43
  └── sse.ts
44
44
  ```
45
45
 
46
- 3. Each file should export an oRPC function
46
+ 3. Each file should export an oRPC function (non-oRPC exports will be ignored)
47
47
 
48
- 4. Simply replace router in your handlers with the result of the `createRouter`
49
- function:
48
+ 4. Generate your router before starting the server using the `generateRouter` function:
50
49
 
51
50
  ```typescript
52
- import { RPCHandler } from "@orpc/server/node";
53
- import { createRouter } from "orpc-file-based-router";
54
-
55
- const routesDir = new URL("./routes", import.meta.url).pathname;
56
- const router = await createRouter(routesDir);
57
-
58
- const handler = new RPCHandler(router);
59
-
60
- ```
61
- **Note:** If your environment doesn't support top-level await, just use `lazy` for example in expressjs it could be:
62
- ```typescript
63
- import { RPCHandler } from "@orpc/server/node";
64
- import { lazy, createRouter } from "orpc-file-based-router";
51
+ // router-gen.ts
52
+ import { generateRouter } from "orpc-file-based-router";
65
53
 
66
54
  const routesDir = new URL("./routes", import.meta.url).pathname;
55
+ const outputFile = new URL("./router.ts", import.meta.url).pathname;
67
56
 
68
- const router = lazy(() => createRouter(routesDir))
69
-
70
- app.use('/rpc{/*path}', async (req, res, next) => {
71
-
72
- const handler = new RPCHandler(await router());
73
-
74
- const { matched } = await handler.handle(req, res, {
75
- prefix: '/rpc',
76
- })
77
-
78
- if (matched) {
79
- return
80
- }
81
-
82
- next()
83
- })
57
+ generateRouter(routesDir, outputFile);
84
58
  ```
85
59
 
86
- ## 🔒 Type-Safe Client Configuration (Optional)
87
-
88
- For users of the [oRPC client](https://orpc.unnoq.com/docs/client/client-side), we provide automatic configuration generation for enhanced type safety and improved developer experience.
89
-
90
- 1. You can add this code either directly in your server project (e.g., in server.ts or main.ts) or put it into a separate script (e.g., router-gen.ts).
60
+ 5. Import and use the generated router in your server:
91
61
 
92
62
  ```typescript
93
- import { generateRouter } from "orpc-file-based-router";
63
+ import { RPCHandler } from "@orpc/server/node";
64
+ import { router } from "./router.js"; // Import the generated router
94
65
 
95
- const routesDir = new URL("./routes", import.meta.url).pathname;
96
- const outputFile = new URL("./router.ts", import.meta.url).pathname;
97
- generateRouter(routesDir, outputFile);
66
+ const handler = new RPCHandler(router);
98
67
  ```
99
68
 
100
- 2. Generated router is ready to use in client:
101
-
102
- ```typescript
103
- // router.ts
104
-
105
- import { me as auth_me__me } from "./routes/auth/me.js"
106
- import { signin as auth_signin__signin } from "./routes/auth/signin.js"
107
- import { signup as auth_signup__signup } from "./routes/auth/signup.js"
108
- import { createPlanet as planets_create__createPlanet } from "./routes/planets/create.js"
109
- import { indexRoute as planets_index__indexRoute } from "./routes/planets/index.js"
110
- import { listPlanets as planets_list__listPlanets } from "./routes/planets/list.js"
111
- import { findPlanet as planets_id_find__findPlanet } from "./routes/planets/{id}/find.js"
112
- import { updatePlanet as planets_id_update__updatePlanet } from "./routes/planets/{id}/update.js"
113
- import { sse as sse__sse } from "./routes/sse.js"
114
-
115
- export const router = {
116
- auth: {
117
- me: auth_me__me.route({ path: '/auth/me', method: 'GET' }),
118
- signin: auth_signin__signin.route({ path: '/auth/signin', method: 'POST' }),
119
- signup: auth_signup__signup.route({ path: '/auth/signup', method: 'POST' })
120
- },
121
- planets: {
122
- create: planets_create__createPlanet.route({ path: '/planets/create', method: 'POST' }),
123
- index: planets_index__indexRoute.route({ path: '/planets', method: 'GET' }),
124
- list: planets_list__listPlanets.route({ path: '/planets/list', method: 'GET' }),
125
- find: planets_id_find__findPlanet.route({ path: '/planets/{id}/find', method: 'GET' }),
126
- update: planets_id_update__updatePlanet.route({ path: '/planets/{id}/update', method: 'PUT' })
127
- },
128
- sse: sse__sse.route({ path: '/sse', method: 'GET' })
129
- }
69
+ ## 🔒 Enhanced Type-Safe Client Configuration
130
70
 
71
+ After generating your router (as shown in step 4 above), you can use it in your client:
131
72
 
73
+ ```typescript
132
74
  // lib/orpc.ts
75
+ import { router } from "../router.js"; // Use the same generated router
133
76
  const client: RouterClient<typeof router> = createORPCClient(link)
134
-
135
77
  ```
136
78
 
79
+
137
80
  ## 🛠 Configuration Options
138
81
 
139
82
  When using `generateRouter`, you can provide additional options to customize the output:
@@ -141,7 +84,6 @@ When using `generateRouter`, you can provide additional options to customize the
141
84
  | Field | Type | Required | Default Value | Description |
142
85
  |-------------------|----------|--------------|-----------------------|------------------------------------------------------------------------------------------------------------------------------|
143
86
  | `importExtension` | string | false | `""`(No extension) | File extension to append to import statements in the generated router. Useful when your build setup requires specific extensions. <br>Example: `.js` → `import { me } from "./routes/auth/me.js"` |
144
- | `enableOpenAPI` | boolean | false | `true` | When set to true, each route will be wrapped with OpenAPI .route({ path: '...', method: '...' }) call |
145
87
  | `additionalMethods` | string[] | false | `[]` | Additional HTTP methods to recognize from export names. |
146
88
 
147
89
 
package/dist/index.cjs CHANGED
@@ -46,15 +46,6 @@ function walkTree(directory, tree = []) {
46
46
  function mergePaths(...paths) {
47
47
  return `/${paths.map((path2) => path2.replace(/^\/|\/$/g, "")).filter((path2) => path2 !== "").join("/")}`;
48
48
  }
49
- async function createRouter(routesDir) {
50
- const files = walkTree(
51
- routesDir
52
- );
53
- const exports = await generateRoutes(files);
54
- return buildRouter(exports, (r, e) => {
55
- return r.exports[e].route({ path: `${r.path}` });
56
- });
57
- }
58
49
  const lazy = (create) => {
59
50
  let cache = null;
60
51
  const fn = () => {
@@ -68,6 +59,10 @@ const lazy = (create) => {
68
59
  };
69
60
  return fn;
70
61
  };
62
+ async function createRouter(routesDir) {
63
+ const msg = "createRouter is deprecated. Please use generateRouter to generate router file at build time. See https://github.com/zeeeeby/orpc-file-based-router?tab=readme-ov-file#quickstart for more details.";
64
+ throw new Error(msg);
65
+ }
71
66
  async function generateRouter(routesDir, outputFile, options) {
72
67
  const files = walkTree(
73
68
  routesDir
@@ -76,13 +71,10 @@ async function generateRouter(routesDir, outputFile, options) {
76
71
  const importPaths = exports.map((x) => path.relative(path.dirname(outputFile), routesDir).concat(x.path));
77
72
  const content = buildRouter(exports, (r, e) => {
78
73
  const alias = routePathToAlias(r.path, e);
79
- if (options?.enableOpenAPI ?? true) {
80
- const orpcMeta = isORPCProcedure(r.exports[e]) ? r.exports[e]["~orpc"] : void 0;
81
- const method = isMethodExport(e, options?.additionalMethods || []) ? getMethodKey(e) : orpcMeta?.route?.method || "POST";
82
- const config = { path: r.path.replace(/\/{0,1}index$/, ""), method };
83
- return `${alias}.route({ path: '${config.path}', method: '${config.method}' })`;
84
- }
85
- return alias;
74
+ const orpcMeta = isORPCProcedure(r.exports[e]) ? r.exports[e]["~orpc"] : void 0;
75
+ const method = isMethodExport(e, options?.additionalMethods || []) ? getMethodKey(e) : orpcMeta?.route?.method || "POST";
76
+ const config = { path: r.path.replace(/\/{0,1}index$/, ""), method };
77
+ return `${alias}.route({ path: '${config.path}', method: '${config.method}' })`;
86
78
  });
87
79
  let routerContent = `// This file is auto-generated
88
80
 
@@ -155,13 +147,24 @@ async function generateRoutes(files) {
155
147
  const parsedFile = path__default.parse(file.rel);
156
148
  const routePath = buildRoutePath(parsedFile);
157
149
  const exports = await import(MODULE_IMPORT_PREFIX + path__default.join(file.path, file.name));
158
- routes.push({
159
- exports,
160
- path: routePath
161
- });
150
+ const cleanedExports = removeNonOrpcExports(exports);
151
+ if (Object.keys(cleanedExports).length > 0)
152
+ routes.push({
153
+ exports: cleanedExports,
154
+ path: routePath
155
+ });
162
156
  }
163
157
  return routes;
164
158
  }
159
+ const removeNonOrpcExports = (exports) => {
160
+ const cleanedExports = {};
161
+ for (const key in exports) {
162
+ if (isORPCProcedure(exports[key])) {
163
+ cleanedExports[key] = exports[key];
164
+ }
165
+ }
166
+ return cleanedExports;
167
+ };
165
168
 
166
169
  exports.createRouter = createRouter;
167
170
  exports.generateRouter = generateRouter;
package/dist/index.d.cts CHANGED
@@ -1,8 +1,11 @@
1
- declare function createRouter(routesDir: string): Promise<Router>;
2
1
  declare const lazy: <T>(create: () => Promise<T> | T) => {
3
2
  (): Promise<T>;
4
3
  reset(): void;
5
4
  };
5
+ /**
6
+ * @deprecated use generateRouter instead. See https://github.com/zeeeeby/orpc-file-based-router?tab=readme-ov-file#quickstart for more details.
7
+ */
8
+ declare function createRouter(routesDir: string): Promise<void>;
6
9
  type GeneratorOptions = {
7
10
  /**
8
11
  * File extension to append to import statements in the generated router.
@@ -11,17 +14,11 @@ type GeneratorOptions = {
11
14
  * @default "" (no extension)
12
15
  */
13
16
  importExtension?: string;
14
- /**
15
- * When set to true, each route will be wrapped with OpenAPI .route({ path: '...', method: '...' }) call
16
- * @default true
17
- */
18
- enableOpenAPI?: boolean;
19
17
  /**
20
18
  * Additional HTTP methods to recognize from export names.
21
19
  */
22
20
  additionalMethods?: string[];
23
21
  };
24
22
  declare function generateRouter(routesDir: string, outputFile: string, options?: GeneratorOptions): Promise<void>;
25
- type Router = Record<string, any>;
26
23
 
27
24
  export { createRouter, generateRouter, lazy };
package/dist/index.d.mts CHANGED
@@ -1,8 +1,11 @@
1
- declare function createRouter(routesDir: string): Promise<Router>;
2
1
  declare const lazy: <T>(create: () => Promise<T> | T) => {
3
2
  (): Promise<T>;
4
3
  reset(): void;
5
4
  };
5
+ /**
6
+ * @deprecated use generateRouter instead. See https://github.com/zeeeeby/orpc-file-based-router?tab=readme-ov-file#quickstart for more details.
7
+ */
8
+ declare function createRouter(routesDir: string): Promise<void>;
6
9
  type GeneratorOptions = {
7
10
  /**
8
11
  * File extension to append to import statements in the generated router.
@@ -11,17 +14,11 @@ type GeneratorOptions = {
11
14
  * @default "" (no extension)
12
15
  */
13
16
  importExtension?: string;
14
- /**
15
- * When set to true, each route will be wrapped with OpenAPI .route({ path: '...', method: '...' }) call
16
- * @default true
17
- */
18
- enableOpenAPI?: boolean;
19
17
  /**
20
18
  * Additional HTTP methods to recognize from export names.
21
19
  */
22
20
  additionalMethods?: string[];
23
21
  };
24
22
  declare function generateRouter(routesDir: string, outputFile: string, options?: GeneratorOptions): Promise<void>;
25
- type Router = Record<string, any>;
26
23
 
27
24
  export { createRouter, generateRouter, lazy };
package/dist/index.d.ts CHANGED
@@ -1,8 +1,11 @@
1
- declare function createRouter(routesDir: string): Promise<Router>;
2
1
  declare const lazy: <T>(create: () => Promise<T> | T) => {
3
2
  (): Promise<T>;
4
3
  reset(): void;
5
4
  };
5
+ /**
6
+ * @deprecated use generateRouter instead. See https://github.com/zeeeeby/orpc-file-based-router?tab=readme-ov-file#quickstart for more details.
7
+ */
8
+ declare function createRouter(routesDir: string): Promise<void>;
6
9
  type GeneratorOptions = {
7
10
  /**
8
11
  * File extension to append to import statements in the generated router.
@@ -11,17 +14,11 @@ type GeneratorOptions = {
11
14
  * @default "" (no extension)
12
15
  */
13
16
  importExtension?: string;
14
- /**
15
- * When set to true, each route will be wrapped with OpenAPI .route({ path: '...', method: '...' }) call
16
- * @default true
17
- */
18
- enableOpenAPI?: boolean;
19
17
  /**
20
18
  * Additional HTTP methods to recognize from export names.
21
19
  */
22
20
  additionalMethods?: string[];
23
21
  };
24
22
  declare function generateRouter(routesDir: string, outputFile: string, options?: GeneratorOptions): Promise<void>;
25
- type Router = Record<string, any>;
26
23
 
27
24
  export { createRouter, generateRouter, lazy };
package/dist/index.mjs CHANGED
@@ -40,15 +40,6 @@ function walkTree(directory, tree = []) {
40
40
  function mergePaths(...paths) {
41
41
  return `/${paths.map((path2) => path2.replace(/^\/|\/$/g, "")).filter((path2) => path2 !== "").join("/")}`;
42
42
  }
43
- async function createRouter(routesDir) {
44
- const files = walkTree(
45
- routesDir
46
- );
47
- const exports = await generateRoutes(files);
48
- return buildRouter(exports, (r, e) => {
49
- return r.exports[e].route({ path: `${r.path}` });
50
- });
51
- }
52
43
  const lazy = (create) => {
53
44
  let cache = null;
54
45
  const fn = () => {
@@ -62,6 +53,10 @@ const lazy = (create) => {
62
53
  };
63
54
  return fn;
64
55
  };
56
+ async function createRouter(routesDir) {
57
+ const msg = "createRouter is deprecated. Please use generateRouter to generate router file at build time. See https://github.com/zeeeeby/orpc-file-based-router?tab=readme-ov-file#quickstart for more details.";
58
+ throw new Error(msg);
59
+ }
65
60
  async function generateRouter(routesDir, outputFile, options) {
66
61
  const files = walkTree(
67
62
  routesDir
@@ -70,13 +65,10 @@ async function generateRouter(routesDir, outputFile, options) {
70
65
  const importPaths = exports.map((x) => relative(dirname(outputFile), routesDir).concat(x.path));
71
66
  const content = buildRouter(exports, (r, e) => {
72
67
  const alias = routePathToAlias(r.path, e);
73
- if (options?.enableOpenAPI ?? true) {
74
- const orpcMeta = isORPCProcedure(r.exports[e]) ? r.exports[e]["~orpc"] : void 0;
75
- const method = isMethodExport(e, options?.additionalMethods || []) ? getMethodKey(e) : orpcMeta?.route?.method || "POST";
76
- const config = { path: r.path.replace(/\/{0,1}index$/, ""), method };
77
- return `${alias}.route({ path: '${config.path}', method: '${config.method}' })`;
78
- }
79
- return alias;
68
+ const orpcMeta = isORPCProcedure(r.exports[e]) ? r.exports[e]["~orpc"] : void 0;
69
+ const method = isMethodExport(e, options?.additionalMethods || []) ? getMethodKey(e) : orpcMeta?.route?.method || "POST";
70
+ const config = { path: r.path.replace(/\/{0,1}index$/, ""), method };
71
+ return `${alias}.route({ path: '${config.path}', method: '${config.method}' })`;
80
72
  });
81
73
  let routerContent = `// This file is auto-generated
82
74
 
@@ -149,12 +141,23 @@ async function generateRoutes(files) {
149
141
  const parsedFile = path.parse(file.rel);
150
142
  const routePath = buildRoutePath(parsedFile);
151
143
  const exports = await import(MODULE_IMPORT_PREFIX + path.join(file.path, file.name));
152
- routes.push({
153
- exports,
154
- path: routePath
155
- });
144
+ const cleanedExports = removeNonOrpcExports(exports);
145
+ if (Object.keys(cleanedExports).length > 0)
146
+ routes.push({
147
+ exports: cleanedExports,
148
+ path: routePath
149
+ });
156
150
  }
157
151
  return routes;
158
152
  }
153
+ const removeNonOrpcExports = (exports) => {
154
+ const cleanedExports = {};
155
+ for (const key in exports) {
156
+ if (isORPCProcedure(exports[key])) {
157
+ cleanedExports[key] = exports[key];
158
+ }
159
+ }
160
+ return cleanedExports;
161
+ };
159
162
 
160
163
  export { createRouter, generateRouter, lazy };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orpc-file-based-router",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
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",