Repository: Redocly/openapi-cli
Pull request #227 (2020-11-20 11:32:29)

feat: bundle document before gathering stats


Patch:
diff --git a/package-lock.json b/package-lock.json
index 17da9705..4c838b81 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -39,9 +39,9 @@
       }
     },
     "node_modules/@babel/core": {
-      "version": "7.12.8",
-      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.8.tgz",
-      "integrity": "sha512-ra28JXL+5z73r1IC/t+FT1ApXU5LsulFDnTDntNfLQaScJUJmcHL5Qxm/IWanCToQk3bPWQo5bflbplU5r15pg==",
+      "version": "7.12.9",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz",
+      "integrity": "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==",
       "dev": true,
       "dependencies": {
         "@babel/code-frame": "^7.10.4",
@@ -50,7 +50,7 @@
         "@babel/helpers": "^7.12.5",
         "@babel/parser": "^7.12.7",
         "@babel/template": "^7.12.7",
-        "@babel/traverse": "^7.12.8",
+        "@babel/traverse": "^7.12.9",
         "@babel/types": "^7.12.7",
         "convert-source-map": "^1.7.0",
         "debug": "^4.1.0",
@@ -465,9 +465,9 @@
       }
     },
     "node_modules/@babel/traverse": {
-      "version": "7.12.8",
-      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.8.tgz",
-      "integrity": "sha512-EIRQXPTwFEGRZyu6gXbjfpNORN1oZvwuzJbxcXjAgWV0iqXYDszN1Hx3FVm6YgZfu1ZQbCVAk3l+nIw95Xll9Q==",
+      "version": "7.12.9",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.9.tgz",
+      "integrity": "sha512-iX9ajqnLdoU1s1nHt36JDI9KG4k+vmI8WgjK5d+aDTwQbL2fUnzedNedssA645Ede3PM2ma1n8Q4h2ohwXgMXw==",
       "dev": true,
       "dependencies": {
         "@babel/code-frame": "^7.10.4",
@@ -940,9 +940,9 @@
       "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA=="
     },
     "node_modules/@types/node": {
-      "version": "14.14.9",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.9.tgz",
-      "integrity": "sha512-JsoLXFppG62tWTklIoO4knA+oDTYsmqWxHRvd4lpmfQRNhX6osheUOWETP2jMoV/2bEHuMra8Pp3Dmo/stBFcw=="
+      "version": "14.14.10",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.10.tgz",
+      "integrity": "sha512-J32dgx2hw8vXrSbu4ZlVhn1Nm3GbeCFNw2FWL8S5QKucHGY0cyNwjdQdO+KMBZ4wpmC7KhLCiNsdk1RFRIYUQQ=="
     },
     "node_modules/@types/node-fetch": {
       "version": "2.5.7",
@@ -2014,6 +2014,19 @@
         "fsevents": "~2.1.2"
       }
     },
+    "node_modules/chokidar/node_modules/fsevents": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
+      "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+      "hasInstallScript": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
     "node_modules/chownr": {
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
@@ -3526,9 +3539,10 @@
       "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
     },
     "node_modules/fsevents": {
-      "version": "2.1.3",
-      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
-      "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.2.1.tgz",
+      "integrity": "sha512-bTLYHSeC0UH/EFXS9KqWnXuOl/wHK5Z/d+ghd5AsFMYN7wIGkUCOJyzy88+wJKkZPGON8u4Z9f6U4FdgURE9qA==",
+      "dev": true,
       "hasInstallScript": true,
       "optional": true,
       "os": [
@@ -8318,6 +8332,18 @@
         "node": ">=4.2.0"
       }
     },
