diff --git a/.github/workflows/publish-cli.yml b/.github/workflows/publish-cli.yml index 9d7818ef..ea39efae 100644 --- a/.github/workflows/publish-cli.yml +++ b/.github/workflows/publish-cli.yml @@ -100,6 +100,7 @@ jobs: - uses: actions/setup-node@v1 with: node-version: "14.x" + registry-url: 'https://registry.npmjs.org' - uses: actions/checkout@v2 - name: Download cli bundled artifact uses: actions/download-artifact@v2 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index debe2cb8..b6d05316 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -98,5 +98,3 @@ jobs: run: npm publish env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - name: After script - run: cat ./coverage/lcov.info | coveralls diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 00000000..d0612ad3 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npm run pre-commit diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..dd449725 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +*.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ee0e446..a528cc9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +# [2.0.0-rc.58](https://github.com/Redocly/redoc/compare/v2.0.0-rc.57...v2.0.0-rc.58) (2021-11-29) + + +### Bug Fixes + +* add browser build for webpack 5 ([#1796](https://github.com/Redocly/redoc/issues/1796)) ([0e43ad3](https://github.com/Redocly/redoc/commit/0e43ad3102cfba8c4b30e59500ad4efc53f01c2d)) +* Default boolean property value not rendered [#1779](https://github.com/Redocly/redoc/issues/1779) ([#1781](https://github.com/Redocly/redoc/issues/1781)) ([734080c](https://github.com/Redocly/redoc/commit/734080c35471d16f87004f7f9a51dcdeee1278a6)) +* exclusiveMin/Max shows incorect range ([#1799](https://github.com/Redocly/redoc/issues/1799)) ([b604bd8](https://github.com/Redocly/redoc/commit/b604bd8da874f07e9e9f8b193ad10117a5f5059c)) +* mobile view in docker image ([#1795](https://github.com/Redocly/redoc/issues/1795)) ([ad652b9](https://github.com/Redocly/redoc/commit/ad652b9c7fbcd84a6e83397272de64e57213fe9a)) + + + # [2.0.0-rc.57](https://github.com/Redocly/redoc/compare/v2.0.0-rc.56...v2.0.0-rc.57) (2021-10-11) diff --git a/README.md b/README.md index 6d3f335a..4af6ad0e 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ # Generate interactive API documentation from OpenAPI definitions - [![Build Status](https://travis-ci.com/Redocly/redoc.svg?branch=master)](https://travis-ci.com/Redocly/redoc) [![Coverage Status](https://coveralls.io/repos/Redocly/redoc/badge.svg?branch=master&service=github)](https://coveralls.io/github/Redocly/redoc?branch=master) [![dependencies Status](https://david-dm.org/Redocly/redoc/status.svg)](https://david-dm.org/Redocly/redoc) [![devDependencies Status](https://david-dm.org/Redocly/redoc/dev-status.svg)](https://david-dm.org/Redocly/redoc#info=devDependencies) [![npm](http://img.shields.io/npm/v/redoc.svg)](https://www.npmjs.com/package/redoc) [![License](https://img.shields.io/npm/l/redoc.svg)](https://github.com/Redocly/redoc/blob/master/LICENSE) + [![Build Status](https://travis-ci.com/Redocly/redoc.svg?branch=master)](https://travis-ci.com/Redocly/redoc) [![Coverage Status](https://coveralls.io/repos/Redocly/redoc/badge.svg?branch=master&service=github)](https://coveralls.io/github/Redocly/redoc?branch=master) [![npm](http://img.shields.io/npm/v/redoc.svg)](https://www.npmjs.com/package/redoc) [![License](https://img.shields.io/npm/l/redoc.svg)](https://github.com/Redocly/redoc/blob/master/LICENSE) [![bundle size](http://img.badgesize.io/https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js?compression=gzip&max=300000)](https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js) [![npm](https://img.shields.io/npm/dm/redoc.svg)](https://www.npmjs.com/package/redoc) [![](https://data.jsdelivr.com/v1/package/npm/redoc/badge)](https://www.jsdelivr.com/package/npm/redoc) [![Docker Build Status](https://img.shields.io/docker/build/redocly/redoc.svg)](https://hub.docker.com/r/redocly/redoc/) @@ -183,6 +183,7 @@ IE support for Redoc. For more information on Redoc's commmand-line interface, refer to [**Using the Redoc CLI**](https://redoc.ly/docs/redoc/quickstart/cli/). + ## Configuration ### Security Definition location diff --git a/cli/npm-shrinkwrap.json b/cli/npm-shrinkwrap.json index 117d9c06..c5d3ac14 100644 --- a/cli/npm-shrinkwrap.json +++ b/cli/npm-shrinkwrap.json @@ -1,12 +1,12 @@ { "name": "redoc-cli", - "version": "0.12.3", + "version": "0.13.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "redoc-cli", - "version": "0.12.3", + "version": "0.13.0", "license": "MIT", "dependencies": { "chokidar": "^3.5.1", @@ -17,7 +17,7 @@ "node-libs-browser": "^2.2.1", "react": "^17.0.1", "react-dom": "^17.0.1", - "redoc": "2.0.0-rc.56", + "redoc": "2.0.0-rc.57", "styled-components": "^5.3.0", "yargs": "^17.0.1" }, @@ -198,13 +198,13 @@ "integrity": "sha512-GoXw0U2Qaa33m3eUcxuHnHpNvHjNlLo0gtV091XBpaRINaB4X6FGCG5XKxSFNFiPpugUDqNruHzaqpTdDm4AOg==" }, "node_modules/@redocly/ajv": { - "version": "6.12.4", - "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-6.12.4.tgz", - "integrity": "sha512-RB6vWO78v6c+SW/3bZh+XZMr4nGdJKAiPGsBALuUZnLuCiQ7aXCT1AuFHqnfS2gyXbEUEj+kw8p4ux8KdAfs3A==", + "version": "8.6.2", + "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.6.2.tgz", + "integrity": "sha512-tU8fQs0D76ZKhJ2cWtnfQthWqiZgGBx0gH0+5D8JvaBEBaqA8foPPBt3Nonwr3ygyv5xrw2IzKWgIY86BlGs+w==", "dependencies": { "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", "uri-js": "^4.2.2" }, "funding": { @@ -213,11 +213,11 @@ } }, "node_modules/@redocly/openapi-core": { - "version": "1.0.0-beta.50", - "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.0.0-beta.50.tgz", - "integrity": "sha512-GuXn4IETxpbRd8dlAQDQPtvqOpbMvPMeC/e5mv5MOXkLIznNk4vjiQVe6QSCbZbCHzzpb2+89B6S7asebPm4Rg==", + "version": "1.0.0-beta.62", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.0.0-beta.62.tgz", + "integrity": "sha512-g4iPB7qpSNFCOpf/FgKiic+QCaCn4mdNQOWVEPuwpN7l72hlQ7J3YUa9cssJomkJXXxZ1zdP4h208s12LkhwVA==", "dependencies": { - "@redocly/ajv": "^6.12.3", + "@redocly/ajv": "^8.6.2", "@types/node": "^14.11.8", "colorette": "^1.2.0", "js-levenshtein": "^1.1.6", @@ -232,9 +232,9 @@ } }, "node_modules/@redocly/openapi-core/node_modules/@types/node": { - "version": "14.17.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.3.tgz", - "integrity": "sha512-e6ZowgGJmTuXa3GyaPbTGxX17tnThl2aSSizrFthQ7m9uLGZBXiGhgE55cjRZTF5kjZvYn9EOPOMljdjwbflxw==" + "version": "14.17.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.21.tgz", + "integrity": "sha512-zv8ukKci1mrILYiQOwGSV4FpkZhyxQtuFWGya2GujWg+zVAeRQ4qbaMmWp9vb9889CFA8JECH7lkwCL6Ygg8kA==" }, "node_modules/@redocly/react-dropdown-aria": { "version": "2.0.12", @@ -283,7 +283,8 @@ "node_modules/@types/node": { "version": "15.12.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.2.tgz", - "integrity": "sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww==" + "integrity": "sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww==", + "dev": true }, "node_modules/ansi-regex": { "version": "5.0.0", @@ -637,9 +638,9 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "node_modules/colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==" }, "node_modules/concat-map": { "version": "0.0.1", @@ -893,11 +894,6 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, "node_modules/fast-safe-stringify": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", @@ -1175,9 +1171,9 @@ } }, "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "node_modules/lodash": { "version": "4.17.21", @@ -1751,14 +1747,13 @@ } }, "node_modules/redoc": { - "version": "2.0.0-rc.56", - "resolved": "https://registry.npmjs.org/redoc/-/redoc-2.0.0-rc.56.tgz", - "integrity": "sha512-ir2TtQ2d/1FqZWIoLmUZ3qvAAnO6jg8dt0SV75TanmfCXpEABcElXWH3mtUf6qKlvgDVt40diDCVuSvyPPxkAw==", + "version": "2.0.0-rc.57", + "resolved": "https://registry.npmjs.org/redoc/-/redoc-2.0.0-rc.57.tgz", + "integrity": "sha512-f8XIqvZF1agphq6xmOU9jTDVNDFHJt3MzDq1lUgZojb/7YY4eqLyDi6er/yCWYkY9DuB+v2jHCOn5UUbMuKAfg==", "dependencies": { "@babel/runtime": "^7.14.0", - "@redocly/openapi-core": "^1.0.0-beta.50", + "@redocly/openapi-core": "^1.0.0-beta.54", "@redocly/react-dropdown-aria": "^2.0.11", - "@types/node": "^15.6.1", "classnames": "^2.3.1", "decko": "^1.2.0", "dompurify": "^2.2.8", @@ -1819,6 +1814,14 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ripemd160": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", @@ -2451,22 +2454,22 @@ "integrity": "sha512-GoXw0U2Qaa33m3eUcxuHnHpNvHjNlLo0gtV091XBpaRINaB4X6FGCG5XKxSFNFiPpugUDqNruHzaqpTdDm4AOg==" }, "@redocly/ajv": { - "version": "6.12.4", - "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-6.12.4.tgz", - "integrity": "sha512-RB6vWO78v6c+SW/3bZh+XZMr4nGdJKAiPGsBALuUZnLuCiQ7aXCT1AuFHqnfS2gyXbEUEj+kw8p4ux8KdAfs3A==", + "version": "8.6.2", + "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.6.2.tgz", + "integrity": "sha512-tU8fQs0D76ZKhJ2cWtnfQthWqiZgGBx0gH0+5D8JvaBEBaqA8foPPBt3Nonwr3ygyv5xrw2IzKWgIY86BlGs+w==", "requires": { "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, "@redocly/openapi-core": { - "version": "1.0.0-beta.50", - "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.0.0-beta.50.tgz", - "integrity": "sha512-GuXn4IETxpbRd8dlAQDQPtvqOpbMvPMeC/e5mv5MOXkLIznNk4vjiQVe6QSCbZbCHzzpb2+89B6S7asebPm4Rg==", + "version": "1.0.0-beta.62", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.0.0-beta.62.tgz", + "integrity": "sha512-g4iPB7qpSNFCOpf/FgKiic+QCaCn4mdNQOWVEPuwpN7l72hlQ7J3YUa9cssJomkJXXxZ1zdP4h208s12LkhwVA==", "requires": { - "@redocly/ajv": "^6.12.3", + "@redocly/ajv": "^8.6.2", "@types/node": "^14.11.8", "colorette": "^1.2.0", "js-levenshtein": "^1.1.6", @@ -2478,9 +2481,9 @@ }, "dependencies": { "@types/node": { - "version": "14.17.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.3.tgz", - "integrity": "sha512-e6ZowgGJmTuXa3GyaPbTGxX17tnThl2aSSizrFthQ7m9uLGZBXiGhgE55cjRZTF5kjZvYn9EOPOMljdjwbflxw==" + "version": "14.17.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.21.tgz", + "integrity": "sha512-zv8ukKci1mrILYiQOwGSV4FpkZhyxQtuFWGya2GujWg+zVAeRQ4qbaMmWp9vb9889CFA8JECH7lkwCL6Ygg8kA==" } } }, @@ -2525,7 +2528,8 @@ "@types/node": { "version": "15.12.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.2.tgz", - "integrity": "sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww==" + "integrity": "sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww==", + "dev": true }, "ansi-regex": { "version": "5.0.0", @@ -2841,9 +2845,9 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==" }, "concat-map": { "version": "0.0.1", @@ -3064,11 +3068,6 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, "fast-safe-stringify": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", @@ -3273,9 +3272,9 @@ } }, "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "lodash": { "version": "4.17.21", @@ -3741,14 +3740,13 @@ } }, "redoc": { - "version": "2.0.0-rc.56", - "resolved": "https://registry.npmjs.org/redoc/-/redoc-2.0.0-rc.56.tgz", - "integrity": "sha512-ir2TtQ2d/1FqZWIoLmUZ3qvAAnO6jg8dt0SV75TanmfCXpEABcElXWH3mtUf6qKlvgDVt40diDCVuSvyPPxkAw==", + "version": "2.0.0-rc.57", + "resolved": "https://registry.npmjs.org/redoc/-/redoc-2.0.0-rc.57.tgz", + "integrity": "sha512-f8XIqvZF1agphq6xmOU9jTDVNDFHJt3MzDq1lUgZojb/7YY4eqLyDi6er/yCWYkY9DuB+v2jHCOn5UUbMuKAfg==", "requires": { "@babel/runtime": "^7.14.0", - "@redocly/openapi-core": "^1.0.0-beta.50", + "@redocly/openapi-core": "^1.0.0-beta.54", "@redocly/react-dropdown-aria": "^2.0.11", - "@types/node": "^15.6.1", "classnames": "^2.3.1", "decko": "^1.2.0", "dompurify": "^2.2.8", @@ -3794,6 +3792,11 @@ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, "ripemd160": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", diff --git a/cli/package.json b/cli/package.json index 379f5cfe..c5289576 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "redoc-cli", - "version": "0.12.3", + "version": "0.13.0", "description": "ReDoc's Command Line Interface", "main": "index.js", "bin": "index.js", @@ -19,7 +19,7 @@ "node-libs-browser": "^2.2.1", "react": "^17.0.1", "react-dom": "^17.0.1", - "redoc": "2.0.0-rc.56", + "redoc": "2.0.0-rc.57", "styled-components": "^5.3.0", "yargs": "^17.0.1" }, diff --git a/config/docker/index.tpl.html b/config/docker/index.tpl.html index 2995ff26..15171edc 100644 --- a/config/docker/index.tpl.html +++ b/config/docker/index.tpl.html @@ -1,26 +1,28 @@ + + + + %PAGE_TITLE% + + + + - redoc { - display: block; - } - - - - - - - - - - \ No newline at end of file + + + + + diff --git a/docs/deployment/cli.md b/docs/deployment/cli.md new file mode 100644 index 00000000..8bfc9e2c --- /dev/null +++ b/docs/deployment/cli.md @@ -0,0 +1,114 @@ +--- +title: Using the Redoc CLI +redirectFrom: + - /docs/quickstart/cli/ +--- + +# Using the Redoc CLI + +With Redoc's command-line interface you can bundle your OpenAPI definition and API documentation +(made with Redoc) into a zero-dependency HTML file and locally render your +OpenAPI definition with Redoc. + +## Step 1 - Install Redoc CLI + +You can install the `redoc-cli` package globally using one of the following package managers: + +- [npm](https://docs.npmjs.com/about-npm) +- [yarn](https://classic.yarnpkg.com/en/docs/getting-started) + +Or you can install `redoc-cli` using [npx](https://www.freecodecamp.org/news/npm-vs-npx-whats-the-difference/). + +### Install Redoc CLI with yarn + +To install the `redoc-cli` package globally with yarn: + +```bash +yarn global add redoc-cli +``` + +### Install Redoc with npm + +To install the `redoc-cli` package globally with npm: + +```bash +npm i -g redoc-cli +``` + +### Install with `npx` + +To install the `redoc-cli` package locally with `npx`, navigate to your project +directory in your terminal, then use the following command: + +```bash +npx redoc-cli +``` + +## Step 2 - Use the CLI + +### Redoc CLI commands + +The CLI includes the following commands: + +- **`redoc-cli serve [spec]`:** Starts a local server with Redoc. You must include the required parameter, spec, which is + a reference to an OpenAPI definition. Options include: + - `--ssr`: Implements a server-side rendering model. + - `--watch`: Automatically reloads the server while you edit your OpenAPI definition. + - `--options`: Customizes your output using [Redoc options](https://redoc.ly/docs/api-reference-docs/configuration/). + To add nested options, use dot notation. +- **`redoc-cli bundle [spec]`:** Bundles `spec` and Redoc into a zero-dependency HTML file. Options include: + - `-t` or `--template`: Uses custom [Handlebars](https://handlebarsjs.com/) templates to render your OpenAPI definition. + - `--templateOptions`: Adds template options you want to pass to your + custom Handlebars template. To add options, use dot notation. +- **`--help`:** Prints help text for the Redoc CLI commands and options. +- **`--version`:** Prints the version of the `redoc-cli` package you have installed. + +### Redoc CLI examples + +#### Bundle + +Bundle with the main color changed to `orange`: + +```bash +redoc-cli bundle openapi.yaml --options.theme.colors.primary.main=orange +``` + +Bundle using a custom Handlebars template and add custom `templateOptions`: + +```bash +redoc-cli bundle http://petstore.swagger.io/v2/swagger.json -t custom.hbs --templateOptions.metaDescription "Page meta description" +``` + +Sample Handlebars template: + +```handlebars + + + + + {{title}} + + + + + {{{redocHead}}} + {{#unless disableGoogleFont}}{{/unless}} + + + {{{redocHTML}}} + + +``` + +#### Serve + +Serve with the `nativeScrollbars` option set to `true`: + +```bash +redoc-cli serve openapi/dist.yaml --options.nativeScrollbars +``` diff --git a/docs/deployment/docker.md b/docs/deployment/docker.md new file mode 100644 index 00000000..a8c3c9b8 --- /dev/null +++ b/docs/deployment/docker.md @@ -0,0 +1,41 @@ +--- +title: Using the Redoc Docker image +redirectFrom: + - /docs/quickstart/docker/ +--- + +# Using the Redoc Docker image + +Redoc is available as a pre-built Docker image in [Docker Hub](https://hub.docker.com/r/redocly/redoc/). + +If you have [Docker](https://docs.docker.com/get-docker/) installed, pull the image with the following command: + +```docker +docker pull redocly/redoc +``` + +Then run the image with the following command: + +```docker +docker run -p 8080:80 redocly/redoc +``` + +The preview starts on port 8080, based on the port used in the command, +and can be accessed at `http://localhost:8080`. +To exit the preview, use `control+C`. + +By default Redoc starts with a demo Swagger Petstore OpenAPI definition located at +http://petstore.swagger.io/v2/swagger.json. You can update this URL using +the environment variable `SPEC_URL`. + +For example: + +```bash +docker run -p 8080:80 -e SPEC_URL=https://api.example.com/openapi.json redocly/redoc +``` + +## Using a Dockerfile + +You can also create a Dockerfile with some predefined environment variables. Check out +a sample [Dockerfile](https://github.com/Redocly/redoc/blob/master/config/docker/Dockerfile) +in our code repo. \ No newline at end of file diff --git a/docs/deployment/html.md b/docs/deployment/html.md new file mode 100644 index 00000000..872d05ac --- /dev/null +++ b/docs/deployment/html.md @@ -0,0 +1,123 @@ +--- +title: Using the Redoc HTML element +redirectFrom: + - /docs/quickstart/html/ +--- + +# Using the Redoc HTML element + +## Step 1 - Install Redoc + +You can install Redoc using one of the following package managers: + +- [npm](https://docs.npmjs.com/about-npm) +- [yarn](https://classic.yarnpkg.com/en/docs/getting-started) + +:::attention Initialize your package manager +If you do not have a `package.json` file in your project directory, +you need to add one by initializing npm or yarn in your project. Use the command `npm init` for npm, +or `yarn init` for yarn. These initialization commands will lead you through the process +of creating a `package.json` file in your project. + +For more information, see +[Creating a package.json file](https://docs.npmjs.com/creating-a-package-json-file) +in the npm documentation or [Yarn init](https://classic.yarnpkg.com/en/docs/cli/init/) +in the yarn documentation. +::: + +### Install Redoc with yarn + +After navigating to your project directory in your terminal, use the following command: + +```bash +yarn add redoc +``` + +### Install Redoc with npm + +After navigating to your project directory in your terminal, use the following command: + +```bash +npm i redoc +``` + +## Step 2 - Reference the Redoc script + +You can reference the Redoc script using either a link to the files hosted on a CDN +or the files located in your `node modules` folder. + +### CDN link + +To reference the Redoc script with a CDN link: + +```html + +``` + +### Node modules link + +To reference the Redoc script with a node modules link: + +```html + +``` + +## Step 3 - Add the element + +You can add the element to your HTML page and reference your OpenAPI +definition using the `spec-url` attribute, or you can initialize Redoc using +a globally exposed Redoc object. + +### Using the `spec-url` attribute + +To add the element with the `spec-url` attribute: + +```html + +``` + +#### Examples + +```html + +``` + +You can also use a local file (JSON or YAML) in your project, for instance: + +```html + +``` + +### Using a Redoc object + +To add the element with a globally exposed Redoc object: + +```js +Redoc.init(specOrSpecUrl, options, element, callback) +``` +- `specOrSpecUrl`: Either a JSON object with the OpenAPI definition or a URL to the + definition in JSON or YAML format. +- `options`: See [options object](https://redoc.ly/docs/api-reference-docs/configuration/) reference. +- `element`: DOM element Redoc will be inserted into. +- `callback`(optional): Callback to be called after Redoc has been fully rendered. + It is also called on errors with `error` as the first argument. + +#### Examples + +```html + +``` + +You can also use a local file (JSON or YAML) in your project, for instance: + +```html + +``` \ No newline at end of file diff --git a/docs/deployment/intro.md b/docs/deployment/intro.md new file mode 100644 index 00000000..43eea451 --- /dev/null +++ b/docs/deployment/intro.md @@ -0,0 +1,112 @@ +--- +title: Redoc deployment guide +redirectFrom: + - /docs/quickstart/intro/ +--- + +# Redoc deployment guide + +Redoc offers multiple options for rendering your OpenAPI definition. +You should select the option that best fits your needs. + +The following options are supported: + +- **[Live demo](https://redocly.github.io/redoc/):** + The live demo offers a fast way to see how your OpenAPI will render with Redoc. + A version of the Swagger Petstore API is displayed by default. To test it with your own OpenAPI definition, enter the URL for your + definition and select **TRY IT**. +- **[HTML element](./html.md):** + Using the HTML element works well for typical website deployments. +- **[React component](./react.md):** + Using the React component is an option for users with a React-based application. +- **[Docker image](./docker.md):** + Using the Docker image works in a container-based deployment. +- **[CLI](./cli.md):** + Using the CLI is an option for users who prefer to use a command-line interface. + +## Before you start + +### OpenAPI definition + +You will need an OpenAPI definition. For testing purposes, you can use one of the following sample OpenAPI definitions: + +- OpenAPI 3.0 + - [Rebilly Users OpenAPI Definition](https://raw.githubusercontent.com/Rebilly/api-definitions/main/openapi/users.yaml) + - [Swagger Petstore Sample OpenAPI Definition](https://petstore3.swagger.io/api/v3/openapi.json) +- OpenAPI 2.0 + - [Thingful OpenAPI Definition](https://raw.githubusercontent.com/thingful/openapi-spec/master/spec/swagger.yaml) + - [Fitbit Plus OpenAPI Definition](https://raw.githubusercontent.com/TwineHealth/TwineDeveloperDocs/master/spec/swagger.yaml) + +:::info OpenAPI specification +For more information on the OpenAPI specification, refer to the [Learning OpenAPI 3](https://redoc.ly/docs/resources/learning-openapi/) +section in the documentation. +::: + +### Running Redoc locally + +If you want to view your Redoc output locally, you can simulate an HTTP server. + +#### Using Redocly OpenAPI CLI + +Redocly OpenAPI CLI is an open source command-line tool that includes a command +for simulating an HTTP server to provide a preview of your OpenAPI definition locally. + +If you have [OpenAPI CLI](https://redoc.ly/docs/cli/#installation-and-usage) installed, `cd` into your +project directory and run the following command: + +```bash +openapi preview-docs openapi.yaml +``` + +Replace `openapi.yaml` in the example command with the file path to your OpenAPI definition. + +By default, without providing a port, the preview starts on port 8080, and can be accessed at `http://localhost:8080`. +To exit the preview, use `control+C`. + +You can alter the port if you are using 8080 already, for example: + +```bash +openapi preview-docs -p 8888 openapi.yaml +``` + +Replace `openapi.yaml` in the example command with the file path to your OpenAPI definition. + +For more information about the `preview-docs` command, refer to +[OpenAPI CLI commands](https://redoc.ly/docs/cli/commands/preview-docs/#preview-docs) in the OpenAPI CLI documentation. + +#### Using Python + +If you have [Python 3](https://www.python.org/downloads/) installed, `cd` into your +project directory and run the following command: + +```python +python3 -m http.server +``` + +If you have [Python 2](https://www.python.org/downloads/) installed, `cd` into your +project directory and run the following command: + +```python +python -m SimpleHTTPServer 8000 +``` + +The output after entering the command provides the local URL where the preview can be accessed. +To exit the preview, use `control-C`. + +#### Using Node.js + +If you have [Node.js](https://nodejs.org/en/download/) installed, install `http-server` +using the following npm command: + +```bash +npm install -g http-server +``` + +Then, `cd` into your project directory and run the following command: + +```node +http-server +``` + +The output after entering the command provides the local URL where the preview can be accessed. +To exit the preview, use `control-C`. diff --git a/docs/deployment/react.md b/docs/deployment/react.md new file mode 100644 index 00000000..84050c3c --- /dev/null +++ b/docs/deployment/react.md @@ -0,0 +1,80 @@ +--- +title: Using the Redoc React component +redirectFrom: + - /docs/quickstart/react/ +--- + +# Using the Redoc React component + +## Before you start + +Install the following dependencies required by Redoc if you do not already have them installed: + +- `react` +- `react-dom` +- `mobx` +- `styled-components` +- `core-js` + +If you have npm installed, you can install these dependencies using the following command: + +```js +npm i react react-dom mobx styled-components core-js +``` + +## Step 1 - Import the `RedocStandalone` component + +```js +import { RedocStandalone } from 'redoc'; +``` + +## Step 2 - Use the component + +You can either link to your OpenAPI definition with a URL, using the following format: + +```react + +``` + +Or you can pass your OpenAPI definition as an object, using the following format: + +```js + +``` + +## Optional - Pass options + +Options can be passed into the RedocStandalone component to alter how it renders. + +For example: + +```js + +``` + +For more information on configuration options, refer to the +[Configuration options for Reference docs](https://redoc.ly/docs/api-reference-docs/configuration/) +section of the documentation. Options available for Redoc are noted, +"Supported in Redoc CE". + +## Optional - Specify `onLoaded` callback + +You can also specify the `onLoaded` callback, which is called each time Redoc +is fully rendered or when an error occurs (with an error as the first argument). + +```js + { + if (!error) { + console.log('Yay!'); + } + }} +/> +``` diff --git a/docs/quickstart.md b/docs/quickstart.md new file mode 100644 index 00000000..2dba080d --- /dev/null +++ b/docs/quickstart.md @@ -0,0 +1,53 @@ +--- +title: Redoc quickstart guide +--- + +# Redoc quickstart guide + +To render your OpenAPI definition using Redoc, use the following HTML code sample and +replace the `spec-url` attribute with the URL or local file address to your definition. + +```html + + + + Redoc + + + + + + + + + + + + + + + +``` + +:::attention Running Redoc locally requires an HTTP server +Loading local OpenAPI definitions is impossible without running a web server because of issues with +[same-origin policy](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy) and +other security reasons. Refer to [Running Redoc locally](./deployment/intro.md#running-redoc-locally) for +more information. +::: + +For a more detailed explanation with step-by-step instructions and additional options for using Redoc, refer to the [Redoc deployment guide](./deployment/intro.md). diff --git a/docs/sidebars.yaml b/docs/sidebars.yaml deleted file mode 100644 index 2189f25b..00000000 --- a/docs/sidebars.yaml +++ /dev/null @@ -1,13 +0,0 @@ -redoc: - - group: Quickstart - expanded: false - page: redoc/quickstart/intro.md - pages: - - label: HTML element - page: redoc/quickstart/html.md - - label: React component - page: redoc/quickstart/react.md - - label: Docker image - page: redoc/quickstart/docker.md - - label: Command-line interface - page: redoc/quickstart/cli.md \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 9e8aca4a..7f81a015 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,12 @@ { "name": "redoc", - "version": "2.0.0-rc.57", + "version": "2.0.0-rc.58", "lockfileVersion": 2, "requires": true, "packages": { "": { - "version": "2.0.0-rc.57", + "name": "redoc", + "version": "2.0.0-rc.58", "license": "MIT", "dependencies": { "@babel/runtime": "^7.14.0", @@ -89,12 +90,14 @@ "eslint-plugin-react": "^7.24.0", "fork-ts-checker-webpack-plugin": "^6.2.10", "html-webpack-plugin": "^5.3.1", + "husky": "^7.0.0", "jest": "^27.0.3", "js-yaml": "^4.1.0", "license-checker": "^25.0.1", "lodash": "^4.17.21", "mobx": "^6.3.2", - "prettier": "^2.3.0", + "prettier": "^2.3.2", + "pretty-quick": "^3.0.0", "raf": "^3.4.1", "react": "^17.0.2", "react-dom": "^17.0.2", @@ -3888,6 +3891,15 @@ "node": ">=0.10.0" } }, + "node_modules/array-differ": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz", + "integrity": "sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/array-find-index": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", @@ -10204,6 +10216,21 @@ "node": ">=8.12.0" } }, + "node_modules/husky": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", + "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", + "dev": true, + "bin": { + "husky": "lib/bin.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -14466,6 +14493,15 @@ "integrity": "sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==", "dev": true }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -14490,6 +14526,31 @@ "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", "dev": true }, + "node_modules/multimatch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-4.0.0.tgz", + "integrity": "sha512-lDmx79y1z6i7RNx0ZGCPq1bzJ6ZoDDKbvh7jxr9SJcWLkShMzXrHbYVpTdnhNM5MXpDUxCQ4DgqVttVXlBgiBQ==", + "dev": true, + "dependencies": { + "@types/minimatch": "^3.0.3", + "array-differ": "^3.0.0", + "array-union": "^2.1.0", + "arrify": "^2.0.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/multimatch/node_modules/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/nan": { "version": "2.14.2", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", @@ -15782,9 +15843,9 @@ } }, "node_modules/prettier": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz", - "integrity": "sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", + "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==", "dev": true, "bin": { "prettier": "bin-prettier.js" @@ -15925,6 +15986,166 @@ "node": ">=8" } }, + "node_modules/pretty-quick": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-3.1.2.tgz", + "integrity": "sha512-T+fpTJrDjTzewql4p3lKrRA7z3MrNyjBK1MKeaBm5PpKwATgVm885TpY7TgY8KFt5Q1Qn3QDseRQcyX9AKTKkA==", + "dev": true, + "dependencies": { + "chalk": "^3.0.0", + "execa": "^4.0.0", + "find-up": "^4.1.0", + "ignore": "^5.1.4", + "mri": "^1.1.5", + "multimatch": "^4.0.0" + }, + "bin": { + "pretty-quick": "bin/pretty-quick.js" + }, + "engines": { + "node": ">=10.13" + }, + "peerDependencies": { + "prettier": ">=2.0.0" + } + }, + "node_modules/pretty-quick/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-quick/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-quick/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/pretty-quick/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/pretty-quick/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-quick/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-quick/node_modules/ignore": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", + "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/pretty-quick/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-quick/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-quick/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-quick/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-quick/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/prettycli": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/prettycli/-/prettycli-1.4.3.tgz", @@ -23993,6 +24214,12 @@ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "dev": true }, + "array-differ": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz", + "integrity": "sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==", + "dev": true + }, "array-find-index": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", @@ -28930,6 +29157,12 @@ "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", "dev": true }, + "husky": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", + "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", + "dev": true + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -32084,6 +32317,12 @@ "integrity": "sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==", "dev": true }, + "mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -32105,6 +32344,27 @@ "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", "dev": true }, + "multimatch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-4.0.0.tgz", + "integrity": "sha512-lDmx79y1z6i7RNx0ZGCPq1bzJ6ZoDDKbvh7jxr9SJcWLkShMzXrHbYVpTdnhNM5MXpDUxCQ4DgqVttVXlBgiBQ==", + "dev": true, + "requires": { + "@types/minimatch": "^3.0.3", + "array-differ": "^3.0.0", + "array-union": "^2.1.0", + "arrify": "^2.0.1", + "minimatch": "^3.0.4" + }, + "dependencies": { + "arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "dev": true + } + } + }, "nan": { "version": "2.14.2", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", @@ -33103,9 +33363,9 @@ "dev": true }, "prettier": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz", - "integrity": "sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", + "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==", "dev": true }, "pretty-bytes": { @@ -33209,6 +33469,120 @@ } } }, + "pretty-quick": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-3.1.2.tgz", + "integrity": "sha512-T+fpTJrDjTzewql4p3lKrRA7z3MrNyjBK1MKeaBm5PpKwATgVm885TpY7TgY8KFt5Q1Qn3QDseRQcyX9AKTKkA==", + "dev": true, + "requires": { + "chalk": "^3.0.0", + "execa": "^4.0.0", + "find-up": "^4.1.0", + "ignore": "^5.1.4", + "mri": "^1.1.5", + "multimatch": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "ignore": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", + "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "prettycli": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/prettycli/-/prettycli-1.4.3.tgz", diff --git a/package.json b/package.json index 905992a1..8794ecc0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redoc", - "version": "2.0.0-rc.57", + "version": "2.0.0-rc.58", "description": "ReDoc", "repository": { "type": "git", @@ -27,6 +27,7 @@ "React.js" ], "main": "bundles/redoc.lib.js", + "browser": "bundles/redoc.browser.lib.js", "types": "typings/index.d.ts", "scripts": { "start": "webpack serve --mode=development --env playground --hot --config demo/webpack.config.ts", @@ -42,7 +43,8 @@ "bundle:clean": "rimraf bundles", "bundle:standalone": "webpack --env production --env standalone --mode=production", "bundle:lib": "webpack --mode=production && npm run declarations", - "bundle": "npm run bundle:clean && npm run bundle:lib && npm run bundle:standalone", + "bundle:browser": "webpack --env production --env browser --mode=production", + "bundle": "npm run bundle:clean && npm run bundle:lib && npm run bundle:browser && npm run bundle:standalone", "declarations": "tsc --emitDeclarationOnly -p tsconfig.lib.json && cp -R src/types typings/", "stats": "webpack --env production --env standalone --json --profile --mode=production > stats.json", "prettier": "prettier --write \"cli/index.ts\" \"src/**/*.{ts,tsx}\"", @@ -54,7 +56,9 @@ "build:demo": "webpack --mode=production --config demo/webpack.config.ts", "deploy:demo": "aws s3 sync demo/dist s3://production-redoc-demo --acl=public-read", "license-check": "license-checker --production --onlyAllow 'MIT;ISC;Apache-2.0;BSD;BSD-2-Clause;BSD-3-Clause' --summary", - "docker:build": "docker build -f config/docker/Dockerfile -t redoc ." + "docker:build": "docker build -f config/docker/Dockerfile -t redoc .", + "prepare": "husky install", + "pre-commit": "pretty-quick --staged && npm run lint" }, "devDependencies": { "@babel/core": "^7.14.3", @@ -112,12 +116,14 @@ "eslint-plugin-react": "^7.24.0", "fork-ts-checker-webpack-plugin": "^6.2.10", "html-webpack-plugin": "^5.3.1", + "husky": "^7.0.0", "jest": "^27.0.3", "js-yaml": "^4.1.0", "license-checker": "^25.0.1", "lodash": "^4.17.21", "mobx": "^6.3.2", - "prettier": "^2.3.0", + "prettier": "^2.3.2", + "pretty-quick": "^3.0.0", "raf": "^3.4.1", "react": "^17.0.2", "react-dom": "^17.0.2", @@ -207,6 +213,6 @@ "singleQuote": true, "trailingComma": "all", "printWidth": 100, - "parser": "typescript" + "arrowParens": "avoid" } } diff --git a/src/common-elements/dropdown.ts b/src/common-elements/dropdown.ts index f4bcc281..4fba9a55 100644 --- a/src/common-elements/dropdown.ts +++ b/src/common-elements/dropdown.ts @@ -28,16 +28,16 @@ export const StyledDropdown = styled(Dropdown)` width: auto; background: white; color: #263238; - font-family: ${(props) => props.theme.typography.headings.fontFamily}; + font-family: ${props => props.theme.typography.headings.fontFamily}; font-size: 0.929em; line-height: 1.5em; cursor: pointer; transition: border 0.25s ease, color 0.25s ease, box-shadow 0.25s ease; &:hover, &:focus-within { - border: 1px solid ${(props) => props.theme.colors.primary.main}; - color: ${(props) => props.theme.colors.primary.main}; - box-shadow: 0px 0px 0px 1px ${(props) => props.theme.colors.primary.main}; + border: 1px solid ${props => props.theme.colors.primary.main}; + color: ${props => props.theme.colors.primary.main}; + box-shadow: 0px 0px 0px 1px ${props => props.theme.colors.primary.main}; } .dropdown-selector { display: inline-flex; @@ -48,7 +48,7 @@ export const StyledDropdown = styled(Dropdown)` margin-bottom: 5px; } .dropdown-selector-value { - font-family: ${(props) => props.theme.typography.headings.fontFamily}; + font-family: ${props => props.theme.typography.headings.fontFamily}; position: relative; font-size: 0.929em; width: 100%; @@ -63,7 +63,7 @@ export const StyledDropdown = styled(Dropdown)` right: 3px; top: 50%; transform: translateY(-50%); - border-color: ${(props) => props.theme.colors.primary.main} transparent transparent; + border-color: ${props => props.theme.colors.primary.main} transparent transparent; border-style: solid; border-width: 0.35em 0.35em 0; width: 0; @@ -128,8 +128,8 @@ export const SimpleDropdown = styled(StyledDropdown)` border: none; box-shadow: none; .dropdown-selector-value { - color: ${(props) => props.theme.colors.primary.main}; - text-shadow: 0px 0px 0px ${(props) => props.theme.colors.primary.main}; + color: ${props => props.theme.colors.primary.main}; + text-shadow: 0px 0px 0px ${props => props.theme.colors.primary.main}; } } } diff --git a/src/common-elements/fields-layout.ts b/src/common-elements/fields-layout.ts index 8738b014..a1fd214b 100644 --- a/src/common-elements/fields-layout.ts +++ b/src/common-elements/fields-layout.ts @@ -66,7 +66,7 @@ export const PropertyNameCell = styled(PropertyCell)` line-height: 20px; white-space: nowrap; font-size: 13px; - font-family: ${(props) => props.theme.typography.code.fontFamily}; + font-family: ${props => props.theme.typography.code.fontFamily}; &.deprecated { ${deprecatedCss}; @@ -80,7 +80,7 @@ export const PropertyNameCell = styled(PropertyCell)` export const PropertyDetailsCell = styled.td` border-bottom: 1px solid #9fb4be; padding: 10px 0; - width: ${(props) => props.theme.schema.defaultDetailsWidth}; + width: ${props => props.theme.schema.defaultDetailsWidth}; box-sizing: border-box; tr.expanded & { @@ -90,7 +90,7 @@ export const PropertyDetailsCell = styled.td` ${media.lessThan('small')` padding: 0 20px; border-bottom: none; - border-left: 1px solid ${(props) => props.theme.schema.linesColor}; + border-left: 1px solid ${props => props.theme.schema.linesColor}; tr.last > & { border-left: none; diff --git a/src/common-elements/linkify.tsx b/src/common-elements/linkify.tsx index 0b059968..fe8f942b 100644 --- a/src/common-elements/linkify.tsx +++ b/src/common-elements/linkify.tsx @@ -6,7 +6,7 @@ import styled, { css } from '../styled-components'; import { HistoryService } from '../services'; // tslint:disable-next-line -export const linkifyMixin = (className) => css` +export const linkifyMixin = className => css` ${className} { cursor: pointer; margin-left: -20px; @@ -33,13 +33,13 @@ export const linkifyMixin = (className) => css` } `; -const isModifiedEvent = (event) => +const isModifiedEvent = event => !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey); export function Link(props: { to: string; className?: string; children?: any }) { const store = React.useContext(StoreContext); const clickHandler = React.useCallback( - (event: React.MouseEvent) => { + (event: React.MouseEvent) => { if (!store) return; navigate(store.menu.history, event, props.to); }, diff --git a/src/common-elements/perfect-scrollbar.tsx b/src/common-elements/perfect-scrollbar.tsx index 5d58b4de..33e12c59 100644 --- a/src/common-elements/perfect-scrollbar.tsx +++ b/src/common-elements/perfect-scrollbar.tsx @@ -12,7 +12,7 @@ import styled, { createGlobalStyle } from '../styled-components'; * That's why the following ugly fix is required */ const PerfectScrollbarConstructor = - PerfectScrollbarNamespace.default || ((PerfectScrollbarNamespace as any) as PerfectScrollbarType); + PerfectScrollbarNamespace.default || (PerfectScrollbarNamespace as any as PerfectScrollbarType); const PSStyling = createGlobalStyle`${psStyles && psStyles.toString()}`; diff --git a/src/components/ApiInfo/ApiInfo.tsx b/src/components/ApiInfo/ApiInfo.tsx index 6debaf7e..4e3ce62e 100644 --- a/src/components/ApiInfo/ApiInfo.tsx +++ b/src/components/ApiInfo/ApiInfo.tsx @@ -39,7 +39,12 @@ export class ApiInfo extends React.Component { const license = (info.license && ( - License: {info.license.identifier ? info.license.identifier : ({info.license.name})} + License:{' '} + {info.license.identifier ? ( + info.license.identifier + ) : ( + {info.license.name} + )} )) || null; @@ -101,8 +106,8 @@ export class ApiInfo extends React.Component { )) || null} - - + + {externalDocs && } diff --git a/src/components/Callbacks/CallbackTitle.tsx b/src/components/Callbacks/CallbackTitle.tsx index 10b888f3..d6c34ff8 100644 --- a/src/components/Callbacks/CallbackTitle.tsx +++ b/src/components/Callbacks/CallbackTitle.tsx @@ -48,10 +48,10 @@ const CallbackTitleWrapper = styled.button` `; const CallbackName = styled.span<{ deprecated?: boolean }>` - text-decoration: ${(props) => (props.deprecated ? 'line-through' : 'none')}; + text-decoration: ${props => (props.deprecated ? 'line-through' : 'none')}; margin-right: 8px; `; const OperationBadgeStyled = styled(OperationBadge)` - margin: 0px 5px 0px 0px; + margin: 0 5px 0 0; `; diff --git a/src/components/Endpoint/styled.elements.ts b/src/components/Endpoint/styled.elements.ts index 041de2d4..e24e07c5 100644 --- a/src/components/Endpoint/styled.elements.ts +++ b/src/components/Endpoint/styled.elements.ts @@ -35,7 +35,7 @@ export const EndpointInfo = styled.button<{ expanded?: boolean; inverted?: boole (props.expanded && !props.inverted && `border-color: ${props.theme.colors.border.dark};`) || ''} .${ServerRelativeURL} { - color: ${props => (props.inverted ? props.theme.colors.text.primary : '#ffffff')} + color: ${props => (props.inverted ? props.theme.colors.text.primary : '#ffffff')}; } &:focus { box-shadow: inset 0 2px 2px rgba(0, 0, 0, 0.45), 0 2px 0 rgba(128, 128, 128, 0.25); diff --git a/src/components/Fields/EnumValues.tsx b/src/components/Fields/EnumValues.tsx index d10f9f69..05bb8072 100644 --- a/src/components/Fields/EnumValues.tsx +++ b/src/components/Fields/EnumValues.tsx @@ -59,7 +59,7 @@ export class EnumValues extends React.PureComponent{' '} {displayedItems.map((value, idx) => { - const exampleValue = enumSkipQuotes ? value : JSON.stringify(value); + const exampleValue = enumSkipQuotes ? String(value) : JSON.stringify(value); return ( {exampleValue}{' '} diff --git a/src/components/Fields/FieldDetail.tsx b/src/components/Fields/FieldDetail.tsx index 0697aeea..e5b28189 100644 --- a/src/components/Fields/FieldDetail.tsx +++ b/src/components/Fields/FieldDetail.tsx @@ -13,7 +13,7 @@ export class FieldDetail extends React.PureComponent { return null; } - const value = this.props.raw ? this.props.value : JSON.stringify(this.props.value); + const value = this.props.raw ? String(this.props.value) : JSON.stringify(this.props.value); return (
diff --git a/src/components/Fields/FieldDetails.tsx b/src/components/Fields/FieldDetails.tsx index 1aad8c61..3df94ded 100644 --- a/src/components/Fields/FieldDetails.tsx +++ b/src/components/Fields/FieldDetails.tsx @@ -59,7 +59,9 @@ export class FieldDetails extends React.PureComponent; + renderedExamples = ( + + ); } } @@ -78,14 +80,16 @@ export class FieldDetails extends React.PureComponent - {' '}< + {' '} + < {schema.contentEncoding} >{' '} )} {schema.contentMediaType && ( - {' '}< + {' '} + < {schema.contentMediaType} >{' '} @@ -124,7 +128,7 @@ export class FieldDetails extends React.PureComponent )} {(renderDiscriminatorSwitch && renderDiscriminatorSwitch(this.props)) || null} - {field.const && () || null} + {(field.const && ) || null}
); } @@ -142,7 +146,8 @@ function Examples({ field }: { field: FieldModel }) { {Object.values(field.examples).map((example, idx) => { return (
  • - {getSerializedValue(field, example.value)} - {example.summary || example.description} + {getSerializedValue(field, example.value)} -{' '} + {example.summary || example.description}
  • ); })} @@ -160,7 +165,6 @@ function getSerializedValue(field: FieldModel, example: any) { } } - const ExamplesList = styled.ul` margin-top: 1em; padding-left: 0; diff --git a/src/components/JsonViewer/JsonViewer.tsx b/src/components/JsonViewer/JsonViewer.tsx index ba9f20f1..2bcfc146 100644 --- a/src/components/JsonViewer/JsonViewer.tsx +++ b/src/components/JsonViewer/JsonViewer.tsx @@ -34,11 +34,11 @@ class Json extends React.PureComponent { - {(options) => ( + {options => ( (this.node = node!)} + ref={node => (this.node = node!)} dangerouslySetInnerHTML={{ __html: jsonToHTML(this.props.data, options.jsonSampleExpandLevel), }} diff --git a/src/components/JsonViewer/style.ts b/src/components/JsonViewer/style.ts index 82b1677a..4fcb9ea4 100644 --- a/src/components/JsonViewer/style.ts +++ b/src/components/JsonViewer/style.ts @@ -6,8 +6,8 @@ export const jsonStyles = css` pointer-events: none; } - font-family: ${(props) => props.theme.typography.code.fontFamily}; - font-size: ${(props) => props.theme.typography.code.fontSize}; + font-family: ${props => props.theme.typography.code.fontFamily}; + font-size: ${props => props.theme.typography.code.fontSize}; white-space: ${({ theme }) => (theme.typography.code.wrap ? 'pre-wrap' : 'pre')}; contain: content; @@ -51,8 +51,8 @@ export const jsonStyles = css` background-color: transparent; border: 0; color: #fff; - font-family: ${(props) => props.theme.typography.code.fontFamily}; - font-size: ${(props) => props.theme.typography.code.fontSize}; + font-family: ${props => props.theme.typography.code.fontFamily}; + font-size: ${props => props.theme.typography.code.fontSize}; padding-right: 6px; padding-left: 6px; padding-top: 0; diff --git a/src/components/Markdown/styled.elements.tsx b/src/components/Markdown/styled.elements.tsx index 64a0be9a..3c1b9210 100644 --- a/src/components/Markdown/styled.elements.tsx +++ b/src/components/Markdown/styled.elements.tsx @@ -26,7 +26,6 @@ export const StyledMarkdownBlock = styled( { compact?: boolean; inline?: boolean } >, )` - font-family: ${props => props.theme.typography.fontFamily}; font-weight: ${props => props.theme.typography.fontWeightRegular}; line-height: ${props => props.theme.typography.lineHeight}; @@ -81,7 +80,7 @@ export const StyledMarkdownBlock = styled( pre { font-family: ${props => props.theme.typography.code.fontFamily}; - white-space:${({ theme }) => (theme.typography.code.wrap ? 'pre-wrap' : 'pre')}; + white-space: ${({ theme }) => (theme.typography.code.wrap ? 'pre-wrap' : 'pre')}; background-color: ${({ theme }) => theme.codeBlock.backgroundColor}; color: white; padding: ${props => props.theme.spacing.unit * 4}px; @@ -121,7 +120,8 @@ export const StyledMarkdownBlock = styled( margin: 0; margin-bottom: 1em; - ul, ol { + ul, + ol { margin-bottom: 0; margin-top: 0; } diff --git a/src/components/Operation/Operation.tsx b/src/components/Operation/Operation.tsx index 57776cf0..3d970aa2 100644 --- a/src/components/Operation/Operation.tsx +++ b/src/components/Operation/Operation.tsx @@ -42,7 +42,7 @@ export class Operation extends React.Component { return ( - {(options) => ( + {options => (

    diff --git a/src/components/Parameters/Parameters.tsx b/src/components/Parameters/Parameters.tsx index eb3d2d61..d6fade69 100644 --- a/src/components/Parameters/Parameters.tsx +++ b/src/components/Parameters/Parameters.tsx @@ -67,7 +67,10 @@ function DropdownWithinHeader(props) { ); } -export function BodyContent(props: { content: MediaContentModel; description?: string }): JSX.Element { +export function BodyContent(props: { + content: MediaContentModel; + description?: string; +}): JSX.Element { const { content, description } = props; const { isRequestType } = content; return ( diff --git a/src/components/RedocStandalone.tsx b/src/components/RedocStandalone.tsx index 40bf73cb..c63a8385 100644 --- a/src/components/RedocStandalone.tsx +++ b/src/components/RedocStandalone.tsx @@ -1,6 +1,10 @@ import * as React from 'react'; -import { argValueToBoolean, RedocNormalizedOptions, RedocRawOptions } from '../services/RedocNormalizedOptions'; +import { + argValueToBoolean, + RedocNormalizedOptions, + RedocRawOptions, +} from '../services/RedocNormalizedOptions'; import { ErrorBoundary } from './ErrorBoundary'; import { Loading } from './Loading/Loading'; import { Redoc } from './Redoc/Redoc'; @@ -32,4 +36,4 @@ export const RedocStandalone = function (props: RedocStandaloneProps) { ); -} +}; diff --git a/src/components/Responses/styled.elements.ts b/src/components/Responses/styled.elements.ts index 92b790c2..531236dd 100644 --- a/src/components/Responses/styled.elements.ts +++ b/src/components/Responses/styled.elements.ts @@ -14,13 +14,13 @@ export const StyledResponseTitle = styled(ResponseTitle)` background-color: #f2f2f2; cursor: pointer; - color: ${(props) => props.theme.colors.responses[props.type].color}; - background-color: ${(props) => props.theme.colors.responses[props.type].backgroundColor}; + color: ${props => props.theme.colors.responses[props.type].color}; + background-color: ${props => props.theme.colors.responses[props.type].backgroundColor}; &:focus { outline: auto; - outline-color: ${(props) => props.theme.colors.responses[props.type].color}; + outline-color: ${props => props.theme.colors.responses[props.type].color}; } - ${(props) => + ${props => (props.empty && ` cursor: default; diff --git a/src/components/Schema/ArraySchema.tsx b/src/components/Schema/ArraySchema.tsx index 0b555c07..25ac633e 100644 --- a/src/components/Schema/ArraySchema.tsx +++ b/src/components/Schema/ArraySchema.tsx @@ -16,14 +16,17 @@ export class ArraySchema extends React.PureComponent { const schema = this.props.schema; const itemsSchema = schema.items; - const minMaxItems = schema.minItems === undefined && schema.maxItems === undefined ? - '' : - `(${humanizeConstraints(schema)})`; + const minMaxItems = + schema.minItems === undefined && schema.maxItems === undefined + ? '' + : `(${humanizeConstraints(schema)})`; if (schema.displayType && !itemsSchema && !minMaxItems.length) { - return (
    - {schema.displayType} -
    ); + return ( +
    + {schema.displayType} +
    + ); } return ( diff --git a/src/components/Schema/Schema.tsx b/src/components/Schema/Schema.tsx index 392a0f23..f22fb9fe 100644 --- a/src/components/Schema/Schema.tsx +++ b/src/components/Schema/Schema.tsx @@ -73,7 +73,7 @@ export class Schema extends React.Component> { } // TODO: maybe adjust FieldDetails to accept schema - const field = ({ + const field = { schema, name: '', required: false, @@ -82,7 +82,7 @@ export class Schema extends React.Component> { deprecated: false, toggle: () => null, expanded: false, - } as any) as FieldModel; // cast needed for hot-loader to not fail + } as any as FieldModel; // cast needed for hot-loader to not fail return (
    diff --git a/src/components/SearchBox/SearchBox.tsx b/src/components/SearchBox/SearchBox.tsx index f0427eb8..51015fa2 100644 --- a/src/components/SearchBox/SearchBox.tsx +++ b/src/components/SearchBox/SearchBox.tsx @@ -100,7 +100,7 @@ export class SearchBox extends React.PureComponent props.theme.typography.code.fontSize}; - font-family: ${(props) => props.theme.typography.code.fontFamily}; + font-size: ${props => props.theme.typography.code.fontSize}; + font-family: ${props => props.theme.typography.code.fontFamily}; border: 1px solid ${({ theme }) => theme.colors.border.dark}; margin: 0 3px; padding: 0.2em; @@ -67,12 +67,12 @@ export class SecurityRequirement extends React.PureComponent {security.schemes.length ? ( - security.schemes.map((scheme) => { + security.schemes.map(scheme => { return ( {scheme.id} {scheme.scopes.length > 0 && ' ('} - {scheme.scopes.map((scope) => ( + {scheme.scopes.map(scope => ( {scope} ))} {scheme.scopes.length > 0 && ') '} @@ -92,7 +92,7 @@ const AuthHeaderColumn = styled.div` `; const SecuritiesColumn = styled.div` - width: ${(props) => props.theme.schema.defaultDetailsWidth}; + width: ${props => props.theme.schema.defaultDetailsWidth}; ${media.lessThan('small')` margin-top: 10px; `} diff --git a/src/components/StoreBuilder.ts b/src/components/StoreBuilder.ts index 527d3700..7c12784c 100644 --- a/src/components/StoreBuilder.ts +++ b/src/components/StoreBuilder.ts @@ -30,7 +30,7 @@ const { Provider, Consumer } = StoreContext; export { Provider as StoreProvider, Consumer as StoreConsumer, StoreContext }; export function StoreBuilder(props: StoreBuilderProps) { - const {spec, specUrl, options, onLoaded, children } = props; + const { spec, specUrl, options, onLoaded, children } = props; const [resolvedSpec, setResolvedSpec] = React.useState(null); @@ -44,7 +44,7 @@ export function StoreBuilder(props: StoreBuilderProps) { setResolvedSpec(resolved); } load(); - }, [spec, specUrl]) + }, [spec, specUrl]); const store = React.useMemo(() => { if (!resolvedSpec) return null; @@ -62,7 +62,7 @@ export function StoreBuilder(props: StoreBuilderProps) { if (store && onLoaded) { onLoaded(); } - }, [store, onLoaded]) + }, [store, onLoaded]); return children({ loading: !store, diff --git a/src/components/__tests__/SecurityRequirement.test.tsx b/src/components/__tests__/SecurityRequirement.test.tsx index 723c0505..097777a6 100644 --- a/src/components/__tests__/SecurityRequirement.test.tsx +++ b/src/components/__tests__/SecurityRequirement.test.tsx @@ -10,14 +10,15 @@ const options = new RedocNormalizedOptions({}); describe('Components', () => { describe('SecurityRequirement', () => { describe('SecurityRequirement', () => { - it('should render \'None\' when empty object in security open api', () => { - const parser = new OpenAPIParser({ openapi: '3.0', info: { title: 'test', version: '0' }, paths: {} }, + it("should render 'None' when empty object in security open api", () => { + const parser = new OpenAPIParser( + { openapi: '3.0', info: { title: 'test', version: '0' }, paths: {} }, undefined, options, ); const securityRequirement = new SecurityRequirementModel({}, parser); const securityElement = shallow( - + , ).getElement(); expect(securityElement.props.children.type.target).toEqual('span'); expect(securityElement.props.children.props.children).toEqual('None'); diff --git a/src/services/MarkdownRenderer.ts b/src/services/MarkdownRenderer.ts index 9276a096..9ef98d60 100644 --- a/src/services/MarkdownRenderer.ts +++ b/src/services/MarkdownRenderer.ts @@ -121,10 +121,7 @@ export class MarkdownRenderer { prevRegexp = regexp; prevPos = currentPos; } - prevHeading.description = rawText - .substring(prevPos) - .replace(prevRegexp, '') - .trim(); + prevHeading.description = rawText.substring(prevPos).replace(prevRegexp, '').trim(); } headingRule = ( diff --git a/src/services/MenuBuilder.ts b/src/services/MenuBuilder.ts index 2d4f9309..767858fc 100644 --- a/src/services/MenuBuilder.ts +++ b/src/services/MenuBuilder.ts @@ -243,7 +243,7 @@ export class MenuBuilder { getTags(parser, webhooks, true); } - if (spec.paths){ + if (spec.paths) { getTags(parser, spec.paths); } diff --git a/src/services/OpenAPIParser.ts b/src/services/OpenAPIParser.ts index 4881c99e..a82b0d83 100644 --- a/src/services/OpenAPIParser.ts +++ b/src/services/OpenAPIParser.ts @@ -193,7 +193,10 @@ export class OpenAPIParser { if (keys.length === 0) { return resolved; } - if (mergeAsAllOf && keys.some((k) => k !== 'description' && k !== 'title' && k !== 'externalDocs')) { + if ( + mergeAsAllOf && + keys.some(k => k !== 'description' && k !== 'title' && k !== 'externalDocs') + ) { return { allOf: [rest, resolved], }; @@ -244,7 +247,7 @@ export class OpenAPIParser { } const allOfSchemas = schema.allOf - .map((subSchema) => { + .map(subSchema => { if (subSchema && subSchema.$ref && used$Refs.has(subSchema.$ref)) { return undefined; } @@ -258,7 +261,7 @@ export class OpenAPIParser { schema: subMerged, }; }) - .filter((child) => child !== undefined) as Array<{ + .filter(child => child !== undefined) as Array<{ $ref: string | undefined; schema: MergedOpenAPISchema; }>; @@ -337,7 +340,7 @@ export class OpenAPIParser { const def = this.deref(schemas[defName]); if ( def.allOf !== undefined && - def.allOf.find((obj) => obj.$ref !== undefined && $refs.indexOf(obj.$ref) > -1) + def.allOf.find(obj => obj.$ref !== undefined && $refs.indexOf(obj.$ref) > -1) ) { res['#/components/schemas/' + defName] = [def['x-discriminator-value'] || defName]; } @@ -363,7 +366,7 @@ export class OpenAPIParser { const beforeAllOf = allOf.slice(0, i); const afterAllOf = allOf.slice(i + 1); return { - oneOf: sub.oneOf.map((part) => { + oneOf: sub.oneOf.map(part => { const merged = this.mergeAllOf({ allOf: [...beforeAllOf, part, ...afterAllOf], }); diff --git a/src/services/RedocNormalizedOptions.ts b/src/services/RedocNormalizedOptions.ts index f1bb3c49..f4a355e8 100644 --- a/src/services/RedocNormalizedOptions.ts +++ b/src/services/RedocNormalizedOptions.ts @@ -73,7 +73,7 @@ export class RedocNormalizedOptions { } if (typeof value === 'string') { const res = {}; - value.split(',').forEach((code) => { + value.split(',').forEach(code => { res[code.trim()] = true; }); return res; @@ -139,7 +139,7 @@ export class RedocNormalizedOptions { case 'false': return false; default: - return value.split(',').map((ext) => ext.trim()); + return value.split(',').map(ext => ext.trim()); } } @@ -277,7 +277,7 @@ export class RedocNormalizedOptions { this.maxDisplayedEnumValues = argValueToNumber(raw.maxDisplayedEnumValues); const ignoreNamedSchemas = Array.isArray(raw.ignoreNamedSchemas) ? raw.ignoreNamedSchemas - : raw.ignoreNamedSchemas?.split(',').map((s) => s.trim()); + : raw.ignoreNamedSchemas?.split(',').map(s => s.trim()); this.ignoreNamedSchemas = new Set(ignoreNamedSchemas); this.hideSchemaPattern = argValueToBoolean(raw.hideSchemaPattern); this.generatedPayloadSamplesMaxDepth = diff --git a/src/services/SearchStore.ts b/src/services/SearchStore.ts index ebf2ca56..927bc14c 100644 --- a/src/services/SearchStore.ts +++ b/src/services/SearchStore.ts @@ -59,7 +59,7 @@ export class SearchStore { fromExternalJS(path?: string, exportName?: string) { if (path && exportName) { - this.searchWorker.fromExternalJS(path, exportName) + this.searchWorker.fromExternalJS(path, exportName); } } } diff --git a/src/services/SearchWorker.worker.ts b/src/services/SearchWorker.worker.ts index a90287b5..a4a6da4b 100644 --- a/src/services/SearchWorker.worker.ts +++ b/src/services/SearchWorker.worker.ts @@ -1,12 +1,5 @@ import * as lunr from 'lunr'; -try { - // tslint:disable-next-line - require('core-js/es/promise'); // bundle into worker -} catch (_) { - // nope -} - /* just for better typings */ export default class Worker { add: typeof add = add; diff --git a/src/services/SpecStore.ts b/src/services/SpecStore.ts index 277e2c03..783e1b59 100644 --- a/src/services/SpecStore.ts +++ b/src/services/SpecStore.ts @@ -28,7 +28,10 @@ export class SpecStore { this.externalDocs = this.parser.spec.externalDocs; this.contentItems = MenuBuilder.buildStructure(this.parser, this.options); this.securitySchemes = new SecuritySchemesModel(this.parser); - const webhookPath: Referenced = {...this.parser?.spec?.['x-webhooks'], ...this.parser?.spec.webhooks}; + const webhookPath: Referenced = { + ...this.parser?.spec?.['x-webhooks'], + ...this.parser?.spec.webhooks, + }; this.webhooks = new WebhookModel(this.parser, options, webhookPath); } } diff --git a/src/services/__tests__/OpenAPIParser.test.ts b/src/services/__tests__/OpenAPIParser.test.ts index b8b211ba..60dea809 100644 --- a/src/services/__tests__/OpenAPIParser.test.ts +++ b/src/services/__tests__/OpenAPIParser.test.ts @@ -26,6 +26,5 @@ describe('Models', () => { expect(parser.shallowDeref(schemaOrRef)).toMatchSnapshot(); }); - }); }); diff --git a/src/services/__tests__/models/ApiInfo.test.ts b/src/services/__tests__/models/ApiInfo.test.ts index 5db42527..0bf148a1 100644 --- a/src/services/__tests__/models/ApiInfo.test.ts +++ b/src/services/__tests__/models/ApiInfo.test.ts @@ -54,8 +54,8 @@ describe('Models', () => { license: { name: 'MIT', identifier: 'MIT', - url: 'https://opensource.org/licenses/MIT' - } + url: 'https://opensource.org/licenses/MIT', + }, }, } as any; diff --git a/src/services/__tests__/models/MenuBuilder.test.ts b/src/services/__tests__/models/MenuBuilder.test.ts index ed930639..7deb3ef3 100644 --- a/src/services/__tests__/models/MenuBuilder.test.ts +++ b/src/services/__tests__/models/MenuBuilder.test.ts @@ -19,7 +19,6 @@ describe('Models', () => { expect(contentItems[0].id).toEqual('tag/pet'); expect(contentItems[0].name).toEqual('pet'); expect(contentItems[0].type).toEqual('tag'); - }); }); }); diff --git a/src/services/__tests__/models/Response.test.ts b/src/services/__tests__/models/Response.test.ts index ae3c1fd1..ebb836c0 100644 --- a/src/services/__tests__/models/Response.test.ts +++ b/src/services/__tests__/models/Response.test.ts @@ -20,23 +20,23 @@ describe('Models', () => { }); test('should calculate response type based on code', () => { - let resp = new ResponseModel({...props, code: '200' }); + let resp = new ResponseModel({ ...props, code: '200' }); expect(resp.type).toEqual('success'); - resp = new ResponseModel({...props, code: '120' }); + resp = new ResponseModel({ ...props, code: '120' }); expect(resp.type).toEqual('info'); - resp = new ResponseModel({...props, code: '301' }); + resp = new ResponseModel({ ...props, code: '301' }); expect(resp.type).toEqual('redirect'); - resp = new ResponseModel({...props, code: '400' }); + resp = new ResponseModel({ ...props, code: '400' }); expect(resp.type).toEqual('error'); }); test('default should be successful by default', () => { - const resp = new ResponseModel({...props, code: 'default' }); + const resp = new ResponseModel({ ...props, code: 'default' }); expect(resp.type).toEqual('success'); }); test('default should be error if defaultAsError is true', () => { - const resp = new ResponseModel({...props, code: 'default', defaultAsError: true }); + const resp = new ResponseModel({ ...props, code: 'default', defaultAsError: true }); expect(resp.type).toEqual('error'); }); }); diff --git a/src/services/models/Operation.ts b/src/services/models/Operation.ts index 6d12808d..88f30d56 100644 --- a/src/services/models/Operation.ts +++ b/src/services/models/Operation.ts @@ -109,7 +109,7 @@ export class OperationModel implements IMenuItem { // NOTE: Callbacks by default should not inherit the specification's global `security` definition. // Can be defined individually per-callback in the specification. Defaults to none. this.security = (operationSpec.security || []).map( - (security) => new SecurityRequirementModel(security, parser), + security => new SecurityRequirementModel(security, parser), ); // TODO: update getting pathInfo for overriding servers on path level @@ -123,7 +123,7 @@ export class OperationModel implements IMenuItem { : this.pointer; this.security = (operationSpec.security || parser.spec.security || []).map( - (security) => new SecurityRequirementModel(security, parser), + security => new SecurityRequirementModel(security, parser), ); this.servers = normalizeServers( @@ -174,7 +174,8 @@ export class OperationModel implements IMenuItem { @memoize get requestBody() { return ( - this.operationSpec.requestBody && new RequestBodyModel({ + this.operationSpec.requestBody && + new RequestBodyModel({ parser: this.parser, infoOrRef: this.operationSpec.requestBody, options: this.options, @@ -219,7 +220,7 @@ export class OperationModel implements IMenuItem { this.operationSpec.pathParameters, this.operationSpec.parameters, // TODO: fix pointer - ).map((paramOrRef) => new FieldModel(this.parser, paramOrRef, this.pointer, this.options)); + ).map(paramOrRef => new FieldModel(this.parser, paramOrRef, this.pointer, this.options)); if (this.options.sortPropsAlphabetically) { return sortByField(_parameters, 'name'); @@ -235,7 +236,7 @@ export class OperationModel implements IMenuItem { get responses() { let hasSuccessResponses = false; return Object.keys(this.operationSpec.responses || []) - .filter((code) => { + .filter(code => { if (code === 'default') { return true; } @@ -246,7 +247,7 @@ export class OperationModel implements IMenuItem { return isStatusCode(code); }) // filter out other props (e.g. x-props) - .map((code) => { + .map(code => { return new ResponseModel({ parser: this.parser, code, @@ -260,7 +261,7 @@ export class OperationModel implements IMenuItem { @memoize get callbacks() { - return Object.keys(this.operationSpec.callbacks || []).map((callbackEventName) => { + return Object.keys(this.operationSpec.callbacks || []).map(callbackEventName => { return new CallbackModel( this.parser, callbackEventName, diff --git a/src/services/models/RequestBody.ts b/src/services/models/RequestBody.ts index 3ca3dc21..2be8ebcf 100644 --- a/src/services/models/RequestBody.ts +++ b/src/services/models/RequestBody.ts @@ -9,7 +9,7 @@ type RequestBodyProps = { infoOrRef: Referenced; options: RedocNormalizedOptions; isEvent: boolean; -} +}; export class RequestBodyModel { description: string; diff --git a/src/services/models/Response.ts b/src/services/models/Response.ts index d3ed554a..0a264132 100644 --- a/src/services/models/Response.ts +++ b/src/services/models/Response.ts @@ -9,13 +9,13 @@ import { FieldModel } from './Field'; import { MediaContentModel } from './MediaContent'; type ResponseProps = { - parser: OpenAPIParser, - code: string, - defaultAsError: boolean, - infoOrRef: Referenced, - options: RedocNormalizedOptions, - isEvent: boolean, -} + parser: OpenAPIParser; + code: string; + defaultAsError: boolean; + infoOrRef: Referenced; + options: RedocNormalizedOptions; + isEvent: boolean; +}; export class ResponseModel { @observable diff --git a/src/services/models/Schema.ts b/src/services/models/Schema.ts index b155d254..b48f232c 100644 --- a/src/services/models/Schema.ts +++ b/src/services/models/Schema.ts @@ -134,7 +134,10 @@ export class SchemaModel { this.maxItems = schema.maxItems; if (!!schema.nullable || schema['x-nullable']) { - if (Array.isArray(this.type) && !this.type.some((value) => value === null || value === 'null')) { + if ( + Array.isArray(this.type) && + !this.type.some(value => value === null || value === 'null') + ) { this.type = [...this.type, 'null']; } else if (!Array.isArray(this.type) && (this.type !== null || this.type !== 'null')) { this.type = [this.type, 'null']; @@ -142,7 +145,7 @@ export class SchemaModel { } this.displayType = Array.isArray(this.type) - ? this.type.map(item => item === null ? 'null' : item).join(' or ') + ? this.type.map(item => (item === null ? 'null' : item)).join(' or ') : this.type; if (this.isCircular) { @@ -155,7 +158,7 @@ export class SchemaModel { } else if ( isChild && Array.isArray(schema.oneOf) && - schema.oneOf.find((s) => s.$ref === this.pointer) + schema.oneOf.find(s => s.$ref === this.pointer) ) { // we hit allOf of the schema with the parent discriminator delete schema.oneOf; @@ -195,8 +198,7 @@ export class SchemaModel { } if (Array.isArray(this.type)) { const filteredType = this.type.filter(item => item !== 'array'); - if (filteredType.length) - this.displayType += ` or ${filteredType.join(' or ')}`; + if (filteredType.length) this.displayType += ` or ${filteredType.join(' or ')}`; } } @@ -215,7 +217,7 @@ export class SchemaModel { const title = isNamedDefinition(variant.$ref) && !merged.title ? JsonPointer.baseName(variant.$ref) - : `${(merged.title || '')}${(merged.const && JSON.stringify(merged.const)) || ''}`; + : `${merged.title || ''}${(merged.const && JSON.stringify(merged.const)) || ''}`; const schema = new SchemaModel( parser, @@ -243,7 +245,7 @@ export class SchemaModel { this.displayType = types.join(' or '); } else { this.displayType = this.oneOf - .map((schema) => { + .map(schema => { let name = schema.typePrefix + (schema.title ? `${schema.title} (${schema.displayType})` : schema.displayType); @@ -364,7 +366,7 @@ function buildFields( const props = schema.properties || {}; const additionalProps = schema.additionalProperties; const defaults = schema.default; - let fields = Object.keys(props || []).map((fieldName) => { + let fields = Object.keys(props || []).map(fieldName => { let field = props[fieldName]; if (!field) { diff --git a/src/standalone.tsx b/src/standalone.tsx index 12b65daa..5a331d69 100644 --- a/src/standalone.tsx +++ b/src/standalone.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { hydrate as hydrateComponent, render } from 'react-dom'; -import { configure } from "mobx" +import { configure } from 'mobx'; import { Redoc, RedocStandalone } from './components/'; import { AppStore, StoreState } from './services/AppStore'; @@ -8,8 +8,8 @@ import { debugTime, debugTimeEnd } from './utils/debug'; import { querySelector } from './utils/dom'; configure({ - useProxies: 'ifavailable' -}) + useProxies: 'ifavailable', +}); export { Redoc, AppStore } from '.'; diff --git a/src/styled-components.ts b/src/styled-components.ts index 689ad538..43f65830 100644 --- a/src/styled-components.ts +++ b/src/styled-components.ts @@ -16,7 +16,7 @@ export const media = { lessThan(breakpoint, print?: boolean, extra?: string) { return (...args) => css` @media ${print ? 'print, ' : ''} screen and (max-width: ${props => - props.theme.breakpoints[breakpoint]})${extra || ''} { + props.theme.breakpoints[breakpoint]}) ${extra || ''} { ${(css as any)(...args)}; } `; diff --git a/src/utils/__tests__/loadAndBundleSpec.test.ts b/src/utils/__tests__/loadAndBundleSpec.test.ts index 460a1a8b..4abb3a6c 100644 --- a/src/utils/__tests__/loadAndBundleSpec.test.ts +++ b/src/utils/__tests__/loadAndBundleSpec.test.ts @@ -11,7 +11,9 @@ describe('#loadAndBundleSpec', () => { }); it('should load And Bundle Spec demo/openapi-3-1.yaml', async () => { - const spec = yaml.load(readFileSync(resolve(__dirname, '../../../demo/openapi-3-1.yaml'), 'utf-8')); + const spec = yaml.load( + readFileSync(resolve(__dirname, '../../../demo/openapi-3-1.yaml'), 'utf-8'), + ); const bundledSpec = await loadAndBundleSpec(spec); expect(bundledSpec).toMatchSnapshot(); }); diff --git a/src/utils/__tests__/openapi.test.ts b/src/utils/__tests__/openapi.test.ts index 6f40d974..e2ed3db4 100644 --- a/src/utils/__tests__/openapi.test.ts +++ b/src/utils/__tests__/openapi.test.ts @@ -10,6 +10,7 @@ import { pluralizeType, serializeParameterValue, sortByRequired, + humanizeNumberRange, } from '../'; import { FieldModel, OpenAPIParser, RedocNormalizedOptions } from '../../services'; @@ -103,7 +104,7 @@ describe('Utils', () => { it('Should return pathName if no summary, operationId, description', () => { const operation = { - pathName: '/sandbox/test' + pathName: '/sandbox/test', }; expect(getOperationSummary(operation as any)).toBe('/sandbox/test'); }); @@ -174,7 +175,7 @@ describe('Utils', () => { expect(isPrimitiveType(schema)).toEqual(false); }); - it('should return true for array contains object and schema hasn\'t properties', () => { + it("should return true for array contains object and schema hasn't properties", () => { const schema = { type: ['object', 'string'], }; @@ -231,10 +232,10 @@ describe('Utils', () => { const schema = { type: 'array', items: { - type: 'array', - items: { - type: 'string' - }, + type: 'array', + items: { + type: 'string', + }, }, }; expect(isPrimitiveType(schema)).toEqual(true); @@ -410,12 +411,82 @@ describe('Utils', () => { }); }); + describe('openapi humanizeNumberRange', () => { + it('should return `>=` when only minimum value present or exclusiveMinimum = false', () => { + const expected = '>= 0'; + expect(humanizeNumberRange({ minimum: 0 })).toEqual(expected); + expect(humanizeNumberRange({ minimum: 0, exclusiveMinimum: false })).toEqual(expected); + }); + + it('should return `>` when minimum value present and exclusiveMinimum set to true', () => { + expect(humanizeNumberRange({ minimum: 0, exclusiveMinimum: true })).toEqual('> 0'); + }); + + it('should return `<=` when only maximum value present or exclusiveMinimum = false', () => { + const expected = '<= 10'; + expect(humanizeNumberRange({ maximum: 10 })).toEqual(expected); + expect(humanizeNumberRange({ maximum: 10, exclusiveMaximum: false })).toEqual(expected); + }); + + it('should return `<` when maximum value present and exclusiveMaximum set to true', () => { + expect(humanizeNumberRange({ maximum: 10, exclusiveMaximum: true })).toEqual('< 10'); + }); + + it('should return correct range for minimum and maximum values and with different exclusive set', () => { + expect(humanizeNumberRange({ minimum: 0, maximum: 10 })).toEqual('[ 0 .. 10 ]'); + expect( + humanizeNumberRange({ + minimum: 0, + exclusiveMinimum: true, + maximum: 10, + exclusiveMaximum: true, + }), + ).toEqual('( 0 .. 10 )'); + expect( + humanizeNumberRange({ + minimum: 0, + maximum: 10, + exclusiveMaximum: true, + }), + ).toEqual('[ 0 .. 10 )'); + expect( + humanizeNumberRange({ + minimum: 0, + exclusiveMinimum: true, + maximum: 10, + }), + ).toEqual('( 0 .. 10 ]'); + }); + + it('should return correct range exclusive values only', () => { + expect(humanizeNumberRange({ exclusiveMinimum: 0 })).toEqual('> 0'); + expect(humanizeNumberRange({ exclusiveMaximum: 10 })).toEqual('< 10'); + expect(humanizeNumberRange({ exclusiveMinimum: 0, exclusiveMaximum: 10 })).toEqual( + '( 0 .. 10 )', + ); + }); + + it('should return correct min value', () => { + expect(humanizeNumberRange({ minimum: 5, exclusiveMinimum: 10 })).toEqual('> 5'); + expect(humanizeNumberRange({ minimum: -5, exclusiveMinimum: -10 })).toEqual('> -10'); + }); + + it('should return correct max value', () => { + expect(humanizeNumberRange({ maximum: 10, exclusiveMaximum: 15 })).toEqual('< 15'); + expect(humanizeNumberRange({ maximum: -10, exclusiveMaximum: -15 })).toEqual('< -10'); + }); + + it('should return undefined', () => { + expect(humanizeNumberRange({})).toEqual(undefined); + }); + }); + describe('openapi humanizeConstraints', () => { const itemConstraintSchema = ( min?: number, max?: number, multipleOf?: number, - uniqueItems?: boolean + uniqueItems?: boolean, ) => ({ type: 'array', minItems: min, maxItems: max, multipleOf, uniqueItems }); it('should not have a humanized constraint without schema constraints', () => { @@ -455,9 +526,9 @@ describe('Utils', () => { }); it('should have a humanized constraint when uniqueItems is set', () => { - expect(humanizeConstraints(itemConstraintSchema(undefined, undefined, undefined, true))).toContain( - 'unique', - ); + expect( + humanizeConstraints(itemConstraintSchema(undefined, undefined, undefined, true)), + ).toContain('unique'); }); }); diff --git a/src/utils/decorators.ts b/src/utils/decorators.ts index bfe5726f..ef26087d 100644 --- a/src/utils/decorators.ts +++ b/src/utils/decorators.ts @@ -12,7 +12,7 @@ function throttle(func, wait) { context = args = null; } }; - return function() { + return function () { const now = new Date().getTime(); const remaining = wait - (now - previous); context = this; diff --git a/src/utils/dom.ts b/src/utils/dom.ts index ce91a33a..515d9a2b 100644 --- a/src/utils/dom.ts +++ b/src/utils/dom.ts @@ -27,7 +27,7 @@ export function html2Str(html: string): string { // scrollIntoViewIfNeeded polyfill if (typeof Element !== 'undefined' && !(Element as any).prototype.scrollIntoViewIfNeeded) { - (Element as any).prototype.scrollIntoViewIfNeeded = function(centerIfNeeded) { + (Element as any).prototype.scrollIntoViewIfNeeded = function (centerIfNeeded) { centerIfNeeded = arguments.length === 0 ? true : !!centerIfNeeded; const parent = this.parentNode; diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index d84217a9..32a5956f 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -50,7 +50,7 @@ export function flattenByProp( for (const item of items) { res.push(item); if (item[prop]) { - iterate((item[prop] as any) as T[]); + iterate(item[prop] as any as T[]); } } }; diff --git a/src/utils/loadAndBundleSpec.ts b/src/utils/loadAndBundleSpec.ts index e51aad5c..8ffb21c2 100644 --- a/src/utils/loadAndBundleSpec.ts +++ b/src/utils/loadAndBundleSpec.ts @@ -14,8 +14,8 @@ export async function loadAndBundleSpec(specUrlOrObject: object | string): Promi const config = new Config({}); const bundleOpts = { config, - base: IS_BROWSER ? window.location.href : process.cwd() - } + base: IS_BROWSER ? window.location.href : process.cwd(), + }; if (IS_BROWSER) { config.resolve.http.customFetch = global.fetch; @@ -24,13 +24,15 @@ export async function loadAndBundleSpec(specUrlOrObject: object | string): Promi if (typeof specUrlOrObject === 'object' && specUrlOrObject !== null) { bundleOpts['doc'] = { source: { absoluteRef: '' } as Source, - parsed: specUrlOrObject - } as Document + parsed: specUrlOrObject, + } as Document; } else { bundleOpts['ref'] = specUrlOrObject; } - const { bundle: { parsed } } = await bundle(bundleOpts); + const { + bundle: { parsed }, + } = await bundle(bundleOpts); return parsed.swagger !== undefined ? convertSwagger2OpenAPI(parsed) : parsed; } diff --git a/src/utils/memoize.ts b/src/utils/memoize.ts index ecdfd7f8..a0c0209c 100644 --- a/src/utils/memoize.ts +++ b/src/utils/memoize.ts @@ -3,7 +3,7 @@ const SENTINEL = {}; export function memoize(target: any, name: string, descriptor: TypedPropertyDescriptor) { if (typeof descriptor.value === 'function') { - return (_memoizeMethod(target, name, descriptor) as any) as TypedPropertyDescriptor; + return _memoizeMethod(target, name, descriptor) as any as TypedPropertyDescriptor; } else if (typeof descriptor.get === 'function') { return _memoizeGetter(target, name, descriptor) as TypedPropertyDescriptor; } else { diff --git a/src/utils/openapi.ts b/src/utils/openapi.ts index d0b081f5..6c4cb952 100644 --- a/src/utils/openapi.ts +++ b/src/utils/openapi.ts @@ -113,7 +113,10 @@ export function detectType(schema: OpenAPISchema): string { return 'any'; } -export function isPrimitiveType(schema: OpenAPISchema, type: string | string[] | undefined = schema.type) { +export function isPrimitiveType( + schema: OpenAPISchema, + type: string | string[] | undefined = schema.type, +) { if (schema.oneOf !== undefined || schema.anyOf !== undefined) { return false; } @@ -122,9 +125,10 @@ export function isPrimitiveType(schema: OpenAPISchema, type: string | string[] | const isArray = Array.isArray(type); if (type === 'object' || (isArray && type?.includes('object'))) { - isPrimitive = schema.properties !== undefined - ? Object.keys(schema.properties).length === 0 - : schema.additionalProperties === undefined; + isPrimitive = + schema.properties !== undefined + ? Object.keys(schema.properties).length === 0 + : schema.additionalProperties === undefined; } if (schema.items !== undefined && (type === 'array' || (isArray && type?.includes('array')))) { @@ -376,7 +380,7 @@ export function isNamedDefinition(pointer?: string): boolean { export function getDefinitionName(pointer?: string): string | undefined { if (!pointer) return undefined; const match = pointer.match(/^#\/components\/(schemas|pathItems)\/([^\/]+)$/); - return match === null ? undefined : match[1] + return match === null ? undefined : match[1]; } function humanizeMultipleOfConstraint(multipleOf: number | undefined): string | undefined { @@ -415,6 +419,29 @@ function humanizeRangeConstraint( return stringRange; } +export function humanizeNumberRange(schema: OpenAPISchema): string | undefined { + const minimum = + typeof schema.exclusiveMinimum === 'number' + ? Math.min(schema.exclusiveMinimum, schema.minimum ?? Infinity) + : schema.minimum; + const maximum = + typeof schema.exclusiveMaximum === 'number' + ? Math.max(schema.exclusiveMaximum, schema.maximum ?? -Infinity) + : schema.maximum; + const exclusiveMinimum = typeof schema.exclusiveMinimum === 'number' || schema.exclusiveMinimum; + const exclusiveMaximum = typeof schema.exclusiveMaximum === 'number' || schema.exclusiveMaximum; + + if (minimum !== undefined && maximum !== undefined) { + return `${exclusiveMinimum ? '( ' : '[ '}${minimum} .. ${maximum}${ + exclusiveMaximum ? ' )' : ' ]' + }`; + } else if (maximum !== undefined) { + return `${exclusiveMaximum ? '< ' : '<= '}${maximum}`; + } else if (minimum !== undefined) { + return `${exclusiveMinimum ? '> ' : '>= '}${minimum}`; + } +} + export function humanizeConstraints(schema: OpenAPISchema): string[] { const res: string[] = []; @@ -433,33 +460,7 @@ export function humanizeConstraints(schema: OpenAPISchema): string[] { res.push(multipleOfConstraint); } - let numberRange; - if (schema.minimum !== undefined && schema.maximum !== undefined) { - numberRange = schema.exclusiveMinimum ? '( ' : '[ '; - numberRange += schema.minimum; - numberRange += ' .. '; - numberRange += schema.maximum; - numberRange += schema.exclusiveMaximum ? ' )' : ' ]'; - } else if (schema.maximum !== undefined) { - numberRange = schema.exclusiveMaximum ? '< ' : '<= '; - numberRange += schema.maximum; - } else if (schema.minimum !== undefined) { - numberRange = schema.exclusiveMinimum ? '> ' : '>= '; - numberRange += schema.minimum; - } - - if (typeof schema.exclusiveMinimum === 'number' || typeof schema.exclusiveMaximum === 'number') { - let minimum = 0; - let maximum = 0; - if (schema.minimum) minimum = schema.minimum; - if (typeof schema.exclusiveMinimum === 'number') minimum = minimum <= schema.exclusiveMinimum ? minimum : schema.exclusiveMinimum; - - if (schema.maximum) maximum = schema.maximum; - if (typeof schema.exclusiveMaximum === 'number') maximum = maximum > schema.exclusiveMaximum ? maximum : schema.exclusiveMaximum; - - numberRange = `[${minimum} .. ${maximum}]` - } - + const numberRange = humanizeNumberRange(schema); if (numberRange !== undefined) { res.push(numberRange); } @@ -589,10 +590,10 @@ export function setSecuritySchemePrefix(prefix: string) { } export const shortenHTTPVerb = verb => -({ - delete: 'del', - options: 'opts', -}[verb] || verb); + ({ + delete: 'del', + options: 'opts', + }[verb] || verb); export function isRedocExtension(key: string): boolean { const redocExtensions = { diff --git a/webpack.config.ts b/webpack.config.ts index dee1ada4..c5778b4d 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -32,10 +32,10 @@ const BANNER = `ReDoc - OpenAPI/Swagger-generated API Reference Documentation Version: ${VERSION} Repo: https://github.com/Redocly/redoc`; -export default (env: { standalone?: boolean } = {}) => ({ +export default (env: { standalone?: boolean, browser?: boolean } = {}) => ({ entry: env.standalone ? ['./src/polyfills.ts', './src/standalone.tsx'] : './src/index.ts', output: { - filename: env.standalone ? 'redoc.standalone.js' : 'redoc.lib.js', + filename: env.standalone ? 'redoc.standalone.js' : env.browser ? 'redoc.browser.lib.js' : 'redoc.lib.js', path: path.join(__dirname, '/bundles'), library: 'Redoc', libraryTarget: 'umd', @@ -47,13 +47,13 @@ export default (env: { standalone?: boolean } = {}) => ({ fallback: { path: require.resolve('path-browserify'), http: false, - fs: false, - os: false, + fs: path.resolve(__dirname, 'src/empty.js'), + os: path.resolve(__dirname, 'src/empty.js'), + tty: path.resolve(__dirname, 'src/empty.js'), } }, performance: false, - // target: 'node', - externalsPresets: env.standalone ? {} : { node: true }, + externalsPresets: env.standalone || env.browser ? {} : { node: true }, externals: env.standalone ? { esprima: 'null',