@team-supercharge/oasg 11.0.0 → 12.0.1

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 CHANGED
@@ -70,11 +70,7 @@ For detailed configuration instructions please refer to the [Configuration](#con
70
70
 
71
71
  ## Configure Linter
72
72
 
73
- Create a `.spectral.yaml` or `.spectral.js` file in the repo's root directory with the following minimal content:
74
-
75
- ```yaml
76
- extends: '@team-supercharge/oasg/ruleset'
77
- ```
73
+ Create a `.spectral.js` file in the repo's root directory with the following minimal content:
78
74
 
79
75
  ```js
80
76
  import oasgRuleset from '@team-supercharge/oasg/ruleset';
@@ -84,9 +80,9 @@ export default {
84
80
  };
85
81
  ```
86
82
 
87
- The file is used for configuring [Spectral](https://stoplight.io/open-source/spectral/) on a per-project basis and will enable usage of custom _OASg_ rulesets.
83
+ The file is used for configuring [Spectral](https://stoplight.io/open-source/spectral/) on a per-project basis and will enable usage of the base _OASg_ ruleset and enables to add project-specific rules as well. For detailed instructions on how to use custom rules refer to the [Linter rules](#linter-rules) section below.
88
84
 
89
- For detailed instructions on how to use custom rules refer to the [Linter rules](#linter-rules) section below.
85
+ > ⚠️ The YAML ruleset format is highly discouraged to be used, as it won't work seamlessly [when being used with Docker](#use-with-docker).
90
86
 
91
87
  ## Use with Docker
92
88
 
@@ -160,17 +156,43 @@ $ npx oasg publish [target]
160
156
 
161
157
  _OASg_ makes use of the awesome [Spectral](https://stoplight.io/open-source/spectral/) project to provide linting capabilities and comes with a set of more strict default rules we call the _OASg Ruleset_ which is defined in the file [ruleset/ruleset.js](ruleset/ruleset.js) and exported as `@team-supercharge/oasg/ruleset` for the outside world.
162
158
 
163
- The project's `.spectral.yaml` or `.spectral.js` file in the API repository should be always configured to extend the default _OASg Ruleset_ using the methods described in the [Configure linter](#configure-linter) section.
159
+ The project's `.spectral.js` file in the API repository should be always configured to extend the default _OASg Ruleset_ using the methods described in the [Configure linter](#configure-linter) section.
164
160
 
165
161
  Rules defined in the ruleset can be split into two categories:
166
162
 
167
163
  * **default rules** - rules that are enabled by default, can be disabled with the `rule-name: off` syntax
168
164
  * **opt-in rules** - rules that need to be explicitly enabled with the `rule-name: true` syntax
169
165
 
170
- You can further customize the ruleset in `.spectral.yaml` according to the [documentation](https://meta.stoplight.io/docs/spectral/docs/guides/4-custom-rulesets.md) using the `rules:` property.
166
+ You can further customize the ruleset in `.spectral.js` according to the [documentation](https://meta.stoplight.io/docs/spectral/docs/guides/4-custom-rulesets.md) using the `rules:` property.
171
167
 
172
168
  An example configuration can be found here:
173
169
 
170
+ ```js
171
+ import oasgRuleset from '@team-supercharge/oasg/ruleset';
172
+
173
+ import { truthy } from '@stoplight/spectral-functions';
174
+
175
+ export default {
176
+ extends: oasgRuleset,
177
+ rules: {
178
+ 'oasg-object-names-pascal-case': false, // disables a default rule - don't do this in general :)
179
+ 'oasg-object-names-api-model-suffix': true, // enables a non-default rule
180
+
181
+ 'oasg-path-casing-kebab': false, // switches a default rule to another one
182
+ 'oasg-path-casing-pascal': true,
183
+
184
+ 'my-rule-name': { // adds a fully custom rule which applies to only your project
185
+ description: 'Tags must have a description.',
186
+ given: '$.tags[*]',
187
+ severity: 'error',
188
+ then: {
189
+ field: 'description',
190
+ function: truthy
191
+ }
192
+ }
193
+ ```
194
+
195
+
174
196
  ```yaml
175
197
  extends: '@team-supercharge/oasg/ruleset'
176
198
 
@@ -285,8 +307,51 @@ Common source parameters
285
307
  |-|-|-|-|
286
308
  | id | Unique identifier | Y | - |
287
309
  | type | Source type: `simple` / `merged` | N | `simple` |
310
+ | bundle | Bundle specification into a single file | N | `true` |
311
+ | sortSchemas | Sort `components.schemas` alphabetically | N | `true` |
312
+ | decorators | Array of files for decorator functions | N | `[]` |
313
+ | cleanup | Cleans specification from unused paths, tags and schemas | N | `true` |
288
314
  | overrides | Override properties of the OpenApi file | N | - |
289
315
 
316
+ 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.
317
+
318
+ > ⚠️ 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.
319
+
320
+ Decorator files must export a `decorate(document)` function which transforms the parsed OpenAPI document and returns it at the end of the function.
321
+
322
+ Full example with decorators:
323
+
324
+ ```json
325
+ {
326
+ "id": "platform-api",
327
+ "type": "merged",
328
+ "inputs": [
329
+ "api/*.openapi.yaml"
330
+ ],
331
+ "bundle": false,
332
+ "sortSchemas": false,
333
+ "decorators": ["decorators/one", "decorators/two"],
334
+ "cleanup": false
335
+ }
336
+ ```
337
+
338
+ ```js
339
+ // decorators/one.js
340
+
341
+ function decorate(document) {
342
+ document.info.title = 'My Custom Decorated Title';
343
+
344
+ return document;
345
+ }
346
+
347
+ exports.decorate = decorate;
348
+ ```
349
+
350
+ After the decorators have run, the specification is by default cleaned up (can be turned off by setting the `"cleanup": false` option):
351
+ - `paths` with no methods in them are removed
352
+ - `tags` with no endpoints using them are removed
353
+ - `components` that are unused are removed (only if `bundle` option is also enabled)
354
+
290
355
  ### Source Types
291
356
 
292
357
  #### Simple
@@ -688,74 +753,51 @@ describe('Auth', function () {
688
753
  |Parameter| Description| Required | Default |
689
754
  |-|-|-|-|
690
755
  | 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
-
700
- Decorator files must export a `decorate(document)` function which transforms the parsed OpenAPI document and returns it at the end of the function.
701
-
702
- Full example with decorators:
703
756
 
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
- ```
716
-
717
- ```js
718
- // decorators/one.js
757
+ ---
719
758
 
720
- function decorate(document) {
721
- document.info.title = 'My Custom Decorated Title';
759
+ # Migration Guide
722
760
 
723
- return document;
724
- }
761
+ This section covers the breaking changes and their migrations across major version upgrades.
725
762
 
726
- exports.decorate = decorate;
727
- ```
763
+ ## From `11.x.x` to `12.0.0`
728
764
 
729
- After the decorators have run, the specification is by default cleaned up (can be turned off by setting the `"cleanup": false` option):
730
- - `paths` with no methods in them are removed
731
- - `tags` with no endpoints using them are removed
732
- - `components` that are unused are removed (only if `bundle` option is also enabled)
765
+ The following options from the `openapi` target type has been moved to the [Source](#source) configuration.
733
766
 
734
- ---
767
+ * `bundle`
768
+ * `sortSchemas`
769
+ * `decorators`
770
+ * `cleanup`
735
771
 
736
- # Migration Guide
772
+ Please update your `config.json` accordingly: move these properties - if they exist - to the respective `source` configuration.
737
773
 
738
- This section covers the breaking changes and their migrations across major version upgrades.
774
+ > ⚠️ 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
775
 
740
776
  ## From `10.x.x` to `11.0.0`
741
777
 
742
778
  ### Linting
743
779
 
744
- If no [Custom Functions](#custom-functions) were used in `.spectral.yaml`, the YAML format can be kept, just update the reference in the `extends` statement:
780
+ The linter configuration previously stored in `.spectral.yaml` needs to be updated to the JS format:
781
+
782
+ 1. rename the file to `.spectral.js` (don't forget to update `spectral.rulesetFile` in `.vscode/settings.json` if used!)
783
+ 2. follow the [instructions](#configure-linter) for extending the base _OASg_ ruleset in JS
784
+ 3. migrate project-specific custom [rules](#custom-rules) and [functions](#custom-functions) to JS format
785
+
786
+ The typical minimal configuration from this in YAML
745
787
 
746
788
  ```yaml
747
- # old one
748
789
  extends: '@team-supercharge/oasg/rules/default.yaml'
749
-
750
- # new one
751
- extends: '@team-supercharge/oasg/ruleset'
752
790
  ```
753
791
 
754
- If there were any custom functions used in `.spectral.yaml`, it needs to be updated to the JS format:
792
+ must become this in JS (if no custom rules or functions are used):
755
793
 
756
- 1. rename the file to `.spectral.js` (don't forget to update `spectral.rulesetFile` in `.vscode/settings.json`!)
757
- 2. follow the [instructions](#configure-linter) for extending the base _OASg_ ruleset in JS
758
- 3. migrate project-specific custom [rules](#custom-rules) and [functions](#custom-functions) to JS format
794
+ ```js
795
+ import oasgRuleset from '@team-supercharge/oasg/ruleset';
796
+
797
+ export default {
798
+ extends: oasgRuleset
799
+ }
800
+ ```
759
801
 
760
802
  ## From `9.x.x` to `10.0.0`
761
803
 
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], VERSION);
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
  }
@@ -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 bundleSpec(source, target, remove) {
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.rmdirSync(outDir, { recursive: true })
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(bundle ? outFile : source);
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
- console.log(`applied decorator '${decoratorFile}'`);
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,138 @@
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 && document.components && document.components.schemas) {
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 maybeOperation = path[methodKey];
87
+
88
+ if (!maybeOperation.tags) {
89
+ continue;
90
+ }
91
+
92
+ maybeOperation.tags.forEach(tag => {
93
+ if (!usedTags.includes(tag)) {
94
+ usedTags.push(tag);
95
+ }
96
+ });
97
+ }
98
+ }
99
+
100
+ if (removedPaths.length !== 0) {
101
+ console.log(`cleaned up unused paths:\n ${removedPaths.join('\n ')}`);
102
+ }
103
+
104
+ // remouve unused tags
105
+ const tags = [...document.tags];
106
+ tags.forEach(tag => {
107
+ if (!usedTags.includes(tag.name)) {
108
+ removedTags.push(tag.name);
109
+ document.tags.splice(document.tags.indexOf(tag), 1);
110
+ }
111
+ });
112
+
113
+ if (removedTags.length !== 0) {
114
+ console.log(`cleaned up unused tags:\n ${removedTags.join('\n ')}`);
115
+ }
116
+ }
117
+
118
+ // write file
119
+ dump(document, outFile);
120
+
121
+ // re-bundle to remove unused components
122
+ if (source.cleanup && source.bundle) {
123
+ console.log(`re-bundling specification to remove unused components`);
124
+ let currentSize;
125
+ let bundledSize;
126
+ do {
127
+ currentSize = fs.statSync(outFile).size;
128
+ bundleSpec(outFile, outFile, true);
129
+ console.log('.');
130
+ bundledSize = fs.statSync(outFile).size;
131
+ }
132
+ while (bundledSize !== currentSize)
133
+ }
134
+
135
+ return outFile;
136
+ }
137
+
138
+ 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@team-supercharge/oasg",
3
- "version": "11.0.0",
3
+ "version": "12.0.1",
4
4
  "description": "Node-based tool to lint OpenAPI documents and generate clients, servers and documentation from them",
5
5
  "author": "Supercharge",
6
6
  "license": "MIT",