@automatons/typescript-server-nestjs 1.0.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) tanmen (yt.prog@gmail.com)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # @automatons/typescript-server-nestjs
2
+ [![CI/CD](https://github.com/openapi-automatons/typescript-server-nestjs/actions/workflows/ci-cd.yml/badge.svg)](https://github.com/openapi-automatons/typescript-server-nestjs/actions/workflows/ci-cd.yml)
3
+ [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
4
+ [![npm downloads](https://img.shields.io/npm/dw/@automatons/typescript-server-nestjs)](https://www.npmjs.com/package/@automatons/typescript-server-nestjs)
5
+
6
+ ## What is @automatons/typescript-server-nestjs
7
+ This is a server generator that emits [NestJS](https://nestjs.com) controllers, validated DTOs and
8
+ service contracts from an OpenAPI document.
9
+ Only use openapi-automatons.
10
+
11
+ This package is **ESM-only** and requires **Node.js >= 22**.
12
+
13
+ It is **contract-first and non-destructive**: every file it writes is safe to regenerate, because your
14
+ business logic lives in your own service implementations, not in the generated code. For each tag it emits:
15
+
16
+ - `dto/` — `class-validator` / `class-transformer` decorated DTO classes (object schemas) and type aliases,
17
+ - `services/<tag>.service.ts` — an **abstract** `<Tag>Service` class declaring the methods you must implement,
18
+ - `controllers/<tag>.controller.ts` — a `@Controller()` that wires the routes and delegates to the injected service.
19
+
20
+ You implement the abstract service and wire it into your own module — regenerating never touches that code.
21
+
22
+ `@nestjs/common`, `class-validator` and `class-transformer` are peer dependencies.
23
+
24
+ ## Generated server
25
+ ```ts
26
+ // your code — implement the generated contract
27
+ import { Injectable } from "@nestjs/common";
28
+ import { PetsService } from "./generated/services";
29
+ import { Pet } from "./generated/dto";
30
+
31
+ @Injectable()
32
+ export class PetsServiceImpl extends PetsService {
33
+ async showPetById(petId: string): Promise<Pet> {
34
+ // ...your logic
35
+ }
36
+ }
37
+
38
+ // wire it in your module
39
+ @Module({
40
+ controllers: [PetsController],
41
+ providers: [{ provide: PetsService, useClass: PetsServiceImpl }],
42
+ })
43
+ export class PetsModule {}
44
+ ```
45
+
46
+ ## How can I use @automatons/typescript-server-nestjs?
47
+ This library is designed to be used by [openapi-automatons](https://github.com/openapi-automatons/openapi-automatons).
48
+ Please read the [readme](https://github.com/openapi-automatons/openapi-automatons/blob/main/README.md) of [openapi-automatons](https://github.com/openapi-automatons/openapi-automatons) for how to use it.
package/index.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { Automaton } from '@automatons/tools';
2
+
3
+ declare const generatorTypescriptNestjsServer: Automaton;
4
+
5
+ export { generatorTypescriptNestjsServer as default };
package/index.js ADDED
@@ -0,0 +1,352 @@
1
+ // src/generator/generate.ts
2
+ import { parser } from "@automatons/parser";
3
+
4
+ // src/generator/render.ts
5
+ import path from "path";
6
+ import { mkdir, writeFile } from "fs/promises";
7
+ import { IndentationText, Project, QuoteKind } from "ts-morph";
8
+ import { format } from "prettier";
9
+ var project = new Project({
10
+ useInMemoryFileSystem: true,
11
+ manipulationSettings: {
12
+ quoteKind: QuoteKind.Double,
13
+ indentationText: IndentationText.TwoSpaces,
14
+ useTrailingCommas: true
15
+ }
16
+ });
17
+ var counter = 0;
18
+ var render = (build) => {
19
+ const sf = project.createSourceFile(`__gen_${counter++}.ts`, "", { overwrite: true });
20
+ build(sf);
21
+ const text = sf.getFullText();
22
+ project.removeSourceFile(sf);
23
+ return text;
24
+ };
25
+ var write = async (outDir, relPath, text) => {
26
+ const outputPath = path.resolve(outDir, relPath);
27
+ await mkdir(path.dirname(outputPath), { recursive: true });
28
+ const formatted = await format(text, { parser: "typescript" });
29
+ await writeFile(outputPath, formatted, { encoding: "utf-8" });
30
+ };
31
+
32
+ // src/generator/schema.ts
33
+ var quoteKey = (name) => name.includes("-") ? `"${name}"` : name;
34
+ var nullable = (schema) => schema.nullable ? " | null" : "";
35
+ var schemaToType = (schema) => {
36
+ switch (schema.type) {
37
+ case "model":
38
+ return `${schema.name}${nullable(schema)}`;
39
+ case "object": {
40
+ if (schema.properties && schema.properties.length) {
41
+ const props = schema.properties.map(
42
+ (property) => `${quoteKey(property.name)}${property.required ? "" : "?"}: ${schemaToType(property.schema)};`
43
+ ).join("\n");
44
+ return `{
45
+ ${props}
46
+ }${nullable(schema)}`;
47
+ }
48
+ return `Record<string, unknown>${nullable(schema)}`;
49
+ }
50
+ case "allOf":
51
+ return `(${schema.schemas.map(schemaToType).join(" & ")})${nullable(schema)}`;
52
+ case "oneOf":
53
+ case "anyOf":
54
+ return `(${schema.schemas.map(schemaToType).join(" | ")})${nullable(schema)}`;
55
+ case "array":
56
+ return `${schema.items ? `Array<${schemaToType(schema.items)}>` : "any[]"}${nullable(schema)}`;
57
+ case "boolean":
58
+ return `boolean${nullable(schema)}`;
59
+ case "string": {
60
+ const base = schema.enum && schema.enum.length ? schema.enum.map((value) => `"${value}"`).join(" | ") : schema.format === "date" || schema.format === "date-time" ? "Date" : schema.format === "url" ? "URL" : "string";
61
+ return `${base}${nullable(schema)}`;
62
+ }
63
+ case "integer":
64
+ case "number": {
65
+ const base = schema.enum && schema.enum.length ? schema.enum.join(" | ") : "number";
66
+ return `${base}${nullable(schema)}`;
67
+ }
68
+ default:
69
+ throw new Error(`Unsupported schema type: ${schema.type}`);
70
+ }
71
+ };
72
+
73
+ // src/generator/dto.ts
74
+ var enumValues = (values) => `[${values.map((value) => `"${value}"`).join(", ")}]`;
75
+ var typeDecorators = (schema, decos, each) => {
76
+ const eachArg = each ? "{ each: true }" : void 0;
77
+ const validationArgs = (head) => {
78
+ if (each) return head ? [head, "{ each: true }"] : ["undefined", "{ each: true }"];
79
+ return head ? [head] : [];
80
+ };
81
+ switch (schema.type) {
82
+ case "model":
83
+ decos.push({ name: "ValidateNested", from: "class-validator", args: eachArg ? [eachArg] : [] });
84
+ decos.push({ name: "Type", from: "class-transformer", args: [`() => ${schema.name}`] });
85
+ break;
86
+ case "array":
87
+ decos.push({ name: "IsArray", from: "class-validator" });
88
+ if (schema.items) typeDecorators(schema.items, decos, true);
89
+ break;
90
+ case "boolean":
91
+ decos.push({ name: "IsBoolean", from: "class-validator", args: eachArg ? [eachArg] : [] });
92
+ break;
93
+ case "integer":
94
+ case "number":
95
+ decos.push({ name: "IsNumber", from: "class-validator", args: validationArgs("{}") });
96
+ break;
97
+ case "string":
98
+ if (schema.enum && schema.enum.length) {
99
+ decos.push({ name: "IsIn", from: "class-validator", args: validationArgs(enumValues(schema.enum)) });
100
+ } else if (schema.format === "date" || schema.format === "date-time") {
101
+ decos.push({ name: "IsDate", from: "class-validator", args: eachArg ? [eachArg] : [] });
102
+ decos.push({ name: "Type", from: "class-transformer", args: ["() => Date"] });
103
+ } else if (schema.format === "url") {
104
+ decos.push({ name: "IsUrl", from: "class-validator", args: validationArgs() });
105
+ } else {
106
+ decos.push({ name: "IsString", from: "class-validator", args: eachArg ? [eachArg] : [] });
107
+ }
108
+ break;
109
+ case "object":
110
+ decos.push({ name: "IsObject", from: "class-validator", args: eachArg ? [eachArg] : [] });
111
+ break;
112
+ default:
113
+ break;
114
+ }
115
+ };
116
+ var propertyDecorators = (property) => {
117
+ const decos = [];
118
+ if (!property.required) decos.push({ name: "IsOptional", from: "class-validator" });
119
+ typeDecorators(property.schema, decos, false);
120
+ return decos;
121
+ };
122
+ var toStructure = (deco) => ({
123
+ name: deco.name,
124
+ arguments: deco.args ?? []
125
+ });
126
+ var isObjectModel = (model) => model.schema.type === "object" && Boolean(model.schema.properties && model.schema.properties.length);
127
+ var emitDto = (model) => render((sf) => {
128
+ if (!isObjectModel(model)) {
129
+ model.imports.forEach(
130
+ (imported) => sf.addImportDeclaration({ isTypeOnly: true, namedImports: [imported.title], moduleSpecifier: `./${imported.filename}` })
131
+ );
132
+ sf.addTypeAlias({ isExported: true, name: model.title, type: schemaToType(model.schema) });
133
+ return;
134
+ }
135
+ const properties = model.schema.type === "object" && model.schema.properties || [];
136
+ const propertyDecos = properties.map((property) => ({ property, decos: propertyDecorators(property) }));
137
+ const validators = /* @__PURE__ */ new Set();
138
+ let usesType = false;
139
+ propertyDecos.forEach(
140
+ ({ decos }) => decos.forEach((deco) => {
141
+ if (deco.from === "class-transformer") usesType = true;
142
+ else validators.add(deco.name);
143
+ })
144
+ );
145
+ if (validators.size) {
146
+ sf.addImportDeclaration({ namedImports: [...validators].sort(), moduleSpecifier: "class-validator" });
147
+ }
148
+ if (usesType) {
149
+ sf.addImportDeclaration({ namedImports: ["Type"], moduleSpecifier: "class-transformer" });
150
+ }
151
+ model.imports.forEach(
152
+ (imported) => sf.addImportDeclaration({ namedImports: [imported.title], moduleSpecifier: `./${imported.filename}` })
153
+ );
154
+ const dtoClass = sf.addClass({ isExported: true, name: model.title });
155
+ propertyDecos.forEach(
156
+ ({ property, decos }) => dtoClass.addProperty({
157
+ name: property.name.includes("-") ? `"${property.name}"` : property.name,
158
+ type: schemaToType(property.schema),
159
+ hasQuestionToken: !property.required,
160
+ hasExclamationToken: property.required,
161
+ decorators: decos.map(toStructure)
162
+ })
163
+ );
164
+ });
165
+ var emitDtoIndex = (models) => render((sf) => models.forEach((model) => sf.addExportDeclaration({ moduleSpecifier: `./${model.filename}` })));
166
+
167
+ // src/generator/meta.ts
168
+ var baseName = (api) => api.title.replace(/Api$/, "");
169
+ var baseFilename = (api) => api.filename.replace(/Api$/, "");
170
+ var methodDecorator = (method) => {
171
+ const map = {
172
+ get: "Get",
173
+ post: "Post",
174
+ put: "Put",
175
+ patch: "Patch",
176
+ delete: "Delete",
177
+ head: "Head",
178
+ options: "Options"
179
+ };
180
+ return map[method.toLowerCase()] ?? "All";
181
+ };
182
+ var routePath = (path2) => path2.replace(/^\//, "").replace(/\{([^}]+)}/g, ":$1");
183
+ var identifier = (name) => name.replace(/[^A-Za-z0-9_$]/g, "_");
184
+ var isAffect = (path2) => "forms" in path2;
185
+ var endpointParams = (path2) => {
186
+ const params = [];
187
+ (path2.parameters ?? []).forEach(
188
+ (parameter) => params.push({
189
+ id: identifier(parameter.name),
190
+ type: schemaToType(parameter.schema),
191
+ optional: false,
192
+ decorator: { name: "Param", argument: `"${parameter.name}"` }
193
+ })
194
+ );
195
+ const forms = isAffect(path2) ? path2.forms ?? [] : [];
196
+ if (forms.length) {
197
+ params.push({
198
+ id: "body",
199
+ type: forms.map((form) => schemaToType(form.schema)).join(" | "),
200
+ optional: !forms.some((form) => form.required),
201
+ decorator: { name: "Body" }
202
+ });
203
+ }
204
+ (path2.queries ?? []).forEach(
205
+ (query) => params.push({
206
+ id: identifier(query.name),
207
+ type: schemaToType(query.schema),
208
+ optional: !query.required,
209
+ decorator: { name: "Query", argument: `"${query.name}"` }
210
+ })
211
+ );
212
+ return params.sort((a, b) => Number(a.optional) - Number(b.optional));
213
+ };
214
+ var responseType = (path2) => path2.schema ? schemaToType(path2.schema) : "void";
215
+ var collectFromSchema = (schema, names) => {
216
+ if (!schema) return;
217
+ switch (schema.type) {
218
+ case "model":
219
+ names.add(schema.name);
220
+ break;
221
+ case "array":
222
+ collectFromSchema(schema.items, names);
223
+ break;
224
+ case "object":
225
+ (schema.properties ?? []).forEach((property) => collectFromSchema(property.schema, names));
226
+ break;
227
+ case "allOf":
228
+ case "oneOf":
229
+ case "anyOf":
230
+ schema.schemas.forEach((inner) => collectFromSchema(inner, names));
231
+ break;
232
+ default:
233
+ break;
234
+ }
235
+ };
236
+ var referencedModels = (api) => {
237
+ const names = /* @__PURE__ */ new Set();
238
+ api.paths.forEach((path2) => {
239
+ (path2.parameters ?? []).forEach((parameter) => collectFromSchema(parameter.schema, names));
240
+ (path2.queries ?? []).forEach((query) => collectFromSchema(query.schema, names));
241
+ if (isAffect(path2)) (path2.forms ?? []).forEach((form) => collectFromSchema(form.schema, names));
242
+ collectFromSchema(path2.schema, names);
243
+ });
244
+ return api.imports.filter((model) => names.has(model.title));
245
+ };
246
+
247
+ // src/generator/service.ts
248
+ var emitService = (api) => render((sf) => {
249
+ const models = referencedModels(api);
250
+ if (models.length) {
251
+ sf.addImportDeclaration({
252
+ isTypeOnly: true,
253
+ namedImports: models.map((model) => model.title),
254
+ moduleSpecifier: "../dto"
255
+ });
256
+ }
257
+ const serviceClass = sf.addClass({ isExported: true, isAbstract: true, name: `${baseName(api)}Service` });
258
+ api.paths.forEach(
259
+ (path2) => serviceClass.addMethod({
260
+ isAbstract: true,
261
+ name: path2.name,
262
+ parameters: endpointParams(path2).map((param) => ({
263
+ name: param.id,
264
+ type: param.type,
265
+ hasQuestionToken: param.optional
266
+ })),
267
+ returnType: `Promise<${responseType(path2)}>`
268
+ })
269
+ );
270
+ });
271
+ var emitServicesIndex = (apis) => render((sf) => apis.forEach((api) => sf.addExportDeclaration({ moduleSpecifier: `./${baseFilename(api)}.service` })));
272
+
273
+ // src/generator/controller.ts
274
+ import { Scope } from "ts-morph";
275
+ var paramDecorator = (decorator) => ({
276
+ name: decorator.name,
277
+ arguments: decorator.argument ? [decorator.argument] : []
278
+ });
279
+ var emitController = (api) => render((sf) => {
280
+ const base = baseName(api);
281
+ const nestImports = /* @__PURE__ */ new Set(["Controller"]);
282
+ api.paths.forEach((path2) => {
283
+ nestImports.add(methodDecorator(path2.method));
284
+ endpointParams(path2).forEach((param) => nestImports.add(param.decorator.name));
285
+ });
286
+ sf.addImportDeclaration({ namedImports: [...nestImports].sort(), moduleSpecifier: "@nestjs/common" });
287
+ sf.addImportDeclaration({ namedImports: [`${base}Service`], moduleSpecifier: "../services" });
288
+ const models = referencedModels(api);
289
+ if (models.length) {
290
+ sf.addImportDeclaration({ namedImports: models.map((model) => model.title), moduleSpecifier: "../dto" });
291
+ }
292
+ const controllerClass = sf.addClass({
293
+ isExported: true,
294
+ name: `${base}Controller`,
295
+ decorators: [{ name: "Controller", arguments: [] }]
296
+ });
297
+ controllerClass.addConstructor({
298
+ parameters: [{ name: "service", type: `${base}Service`, scope: Scope.Private, isReadonly: true }]
299
+ });
300
+ api.paths.forEach((path2) => {
301
+ const params = endpointParams(path2);
302
+ controllerClass.addMethod({
303
+ name: path2.name,
304
+ decorators: [{ name: methodDecorator(path2.method), arguments: [`"${routePath(path2.path)}"`] }],
305
+ parameters: params.map((param) => ({
306
+ name: param.id,
307
+ type: param.type,
308
+ hasQuestionToken: param.optional,
309
+ decorators: [paramDecorator(param.decorator)]
310
+ })),
311
+ returnType: `Promise<${responseType(path2)}>`,
312
+ statements: [`return this.service.${path2.name}(${params.map((param) => param.id).join(", ")});`]
313
+ });
314
+ });
315
+ });
316
+ var emitControllersIndex = (apis) => render((sf) => apis.forEach((api) => sf.addExportDeclaration({ moduleSpecifier: `./${baseFilename(api)}.controller` })));
317
+
318
+ // src/generator/generate.ts
319
+ var emitIndex = (hasDto, hasApis) => render((sf) => {
320
+ if (hasDto) sf.addExportDeclaration({ moduleSpecifier: "./dto" });
321
+ if (hasApis) {
322
+ sf.addExportDeclaration({ moduleSpecifier: "./services" });
323
+ sf.addExportDeclaration({ moduleSpecifier: "./controllers" });
324
+ }
325
+ });
326
+ var generate = async (openapi, settings) => {
327
+ const { outDir } = settings;
328
+ const { models, apis } = await parser(openapi, settings);
329
+ const tasks = [];
330
+ if (models.length) {
331
+ tasks.push(write(outDir, "dto/index.ts", emitDtoIndex(models)));
332
+ models.forEach((model) => tasks.push(write(outDir, `dto/${model.filename}.ts`, emitDto(model))));
333
+ }
334
+ if (apis.length) {
335
+ tasks.push(write(outDir, "services/index.ts", emitServicesIndex(apis)));
336
+ tasks.push(write(outDir, "controllers/index.ts", emitControllersIndex(apis)));
337
+ apis.forEach((api) => {
338
+ tasks.push(write(outDir, `services/${baseFilename(api)}.service.ts`, emitService(api)));
339
+ tasks.push(write(outDir, `controllers/${baseFilename(api)}.controller.ts`, emitController(api)));
340
+ });
341
+ }
342
+ tasks.push(write(outDir, "index.ts", emitIndex(models.length > 0, apis.length > 0)));
343
+ return Promise.all(tasks);
344
+ };
345
+
346
+ // src/index.ts
347
+ var generatorTypescriptNestjsServer = (openapi, settings) => generate(openapi, settings);
348
+ var index_default = generatorTypescriptNestjsServer;
349
+ export {
350
+ index_default as default
351
+ };
352
+ //# sourceMappingURL=index.js.map
package/index.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/generator/generate.ts","../src/generator/render.ts","../src/generator/schema.ts","../src/generator/dto.ts","../src/generator/meta.ts","../src/generator/service.ts","../src/generator/controller.ts","../src/index.ts"],"sourcesContent":["import {AutomatonSettings, Openapi} from \"@automatons/tools\";\nimport {parser} from \"@automatons/parser\";\nimport {render, write} from \"./render\";\nimport {emitDto, emitDtoIndex} from \"./dto\";\nimport {emitService, emitServicesIndex} from \"./service\";\nimport {emitController, emitControllersIndex} from \"./controller\";\nimport {baseFilename} from \"./meta\";\n\nconst emitIndex = (hasDto: boolean, hasApis: boolean): string =>\n render((sf) => {\n if (hasDto) sf.addExportDeclaration({moduleSpecifier: \"./dto\"});\n if (hasApis) {\n sf.addExportDeclaration({moduleSpecifier: \"./services\"});\n sf.addExportDeclaration({moduleSpecifier: \"./controllers\"});\n }\n });\n\nexport const generate = async (openapi: Openapi, settings: AutomatonSettings): Promise<void[]> => {\n const {outDir} = settings;\n const {models, apis} = await parser(openapi, settings);\n const tasks: Promise<void>[] = [];\n\n if (models.length) {\n tasks.push(write(outDir, \"dto/index.ts\", emitDtoIndex(models)));\n models.forEach((model) => tasks.push(write(outDir, `dto/${model.filename}.ts`, emitDto(model))));\n }\n\n if (apis.length) {\n tasks.push(write(outDir, \"services/index.ts\", emitServicesIndex(apis)));\n tasks.push(write(outDir, \"controllers/index.ts\", emitControllersIndex(apis)));\n apis.forEach((api) => {\n tasks.push(write(outDir, `services/${baseFilename(api)}.service.ts`, emitService(api)));\n tasks.push(write(outDir, `controllers/${baseFilename(api)}.controller.ts`, emitController(api)));\n });\n }\n\n tasks.push(write(outDir, \"index.ts\", emitIndex(models.length > 0, apis.length > 0)));\n\n return Promise.all(tasks);\n};\n","import path from \"node:path\";\nimport {mkdir, writeFile} from \"node:fs/promises\";\nimport {IndentationText, Project, QuoteKind, SourceFile} from \"ts-morph\";\nimport {format} from \"prettier\";\n\nconst project = new Project({\n useInMemoryFileSystem: true,\n manipulationSettings: {\n quoteKind: QuoteKind.Double,\n indentationText: IndentationText.TwoSpaces,\n useTrailingCommas: true,\n },\n});\n\nlet counter = 0;\n\n/**\n * Build a TypeScript source string by manipulating a throwaway ts-morph SourceFile.\n */\nexport const render = (build: (sf: SourceFile) => void): string => {\n const sf = project.createSourceFile(`__gen_${counter++}.ts`, \"\", {overwrite: true});\n build(sf);\n const text = sf.getFullText();\n project.removeSourceFile(sf);\n return text;\n};\n\n/**\n * Format with prettier and write the file under outDir.\n */\nexport const write = async (outDir: string, relPath: string, text: string): Promise<void> => {\n const outputPath = path.resolve(outDir, relPath);\n await mkdir(path.dirname(outputPath), {recursive: true});\n const formatted = await format(text, {parser: \"typescript\"});\n await writeFile(outputPath, formatted, {encoding: \"utf-8\"});\n};\n","import {Schema} from \"@automatons/parser\";\n\nconst quoteKey = (name: string): string => (name.includes(\"-\") ? `\"${name}\"` : name);\n\nconst nullable = (schema: {nullable?: boolean}): string => (schema.nullable ? \" | null\" : \"\");\n\n/**\n * Render a parser Schema as a TypeScript type string. Model references resolve to\n * the generated DTO class names (used as types).\n */\nexport const schemaToType = (schema: Schema): string => {\n switch (schema.type) {\n case \"model\":\n return `${schema.name}${nullable(schema)}`;\n case \"object\": {\n if (schema.properties && schema.properties.length) {\n const props = schema.properties\n .map(\n (property) =>\n `${quoteKey(property.name)}${property.required ? \"\" : \"?\"}: ${schemaToType(property.schema)};`,\n )\n .join(\"\\n\");\n return `{\\n${props}\\n}${nullable(schema)}`;\n }\n return `Record<string, unknown>${nullable(schema)}`;\n }\n case \"allOf\":\n return `(${schema.schemas.map(schemaToType).join(\" & \")})${nullable(schema)}`;\n case \"oneOf\":\n case \"anyOf\":\n return `(${schema.schemas.map(schemaToType).join(\" | \")})${nullable(schema)}`;\n case \"array\":\n return `${schema.items ? `Array<${schemaToType(schema.items)}>` : \"any[]\"}${nullable(schema)}`;\n case \"boolean\":\n return `boolean${nullable(schema)}`;\n case \"string\": {\n const base =\n schema.enum && schema.enum.length\n ? schema.enum.map((value) => `\"${value}\"`).join(\" | \")\n : schema.format === \"date\" || schema.format === \"date-time\"\n ? \"Date\"\n : schema.format === \"url\"\n ? \"URL\"\n : \"string\";\n return `${base}${nullable(schema)}`;\n }\n case \"integer\":\n case \"number\": {\n const base = schema.enum && schema.enum.length ? schema.enum.join(\" | \") : \"number\";\n return `${base}${nullable(schema)}`;\n }\n default:\n throw new Error(`Unsupported schema type: ${(schema as {type: string}).type}`);\n }\n};\n","import {Model, Property, Schema} from \"@automatons/parser\";\nimport {OptionalKind, DecoratorStructure} from \"ts-morph\";\nimport {render} from \"./render\";\nimport {schemaToType} from \"./schema\";\n\ntype Deco = {name: string; args?: string[]; from: \"class-validator\" | \"class-transformer\"};\n\nconst enumValues = (values: ReadonlyArray<string>): string => `[${values.map((value) => `\"${value}\"`).join(\", \")}]`;\n\n/** Collect the class-validator / class-transformer decorators for a single schema. */\nconst typeDecorators = (schema: Schema, decos: Deco[], each: boolean): void => {\n const eachArg = each ? \"{ each: true }\" : undefined;\n const validationArgs = (head?: string): string[] => {\n if (each) return head ? [head, \"{ each: true }\"] : [\"undefined\", \"{ each: true }\"];\n return head ? [head] : [];\n };\n switch (schema.type) {\n case \"model\":\n decos.push({name: \"ValidateNested\", from: \"class-validator\", args: eachArg ? [eachArg] : []});\n decos.push({name: \"Type\", from: \"class-transformer\", args: [`() => ${schema.name}`]});\n break;\n case \"array\":\n decos.push({name: \"IsArray\", from: \"class-validator\"});\n if (schema.items) typeDecorators(schema.items, decos, true);\n break;\n case \"boolean\":\n decos.push({name: \"IsBoolean\", from: \"class-validator\", args: eachArg ? [eachArg] : []});\n break;\n case \"integer\":\n case \"number\":\n decos.push({name: \"IsNumber\", from: \"class-validator\", args: validationArgs(\"{}\")});\n break;\n case \"string\":\n if (schema.enum && schema.enum.length) {\n decos.push({name: \"IsIn\", from: \"class-validator\", args: validationArgs(enumValues(schema.enum))});\n } else if (schema.format === \"date\" || schema.format === \"date-time\") {\n decos.push({name: \"IsDate\", from: \"class-validator\", args: eachArg ? [eachArg] : []});\n decos.push({name: \"Type\", from: \"class-transformer\", args: [\"() => Date\"]});\n } else if (schema.format === \"url\") {\n decos.push({name: \"IsUrl\", from: \"class-validator\", args: validationArgs()});\n } else {\n decos.push({name: \"IsString\", from: \"class-validator\", args: eachArg ? [eachArg] : []});\n }\n break;\n case \"object\":\n decos.push({name: \"IsObject\", from: \"class-validator\", args: eachArg ? [eachArg] : []});\n break;\n default:\n // allOf / oneOf / anyOf: compile-time type only, no runtime decorator.\n break;\n }\n};\n\nconst propertyDecorators = (property: Property): Deco[] => {\n const decos: Deco[] = [];\n if (!property.required) decos.push({name: \"IsOptional\", from: \"class-validator\"});\n typeDecorators(property.schema, decos, false);\n return decos;\n};\n\nconst toStructure = (deco: Deco): OptionalKind<DecoratorStructure> => ({\n name: deco.name,\n arguments: deco.args ?? [],\n});\n\nconst isObjectModel = (model: Model): boolean =>\n model.schema.type === \"object\" && Boolean(model.schema.properties && model.schema.properties.length);\n\n/**\n * Emit a single dto file: a decorated DTO class for object schemas, or a type alias otherwise.\n */\nexport const emitDto = (model: Model): string =>\n render((sf) => {\n if (!isObjectModel(model)) {\n model.imports.forEach((imported) =>\n sf.addImportDeclaration({isTypeOnly: true, namedImports: [imported.title], moduleSpecifier: `./${imported.filename}`}),\n );\n sf.addTypeAlias({isExported: true, name: model.title, type: schemaToType(model.schema)});\n return;\n }\n\n const properties = (model.schema.type === \"object\" && model.schema.properties) || [];\n const propertyDecos = properties.map((property) => ({property, decos: propertyDecorators(property)}));\n\n const validators = new Set<string>();\n let usesType = false;\n propertyDecos.forEach(({decos}) =>\n decos.forEach((deco) => {\n if (deco.from === \"class-transformer\") usesType = true;\n else validators.add(deco.name);\n }),\n );\n\n if (validators.size) {\n sf.addImportDeclaration({namedImports: [...validators].sort(), moduleSpecifier: \"class-validator\"});\n }\n if (usesType) {\n sf.addImportDeclaration({namedImports: [\"Type\"], moduleSpecifier: \"class-transformer\"});\n }\n model.imports.forEach((imported) =>\n sf.addImportDeclaration({namedImports: [imported.title], moduleSpecifier: `./${imported.filename}`}),\n );\n\n const dtoClass = sf.addClass({isExported: true, name: model.title});\n propertyDecos.forEach(({property, decos}) =>\n dtoClass.addProperty({\n name: property.name.includes(\"-\") ? `\"${property.name}\"` : property.name,\n type: schemaToType(property.schema),\n hasQuestionToken: !property.required,\n hasExclamationToken: property.required,\n decorators: decos.map(toStructure),\n }),\n );\n });\n\n/**\n * Emit dto/index.ts re-exporting every dto.\n */\nexport const emitDtoIndex = (models: Model[]): string =>\n render((sf) => models.forEach((model) => sf.addExportDeclaration({moduleSpecifier: `./${model.filename}`})));\n","import {AffectPath, Api, Model, Path, Schema} from \"@automatons/parser\";\nimport {schemaToType} from \"./schema\";\n\n/** `PetsApi` -> `Pets` (the controller/service base name). */\nexport const baseName = (api: Api): string => api.title.replace(/Api$/, \"\");\n/** `petsApi` -> `pets` (the controller/service file name). */\nexport const baseFilename = (api: Api): string => api.filename.replace(/Api$/, \"\");\n\n/** Map an HTTP method to its `@nestjs/common` route decorator. */\nexport const methodDecorator = (method: string): string => {\n const map: Record<string, string> = {\n get: \"Get\",\n post: \"Post\",\n put: \"Put\",\n patch: \"Patch\",\n delete: \"Delete\",\n head: \"Head\",\n options: \"Options\",\n };\n return map[method.toLowerCase()] ?? \"All\";\n};\n\n/** `/pets/{petId}` -> `pets/:petId` (a NestJS route path). */\nexport const routePath = (path: string): string => path.replace(/^\\//, \"\").replace(/\\{([^}]+)}/g, \":$1\");\n\n/** Make an OpenAPI parameter name safe to use as a JS identifier. */\nexport const identifier = (name: string): string => name.replace(/[^A-Za-z0-9_$]/g, \"_\");\n\nconst isAffect = (path: Path): path is AffectPath => \"forms\" in path;\n\nexport type EndpointParam = {\n id: string;\n type: string;\n optional: boolean;\n decorator: {name: string; argument?: string};\n};\n\n/** Build the (decorator-aware) parameter list for an operation, required-first. */\nexport const endpointParams = (path: Path): EndpointParam[] => {\n const params: EndpointParam[] = [];\n\n (path.parameters ?? []).forEach((parameter) =>\n params.push({\n id: identifier(parameter.name),\n type: schemaToType(parameter.schema),\n optional: false,\n decorator: {name: \"Param\", argument: `\"${parameter.name}\"`},\n }),\n );\n\n const forms = isAffect(path) ? (path.forms ?? []) : [];\n if (forms.length) {\n params.push({\n id: \"body\",\n type: forms.map((form) => schemaToType(form.schema)).join(\" | \"),\n optional: !forms.some((form) => form.required),\n decorator: {name: \"Body\"},\n });\n }\n\n (path.queries ?? []).forEach((query) =>\n params.push({\n id: identifier(query.name),\n type: schemaToType(query.schema),\n optional: !query.required,\n decorator: {name: \"Query\", argument: `\"${query.name}\"`},\n }),\n );\n\n return params.sort((a, b) => Number(a.optional) - Number(b.optional));\n};\n\n/** The response type of an operation (`void` when there is no body schema). */\nexport const responseType = (path: Path): string => (path.schema ? schemaToType(path.schema) : \"void\");\n\nconst collectFromSchema = (schema: Schema | undefined, names: Set<string>): void => {\n if (!schema) return;\n switch (schema.type) {\n case \"model\":\n names.add(schema.name);\n break;\n case \"array\":\n collectFromSchema(schema.items, names);\n break;\n case \"object\":\n (schema.properties ?? []).forEach((property) => collectFromSchema(property.schema, names));\n break;\n case \"allOf\":\n case \"oneOf\":\n case \"anyOf\":\n schema.schemas.forEach((inner) => collectFromSchema(inner, names));\n break;\n default:\n break;\n }\n};\n\n/** The DTO models actually referenced by an api's operation signatures. */\nexport const referencedModels = (api: Api): Model[] => {\n const names = new Set<string>();\n api.paths.forEach((path) => {\n (path.parameters ?? []).forEach((parameter) => collectFromSchema(parameter.schema, names));\n (path.queries ?? []).forEach((query) => collectFromSchema(query.schema, names));\n if (isAffect(path)) (path.forms ?? []).forEach((form) => collectFromSchema(form.schema, names));\n collectFromSchema(path.schema, names);\n });\n return api.imports.filter((model) => names.has(model.title));\n};\n","import {Api} from \"@automatons/parser\";\nimport {render} from \"./render\";\nimport {baseFilename, baseName, endpointParams, referencedModels, responseType} from \"./meta\";\n\n/**\n * Emit services/<tag>.service.ts: an abstract class declaring the contract the user must implement.\n * Regenerating this file never touches the user's concrete implementation.\n */\nexport const emitService = (api: Api): string =>\n render((sf) => {\n const models = referencedModels(api);\n if (models.length) {\n sf.addImportDeclaration({\n isTypeOnly: true,\n namedImports: models.map((model) => model.title),\n moduleSpecifier: \"../dto\",\n });\n }\n\n const serviceClass = sf.addClass({isExported: true, isAbstract: true, name: `${baseName(api)}Service`});\n api.paths.forEach((path) =>\n serviceClass.addMethod({\n isAbstract: true,\n name: path.name,\n parameters: endpointParams(path).map((param) => ({\n name: param.id,\n type: param.type,\n hasQuestionToken: param.optional,\n })),\n returnType: `Promise<${responseType(path)}>`,\n }),\n );\n });\n\n/**\n * Emit services/index.ts re-exporting every service.\n */\nexport const emitServicesIndex = (apis: Api[]): string =>\n render((sf) => apis.forEach((api) => sf.addExportDeclaration({moduleSpecifier: `./${baseFilename(api)}.service`})));\n","import {Api} from \"@automatons/parser\";\nimport {OptionalKind, DecoratorStructure, Scope} from \"ts-morph\";\nimport {render} from \"./render\";\nimport {\n baseFilename,\n baseName,\n endpointParams,\n methodDecorator,\n referencedModels,\n responseType,\n routePath,\n} from \"./meta\";\n\nconst paramDecorator = (decorator: {name: string; argument?: string}): OptionalKind<DecoratorStructure> => ({\n name: decorator.name,\n arguments: decorator.argument ? [decorator.argument] : [],\n});\n\n/**\n * Emit controllers/<tag>.controller.ts: a @Controller that wires the routes and delegates\n * each operation to the injected abstract service.\n */\nexport const emitController = (api: Api): string =>\n render((sf) => {\n const base = baseName(api);\n\n const nestImports = new Set<string>([\"Controller\"]);\n api.paths.forEach((path) => {\n nestImports.add(methodDecorator(path.method));\n endpointParams(path).forEach((param) => nestImports.add(param.decorator.name));\n });\n sf.addImportDeclaration({namedImports: [...nestImports].sort(), moduleSpecifier: \"@nestjs/common\"});\n\n sf.addImportDeclaration({namedImports: [`${base}Service`], moduleSpecifier: \"../services\"});\n\n const models = referencedModels(api);\n if (models.length) {\n sf.addImportDeclaration({namedImports: models.map((model) => model.title), moduleSpecifier: \"../dto\"});\n }\n\n const controllerClass = sf.addClass({\n isExported: true,\n name: `${base}Controller`,\n decorators: [{name: \"Controller\", arguments: []}],\n });\n controllerClass.addConstructor({\n parameters: [{name: \"service\", type: `${base}Service`, scope: Scope.Private, isReadonly: true}],\n });\n\n api.paths.forEach((path) => {\n const params = endpointParams(path);\n controllerClass.addMethod({\n name: path.name,\n decorators: [{name: methodDecorator(path.method), arguments: [`\"${routePath(path.path)}\"`]}],\n parameters: params.map((param) => ({\n name: param.id,\n type: param.type,\n hasQuestionToken: param.optional,\n decorators: [paramDecorator(param.decorator)],\n })),\n returnType: `Promise<${responseType(path)}>`,\n statements: [`return this.service.${path.name}(${params.map((param) => param.id).join(\", \")});`],\n });\n });\n });\n\n/**\n * Emit controllers/index.ts re-exporting every controller.\n */\nexport const emitControllersIndex = (apis: Api[]): string =>\n render((sf) => apis.forEach((api) => sf.addExportDeclaration({moduleSpecifier: `./${baseFilename(api)}.controller`})));\n","import {Automaton} from \"@automatons/tools\";\nimport {generate} from \"./generator\";\n\nconst generatorTypescriptNestjsServer: Automaton = (openapi, settings) =>\n generate(openapi, settings);\n\nexport default generatorTypescriptNestjsServer;\n"],"mappings":"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","names":["path","path","path"]}
package/package.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "@automatons/typescript-server-nestjs",
3
+ "version": "1.0.0",
4
+ "repository": "https://github.com/openapi-automatons/typescript-server-nestjs.git",
5
+ "author": "tanmen <yt.prog@gmail.com>",
6
+ "license": "MIT",
7
+ "packageManager": "yarn@1.22.22",
8
+ "type": "module",
9
+ "keywords": [
10
+ "openapi",
11
+ "openapi-automatons",
12
+ "automatons",
13
+ "nestjs",
14
+ "server"
15
+ ],
16
+ "main": "index.js",
17
+ "module": "index.js",
18
+ "types": "index.d.ts",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./index.d.ts",
22
+ "import": "./index.js"
23
+ }
24
+ },
25
+ "engines": {
26
+ "node": ">=22"
27
+ },
28
+ "scripts": {
29
+ "prebuild": "depcheck",
30
+ "build": "tsup && cp package.json README.md LICENSE dist",
31
+ "lint": "eslint src",
32
+ "test": "vitest run",
33
+ "test:watch": "vitest",
34
+ "test:coverage": "vitest run --coverage",
35
+ "prepare": "husky"
36
+ },
37
+ "dependencies": {
38
+ "@automatons/parser": "^1.2.0",
39
+ "@automatons/tools": "^2.2.0",
40
+ "prettier": "^3.8.3",
41
+ "ts-morph": "^28.0.0"
42
+ },
43
+ "peerDependencies": {
44
+ "@nestjs/common": "^10 || ^11",
45
+ "class-transformer": "^0.5",
46
+ "class-validator": "^0.14",
47
+ "openapi-automatons": "^2"
48
+ },
49
+ "devDependencies": {
50
+ "@commitlint/cli": "^21.0.2",
51
+ "@commitlint/config-conventional": "^21.0.2",
52
+ "@eslint/js": "^10.0.1",
53
+ "@nestjs/common": "^11.0.0",
54
+ "@semantic-release/changelog": "^6.0.3",
55
+ "@semantic-release/git": "^10.0.1",
56
+ "@types/node": "^25.9.2",
57
+ "@vitest/coverage-v8": "^3.2.0",
58
+ "class-transformer": "^0.5.1",
59
+ "class-validator": "^0.15.1",
60
+ "depcheck": "^1.4.7",
61
+ "eslint": "^10.4.1",
62
+ "eslint-config-prettier": "^10.1.8",
63
+ "husky": "^9.1.7",
64
+ "lint-staged": "^17.0.7",
65
+ "reflect-metadata": "^0.2.0",
66
+ "rxjs": "^7.8.0",
67
+ "semantic-release": "^25.0.3",
68
+ "tsup": "^8.5.0",
69
+ "typescript": "^6.0.3",
70
+ "typescript-eslint": "^8.46.0",
71
+ "vitest": "^3.2.0"
72
+ },
73
+ "publishConfig": {
74
+ "access": "public"
75
+ }
76
+ }