@tuyau/core 0.0.6 → 0.1.0

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.
@@ -8,9 +8,11 @@ import { BaseCommand, flags } from "@adonisjs/core/ace";
8
8
 
9
9
  // src/codegen/api_types_generator.ts
10
10
  import { Node } from "ts-morph";
11
+ import matchit from "@poppinss/matchit";
11
12
  import { fileURLToPath } from "node:url";
12
13
  import { dirname, relative } from "node:path";
13
14
  import { existsSync, mkdirSync } from "node:fs";
15
+ import string from "@adonisjs/core/helpers/string";
14
16
  import { parseBindingReference } from "@adonisjs/core/helpers";
15
17
  var ApiTypesGenerator = class {
16
18
  #appRoot;
@@ -34,7 +36,7 @@ var ApiTypesGenerator = class {
34
36
  * Create the destination directory if it does not exists
35
37
  */
36
38
  #prepareDestination() {
37
- this.#destination = new URL("./.adonisjs/types/api.d.ts", this.#appRoot);
39
+ this.#destination = new URL("./.adonisjs/api.ts", this.#appRoot);
38
40
  const directory = this.#getDestinationDirectory();
39
41
  if (!existsSync(directory)) {
40
42
  mkdirSync(directory, { recursive: true });
@@ -90,13 +92,13 @@ var ApiTypesGenerator = class {
90
92
  /**
91
93
  * Generate the final interface containing all routes, request, and response
92
94
  */
93
- #generateFinalInterface(types, indent = " ") {
95
+ #generateDefinitionInterface(types, indent = " ") {
94
96
  let interfaceContent = "";
95
97
  Object.entries(types).forEach(([key, value]) => {
96
98
  if (typeof value === "object") {
97
99
  interfaceContent += `${indent}'${key}': {
98
100
  `;
99
- interfaceContent += this.#generateFinalInterface(value, indent + " ");
101
+ interfaceContent += this.#generateDefinitionInterface(value, indent + " ");
100
102
  interfaceContent += `${indent}};
101
103
  `;
102
104
  } else {
@@ -106,22 +108,6 @@ var ApiTypesGenerator = class {
106
108
  });
107
109
  return interfaceContent;
108
110
  }
109
- /**
110
- * Write the final interface containing all routes, request, and response
111
- * in a routes.d.ts file
112
- */
113
- async #writeFinalInterface(types) {
114
- const file = this.#project.createSourceFile(fileURLToPath(this.#destination), "", {
115
- overwrite: true
116
- });
117
- if (!file)
118
- throw new Error("Unable to create the api.d.ts file");
119
- file?.removeText();
120
- file.insertText(0, (writer) => {
121
- writer.writeLine(`import type { MakeTuyauRequest, MakeTuyauResponse } from '@tuyau/utils/types'`).writeLine(`import type { InferInput } from '@vinejs/vine/types'`).newLine().writeLine(`export interface AdonisApi {`).write(this.#generateFinalInterface(types, " ")).writeLine(`}`);
122
- });
123
- await file.save();
124
- }
125
111
  /**
126
112
  * Filter routes to generate based on the ignoreRoutes config
127
113
  */
@@ -141,8 +127,45 @@ var ApiTypesGenerator = class {
141
127
  return true;
142
128
  });
143
129
  }
130
+ #generateRoutesNameArray(routes) {
131
+ return routes.map(({ name, pattern, methods }) => {
132
+ const params = matchit.parse(pattern).filter((node) => node.type !== 0).map((node) => node.val);
133
+ const typeName = string.pascalCase(string.slug(pattern)) + string.pascalCase(methods.join(" "));
134
+ return { params, name, path: pattern, method: methods, types: typeName };
135
+ }).filter((route) => !!route.name);
136
+ }
137
+ async #writeApiFile(options) {
138
+ const path = fileURLToPath(this.#destination);
139
+ const file = this.#project.createSourceFile(path, "", { overwrite: true });
140
+ if (!file)
141
+ throw new Error("Unable to create the api.ts file");
142
+ file.removeText().insertText(0, (writer) => {
143
+ writer.writeLine(`import type { MakeTuyauRequest, MakeTuyauResponse } from '@tuyau/utils/types'`).writeLine(`import type { InferInput } from '@vinejs/vine/types'`).newLine();
144
+ Object.entries(options.typesByPattern).forEach(([key, value]) => {
145
+ writer.writeLine(`type ${key} = {`);
146
+ writer.writeLine(` request: ${value.request}`);
147
+ writer.writeLine(` response: ${value.response}`);
148
+ writer.writeLine(`}`);
149
+ });
150
+ writer.writeLine(`interface AdonisApi {`).write(this.#generateDefinitionInterface(options.definition, " ")).writeLine(`}`);
151
+ writer.writeLine(`const routes = [`);
152
+ for (const route of options.routesNameArray) {
153
+ writer.writeLine(` {`);
154
+ writer.writeLine(` params: ${JSON.stringify(route.params)},`);
155
+ writer.writeLine(` name: '${route.name}',`);
156
+ writer.writeLine(` path: '${route.path}',`);
157
+ writer.writeLine(` method: ${JSON.stringify(route.method)},`);
158
+ writer.writeLine(` types: {} as ${route.types},`);
159
+ writer.writeLine(` },`);
160
+ }
161
+ writer.writeLine(`] as const;`);
162
+ writer.writeLine(`export const api = {`).writeLine(` routes,`).writeLine(` definition: {} as AdonisApi`).writeLine(`}`);
163
+ });
164
+ await file.save();
165
+ }
144
166
  async generate() {
145
- const types = {};
167
+ const definition = {};
168
+ const typesByPattern = {};
146
169
  const sourcesFiles = this.#project.getSourceFiles();
147
170
  const routes = this.#filterRoutesToGenerate(this.#routes);
148
171
  for (const route of routes) {
@@ -165,25 +188,30 @@ var ApiTypesGenerator = class {
165
188
  const schemaImport = this.#extractRequest(handlerData);
166
189
  const methods = route.methods.map((method) => "$" + method.toLowerCase()).filter((method) => method !== "head");
167
190
  const segments = route.pattern.split("/").filter(Boolean);
168
- let currentLevel = types;
191
+ let currentLevel = definition;
169
192
  const relativePath = relative(this.#getDestinationDirectory(), file.getFilePath());
170
193
  segments.forEach((segment, i) => {
171
- if (!currentLevel[segment]) {
194
+ if (!currentLevel[segment])
172
195
  currentLevel[segment] = {};
173
- }
174
196
  currentLevel = currentLevel[segment];
175
197
  if (i !== segments.length - 1)
176
198
  return;
199
+ const typeName = string.pascalCase(string.slug(route.pattern)) + string.pascalCase(methods.join(""));
200
+ typesByPattern[typeName] = {
201
+ request: schemaImport ? `MakeTuyauRequest<${schemaImport}>` : "unknown",
202
+ response: `MakeTuyauResponse<import('${relativePath}').default['${routeHandler.method}']>`
203
+ };
177
204
  currentLevel.$url = {};
178
205
  for (const method of methods) {
179
- currentLevel[method] = {
180
- request: schemaImport ? `MakeTuyauRequest<${schemaImport}>` : "unknown",
181
- response: `MakeTuyauResponse<import('${relativePath}').default['${routeHandler.method}']>`
182
- };
206
+ currentLevel[method] = `${string.pascalCase(typeName)}`;
183
207
  }
184
208
  });
185
209
  }
186
- await this.#writeFinalInterface(types);
210
+ await this.#writeApiFile({
211
+ definition,
212
+ typesByPattern,
213
+ routesNameArray: this.#generateRoutesNameArray(routes)
214
+ });
187
215
  }
188
216
  };
189
217
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tuyau/core",
3
3
  "type": "module",
4
- "version": "0.0.6",
4
+ "version": "0.1.0",
5
5
  "description": "",
6
6
  "author": "",
7
7
  "license": "MIT",
@@ -31,8 +31,8 @@
31
31
  "@poppinss/cliui": "^6.4.1",
32
32
  "@poppinss/matchit": "^3.1.2",
33
33
  "@types/node": "^20.12.7",
34
- "@tuyau/client": "0.0.11",
35
- "@tuyau/utils": "0.0.3"
34
+ "@tuyau/utils": "0.0.4",
35
+ "@tuyau/client": "0.1.0"
36
36
  },
37
37
  "publishConfig": {
38
38
  "access": "public",