+    "node_modules/uglify-js": {
+      "version": "3.12.0",
+      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.0.tgz",
+      "integrity": "sha512-8lBMSkFZuAK7gGF8LswsXmir8eX8d2AAMOnxSDWjKBx/fBR6MypQjs78m6ML9zQVp1/hD4TBdfeMZMC7nW1TAA==",
+      "extraneous": true,
+      "bin": {
+        "uglifyjs": "bin/uglifyjs"
+      },
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
     "node_modules/union-value": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
@@ -9813,9 +9839,9 @@
       }
     },
     "@babel/core": {
-      "version": "7.12.8",
-      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.8.tgz",
-      "integrity": "sha512-ra28JXL+5z73r1IC/t+FT1ApXU5LsulFDnTDntNfLQaScJUJmcHL5Qxm/IWanCToQk3bPWQo5bflbplU5r15pg==",
+      "version": "7.12.9",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz",
+      "integrity": "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==",
       "dev": true,
       "requires": {
         "@babel/code-frame": "^7.10.4",
@@ -9824,7 +9850,7 @@
         "@babel/helpers": "^7.12.5",
         "@babel/parser": "^7.12.7",
         "@babel/template": "^7.12.7",
-        "@babel/traverse": "^7.12.8",
+        "@babel/traverse": "^7.12.9",
         "@babel/types": "^7.12.7",
         "convert-source-map": "^1.7.0",
         "debug": "^4.1.0",
@@ -10175,9 +10201,9 @@
       }
     },
     "@babel/traverse": {
-      "version": "7.12.8",
-      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.8.tgz",
-      "integrity": "sha512-EIRQXPTwFEGRZyu6gXbjfpNORN1oZvwuzJbxcXjAgWV0iqXYDszN1Hx3FVm6YgZfu1ZQbCVAk3l+nIw95Xll9Q==",
+      "version": "7.12.9",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.9.tgz",
+      "integrity": "sha512-iX9ajqnLdoU1s1nHt36JDI9KG4k+vmI8WgjK5d+aDTwQbL2fUnzedNedssA645Ede3PM2ma1n8Q4h2ohwXgMXw==",
       "dev": true,
       "requires": {
         "@babel/code-frame": "^7.10.4",
@@ -10628,9 +10654,9 @@
       "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA=="
     },
     "@types/node": {
-      "version": "14.14.9",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.9.tgz",
-      "integrity": "sha512-JsoLXFppG62tWTklIoO4knA+oDTYsmqWxHRvd4lpmfQRNhX6osheUOWETP2jMoV/2bEHuMra8Pp3Dmo/stBFcw=="
+      "version": "14.14.10",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.10.tgz",
+      "integrity": "sha512-J32dgx2hw8vXrSbu4ZlVhn1Nm3GbeCFNw2FWL8S5QKucHGY0cyNwjdQdO+KMBZ4wpmC7KhLCiNsdk1RFRIYUQQ=="
     },
     "@types/node-fetch": {
       "version": "2.5.7",
@@ -11544,6 +11570,14 @@
         "is-glob": "~4.0.1",
         "normalize-path": "~3.0.0",
         "readdirp": "~3.5.0"
+      },
+      "dependencies": {
+        "fsevents": {
+          "version": "2.1.3",
+          "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
+          "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+          "optional": true
+        }
       }
     },
     "chownr": {
@@ -12799,9 +12833,10 @@
       "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
     },
     "fsevents": {
-      "version": "2.1.3",
-      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
-      "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.2.1.tgz",
+      "integrity": "sha512-bTLYHSeC0UH/EFXS9KqWnXuOl/wHK5Z/d+ghd5AsFMYN7wIGkUCOJyzy88+wJKkZPGON8u4Z9f6U4FdgURE9qA==",
+      "dev": true,
       "optional": true
     },
     "function-bind": {
@@ -16568,6 +16603,12 @@
       "integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==",
       "dev": true
     },
+    "uglify-js": {
+      "version": "3.12.0",
+      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.0.tgz",
+      "integrity": "sha512-8lBMSkFZuAK7gGF8LswsXmir8eX8d2AAMOnxSDWjKBx/fBR6MypQjs78m6ML9zQVp1/hD4TBdfeMZMC7nW1TAA==",
+      "extraneous": true
+    },
     "union-value": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
