swaggerjsontoapidocs 1.5.2 → 1.7.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/README.md +28 -7
- package/bin/index.js +13 -3
- package/bin/jsonToApiDocs.js +70 -64
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,6 +40,7 @@ MSYS_NO_PATHCONV=1 npx swaggerjsontoapidocs [options]
|
|
|
40
40
|
- `--bp <path>`: Base path to remove from endpoints (e.g., `/api/`).
|
|
41
41
|
- `-o, --output <path>`: Path to the output folder destination.
|
|
42
42
|
- `--skip-folder`: Generates flat files instead of nested folders.
|
|
43
|
+
- `--fnl, --function-name-lowercase`: Force all function names to lowercase for consistency.
|
|
43
44
|
|
|
44
45
|
### Example Usage
|
|
45
46
|
|
|
@@ -203,17 +204,37 @@ In this example:
|
|
|
203
204
|
|
|
204
205
|
```bash
|
|
205
206
|
├── api_docs
|
|
206
|
-
│ ├──
|
|
207
|
-
│ └──
|
|
207
|
+
│ ├── products
|
|
208
|
+
│ └── products.ts
|
|
208
209
|
```
|
|
209
210
|
|
|
210
211
|
```typescript
|
|
211
|
-
//
|
|
212
|
-
export const Users = () => `Users`;
|
|
212
|
+
// products.ts
|
|
213
213
|
/**
|
|
214
|
+
* @endpoint /api/products
|
|
215
|
+
* @methods GET - POST
|
|
216
|
+
*/
|
|
217
|
+
export const products = () => `products`;
|
|
218
|
+
/**
|
|
219
|
+
* @endpoint /api/products/{id}/category/{categoryId}
|
|
220
|
+
* @methods GET - PUT - DELETE
|
|
221
|
+
* @param id
|
|
222
|
+
*/
|
|
223
|
+
export const products_id_category_categoryId = (id: any) =>
|
|
224
|
+
`products/${id}/category/${categoryId}`;
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Result --function-name-lowercase
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
// products.ts
|
|
231
|
+
/**
|
|
232
|
+
* @endpoint /api/products/{id}/category/{categoryId}
|
|
233
|
+
* @methods GET - PUT - DELETE
|
|
214
234
|
* @param id
|
|
215
235
|
*/
|
|
216
|
-
export const
|
|
236
|
+
export const products_id_category_categoryid = (id: any) =>
|
|
237
|
+
`products/${id}/category/${categoryId}`;
|
|
217
238
|
```
|
|
218
239
|
|
|
219
240
|
### Advanced Usage
|
|
@@ -232,8 +253,8 @@ In this example:
|
|
|
232
253
|
```bash
|
|
233
254
|
docs
|
|
234
255
|
└── api_docs
|
|
235
|
-
├──
|
|
236
|
-
└──
|
|
256
|
+
├── products.ts
|
|
257
|
+
└── weatherforecast.ts
|
|
237
258
|
```
|
|
238
259
|
|
|
239
260
|
## License
|
package/bin/index.js
CHANGED
|
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
const
|
|
7
|
+
const node_fs_1 = require("node:fs");
|
|
8
8
|
const chalk_1 = __importDefault(require("chalk"));
|
|
9
9
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
10
|
const yargs_1 = __importDefault(require("yargs"));
|
|
@@ -33,6 +33,12 @@ const argv = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
|
|
|
33
33
|
describe: "Flat files",
|
|
34
34
|
default: false,
|
|
35
35
|
},
|
|
36
|
+
"function-name-lowercase": {
|
|
37
|
+
alias: "fnl",
|
|
38
|
+
type: "boolean",
|
|
39
|
+
describe: "Force all function names to lowercase for consistency",
|
|
40
|
+
default: false,
|
|
41
|
+
},
|
|
36
42
|
})
|
|
37
43
|
.version()
|
|
38
44
|
.help()
|
|
@@ -45,6 +51,7 @@ async function main() {
|
|
|
45
51
|
const basePath = argv.bp;
|
|
46
52
|
const skipFolder = argv.skipFolder;
|
|
47
53
|
const output = argv.output;
|
|
54
|
+
const functionNameLowercase = argv.functionNameLowercase;
|
|
48
55
|
console.log(chalk_1.default.blue(`Swagger Path: ${swaggerPath}`));
|
|
49
56
|
console.log(chalk_1.default.blue(`Basepath: ${basePath}`));
|
|
50
57
|
if (skipFolder) {
|
|
@@ -53,13 +60,16 @@ async function main() {
|
|
|
53
60
|
if (output) {
|
|
54
61
|
console.log(chalk_1.default.blue(`output folder: ${output}api_docs`));
|
|
55
62
|
}
|
|
56
|
-
|
|
63
|
+
if (functionNameLowercase) {
|
|
64
|
+
console.log(chalk_1.default.blue(`functionNameLowercase: ${functionNameLowercase}`));
|
|
65
|
+
}
|
|
66
|
+
(0, node_fs_1.writeFile)(node_path_1.default.join(__dirname, "config.json"), `{"BASEPATH": "${basePath.trim()}","PATH": "${swaggerPath.trim()}"}`, "utf8", (err) => {
|
|
57
67
|
if (err) {
|
|
58
68
|
console.error(chalk_1.default.red("Error writing the configuration file:"), err);
|
|
59
69
|
}
|
|
60
70
|
else {
|
|
61
71
|
console.log(chalk_1.default.green("Configuration file created successfully."));
|
|
62
|
-
const params = { skipFolder, output };
|
|
72
|
+
const params = { skipFolder, output, functionNameLowercase };
|
|
63
73
|
(0, jsonToApiDocs_1.initScript)(params);
|
|
64
74
|
}
|
|
65
75
|
});
|
package/bin/jsonToApiDocs.js
CHANGED
|
@@ -4,18 +4,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.initScript = initScript;
|
|
7
|
-
const promises_1 = require("fs/promises");
|
|
7
|
+
const promises_1 = require("node:fs/promises");
|
|
8
8
|
const fs_extra_1 = require("fs-extra");
|
|
9
|
-
const
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
10
|
const chalk_1 = __importDefault(require("chalk"));
|
|
11
11
|
const prettier_1 = require("prettier");
|
|
12
|
-
const os_1 = __importDefault(require("os"));
|
|
13
|
-
const child_process_1 = require("child_process");
|
|
14
12
|
const folderName = "api_docs";
|
|
15
|
-
const mainFolderOutPut = (0,
|
|
13
|
+
const mainFolderOutPut = (0, node_path_1.join)(__dirname, folderName);
|
|
16
14
|
let urlSwaggerJson = "";
|
|
17
15
|
let basepath = "";
|
|
18
|
-
let paramsConfig = {
|
|
16
|
+
let paramsConfig = {
|
|
17
|
+
skipFolder: false,
|
|
18
|
+
output: undefined,
|
|
19
|
+
functionNameLowercase: false,
|
|
20
|
+
};
|
|
19
21
|
async function cleanFolderOutPut() {
|
|
20
22
|
await (0, promises_1.rm)(mainFolderOutPut, {
|
|
21
23
|
recursive: true,
|
|
@@ -25,7 +27,7 @@ async function cleanFolderOutPut() {
|
|
|
25
27
|
async function initScript(params) {
|
|
26
28
|
paramsConfig = params;
|
|
27
29
|
await cleanFolderOutPut();
|
|
28
|
-
const raw = await (0, promises_1.readFile)((0,
|
|
30
|
+
const raw = await (0, promises_1.readFile)((0, node_path_1.join)(__dirname, "config.json"), "utf8");
|
|
29
31
|
const config = await JSON.parse(raw);
|
|
30
32
|
urlSwaggerJson = config.PATH;
|
|
31
33
|
basepath = config.BASEPATH;
|
|
@@ -33,7 +35,7 @@ async function initScript(params) {
|
|
|
33
35
|
const response = await fetch(urlSwaggerJson);
|
|
34
36
|
const data = await response.json();
|
|
35
37
|
try {
|
|
36
|
-
await (0, promises_1.writeFile)((0,
|
|
38
|
+
await (0, promises_1.writeFile)((0, node_path_1.join)(__dirname, "paths.json"), `${JSON.stringify(data, null, 2)}`, "utf8");
|
|
37
39
|
await filterPathsObject();
|
|
38
40
|
}
|
|
39
41
|
catch (err) {
|
|
@@ -53,17 +55,23 @@ async function initScript(params) {
|
|
|
53
55
|
}
|
|
54
56
|
}
|
|
55
57
|
async function filterPathsObject() {
|
|
56
|
-
const raw = await (0, promises_1.readFile)((0,
|
|
58
|
+
const raw = await (0, promises_1.readFile)((0, node_path_1.join)(__dirname, "paths.json"), "utf8");
|
|
57
59
|
const pathsObj = await JSON.parse(raw);
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
+
const regexStartWithSlash = /^\//;
|
|
61
|
+
const apiEndpoints = Object.entries(pathsObj.paths).map((path) => ({
|
|
62
|
+
endpoint: path[0].replace(basepath, "").replace(regexStartWithSlash, ""),
|
|
63
|
+
methods: Object.keys(path[1]),
|
|
64
|
+
apiEndpoint: path[0],
|
|
65
|
+
}));
|
|
66
|
+
const endpoints = apiEndpoints.map(({ endpoint }) => endpoint);
|
|
67
|
+
const foldersName = endpoints.map((endpoint) => endpoint.split("/")[0].toLocaleLowerCase());
|
|
60
68
|
await makeFolders(foldersName);
|
|
61
|
-
await makeFileContainer(
|
|
69
|
+
await makeFileContainer(apiEndpoints, foldersName);
|
|
62
70
|
if (paramsConfig.output) {
|
|
63
71
|
await moveFolderToChoosePath();
|
|
64
72
|
}
|
|
65
73
|
else {
|
|
66
|
-
await
|
|
74
|
+
await destinationPath(mainFolderOutPut);
|
|
67
75
|
}
|
|
68
76
|
await cleanFileAndConfig();
|
|
69
77
|
}
|
|
@@ -73,50 +81,65 @@ async function cleanFileAndConfig() {
|
|
|
73
81
|
console.log("🧹 Cleaned.");
|
|
74
82
|
}
|
|
75
83
|
async function cleanFile() {
|
|
76
|
-
await (0, promises_1.rm)((0,
|
|
84
|
+
await (0, promises_1.rm)((0, node_path_1.join)(__dirname, "paths.json"), { force: true });
|
|
77
85
|
}
|
|
78
86
|
async function cleanConfig() {
|
|
79
|
-
await (0, promises_1.rm)((0,
|
|
87
|
+
await (0, promises_1.rm)((0, node_path_1.join)(__dirname, "config.json"), { force: true });
|
|
80
88
|
}
|
|
81
89
|
async function makeFolders(foldersName) {
|
|
82
90
|
await (0, promises_1.mkdir)(mainFolderOutPut, { recursive: true });
|
|
83
91
|
if (!paramsConfig.skipFolder) {
|
|
84
|
-
for (const folder of
|
|
85
|
-
const folderPath = (0,
|
|
92
|
+
for (const folder of new Set(foldersName)) {
|
|
93
|
+
const folderPath = (0, node_path_1.join)(mainFolderOutPut, folder);
|
|
86
94
|
await (0, promises_1.mkdir)(folderPath, { recursive: true });
|
|
87
95
|
}
|
|
88
96
|
}
|
|
89
97
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
+
const getFilePath = (folder) => {
|
|
99
|
+
return paramsConfig.skipFolder
|
|
100
|
+
? `${mainFolderOutPut}/${folder}.ts`
|
|
101
|
+
: `${mainFolderOutPut}/${folder}/${folder}.ts`;
|
|
102
|
+
};
|
|
103
|
+
function normalizeEndpoint(endpoint, toLowercase) {
|
|
104
|
+
let name = endpoint
|
|
105
|
+
.replace(/[/|{}]/g, "_")
|
|
106
|
+
.replace(/_+/g, "_")
|
|
107
|
+
.replace(/(^_)|(_$)/g, "");
|
|
108
|
+
return toLowercase ? name.toLowerCase() : name;
|
|
109
|
+
}
|
|
110
|
+
const formatEndpointNames = (endpoint) => {
|
|
111
|
+
const name = normalizeEndpoint(endpoint, paramsConfig.functionNameLowercase);
|
|
112
|
+
const templatePath = endpoint.replace(/\{/g, "${");
|
|
113
|
+
return { name, templatePath };
|
|
114
|
+
};
|
|
115
|
+
const generateDocumentation = (endpoint, methods, apiEndpoint) => {
|
|
116
|
+
const paramsMatch = endpoint.match(/\{([^{}]+)\}/g) || [];
|
|
117
|
+
const args = paramsMatch
|
|
118
|
+
.map((p) => `${p.replace(/[{}]/g, "")}:any`)
|
|
119
|
+
.join(", ");
|
|
120
|
+
const paramsDoc = paramsMatch
|
|
121
|
+
.map((p) => `* @param ${p.replace(/[{}]/g, "")}`)
|
|
122
|
+
.join("\n");
|
|
123
|
+
const endpointLine = apiEndpoint ? `\n* @endpoint ${apiEndpoint}\n` : "";
|
|
124
|
+
const methodsLine = methods
|
|
125
|
+
? `* @methods ${methods.join(" - ").toUpperCase()}\n`
|
|
126
|
+
: "";
|
|
127
|
+
const paramsLine = paramsDoc ? `${paramsDoc}\n` : "";
|
|
128
|
+
const jsDoc = `/**${endpointLine}${methodsLine}${paramsLine}*/\n`;
|
|
129
|
+
return {
|
|
130
|
+
args,
|
|
131
|
+
jsDoc,
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
async function makeFileContainer(apiEndpoints, foldersName) {
|
|
135
|
+
for (const folder of new Set(foldersName)) {
|
|
136
|
+
await (0, promises_1.appendFile)(getFilePath(folder), "");
|
|
98
137
|
}
|
|
99
138
|
for (const [index, folder] of foldersName.entries()) {
|
|
100
|
-
const endpoint =
|
|
101
|
-
const filePath =
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const paramsMatch = endpoint.match(/\{([^}]+)\}/g);
|
|
105
|
-
const args = paramsMatch
|
|
106
|
-
? paramsMatch.map((p) => p.replace(/[{}]/g, "").concat(":any")).join(", ")
|
|
107
|
-
: "";
|
|
108
|
-
let jsDoc = "";
|
|
109
|
-
if (paramsMatch?.length) {
|
|
110
|
-
const paramsDoc = paramsMatch
|
|
111
|
-
.map((p) => ` * @param ${p.replace(/[{}]/g, "")}`)
|
|
112
|
-
.join("\n");
|
|
113
|
-
jsDoc = `/**\n${paramsDoc}\n */\n`;
|
|
114
|
-
}
|
|
115
|
-
const name = endpoint
|
|
116
|
-
.replace(/[/|{}]/g, "_")
|
|
117
|
-
.replace(/_+/g, "_")
|
|
118
|
-
.replace(/^_|_$/g, "");
|
|
119
|
-
const templatePath = endpoint.replace(/\{/g, "${");
|
|
139
|
+
const { endpoint, methods, apiEndpoint } = apiEndpoints[index];
|
|
140
|
+
const filePath = getFilePath(folder);
|
|
141
|
+
const { name, templatePath } = formatEndpointNames(endpoint);
|
|
142
|
+
const { args, jsDoc } = generateDocumentation(endpoint, methods, apiEndpoint);
|
|
120
143
|
const line = `${jsDoc} export const ${name} = (${args}) => \`${templatePath}\`;\n`;
|
|
121
144
|
try {
|
|
122
145
|
await (0, promises_1.appendFile)(filePath, line);
|
|
@@ -128,31 +151,14 @@ async function makeFileContainer(endpoints, foldersName) {
|
|
|
128
151
|
}
|
|
129
152
|
}
|
|
130
153
|
}
|
|
131
|
-
async function
|
|
132
|
-
const platform = os_1.default.platform();
|
|
133
|
-
let command = "";
|
|
134
|
-
switch (platform) {
|
|
135
|
-
case "win32":
|
|
136
|
-
command = `explorer`;
|
|
137
|
-
break;
|
|
138
|
-
case "darwin":
|
|
139
|
-
command = `open`;
|
|
140
|
-
break;
|
|
141
|
-
case "linux":
|
|
142
|
-
command = `xdg-open`;
|
|
143
|
-
break;
|
|
144
|
-
default:
|
|
145
|
-
console.error(`Platform ${platform} is not supported.`);
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
(0, child_process_1.exec)(`${command} "${fullPath}"`);
|
|
154
|
+
async function destinationPath(fullPath) {
|
|
149
155
|
console.log("💾 show result --->", fullPath);
|
|
150
156
|
}
|
|
151
157
|
async function moveFolderToChoosePath() {
|
|
152
158
|
await (0, fs_extra_1.move)(mainFolderOutPut, `${paramsConfig.output}${folderName}`, {
|
|
153
159
|
overwrite: true,
|
|
154
160
|
});
|
|
155
|
-
await
|
|
161
|
+
await destinationPath((0, node_path_1.resolve)(`${paramsConfig.output}${folderName}`));
|
|
156
162
|
}
|
|
157
163
|
async function formatWithPrettier(filePath) {
|
|
158
164
|
const content = await (0, promises_1.readFile)(filePath, "utf8");
|
package/package.json
CHANGED