orpc-file-based-router 0.1.5 → 0.1.6

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
@@ -99,35 +99,34 @@ generateRouter(routesDir, outputFile);
99
99
 
100
100
  2. Generated router is ready to use in client:
101
101
 
102
- > ⚠️ If you don't want plugin to generate openapi `route({})` suffix, just set parameter `includeRoute` to `false`
103
-
104
102
  ```typescript
105
103
  // router.ts
106
- import { me } from "./routes/auth/me";
107
- import { signin } from "./routes/auth/signin";
108
- import { signup } from "./routes/auth/signup";
109
- import { createPlanet } from "./routes/planets/create";
110
- import { indexRoute } from "./routes/planets";
111
- import { listPlanets } from "./routes/planets/list";
112
- import { findPlanet } from "./routes/planets/{id}/find";
113
- import { updatePlanet } from "./routes/planets/{id}/update";
114
- import { sse } from "./routes/sse";
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"
115
114
 
116
115
  export const router = {
117
116
  auth: {
118
- me: me.route({ path: "/auth/me" }),
119
- signin: signin.route({ path: "/auth/signin" }),
120
- signup: signup.route({ path: "/auth/signup" }),
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' })
121
120
  },
122
121
  planets: {
123
- create: createPlanet.route({ path: "/planets/create" }),
124
- indexRoute: indexRoute.route({ path: "/planets" }),
125
- list: listPlanets.route({ path: "/planets/list" }),
126
- find: findPlanet.route({ path: "/planets/{id}/find" }),
127
- update: updatePlanet.route({ path: "/planets/{id}/update" }),
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' })
128
127
  },
129
- sse: sse.route({ path: "/sse" }),
130
- };
128
+ sse: sse__sse.route({ path: '/sse', method: 'GET' })
129
+ }
131
130
 
132
131
 
133
132
  // lib/orpc.ts
@@ -142,9 +141,21 @@ When using `generateRouter`, you can provide additional options to customize the
142
141
  | Field | Type | Required | Default Value | Description |
143
142
  |-------------------|----------|--------------|-----------------------|------------------------------------------------------------------------------------------------------------------------------|
144
143
  | `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"` |
145
- | `includeRoute` | boolean | false | `true` | When set to true, each route will be wrapped with openapi `.route({ path: '...' })` call |
144
+ | `enableOpenAPI` | boolean | false | `true` | When set to true, each route will be wrapped with OpenAPI .route({ path: '...', method: '...' }) call |
145
+ | `additionalMethods` | string[] | false | `[]` | Additional HTTP methods to recognize from export names. |
146
146
 
147
147
 
148
+ ## Examples
149
+ ### HTTP Method Matching
150
+ If you export functions named e.g. `get`, `post`, `put`, `patch`, `delete/del` etc. from a route file, those will get matched their corresponding http method automatically.
151
+
152
+ ```typescript
153
+ // ./routes/planets.ts
154
+
155
+ export const get = orpc.handler(async ({ input, context }) => {})
156
+
157
+ export const post = orpc.handler(async ({ input, context }) => {})
158
+ ```
148
159
 
149
160
  ## 📄 License
150
161
 
package/dist/index.cjs CHANGED
@@ -7,6 +7,25 @@ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'defau
7
7
 
8
8
  const path__default = /*#__PURE__*/_interopDefaultCompat(path);
9
9
 
