@tuyau/core 0.1.0 → 0.1.2
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.
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
} from "../chunk-3KNDPG6Y.js";
|
|
4
4
|
|
|
5
5
|
// commands/generate.ts
|
|
6
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
6
7
|
import { Project, QuoteKind } from "ts-morph";
|
|
7
8
|
import { BaseCommand, flags } from "@adonisjs/core/ace";
|
|
8
9
|
|
|
@@ -30,13 +31,13 @@ var ApiTypesGenerator = class {
|
|
|
30
31
|
this.#prepareDestination();
|
|
31
32
|
}
|
|
32
33
|
#getDestinationDirectory() {
|
|
33
|
-
return dirname(this.#destination
|
|
34
|
+
return dirname(this.#destination);
|
|
34
35
|
}
|
|
35
36
|
/**
|
|
36
37
|
* Create the destination directory if it does not exists
|
|
37
38
|
*/
|
|
38
39
|
#prepareDestination() {
|
|
39
|
-
this.#destination = new URL("./.adonisjs/api.ts", this.#appRoot);
|
|
40
|
+
this.#destination = fileURLToPath(new URL("./.adonisjs/api.ts", this.#appRoot));
|
|
40
41
|
const directory = this.#getDestinationDirectory();
|
|
41
42
|
if (!existsSync(directory)) {
|
|
42
43
|
mkdirSync(directory, { recursive: true });
|
|
@@ -109,34 +110,59 @@ var ApiTypesGenerator = class {
|
|
|
109
110
|
return interfaceContent;
|
|
110
111
|
}
|
|
111
112
|
/**
|
|
112
|
-
* Filter routes to generate based on the
|
|
113
|
+
* Filter routes to generate based on the config
|
|
113
114
|
*/
|
|
114
|
-
#
|
|
115
|
+
#filterRoutes(routes, mode) {
|
|
116
|
+
const config = this.#config.codegen?.[mode];
|
|
117
|
+
if (!config || !config.only && !config.except)
|
|
118
|
+
return routes;
|
|
115
119
|
return routes.filter((route) => {
|
|
116
|
-
if (
|
|
117
|
-
return
|
|
118
|
-
if (typeof
|
|
119
|
-
return !
|
|
120
|
+
if (typeof config.only === "function")
|
|
121
|
+
return config.only(route);
|
|
122
|
+
if (typeof config.except === "function")
|
|
123
|
+
return !config.except(route);
|
|
124
|
+
if (config.only) {
|
|
125
|
+
for (const pattern of config.only) {
|
|
126
|
+
if (pattern instanceof RegExp && pattern.test(route.pattern))
|
|
127
|
+
return true;
|
|
128
|
+
if (route.pattern === pattern)
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
return false;
|
|
120
132
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
133
|
+
if (config.except) {
|
|
134
|
+
for (const pattern of config.except) {
|
|
135
|
+
if (pattern instanceof RegExp && pattern.test(route.pattern))
|
|
136
|
+
return false;
|
|
137
|
+
if (route.pattern === pattern)
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
return true;
|
|
126
141
|
}
|
|
127
142
|
return true;
|
|
128
143
|
});
|
|
129
144
|
}
|
|
130
|
-
|
|
145
|
+
/**
|
|
146
|
+
* Generate a type name based on the route pattern and methods
|
|
147
|
+
*
|
|
148
|
+
* GET /users/:id => UsersIdGet
|
|
149
|
+
*/
|
|
150
|
+
#generateTypeName(route) {
|
|
151
|
+
const remappedSegments = route.pattern.split("/").filter(Boolean).map((segment) => segment.startsWith(":") ? "id" : segment).join(" ");
|
|
152
|
+
const methods = string.pascalCase(route.methods.join(" "));
|
|
153
|
+
return string.pascalCase(remappedSegments) + methods;
|
|
154
|
+
}
|
|
155
|
+
#generateRoutesNameArray(routes, typesByPattern) {
|
|
131
156
|
return routes.map(({ name, pattern, methods }) => {
|
|
132
157
|
const params = matchit.parse(pattern).filter((node) => node.type !== 0).map((node) => node.val);
|
|
133
|
-
|
|
158
|
+
let typeName = this.#generateTypeName({ pattern, methods });
|
|
159
|
+
if (!typesByPattern[typeName])
|
|
160
|
+
typeName = "unknown";
|
|
134
161
|
return { params, name, path: pattern, method: methods, types: typeName };
|
|
135
162
|
}).filter((route) => !!route.name);
|
|
136
163
|
}
|
|
137
164
|
async #writeApiFile(options) {
|
|
138
|
-
const
|
|
139
|
-
const file = this.#project.createSourceFile(path, "", { overwrite: true });
|
|
165
|
+
const file = this.#project.createSourceFile(this.#destination, "", { overwrite: true });
|
|
140
166
|
if (!file)
|
|
141
167
|
throw new Error("Unable to create the api.ts file");
|
|
142
168
|
file.removeText().insertText(0, (writer) => {
|
|
@@ -147,7 +173,7 @@ var ApiTypesGenerator = class {
|
|
|
147
173
|
writer.writeLine(` response: ${value.response}`);
|
|
148
174
|
writer.writeLine(`}`);
|
|
149
175
|
});
|
|
150
|
-
writer.writeLine(`interface
|
|
176
|
+
writer.writeLine(`export interface ApiDefinition {`).write(this.#generateDefinitionInterface(options.definition, " ")).writeLine(`}`);
|
|
151
177
|
writer.writeLine(`const routes = [`);
|
|
152
178
|
for (const route of options.routesNameArray) {
|
|
153
179
|
writer.writeLine(` {`);
|
|
@@ -159,7 +185,11 @@ var ApiTypesGenerator = class {
|
|
|
159
185
|
writer.writeLine(` },`);
|
|
160
186
|
}
|
|
161
187
|
writer.writeLine(`] as const;`);
|
|
162
|
-
writer.writeLine(`export const api = {`).writeLine(` routes,`).writeLine(` definition: {} as
|
|
188
|
+
writer.writeLine(`export const api = {`).writeLine(` routes,`).writeLine(` definition: {} as ApiDefinition`).writeLine(`}`);
|
|
189
|
+
writer.writeLine(`declare module '@tuyau/inertia/types' {`);
|
|
190
|
+
writer.writeLine(` type ApiDefinition = typeof api`);
|
|
191
|
+
writer.writeLine(` export interface Api extends ApiDefinition {}`);
|
|
192
|
+
writer.writeLine(`}`);
|
|
163
193
|
});
|
|
164
194
|
await file.save();
|
|
165
195
|
}
|
|
@@ -167,7 +197,7 @@ var ApiTypesGenerator = class {
|
|
|
167
197
|
const definition = {};
|
|
168
198
|
const typesByPattern = {};
|
|
169
199
|
const sourcesFiles = this.#project.getSourceFiles();
|
|
170
|
-
const routes = this.#
|
|
200
|
+
const routes = this.#filterRoutes(this.#routes, "definitions");
|
|
171
201
|
for (const route of routes) {
|
|
172
202
|
if (typeof route.handler === "function")
|
|
173
203
|
continue;
|
|
@@ -196,22 +226,21 @@ var ApiTypesGenerator = class {
|
|
|
196
226
|
currentLevel = currentLevel[segment];
|
|
197
227
|
if (i !== segments.length - 1)
|
|
198
228
|
return;
|
|
199
|
-
const typeName =
|
|
229
|
+
const typeName = this.#generateTypeName(route);
|
|
200
230
|
typesByPattern[typeName] = {
|
|
201
231
|
request: schemaImport ? `MakeTuyauRequest<${schemaImport}>` : "unknown",
|
|
202
232
|
response: `MakeTuyauResponse<import('${relativePath}').default['${routeHandler.method}']>`
|
|
203
233
|
};
|
|
204
234
|
currentLevel.$url = {};
|
|
205
|
-
for (const method of methods)
|
|
206
|
-
currentLevel[method] =
|
|
207
|
-
}
|
|
235
|
+
for (const method of methods)
|
|
236
|
+
currentLevel[method] = typeName;
|
|
208
237
|
});
|
|
209
238
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
typesByPattern
|
|
213
|
-
|
|
214
|
-
});
|
|
239
|
+
const routesNameArray = this.#generateRoutesNameArray(
|
|
240
|
+
this.#filterRoutes(routes, "routes"),
|
|
241
|
+
typesByPattern
|
|
242
|
+
);
|
|
243
|
+
await this.#writeApiFile({ definition, typesByPattern, routesNameArray });
|
|
215
244
|
}
|
|
216
245
|
};
|
|
217
246
|
|
|
@@ -234,7 +263,7 @@ var CodegenTypes = class extends BaseCommand {
|
|
|
234
263
|
async run() {
|
|
235
264
|
const project = new Project({
|
|
236
265
|
manipulationSettings: { quoteKind: QuoteKind.Single },
|
|
237
|
-
tsConfigFilePath: new URL("./tsconfig.json", this.app.appRoot)
|
|
266
|
+
tsConfigFilePath: fileURLToPath2(new URL("./tsconfig.json", this.app.appRoot))
|
|
238
267
|
});
|
|
239
268
|
const apiTypesGenerator = new ApiTypesGenerator({
|
|
240
269
|
project,
|
|
@@ -6,9 +6,14 @@ import { defineConfig } from '@tuyau/core'
|
|
|
6
6
|
const tuyauConfig = defineConfig({
|
|
7
7
|
codegen: {
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
9
|
+
* Filters the definitions and named routes to be generated
|
|
10
10
|
*/
|
|
11
|
-
|
|
11
|
+
// definitions: {
|
|
12
|
+
// only: [],
|
|
13
|
+
// }
|
|
14
|
+
// routes: {
|
|
15
|
+
// only: [],
|
|
16
|
+
// }
|
|
12
17
|
}
|
|
13
18
|
})
|
|
14
19
|
|
package/build/src/types.d.ts
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
import { RouteJSON } from '@adonisjs/core/types/http';
|
|
2
2
|
|
|
3
|
+
type Without<T, U> = {
|
|
4
|
+
[P in Exclude<keyof T, keyof U>]?: never;
|
|
5
|
+
};
|
|
6
|
+
type XOR<T, U> = T | U extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U;
|
|
7
|
+
type FilterConfig<FilterType> = XOR<{
|
|
8
|
+
only: FilterType;
|
|
9
|
+
}, {
|
|
10
|
+
except: FilterType;
|
|
11
|
+
}>;
|
|
3
12
|
interface TuyauConfig {
|
|
4
13
|
codegen?: {
|
|
5
14
|
/**
|
|
6
|
-
*
|
|
15
|
+
* Filters the definitions to be generated
|
|
7
16
|
*/
|
|
8
|
-
|
|
17
|
+
definitions?: FilterConfig<Array<string | RegExp> | ((route: RouteJSON) => boolean)>;
|
|
18
|
+
/**
|
|
19
|
+
* Filters the named routes to be generated
|
|
20
|
+
*/
|
|
21
|
+
routes?: FilterConfig<Array<string | RegExp> | ((route: RouteJSON) => boolean)>;
|
|
9
22
|
};
|
|
10
23
|
}
|
|
11
24
|
|
|
12
|
-
export type { TuyauConfig };
|
|
25
|
+
export type { FilterConfig, TuyauConfig };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tuyau/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.2",
|
|
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/
|
|
35
|
-
"@tuyau/
|
|
34
|
+
"@tuyau/client": "0.1.1",
|
|
35
|
+
"@tuyau/utils": "0.0.4"
|
|
36
36
|
},
|
|
37
37
|
"publishConfig": {
|
|
38
38
|
"access": "public",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
},
|
|
63
63
|
"scripts": {
|
|
64
64
|
"clean": "del-cli build",
|
|
65
|
-
"copy:templates": "copyfiles \"stubs/**/*.stub\" build",
|
|
65
|
+
"copy:templates": "copyfiles --up 1 \"stubs/**/*.stub\" build",
|
|
66
66
|
"typecheck": "tsc --noEmit",
|
|
67
67
|
"format": "prettier --write .",
|
|
68
68
|
"quick:test": "node --import=./tsnode.esm.js --enable-source-maps bin/test.ts",
|