diff --git a/packages/cli/src/commands/bundle.ts b/packages/cli/src/commands/bundle.ts
index 35c8d3ad..e7eb7985 100644
--- a/packages/cli/src/commands/bundle.ts
+++ b/packages/cli/src/commands/bundle.ts
@@ -30,10 +30,9 @@ version: string
   config.lint.skipRules(argv['skip-rule']);
   config.lint.skipPreprocessors(argv['skip-preprocessor']);
   config.lint.skipDecorators(argv['skip-decorator']);
-
   const entrypoints = await getFallbackEntryPointsOrExit(argv.entrypoints, config);
-
   const totals: Totals = { errors: 0, warnings: 0, ignored: 0 };
+
   for (const entrypoint of entrypoints) {
     try {
       const startedAt = performance.now();
@@ -45,7 +44,6 @@ version: string
       });
 
       const fileTotals = getTotals(problems);
-
       const { outputFile, ext } = getOutputFileName(
         entrypoint,
         entrypoints.length,
@@ -55,10 +53,10 @@ version: string
 
       if (fileTotals.errors === 0 || argv.force) {
         if (!argv.output) {
-          const output = dumpBundle(result, argv.ext || 'yaml', argv.dereferenced);
+          const output = dumpBundle(result.parsed, argv.ext || 'yaml', argv.dereferenced);
           process.stdout.write(output);
         } else {
-          const output = dumpBundle(result, ext, argv.dereferenced);
+          const output = dumpBundle(result.parsed, ext, argv.dereferenced);
           saveBundle(outputFile, output);
         }
       }
diff --git a/packages/cli/src/commands/join.ts b/packages/cli/src/commands/join.ts
index a18aa728..d7110bdd 100644
--- a/packages/cli/src/commands/join.ts
+++ b/packages/cli/src/commands/join.ts
@@ -22,7 +22,8 @@ import {
   printExecutionTime,
   handleError,
   printLintTotals,
-  writeYaml
+  writeYaml,
+  exitWithError
 } from '../utils';
 import { isObject, isString } from '../js-utils';
 
@@ -432,11 +433,6 @@ async function validateEntrypoint(document: Document, config: LintConfig, extern
   }
 }
 
