@omer-x/next-openapi-scaffold-generator 0.3.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 +98 -89
- package/package.json +12 -11
package/bin/index.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
import prompts from "prompts";
|
|
5
5
|
|
|
6
6
|
// src/core/file.ts
|
|
7
|
-
import fs from "
|
|
8
|
-
import path from "
|
|
7
|
+
import fs from "fs/promises";
|
|
8
|
+
import path from "path";
|
|
9
9
|
async function saveFile(directory, fileName, content) {
|
|
10
10
|
const dirPath = path.resolve(process.cwd(), directory);
|
|
11
11
|
await fs.mkdir(dirPath, { recursive: true });
|
|
@@ -18,6 +18,19 @@ async function appendFile(directory, fileName, content) {
|
|
|
18
18
|
const filePath = path.resolve(dirPath, fileName);
|
|
19
19
|
await fs.appendFile(filePath, content + "\n", "utf8");
|
|
20
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
|
+
}
|
|
21
34
|
|
|
22
35
|
// src/core/string.ts
|
|
23
36
|
import * as changeCase from "change-case";
|
|
@@ -53,6 +66,10 @@ function capitalCase2(text, plural, onlyFirst) {
|
|
|
53
66
|
}
|
|
54
67
|
return changeCase.capitalCase(properText);
|
|
55
68
|
}
|
|
69
|
+
function constantCase2(text, plural) {
|
|
70
|
+
const properText = plural ? pluralize(text) : pluralize.singular(text);
|
|
71
|
+
return changeCase.constantCase(properText);
|
|
72
|
+
}
|
|
56
73
|
|
|
57
74
|
// src/core/template.ts
|
|
58
75
|
import Handlebars from "handlebars";
|
|
@@ -60,11 +77,11 @@ function getTemplate(input) {
|
|
|
60
77
|
return Handlebars.compile(input);
|
|
61
78
|
}
|
|
62
79
|
|
|
63
|
-
//
|
|
64
|
-
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';
|
|
65
82
|
|
|
66
|
-
//
|
|
67
|
-
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';
|
|
68
85
|
|
|
69
86
|
// src/templates/routes/create.ts
|
|
70
87
|
function generateCreateOperationRoute(modelName2) {
|
|
@@ -78,13 +95,14 @@ function generateCreateOperationRoute(modelName2) {
|
|
|
78
95
|
});
|
|
79
96
|
}
|
|
80
97
|
|
|
81
|
-
//
|
|
82
|
-
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';
|
|
83
100
|
|
|
84
101
|
// src/templates/routes/read-all.ts
|
|
85
102
|
function generateReadAllOperationRoute(modelName2) {
|
|
86
103
|
const template = getTemplate(read_all_default);
|
|
87
104
|
return template({
|
|
105
|
+
camelCasePlural: camelCase2(modelName2, true),
|
|
88
106
|
capitalCasePlural: capitalCase2(modelName2, true, false),
|
|
89
107
|
noCasePlural: noCase2(modelName2, true),
|
|
90
108
|
pascalCaseSingular: pascalCase2(modelName2, false),
|
|
@@ -96,6 +114,7 @@ function generateReadAllOperationRoute(modelName2) {
|
|
|
96
114
|
function generateApiRoute(modelName2) {
|
|
97
115
|
const template = getTemplate(api_route_default);
|
|
98
116
|
return template({
|
|
117
|
+
camelCasePlural: camelCase2(modelName2, true),
|
|
99
118
|
kebabCasePlural: kebabCase2(modelName2, true),
|
|
100
119
|
kebabCaseSingular: kebabCase2(modelName2, false),
|
|
101
120
|
pascalCasePlural: pascalCase2(modelName2, true),
|
|
@@ -105,11 +124,11 @@ function generateApiRoute(modelName2) {
|
|
|
105
124
|
});
|
|
106
125
|
}
|
|
107
126
|
|
|
108
|
-
//
|
|
109
|
-
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';
|
|
110
129
|
|
|
111
|
-
//
|
|
112
|
-
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';
|
|
113
132
|
|
|
114
133
|
// src/templates/routes/delete.ts
|
|
115
134
|
function generateDeleteOperationRoute(modelName2) {
|
|
@@ -117,14 +136,15 @@ function generateDeleteOperationRoute(modelName2) {
|
|
|
117
136
|
return template({
|
|
118
137
|
camelCaseSingular: camelCase2(modelName2, false),
|
|
119
138
|
capitalCasePlural: capitalCase2(modelName2, true, false),
|
|
139
|
+
constantCaseSingular: constantCase2(modelName2, false),
|
|
120
140
|
noCaseSingular: noCase2(modelName2, false),
|
|
121
141
|
onlyFirstCapitalCaseSingular: capitalCase2(modelName2, false, true),
|
|
122
142
|
pascalCaseSingular: pascalCase2(modelName2, false)
|
|
123
143
|
});
|
|
124
144
|
}
|
|
125
145
|
|
|
126
|
-
//
|
|
127
|
-
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';
|
|
128
148
|
|
|
129
149
|
// src/templates/routes/read.ts
|
|
130
150
|
function generateReadOperationRoute(modelName2) {
|
|
@@ -132,14 +152,15 @@ function generateReadOperationRoute(modelName2) {
|
|
|
132
152
|
return template({
|
|
133
153
|
camelCaseSingular: camelCase2(modelName2, false),
|
|
134
154
|
capitalCasePlural: capitalCase2(modelName2, true, false),
|
|
155
|
+
constantCaseSingular: constantCase2(modelName2, false),
|
|
135
156
|
noCaseSingular: noCase2(modelName2, false),
|
|
136
157
|
onlyFirstCapitalCaseSingular: capitalCase2(modelName2, false, true),
|
|
137
158
|
pascalCaseSingular: pascalCase2(modelName2, false)
|
|
138
159
|
});
|
|
139
160
|
}
|
|
140
161
|
|
|
141
|
-
//
|
|
142
|
-
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';
|
|
143
164
|
|
|
144
165
|
// src/templates/routes/update.ts
|
|
145
166
|
function generateUpdateOperationRoute(modelName2) {
|
|
@@ -147,6 +168,7 @@ function generateUpdateOperationRoute(modelName2) {
|
|
|
147
168
|
return template({
|
|
148
169
|
camelCaseSingular: camelCase2(modelName2, false),
|
|
149
170
|
capitalCasePlural: capitalCase2(modelName2, true, false),
|
|
171
|
+
constantCaseSingular: constantCase2(modelName2, false),
|
|
150
172
|
noCaseSingular: noCase2(modelName2, false),
|
|
151
173
|
onlyFirstCapitalCaseSingular: capitalCase2(modelName2, false, true),
|
|
152
174
|
pascalCaseSingular: pascalCase2(modelName2, false)
|
|
@@ -165,98 +187,92 @@ function generateApiRouteWithId(modelName2) {
|
|
|
165
187
|
});
|
|
166
188
|
}
|
|
167
189
|
|
|
168
|
-
//
|
|
169
|
-
var model_default = 'import { createInsertSchema } from "drizzle-zod";\nimport { {{ camelCasePlural }} } from "~/database/schema/{{ kebabCasePlural }}";\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';
|
|
170
192
|
|
|
171
193
|
// src/templates/model.ts
|
|
172
|
-
function generateModel(modelName2
|
|
173
|
-
|
|
174
|
-
return template({
|
|
194
|
+
function generateModel(modelName2) {
|
|
195
|
+
return getTemplate(model_default)({
|
|
175
196
|
camelCasePlural: camelCase2(modelName2, true),
|
|
176
197
|
kebabCasePlural: kebabCase2(modelName2, true),
|
|
177
198
|
noCaseSingular: noCase2(modelName2, false),
|
|
178
|
-
pascalCaseSingular: pascalCase2(modelName2, false)
|
|
179
|
-
firstField: firstField2
|
|
199
|
+
pascalCaseSingular: pascalCase2(modelName2, false)
|
|
180
200
|
});
|
|
181
201
|
}
|
|
182
202
|
|
|
183
|
-
//
|
|
184
|
-
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';
|
|
185
205
|
|
|
186
206
|
// src/templates/operation-tests/create.ts
|
|
187
207
|
function generateCreateTest(modelName2) {
|
|
188
|
-
|
|
189
|
-
return template({
|
|
190
|
-
camelCaseSingular: camelCase2(modelName2, false),
|
|
208
|
+
return getTemplate(create_default2)({
|
|
191
209
|
kebabCaseSingular: kebabCase2(modelName2, false),
|
|
192
210
|
noCaseSingular: noCase2(modelName2, false),
|
|
193
|
-
pascalCaseSingular: pascalCase2(modelName2, false)
|
|
211
|
+
pascalCaseSingular: pascalCase2(modelName2, false),
|
|
212
|
+
snakeCasePlural: snakeCase2(modelName2, true)
|
|
194
213
|
});
|
|
195
214
|
}
|
|
196
215
|
|
|
197
|
-
//
|
|
198
|
-
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';
|
|
199
218
|
|
|
200
219
|
// src/templates/operation-tests/delete.ts
|
|
201
220
|
function generateDeleteTest(modelName2) {
|
|
202
|
-
|
|
203
|
-
return template({
|
|
204
|
-
camelCaseSingular: camelCase2(modelName2, false),
|
|
221
|
+
return getTemplate(delete_default2)({
|
|
205
222
|
kebabCaseSingular: kebabCase2(modelName2, false),
|
|
206
223
|
noCaseSingular: noCase2(modelName2, false),
|
|
207
|
-
pascalCaseSingular: pascalCase2(modelName2, false)
|
|
224
|
+
pascalCaseSingular: pascalCase2(modelName2, false),
|
|
225
|
+
snakeCasePlural: snakeCase2(modelName2, true)
|
|
208
226
|
});
|
|
209
227
|
}
|
|
210
228
|
|
|
211
|
-
//
|
|
212
|
-
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';
|
|
213
231
|
|
|
214
232
|
// src/templates/operation-tests/read.ts
|
|
215
233
|
function generateReadTest(modelName2) {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
camelCaseSingular: camelCase2(modelName2, false),
|
|
234
|
+
return getTemplate(read_default2)({
|
|
235
|
+
camelCasePlural: camelCase2(modelName2, true),
|
|
219
236
|
kebabCaseSingular: kebabCase2(modelName2, false),
|
|
220
237
|
noCaseSingular: noCase2(modelName2, false),
|
|
221
|
-
pascalCaseSingular: pascalCase2(modelName2, false)
|
|
238
|
+
pascalCaseSingular: pascalCase2(modelName2, false),
|
|
239
|
+
snakeCasePlural: snakeCase2(modelName2, true)
|
|
222
240
|
});
|
|
223
241
|
}
|
|
224
242
|
|
|
225
|
-
//
|
|
226
|
-
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';
|
|
227
245
|
|
|
228
246
|
// src/templates/operation-tests/read-all.ts
|
|
229
247
|
function generateReadAllTest(modelName2) {
|
|
230
|
-
|
|
231
|
-
|
|
248
|
+
return getTemplate(read_all_default2)({
|
|
249
|
+
camelCasePlural: camelCase2(modelName2, true),
|
|
232
250
|
kebabCaseSingular: kebabCase2(modelName2, false),
|
|
251
|
+
noCasePlural: noCase2(modelName2, true),
|
|
233
252
|
pascalCasePlural: pascalCase2(modelName2, true),
|
|
234
|
-
|
|
253
|
+
snakeCasePlural: snakeCase2(modelName2, true)
|
|
235
254
|
});
|
|
236
255
|
}
|
|
237
256
|
|
|
238
|
-
//
|
|
239
|
-
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';
|
|
240
259
|
|
|
241
260
|
// src/templates/operation-tests/update.ts
|
|
242
|
-
function generateUpdateTest(modelName2
|
|
243
|
-
|
|
244
|
-
return template({
|
|
245
|
-
camelCaseSingular: camelCase2(modelName2, false),
|
|
261
|
+
function generateUpdateTest(modelName2) {
|
|
262
|
+
return getTemplate(update_default2)({
|
|
246
263
|
kebabCaseSingular: kebabCase2(modelName2, false),
|
|
247
264
|
noCaseSingular: noCase2(modelName2, false),
|
|
248
265
|
pascalCaseSingular: pascalCase2(modelName2, false),
|
|
249
|
-
|
|
266
|
+
snakeCasePlural: snakeCase2(modelName2, true)
|
|
250
267
|
});
|
|
251
268
|
}
|
|
252
269
|
|
|
253
|
-
//
|
|
254
|
-
var create_default3 = 'import type database from "~/database";\nimport { {{ camelCasePlural }} } from "~/database/schema/{{ kebabCasePlural }}";\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';
|
|
255
272
|
|
|
256
273
|
// src/templates/operations/create.ts
|
|
257
274
|
function generateCreateOperation(modelName2) {
|
|
258
|
-
|
|
259
|
-
return template({
|
|
275
|
+
return getTemplate(create_default3)({
|
|
260
276
|
camelCasePlural: camelCase2(modelName2, true),
|
|
261
277
|
camelCaseSingular: camelCase2(modelName2, false),
|
|
262
278
|
kebabCasePlural: kebabCase2(modelName2, true),
|
|
@@ -265,13 +281,12 @@ function generateCreateOperation(modelName2) {
|
|
|
265
281
|
});
|
|
266
282
|
}
|
|
267
283
|
|
|
268
|
-
//
|
|
269
|
-
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: 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';
|
|
270
286
|
|
|
271
287
|
// src/templates/operations/delete.ts
|
|
272
288
|
function generateDeleteOperation(modelName2) {
|
|
273
|
-
|
|
274
|
-
return template({
|
|
289
|
+
return getTemplate(delete_default3)({
|
|
275
290
|
camelCasePlural: camelCase2(modelName2, true),
|
|
276
291
|
camelCaseSingular: camelCase2(modelName2, false),
|
|
277
292
|
kebabCasePlural: kebabCase2(modelName2, true),
|
|
@@ -279,26 +294,24 @@ function generateDeleteOperation(modelName2) {
|
|
|
279
294
|
});
|
|
280
295
|
}
|
|
281
296
|
|
|
282
|
-
//
|
|
283
|
-
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';
|
|
284
299
|
|
|
285
300
|
// src/templates/operations/read.ts
|
|
286
301
|
function generateReadOperation(modelName2) {
|
|
287
|
-
|
|
288
|
-
return template({
|
|
302
|
+
return getTemplate(read_default3)({
|
|
289
303
|
camelCasePlural: camelCase2(modelName2, true),
|
|
290
304
|
camelCaseSingular: camelCase2(modelName2, false),
|
|
291
305
|
pascalCaseSingular: pascalCase2(modelName2, false)
|
|
292
306
|
});
|
|
293
307
|
}
|
|
294
308
|
|
|
295
|
-
//
|
|
296
|
-
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';
|
|
297
311
|
|
|
298
312
|
// src/templates/operations/read-all.ts
|
|
299
313
|
function generateReadAllOperation(modelName2) {
|
|
300
|
-
|
|
301
|
-
return template({
|
|
314
|
+
return getTemplate(read_all_default3)({
|
|
302
315
|
camelCasePlural: camelCase2(modelName2, true),
|
|
303
316
|
kebabCaseSingular: kebabCase2(modelName2, false),
|
|
304
317
|
pascalCasePlural: pascalCase2(modelName2, true),
|
|
@@ -306,13 +319,12 @@ function generateReadAllOperation(modelName2) {
|
|
|
306
319
|
});
|
|
307
320
|
}
|
|
308
321
|
|
|
309
|
-
//
|
|
310
|
-
var update_default3 = 'import { eq } from "drizzle-orm";\nimport type database from "~/database";\nimport { {{ camelCasePlural }} } from "~/database/schema/{{ kebabCasePlural }}";\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';
|
|
311
324
|
|
|
312
325
|
// src/templates/operations/update.ts
|
|
313
326
|
function generateUpdateOperation(modelName2) {
|
|
314
|
-
|
|
315
|
-
return template({
|
|
327
|
+
return getTemplate(update_default3)({
|
|
316
328
|
camelCasePlural: camelCase2(modelName2, true),
|
|
317
329
|
camelCaseSingular: camelCase2(modelName2, false),
|
|
318
330
|
kebabCasePlural: kebabCase2(modelName2, true),
|
|
@@ -321,17 +333,16 @@ function generateUpdateOperation(modelName2) {
|
|
|
321
333
|
});
|
|
322
334
|
}
|
|
323
335
|
|
|
324
|
-
//
|
|
325
|
-
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(
|
|
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';
|
|
326
338
|
|
|
327
339
|
// src/templates/schema.ts
|
|
328
|
-
function generateSchema(modelName2
|
|
340
|
+
function generateSchema(modelName2) {
|
|
329
341
|
const template = getTemplate(schema_default);
|
|
330
342
|
return template({
|
|
331
343
|
camelCasePlural: camelCase2(modelName2, true),
|
|
332
344
|
pascalCasePlural: pascalCase2(modelName2, true),
|
|
333
|
-
snakeCasePlural: snakeCase2(modelName2, true)
|
|
334
|
-
firstField: firstField2
|
|
345
|
+
snakeCasePlural: snakeCase2(modelName2, true)
|
|
335
346
|
});
|
|
336
347
|
}
|
|
337
348
|
|
|
@@ -351,12 +362,6 @@ var { orm } = await prompts({
|
|
|
351
362
|
],
|
|
352
363
|
initial: 0
|
|
353
364
|
});
|
|
354
|
-
var { firstField } = await prompts({
|
|
355
|
-
type: "text",
|
|
356
|
-
name: "firstField",
|
|
357
|
-
message: "Enter name of the first field",
|
|
358
|
-
initial: "name"
|
|
359
|
-
});
|
|
360
365
|
var { operationTests } = await prompts({
|
|
361
366
|
type: "toggle",
|
|
362
367
|
name: "operationTests",
|
|
@@ -365,14 +370,18 @@ var { operationTests } = await prompts({
|
|
|
365
370
|
active: "yes",
|
|
366
371
|
inactive: "no"
|
|
367
372
|
});
|
|
368
|
-
if (modelName
|
|
373
|
+
if (modelName) {
|
|
369
374
|
await saveFile(`src/app/${kebabCase2(modelName, true)}`, "route.ts", generateApiRoute(modelName));
|
|
370
375
|
await saveFile(`src/app/${kebabCase2(modelName, true)}/[id]`, "route.ts", generateApiRouteWithId(modelName));
|
|
371
376
|
if (orm === "drizzle") {
|
|
372
|
-
await saveFile("src/database/schema", `${kebabCase2(modelName, true)}.ts`, generateSchema(modelName
|
|
373
|
-
|
|
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
|
+
}
|
|
374
383
|
}
|
|
375
|
-
await saveFile("src/models", `${kebabCase2(modelName, false)}.ts`, generateModel(modelName
|
|
384
|
+
await saveFile("src/models", `${kebabCase2(modelName, false)}.ts`, generateModel(modelName));
|
|
376
385
|
await saveFile("src/operations", `get${pascalCase2(modelName, true)}.ts`, generateReadAllOperation(modelName));
|
|
377
386
|
await saveFile("src/operations", `create${pascalCase2(modelName, false)}.ts`, generateCreateOperation(modelName));
|
|
378
387
|
await saveFile("src/operations", `get${pascalCase2(modelName, false)}.ts`, generateReadOperation(modelName));
|
|
@@ -382,7 +391,7 @@ if (modelName && firstField) {
|
|
|
382
391
|
await saveFile("src/operations", `get${pascalCase2(modelName, true)}.test.ts`, generateReadAllTest(modelName));
|
|
383
392
|
await saveFile("src/operations", `create${pascalCase2(modelName, false)}.test.ts`, generateCreateTest(modelName));
|
|
384
393
|
await saveFile("src/operations", `get${pascalCase2(modelName, false)}.test.ts`, generateReadTest(modelName));
|
|
385
|
-
await saveFile("src/operations", `update${pascalCase2(modelName, false)}.test.ts`, generateUpdateTest(modelName
|
|
394
|
+
await saveFile("src/operations", `update${pascalCase2(modelName, false)}.test.ts`, generateUpdateTest(modelName));
|
|
386
395
|
await saveFile("src/operations", `delete${pascalCase2(modelName, false)}.test.ts`, generateDeleteTest(modelName));
|
|
387
396
|
}
|
|
388
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,8 +46,9 @@
|
|
|
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
|
},
|
|
@@ -58,16 +59,16 @@
|
|
|
58
59
|
"prompts": "^2.4.2"
|
|
59
60
|
},
|
|
60
61
|
"devDependencies": {
|
|
61
|
-
"@omer-x/eslint-config": "^
|
|
62
|
-
"@types/node": "^
|
|
62
|
+
"@omer-x/eslint-config": "^2.2.6",
|
|
63
|
+
"@types/node": "^25.0.1",
|
|
63
64
|
"@types/pluralize": "^0.0.33",
|
|
64
65
|
"@types/prompts": "^2.4.9",
|
|
65
|
-
"
|
|
66
|
+
"@vitest/coverage-v8": "^4.0.15",
|
|
66
67
|
"esbuild-plugin-inline-import": "^1.1.0",
|
|
67
|
-
"eslint": "^
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
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"
|
|
72
73
|
}
|
|
73
74
|
}
|