widdershins-reborn 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.
Files changed (63) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +187 -0
  3. package/include/abstract.md +2 -0
  4. package/include/sotd.md +2 -0
  5. package/lib/apiblueprint.js +42 -0
  6. package/lib/asyncapi1.js +159 -0
  7. package/lib/common.js +557 -0
  8. package/lib/harGenerator.js +46 -0
  9. package/lib/httpsnippetGenerator.js +23 -0
  10. package/lib/index.js +35 -0
  11. package/lib/jsonTrunc.js +94 -0
  12. package/lib/openapi2.js +25 -0
  13. package/lib/openapi3.js +757 -0
  14. package/lib/semoasa.js +105 -0
  15. package/package.json +82 -0
  16. package/resources/oas_descs.js +49 -0
  17. package/statusCodes.json +450 -0
  18. package/templates/asyncapi1/README.md +36 -0
  19. package/templates/asyncapi1/authentication.def +6 -0
  20. package/templates/asyncapi1/authentication_none.def +3 -0
  21. package/templates/asyncapi1/code_go.dot +1 -0
  22. package/templates/asyncapi1/code_java.dot +8 -0
  23. package/templates/asyncapi1/code_javascript.dot +1 -0
  24. package/templates/asyncapi1/code_nodejs.dot +7 -0
  25. package/templates/asyncapi1/code_python.dot +1 -0
  26. package/templates/asyncapi1/code_ruby.dot +1 -0
  27. package/templates/asyncapi1/discovery.dot +11 -0
  28. package/templates/asyncapi1/footer.def +2 -0
  29. package/templates/asyncapi1/main.dot +202 -0
  30. package/templates/asyncapi1/message.def +86 -0
  31. package/templates/asyncapi1/security.def +36 -0
  32. package/templates/asyncapi1/translations.dot +16 -0
  33. package/templates/openapi3/README.md +64 -0
  34. package/templates/openapi3/authentication.def +5 -0
  35. package/templates/openapi3/authentication_none.def +3 -0
  36. package/templates/openapi3/callbacks.def +38 -0
  37. package/templates/openapi3/code_csharp.dot +133 -0
  38. package/templates/openapi3/code_go.dot +23 -0
  39. package/templates/openapi3/code_http.dot +7 -0
  40. package/templates/openapi3/code_java.dot +13 -0
  41. package/templates/openapi3/code_javascript.dot +16 -0
  42. package/templates/openapi3/code_jquery.dot +14 -0
  43. package/templates/openapi3/code_nodejs.dot +17 -0
  44. package/templates/openapi3/code_php.dot +28 -0
  45. package/templates/openapi3/code_python.dot +10 -0
  46. package/templates/openapi3/code_ruby.dot +14 -0
  47. package/templates/openapi3/code_shell.dot +4 -0
  48. package/templates/openapi3/debug.def +1 -0
  49. package/templates/openapi3/discovery.def +11 -0
  50. package/templates/openapi3/footer.def +2 -0
  51. package/templates/openapi3/links.def +18 -0
  52. package/templates/openapi3/main.dot +139 -0
  53. package/templates/openapi3/operation.dot +54 -0
  54. package/templates/openapi3/parameters.def +40 -0
  55. package/templates/openapi3/responses.def +87 -0
  56. package/templates/openapi3/security.def +31 -0
  57. package/templates/openapi3/translations.dot +19 -0
  58. package/templates/semoasa/README.md +19 -0
  59. package/templates/semoasa/ext.def +69 -0
  60. package/templates/semoasa/main.dot +34 -0
  61. package/templates/semoasa/translations.dot +16 -0
  62. package/templates/semoasa/usage.def +23 -0
  63. package/widdershins.js +218 -0
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Original work Copyright (c) 2016 Mermade Software (Mike Ralphson)
4
+ Fork maintained by Sikandar
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,187 @@
1
+ # widdershins-reborn
2
+
3
+ > A maintained fork of [widdershins](https://github.com/Mermade/widdershins) — designed as a **library-first** API for converting OpenAPI/Swagger/AsyncAPI definitions to Markdown or HTML.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/widdershins-reborn.svg)](https://www.npmjs.com/package/widdershins-reborn)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Why widdershins-reborn?
9
+
10
+ The original `widdershins` package was CLI-focused and unmaintained since 2020. This fork is:
11
+
12
+ - **Library-first** — clean JS API, perfect for apps, build pipelines, and doc generators
13
+ - **Actively maintained** — bug fixes, dependency updates, new features
14
+ - **Modern Node.js** — supports Node 14.x through 22.x
15
+ - **TypeScript-ready** — type definitions included (coming soon)
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ npm install widdershins-reborn
21
+ ```
22
+
23
+ ## Quick Start (JavaScript)
24
+
25
+ ```javascript
26
+ const widdershins = require('widdershins-reborn');
27
+ const fs = require('fs');
28
+
29
+ // Load your OpenAPI/Swagger/AsyncAPI definition
30
+ const api = JSON.parse(fs.readFileSync('petstore.json', 'utf8'));
31
+
32
+ // Convert to Markdown
33
+ widdershins.convert(api, {
34
+ search: true,
35
+ sample: true,
36
+ language_tabs: [
37
+ { javascript: 'JavaScript' },
38
+ { python: 'Python' }
39
+ ]
40
+ })
41
+ .then(markdown => {
42
+ fs.writeFileSync('api-docs.md', markdown, 'utf8');
43
+ console.log('Documentation generated!');
44
+ })
45
+ .catch(err => {
46
+ console.error('Conversion failed:', err);
47
+ });
48
+ ```
49
+
50
+ ## Quick Start (ESM / Modern Node)
51
+
52
+ ```javascript
53
+ import widdershins from 'widdershins-reborn';
54
+ import { readFileSync } from 'fs';
55
+
56
+ const api = JSON.parse(readFileSync('petstore.json', 'utf8'));
57
+
58
+ const markdown = await widdershins.convert(api, {
59
+ search: true,
60
+ sample: true
61
+ });
62
+
63
+ console.log(markdown);
64
+ ```
65
+
66
+ ## API Reference
67
+
68
+ ### `widdershins.convert(api, options)`
69
+
70
+ Converts an API definition to Markdown. Returns a `Promise<string>`.
71
+
72
+ **Parameters:**
73
+
74
+ | Name | Type | Description |
75
+ |------|------|-------------|
76
+ | `api` | `object \| string` | Parsed OpenAPI/Swagger/AsyncAPI object, or a string (for API Blueprint) |
77
+ | `options` | `object` | Conversion options (see below) |
78
+
79
+ **Options:**
80
+
81
+ | Option | Type | Default | Description |
82
+ |--------|------|---------|-------------|
83
+ | `search` | `boolean` | `true` | Include search in output |
84
+ | `sample` | `boolean` | `true` | Generate example values (false = raw schemas) |
85
+ | `codeSamples` | `boolean` | `false` | Include generated code samples |
86
+ | `httpsnippet` | `boolean` | `false` | Use httpsnippet for code generation |
87
+ | `language_tabs` | `array` | `[]` | Language tabs: `[{ javascript: 'JavaScript' }]` |
88
+ | `language_clients` | `array` | `[]` | Client libraries: `[{ shell: 'curl' }]` |
89
+ | `theme` | `string` | `'darkula'` | Syntax highlighting theme |
90
+ | `discovery` | `boolean` | `false` | Include schema.org WebAPI discovery data |
91
+ | `headings` | `number` | `2` | Heading depth for TOC |
92
+ | `tocSummary` | `boolean` | `false` | Use operation summary in TOC |
93
+ | `shallowSchemas` | `boolean` | `false` | Don't expand $ref schemas |
94
+ | `expandBody` | `boolean` | `false` | Expand request body schemas |
95
+ | `maxDepth` | `number` | `10` | Max depth for schema examples |
96
+ | `yaml` | `boolean` | `false` | Display schemas in YAML format |
97
+ | `resolve` | `boolean` | `false` | Resolve external $refs |
98
+ | `source` | `string` | — | Base URL for resolving refs |
99
+ | `includes` | `string[]` | `[]` | Files to include in output |
100
+ | `user_templates` | `string` | — | Directory with custom doT.js templates |
101
+ | `templateCallback` | `function` | — | Called before/after each template |
102
+ | `html` | `boolean` | `false` | Output HTML instead of Markdown |
103
+ | `respec` | `object` | — | ReSpec config object (implies html) |
104
+ | `clipboard` | `boolean` | `true` | Include clipboard support |
105
+ | `customApiKeyValue` | `string` | `'ApiKey'` | Custom API key for code samples |
106
+ | `omitBody` | `boolean` | `false` | Omit body param from table |
107
+ | `omitHeader` | `boolean` | `false` | Omit YAML front-matter |
108
+ | `useBodyName` | `boolean` | `false` | Use original param name for OAS2 body |
109
+ | `verbose` | `boolean` | `false` | Verbose output |
110
+ | `experimental` | `boolean` | `false` | Use experimental features |
111
+ | `toc_footers` | `array` | `[]` | Footer links for TOC |
112
+
113
+ ### Template Callback
114
+
115
+ The `templateCallback` option lets you hook into template rendering:
116
+
117
+ ```javascript
118
+ widdershins.convert(api, {
119
+ templateCallback: (templateName, stage, data) => {
120
+ // stage is 'pre' or 'post'
121
+ console.log(`${stage} rendering: ${templateName}`);
122
+ return data; // must return data
123
+ }
124
+ });
125
+ ```
126
+
127
+ ## Supported Input Formats
128
+
129
+ | Format | Versions | Notes |
130
+ |--------|----------|-------|
131
+ | OpenAPI | 3.0.x | Full support |
132
+ | Swagger | 2.0 | Auto-converted to OAS3 internally |
133
+ | AsyncAPI | 1.x | Topics-based messaging APIs |
134
+ | Semoasa | 0.1.0 | OpenAPI Extension Format |
135
+ | API Blueprint | — | Passthrough (already Markdown) |
136
+
137
+ ## Supported Output Formats
138
+
139
+ | Format | Use With |
140
+ |--------|----------|
141
+ | Markdown | [Slate](https://github.com/slatedocs/slate), [ReSlate](https://github.com/mermade/reslate), [Shins](https://github.com/mermade/shins) |
142
+ | HTML | [ReSpec](https://github.com/w3c/respec), custom doc sites |
143
+
144
+ ## CLI Usage
145
+
146
+ The CLI is available but the library API is recommended for app integration:
147
+
148
+ ```bash
149
+ npx widdershins-reborn [options] {input-file|url} [[-o] output-markdown]
150
+ ```
151
+
152
+ ```bash
153
+ widdershins-reborn --search false --language_tabs 'python:Python' api.json -o docs.md
154
+ ```
155
+
156
+ Run `widdershins-reborn --help` for all options.
157
+
158
+ ## Custom Templates
159
+
160
+ Override the default doT.js templates by passing a directory:
161
+
162
+ ```javascript
163
+ widdershins.convert(api, {
164
+ user_templates: './my-templates'
165
+ });
166
+ ```
167
+
168
+ Template docs:
169
+ - [OpenAPI 3.0 / Swagger 2.0 templates](/templates/openapi3/README.md)
170
+ - [AsyncAPI 1.x templates](/templates/asyncapi1/README.md)
171
+ - [Semoasa templates](/templates/semoasa/README.md)
172
+
173
+ ## Tests
174
+
175
+ ```bash
176
+ npm test
177
+ ```
178
+
179
+ ## Credits
180
+
181
+ * Original project by [Mike Ralphson](https://github.com/Mermade) (Mermade Software)
182
+ * Logo by [@latgeek](https://github.com/LatGeek)
183
+ * httpsnippet support by [@vfernandestoptal](https://github.com/vfernandestoptal)
184
+
185
+ ## License
186
+
187
+ [MIT](LICENSE) — Original work Copyright (c) 2016 Mermade Software, fork maintained by Sikandar.
@@ -0,0 +1,2 @@
1
+ This is the Abstract include. It may be found in the widdershins `include` directory,
2
+ it may be modified there or the location overridden with the `--abstract` option.
@@ -0,0 +1,2 @@
1
+ This is the State of the Document (sotd) include. It may be found in the widdershins `include` directory,
2
+ it may be modified there or the location overridden with the `--sotd` option.
@@ -0,0 +1,42 @@
1
+ 'use strict';
2
+
3
+ function convert(api, options) {
4
+ return new Promise(function (resolve, reject) {
5
+ api = api.split('\r').join('');
6
+ var lines = api.split('\n');
7
+ var title = '';
8
+ var metadata = [];
9
+ var index = 0;
10
+ while ((lines[index].indexOf(':') >= 0) && (index < lines.length)) {
11
+ metadata.push('> ' + lines[index] + '\n');
12
+ lines[index] = '';
13
+ index++;
14
+ }
15
+ while (lines[index] && !lines[index].startsWith('# ') && !lines[index].startsWith('==') && (index < lines.length)) {
16
+ index++;
17
+ }
18
+ if ((index < lines.length) && lines[index].startsWith('# ')) {
19
+ title = lines[index];
20
+ }
21
+ else {
22
+ title = lines[index - 1];
23
+ }
24
+ lines.splice(index + 1, 0, ...metadata);
25
+ api = lines.join('\n');
26
+ lines = [];
27
+ api = '\n' + api + '\n';
28
+ api = '---\ntitle: ' + (title.replace('# ', '')) + '\n' + `language_tabs:
29
+ toc_footers: []
30
+ includes: []
31
+ search: true
32
+ code_clipboard: true
33
+ highlight_theme: darkula
34
+ ---
35
+ ` + api;
36
+ resolve(api);
37
+ });
38
+ }
39
+
40
+ module.exports = {
41
+ convert: convert
42
+ };
@@ -0,0 +1,159 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const util = require('util');
5
+
6
+ const yaml = require('yaml');
7
+ const dot = require('dot');
8
+ dot.templateSettings.strip = false;
9
+ dot.templateSettings.varname = 'data';
10
+
11
+ const common = require('./common.js');
12
+ const resolver = require('oas-resolver');
13
+ const dereference = require('reftools/lib/dereference.js').dereference;
14
+
15
+ let templates;
16
+
17
+ function preProcessor(api) {
18
+ return api;
19
+ }
20
+
21
+ function convertToToc(source) {
22
+ let resources = {};
23
+ for (let t in source.topics) {
24
+ let topic = source.topics[t];
25
+ for (var m in topic) {
26
+ var message = topic[m];
27
+ var tagName = 'Default';
28
+ if (message.tags && message.tags.length > 0) {
29
+ tagName = message.tags[0].name;
30
+ }
31
+ if (!resources[tagName]) {
32
+ resources[tagName] = {};
33
+ if (source.tags) {
34
+ for (let tag of source.tags) {
35
+ if (tag.name === tagName) {
36
+ resources[tagName].description = tag.description;
37
+ resources[tagName].externalDocs = tag.externalDocs;
38
+ }
39
+ }
40
+ }
41
+ }
42
+ if (!resources[tagName].topics) resources[tagName].topics = {};
43
+ resources[tagName].topics[t] = { messages: {}, parameters: topic.parameters };
44
+ resources[tagName].topics[t].messages[m] = message;
45
+ }
46
+ }
47
+ return resources;
48
+ }
49
+
50
+ function getParameters(params) {
51
+ for (let p of params) {
52
+ if (p === false) p = { schema: {} }; // for external $refs when resolve not true
53
+ if (!p.in) p.in = 'topic';
54
+ if (typeof p.required === 'undefined') p.required = true;
55
+ p.safeType = p.schema.type;
56
+ p.shortDesc = p.description;
57
+ }
58
+ return params;
59
+ }
60
+
61
+ function convertInner(api, options) {
62
+ return new Promise(function (resolve, reject) {
63
+ let data = {};
64
+ if (options.verbose) console.warn('starting deref', api.info.title);
65
+ data.api = dereference(api, api, { verbose: options.verbose, $ref: 'x-widdershins-oldRef' });
66
+ if (options.verbose) console.warn('finished deref');
67
+ data.version = (data.api.info && data.api.info.version && data.api.info.version.toLowerCase().startsWith('v') ? data.api.info.version : 'v' + (data.api.info && data.api.info.version ? data.api.info.version : '1.0.0'));
68
+ data.widdershins = require('../package.json');
69
+
70
+ let header = {};
71
+ header.title = api.info && api.info.title ? ' ' + data.version : ' 1.0.0';
72
+
73
+ header.language_tabs = options.language_tabs;
74
+ header.headingLevel = Math.max(options.headings || 0, 3);
75
+
76
+ header.toc_footers = [];
77
+ if (api.externalDocs) {
78
+ if (api.externalDocs.url) {
79
+ header.toc_footers.push('<a href="' + api.externalDocs.url + '">' + (api.externalDocs.description ? api.externalDocs.description : 'External Docs') + '</a>');
80
+ }
81
+ }
82
+ if (options.toc_footers) {
83
+ for (var key in options.toc_footers) {
84
+ header.toc_footers.push('<a href="' + options.toc_footers[key].url + '">' + options.toc_footers[key].description + '</a>');
85
+ }
86
+ }
87
+ header.includes = options.includes;
88
+ header.search = options.search;
89
+ header.code_clipboard = options.clipboard;
90
+ header.highlight_theme = options.theme;
91
+ header.generator = data.widdershins.name+' v'+data.widdershins.version;
92
+
93
+ if (typeof templates === 'undefined') {
94
+ templates = dot.process({ path: path.join(__dirname, '..', 'templates', 'asyncapi1') });
95
+ }
96
+ if (options.user_templates) {
97
+ templates = Object.assign(templates, dot.process({ path: options.user_templates }));
98
+ }
99
+
100
+ data.options = options;
101
+ data.header = header;
102
+ data.templates = templates;
103
+ data.translations = {};
104
+ templates.translations(data);
105
+ data.resources = convertToToc(data.api);
106
+
107
+ data.utils = {};
108
+ data.utils.inspect = util.inspect;
109
+ data.utils.yaml = yaml;
110
+ data.utils.getSample = common.getSample;
111
+ data.utils.getParameters = getParameters;
112
+ data.utils.schemaToArray = common.schemaToArray;
113
+ data.utils.getCodeSamples = common.getCodeSamples;
114
+ data.utils.slugify = common.slugify;
115
+
116
+ let content = '';
117
+ try {
118
+ if (!options.omitHeader) content += '---\n' + yaml.stringify(header) + '\n---\n\n';
119
+ content += templates.main(data);
120
+ content = common.removeDupeBlankLines(content);
121
+ }
122
+ catch (ex) {
123
+ return reject(ex);
124
+ }
125
+ content = common.removeDupeBlankLines(content);
126
+
127
+ if (options.html) content = common.html(content, header, options);
128
+
129
+ resolve(content);
130
+ });
131
+ }
132
+
133
+ function convert(api, options) {
134
+ api = preProcessor(api);
135
+
136
+ let defaults = {};
137
+ defaults.includes = [];
138
+ defaults.search = true;
139
+ defaults.clipboard = true;
140
+ defaults.theme = 'darkula';
141
+ defaults.language_tabs = [{ 'javascript--nodejs': 'Node.JS' }, { 'javascript': 'JavaScript' }, { 'ruby': 'Ruby' }, { 'python': 'Python' }, { 'java': 'Java' }, { 'go': 'Go' }];
142
+ defaults.sample = true;
143
+
144
+ options = Object.assign({}, defaults, options);
145
+ options.openapi = api;
146
+
147
+ return resolver.optionalResolve(options)
148
+ .then(function (options) {
149
+ return convertInner(options.openapi, options);
150
+ })
151
+ .catch(function (ex) {
152
+ throw ex;
153
+ });
154
+ }
155
+
156
+ module.exports = {
157
+ convert: convert
158
+ };
159
+