@omer-x/next-openapi-scaffold-generator 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/index.js +166 -0
- package/package.json +71 -0
package/bin/index.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// src/core/file.ts
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
async function saveFile(directory, fileName, content) {
|
|
5
|
+
const dirPath = path.resolve(process.cwd(), directory);
|
|
6
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
7
|
+
const filePath = path.resolve(dirPath, fileName);
|
|
8
|
+
await fs.writeFile(filePath, content, "utf8");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// src/core/string.ts
|
|
12
|
+
import { camelCase, capitalCase, kebabCase, noCase, pascalCase } from "change-case";
|
|
13
|
+
import pluralize from "pluralize";
|
|
14
|
+
function capitalize(text) {
|
|
15
|
+
if (text.length === 0) return text;
|
|
16
|
+
return text.charAt(0).toUpperCase() + text.slice(1).toLowerCase();
|
|
17
|
+
}
|
|
18
|
+
function convertToCamelCase(text, plural) {
|
|
19
|
+
const properText = plural ? pluralize(text) : pluralize.singular(text);
|
|
20
|
+
return camelCase(properText);
|
|
21
|
+
}
|
|
22
|
+
function convertToKebabCase(text, plural) {
|
|
23
|
+
const properText = plural ? pluralize(text) : pluralize.singular(text);
|
|
24
|
+
return kebabCase(properText);
|
|
25
|
+
}
|
|
26
|
+
function convertToPascalCase(text, plural) {
|
|
27
|
+
const properText = plural ? pluralize(text) : pluralize.singular(text);
|
|
28
|
+
return pascalCase(properText);
|
|
29
|
+
}
|
|
30
|
+
function convertToNoCase(text, plural) {
|
|
31
|
+
const properText = plural ? pluralize(text) : pluralize.singular(text);
|
|
32
|
+
return noCase(properText);
|
|
33
|
+
}
|
|
34
|
+
function convertToCapitalCase(text, plural, onlyFirst) {
|
|
35
|
+
const properText = plural ? pluralize(text) : pluralize.singular(text);
|
|
36
|
+
if (onlyFirst) {
|
|
37
|
+
return capitalize(noCase(properText));
|
|
38
|
+
}
|
|
39
|
+
return capitalCase(properText);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// src/prompt.ts
|
|
43
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
44
|
+
import * as readline from "node:readline/promises";
|
|
45
|
+
var rl = readline.createInterface({ input, output });
|
|
46
|
+
async function askQuestions(queries) {
|
|
47
|
+
const answers = await Promise.all(queries.map((query) => rl.question(query)));
|
|
48
|
+
rl.close();
|
|
49
|
+
return answers;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/core/template.ts
|
|
53
|
+
import Handlebars from "handlebars";
|
|
54
|
+
function getTemplate(input2) {
|
|
55
|
+
return Handlebars.compile(input2);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// _td9nf9mua:/home/omer/Projects/next-openapi-scaffold-generator/src/templates/api-route.hbs
|
|
59
|
+
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-{{ kebabCaseSingular }}";\nimport get{{ pascalCasePlural }} from "~/operations/get-{{ kebabCasePlural }}";\n\n{{{ readAllOperation }}}\n\n{{{ createOperation }}}\n';
|
|
60
|
+
|
|
61
|
+
// _td9nf9mua:/home/omer/Projects/next-openapi-scaffold-generator/src/templates/create.hbs
|
|
62
|
+
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';
|
|
63
|
+
|
|
64
|
+
// src/templates/create.ts
|
|
65
|
+
function generateCreateOperation(modelName2) {
|
|
66
|
+
const template = getTemplate(create_default);
|
|
67
|
+
return template({
|
|
68
|
+
camelCaseSingular: convertToCamelCase(modelName2, false),
|
|
69
|
+
capitalCasePlural: convertToCapitalCase(modelName2, true, false),
|
|
70
|
+
noCaseSingular: convertToNoCase(modelName2, false),
|
|
71
|
+
onlyFirstCapitalCaseSingular: convertToCapitalCase(modelName2, false, true),
|
|
72
|
+
pascalCaseSingular: convertToPascalCase(modelName2, false)
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// _td9nf9mua:/home/omer/Projects/next-openapi-scaffold-generator/src/templates/read-all.hbs
|
|
77
|
+
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 return Response.json(await get{{ pascalCasePlural }}(db, queryParams.select));\n },\n responses: {\n 200: { description: "Returns a list of {{ noCasePlural }}", content: {{ pascalCaseSingular }}DTO, isArray: true },\n },\n});\n';
|
|
78
|
+
|
|
79
|
+
// src/templates/read-all.ts
|
|
80
|
+
function generateReadAllOperation(modelName2) {
|
|
81
|
+
const template = getTemplate(read_all_default);
|
|
82
|
+
return template({
|
|
83
|
+
capitalCasePlural: convertToCapitalCase(modelName2, true, false),
|
|
84
|
+
noCasePlural: convertToNoCase(modelName2, true),
|
|
85
|
+
pascalCaseSingular: convertToPascalCase(modelName2, false),
|
|
86
|
+
pascalCasePlural: convertToPascalCase(modelName2, true)
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/templates/api-route.ts
|
|
91
|
+
function generateApiRoute(modelName2) {
|
|
92
|
+
const template = getTemplate(api_route_default);
|
|
93
|
+
return template({
|
|
94
|
+
kebabCasePlural: convertToKebabCase(modelName2, true),
|
|
95
|
+
kebabCaseSingular: convertToKebabCase(modelName2, false),
|
|
96
|
+
pascalCasePlural: convertToPascalCase(modelName2, true),
|
|
97
|
+
pascalCaseSingular: convertToPascalCase(modelName2, false),
|
|
98
|
+
readAllOperation: generateReadAllOperation(modelName2).slice(0, -1),
|
|
99
|
+
createOperation: generateCreateOperation(modelName2).slice(0, -1)
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// _td9nf9mua:/home/omer/Projects/next-openapi-scaffold-generator/src/templates/api-route-with-id.hbs
|
|
104
|
+
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-{{ kebabCaseSingular }}";\nimport get{{ pascalCaseSingular }} from "~/operations/get-{{ kebabCaseSingular }}";\nimport update{{ pascalCaseSingular }} from "~/operations/update-{{ kebabCaseSingular }}";\n\n{{{ readOperation }}}\n\n{{{ updateOperation }}}\n\n{{{ deleteOperation }}}\n';
|
|
105
|
+
|
|
106
|
+
// _td9nf9mua:/home/omer/Projects/next-openapi-scaffold-generator/src/templates/delete.hbs
|
|
107
|
+
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 new Response(null, { status: 404 });\n },\n responses: {\n 204: { description: "{{ onlyFirstCapitalCaseSingular }} deleted successfully" },\n 404: { description: "{{ onlyFirstCapitalCaseSingular }} not found" },\n },\n});\n';
|
|
108
|
+
|
|
109
|
+
// src/templates/delete.ts
|
|
110
|
+
function generateDeleteOperation(modelName2) {
|
|
111
|
+
const template = getTemplate(delete_default);
|
|
112
|
+
return template({
|
|
113
|
+
camelCaseSingular: convertToCamelCase(modelName2, false),
|
|
114
|
+
capitalCasePlural: convertToCapitalCase(modelName2, true, false),
|
|
115
|
+
noCaseSingular: convertToNoCase(modelName2, false),
|
|
116
|
+
onlyFirstCapitalCaseSingular: convertToCapitalCase(modelName2, false, true),
|
|
117
|
+
pascalCaseSingular: convertToPascalCase(modelName2, false)
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// _td9nf9mua:/home/omer/Projects/next-openapi-scaffold-generator/src/templates/read.hbs
|
|
122
|
+
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 new Response(null, { status: 404 });\n },\n responses: {\n 200: { description: "{{ onlyFirstCapitalCaseSingular }} found", content: {{ pascalCaseSingular }}DTO },\n 404: { description: "{{ onlyFirstCapitalCaseSingular }} not found" },\n },\n});\n';
|
|
123
|
+
|
|
124
|
+
// src/templates/read.ts
|
|
125
|
+
function generateReadOperation(modelName2) {
|
|
126
|
+
const template = getTemplate(read_default);
|
|
127
|
+
return template({
|
|
128
|
+
camelCaseSingular: convertToCamelCase(modelName2, false),
|
|
129
|
+
capitalCasePlural: convertToCapitalCase(modelName2, true, false),
|
|
130
|
+
noCaseSingular: convertToNoCase(modelName2, false),
|
|
131
|
+
onlyFirstCapitalCaseSingular: convertToCapitalCase(modelName2, false, true),
|
|
132
|
+
pascalCaseSingular: convertToPascalCase(modelName2, false)
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// _td9nf9mua:/home/omer/Projects/next-openapi-scaffold-generator/src/templates/update.hbs
|
|
137
|
+
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 new Response(null, { status: 404 });\n },\n responses: {\n 200: { description: "{{ onlyFirstCapitalCaseSingular }} updated successfully", content: {{ pascalCaseSingular }}DTO },\n 404: { description: "{{ onlyFirstCapitalCaseSingular }} not found" },\n },\n});\n';
|
|
138
|
+
|
|
139
|
+
// src/templates/update.ts
|
|
140
|
+
function generateUpdateOperation(modelName2) {
|
|
141
|
+
const template = getTemplate(update_default);
|
|
142
|
+
return template({
|
|
143
|
+
camelCaseSingular: convertToCamelCase(modelName2, false),
|
|
144
|
+
capitalCasePlural: convertToCapitalCase(modelName2, true, false),
|
|
145
|
+
noCaseSingular: convertToNoCase(modelName2, false),
|
|
146
|
+
onlyFirstCapitalCaseSingular: convertToCapitalCase(modelName2, false, true),
|
|
147
|
+
pascalCaseSingular: convertToPascalCase(modelName2, false)
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// src/templates/api-route-with-id.ts
|
|
152
|
+
function generateApiRouteWithId(modelName2) {
|
|
153
|
+
const template = getTemplate(api_route_with_id_default);
|
|
154
|
+
return template({
|
|
155
|
+
kebabCaseSingular: convertToKebabCase(modelName2, false),
|
|
156
|
+
pascalCaseSingular: convertToPascalCase(modelName2, false),
|
|
157
|
+
readOperation: generateReadOperation(modelName2).slice(0, -1),
|
|
158
|
+
updateOperation: generateUpdateOperation(modelName2).slice(0, -1),
|
|
159
|
+
deleteOperation: generateDeleteOperation(modelName2).slice(0, -1)
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// src/index.ts
|
|
164
|
+
var [modelName] = await askQuestions(["Enter Model Name: "]);
|
|
165
|
+
await saveFile(`src/app/${convertToKebabCase(modelName, true)}`, "route.ts", generateApiRoute(modelName));
|
|
166
|
+
await saveFile(`src/app/${convertToKebabCase(modelName, true)}/[id]`, "route.ts", generateApiRouteWithId(modelName));
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@omer-x/next-openapi-scaffold-generator",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Generates model, schema and API routes for CRUD operations",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"next",
|
|
7
|
+
"openapi",
|
|
8
|
+
"scaffold",
|
|
9
|
+
"generator",
|
|
10
|
+
"swagger",
|
|
11
|
+
"api",
|
|
12
|
+
"crud",
|
|
13
|
+
"schema",
|
|
14
|
+
"model",
|
|
15
|
+
"typescript",
|
|
16
|
+
"ts",
|
|
17
|
+
"nextjs"
|
|
18
|
+
],
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/omermecitoglu/next-openapi-scaffold-generator.git"
|
|
22
|
+
},
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/omermecitoglu/next-openapi-scaffold-generator/issues"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://github.com/omermecitoglu/next-openapi-scaffold-generator#readme",
|
|
27
|
+
"private": false,
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"author": {
|
|
32
|
+
"name": "Omer Mecitoglu",
|
|
33
|
+
"email": "omer.mecitoglu@gmail.com",
|
|
34
|
+
"url": "https://omermecitoglu.github.io"
|
|
35
|
+
},
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"main": "bin/index.js",
|
|
38
|
+
"bin": {
|
|
39
|
+
"generate-scaffold": "bin/index.js"
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"bin/"
|
|
43
|
+
],
|
|
44
|
+
"type": "module",
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=14.8.0"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"gh:release": "conventional-github-releaser -p angular",
|
|
50
|
+
"test": "jest",
|
|
51
|
+
"dev": "tsup --watch",
|
|
52
|
+
"build": "tsup"
|
|
53
|
+
},
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"change-case": "^5.4.4",
|
|
56
|
+
"handlebars": "^4.7.8",
|
|
57
|
+
"pluralize": "^8.0.0"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@omer-x/eslint-config": "^1.0.7",
|
|
61
|
+
"@types/node": "^22.5.0",
|
|
62
|
+
"@types/pluralize": "^0.0.33",
|
|
63
|
+
"conventional-github-releaser": "^3.1.5",
|
|
64
|
+
"esbuild-plugin-inline-import": "^1.1.0",
|
|
65
|
+
"eslint": "^8.57.0",
|
|
66
|
+
"ts-jest": "^29.2.4",
|
|
67
|
+
"ts-node": "^10.9.2",
|
|
68
|
+
"tsup": "^8.2.4",
|
|
69
|
+
"typescript": "^5.5.4"
|
|
70
|
+
}
|
|
71
|
+
}
|