-function exitWithError(message: string) {
-  process.stderr.write(red(message));
-  process.exit(1);
-}
-
 function crawl(object: any, visitor: any) {
   if (!isObject(object)) return;
   for (const key of Object.keys(object)) {
diff --git a/packages/cli/src/commands/preview-docs/index.ts b/packages/cli/src/commands/preview-docs/index.ts
index 2499c579..36ddd4d5 100644
--- a/packages/cli/src/commands/preview-docs/index.ts
+++ b/packages/cli/src/commands/preview-docs/index.ts
@@ -55,7 +55,7 @@ export async function previewDocs(argv: {
         );
       }
 
-      return openapiBundle;
+      return openapiBundle.parsed;
     } catch (e) {
       handleError(e, entrypoint);
     }
diff --git a/packages/cli/src/commands/split/index.ts b/packages/cli/src/commands/split/index.ts
index 9fd62a7b..e4773403 100644
--- a/packages/cli/src/commands/split/index.ts
+++ b/packages/cli/src/commands/split/index.ts
@@ -4,7 +4,7 @@ import * as yaml from 'js-yaml';
 import * as path from 'path';
 import { performance } from 'perf_hooks';
 const isEqual = require('lodash.isequal');
-import { printExecutionTime, pathToFilename, readYaml, writeYaml } from '../../utils';
+import { printExecutionTime, pathToFilename, readYaml, writeYaml, exitWithError } from '../../utils';
 import { isString, isObject, isEmptyObject } from '../../js-utils';
 import {
   Definition,
@@ -69,11 +69,6 @@ function isNotYaml(filename: string) {
   return !(filename.endsWith('.yaml') || filename.endsWith('.yml'));
 }
 
-function exitWithError(message: string) {
-  process.stderr.write(red(message));
-  process.exit(1);
-}
-
 function loadFile(fileName: string) {
   try {
     return yaml.safeLoad(fs.readFileSync(fileName, 'utf8')) as Definition;
diff --git a/packages/cli/src/commands/stats.ts b/packages/cli/src/commands/stats.ts
index 2b36a117..5830d25c 100644
--- a/packages/cli/src/commands/stats.ts
+++ b/packages/cli/src/commands/stats.ts
@@ -10,7 +10,6 @@ import {
   StatsAccumulator,
   StatsName,
   BaseResolver,
-  Document,
   resolveDocument,
   detectOpenAPI,
   OasMajorVersion,
@@ -18,8 +17,9 @@ import {
   normalizeVisitors,
   WalkContext,
   walkDocument,
-  Stats
-} from "@redocly/openapi-core";
+  Stats,
+  bundle
+} from '@redocly/openapi-core';
 
 import { getFallbackEntryPointsOrExit } from '../utils'
 import { printExecutionTime } from '../utils';
@@ -70,7 +70,7 @@ export async function handleStats (argv: {
   const entrypoints = await getFallbackEntryPointsOrExit(argv.entrypoint ? [argv.entrypoint] : [], config);
   const entrypoint = entrypoints[0];
   const externalRefResolver = new BaseResolver(config.resolve);
-  const document = (await externalRefResolver.resolveDocument(null, entrypoint)) as Document;
+  const { bundle: document } = await bundle({ config, ref: entrypoint });
   const lintConfig: LintConfig = config.lint;
   const oasVersion = detectOpenAPI(document.parsed);
   const oasMajorVersion = openAPIMajor(oasVersion);
diff --git a/packages/cli/src/utils.ts b/packages/cli/src/utils.ts
index 3d6d67e3..e790d514 100644
--- a/packages/cli/src/utils.ts
+++ b/packages/cli/src/utils.ts
@@ -277,3 +277,8 @@ export function printUnusedWarnings(config: LintConfig) {
     process.stderr.write(`Check the spelling and verify you added plugin prefix.\n`);
   }
 }
+
+export function exitWithError(message: string) {
+  process.stderr.write(red(message));
+  process.exit(1);
+}
diff --git a/packages/core/src/bundle.ts b/packages/core/src/bundle.ts
index d6bb961b..54cd9747 100644
--- a/packages/core/src/bundle.ts
+++ b/packages/core/src/bundle.ts
@@ -94,7 +94,7 @@ export async function bundleDocument(opts: {
   });
 
   return {
-    bundle: document.parsed,
+    bundle: document,
     problems: ctx.problems.map((problem) => config.addProblemToIgnore(problem)),
     fileDependencies: externalRefResolver.getFiles(),
   };


Test patch:
diff --git a/packages/core/__tests__/bundle.test.ts b/packages/core/__tests__/bundle.test.ts
index 0e91b7ad..0ea0bbc5 100644
--- a/packages/core/__tests__/bundle.test.ts
+++ b/packages/core/__tests__/bundle.test.ts
@@ -45,7 +45,7 @@ describe('bundle', () => {
     const origCopy = JSON.parse(JSON.stringify(document.parsed));
 
     expect(problems).toHaveLength(0);
-    expect(bundle).toEqual(origCopy);
+    expect(bundle.parsed).toEqual(origCopy);
   });
 
   it('should bundle external refs', async () => {
@@ -54,7 +54,7 @@ describe('bundle', () => {
       ref: path.join(__dirname, 'fixtures/refs/openapi-with-external-refs.yaml'),
     });
     expect(problems).toHaveLength(0);
-    expect(res).toMatchSnapshot();
+    expect(res.parsed).toMatchSnapshot();
   });
 
   it('should bundle external refs and warn for conflicting names', async () => {
@@ -66,6 +66,6 @@ describe('bundle', () => {
     expect(problems[0].message).toEqual(
       `Two schemas are referenced with the same name but different content. Renamed param-b to param-b-2.`,
     );
-    expect(res).toMatchSnapshot();
+    expect(res.parsed).toMatchSnapshot();
   });
 });