@team-supercharge/oasg 11.0.0 → 12.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/README.md +54 -40
- package/bin/merger.js +3 -1
- package/bin/oasg +46 -6
- package/bin/openapi-target.js +4 -108
- package/bin/process-source.js +134 -0
- package/config.schema.yml +10 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -285,8 +285,51 @@ Common source parameters
|
|
|
285
285
|
|-|-|-|-|
|
|
286
286
|
| id | Unique identifier | Y | - |
|
|
287
287
|
| type | Source type: `simple` / `merged` | N | `simple` |
|
|
288
|
+
| bundle | Bundle specification into a single file | N | `true` |
|
|
289
|
+
| sortSchemas | Sort `components.schemas` alphabetically | N | `true` |
|
|
290
|
+
| decorators | Array of files for decorator functions | N | `[]` |
|
|
291
|
+
| cleanup | Cleans specification from unused paths, tags and schemas | N | `true` |
|
|
288
292
|
| overrides | Override properties of the OpenApi file | N | - |
|
|
289
293
|
|
|
294
|
+
The source specification is by default bundled into a single file (resolving external dependencies) using the [@redocly/cli package's](https://www.npmjs.com/package/@redocly/cli) `bundle` command.
|
|
295
|
+
|
|
296
|
+
> ⚠️ It is higly recommended to keep `bundle` on if you plan to use the `openapi` target type, as external refs won't be part of the target artifact.
|
|
297
|
+
|
|
298
|
+
Decorator files must export a `decorate(document)` function which transforms the parsed OpenAPI document and returns it at the end of the function.
|
|
299
|
+
|
|
300
|
+
Full example with decorators:
|
|
301
|
+
|
|
302
|
+
```json
|
|
303
|
+
{
|
|
304
|
+
"id": "platform-api",
|
|
305
|
+
"type": "merged",
|
|
306
|
+
"inputs": [
|
|
307
|
+
"api/*.openapi.yaml"
|
|
308
|
+
],
|
|
309
|
+
"bundle": false,
|
|
310
|
+
"sortSchemas": false,
|
|
311
|
+
"decorators": ["decorators/one", "decorators/two"],
|
|
312
|
+
"cleanup": false
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
```js
|
|
317
|
+
// decorators/one.js
|
|
318
|
+
|
|
319
|
+
function decorate(document) {
|
|
320
|
+
document.info.title = 'My Custom Decorated Title';
|
|
321
|
+
|
|
322
|
+
return document;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
exports.decorate = decorate;
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
After the decorators have run, the specification is by default cleaned up (can be turned off by setting the `"cleanup": false` option):
|
|
329
|
+
- `paths` with no methods in them are removed
|
|
330
|
+
- `tags` with no endpoints using them are removed
|
|
331
|
+
- `components` that are unused are removed (only if `bundle` option is also enabled)
|
|
332
|
+
|
|
290
333
|
### Source Types
|
|
291
334
|
|
|
292
335
|
#### Simple
|
|
@@ -688,54 +731,25 @@ describe('Auth', function () {
|
|
|
688
731
|
|Parameter| Description| Required | Default |
|
|
689
732
|
|-|-|-|-|
|
|
690
733
|
| fileName | Name of the generated file | N | `openapi.yaml` |
|
|
691
|
-
| bundle | Bundle specification into a single file | N | `true` |
|
|
692
|
-
| sortSchemas | Sort `components.schemas` alphabetically | N | `true` |
|
|
693
|
-
| decorators | Array of files for decorator functions | N | `[]` |
|
|
694
|
-
| cleanup | Cleans specification from unused paths, tags and schemas | N | `true` |
|
|
695
|
-
|
|
696
|
-
The target specification is by default bundled into a single file (resolving external dependencies) using the [@redocly/cli package's](https://www.npmjs.com/package/@redocly/cli) `bundle` command.
|
|
697
|
-
|
|
698
|
-
> ⚠️ It is higly recommended to keep `bundle` on, as external refs won't be part of the target artifact.
|
|
699
734
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
Full example with decorators:
|
|
703
|
-
|
|
704
|
-
```json
|
|
705
|
-
{
|
|
706
|
-
"id": "api-docs",
|
|
707
|
-
"type": "openapi",
|
|
708
|
-
"source": "source-simple",
|
|
709
|
-
"fileName": "my-openapi-file.yaml",
|
|
710
|
-
"bundle": false,
|
|
711
|
-
"sortSchemas": false,
|
|
712
|
-
"decorators": ["decorators/one", "decorators/two"],
|
|
713
|
-
"cleanup": false
|
|
714
|
-
}
|
|
715
|
-
```
|
|
735
|
+
---
|
|
716
736
|
|
|
717
|
-
|
|
718
|
-
// decorators/one.js
|
|
737
|
+
# Migration Guide
|
|
719
738
|
|
|
720
|
-
|
|
721
|
-
document.info.title = 'My Custom Decorated Title';
|
|
739
|
+
This section covers the breaking changes and their migrations across major version upgrades.
|
|
722
740
|
|
|
723
|
-
|
|
724
|
-
}
|
|
741
|
+
## From `11.x.x` to `12.0.0`
|
|
725
742
|
|
|
726
|
-
|
|
727
|
-
```
|
|
743
|
+
The following options from the `openapi` target type has been moved to the [Source](#source) configuration.
|
|
728
744
|
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
745
|
+
* `bundle`
|
|
746
|
+
* `sortSchemas`
|
|
747
|
+
* `decorators`
|
|
748
|
+
* `cleanup`
|
|
733
749
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
# Migration Guide
|
|
750
|
+
Please update your `config.json` accordingly: move these properties - if they exist - to the respective `source` configuration.
|
|
737
751
|
|
|
738
|
-
|
|
752
|
+
> ⚠️ As the `bundle`, `sortSchemas` and `cleanup` flags are **enabled by default**, even without using custom decorators some normalization steps are applied to the source specification before generating the targets. If this causes any problems in your project (although highly unlikely it will) consider **disable** these flags.
|
|
739
753
|
|
|
740
754
|
## From `10.x.x` to `11.0.0`
|
|
741
755
|
|
package/bin/merger.js
CHANGED
|
@@ -26,6 +26,8 @@ async function merge(source) {
|
|
|
26
26
|
|
|
27
27
|
dump(document, outFile);
|
|
28
28
|
|
|
29
|
+
console.log(`merged input files =>\n ${inputFiles.join('\n ')}\ninto\n ${outFile}\n`);
|
|
30
|
+
|
|
29
31
|
return outFile;
|
|
30
32
|
}
|
|
31
33
|
|
|
@@ -41,7 +43,7 @@ async function getInputFilesWithGlob(inputs) {
|
|
|
41
43
|
const inputFiles = [];
|
|
42
44
|
for (const input of inputs) {
|
|
43
45
|
const files = await glob(input);
|
|
44
|
-
inputFiles.push(...files);
|
|
46
|
+
inputFiles.push(...files.sort());
|
|
45
47
|
}
|
|
46
48
|
const uniqueInputFiles = inputFiles.filter((value, index, self) => self.indexOf(value) === index);
|
|
47
49
|
return uniqueInputFiles;
|
package/bin/oasg
CHANGED
|
@@ -18,6 +18,7 @@ const { exec } = require(`${__dirname}/exec.js`);
|
|
|
18
18
|
const { merge } = require(`${__dirname}/merger.js`);
|
|
19
19
|
const { applyOverrides } = require(`${__dirname}/overrider.js`);
|
|
20
20
|
const { openApiTarget } = require(`${__dirname}/openapi-target.js`);
|
|
21
|
+
const { processSource } = require(`${__dirname}/process-source.js`);
|
|
21
22
|
const { globSync } = require('glob');
|
|
22
23
|
|
|
23
24
|
const projectPackageJson = JSON.parse(fs.readFileSync('package.json'));
|
|
@@ -215,6 +216,25 @@ async function parseConfig() {
|
|
|
215
216
|
config.sources = [DEFAULT_SOURCE];
|
|
216
217
|
}
|
|
217
218
|
|
|
219
|
+
// set default source values
|
|
220
|
+
config.sources.forEach(s => {
|
|
221
|
+
if (s.bundle === undefined) {
|
|
222
|
+
s.bundle = true;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (s.sortSchemas === undefined) {
|
|
226
|
+
s.sortSchemas = true;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (s.decorators === undefined) {
|
|
230
|
+
s.decorators = [];
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (s.cleanup === undefined) {
|
|
234
|
+
s.cleanup = true;
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
|
|
218
238
|
// set default source to targets with undefined
|
|
219
239
|
config.targets.forEach(t => {
|
|
220
240
|
if (!t.source) {
|
|
@@ -313,11 +333,19 @@ async function parseConfig() {
|
|
|
313
333
|
return config;
|
|
314
334
|
}
|
|
315
335
|
|
|
316
|
-
async function buildSources() {
|
|
336
|
+
async function buildSources(sourceIds) {
|
|
317
337
|
const sources = {};
|
|
318
338
|
|
|
319
339
|
for (var i = 0; i < config.sources.length; i++) {
|
|
320
340
|
const source = config.sources[i];
|
|
341
|
+
|
|
342
|
+
// only build sources that are defined
|
|
343
|
+
if (!sourceIds.includes(source.id)) {
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
console.log(`\n=====\n id:\t\t${source.id}\n type:\t\t${source.type}\n bundle:\t${source.bundle}\n sortSchemas:\t${source.sortSchemas}\n decorators: \t${JSON.stringify(source.decorators)}\n cleanup:\t${source.cleanup}\n---\n`);
|
|
348
|
+
|
|
321
349
|
let file;
|
|
322
350
|
|
|
323
351
|
switch (source.type) {
|
|
@@ -330,6 +358,7 @@ async function buildSources() {
|
|
|
330
358
|
break;
|
|
331
359
|
}
|
|
332
360
|
|
|
361
|
+
file = processSource(source, file, VERSION);
|
|
333
362
|
file = await applyOverrides(source, file);
|
|
334
363
|
|
|
335
364
|
sources[source.id] = file;
|
|
@@ -368,7 +397,7 @@ async function serve(argv) {
|
|
|
368
397
|
checkSourceId(sourceId);
|
|
369
398
|
|
|
370
399
|
// handle input
|
|
371
|
-
const sources = await buildSources();
|
|
400
|
+
const sources = await buildSources([sourceId]);
|
|
372
401
|
const input = sources[sourceId];
|
|
373
402
|
const document = YAML.load(input);
|
|
374
403
|
|
|
@@ -415,7 +444,7 @@ async function proxy(argv) {
|
|
|
415
444
|
checkSourceId(sourceId);
|
|
416
445
|
|
|
417
446
|
// handle input
|
|
418
|
-
const sources = await buildSources();
|
|
447
|
+
const sources = await buildSources([sourceId]);
|
|
419
448
|
const input = sources[sourceId];
|
|
420
449
|
const document = YAML.load(input);
|
|
421
450
|
|
|
@@ -446,7 +475,7 @@ async function generate(argv) {
|
|
|
446
475
|
VERSION = determineArtifactVersion(argv);
|
|
447
476
|
const preRelease = determinePreRelease(argv);
|
|
448
477
|
|
|
449
|
-
const sources = await buildSources();
|
|
478
|
+
const sources = await buildSources(sourceIdsFromTargetIds(targetIds));
|
|
450
479
|
|
|
451
480
|
console.log(`generate targets: ${targetIds}`);
|
|
452
481
|
|
|
@@ -455,7 +484,7 @@ async function generate(argv) {
|
|
|
455
484
|
|
|
456
485
|
// handle docs target
|
|
457
486
|
if (target.type === 'openapi') {
|
|
458
|
-
openApiTarget(target, sources[target.source]
|
|
487
|
+
openApiTarget(target, sources[target.source]);
|
|
459
488
|
return;
|
|
460
489
|
}
|
|
461
490
|
|
|
@@ -478,7 +507,7 @@ async function generate(argv) {
|
|
|
478
507
|
// publish
|
|
479
508
|
async function publish(argv) {
|
|
480
509
|
const targetIds = determineTargetIds(argv);
|
|
481
|
-
const sources = await buildSources();
|
|
510
|
+
const sources = await buildSources(sourceIdsFromTargetIds(targetIds));
|
|
482
511
|
const preRelease = determinePreRelease(argv);
|
|
483
512
|
|
|
484
513
|
console.log(`publish targets: ${targetIds}`);
|
|
@@ -630,6 +659,17 @@ function determineTargetIds(argv) {
|
|
|
630
659
|
return targetIds;
|
|
631
660
|
}
|
|
632
661
|
|
|
662
|
+
function sourceIdsFromTargetIds(targetIds) {
|
|
663
|
+
const sourceIds = [];
|
|
664
|
+
targetIds.forEach(targetId => {
|
|
665
|
+
const target = config.targets.find(t => t.id === targetId);
|
|
666
|
+
if (target.source) {
|
|
667
|
+
sourceIds.push(target.source);
|
|
668
|
+
}
|
|
669
|
+
});
|
|
670
|
+
return sourceIds;
|
|
671
|
+
}
|
|
672
|
+
|
|
633
673
|
function validTargetIds() {
|
|
634
674
|
return config.targets.map(t => t.id);
|
|
635
675
|
}
|
package/bin/openapi-target.js
CHANGED
|
@@ -2,128 +2,24 @@ const fs = require('fs');
|
|
|
2
2
|
const SwaggerParser = require("@apidevtools/swagger-parser");
|
|
3
3
|
|
|
4
4
|
const { dump } = require(`${__dirname}/dump.js`);
|
|
5
|
-
const { exec } = require(`${__dirname}/exec.js`);
|
|
6
5
|
|
|
7
|
-
function
|
|
8
|
-
exec(`npx redocly bundle ${source} --keep-url-references${remove ? ' --remove-unused-components' : ''} --output ${target}`, 'pipe');
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
async function openApiTarget(target, source, version) {
|
|
6
|
+
async function openApiTarget(target, source) {
|
|
12
7
|
const outDir = `out/${target.id}`;
|
|
13
8
|
let outFile = target.fileName || `openapi.yaml`;
|
|
14
9
|
outFile = `${outDir}/${outFile}`;
|
|
15
10
|
|
|
16
11
|
if (fs.existsSync(outDir)) {
|
|
17
|
-
fs.
|
|
12
|
+
fs.rmSync(outDir, { recursive: true })
|
|
18
13
|
}
|
|
19
14
|
fs.mkdirSync(outDir, { recursive: true });
|
|
20
15
|
|
|
21
|
-
// bundle spec to a single file by default
|
|
22
|
-
const bundle = target.bundle === undefined || target.bundle;
|
|
23
|
-
if (bundle) {
|
|
24
|
-
bundleSpec(source, outFile, false);
|
|
25
|
-
console.log(`bundled specification`);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
16
|
// parse document
|
|
29
|
-
let document = await SwaggerParser.parse(
|
|
30
|
-
|
|
31
|
-
// set version of target document
|
|
32
|
-
document.info.version = version;
|
|
33
|
-
|
|
34
|
-
// sort schemas alphabeticaly
|
|
35
|
-
const sortSchemas = target.sortSchemas === undefined || target.sortSchemas;
|
|
36
|
-
if (sortSchemas) {
|
|
37
|
-
document.components.schemas = Object.keys(document.components.schemas)
|
|
38
|
-
.sort()
|
|
39
|
-
.reduce((obj, key) => { obj[key] = document.components.schemas[key]; return obj; }, {});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// apply custom decorators
|
|
43
|
-
const decorators = target.decorators || [];
|
|
44
|
-
decorators.forEach(d => {
|
|
45
|
-
const decoratorFile = `${d}.js`;
|
|
46
|
-
const decoratorPath = `${process.cwd()}/${decoratorFile}`;
|
|
47
|
-
|
|
48
|
-
if (!fs.existsSync(decoratorPath)) {
|
|
49
|
-
console.error(`skipped decorator '${decoratorFile}' - file does not exist`);
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const decorator = require(decoratorPath);
|
|
54
|
-
|
|
55
|
-
if (!decorator.decorate) {
|
|
56
|
-
console.error(`skipped decorator '${decoratorFile}' - must export a 'decorate(document)' function`);
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
document = decorator.decorate(document);
|
|
17
|
+
let document = await SwaggerParser.parse(source);
|
|
61
18
|
|
|
62
|
-
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
// clean up unused things
|
|
66
|
-
const cleanup = target.cleanup === undefined || target.cleanup;
|
|
67
|
-
if (cleanup) {
|
|
68
|
-
const removedPaths = [];
|
|
69
|
-
const removedTags = [];
|
|
70
|
-
const usedTags = [];
|
|
71
|
-
|
|
72
|
-
// remove empty paths & gather tags
|
|
73
|
-
const paths = document.paths;
|
|
74
|
-
for (const pathKey in paths) {
|
|
75
|
-
const path = document.paths[pathKey];
|
|
76
|
-
|
|
77
|
-
if (Object.keys(path).length === 0) {
|
|
78
|
-
removedPaths.push(pathKey);
|
|
79
|
-
delete paths[pathKey];
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
for (const methodKey in path) {
|
|
83
|
-
const operation = path[methodKey];
|
|
84
|
-
|
|
85
|
-
operation.tags.forEach(tag => {
|
|
86
|
-
if (!usedTags.includes(tag)) {
|
|
87
|
-
usedTags.push(tag);
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (removedPaths.length !== 0) {
|
|
94
|
-
console.log(`cleaned up unused paths:\n ${removedPaths.join('\n ')}`);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// remouve unused tags
|
|
98
|
-
const tags = [...document.tags];
|
|
99
|
-
tags.forEach(tag => {
|
|
100
|
-
if (!usedTags.includes(tag.name)) {
|
|
101
|
-
removedTags.push(tag.name);
|
|
102
|
-
document.tags.splice(document.tags.indexOf(tag), 1);
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
if (removedTags.length !== 0) {
|
|
107
|
-
console.log(`cleaned up unused tags:\n ${removedTags.join('\n ')}`);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
19
|
+
// target-specific functions, e.g. write as JSON
|
|
110
20
|
|
|
111
21
|
// write file
|
|
112
22
|
dump(document, outFile);
|
|
113
|
-
|
|
114
|
-
// re-bundle to remove unused components
|
|
115
|
-
if (cleanup && bundle) {
|
|
116
|
-
console.log(`re-bundling specification to remove unused components`);
|
|
117
|
-
let currentSize;
|
|
118
|
-
let bundledSize;
|
|
119
|
-
do {
|
|
120
|
-
currentSize = fs.statSync(outFile).size;
|
|
121
|
-
bundleSpec(outFile, outFile, true);
|
|
122
|
-
console.log('.');
|
|
123
|
-
bundledSize = fs.statSync(outFile).size;
|
|
124
|
-
}
|
|
125
|
-
while (bundledSize !== currentSize)
|
|
126
|
-
}
|
|
127
23
|
}
|
|
128
24
|
|
|
129
25
|
exports.openApiTarget = openApiTarget;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
const crypto = require('crypto');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const SwaggerParser = require("@apidevtools/swagger-parser");
|
|
4
|
+
|
|
5
|
+
const { dump } = require(`${__dirname}/dump.js`);
|
|
6
|
+
const { exec } = require(`${__dirname}/exec.js`);
|
|
7
|
+
|
|
8
|
+
function bundleSpec(source, target, remove) {
|
|
9
|
+
exec(`npx redocly bundle ${source} --keep-url-references${remove ? ' --remove-unused-components' : ''} --output ${target}`, 'pipe');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function processSource(source, sourceFile, version) {
|
|
13
|
+
const outDir = `./out/.tmp`;
|
|
14
|
+
let outFile;
|
|
15
|
+
|
|
16
|
+
if (!sourceFile.startsWith(outDir)) {
|
|
17
|
+
if (!fs.existsSync(outDir)) {
|
|
18
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
outFile = `${outDir}/${source.id}-${crypto.randomBytes(4).readUInt32LE(0)}.yaml`;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
outFile = sourceFile;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// bundle spec to a single file by default
|
|
28
|
+
if (source.bundle) {
|
|
29
|
+
bundleSpec(sourceFile, outFile, false);
|
|
30
|
+
console.log(`bundled specification`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// parse document
|
|
34
|
+
let document = await SwaggerParser.parse(source.bundle ? outFile : sourceFile);
|
|
35
|
+
|
|
36
|
+
// set version of target document
|
|
37
|
+
document.info.version = version;
|
|
38
|
+
|
|
39
|
+
// sort schemas alphabeticaly
|
|
40
|
+
if (source.sortSchemas) {
|
|
41
|
+
document.components.schemas = Object.keys(document.components.schemas)
|
|
42
|
+
.sort()
|
|
43
|
+
.reduce((obj, key) => { obj[key] = document.components.schemas[key]; return obj; }, {});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// apply custom decorators
|
|
47
|
+
const decorators = source.decorators || [];
|
|
48
|
+
decorators.forEach(d => {
|
|
49
|
+
const decoratorFile = `${d}.js`;
|
|
50
|
+
const decoratorPath = `${process.cwd()}/${decoratorFile}`;
|
|
51
|
+
|
|
52
|
+
if (!fs.existsSync(decoratorPath)) {
|
|
53
|
+
console.error(`skipped decorator '${decoratorFile}' - file does not exist`);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const decorator = require(decoratorPath);
|
|
58
|
+
|
|
59
|
+
if (!decorator.decorate) {
|
|
60
|
+
console.error(`skipped decorator '${decoratorFile}' - must export a 'decorate(document)' function`);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
document = decorator.decorate(document);
|
|
65
|
+
|
|
66
|
+
console.log(`applied decorator '${decoratorFile}'`);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// clean up unused things
|
|
70
|
+
if (source.cleanup) {
|
|
71
|
+
const removedPaths = [];
|
|
72
|
+
const removedTags = [];
|
|
73
|
+
const usedTags = [];
|
|
74
|
+
|
|
75
|
+
// remove empty paths & gather tags
|
|
76
|
+
const paths = document.paths;
|
|
77
|
+
for (const pathKey in paths) {
|
|
78
|
+
const path = document.paths[pathKey];
|
|
79
|
+
|
|
80
|
+
if (Object.keys(path).length === 0) {
|
|
81
|
+
removedPaths.push(pathKey);
|
|
82
|
+
delete paths[pathKey];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
for (const methodKey in path) {
|
|
86
|
+
const operation = path[methodKey];
|
|
87
|
+
|
|
88
|
+
operation.tags.forEach(tag => {
|
|
89
|
+
if (!usedTags.includes(tag)) {
|
|
90
|
+
usedTags.push(tag);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (removedPaths.length !== 0) {
|
|
97
|
+
console.log(`cleaned up unused paths:\n ${removedPaths.join('\n ')}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// remouve unused tags
|
|
101
|
+
const tags = [...document.tags];
|
|
102
|
+
tags.forEach(tag => {
|
|
103
|
+
if (!usedTags.includes(tag.name)) {
|
|
104
|
+
removedTags.push(tag.name);
|
|
105
|
+
document.tags.splice(document.tags.indexOf(tag), 1);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
if (removedTags.length !== 0) {
|
|
110
|
+
console.log(`cleaned up unused tags:\n ${removedTags.join('\n ')}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// write file
|
|
115
|
+
dump(document, outFile);
|
|
116
|
+
|
|
117
|
+
// re-bundle to remove unused components
|
|
118
|
+
if (source.cleanup && source.bundle) {
|
|
119
|
+
console.log(`re-bundling specification to remove unused components`);
|
|
120
|
+
let currentSize;
|
|
121
|
+
let bundledSize;
|
|
122
|
+
do {
|
|
123
|
+
currentSize = fs.statSync(outFile).size;
|
|
124
|
+
bundleSpec(outFile, outFile, true);
|
|
125
|
+
console.log('.');
|
|
126
|
+
bundledSize = fs.statSync(outFile).size;
|
|
127
|
+
}
|
|
128
|
+
while (bundledSize !== currentSize)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return outFile;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
exports.processSource = processSource;
|
package/config.schema.yml
CHANGED
|
@@ -37,6 +37,16 @@ sources:
|
|
|
37
37
|
pattern: '^[a-zA-Z0-9]+([-_][a-zA-Z0-9]+)*$' # only kebab-case or snake_case IDs
|
|
38
38
|
type:
|
|
39
39
|
type: string
|
|
40
|
+
bundle:
|
|
41
|
+
type: boolean
|
|
42
|
+
sortSchemas:
|
|
43
|
+
type: boolean
|
|
44
|
+
decorators:
|
|
45
|
+
type: array
|
|
46
|
+
items:
|
|
47
|
+
type: string
|
|
48
|
+
cleanup:
|
|
49
|
+
type: boolean
|
|
40
50
|
overrides:
|
|
41
51
|
$ref: '#/definitions/SourceOverrides'
|
|
42
52
|
required:
|
|
@@ -320,16 +330,6 @@ targets:
|
|
|
320
330
|
pattern: "^openapi$"
|
|
321
331
|
fileName:
|
|
322
332
|
type: string
|
|
323
|
-
bundle:
|
|
324
|
-
type: boolean
|
|
325
|
-
sortSchemas:
|
|
326
|
-
type: boolean
|
|
327
|
-
decorators:
|
|
328
|
-
type: array
|
|
329
|
-
items:
|
|
330
|
-
type: string
|
|
331
|
-
cleanup:
|
|
332
|
-
type: boolean
|
|
333
333
|
|
|
334
334
|
definitions:
|
|
335
335
|
# default
|
package/package.json
CHANGED