@omer-x/next-openapi-scaffold-generator 0.2.0 → 0.4.0-alpha.1
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/bin/index.js +149 -97
- package/package.json +15 -12
package/bin/index.js
CHANGED
|
@@ -1,12 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import prompts from "prompts";
|
|
5
|
+
|
|
1
6
|
// src/core/file.ts
|
|
2
|
-
import fs from "
|
|
3
|
-
import path from "
|
|
7
|
+
import fs from "fs/promises";
|
|
8
|
+
import path from "path";
|
|
4
9
|
async function saveFile(directory, fileName, content) {
|
|
5
10
|
const dirPath = path.resolve(process.cwd(), directory);
|
|
6
11
|
await fs.mkdir(dirPath, { recursive: true });
|
|
7
12
|
const filePath = path.resolve(dirPath, fileName);
|
|
8
13
|
await fs.writeFile(filePath, content, "utf8");
|
|
9
14
|
}
|
|
15
|
+
async function appendFile(directory, fileName, content) {
|
|
16
|
+
const dirPath = path.resolve(process.cwd(), directory);
|
|
17
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
18
|
+
const filePath = path.resolve(dirPath, fileName);
|
|
19
|
+
await fs.appendFile(filePath, content + "\n", "utf8");
|
|
20
|
+
}
|
|
21
|
+
async function checkFile(directory, fileName, content) {
|
|
22
|
+
const dirPath = path.resolve(process.cwd(), directory);
|
|
23
|
+
const filePath = path.resolve(dirPath, fileName);
|
|
24
|
+
try {
|
|
25
|
+
const fileContent = await fs.readFile(filePath, "utf8");
|
|
26
|
+
return fileContent.includes(content);
|
|
27
|
+
} catch (err) {
|
|
28
|
+
if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
throw err;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
10
34
|
|
|
11
35
|
// src/core/string.ts
|
|
12
36
|
import * as changeCase from "change-case";
|
|
@@ -42,28 +66,22 @@ function capitalCase2(text, plural, onlyFirst) {
|
|
|
42
66
|
}
|
|
43
67
|
return changeCase.capitalCase(properText);
|
|
44
68
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
import * as readline from "node:readline/promises";
|
|
49
|
-
var rl = readline.createInterface({ input, output });
|
|
50
|
-
async function askQuestions(queries) {
|
|
51
|
-
const answers = await Promise.all(queries.map((query) => rl.question(query)));
|
|
52
|
-
rl.close();
|
|
53
|
-
return answers;
|
|
69
|
+
function constantCase2(text, plural) {
|
|
70
|
+
const properText = plural ? pluralize(text) : pluralize.singular(text);
|
|
71
|
+
return changeCase.constantCase(properText);
|
|
54
72
|
}
|
|
55
73
|
|
|
56
74
|
// src/core/template.ts
|
|
57
75
|
import Handlebars from "handlebars";
|
|
58
|
-
function getTemplate(
|
|
59
|
-
return Handlebars.compile(
|
|
76
|
+
function getTemplate(input) {
|
|
77
|
+
return Handlebars.compile(input);
|
|
60
78
|
}
|
|
61
79
|
|
|
62
|
-
//
|
|
63
|
-
var api_route_default = 'import defineRoute from "@omer-x/next-openapi-route-handler";\nimport z from "zod";\nimport db from "~/database";\nimport { New{{ pascalCaseSingular }}DTO, {{ pascalCaseSingular }}DTO } from "~/models/{{ kebabCaseSingular }}";\nimport create{{ pascalCaseSingular }} from "~/operations/create{{ pascalCaseSingular }}";\nimport get{{ pascalCasePlural }} from "~/operations/get{{ pascalCasePlural }}";\n\n{{{ readAllOperation }}}\n\n{{{ createOperation }}}\n';
|
|
80
|
+
// _ztxb3livn:/home/runner/work/next-openapi-scaffold-generator/next-openapi-scaffold-generator/src/templates/api-route.hbs
|
|
81
|
+
var api_route_default = 'import defineRoute from "@omer-x/next-openapi-route-handler";\nimport { count } from "drizzle-orm";\nimport z from "zod";\nimport db from "~/database";\nimport { {{ camelCasePlural }} } from "~/database/schema/{{ kebabCasePlural }}";\nimport { New{{ pascalCaseSingular }}DTO, {{ pascalCaseSingular }}DTO } from "~/models/{{ kebabCaseSingular }}";\nimport create{{ pascalCaseSingular }} from "~/operations/create{{ pascalCaseSingular }}";\nimport get{{ pascalCasePlural }} from "~/operations/get{{ pascalCasePlural }}";\n\n{{{ readAllOperation }}}\n\n{{{ createOperation }}}\n';
|
|
64
82
|
|
|
65
|
-
//
|
|
66
|
-
var create_default = 'export const { POST } = defineRoute({\n operationId: "create{{ pascalCaseSingular }}",\n method: "POST",\n summary: "Create {{ noCaseSingular }}",\n description: "Create a new {{ noCaseSingular }}",\n tags: ["{{ capitalCasePlural }}"],\n requestBody: New{{ pascalCaseSingular }}DTO,\n action: async ({ body }) => {\n const {{ camelCaseSingular }} = await create{{ pascalCaseSingular }}(db, body);\n return Response.json({{ camelCaseSingular }}, { status: 201 });\n },\n responses: {\n 201: { description: "{{ onlyFirstCapitalCaseSingular }} created successfully", content: {{ pascalCaseSingular }}DTO },\n },\n});\n';
|
|
83
|
+
// _ztxb3livn:/home/runner/work/next-openapi-scaffold-generator/next-openapi-scaffold-generator/src/templates/routes/create.hbs
|
|
84
|
+
var create_default = 'export const { POST } = defineRoute({\n operationId: "create{{ pascalCaseSingular }}",\n method: "POST",\n summary: "Create {{ noCaseSingular }}",\n description: "Create a new {{ noCaseSingular }}",\n tags: ["{{ capitalCasePlural }}"],\n requestBody: New{{ pascalCaseSingular }}DTO,\n action: async ({ body }) => {\n const {{ camelCaseSingular }} = await create{{ pascalCaseSingular }}(db, body);\n return Response.json({{ camelCaseSingular }}, { status: 201 });\n },\n handleErrors(errorType, issues) {\n switch (errorType) {\n case "PARSE_REQUEST_BODY":\n return Response.json({ errorCode: "BAD_REQUEST" }, { status: 400 });\n default:\n // eslint-disable-next-line no-console\n console.log(`---------------[${errorType}]---------------`);\n // eslint-disable-next-line no-console\n console.log(issues);\n // eslint-disable-next-line no-console\n console.log("".padEnd(errorType.length + 32, "-"));\n return Response.json({ errorCode: "UNKNOWN_ERROR" }, { status: 500 });\n }\n },\n responses: {\n 201: { description: "{{ onlyFirstCapitalCaseSingular }} created successfully", content: {{ pascalCaseSingular }}DTO },\n 400: { description: "Bad Request", content: z.object({ errorCode: z.literal(["BAD_REQUEST"]) }) },\n 500: { description: "Internal Server Error", content: z.object({ errorCode: z.literal(["UNKNOWN_ERROR"]) }) },\n },\n});\n';
|
|
67
85
|
|
|
68
86
|
// src/templates/routes/create.ts
|
|
69
87
|
function generateCreateOperationRoute(modelName2) {
|
|
@@ -77,13 +95,14 @@ function generateCreateOperationRoute(modelName2) {
|
|
|
77
95
|
});
|
|
78
96
|
}
|
|
79
97
|
|
|
80
|
-
//
|
|
81
|
-
var read_all_default = 'export const { GET } = defineRoute({\n operationId: "get{{ pascalCasePlural }}",\n method: "GET",\n summary: "Get all {{ noCasePlural }}",\n description: "Retrieve a list of {{ noCasePlural }}",\n tags: ["{{ capitalCasePlural }}"],\n queryParams: z.object({\n select: {{ pascalCaseSingular }}DTO.keyof().array().default([])\n .describe("List of the column names"),\n }),\n action: async ({ queryParams }) => {\n
|
|
98
|
+
// _ztxb3livn:/home/runner/work/next-openapi-scaffold-generator/next-openapi-scaffold-generator/src/templates/routes/read-all.hbs
|
|
99
|
+
var read_all_default = 'export const { GET } = defineRoute({\n operationId: "get{{ pascalCasePlural }}",\n method: "GET",\n summary: "Get all {{ noCasePlural }}",\n description: "Retrieve a list of {{ noCasePlural }}",\n tags: ["{{ capitalCasePlural }}"],\n queryParams: z.object({\n offset: z.int32().min(0),\n limit: z.int32().min(0),\n select: {{ pascalCaseSingular }}DTO.keyof().array().default([])\n .describe("List of the column names"),\n }),\n action: async ({ queryParams }) => {\n const [\n [total],\n items,\n ] = await Promise.all([\n db.select({ count: count() }).from({{ camelCasePlural }}),\n get{{ pascalCasePlural }}(db, queryParams),\n ]);\n return Response.json({\n items,\n pagination: {\n offset: queryParams.offset,\n limit: queryParams.limit,\n total: total.count,\n },\n });\n },\n handleErrors(errorType, issues) {\n switch (errorType) {\n case "PARSE_SEARCH_PARAMS":\n return Response.json({ errorCode: "BAD_REQUEST" }, { status: 400 });\n default:\n // eslint-disable-next-line no-console\n console.log(`---------------[${errorType}]---------------`);\n // eslint-disable-next-line no-console\n console.log(issues);\n // eslint-disable-next-line no-console\n console.log("".padEnd(errorType.length + 32, "-"));\n return Response.json({ errorCode: "UNKNOWN_ERROR" }, { status: 500 });\n }\n },\n responses: {\n 200: {\n description: "Returns a list of {{ noCasePlural }}",\n content: z.object({\n items: {{ pascalCaseSingular }}DTO.array(),\n pagination: z.object({\n offset: z.int32().min(0),\n limit: z.int32().min(0),\n total: z.int(),\n }),\n }),\n },\n 400: { description: "Bad Request", content: z.object({ errorCode: z.literal(["BAD_REQUEST"]) }) },\n 500: { description: "Internal Server Error", content: z.object({ errorCode: z.literal(["UNKNOWN_ERROR"]) }) },\n },\n});\n';
|
|
82
100
|
|
|
83
101
|
// src/templates/routes/read-all.ts
|
|
84
102
|
function generateReadAllOperationRoute(modelName2) {
|
|
85
103
|
const template = getTemplate(read_all_default);
|
|
86
104
|
return template({
|
|
105
|
+
camelCasePlural: camelCase2(modelName2, true),
|
|
87
106
|
capitalCasePlural: capitalCase2(modelName2, true, false),
|
|
88
107
|
noCasePlural: noCase2(modelName2, true),
|
|
89
108
|
pascalCaseSingular: pascalCase2(modelName2, false),
|
|
@@ -95,6 +114,7 @@ function generateReadAllOperationRoute(modelName2) {
|
|
|
95
114
|
function generateApiRoute(modelName2) {
|
|
96
115
|
const template = getTemplate(api_route_default);
|
|
97
116
|
return template({
|
|
117
|
+
camelCasePlural: camelCase2(modelName2, true),
|
|
98
118
|
kebabCasePlural: kebabCase2(modelName2, true),
|
|
99
119
|
kebabCaseSingular: kebabCase2(modelName2, false),
|
|
100
120
|
pascalCasePlural: pascalCase2(modelName2, true),
|
|
@@ -104,11 +124,11 @@ function generateApiRoute(modelName2) {
|
|
|
104
124
|
});
|
|
105
125
|
}
|
|
106
126
|
|
|
107
|
-
//
|
|
108
|
-
var api_route_with_id_default = 'import defineRoute from "@omer-x/next-openapi-route-handler";\nimport db from "~/database";\nimport { {{ pascalCaseSingular }}DTO, {{ pascalCaseSingular }}PatchDTO } from "~/models/{{ kebabCaseSingular }}";\nimport delete{{ pascalCaseSingular }} from "~/operations/delete{{ pascalCaseSingular }}";\nimport get{{ pascalCaseSingular }} from "~/operations/get{{ pascalCaseSingular }}";\nimport update{{ pascalCaseSingular }} from "~/operations/update{{ pascalCaseSingular }}";\n\n{{{ readOperation }}}\n\n{{{ updateOperation }}}\n\n{{{ deleteOperation }}}\n';
|
|
127
|
+
// _ztxb3livn:/home/runner/work/next-openapi-scaffold-generator/next-openapi-scaffold-generator/src/templates/api-route-with-id.hbs
|
|
128
|
+
var api_route_with_id_default = 'import defineRoute from "@omer-x/next-openapi-route-handler";\nimport z from "zod";\nimport db from "~/database";\nimport { {{ pascalCaseSingular }}DTO, {{ pascalCaseSingular }}PatchDTO } from "~/models/{{ kebabCaseSingular }}";\nimport delete{{ pascalCaseSingular }} from "~/operations/delete{{ pascalCaseSingular }}";\nimport get{{ pascalCaseSingular }} from "~/operations/get{{ pascalCaseSingular }}";\nimport update{{ pascalCaseSingular }} from "~/operations/update{{ pascalCaseSingular }}";\n\n{{{ readOperation }}}\n\n{{{ updateOperation }}}\n\n{{{ deleteOperation }}}\n';
|
|
109
129
|
|
|
110
|
-
//
|
|
111
|
-
var delete_default = 'export const { DELETE } = defineRoute({\n operationId: "delete{{ pascalCaseSingular }}",\n method: "DELETE",\n summary: "Delete {{ noCaseSingular }}",\n description: "Delete a specific {{ noCaseSingular }} by ID",\n tags: ["{{ capitalCasePlural }}"],\n pathParams: {{ pascalCaseSingular }}DTO.pick({ id: true }),\n action: async ({ pathParams }) => {\n const {{ camelCaseSingular }} = await delete{{ pascalCaseSingular }}(db, pathParams.id);\n if ({{ camelCaseSingular }}?.id === pathParams.id) {\n return new Response(null, { status: 204 });\n }\n return
|
|
130
|
+
// _ztxb3livn:/home/runner/work/next-openapi-scaffold-generator/next-openapi-scaffold-generator/src/templates/routes/delete.hbs
|
|
131
|
+
var delete_default = 'export const { DELETE } = defineRoute({\n operationId: "delete{{ pascalCaseSingular }}",\n method: "DELETE",\n summary: "Delete {{ noCaseSingular }}",\n description: "Delete a specific {{ noCaseSingular }} by ID",\n tags: ["{{ capitalCasePlural }}"],\n pathParams: {{ pascalCaseSingular }}DTO.pick({ id: true }),\n action: async ({ pathParams }) => {\n const {{ camelCaseSingular }} = await delete{{ pascalCaseSingular }}(db, pathParams.id);\n if ({{ camelCaseSingular }}?.id === pathParams.id) {\n return new Response(null, { status: 204 });\n }\n return Response.json({ errorCode: "{{ constantCaseSingular }}_NOT_FOUND" }, { status: 404 });\n },\n handleErrors(errorType, issues) {\n switch (errorType) {\n case "PARSE_PATH_PARAMS":\n return Response.json({ errorCode: "{{ constantCaseSingular }}_NOT_FOUND" }, { status: 404 });\n default:\n // eslint-disable-next-line no-console\n console.log(`---------------[${errorType}]---------------`);\n // eslint-disable-next-line no-console\n console.log(issues);\n // eslint-disable-next-line no-console\n console.log("".padEnd(errorType.length + 32, "-"));\n return Response.json({ errorCode: "UNKNOWN_ERROR" }, { status: 500 });\n }\n },\n responses: {\n 204: { description: "{{ onlyFirstCapitalCaseSingular }} deleted successfully" },\n 404: { description: "{{ onlyFirstCapitalCaseSingular }} not found", content: z.object({ errorCode: z.literal(["{{ constantCaseSingular }}_NOT_FOUND"]) }) },\n 500: { description: "Internal Server Error", content: z.object({ errorCode: z.literal(["UNKNOWN_ERROR"]) }) },\n },\n});\n';
|
|
112
132
|
|
|
113
133
|
// src/templates/routes/delete.ts
|
|
114
134
|
function generateDeleteOperationRoute(modelName2) {
|
|
@@ -116,14 +136,15 @@ function generateDeleteOperationRoute(modelName2) {
|
|
|
116
136
|
return template({
|
|
117
137
|
camelCaseSingular: camelCase2(modelName2, false),
|
|
118
138
|
capitalCasePlural: capitalCase2(modelName2, true, false),
|
|
139
|
+
constantCaseSingular: constantCase2(modelName2, false),
|
|
119
140
|
noCaseSingular: noCase2(modelName2, false),
|
|
120
141
|
onlyFirstCapitalCaseSingular: capitalCase2(modelName2, false, true),
|
|
121
142
|
pascalCaseSingular: pascalCase2(modelName2, false)
|
|
122
143
|
});
|
|
123
144
|
}
|
|
124
145
|
|
|
125
|
-
//
|
|
126
|
-
var read_default = 'export const { GET } = defineRoute({\n operationId: "get{{ pascalCaseSingular }}",\n method: "GET",\n summary: "Get {{ noCaseSingular }}",\n description: "Get a specific {{ noCaseSingular }} by ID",\n tags: ["{{ capitalCasePlural }}"],\n pathParams: {{ pascalCaseSingular }}DTO.pick({ id: true }),\n action: async ({ pathParams }) => {\n const {{ camelCaseSingular }} = await get{{ pascalCaseSingular }}(db, pathParams.id);\n if ({{ camelCaseSingular }}) {\n return Response.json({{ camelCaseSingular }});\n }\n return
|
|
146
|
+
// _ztxb3livn:/home/runner/work/next-openapi-scaffold-generator/next-openapi-scaffold-generator/src/templates/routes/read.hbs
|
|
147
|
+
var read_default = 'export const { GET } = defineRoute({\n operationId: "get{{ pascalCaseSingular }}",\n method: "GET",\n summary: "Get {{ noCaseSingular }}",\n description: "Get a specific {{ noCaseSingular }} by ID",\n tags: ["{{ capitalCasePlural }}"],\n pathParams: {{ pascalCaseSingular }}DTO.pick({ id: true }),\n action: async ({ pathParams }) => {\n const {{ camelCaseSingular }} = await get{{ pascalCaseSingular }}(db, pathParams.id);\n if ({{ camelCaseSingular }}) {\n return Response.json({{ camelCaseSingular }});\n }\n return Response.json({ errorCode: "{{ constantCaseSingular }}_NOT_FOUND" }, { status: 404 });\n },\n handleErrors(errorType, issues) {\n switch (errorType) {\n case "PARSE_PATH_PARAMS":\n return Response.json({ errorCode: "{{ constantCaseSingular }}_NOT_FOUND" }, { status: 404 });\n default:\n // eslint-disable-next-line no-console\n console.log(`---------------[${errorType}]---------------`);\n // eslint-disable-next-line no-console\n console.log(issues);\n // eslint-disable-next-line no-console\n console.log("".padEnd(errorType.length + 32, "-"));\n return Response.json({ errorCode: "UNKNOWN_ERROR" }, { status: 500 });\n }\n },\n responses: {\n 200: { description: "{{ onlyFirstCapitalCaseSingular }} found", content: {{ pascalCaseSingular }}DTO },\n 404: { description: "{{ onlyFirstCapitalCaseSingular }} not found", content: z.object({ errorCode: z.literal(["{{ constantCaseSingular }}_NOT_FOUND"]) }) },\n 500: { description: "Internal Server Error", content: z.object({ errorCode: z.literal(["UNKNOWN_ERROR"]) }) },\n },\n});\n';
|
|
127
148
|
|
|
128
149
|
// src/templates/routes/read.ts
|
|
129
150
|
function generateReadOperationRoute(modelName2) {
|
|
@@ -131,14 +152,15 @@ function generateReadOperationRoute(modelName2) {
|
|
|
131
152
|
return template({
|
|
132
153
|
camelCaseSingular: camelCase2(modelName2, false),
|
|
133
154
|
capitalCasePlural: capitalCase2(modelName2, true, false),
|
|
155
|
+
constantCaseSingular: constantCase2(modelName2, false),
|
|
134
156
|
noCaseSingular: noCase2(modelName2, false),
|
|
135
157
|
onlyFirstCapitalCaseSingular: capitalCase2(modelName2, false, true),
|
|
136
158
|
pascalCaseSingular: pascalCase2(modelName2, false)
|
|
137
159
|
});
|
|
138
160
|
}
|
|
139
161
|
|
|
140
|
-
//
|
|
141
|
-
var update_default = 'export const { PATCH } = defineRoute({\n operationId: "update{{ pascalCaseSingular }}",\n method: "PATCH",\n summary: "Update {{ noCaseSingular }}",\n description: "Update a specific {{ noCaseSingular }} by ID",\n tags: ["{{ capitalCasePlural }}"],\n pathParams: {{ pascalCaseSingular }}DTO.pick({ id: true }),\n requestBody: {{ pascalCaseSingular }}PatchDTO,\n action: async ({ pathParams, body }) => {\n const {{ camelCaseSingular }} = await update{{ pascalCaseSingular }}(db, pathParams.id, body);\n if ({{ camelCaseSingular }}
|
|
162
|
+
// _ztxb3livn:/home/runner/work/next-openapi-scaffold-generator/next-openapi-scaffold-generator/src/templates/routes/update.hbs
|
|
163
|
+
var update_default = 'export const { PATCH } = defineRoute({\n operationId: "update{{ pascalCaseSingular }}",\n method: "PATCH",\n summary: "Update {{ noCaseSingular }}",\n description: "Update a specific {{ noCaseSingular }} by ID",\n tags: ["{{ capitalCasePlural }}"],\n pathParams: {{ pascalCaseSingular }}DTO.pick({ id: true }),\n requestBody: {{ pascalCaseSingular }}PatchDTO,\n action: async ({ pathParams, body }) => {\n const {{ camelCaseSingular }} = await update{{ pascalCaseSingular }}(db, pathParams.id, body);\n if ({{ camelCaseSingular }}.id === pathParams.id) {\n return Response.json({{ camelCaseSingular }});\n }\n return Response.json({ errorCode: "{{ constantCaseSingular }}_NOT_FOUND" }, { status: 404 });\n },\n handleErrors(errorType, issues) {\n switch (errorType) {\n case "PARSE_REQUEST_BODY":\n return Response.json({ errorCode: "BAD_REQUEST" }, { status: 400 });\n case "PARSE_PATH_PARAMS":\n return Response.json({ errorCode: "{{ constantCaseSingular }}_NOT_FOUND" }, { status: 404 });\n default:\n // eslint-disable-next-line no-console\n console.log(`---------------[${errorType}]---------------`);\n // eslint-disable-next-line no-console\n console.log(issues);\n // eslint-disable-next-line no-console\n console.log("".padEnd(errorType.length + 32, "-"));\n return Response.json({ errorCode: "UNKNOWN_ERROR" }, { status: 500 });\n }\n },\n responses: {\n 200: { description: "{{ onlyFirstCapitalCaseSingular }} updated successfully", content: {{ pascalCaseSingular }}DTO },\n 400: { description: "Bad Request", content: z.object({ errorCode: z.literal(["BAD_REQUEST"]) }) },\n 404: { description: "{{ onlyFirstCapitalCaseSingular }} not found", content: z.object({ errorCode: z.literal(["{{ constantCaseSingular }}_NOT_FOUND"]) }) },\n 500: { description: "Internal Server Error", content: z.object({ errorCode: z.literal(["UNKNOWN_ERROR"]) }) },\n },\n});\n';
|
|
142
164
|
|
|
143
165
|
// src/templates/routes/update.ts
|
|
144
166
|
function generateUpdateOperationRoute(modelName2) {
|
|
@@ -146,6 +168,7 @@ function generateUpdateOperationRoute(modelName2) {
|
|
|
146
168
|
return template({
|
|
147
169
|
camelCaseSingular: camelCase2(modelName2, false),
|
|
148
170
|
capitalCasePlural: capitalCase2(modelName2, true, false),
|
|
171
|
+
constantCaseSingular: constantCase2(modelName2, false),
|
|
149
172
|
noCaseSingular: noCase2(modelName2, false),
|
|
150
173
|
onlyFirstCapitalCaseSingular: capitalCase2(modelName2, false, true),
|
|
151
174
|
pascalCaseSingular: pascalCase2(modelName2, false)
|
|
@@ -164,135 +187,131 @@ function generateApiRouteWithId(modelName2) {
|
|
|
164
187
|
});
|
|
165
188
|
}
|
|
166
189
|
|
|
167
|
-
//
|
|
168
|
-
var model_default = 'import { createInsertSchema } from "drizzle-zod";\nimport { {{ camelCasePlural }} } from "~/database/schema";\nimport type z from "zod";\n\nconst
|
|
190
|
+
// _ztxb3livn:/home/runner/work/next-openapi-scaffold-generator/next-openapi-scaffold-generator/src/templates/model.hbs
|
|
191
|
+
var model_default = 'import { createInsertSchema, createSelectSchema, createUpdateSchema } from "drizzle-zod";\nimport { createSharedRefinements } from "drizzle-zod-shared-refinements";\nimport { {{ camelCasePlural }} } from "~/database/schema/{{ kebabCasePlural }}";\nimport type z from "zod";\n\nconst sharedRefinements = createSharedRefinements({{ camelCasePlural }}, {\n id: schema => schema.readonly().describe("Unique identifier of the {{ noCaseSingular }}"),\n // describe other fields here\n createdAt: schema => schema.readonly().describe("Creation date of the {{ noCaseSingular }} as an ISO 8601 date string"),\n updatedAt: schema => schema.readonly().describe("Modification date of the {{ noCaseSingular }} as an ISO 8601 date string"),\n});\n\nexport const {{ pascalCaseSingular }}DTO = createSelectSchema({{ camelCasePlural }}, sharedRefinements).describe("Represents a {{ noCaseSingular }} definition");\n\nexport const New{{ pascalCaseSingular }}DTO = createInsertSchema({{ camelCasePlural }}, sharedRefinements).omit({\n id: true,\n createdAt: true,\n updatedAt: true,\n}).describe("Data Transfer Object for creating a new {{ noCaseSingular }}");\n\nexport const {{ pascalCaseSingular }}PatchDTO = createUpdateSchema({{ camelCasePlural }}, sharedRefinements).omit({\n id: true,\n createdAt: true,\n updatedAt: true,\n}).describe("Data Transfer Object for updating an existing {{ noCaseSingular }}");\n\nexport type {{ pascalCaseSingular }} = z.infer<typeof {{ pascalCaseSingular }}DTO>;\nexport type New{{ pascalCaseSingular }} = z.infer<typeof New{{ pascalCaseSingular }}DTO>;\nexport type {{ pascalCaseSingular }}Patch = z.infer<typeof {{ pascalCaseSingular }}PatchDTO>;\n';
|
|
169
192
|
|
|
170
193
|
// src/templates/model.ts
|
|
171
194
|
function generateModel(modelName2) {
|
|
172
|
-
|
|
173
|
-
return template({
|
|
195
|
+
return getTemplate(model_default)({
|
|
174
196
|
camelCasePlural: camelCase2(modelName2, true),
|
|
197
|
+
kebabCasePlural: kebabCase2(modelName2, true),
|
|
175
198
|
noCaseSingular: noCase2(modelName2, false),
|
|
176
199
|
pascalCaseSingular: pascalCase2(modelName2, false)
|
|
177
200
|
});
|
|
178
201
|
}
|
|
179
202
|
|
|
180
|
-
//
|
|
181
|
-
var create_default2 = 'import {
|
|
203
|
+
// _ztxb3livn:/home/runner/work/next-openapi-scaffold-generator/next-openapi-scaffold-generator/src/templates/operation-tests/create.hbs
|
|
204
|
+
var create_default2 = 'import { describe, expect, it, vi } from "vitest";\nimport { zocker } from "zocker";\nimport db from "~/database";\nimport { New{{ pascalCaseSingular }}DTO } from "~/models/{{ kebabCaseSingular }}";\nimport create{{ pascalCaseSingular }} from "./create{{ pascalCaseSingular }}";\n\ndescribe("create{{ pascalCaseSingular }}", () => {\n it("should insert a {{ noCaseSingular }} into the database", async () => {\n const mockData = zocker(New{{ pascalCaseSingular }}DTO).generate();\n const spy = vi.spyOn(db.$client, "query").mockImplementation(() => Promise.resolve({ rows: [], rowCount: 0 }));\n\n await create{{ pascalCaseSingular }}(db, mockData);\n\n expect(spy).toHaveBeenCalled();\n const [query, parameters] = spy.mock.calls[0] as unknown as [{ text: string }, string[]];\n const q = /^insert into "{{ snakeCasePlural }}" (.+) values (.+) returning (.+)$/;\n expect(query.text).toMatch(q);\n expect(parameters).toEqual(Object.values(mockData));\n\n spy.mockRestore();\n });\n});\n';
|
|
182
205
|
|
|
183
206
|
// src/templates/operation-tests/create.ts
|
|
184
207
|
function generateCreateTest(modelName2) {
|
|
185
|
-
|
|
186
|
-
return template({
|
|
187
|
-
camelCaseSingular: camelCase2(modelName2, false),
|
|
208
|
+
return getTemplate(create_default2)({
|
|
188
209
|
kebabCaseSingular: kebabCase2(modelName2, false),
|
|
189
210
|
noCaseSingular: noCase2(modelName2, false),
|
|
190
|
-
pascalCaseSingular: pascalCase2(modelName2, false)
|
|
211
|
+
pascalCaseSingular: pascalCase2(modelName2, false),
|
|
212
|
+
snakeCasePlural: snakeCase2(modelName2, true)
|
|
191
213
|
});
|
|
192
214
|
}
|
|
193
215
|
|
|
194
|
-
//
|
|
195
|
-
var delete_default2 = 'import {
|
|
216
|
+
// _ztxb3livn:/home/runner/work/next-openapi-scaffold-generator/next-openapi-scaffold-generator/src/templates/operation-tests/delete.hbs
|
|
217
|
+
var delete_default2 = 'import { describe, expect, it, vi } from "vitest";\nimport { zocker } from "zocker";\nimport db from "~/database";\nimport { {{ pascalCaseSingular }}DTO } from "~/models/{{ kebabCaseSingular }}";\nimport delete{{ pascalCaseSingular }} from "./delete{{ pascalCaseSingular }}";\n\ndescribe("delete{{ pascalCaseSingular }}", () => {\n it("should delete a {{ noCaseSingular }} from the database", async () => {\n const mockData = zocker({{ pascalCaseSingular }}DTO.pick({ id: true })).generate();\n const spy = vi.spyOn(db.$client, "query").mockImplementation(() => Promise.resolve({ rows: [], rowCount: 0 }));\n\n await delete{{ pascalCaseSingular }}(db, mockData.id);\n\n const [query, parameters] = spy.mock.calls[0] as unknown as [{ text: string }, string[]];\n const q = /^delete from "{{ snakeCasePlural }}" where "{{ snakeCasePlural }}"."id" = \\$1 returning (.+)$/;\n expect(query.text).toMatch(q);\n expect(parameters).toEqual([mockData.id]);\n\n spy.mockRestore();\n });\n});\n';
|
|
196
218
|
|
|
197
219
|
// src/templates/operation-tests/delete.ts
|
|
198
220
|
function generateDeleteTest(modelName2) {
|
|
199
|
-
|
|
200
|
-
return template({
|
|
201
|
-
camelCaseSingular: camelCase2(modelName2, false),
|
|
221
|
+
return getTemplate(delete_default2)({
|
|
202
222
|
kebabCaseSingular: kebabCase2(modelName2, false),
|
|
203
223
|
noCaseSingular: noCase2(modelName2, false),
|
|
204
|
-
pascalCaseSingular: pascalCase2(modelName2, false)
|
|
224
|
+
pascalCaseSingular: pascalCase2(modelName2, false),
|
|
225
|
+
snakeCasePlural: snakeCase2(modelName2, true)
|
|
205
226
|
});
|
|
206
227
|
}
|
|
207
228
|
|
|
208
|
-
//
|
|
209
|
-
var read_default2 = 'import {
|
|
229
|
+
// _ztxb3livn:/home/runner/work/next-openapi-scaffold-generator/next-openapi-scaffold-generator/src/templates/operation-tests/read.hbs
|
|
230
|
+
var read_default2 = 'import { describe, expect, it, vi } from "vitest";\nimport { zocker } from "zocker";\nimport db from "~/database";\nimport { {{ pascalCaseSingular }}DTO } from "~/models/{{ kebabCaseSingular }}";\nimport get{{ pascalCaseSingular }} from "./get{{ pascalCaseSingular }}";\n\ndescribe("get{{ pascalCaseSingular }}", () => {\n it("should retrieve a single {{ noCaseSingular }} by id", async () => {\n const mockData = zocker({{ pascalCaseSingular }}DTO.pick({ id: true })).generate();\n const spy = vi.spyOn(db.$client, "query").mockImplementation(() => Promise.resolve({ rows: [], rowCount: 0 }));\n\n await get{{ pascalCaseSingular }}(db, mockData.id);\n\n const [query, parameters] = spy.mock.calls[0] as unknown as [{ text: string }, string[]];\n const q = /^select (.+) from "{{ snakeCasePlural }}" "{{ camelCasePlural }}" where "{{ camelCasePlural }}"\\."id" = \\$1 limit \\$2$/;\n expect(query.text).toMatch(q);\n expect(parameters).toEqual([...Object.values(mockData), 1]);\n\n spy.mockRestore();\n });\n});\n';
|
|
210
231
|
|
|
211
232
|
// src/templates/operation-tests/read.ts
|
|
212
233
|
function generateReadTest(modelName2) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
camelCaseSingular: camelCase2(modelName2, false),
|
|
234
|
+
return getTemplate(read_default2)({
|
|
235
|
+
camelCasePlural: camelCase2(modelName2, true),
|
|
216
236
|
kebabCaseSingular: kebabCase2(modelName2, false),
|
|
217
237
|
noCaseSingular: noCase2(modelName2, false),
|
|
218
|
-
pascalCaseSingular: pascalCase2(modelName2, false)
|
|
238
|
+
pascalCaseSingular: pascalCase2(modelName2, false),
|
|
239
|
+
snakeCasePlural: snakeCase2(modelName2, true)
|
|
219
240
|
});
|
|
220
241
|
}
|
|
221
242
|
|
|
222
|
-
//
|
|
223
|
-
var read_all_default2 = 'import {
|
|
243
|
+
// _ztxb3livn:/home/runner/work/next-openapi-scaffold-generator/next-openapi-scaffold-generator/src/templates/operation-tests/read-all.hbs
|
|
244
|
+
var read_all_default2 = 'import { describe, expect, it, vi } from "vitest";\nimport db from "~/database";\nimport get{{ pascalCasePlural }} from "./get{{ pascalCasePlural }}";\n\ndescribe("get{{ pascalCasePlural }}", () => {\n it("should retrieve all {{ noCasePlural }} from the database", async () => {\n const spy = vi.spyOn(db.$client, "query").mockImplementation(() => Promise.resolve({ rows: [], rowCount: 0 }));\n\n await get{{ pascalCasePlural }}(db);\n\n const [query] = spy.mock.calls[0] as unknown as [{ text: string }, string[]];\n const q = /^select (.+) from "{{ snakeCasePlural }}" "{{ camelCasePlural }}" order by "{{ camelCasePlural }}"."created_at" asc$/;\n expect(query.text).toMatch(q);\n\n spy.mockRestore();\n });\n\n it("should retrieve {{ noCasePlural }} with limit and offset", async () => {\n const spy = vi.spyOn(db.$client, "query").mockImplementation(() => Promise.resolve({ rows: [], rowCount: 0 }));\n\n await get{{ pascalCasePlural }}(db, {\n limit: 50,\n offset: 50,\n });\n\n const [query, parameters] = spy.mock.calls[0] as unknown as [{ text: string }, string[]];\n const q = /^select (.+) from "{{ snakeCasePlural }}" "{{ camelCasePlural }}" order by "{{ camelCasePlural }}"."created_at" asc limit \\$1 offset \\$2$/;\n expect(query.text).toMatch(q);\n expect(parameters).toEqual([50, 50]);\n\n spy.mockRestore();\n });\n\n it("should retrieve {{ noCasePlural }} with specific fields selected", async () => {\n const spy = vi.spyOn(db.$client, "query").mockImplementation(() => Promise.resolve({ rows: [], rowCount: 0 }));\n\n await get{{ pascalCasePlural }}(db, {\n select: ["id", "createdAt"],\n });\n\n const [query] = spy.mock.calls[0] as unknown as [{ text: string }, string[]];\n const q = /^select "id", "created_at" from "{{ snakeCasePlural }}" "{{ camelCasePlural }}" order by "{{ camelCasePlural }}"."created_at" asc$/;\n expect(query.text).toMatch(q);\n\n spy.mockRestore();\n });\n});\n';
|
|
224
245
|
|
|
225
246
|
// src/templates/operation-tests/read-all.ts
|
|
226
247
|
function generateReadAllTest(modelName2) {
|
|
227
|
-
|
|
228
|
-
|
|
248
|
+
return getTemplate(read_all_default2)({
|
|
249
|
+
camelCasePlural: camelCase2(modelName2, true),
|
|
229
250
|
kebabCaseSingular: kebabCase2(modelName2, false),
|
|
251
|
+
noCasePlural: noCase2(modelName2, true),
|
|
230
252
|
pascalCasePlural: pascalCase2(modelName2, true),
|
|
231
|
-
|
|
253
|
+
snakeCasePlural: snakeCase2(modelName2, true)
|
|
232
254
|
});
|
|
233
255
|
}
|
|
234
256
|
|
|
235
|
-
//
|
|
236
|
-
var update_default2 = 'import {
|
|
257
|
+
// _ztxb3livn:/home/runner/work/next-openapi-scaffold-generator/next-openapi-scaffold-generator/src/templates/operation-tests/update.hbs
|
|
258
|
+
var update_default2 = 'import { describe, expect, it, vi } from "vitest";\nimport { zocker } from "zocker";\nimport db from "~/database";\nimport { {{ pascalCaseSingular }}PatchDTO, {{ pascalCaseSingular }}DTO } from "~/models/{{ kebabCaseSingular }}";\nimport update{{ pascalCaseSingular }} from "./update{{ pascalCaseSingular }}";\n\ndescribe("update{{ pascalCaseSingular }}", () => {\n function generateMockPatch() {\n const zock = zocker({{ pascalCaseSingular }}PatchDTO);\n let mock: ReturnType<typeof zock.generate>;\n do {\n mock = zock.generate();\n } while (Object.values(JSON.parse(JSON.stringify(mock))).length < 1); // because .set(patch) might throw "No values to set"\n return mock;\n }\n\n it("should update a {{ noCaseSingular }} in the database", async () => {\n const mockData = zocker({{ pascalCaseSingular }}DTO.pick({ id: true })).generate();\n const mockPatch = generateMockPatch();\n const spy = vi.spyOn(db.$client, "query").mockImplementation(() => Promise.resolve({ rows: [], rowCount: 0 }));\n\n await update{{ pascalCaseSingular }}(db, mockData.id, mockPatch);\n\n expect(spy).toHaveBeenCalled();\n const [query, parameters] = spy.mock.calls[0] as unknown as [{ text: string }, string[]];\n const q = /^update "{{ snakeCasePlural }}" set (.+) where (.+) returning (.+)$/;\n expect(query.text).toMatch(q);\n expect(parameters).toEqual([\n ...Object.values(mockPatch).filter(p => typeof p !== "undefined"),\n mockData.id,\n ]);\n\n spy.mockRestore();\n });\n});\n';
|
|
237
259
|
|
|
238
260
|
// src/templates/operation-tests/update.ts
|
|
239
261
|
function generateUpdateTest(modelName2) {
|
|
240
|
-
|
|
241
|
-
return template({
|
|
242
|
-
camelCaseSingular: camelCase2(modelName2, false),
|
|
262
|
+
return getTemplate(update_default2)({
|
|
243
263
|
kebabCaseSingular: kebabCase2(modelName2, false),
|
|
244
264
|
noCaseSingular: noCase2(modelName2, false),
|
|
245
|
-
pascalCaseSingular: pascalCase2(modelName2, false)
|
|
265
|
+
pascalCaseSingular: pascalCase2(modelName2, false),
|
|
266
|
+
snakeCasePlural: snakeCase2(modelName2, true)
|
|
246
267
|
});
|
|
247
268
|
}
|
|
248
269
|
|
|
249
|
-
//
|
|
250
|
-
var create_default3 = 'import type database from "~/database";\nimport { {{ camelCasePlural }} } from "~/database/schema";\nimport type
|
|
270
|
+
// _ztxb3livn:/home/runner/work/next-openapi-scaffold-generator/next-openapi-scaffold-generator/src/templates/operations/create.hbs
|
|
271
|
+
var create_default3 = 'import type database from "~/database";\nimport { {{ camelCasePlural }} } from "~/database/schema/{{ kebabCasePlural }}";\nimport { type New{{ pascalCaseSingular }} } from "~/models/{{ kebabCaseSingular }}";\n\nexport default async function create{{ pascalCaseSingular }}(db: Omit<typeof database, "$client">, data: New{{ pascalCaseSingular }}) {\n const [{{ camelCaseSingular }}] = await db.insert({{ camelCasePlural }}).values(data).returning({ id: {{ camelCasePlural }}.id });\n return {{ camelCaseSingular }};\n}\n';
|
|
251
272
|
|
|
252
273
|
// src/templates/operations/create.ts
|
|
253
274
|
function generateCreateOperation(modelName2) {
|
|
254
|
-
|
|
255
|
-
return template({
|
|
275
|
+
return getTemplate(create_default3)({
|
|
256
276
|
camelCasePlural: camelCase2(modelName2, true),
|
|
257
277
|
camelCaseSingular: camelCase2(modelName2, false),
|
|
278
|
+
kebabCasePlural: kebabCase2(modelName2, true),
|
|
258
279
|
kebabCaseSingular: kebabCase2(modelName2, false),
|
|
259
280
|
pascalCaseSingular: pascalCase2(modelName2, false)
|
|
260
281
|
});
|
|
261
282
|
}
|
|
262
283
|
|
|
263
|
-
//
|
|
264
|
-
var delete_default3 = 'import { eq } from "drizzle-orm";\nimport type database from "~/database";\nimport { {{ camelCasePlural }} } from "~/database/schema";\n\nexport default async function delete{{ pascalCaseSingular }}(db: typeof database, {{ camelCaseSingular }}Id: string) {\n const results = await db.delete({{ camelCasePlural }}).where(eq({{ camelCasePlural }}.id, {{ camelCaseSingular }}Id)).returning({\n id: {{ camelCasePlural }}.id,\n });\n return results.shift();\n}\n';
|
|
284
|
+
// _ztxb3livn:/home/runner/work/next-openapi-scaffold-generator/next-openapi-scaffold-generator/src/templates/operations/delete.hbs
|
|
285
|
+
var delete_default3 = 'import { eq } from "drizzle-orm";\nimport type database from "~/database";\nimport { {{ camelCasePlural }} } from "~/database/schema/{{ kebabCasePlural }}";\n\nexport default async function delete{{ pascalCaseSingular }}(db: Omit<typeof database, "$client">, {{ camelCaseSingular }}Id: string) {\n const results = await db.delete({{ camelCasePlural }}).where(eq({{ camelCasePlural }}.id, {{ camelCaseSingular }}Id)).returning({\n id: {{ camelCasePlural }}.id,\n });\n return results.shift();\n}\n';
|
|
265
286
|
|
|
266
287
|
// src/templates/operations/delete.ts
|
|
267
288
|
function generateDeleteOperation(modelName2) {
|
|
268
|
-
|
|
269
|
-
return template({
|
|
289
|
+
return getTemplate(delete_default3)({
|
|
270
290
|
camelCasePlural: camelCase2(modelName2, true),
|
|
271
291
|
camelCaseSingular: camelCase2(modelName2, false),
|
|
292
|
+
kebabCasePlural: kebabCase2(modelName2, true),
|
|
272
293
|
pascalCaseSingular: pascalCase2(modelName2, false)
|
|
273
294
|
});
|
|
274
295
|
}
|
|
275
296
|
|
|
276
|
-
//
|
|
277
|
-
var read_default3 = 'import type database from "~/database";\n\nexport default function get{{ pascalCaseSingular }}(db: typeof database, {{ camelCaseSingular }}Id: string) {\n return db.query.{{ camelCasePlural }}.findFirst({\n where: (table, { eq }) => eq(table.id, {{ camelCaseSingular }}Id),\n });\n}\n';
|
|
297
|
+
// _ztxb3livn:/home/runner/work/next-openapi-scaffold-generator/next-openapi-scaffold-generator/src/templates/operations/read.hbs
|
|
298
|
+
var read_default3 = 'import type database from "~/database";\n\nexport default function get{{ pascalCaseSingular }}(db: Omit<typeof database, "$client">, {{ camelCaseSingular }}Id: string) {\n return db.query.{{ camelCasePlural }}.findFirst({\n where: (table, { eq }) => eq(table.id, {{ camelCaseSingular }}Id),\n });\n}\n';
|
|
278
299
|
|
|
279
300
|
// src/templates/operations/read.ts
|
|
280
301
|
function generateReadOperation(modelName2) {
|
|
281
|
-
|
|
282
|
-
return template({
|
|
302
|
+
return getTemplate(read_default3)({
|
|
283
303
|
camelCasePlural: camelCase2(modelName2, true),
|
|
284
304
|
camelCaseSingular: camelCase2(modelName2, false),
|
|
285
305
|
pascalCaseSingular: pascalCase2(modelName2, false)
|
|
286
306
|
});
|
|
287
307
|
}
|
|
288
308
|
|
|
289
|
-
//
|
|
290
|
-
var read_all_default3 = 'import
|
|
309
|
+
// _ztxb3livn:/home/runner/work/next-openapi-scaffold-generator/next-openapi-scaffold-generator/src/templates/operations/read-all.hbs
|
|
310
|
+
var read_all_default3 = 'import type database from "~/database";\nimport { type {{ pascalCaseSingular }} } from "~/models/{{ kebabCaseSingular }}";\nimport selectColumns from "~/utils/column";\n\ntype Options = {\n offset?: number,\n limit?: number,\n select?: (keyof {{ pascalCaseSingular }})[],\n};\n\nexport default function get{{ pascalCasePlural }}(db: Omit<typeof database, "$client">, options: Options = {}) {\n const { offset, limit, select } = options;\n return db.query.{{ camelCasePlural }}.findMany({\n columns: selectColumns(select ?? []),\n orderBy: (u, { asc }) => [asc(u.createdAt)],\n limit,\n offset,\n });\n}\n';
|
|
291
311
|
|
|
292
312
|
// src/templates/operations/read-all.ts
|
|
293
313
|
function generateReadAllOperation(modelName2) {
|
|
294
|
-
|
|
295
|
-
return template({
|
|
314
|
+
return getTemplate(read_all_default3)({
|
|
296
315
|
camelCasePlural: camelCase2(modelName2, true),
|
|
297
316
|
kebabCaseSingular: kebabCase2(modelName2, false),
|
|
298
317
|
pascalCasePlural: pascalCase2(modelName2, true),
|
|
@@ -300,22 +319,22 @@ function generateReadAllOperation(modelName2) {
|
|
|
300
319
|
});
|
|
301
320
|
}
|
|
302
321
|
|
|
303
|
-
//
|
|
304
|
-
var update_default3 = 'import { eq } from "drizzle-orm";\nimport type database from "~/database";\nimport { {{ camelCasePlural }} } from "~/database/schema";\nimport type {
|
|
322
|
+
// _ztxb3livn:/home/runner/work/next-openapi-scaffold-generator/next-openapi-scaffold-generator/src/templates/operations/update.hbs
|
|
323
|
+
var update_default3 = 'import { eq } from "drizzle-orm";\nimport type database from "~/database";\nimport { {{ camelCasePlural }} } from "~/database/schema/{{ kebabCasePlural }}";\nimport { type {{ pascalCaseSingular }}Patch } from "~/models/{{ kebabCaseSingular }}";\n\nexport default async function update{{ pascalCaseSingular }}(db: Omit<typeof database, "$client">, {{ camelCaseSingular }}Id: string, patch: {{ pascalCaseSingular }}Patch) {\n const [updated{{ pascalCaseSingular }}] = await db.update({{ camelCasePlural }})\n .set(patch)\n .where(eq({{ camelCasePlural }}.id, {{ camelCaseSingular }}Id))\n .returning();\n return updated{{ pascalCaseSingular }};\n}\n';
|
|
305
324
|
|
|
306
325
|
// src/templates/operations/update.ts
|
|
307
326
|
function generateUpdateOperation(modelName2) {
|
|
308
|
-
|
|
309
|
-
return template({
|
|
327
|
+
return getTemplate(update_default3)({
|
|
310
328
|
camelCasePlural: camelCase2(modelName2, true),
|
|
311
329
|
camelCaseSingular: camelCase2(modelName2, false),
|
|
330
|
+
kebabCasePlural: kebabCase2(modelName2, true),
|
|
312
331
|
kebabCaseSingular: kebabCase2(modelName2, false),
|
|
313
332
|
pascalCaseSingular: pascalCase2(modelName2, false)
|
|
314
333
|
});
|
|
315
334
|
}
|
|
316
335
|
|
|
317
|
-
//
|
|
318
|
-
var schema_default = 'import { relations, sql } from "drizzle-orm";\nimport { bigint, boolean, char, index, integer, pgTable,
|
|
336
|
+
// _ztxb3livn:/home/runner/work/next-openapi-scaffold-generator/next-openapi-scaffold-generator/src/templates/schema.hbs
|
|
337
|
+
var schema_default = 'import { relations, sql } from "drizzle-orm";\nimport { bigint, boolean, char, index, integer, pgTable, smallint, text, timestamp, uuid, varchar } from "drizzle-orm/pg-core";\n\nexport const {{ camelCasePlural }} = pgTable("{{ snakeCasePlural }}", {\n id: uuid().defaultRandom().primaryKey(),\n // ...\n exampleInteger: integer().notNull().default(2147483647),\n exampleSmallInt: smallint().notNull().default(32767),\n exampleBigInt: bigint({ mode: "number" }).notNull().default(Number.MAX_SAFE_INTEGER),\n exampleBiggerInt: bigint({ mode: "bigint" }).notNull().default(0n),\n exampleBoolean: boolean().notNull().default(false),\n exampleText: text().notNull().default("Lorem ipsum dolor sit amet"),\n exampleVarchar: varchar({ length: 16 }).notNull().default("hello world"),\n examplechar: char({ length: 16 }).notNull().default("hello world "),\n // ...\n createdAt: timestamp({ mode: "string", withTimezone: true, precision: 3 }).notNull().defaultNow(),\n updatedAt: timestamp({ mode: "string", withTimezone: true, precision: 3 }).notNull().defaultNow().$onUpdate(() => sql`now()`),\n}, table => [\n index().on(table.createdAt),\n]);\n\nexport const relationsOf{{ pascalCasePlural }} = relations({{ camelCasePlural }}, ({ many, one }) => ({\n master: one(masters, {\n fields: [{{ camelCasePlural }}.masterId],\n references: [masters.id],\n }),\n slaves: many(slaves),\n}));\n';
|
|
319
338
|
|
|
320
339
|
// src/templates/schema.ts
|
|
321
340
|
function generateSchema(modelName2) {
|
|
@@ -328,18 +347,51 @@ function generateSchema(modelName2) {
|
|
|
328
347
|
}
|
|
329
348
|
|
|
330
349
|
// src/index.ts
|
|
331
|
-
var
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
350
|
+
var { modelName } = await prompts({
|
|
351
|
+
type: "text",
|
|
352
|
+
name: "modelName",
|
|
353
|
+
message: "Enter model name"
|
|
354
|
+
});
|
|
355
|
+
var { orm } = await prompts({
|
|
356
|
+
type: "select",
|
|
357
|
+
name: "orm",
|
|
358
|
+
message: "Pick an ORM",
|
|
359
|
+
choices: [
|
|
360
|
+
{ title: "Drizzle", description: "a headless TypeScript ORM with a head. \u{1F432}", value: "drizzle" },
|
|
361
|
+
{ title: "none", value: null, disabled: true }
|
|
362
|
+
],
|
|
363
|
+
initial: 0
|
|
364
|
+
});
|
|
365
|
+
var { operationTests } = await prompts({
|
|
366
|
+
type: "toggle",
|
|
367
|
+
name: "operationTests",
|
|
368
|
+
message: "Do you want to include operation tests?",
|
|
369
|
+
initial: true,
|
|
370
|
+
active: "yes",
|
|
371
|
+
inactive: "no"
|
|
372
|
+
});
|
|
373
|
+
if (modelName) {
|
|
374
|
+
await saveFile(`src/app/${kebabCase2(modelName, true)}`, "route.ts", generateApiRoute(modelName));
|
|
375
|
+
await saveFile(`src/app/${kebabCase2(modelName, true)}/[id]`, "route.ts", generateApiRouteWithId(modelName));
|
|
376
|
+
if (orm === "drizzle") {
|
|
377
|
+
await saveFile("src/database/schema", `${kebabCase2(modelName, true)}.ts`, generateSchema(modelName));
|
|
378
|
+
const exportStatement = `export * from "./${kebabCase2(modelName, true)}";`;
|
|
379
|
+
const exported = await checkFile("src/database/schema", "index.ts", exportStatement);
|
|
380
|
+
if (!exported) {
|
|
381
|
+
await appendFile("src/database/schema", "index.ts", `export * from "./${kebabCase2(modelName, true)}";`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
await saveFile("src/models", `${kebabCase2(modelName, false)}.ts`, generateModel(modelName));
|
|
385
|
+
await saveFile("src/operations", `get${pascalCase2(modelName, true)}.ts`, generateReadAllOperation(modelName));
|
|
386
|
+
await saveFile("src/operations", `create${pascalCase2(modelName, false)}.ts`, generateCreateOperation(modelName));
|
|
387
|
+
await saveFile("src/operations", `get${pascalCase2(modelName, false)}.ts`, generateReadOperation(modelName));
|
|
388
|
+
await saveFile("src/operations", `update${pascalCase2(modelName, false)}.ts`, generateUpdateOperation(modelName));
|
|
389
|
+
await saveFile("src/operations", `delete${pascalCase2(modelName, false)}.ts`, generateDeleteOperation(modelName));
|
|
390
|
+
if (operationTests) {
|
|
391
|
+
await saveFile("src/operations", `get${pascalCase2(modelName, true)}.test.ts`, generateReadAllTest(modelName));
|
|
392
|
+
await saveFile("src/operations", `create${pascalCase2(modelName, false)}.test.ts`, generateCreateTest(modelName));
|
|
393
|
+
await saveFile("src/operations", `get${pascalCase2(modelName, false)}.test.ts`, generateReadTest(modelName));
|
|
394
|
+
await saveFile("src/operations", `update${pascalCase2(modelName, false)}.test.ts`, generateUpdateTest(modelName));
|
|
395
|
+
await saveFile("src/operations", `delete${pascalCase2(modelName, false)}.test.ts`, generateDeleteTest(modelName));
|
|
396
|
+
}
|
|
397
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omer-x/next-openapi-scaffold-generator",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0-alpha.1",
|
|
4
4
|
"description": "Generates model, schema and API routes for CRUD operations",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"next",
|
|
@@ -46,26 +46,29 @@
|
|
|
46
46
|
"node": ">=14.8.0"
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
|
49
|
-
"
|
|
50
|
-
"test": "
|
|
49
|
+
"lint": "eslint --flag unstable_native_nodejs_ts_config",
|
|
50
|
+
"test": "vitest run --coverage",
|
|
51
|
+
"test:watch": "vitest --coverage",
|
|
51
52
|
"dev": "tsup --watch",
|
|
52
53
|
"build": "tsup"
|
|
53
54
|
},
|
|
54
55
|
"dependencies": {
|
|
55
56
|
"change-case": "^5.4.4",
|
|
56
57
|
"handlebars": "^4.7.8",
|
|
57
|
-
"pluralize": "^8.0.0"
|
|
58
|
+
"pluralize": "^8.0.0",
|
|
59
|
+
"prompts": "^2.4.2"
|
|
58
60
|
},
|
|
59
61
|
"devDependencies": {
|
|
60
|
-
"@omer-x/eslint-config": "^
|
|
61
|
-
"@types/node": "^
|
|
62
|
+
"@omer-x/eslint-config": "^2.2.6",
|
|
63
|
+
"@types/node": "^25.0.1",
|
|
62
64
|
"@types/pluralize": "^0.0.33",
|
|
63
|
-
"
|
|
65
|
+
"@types/prompts": "^2.4.9",
|
|
66
|
+
"@vitest/coverage-v8": "^4.0.15",
|
|
64
67
|
"esbuild-plugin-inline-import": "^1.1.0",
|
|
65
|
-
"eslint": "^
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
68
|
+
"eslint": "^9.39.1",
|
|
69
|
+
"semantic-release": "^25.0.2",
|
|
70
|
+
"tsup": "^8.5.1",
|
|
71
|
+
"typescript": "^5.9.3",
|
|
72
|
+
"vitest": "^4.0.15"
|
|
70
73
|
}
|
|
71
74
|
}
|