prisma-swagger-autogen 1.0.0 → 1.0.3
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/dist/{chunk-SNNQZQLO.js → chunk-6R6DHPOL.js} +40 -141
- package/dist/cli.cjs +41 -142
- package/dist/cli.d.cts +2 -1
- package/dist/cli.d.ts +2 -1
- package/dist/cli.js +1 -2
- package/dist/index.cjs +41 -141
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +4 -2
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import fs from "fs";
|
|
3
3
|
import path from "path";
|
|
4
|
+
import { createRequire } from "module";
|
|
4
5
|
import { globSync } from "glob";
|
|
5
|
-
import { execSync } from "child_process";
|
|
6
6
|
var CONFIG = {
|
|
7
7
|
projectRoot: process.cwd(),
|
|
8
8
|
controllersGlob: "./src/web/api/controllers/**/*.ts",
|
|
@@ -25,6 +25,28 @@ function pluralize(name) {
|
|
|
25
25
|
if (name.endsWith("s")) return `${name}es`;
|
|
26
26
|
return `${name}s`;
|
|
27
27
|
}
|
|
28
|
+
function getRequire() {
|
|
29
|
+
const base = typeof __filename !== "undefined" ? __filename : import.meta.url;
|
|
30
|
+
return createRequire(base);
|
|
31
|
+
}
|
|
32
|
+
function loadDmmfFromProject(schemaPath) {
|
|
33
|
+
const resolvedSchemaPath = schemaPath ? path.resolve(process.cwd(), schemaPath) : path.resolve(process.cwd(), "prisma/schema.prisma");
|
|
34
|
+
if (!fs.existsSync(resolvedSchemaPath)) {
|
|
35
|
+
throw new Error(`Prisma schema not found at ${resolvedSchemaPath}`);
|
|
36
|
+
}
|
|
37
|
+
const datamodel = fs.readFileSync(resolvedSchemaPath, "utf8");
|
|
38
|
+
const require2 = getRequire();
|
|
39
|
+
let internals;
|
|
40
|
+
try {
|
|
41
|
+
internals = require2("@prisma/internals");
|
|
42
|
+
} catch {
|
|
43
|
+
throw new Error(`Unable to load @prisma/internals`);
|
|
44
|
+
}
|
|
45
|
+
if (typeof internals.getDMMF !== "function") {
|
|
46
|
+
throw new Error(`@prisma/internals.getDMMF not available`);
|
|
47
|
+
}
|
|
48
|
+
return internals.getDMMF({ datamodel });
|
|
49
|
+
}
|
|
28
50
|
function scalarToSchema(scalar) {
|
|
29
51
|
switch (scalar) {
|
|
30
52
|
case "String":
|
|
@@ -83,7 +105,9 @@ function stripWriteFields(model, getSchema, omit) {
|
|
|
83
105
|
if (!schema.properties) return schema;
|
|
84
106
|
const relationFieldNames = new Set(model.fields.filter((f) => f.kind === "object").map((f) => f.name));
|
|
85
107
|
for (const key of Object.keys(schema.properties)) {
|
|
86
|
-
if (omit.has(key) || relationFieldNames.has(key))
|
|
108
|
+
if (omit.has(key) || relationFieldNames.has(key)) {
|
|
109
|
+
delete schema.properties[key];
|
|
110
|
+
}
|
|
87
111
|
}
|
|
88
112
|
if (Array.isArray(schema.required)) {
|
|
89
113
|
schema.required = schema.required.filter((k) => !omit.has(k) && !relationFieldNames.has(k));
|
|
@@ -100,95 +124,19 @@ function listResponseSchema(itemRef) {
|
|
|
100
124
|
return {
|
|
101
125
|
type: "object",
|
|
102
126
|
properties: {
|
|
103
|
-
count: { type: "number"
|
|
104
|
-
hasPreviousPage: { type: "boolean"
|
|
105
|
-
hasNextPage: { type: "boolean"
|
|
106
|
-
pageNumber: { type: "number"
|
|
107
|
-
pageSize: { type: "number"
|
|
108
|
-
totalPages: { type: "number"
|
|
127
|
+
count: { type: "number" },
|
|
128
|
+
hasPreviousPage: { type: "boolean" },
|
|
129
|
+
hasNextPage: { type: "boolean" },
|
|
130
|
+
pageNumber: { type: "number" },
|
|
131
|
+
pageSize: { type: "number" },
|
|
132
|
+
totalPages: { type: "number" },
|
|
109
133
|
items: { type: "array", items: { $ref: itemRef } }
|
|
110
134
|
},
|
|
111
135
|
required: ["count", "hasPreviousPage", "hasNextPage", "pageNumber", "pageSize", "totalPages", "items"]
|
|
112
136
|
};
|
|
113
137
|
}
|
|
114
|
-
function
|
|
115
|
-
|
|
116
|
-
switch (type) {
|
|
117
|
-
case "string":
|
|
118
|
-
return "string";
|
|
119
|
-
case "integer":
|
|
120
|
-
return 0;
|
|
121
|
-
case "number":
|
|
122
|
-
return 0;
|
|
123
|
-
case "boolean":
|
|
124
|
-
return true;
|
|
125
|
-
case "object":
|
|
126
|
-
return {};
|
|
127
|
-
default:
|
|
128
|
-
return null;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
function buildExampleFromSchema(schema, components, depth = 0) {
|
|
132
|
-
if (depth > 2) return void 0;
|
|
133
|
-
if (schema.$ref) {
|
|
134
|
-
const name = String(schema.$ref).split("/").pop() || "";
|
|
135
|
-
const target = components[name];
|
|
136
|
-
if (!target) return void 0;
|
|
137
|
-
return buildExampleFromSchema(target, components, depth + 1);
|
|
138
|
-
}
|
|
139
|
-
if (Array.isArray(schema.allOf) && schema.allOf.length) {
|
|
140
|
-
const merged = {};
|
|
141
|
-
for (const part of schema.allOf) {
|
|
142
|
-
const ex = buildExampleFromSchema(part, components, depth + 1);
|
|
143
|
-
if (ex && typeof ex === "object" && !Array.isArray(ex)) Object.assign(merged, ex);
|
|
144
|
-
}
|
|
145
|
-
return Object.keys(merged).length ? merged : void 0;
|
|
146
|
-
}
|
|
147
|
-
if (schema.type === "array" && schema.items) {
|
|
148
|
-
const item = buildExampleFromSchema(schema.items, components, depth + 1);
|
|
149
|
-
return item === void 0 ? [] : [item];
|
|
150
|
-
}
|
|
151
|
-
if (schema.type === "object" && schema.properties) {
|
|
152
|
-
const obj = {};
|
|
153
|
-
for (const [k, v] of Object.entries(schema.properties)) {
|
|
154
|
-
const ex = buildExampleFromSchema(v, components, depth + 1);
|
|
155
|
-
if (ex !== void 0) obj[k] = ex;
|
|
156
|
-
}
|
|
157
|
-
return obj;
|
|
158
|
-
}
|
|
159
|
-
if (Array.isArray(schema.enum) && schema.enum.length) return schema.enum[0];
|
|
160
|
-
if (typeof schema.type === "string") return exampleForScalarType(schema.type, schema.format);
|
|
161
|
-
return void 0;
|
|
162
|
-
}
|
|
163
|
-
function attachExample(schema, components) {
|
|
164
|
-
const s = JSON.parse(JSON.stringify(schema));
|
|
165
|
-
if (s.example === void 0) {
|
|
166
|
-
const ex = buildExampleFromSchema(s, components);
|
|
167
|
-
if (ex !== void 0) s.example = ex;
|
|
168
|
-
}
|
|
169
|
-
return s;
|
|
170
|
-
}
|
|
171
|
-
async function loadDmmfFromProject() {
|
|
172
|
-
const schemaPath = path.resolve(process.cwd(), "prisma/schema.prisma");
|
|
173
|
-
if (!fs.existsSync(schemaPath)) {
|
|
174
|
-
throw new Error(`Prisma schema not found at: ${schemaPath}`);
|
|
175
|
-
}
|
|
176
|
-
const cmd = `npx prisma generate --schema "${schemaPath}" --print`;
|
|
177
|
-
const stdout = execSync(cmd, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
178
|
-
const firstBrace = stdout.indexOf("{");
|
|
179
|
-
const lastBrace = stdout.lastIndexOf("}");
|
|
180
|
-
if (firstBrace === -1 || lastBrace === -1) {
|
|
181
|
-
throw new Error(`Failed to parse Prisma DMMF from: ${cmd}`);
|
|
182
|
-
}
|
|
183
|
-
const jsonText = stdout.slice(firstBrace, lastBrace + 1);
|
|
184
|
-
const parsed = JSON.parse(jsonText);
|
|
185
|
-
const dmmf = parsed?.dmmf ?? parsed;
|
|
186
|
-
if (!dmmf?.datamodel?.models) {
|
|
187
|
-
throw new Error(`Prisma DMMF not found in prisma output. Got keys: ${Object.keys(parsed || {})}`);
|
|
188
|
-
}
|
|
189
|
-
return dmmf;
|
|
190
|
-
}
|
|
191
|
-
function buildSchemasFromDmmf(dmmf) {
|
|
138
|
+
function buildSchemasFromPrismaDmmf(schemaPath) {
|
|
139
|
+
const dmmf = loadDmmfFromProject(schemaPath);
|
|
192
140
|
const schemas = {};
|
|
193
141
|
const getRefName = (modelName) => `Get${modelName}Response`;
|
|
194
142
|
for (const e of dmmf.datamodel.enums) {
|
|
@@ -242,10 +190,6 @@ function buildSchemasFromDmmf(dmmf) {
|
|
|
242
190
|
type: "https://tools.ietf.org/html/rfc7231#section-6.6.1"
|
|
243
191
|
}
|
|
244
192
|
};
|
|
245
|
-
for (const name of Object.keys(schemas)) {
|
|
246
|
-
if (name.startsWith("Post") && name.endsWith("Request")) schemas[name] = attachExample(schemas[name], schemas);
|
|
247
|
-
if (name.startsWith("Put") && name.endsWith("Request")) schemas[name] = attachExample(schemas[name], schemas);
|
|
248
|
-
}
|
|
249
193
|
return schemas;
|
|
250
194
|
}
|
|
251
195
|
function generateSwaggerConfigJs(schemas) {
|
|
@@ -272,60 +216,15 @@ function generateSwaggerConfigJs(schemas) {
|
|
|
272
216
|
security: [{ [CONFIG.securitySchemeName]: ["openid"] }]
|
|
273
217
|
};
|
|
274
218
|
const fileContent = `const swaggerAutogen = require('swagger-autogen')();
|
|
275
|
-
const fs = require('fs');
|
|
276
|
-
const path = require('node:path');
|
|
277
|
-
|
|
278
|
-
function isPlainObject(v){return v!==null && typeof v==='object' && !Array.isArray(v);}
|
|
279
|
-
|
|
280
|
-
function normalizeSchema(schema){
|
|
281
|
-
if(!isPlainObject(schema)) return schema;
|
|
282
|
-
if(schema.$ref) return schema;
|
|
283
|
-
|
|
284
|
-
if(isPlainObject(schema.type) && typeof schema.type.example === 'string') schema.type = schema.type.example;
|
|
285
|
-
if(isPlainObject(schema.format) && typeof schema.format.example === 'string') schema.format = schema.format.example;
|
|
286
|
-
if(isPlainObject(schema.required) && Array.isArray(schema.required.example)) schema.required = schema.required.example;
|
|
287
|
-
if(isPlainObject(schema.enum) && Array.isArray(schema.enum.example)) schema.enum = schema.enum.example;
|
|
288
|
-
|
|
289
|
-
if(Array.isArray(schema.allOf)) schema.allOf = schema.allOf.map(normalizeSchema);
|
|
290
|
-
if(isPlainObject(schema.items)) schema.items = normalizeSchema(schema.items);
|
|
291
|
-
if(isPlainObject(schema.additionalProperties)) schema.additionalProperties = normalizeSchema(schema.additionalProperties);
|
|
292
|
-
|
|
293
|
-
if(isPlainObject(schema.properties)){
|
|
294
|
-
for(const k of Object.keys(schema.properties)){
|
|
295
|
-
schema.properties[k] = normalizeSchema(schema.properties[k]);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return schema;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
function fixOpenApiFile(openapiPath){
|
|
303
|
-
const abs = path.resolve(process.cwd(), openapiPath);
|
|
304
|
-
if(!fs.existsSync(abs)) return;
|
|
305
|
-
const doc = JSON.parse(fs.readFileSync(abs,'utf8'));
|
|
306
|
-
|
|
307
|
-
if(doc && doc.components && isPlainObject(doc.components.schemas)){
|
|
308
|
-
for(const name of Object.keys(doc.components.schemas)){
|
|
309
|
-
doc.components.schemas[name] = normalizeSchema(doc.components.schemas[name]);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
fs.writeFileSync(abs, JSON.stringify(doc,null,2),'utf8');
|
|
314
|
-
}
|
|
315
|
-
|
|
316
219
|
const docs = ${JSON.stringify(docs, null, 2)};
|
|
317
220
|
const routes = ${JSON.stringify(routes, null, 2)};
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
.then(() => fixOpenApiFile('${ensurePosix(CONFIG.openapiOut)}'))
|
|
321
|
-
.catch((e) => { console.error(e); process.exitCode = 1; });
|
|
322
|
-
`;
|
|
323
|
-
const outPath = path.resolve(CONFIG.projectRoot, CONFIG.outFile);
|
|
324
|
-
fs.writeFileSync(outPath, fileContent, "utf8");
|
|
221
|
+
swaggerAutogen('${ensurePosix(CONFIG.openapiOut)}', routes, docs);`;
|
|
222
|
+
fs.writeFileSync(path.resolve(CONFIG.projectRoot, CONFIG.outFile), fileContent, "utf8");
|
|
325
223
|
}
|
|
326
|
-
async function run(
|
|
327
|
-
const
|
|
328
|
-
const
|
|
224
|
+
async function run(args = []) {
|
|
225
|
+
const schemaFlagIndex = args.findIndex((a) => a === "--schema");
|
|
226
|
+
const schemaPath = schemaFlagIndex >= 0 ? args[schemaFlagIndex + 1] : void 0;
|
|
227
|
+
const schemas = buildSchemasFromPrismaDmmf(schemaPath);
|
|
329
228
|
generateSwaggerConfigJs(schemas);
|
|
330
229
|
}
|
|
331
230
|
|
package/dist/cli.cjs
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
"use strict";
|
|
3
2
|
var __create = Object.create;
|
|
4
3
|
var __defProp = Object.defineProperty;
|
|
@@ -26,8 +25,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
25
|
// src/index.ts
|
|
27
26
|
var import_node_fs = __toESM(require("fs"), 1);
|
|
28
27
|
var import_node_path = __toESM(require("path"), 1);
|
|
28
|
+
var import_node_module = require("module");
|
|
29
29
|
var import_glob = require("glob");
|
|
30
|
-
var
|
|
30
|
+
var import_meta = {};
|
|
31
31
|
var CONFIG = {
|
|
32
32
|
projectRoot: process.cwd(),
|
|
33
33
|
controllersGlob: "./src/web/api/controllers/**/*.ts",
|
|
@@ -50,6 +50,28 @@ function pluralize(name) {
|
|
|
50
50
|
if (name.endsWith("s")) return `${name}es`;
|
|
51
51
|
return `${name}s`;
|
|
52
52
|
}
|
|
53
|
+
function getRequire() {
|
|
54
|
+
const base = typeof __filename !== "undefined" ? __filename : import_meta.url;
|
|
55
|
+
return (0, import_node_module.createRequire)(base);
|
|
56
|
+
}
|
|
57
|
+
function loadDmmfFromProject(schemaPath) {
|
|
58
|
+
const resolvedSchemaPath = schemaPath ? import_node_path.default.resolve(process.cwd(), schemaPath) : import_node_path.default.resolve(process.cwd(), "prisma/schema.prisma");
|
|
59
|
+
if (!import_node_fs.default.existsSync(resolvedSchemaPath)) {
|
|
60
|
+
throw new Error(`Prisma schema not found at ${resolvedSchemaPath}`);
|
|
61
|
+
}
|
|
62
|
+
const datamodel = import_node_fs.default.readFileSync(resolvedSchemaPath, "utf8");
|
|
63
|
+
const require2 = getRequire();
|
|
64
|
+
let internals;
|
|
65
|
+
try {
|
|
66
|
+
internals = require2("@prisma/internals");
|
|
67
|
+
} catch {
|
|
68
|
+
throw new Error(`Unable to load @prisma/internals`);
|
|
69
|
+
}
|
|
70
|
+
if (typeof internals.getDMMF !== "function") {
|
|
71
|
+
throw new Error(`@prisma/internals.getDMMF not available`);
|
|
72
|
+
}
|
|
73
|
+
return internals.getDMMF({ datamodel });
|
|
74
|
+
}
|
|
53
75
|
function scalarToSchema(scalar) {
|
|
54
76
|
switch (scalar) {
|
|
55
77
|
case "String":
|
|
@@ -108,7 +130,9 @@ function stripWriteFields(model, getSchema, omit) {
|
|
|
108
130
|
if (!schema.properties) return schema;
|
|
109
131
|
const relationFieldNames = new Set(model.fields.filter((f) => f.kind === "object").map((f) => f.name));
|
|
110
132
|
for (const key of Object.keys(schema.properties)) {
|
|
111
|
-
if (omit.has(key) || relationFieldNames.has(key))
|
|
133
|
+
if (omit.has(key) || relationFieldNames.has(key)) {
|
|
134
|
+
delete schema.properties[key];
|
|
135
|
+
}
|
|
112
136
|
}
|
|
113
137
|
if (Array.isArray(schema.required)) {
|
|
114
138
|
schema.required = schema.required.filter((k) => !omit.has(k) && !relationFieldNames.has(k));
|
|
@@ -125,95 +149,19 @@ function listResponseSchema(itemRef) {
|
|
|
125
149
|
return {
|
|
126
150
|
type: "object",
|
|
127
151
|
properties: {
|
|
128
|
-
count: { type: "number"
|
|
129
|
-
hasPreviousPage: { type: "boolean"
|
|
130
|
-
hasNextPage: { type: "boolean"
|
|
131
|
-
pageNumber: { type: "number"
|
|
132
|
-
pageSize: { type: "number"
|
|
133
|
-
totalPages: { type: "number"
|
|
152
|
+
count: { type: "number" },
|
|
153
|
+
hasPreviousPage: { type: "boolean" },
|
|
154
|
+
hasNextPage: { type: "boolean" },
|
|
155
|
+
pageNumber: { type: "number" },
|
|
156
|
+
pageSize: { type: "number" },
|
|
157
|
+
totalPages: { type: "number" },
|
|
134
158
|
items: { type: "array", items: { $ref: itemRef } }
|
|
135
159
|
},
|
|
136
160
|
required: ["count", "hasPreviousPage", "hasNextPage", "pageNumber", "pageSize", "totalPages", "items"]
|
|
137
161
|
};
|
|
138
162
|
}
|
|
139
|
-
function
|
|
140
|
-
|
|
141
|
-
switch (type) {
|
|
142
|
-
case "string":
|
|
143
|
-
return "string";
|
|
144
|
-
case "integer":
|
|
145
|
-
return 0;
|
|
146
|
-
case "number":
|
|
147
|
-
return 0;
|
|
148
|
-
case "boolean":
|
|
149
|
-
return true;
|
|
150
|
-
case "object":
|
|
151
|
-
return {};
|
|
152
|
-
default:
|
|
153
|
-
return null;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
function buildExampleFromSchema(schema, components, depth = 0) {
|
|
157
|
-
if (depth > 2) return void 0;
|
|
158
|
-
if (schema.$ref) {
|
|
159
|
-
const name = String(schema.$ref).split("/").pop() || "";
|
|
160
|
-
const target = components[name];
|
|
161
|
-
if (!target) return void 0;
|
|
162
|
-
return buildExampleFromSchema(target, components, depth + 1);
|
|
163
|
-
}
|
|
164
|
-
if (Array.isArray(schema.allOf) && schema.allOf.length) {
|
|
165
|
-
const merged = {};
|
|
166
|
-
for (const part of schema.allOf) {
|
|
167
|
-
const ex = buildExampleFromSchema(part, components, depth + 1);
|
|
168
|
-
if (ex && typeof ex === "object" && !Array.isArray(ex)) Object.assign(merged, ex);
|
|
169
|
-
}
|
|
170
|
-
return Object.keys(merged).length ? merged : void 0;
|
|
171
|
-
}
|
|
172
|
-
if (schema.type === "array" && schema.items) {
|
|
173
|
-
const item = buildExampleFromSchema(schema.items, components, depth + 1);
|
|
174
|
-
return item === void 0 ? [] : [item];
|
|
175
|
-
}
|
|
176
|
-
if (schema.type === "object" && schema.properties) {
|
|
177
|
-
const obj = {};
|
|
178
|
-
for (const [k, v] of Object.entries(schema.properties)) {
|
|
179
|
-
const ex = buildExampleFromSchema(v, components, depth + 1);
|
|
180
|
-
if (ex !== void 0) obj[k] = ex;
|
|
181
|
-
}
|
|
182
|
-
return obj;
|
|
183
|
-
}
|
|
184
|
-
if (Array.isArray(schema.enum) && schema.enum.length) return schema.enum[0];
|
|
185
|
-
if (typeof schema.type === "string") return exampleForScalarType(schema.type, schema.format);
|
|
186
|
-
return void 0;
|
|
187
|
-
}
|
|
188
|
-
function attachExample(schema, components) {
|
|
189
|
-
const s = JSON.parse(JSON.stringify(schema));
|
|
190
|
-
if (s.example === void 0) {
|
|
191
|
-
const ex = buildExampleFromSchema(s, components);
|
|
192
|
-
if (ex !== void 0) s.example = ex;
|
|
193
|
-
}
|
|
194
|
-
return s;
|
|
195
|
-
}
|
|
196
|
-
async function loadDmmfFromProject() {
|
|
197
|
-
const schemaPath = import_node_path.default.resolve(process.cwd(), "prisma/schema.prisma");
|
|
198
|
-
if (!import_node_fs.default.existsSync(schemaPath)) {
|
|
199
|
-
throw new Error(`Prisma schema not found at: ${schemaPath}`);
|
|
200
|
-
}
|
|
201
|
-
const cmd = `npx prisma generate --schema "${schemaPath}" --print`;
|
|
202
|
-
const stdout = (0, import_node_child_process.execSync)(cmd, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
203
|
-
const firstBrace = stdout.indexOf("{");
|
|
204
|
-
const lastBrace = stdout.lastIndexOf("}");
|
|
205
|
-
if (firstBrace === -1 || lastBrace === -1) {
|
|
206
|
-
throw new Error(`Failed to parse Prisma DMMF from: ${cmd}`);
|
|
207
|
-
}
|
|
208
|
-
const jsonText = stdout.slice(firstBrace, lastBrace + 1);
|
|
209
|
-
const parsed = JSON.parse(jsonText);
|
|
210
|
-
const dmmf = parsed?.dmmf ?? parsed;
|
|
211
|
-
if (!dmmf?.datamodel?.models) {
|
|
212
|
-
throw new Error(`Prisma DMMF not found in prisma output. Got keys: ${Object.keys(parsed || {})}`);
|
|
213
|
-
}
|
|
214
|
-
return dmmf;
|
|
215
|
-
}
|
|
216
|
-
function buildSchemasFromDmmf(dmmf) {
|
|
163
|
+
function buildSchemasFromPrismaDmmf(schemaPath) {
|
|
164
|
+
const dmmf = loadDmmfFromProject(schemaPath);
|
|
217
165
|
const schemas = {};
|
|
218
166
|
const getRefName = (modelName) => `Get${modelName}Response`;
|
|
219
167
|
for (const e of dmmf.datamodel.enums) {
|
|
@@ -267,10 +215,6 @@ function buildSchemasFromDmmf(dmmf) {
|
|
|
267
215
|
type: "https://tools.ietf.org/html/rfc7231#section-6.6.1"
|
|
268
216
|
}
|
|
269
217
|
};
|
|
270
|
-
for (const name of Object.keys(schemas)) {
|
|
271
|
-
if (name.startsWith("Post") && name.endsWith("Request")) schemas[name] = attachExample(schemas[name], schemas);
|
|
272
|
-
if (name.startsWith("Put") && name.endsWith("Request")) schemas[name] = attachExample(schemas[name], schemas);
|
|
273
|
-
}
|
|
274
218
|
return schemas;
|
|
275
219
|
}
|
|
276
220
|
function generateSwaggerConfigJs(schemas) {
|
|
@@ -297,60 +241,15 @@ function generateSwaggerConfigJs(schemas) {
|
|
|
297
241
|
security: [{ [CONFIG.securitySchemeName]: ["openid"] }]
|
|
298
242
|
};
|
|
299
243
|
const fileContent = `const swaggerAutogen = require('swagger-autogen')();
|
|
300
|
-
const fs = require('fs');
|
|
301
|
-
const path = require('node:path');
|
|
302
|
-
|
|
303
|
-
function isPlainObject(v){return v!==null && typeof v==='object' && !Array.isArray(v);}
|
|
304
|
-
|
|
305
|
-
function normalizeSchema(schema){
|
|
306
|
-
if(!isPlainObject(schema)) return schema;
|
|
307
|
-
if(schema.$ref) return schema;
|
|
308
|
-
|
|
309
|
-
if(isPlainObject(schema.type) && typeof schema.type.example === 'string') schema.type = schema.type.example;
|
|
310
|
-
if(isPlainObject(schema.format) && typeof schema.format.example === 'string') schema.format = schema.format.example;
|
|
311
|
-
if(isPlainObject(schema.required) && Array.isArray(schema.required.example)) schema.required = schema.required.example;
|
|
312
|
-
if(isPlainObject(schema.enum) && Array.isArray(schema.enum.example)) schema.enum = schema.enum.example;
|
|
313
|
-
|
|
314
|
-
if(Array.isArray(schema.allOf)) schema.allOf = schema.allOf.map(normalizeSchema);
|
|
315
|
-
if(isPlainObject(schema.items)) schema.items = normalizeSchema(schema.items);
|
|
316
|
-
if(isPlainObject(schema.additionalProperties)) schema.additionalProperties = normalizeSchema(schema.additionalProperties);
|
|
317
|
-
|
|
318
|
-
if(isPlainObject(schema.properties)){
|
|
319
|
-
for(const k of Object.keys(schema.properties)){
|
|
320
|
-
schema.properties[k] = normalizeSchema(schema.properties[k]);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
return schema;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
function fixOpenApiFile(openapiPath){
|
|
328
|
-
const abs = path.resolve(process.cwd(), openapiPath);
|
|
329
|
-
if(!fs.existsSync(abs)) return;
|
|
330
|
-
const doc = JSON.parse(fs.readFileSync(abs,'utf8'));
|
|
331
|
-
|
|
332
|
-
if(doc && doc.components && isPlainObject(doc.components.schemas)){
|
|
333
|
-
for(const name of Object.keys(doc.components.schemas)){
|
|
334
|
-
doc.components.schemas[name] = normalizeSchema(doc.components.schemas[name]);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
fs.writeFileSync(abs, JSON.stringify(doc,null,2),'utf8');
|
|
339
|
-
}
|
|
340
|
-
|
|
341
244
|
const docs = ${JSON.stringify(docs, null, 2)};
|
|
342
245
|
const routes = ${JSON.stringify(routes, null, 2)};
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
.then(() => fixOpenApiFile('${ensurePosix(CONFIG.openapiOut)}'))
|
|
346
|
-
.catch((e) => { console.error(e); process.exitCode = 1; });
|
|
347
|
-
`;
|
|
348
|
-
const outPath = import_node_path.default.resolve(CONFIG.projectRoot, CONFIG.outFile);
|
|
349
|
-
import_node_fs.default.writeFileSync(outPath, fileContent, "utf8");
|
|
246
|
+
swaggerAutogen('${ensurePosix(CONFIG.openapiOut)}', routes, docs);`;
|
|
247
|
+
import_node_fs.default.writeFileSync(import_node_path.default.resolve(CONFIG.projectRoot, CONFIG.outFile), fileContent, "utf8");
|
|
350
248
|
}
|
|
351
|
-
async function run(
|
|
352
|
-
const
|
|
353
|
-
const
|
|
249
|
+
async function run(args = []) {
|
|
250
|
+
const schemaFlagIndex = args.findIndex((a) => a === "--schema");
|
|
251
|
+
const schemaPath = schemaFlagIndex >= 0 ? args[schemaFlagIndex + 1] : void 0;
|
|
252
|
+
const schemas = buildSchemasFromPrismaDmmf(schemaPath);
|
|
354
253
|
generateSwaggerConfigJs(schemas);
|
|
355
254
|
}
|
|
356
255
|
|
package/dist/cli.d.cts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
export { }
|
package/dist/cli.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
export { }
|
package/dist/cli.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -35,8 +35,9 @@ __export(index_exports, {
|
|
|
35
35
|
module.exports = __toCommonJS(index_exports);
|
|
36
36
|
var import_node_fs = __toESM(require("fs"), 1);
|
|
37
37
|
var import_node_path = __toESM(require("path"), 1);
|
|
38
|
+
var import_node_module = require("module");
|
|
38
39
|
var import_glob = require("glob");
|
|
39
|
-
var
|
|
40
|
+
var import_meta = {};
|
|
40
41
|
var CONFIG = {
|
|
41
42
|
projectRoot: process.cwd(),
|
|
42
43
|
controllersGlob: "./src/web/api/controllers/**/*.ts",
|
|
@@ -59,6 +60,28 @@ function pluralize(name) {
|
|
|
59
60
|
if (name.endsWith("s")) return `${name}es`;
|
|
60
61
|
return `${name}s`;
|
|
61
62
|
}
|
|
63
|
+
function getRequire() {
|
|
64
|
+
const base = typeof __filename !== "undefined" ? __filename : import_meta.url;
|
|
65
|
+
return (0, import_node_module.createRequire)(base);
|
|
66
|
+
}
|
|
67
|
+
function loadDmmfFromProject(schemaPath) {
|
|
68
|
+
const resolvedSchemaPath = schemaPath ? import_node_path.default.resolve(process.cwd(), schemaPath) : import_node_path.default.resolve(process.cwd(), "prisma/schema.prisma");
|
|
69
|
+
if (!import_node_fs.default.existsSync(resolvedSchemaPath)) {
|
|
70
|
+
throw new Error(`Prisma schema not found at ${resolvedSchemaPath}`);
|
|
71
|
+
}
|
|
72
|
+
const datamodel = import_node_fs.default.readFileSync(resolvedSchemaPath, "utf8");
|
|
73
|
+
const require2 = getRequire();
|
|
74
|
+
let internals;
|
|
75
|
+
try {
|
|
76
|
+
internals = require2("@prisma/internals");
|
|
77
|
+
} catch {
|
|
78
|
+
throw new Error(`Unable to load @prisma/internals`);
|
|
79
|
+
}
|
|
80
|
+
if (typeof internals.getDMMF !== "function") {
|
|
81
|
+
throw new Error(`@prisma/internals.getDMMF not available`);
|
|
82
|
+
}
|
|
83
|
+
return internals.getDMMF({ datamodel });
|
|
84
|
+
}
|
|
62
85
|
function scalarToSchema(scalar) {
|
|
63
86
|
switch (scalar) {
|
|
64
87
|
case "String":
|
|
@@ -117,7 +140,9 @@ function stripWriteFields(model, getSchema, omit) {
|
|
|
117
140
|
if (!schema.properties) return schema;
|
|
118
141
|
const relationFieldNames = new Set(model.fields.filter((f) => f.kind === "object").map((f) => f.name));
|
|
119
142
|
for (const key of Object.keys(schema.properties)) {
|
|
120
|
-
if (omit.has(key) || relationFieldNames.has(key))
|
|
143
|
+
if (omit.has(key) || relationFieldNames.has(key)) {
|
|
144
|
+
delete schema.properties[key];
|
|
145
|
+
}
|
|
121
146
|
}
|
|
122
147
|
if (Array.isArray(schema.required)) {
|
|
123
148
|
schema.required = schema.required.filter((k) => !omit.has(k) && !relationFieldNames.has(k));
|
|
@@ -134,95 +159,19 @@ function listResponseSchema(itemRef) {
|
|
|
134
159
|
return {
|
|
135
160
|
type: "object",
|
|
136
161
|
properties: {
|
|
137
|
-
count: { type: "number"
|
|
138
|
-
hasPreviousPage: { type: "boolean"
|
|
139
|
-
hasNextPage: { type: "boolean"
|
|
140
|
-
pageNumber: { type: "number"
|
|
141
|
-
pageSize: { type: "number"
|
|
142
|
-
totalPages: { type: "number"
|
|
162
|
+
count: { type: "number" },
|
|
163
|
+
hasPreviousPage: { type: "boolean" },
|
|
164
|
+
hasNextPage: { type: "boolean" },
|
|
165
|
+
pageNumber: { type: "number" },
|
|
166
|
+
pageSize: { type: "number" },
|
|
167
|
+
totalPages: { type: "number" },
|
|
143
168
|
items: { type: "array", items: { $ref: itemRef } }
|
|
144
169
|
},
|
|
145
170
|
required: ["count", "hasPreviousPage", "hasNextPage", "pageNumber", "pageSize", "totalPages", "items"]
|
|
146
171
|
};
|
|
147
172
|
}
|
|
148
|
-
function
|
|
149
|
-
|
|
150
|
-
switch (type) {
|
|
151
|
-
case "string":
|
|
152
|
-
return "string";
|
|
153
|
-
case "integer":
|
|
154
|
-
return 0;
|
|
155
|
-
case "number":
|
|
156
|
-
return 0;
|
|
157
|
-
case "boolean":
|
|
158
|
-
return true;
|
|
159
|
-
case "object":
|
|
160
|
-
return {};
|
|
161
|
-
default:
|
|
162
|
-
return null;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
function buildExampleFromSchema(schema, components, depth = 0) {
|
|
166
|
-
if (depth > 2) return void 0;
|
|
167
|
-
if (schema.$ref) {
|
|
168
|
-
const name = String(schema.$ref).split("/").pop() || "";
|
|
169
|
-
const target = components[name];
|
|
170
|
-
if (!target) return void 0;
|
|
171
|
-
return buildExampleFromSchema(target, components, depth + 1);
|
|
172
|
-
}
|
|
173
|
-
if (Array.isArray(schema.allOf) && schema.allOf.length) {
|
|
174
|
-
const merged = {};
|
|
175
|
-
for (const part of schema.allOf) {
|
|
176
|
-
const ex = buildExampleFromSchema(part, components, depth + 1);
|
|
177
|
-
if (ex && typeof ex === "object" && !Array.isArray(ex)) Object.assign(merged, ex);
|
|
178
|
-
}
|
|
179
|
-
return Object.keys(merged).length ? merged : void 0;
|
|
180
|
-
}
|
|
181
|
-
if (schema.type === "array" && schema.items) {
|
|
182
|
-
const item = buildExampleFromSchema(schema.items, components, depth + 1);
|
|
183
|
-
return item === void 0 ? [] : [item];
|
|
184
|
-
}
|
|
185
|
-
if (schema.type === "object" && schema.properties) {
|
|
186
|
-
const obj = {};
|
|
187
|
-
for (const [k, v] of Object.entries(schema.properties)) {
|
|
188
|
-
const ex = buildExampleFromSchema(v, components, depth + 1);
|
|
189
|
-
if (ex !== void 0) obj[k] = ex;
|
|
190
|
-
}
|
|
191
|
-
return obj;
|
|
192
|
-
}
|
|
193
|
-
if (Array.isArray(schema.enum) && schema.enum.length) return schema.enum[0];
|
|
194
|
-
if (typeof schema.type === "string") return exampleForScalarType(schema.type, schema.format);
|
|
195
|
-
return void 0;
|
|
196
|
-
}
|
|
197
|
-
function attachExample(schema, components) {
|
|
198
|
-
const s = JSON.parse(JSON.stringify(schema));
|
|
199
|
-
if (s.example === void 0) {
|
|
200
|
-
const ex = buildExampleFromSchema(s, components);
|
|
201
|
-
if (ex !== void 0) s.example = ex;
|
|
202
|
-
}
|
|
203
|
-
return s;
|
|
204
|
-
}
|
|
205
|
-
async function loadDmmfFromProject() {
|
|
206
|
-
const schemaPath = import_node_path.default.resolve(process.cwd(), "prisma/schema.prisma");
|
|
207
|
-
if (!import_node_fs.default.existsSync(schemaPath)) {
|
|
208
|
-
throw new Error(`Prisma schema not found at: ${schemaPath}`);
|
|
209
|
-
}
|
|
210
|
-
const cmd = `npx prisma generate --schema "${schemaPath}" --print`;
|
|
211
|
-
const stdout = (0, import_node_child_process.execSync)(cmd, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
212
|
-
const firstBrace = stdout.indexOf("{");
|
|
213
|
-
const lastBrace = stdout.lastIndexOf("}");
|
|
214
|
-
if (firstBrace === -1 || lastBrace === -1) {
|
|
215
|
-
throw new Error(`Failed to parse Prisma DMMF from: ${cmd}`);
|
|
216
|
-
}
|
|
217
|
-
const jsonText = stdout.slice(firstBrace, lastBrace + 1);
|
|
218
|
-
const parsed = JSON.parse(jsonText);
|
|
219
|
-
const dmmf = parsed?.dmmf ?? parsed;
|
|
220
|
-
if (!dmmf?.datamodel?.models) {
|
|
221
|
-
throw new Error(`Prisma DMMF not found in prisma output. Got keys: ${Object.keys(parsed || {})}`);
|
|
222
|
-
}
|
|
223
|
-
return dmmf;
|
|
224
|
-
}
|
|
225
|
-
function buildSchemasFromDmmf(dmmf) {
|
|
173
|
+
function buildSchemasFromPrismaDmmf(schemaPath) {
|
|
174
|
+
const dmmf = loadDmmfFromProject(schemaPath);
|
|
226
175
|
const schemas = {};
|
|
227
176
|
const getRefName = (modelName) => `Get${modelName}Response`;
|
|
228
177
|
for (const e of dmmf.datamodel.enums) {
|
|
@@ -276,10 +225,6 @@ function buildSchemasFromDmmf(dmmf) {
|
|
|
276
225
|
type: "https://tools.ietf.org/html/rfc7231#section-6.6.1"
|
|
277
226
|
}
|
|
278
227
|
};
|
|
279
|
-
for (const name of Object.keys(schemas)) {
|
|
280
|
-
if (name.startsWith("Post") && name.endsWith("Request")) schemas[name] = attachExample(schemas[name], schemas);
|
|
281
|
-
if (name.startsWith("Put") && name.endsWith("Request")) schemas[name] = attachExample(schemas[name], schemas);
|
|
282
|
-
}
|
|
283
228
|
return schemas;
|
|
284
229
|
}
|
|
285
230
|
function generateSwaggerConfigJs(schemas) {
|
|
@@ -306,60 +251,15 @@ function generateSwaggerConfigJs(schemas) {
|
|
|
306
251
|
security: [{ [CONFIG.securitySchemeName]: ["openid"] }]
|
|
307
252
|
};
|
|
308
253
|
const fileContent = `const swaggerAutogen = require('swagger-autogen')();
|
|
309
|
-
const fs = require('fs');
|
|
310
|
-
const path = require('node:path');
|
|
311
|
-
|
|
312
|
-
function isPlainObject(v){return v!==null && typeof v==='object' && !Array.isArray(v);}
|
|
313
|
-
|
|
314
|
-
function normalizeSchema(schema){
|
|
315
|
-
if(!isPlainObject(schema)) return schema;
|
|
316
|
-
if(schema.$ref) return schema;
|
|
317
|
-
|
|
318
|
-
if(isPlainObject(schema.type) && typeof schema.type.example === 'string') schema.type = schema.type.example;
|
|
319
|
-
if(isPlainObject(schema.format) && typeof schema.format.example === 'string') schema.format = schema.format.example;
|
|
320
|
-
if(isPlainObject(schema.required) && Array.isArray(schema.required.example)) schema.required = schema.required.example;
|
|
321
|
-
if(isPlainObject(schema.enum) && Array.isArray(schema.enum.example)) schema.enum = schema.enum.example;
|
|
322
|
-
|
|
323
|
-
if(Array.isArray(schema.allOf)) schema.allOf = schema.allOf.map(normalizeSchema);
|
|
324
|
-
if(isPlainObject(schema.items)) schema.items = normalizeSchema(schema.items);
|
|
325
|
-
if(isPlainObject(schema.additionalProperties)) schema.additionalProperties = normalizeSchema(schema.additionalProperties);
|
|
326
|
-
|
|
327
|
-
if(isPlainObject(schema.properties)){
|
|
328
|
-
for(const k of Object.keys(schema.properties)){
|
|
329
|
-
schema.properties[k] = normalizeSchema(schema.properties[k]);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
return schema;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
function fixOpenApiFile(openapiPath){
|
|
337
|
-
const abs = path.resolve(process.cwd(), openapiPath);
|
|
338
|
-
if(!fs.existsSync(abs)) return;
|
|
339
|
-
const doc = JSON.parse(fs.readFileSync(abs,'utf8'));
|
|
340
|
-
|
|
341
|
-
if(doc && doc.components && isPlainObject(doc.components.schemas)){
|
|
342
|
-
for(const name of Object.keys(doc.components.schemas)){
|
|
343
|
-
doc.components.schemas[name] = normalizeSchema(doc.components.schemas[name]);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
fs.writeFileSync(abs, JSON.stringify(doc,null,2),'utf8');
|
|
348
|
-
}
|
|
349
|
-
|
|
350
254
|
const docs = ${JSON.stringify(docs, null, 2)};
|
|
351
255
|
const routes = ${JSON.stringify(routes, null, 2)};
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
.then(() => fixOpenApiFile('${ensurePosix(CONFIG.openapiOut)}'))
|
|
355
|
-
.catch((e) => { console.error(e); process.exitCode = 1; });
|
|
356
|
-
`;
|
|
357
|
-
const outPath = import_node_path.default.resolve(CONFIG.projectRoot, CONFIG.outFile);
|
|
358
|
-
import_node_fs.default.writeFileSync(outPath, fileContent, "utf8");
|
|
256
|
+
swaggerAutogen('${ensurePosix(CONFIG.openapiOut)}', routes, docs);`;
|
|
257
|
+
import_node_fs.default.writeFileSync(import_node_path.default.resolve(CONFIG.projectRoot, CONFIG.outFile), fileContent, "utf8");
|
|
359
258
|
}
|
|
360
|
-
async function run(
|
|
361
|
-
const
|
|
362
|
-
const
|
|
259
|
+
async function run(args = []) {
|
|
260
|
+
const schemaFlagIndex = args.findIndex((a) => a === "--schema");
|
|
261
|
+
const schemaPath = schemaFlagIndex >= 0 ? args[schemaFlagIndex + 1] : void 0;
|
|
262
|
+
const schemas = buildSchemasFromPrismaDmmf(schemaPath);
|
|
363
263
|
generateSwaggerConfigJs(schemas);
|
|
364
264
|
}
|
|
365
265
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/index.d.cts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prisma-swagger-autogen",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Generate swagger-autogen config + Prisma DMMF schemas and fix swagger-autogen output.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Joyce Marvin Rafflenbeul",
|
|
@@ -42,9 +42,11 @@
|
|
|
42
42
|
"build": "tsup src/index.ts src/cli.ts --format esm,cjs --dts --out-dir dist --tsconfig tsconfig.json",
|
|
43
43
|
"prepublishOnly": "npm run build"
|
|
44
44
|
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@prisma/internals": "^6.0.0"
|
|
47
|
+
},
|
|
45
48
|
"peerDependencies": {
|
|
46
49
|
"@prisma/client": ">=4",
|
|
47
|
-
"prisma": ">=4",
|
|
48
50
|
"glob": ">=10",
|
|
49
51
|
"swagger-autogen": ">=2"
|
|
50
52
|
},
|