@visulima/jsdoc-open-api 1.0.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/CHANGELOG.md ADDED
@@ -0,0 +1,19 @@
1
+ ## @visulima/jsdoc-open-api [1.0.1](https://github.com/visulima/visulima/compare/@visulima/jsdoc-open-api@1.0.0...@visulima/jsdoc-open-api@1.0.1) (2022-10-26)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **jsdoc-open-api:** make package public ([f266edf](https://github.com/visulima/visulima/commit/f266edf64fb21f2510d9ab15e27e48909d593a58))
7
+
8
+ ## @visulima/jsdoc-open-api 1.0.0 (2022-10-26)
9
+
10
+
11
+ ### Features
12
+
13
+ * **jsdoc-open-api:** added connect as new package for swagger parsing ([a641712](https://github.com/visulima/visulima/commit/a641712ab27616d38250fdd86a6cdc5c865ddd80))
14
+
15
+
16
+
17
+ ### Dependencies
18
+
19
+ * **@visulima/readdir:** upgraded to 1.1.0
package/LICENSE.md ADDED
File without changes
package/README.md ADDED
@@ -0,0 +1,226 @@
1
+ <div align="center">
2
+ <h3>Visulima jsdoc-open-api</h3>
3
+ <p>
4
+
5
+ Visulima jsdoc-open-api parser and generator is a forked version of [openapi-comment-parser](https://github.com/bee-travels/openapi-comment-parser) and [swagger-jsdoc](https://github.com/Surnet/swagger-jsdoc) its built on top of [swagger](https://swagger.io/) and [JSDoc](https://jsdoc.app/), for speed and minimal runtime overhead.
6
+
7
+ </p>
8
+ </div>
9
+
10
+ <br />
11
+
12
+ <div align="center">
13
+
14
+ [![typescript-image]][typescript-url] [![npm-image]][npm-url] [![license-image]][license-url]
15
+
16
+ </div>
17
+
18
+ ---
19
+
20
+ <div align="center">
21
+ <p>
22
+ <sup>
23
+ Daniel Bannert's open source work is supported by the community on <a href="https://github.com/sponsors/prisis">GitHub Sponsors</a>
24
+ </sup>
25
+ </p>
26
+ </div>
27
+
28
+ ---
29
+
30
+ ## Installation
31
+
32
+ ```sh
33
+ npm install @visulima/jsdoc-open-api
34
+ ```
35
+
36
+ ```sh
37
+ yarn add @visulima/jsdoc-open-api
38
+ ```
39
+
40
+ ```sh
41
+ pnpm add @visulima/jsdoc-open-api
42
+ ```
43
+
44
+ ## Usage
45
+
46
+ Choose the syntax you want to use for your OpenAPI definitions:
47
+
48
+ ![choose the syntax](./assets/swagger-difference.png)
49
+
50
+ CLI:
51
+
52
+ ```bash
53
+ # This generate the .openapirc.js config file, this command is only needed on the first run
54
+ jsdoc-open-api init
55
+
56
+ # This will generate the swagger.json file
57
+ jsdoc-open-api generate src/
58
+ ```
59
+
60
+ ## OpenApi yaml syntax
61
+
62
+ The library will take the contents of @openapi (or @swagger):
63
+
64
+ ```ts
65
+ /**
66
+ * @openapi
67
+ * /:
68
+ * get:
69
+ * description: Welcome to swagger-jsdoc!
70
+ * responses:
71
+ * 200:
72
+ * description: Returns a mysterious string.
73
+ */
74
+
75
+ ```
76
+
77
+ ## OpenApi short syntax
78
+
79
+ ### Basic structure
80
+ You can write OpenAPI definitions in JSDoc comments or YAML files.
81
+ In this guide, we use only JSDoc comments examples. However, YAML files work equally as well.
82
+
83
+ Each comment defines individual endpoints (paths) in your API, and the HTTP methods (operations) supported by these endpoints.
84
+ For example, `GET /users` can be described as:
85
+
86
+ ```js
87
+ /**
88
+ * GET /users
89
+ * @summary Returns a list of users.
90
+ * @description Optional extended description in CommonMark or HTML.
91
+ * @response 200 - A JSON array of user names
92
+ * @responseContent {string[]} 200.application/json
93
+ */
94
+ ```
95
+
96
+ #### Parameters
97
+ Operations can have parameters passed via URL path (`/users/{userId}`), query string (`/users?role=admin`),
98
+ headers (`X-CustomHeader: Value`) or cookies (`Cookie: debug=0`).
99
+ You can define the parameter data types, format, whether they are required or optional, and other details:
100
+
101
+ ```js
102
+ /**
103
+ * GET /users/{userId}
104
+ * @summary Returns a user by ID.
105
+ * @pathParam {int64} userId - Parameter description in CommonMark or HTML.
106
+ * @response 200 - OK
107
+ */
108
+ ```
109
+
110
+ For more information, see [Describing Parameters](/docs/short/describing-parameters.md).
111
+
112
+ #### Request body
113
+ If an operation sends a request body, use the `bodyContent` keyword to describe the body content and media type.
114
+ Use `bodyRequired` to indicate that a request body is required.
115
+
116
+ ```js
117
+ /**
118
+ * POST /users
119
+ * @summary Creates a user.
120
+ * @bodyContent {User} application/json
121
+ * @bodyRequired
122
+ * @response 201 - Created
123
+ */
124
+ ```
125
+
126
+ For more information, see [Describing Request Body](/docs/short/describing-request-body.md).
127
+
128
+ #### Responses
129
+ For each operation, you can define possible status codes, such as 200 OK or 404 Not Found, and the response body content.
130
+ You can also provide example responses for different content types:
131
+
132
+ ```js
133
+ /**
134
+ * GET /users/{userId}
135
+ * @summary Returns a user by ID.
136
+ * @pathParam {int64} userId - The ID of the user to return.
137
+ * @response 200 - A user object.
138
+ * @responseContent {User} 200.application/json
139
+ * @response 400 - The specified user ID is invalid (not a number).
140
+ * @response 404 - A user with the specified ID was not found.
141
+ * @response default - Unexpected error
142
+ */
143
+ ```
144
+
145
+ For more information, see [Describing Responses](/docs/short/describing-responses.md).
146
+
147
+ #### Input and output models
148
+ You can create global `components/schemas` section lets you define common data structures used in your API.
149
+ They can be referenced by name whenever a schema is required – in parameters, request bodies, and response bodies.
150
+ For example, this JSON object:
151
+
152
+ ```json
153
+ {
154
+ "id": 4,
155
+ "name": "Arthur Dent"
156
+ }
157
+ ```
158
+
159
+ can be represented as:
160
+
161
+ ```yaml
162
+ components:
163
+ schemas:
164
+ User:
165
+ properties:
166
+ id:
167
+ type: integer
168
+ name:
169
+ type: string
170
+ # Both properties are required
171
+ required:
172
+ - id
173
+ - name
174
+ ```
175
+
176
+ and then referenced in the request body schema and response body schema as follows:
177
+
178
+ ```js
179
+ /**
180
+ * GET /users/{userId}
181
+ * @summary Returns a user by ID.
182
+ * @pathParam {integer} userId
183
+ * @response 200 - OK
184
+ * @responseContent {User} 200.application/json
185
+ */
186
+
187
+ /**
188
+ * POST /users
189
+ * @summary Creates a new user.
190
+ * @bodyContent {User} application/json
191
+ * @bodyRequired
192
+ * @response 201 - Created
193
+ */
194
+ ```
195
+
196
+ #### Authentication
197
+ The `securitySchemes` and `security` keywords are used to describe the authentication methods used in your API.
198
+ ```yaml
199
+ components:
200
+ securitySchemes:
201
+ BasicAuth:
202
+ type: http
203
+ scheme: basic
204
+ ```
205
+
206
+ ```js
207
+ /**
208
+ * GET /users
209
+ * @security BasicAuth
210
+ */
211
+ ```
212
+
213
+ Supported authentication methods are:
214
+ - HTTP authentication: Basic, Bearer, and so on.
215
+ - API key as a header or query parameter or in cookies
216
+ - OAuth 2
217
+ - OpenID Connect Discovery
218
+
219
+ For more information, see [Authentication](/docs/short/authentication.md).
220
+
221
+ [typescript-image]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge&logo=typescript
222
+ [typescript-url]: "typescript"
223
+ [license-image]: https://img.shields.io/npm/l/@visulima/jsdoc-open-api?color=blueviolet&style=for-the-badge
224
+ [license-url]: LICENSE.md "license"
225
+ [npm-image]: https://img.shields.io/npm/v/@visulima/jsdoc-open-api/latest.svg?style=for-the-badge&logo=npm
226
+ [npm-url]: https://www.npmjs.com/package/@visulima/jsdoc-open-api/v/latest "npm"
package/bin/index.js ADDED
@@ -0,0 +1,188 @@
1
+ #!/usr/bin/env node
2
+ // eslint-disable-next-line unicorn/prefer-module
3
+ const path = require("node:path");
4
+ // eslint-disable-next-line unicorn/prefer-module
5
+ const fs = require("node:fs");
6
+ // eslint-disable-next-line unicorn/prefer-module
7
+ const { Command } = require("commander");
8
+ // eslint-disable-next-line unicorn/prefer-module
9
+ const SwaggerParser = require("@apidevtools/swagger-parser");
10
+ // eslint-disable-next-line unicorn/prefer-module
11
+ const { collect } = require("@visulima/readdir");
12
+ // eslint-disable-next-line unicorn/prefer-module
13
+ const cliProgress = require("cli-progress");
14
+
15
+ const {
16
+ jsDocumentCommentsToOpenApi, parseFile, SpecBuilder, swaggerJsDocumentCommentsToOpenApi,
17
+ // eslint-disable-next-line unicorn/prefer-module
18
+ } = require("../dist/index.js");
19
+
20
+ // eslint-disable-next-line unicorn/prefer-module,no-underscore-dangle
21
+ const package_ = require("../package.json");
22
+
23
+ const program = new Command();
24
+ const defaultConfigName = ".openapirc.js";
25
+
26
+ program.name("@visulima/jsdoc-open-api").description("CLI to to generate OpenAPI (Swagger) documentation from JSDoc's").version(package_.version);
27
+
28
+ program
29
+ .command("init")
30
+ .description("Inits a pre-configured @visulima/jsdoc-open-api config file.")
31
+ .action(() => {
32
+ if (fs.existsSync(defaultConfigName)) {
33
+ // eslint-disable-next-line no-console
34
+ console.error("Config file already exists");
35
+ // eslint-disable-next-line no-undef
36
+ process.exit(1);
37
+ }
38
+
39
+ fs.writeFileSync(
40
+ defaultConfigName,
41
+ `module.exports = {
42
+ exclude: [
43
+ 'coverage/**',
44
+ '.github/**',
45
+ 'packages/*/test{,s}/**',
46
+ '**/*.d.ts',
47
+ 'test{,s}/**',
48
+ 'test{,-*}.{js,cjs,mjs,ts,tsx,jsx,yaml,yml}',
49
+ '**/*{.,-}test.{js,cjs,mjs,ts,tsx,jsx,yaml,yml}',
50
+ '**/__tests__/**',
51
+ '**/{ava,babel,nyc}.config.{js,cjs,mjs}',
52
+ '**/jest.config.{js,cjs,mjs,ts}',
53
+ '**/{karma,rollup,webpack}.config.js',
54
+ '**/.{eslint,mocha}rc.{js,cjs}',
55
+ '**/.{travis,yarnrc}.yml',
56
+ '**/{docker-compose,docker}.yml',
57
+ '**/.yamllint.{yaml,yml}',
58
+ '**/node_modules/**',
59
+ '**/pnpm-lock.yaml',
60
+ '**/pnpm-workspace.yaml',
61
+ '**/package-lock.json',
62
+ '**/yarn.lock',
63
+ '**/package.json',
64
+ '**/package.json5',
65
+ '**/.next/**',
66
+ ],
67
+ followSymlinks: false,
68
+ swaggerDefinition: {
69
+ openapi: '3.0.0',
70
+ info: {
71
+ title: 'API',
72
+ version: '1.0.0',
73
+ },
74
+ },
75
+ };
76
+ `,
77
+ );
78
+
79
+ // eslint-disable-next-line no-console
80
+ console.log("Created .openapirc.js");
81
+ });
82
+
83
+ program
84
+ .command("generate")
85
+ .description("Generates OpenAPI (Swagger) documentation from JSDoc's")
86
+ .usage("[options] <path ...>")
87
+ .argument("[path ...]", "Paths to files or directories to parse")
88
+ .option("-c, --config [.openapirc.js]", "@visulima/jsdoc-open-api config file path.")
89
+ .option("-o, --output [swaggerSpec.json]", "Output swagger specification.")
90
+ .option("-v, --verbose", "Verbose output.")
91
+ .option("-vv, --very-verbose", "Very verbose output.")
92
+ // eslint-disable-next-line radar/cognitive-complexity
93
+ .action(async (paths, options) => {
94
+ let openapiConfig = {};
95
+
96
+ try {
97
+ // eslint-disable-next-line unicorn/prefer-module,import/no-dynamic-require
98
+ openapiConfig = require(path.resolve(options.config || defaultConfigName));
99
+ } catch (error) {
100
+ // eslint-disable-next-line no-console
101
+ console.log("No config file found, on:", options.config || ".openapirc.js\n");
102
+ // eslint-disable-next-line no-console
103
+ console.error(error);
104
+ // eslint-disable-next-line no-undef
105
+ process.exit(1);
106
+ }
107
+
108
+ const multibar = new cliProgress.MultiBar(
109
+ {
110
+ clearOnComplete: false,
111
+ hideCursor: true,
112
+ format: "{value}/{total} | {bar} | {filename}",
113
+ },
114
+ cliProgress.Presets.shades_grey,
115
+ );
116
+ const spec = new SpecBuilder(openapiConfig.swaggerDefinition);
117
+
118
+ // eslint-disable-next-line no-restricted-syntax,unicorn/prevent-abbreviations
119
+ for await (const dir of paths) {
120
+ const files = await collect(dir, {
121
+ // eslint-disable-next-line @rushstack/security/no-unsafe-regexp
122
+ skip: [...openapiConfig.exclude, "node_modules/**"],
123
+ extensions: openapiConfig.extension || [".js", ".cjs", ".mjs", ".ts", ".tsx", ".jsx", ".yaml", ".yml"],
124
+ followSymlinks: openapiConfig.followSymlinks || false,
125
+ match: openapiConfig.include,
126
+ minimatchOptions: {
127
+ debug: options.verbose,
128
+ matchBase: true,
129
+ },
130
+ });
131
+
132
+ if (options.verbose || options.veryVerbose) {
133
+ // eslint-disable-next-line no-console
134
+ console.log(`\nFound ${files.length} files in ${dir}`);
135
+ }
136
+
137
+ if (options.veryVerbose) {
138
+ // eslint-disable-next-line no-console
139
+ console.log(files);
140
+ }
141
+
142
+ const bar = multibar.create(files.length, 0);
143
+
144
+ files.forEach((file) => {
145
+ if (options.verbose) {
146
+ // eslint-disable-next-line no-console
147
+ console.log(`Parsing file ${file}`);
148
+ }
149
+
150
+ bar.increment(1, { filename: dir });
151
+
152
+ const parsedJsDocumentFile = parseFile(file, jsDocumentCommentsToOpenApi, this.verbose);
153
+
154
+ spec.addData(parsedJsDocumentFile.map((item) => item.spec));
155
+
156
+ const parsedSwaggerJsDocumentFile = parseFile(file, swaggerJsDocumentCommentsToOpenApi, this.verbose);
157
+
158
+ spec.addData(parsedSwaggerJsDocumentFile.map((item) => item.spec));
159
+ });
160
+ }
161
+
162
+ try {
163
+ await SwaggerParser.validate(JSON.parse(JSON.stringify(spec)));
164
+ } catch (error) {
165
+ // eslint-disable-next-line no-console
166
+ console.error(error.toJSON());
167
+ // eslint-disable-next-line no-undef
168
+ process.exit();
169
+ }
170
+
171
+ const output = options.output || "swagger.json";
172
+
173
+ multibar.stop();
174
+
175
+ fs.writeFileSync(output, JSON.stringify(spec, null, 2));
176
+
177
+ // eslint-disable-next-line no-console
178
+ console.log(`\nSwagger specification is ready, check the${output}file.`);
179
+ });
180
+ // eslint-disable-next-line no-undef
181
+ program.parse(process.argv);
182
+
183
+ // eslint-disable-next-line no-undef
184
+ if (process.argv.slice(2).length === 0) {
185
+ program.help();
186
+ // eslint-disable-next-line no-undef
187
+ process.exit();
188
+ }