10
+ const METHOD_EXPORTS = [
11
+ "GET",
12
+ "POST",
13
+ "PUT",
14
+ "PATCH",
15
+ "DELETE",
16
+ "HEAD"
17
+ ];
18
+
19
+ const getMethodKey = (method) => {
20
+ let methodKey = method.toUpperCase();
21
+ if (methodKey === "DEL") return "DELETE";
22
+ return methodKey;
23
+ };
24
+ const isMethodExport = (exportName, extraMethods) => {
25
+ const methods = [...METHOD_EXPORTS, ...extraMethods].map(getMethodKey);
26
+ return methods.includes(getMethodKey(exportName));
27
+ };
28
+
10
29
  function walkTree(directory, tree = []) {
11
30
  const results = [];
12
31
  for (const fileName of node_fs.readdirSync(directory)) {
@@ -56,20 +75,28 @@ async function generateRouter(routesDir, outputFile, options) {
56
75
  const exports = await generateRoutes(files);
57
76
  const importPaths = exports.map((x) => path.relative(path.dirname(outputFile), routesDir).concat(x.path));
58
77
  const content = buildRouter(exports, (r, e) => {
59
- if (options?.includeRoute ?? true) {
60
- return `${e}.route({ path: '${r.path.replace(/\/{0,1}index$/, "")}' })`;
78
+ 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}' })`;
61
84
  }
62
- return e;
85
+ return alias;
63
86
  });
64
87
  let routerContent = `// This file is auto-generated
65
88
 
66
89
  `;
67
90
  const extension = options?.importExtension || "";
68
- routerContent += importPaths.map((x, i) => `import { ${Object.keys(exports[i].exports).join(", ")} } from "./${x}${extension}"`).join("\n");
91
+ routerContent += importPaths.map((x, i) => `import { ${Object.keys(exports[i].exports).map((y) => `${y} as ${routePathToAlias(exports[i].path, y)}`).join(", ")} } from "./${x}${extension}"`).join("\n");
69
92
  routerContent += "\n\nexport const router = ";
70
93
  routerContent += JSON.stringify(content, null, 2).replace(/"/g, "").replace(/(\s*)([a-zA-Z0-9]+-[a-zA-Z0-9-]+):/g, '$1"$2":');
71
94
  node_fs.writeFileSync(path.join(outputFile), routerContent);
72
95
  }
96
+ function routePathToAlias(routePath, methodName) {
97
+ const cleanPath = routePath.replace(/\{(\w+)\}/g, "$1").replace(/-/g, "_");
98
+ return cleanPath.split("/").filter(Boolean).join("_") + "__" + methodName;
99
+ }
73
100
  function buildRoutePath(parsedFile) {
74
101
  const directory = parsedFile.dir === parsedFile.root ? "" : parsedFile.dir;
75
102
  const name = `/${parsedFile.name}`;
package/dist/index.d.cts CHANGED
@@ -12,11 +12,14 @@ type GeneratorOptions = {
12
12
  */
13
13
  importExtension?: string;
14
14
  /**
15
- * Include openapi route paths in the generated router.
16
- * When set to true, each route will have its openapi path
15
+ * When set to true, each route will be wrapped with OpenAPI .route({ path: '...', method: '...' }) call
17
16
  * @default true
18
17
  */
19
- includeRoute?: boolean;
18
+ enableOpenAPI?: boolean;
19
+ /**
20
+ * Additional HTTP methods to recognize from export names.
21
+ */
22
+ additionalMethods?: string[];
20
23
  };
21
24
  declare function generateRouter(routesDir: string, outputFile: string, options?: GeneratorOptions): Promise<void>;
22
25
  type Router = Record<string, any>;
package/dist/index.d.mts CHANGED
@@ -12,11 +12,14 @@ type GeneratorOptions = {
12
12
  */
13
13
  importExtension?: string;
14
14
  /**
15
- * Include openapi route paths in the generated router.
16
- * When set to true, each route will have its openapi path
15
+ * When set to true, each route will be wrapped with OpenAPI .route({ path: '...', method: '...' }) call
17
16
  * @default true
18
17
  */
19
- includeRoute?: boolean;
18
+ enableOpenAPI?: boolean;
19
+ /**
20
+ * Additional HTTP methods to recognize from export names.
21
+ */
22
+ additionalMethods?: string[];
20
23
  };
21
24
  declare function generateRouter(routesDir: string, outputFile: string, options?: GeneratorOptions): Promise<void>;
22
25
  type Router = Record<string, any>;
package/dist/index.d.ts CHANGED
@@ -12,11 +12,14 @@ type GeneratorOptions = {
12
12
  */
13
13
  importExtension?: string;
14
14
  /**
15
- * Include openapi route paths in the generated router.
16
- * When set to true, each route will have its openapi path
15
+ * When set to true, each route will be wrapped with OpenAPI .route({ path: '...', method: '...' }) call
17
16
  * @default true
18
17
  */
19
- includeRoute?: boolean;
18
+ enableOpenAPI?: boolean;
19
+ /**
20
+ * Additional HTTP methods to recognize from export names.
21
+ */
22
+ additionalMethods?: string[];
20
23
  };
21
24
  declare function generateRouter(routesDir: string, outputFile: string, options?: GeneratorOptions): Promise<void>;
22
25
  type Router = Record<string, any>;
package/dist/index.mjs CHANGED
@@ -1,6 +1,25 @@
1
1
  import { writeFileSync, readdirSync, statSync } from 'node:fs';
2
2
  import path, { relative, dirname, join } from 'node:path';
3
3
 
4
+ const METHOD_EXPORTS = [
5
+ "GET",
6
+ "POST",
7
+ "PUT",
8
+ "PATCH",
9
+ "DELETE",
10
+ "HEAD"
11
+ ];
12
+
13
+ const getMethodKey = (method) => {
14
+ let methodKey = method.toUpperCase();
15
+ if (methodKey === "DEL") return "DELETE";
16
+ return methodKey;
17
+ };
18
+ const isMethodExport = (exportName, extraMethods) => {
19
+ const methods = [...METHOD_EXPORTS, ...extraMethods].map(getMethodKey);
20
+ return methods.includes(getMethodKey(exportName));
21
+ };
22
+
4
23
  function walkTree(directory, tree = []) {
5
24
  const results = [];
6
25
  for (const fileName of readdirSync(directory)) {
@@ -50,20 +69,28 @@ async function generateRouter(routesDir, outputFile, options) {
50
69
  const exports = await generateRoutes(files);
51
70
  const importPaths = exports.map((x) => relative(dirname(outputFile), routesDir).concat(x.path));
52
71
  const content = buildRouter(exports, (r, e) => {
53
- if (options?.includeRoute ?? true) {
54
- return `${e}.route({ path: '${r.path.replace(/\/{0,1}index$/, "")}' })`;
72
+ 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}' })`;
55
78
  }
56
- return e;
79
+ return alias;
57
80
  });
58
81
  let routerContent = `// This file is auto-generated
59
82
 
60
83
  `;
61
84
  const extension = options?.importExtension || "";
62
- routerContent += importPaths.map((x, i) => `import { ${Object.keys(exports[i].exports).join(", ")} } from "./${x}${extension}"`).join("\n");
85
+ routerContent += importPaths.map((x, i) => `import { ${Object.keys(exports[i].exports).map((y) => `${y} as ${routePathToAlias(exports[i].path, y)}`).join(", ")} } from "./${x}${extension}"`).join("\n");
63
86
  routerContent += "\n\nexport const router = ";
64
87
  routerContent += JSON.stringify(content, null, 2).replace(/"/g, "").replace(/(\s*)([a-zA-Z0-9]+-[a-zA-Z0-9-]+):/g, '$1"$2":');
65
88
  writeFileSync(join(outputFile), routerContent);
66
89
  }
90
+ function routePathToAlias(routePath, methodName) {
91
+ const cleanPath = routePath.replace(/\{(\w+)\}/g, "$1").replace(/-/g, "_");
92
+ return cleanPath.split("/").filter(Boolean).join("_") + "__" + methodName;
93
+ }
67
94
  function buildRoutePath(parsedFile) {
68
95
  const directory = parsedFile.dir === parsedFile.root ? "" : parsedFile.dir;
69
96
  const name = `/${parsedFile.name}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orpc-file-based-router",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
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",