├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml ├── labeler.yml └── workflows │ ├── codeql-analysis.yml │ ├── greetings.yml │ ├── label.yml │ └── quisque.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── SECURITY.md ├── example ├── config.json ├── example.gif ├── ghes-2.20.yml └── openapi.yml ├── package.json ├── src-engine ├── cli.js └── index.js ├── src ├── .babelrc ├── .browserslistrc ├── assets │ ├── highlighter-theme-dark.less │ ├── highlighter-theme-light.less │ ├── theme.less │ └── utils.less ├── components │ ├── ContactTeam.vue │ ├── DocEntry.vue │ ├── DocEntryTitle.vue │ ├── DocFooter.vue │ ├── DocHeader.vue │ ├── DocLayout.vue │ ├── DocSidebar.vue │ ├── DocSidebarEntry.vue │ ├── DocSidebarTag.vue │ ├── DocTag.vue │ ├── DocToolbox.vue │ ├── DocToolboxDropdown.vue │ ├── DocToolboxIcon.vue │ ├── Endpoint.vue │ ├── Example.vue │ ├── Highlight.vue │ ├── Markdown.vue │ ├── ReqBody.vue │ ├── ReqResponse.vue │ ├── Shield.vue │ ├── SplitSection.vue │ └── StatusCode.vue ├── environment.json ├── index.html ├── index.js ├── index.vue ├── plugins │ ├── codegen.js │ └── envInject.js ├── store.js └── vue.config.js └── yarn.lock /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. 4 | 5 | Fixes # (issue) 6 | 7 | ### Type of change 8 | 9 | Please delete options that are not relevant. 10 | 11 | - [ ] Bug fix (non-breaking change which fixes an issue) 12 | - [ ] New feature (non-breaking change which adds functionality) 13 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 14 | - [ ] This change requires a documentation update 15 | 16 | ### Checklist: 17 | 18 | - [ ] My code follows the style guidelines of this project 19 | - [ ] I have performed a self-review of my own code 20 | - [ ] I have commented my code in hard-to-understand areas 21 | - [ ] I have made corresponding changes to the documentation 22 | - [ ] My changes generate no new warnings 23 | - [ ] Any dependent changes have been merged and published in downstream modules 24 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | assignees: 8 | - "r4wizard" 9 | pull-request-branch-name: 10 | separator: "-" 11 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | documentation: 2 | - "**/*.md" 3 | 4 | engine: 5 | - "src/**/*" 6 | 7 | template: 8 | - "src-nuxt/**/*" 9 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [master] 9 | schedule: 10 | - cron: '0 19 * * 2' 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | # Override automatic language detection by changing the below list 21 | # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] 22 | language: ['javascript'] 23 | # Learn more... 24 | # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection 25 | 26 | steps: 27 | - name: Checkout repository 28 | uses: actions/checkout@v2 29 | with: 30 | # We must fetch at least the immediate parents so that if this is 31 | # a pull request then we can checkout the head. 32 | fetch-depth: 2 33 | 34 | # If this run was triggered by a pull request event, then checkout 35 | # the head of the pull request instead of the merge commit. 36 | - run: git checkout HEAD^2 37 | if: ${{ github.event_name == 'pull_request' }} 38 | 39 | # Initializes the CodeQL tools for scanning. 40 | - name: Initialize CodeQL 41 | uses: github/codeql-action/init@v1 42 | with: 43 | languages: ${{ matrix.language }} 44 | 45 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 46 | # If this step fails, then you should remove it and run the build manually (see below) 47 | - name: Autobuild 48 | uses: github/codeql-action/autobuild@v1 49 | 50 | # ℹ️ Command-line programs to run using the OS shell. 51 | # 📚 https://git.io/JvXDl 52 | 53 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 54 | # and modify them (or add more) to build your code if your project 55 | # uses a compiled language 56 | 57 | #- run: | 58 | # make bootstrap 59 | # make release 60 | 61 | - name: Perform CodeQL Analysis 62 | uses: github/codeql-action/analyze@v1 63 | -------------------------------------------------------------------------------- /.github/workflows/greetings.yml: -------------------------------------------------------------------------------- 1 | name: Greetings 2 | 3 | on: [pull_request, issues] 4 | 5 | jobs: 6 | greeting: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/first-interaction@v1 10 | with: 11 | repo-token: ${{ secrets.GITHUB_TOKEN }} 12 | issue-message: 'Thank you for opening your first issue! Please ensure you''ve read the [CONTRIBUTING.md](https://github.com/ouropencode/OpenDocumenter/blob/master/CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](https://github.com/ouropencode/OpenDocumenter/blob/master/CODE_OF_CONDUCT.md) files.' 13 | pr-message: 'Thank you for opening your first pull request! Please ensure you''ve read the [CONTRIBUTING.md](https://github.com/ouropencode/OpenDocumenter/blob/master/CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](https://github.com/ouropencode/OpenDocumenter/blob/master/CODE_OF_CONDUCT.md) files.' 14 | -------------------------------------------------------------------------------- /.github/workflows/label.yml: -------------------------------------------------------------------------------- 1 | name: Labeler 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | label: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/labeler@v2 10 | with: 11 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 12 | -------------------------------------------------------------------------------- /.github/workflows/quisque.yml: -------------------------------------------------------------------------------- 1 | name: Quisque API Build 2 | 3 | on: 4 | push: 5 | branches: ["master"] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Setup Node.js Environment 12 | uses: actions/setup-node@v2.1.1 13 | with: 14 | node-version: '>=14.6' 15 | - name: Checkout Git Repository 16 | uses: actions/checkout@master 17 | - name: Install Dependencies (with caching) 18 | uses: bahmutov/npm-install@v1 19 | - name: Generate Documentation 20 | run: npm run dev-generate 21 | - name: Upload Build Artifact 22 | uses: actions/upload-artifact@v2 23 | with: 24 | name: documentation 25 | path: example/docs/ 26 | if-no-files-found: error 27 | - name: Deploy to GitHub Pages 28 | uses: crazy-max/ghaction-github-pages@v2.1.1 29 | with: 30 | target_branch: gh-pages 31 | build_dir: example/docs/ 32 | env: 33 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .nuxt/ 3 | dist/ 4 | docs/ 5 | tmp/ 6 | *.log 7 | package-lock.json 8 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at ouropencode@shadowacre.ltd. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We love pull requests from everyone. 4 | 5 | When contributing to this repository, please first discuss the change you wish to make by opening an issue. Please note we have a [code of conduct](CODE_OF_CONDUCT.md), please follow it in all your interactions with the project. 6 | 7 | ## Development Setup 8 | 9 | ### Fork the repository 10 | ```bash 11 | > git clone git@github.com:ouropencode/opendocumenter.git 12 | ``` 13 | 14 | ### Set up your machine 15 | ```bash 16 | > yarn install 17 | ``` 18 | 19 | ### Execute 20 | ```bash 21 | > yarn start # run the example 'openapi.yml' in ./example 22 | > yarn dev # watch the filesystem for changes and exec `yarn start` 23 | ``` 24 | 25 | ## Pull Request Process 26 | When your feature is ready to show to others, you should follow the below process to allow for an easy contribution flow. 27 | 28 | 1. Make your changes. 29 | 2. If applicable, Update the [README.md](README.md). 30 | 3. If applicable, Update the `./example/config.json` file. 31 | 4. If applicable, Update the `./example/openapi.yml` file. 32 | 5. Push to a new branch. 33 | 6. Create a [draft pull request](https://github.blog/2019-02-14-introducing-draft-pull-requests/). 34 | 7. When ready to merge, update pull request to 'Ready for Review'. 35 | 8. Wait for us, we try to comment on pull requests within a reasonable timeframe. We may suggest changes before accepting the pull request. 36 | 37 | ## Internationalization 38 | Any contributions should consider the existing i18n support available through the `$i18n()` method. Any new strings added to the project should use this method for string internationalization. When adding a new string you will need to edit the `./src/index.js` file, and the `./example/config.json` file. 39 | 40 | ## Configuration 41 | All configuration is handled through a `.json` file provided by the end-user. Provided throughout the project via the `$config.*` variable. When adding a new parameter you will need to edit the `./src/index.js` file, the `./src/environment.json` file, and the `./example/config.json` file. 42 | 43 | Care should be taken when altering the `environment.json` file - the intention if to provide enough structure for the documentation to display even if no env is available to inject (this happens when running `yarn dev-nuxt`). Ideally, this means purely structural elements such as `{}` and `[]` although this isn't a hard requirement. 44 | 45 | ## Versions 46 | For versioning we use the [Semantic Versioning](https://semver.org) versioning scheme. Versioning will be managed by the $ourOpenCode team and contributors should leave all versions at the version forked. 47 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020+ OurOpenCode 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

OpenDocumenter

2 | 3 |

4 | Version 5 | npm 6 | License 7 | Dependencies 8 | Issues 9 | Node Version 10 | Build Status 11 |

12 | 13 |

14 | 15 | Node Version 16 | 17 |

18 | 19 | OpenDocumenter is a automatic documentation generator for [OpenAPI v3](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md) schemas. Simply provide your schema file in JSON or YAML, then sit back and enjoy the documentation. 20 | 21 | Powered by [nuxt.js](https://nuxtjs.org/https://nuxtjs.org/) and [swagger-parser](https://github.com/swagger-api/swagger-parser). 22 | 23 | ## Example 24 | For a live preview of documentation generated with OpenDocumenter you can view our demo documentation "**[Quisque API](https://ouropencode.github.io/OpenDocumenter/)**". This example uses lorem-ipsum for all content to provide placeholder text used to demonstrate the form of a document without relying on meaningful content. 25 | 26 | ## Installation 27 | 28 | ```bash 29 | > npm install -g opendocumenter 30 | ``` 31 | or 32 | ```bash 33 | > yarn global add opendocumenter 34 | ``` 35 | 36 | ## Usage 37 | ``` 38 | > opendocumenter --help 39 | 40 | _____ ____ _ 41 | | |___ ___ ___| \ ___ ___ _ _ _____ ___ ___| |_ ___ ___ 42 | | | | . | -_| | | | . | _| | | | -_| | _| -_| _| 43 | |_____| _|___|_|_|____/|___|___|___|_|_|_|___|_|_|_| |___|_| 44 | |_| 45 | 46 | OpenDocumenter is a automatic documentation generator for OpenAPI v3 schemas. 47 | Simply provide your schema file in JSON or YAML, then sit back and enjoy the documentation. 48 | 49 | Powered by nuxt.js and swagger-parser. 50 | 51 | Usage: 52 | 53 | opendocumenter --schema= --output= 54 | 55 | Arguments: 56 | 57 | --schema= (required) The OpenAPI 3 format file to generate documentation from. 58 | --output= (required) The output destination directory. 59 | --config= A configuration file to load advanced options from. 60 | ``` 61 | 62 | ## Configuration 63 | OpenDocumenter can be configured using a `.json` file stored alongside your schema file. 64 | 65 | ### Merge From Directory 66 | The `mergeFromDirectory` parameter allows you to specify a directory that will be copied over the base template before building. This allows customization of any part of OpenDocumenter to suit your needs. 67 | 68 | ```json 69 | { 70 | "mergeFromDirectory": "./overrides" 71 | } 72 | ``` 73 | 74 | Any part of the OpenDocumenter vue source (see: [./src](./src)) can be overridden, just ensure to follow the same directory structure! 75 | ``` 76 | overrides 77 | |- assets 78 | | '- theme.less 79 | '- components 80 | |- DocHeader.vue 81 | '- DocEntry.vue 82 | ``` 83 | 84 | ### 'Generated Using' Footer 85 | By default, a small 'Generated Using' message is included on the footer of the generated documentation. Although we'd love you to keep it, you can disable this by setting the `disableGeneratedUsingFooter` parameter to true. 86 | 87 | ```json 88 | { 89 | "disableGeneratedUsingFooter": true 90 | } 91 | ``` 92 | 93 | ### Aborting on Invalid Schema 94 | OpenDocumenter is capable of generating documentation for OpenAPI schemas that don't match the OpenAPI Specification entirely. When generating we attempt to validate your schema, display any validation warnings, and then continue to generate. If you would like the generation to abort when a schema is invalid you can set the `abortOnInvalidSchema` parameter to true. 95 | 96 | ```json 97 | { 98 | "abortOnInvalidSchema": true 99 | } 100 | ``` 101 | 102 | ### Shields / Badges 103 | Various shields are included in the generated documentation header, such as the API version. Additional shields can be added using the `shields` parameter. Each shield is an object containing either the `url` key, or a combination of `left`, `right` and `color`. The `translate` parameter can be used (`left`, `right`, `both`) to run the text through the internationalization handler. An optional link can provided with the `href` parameter. All shields are generated using [shields.io](https://shields.io) unless a URL is provided. 104 | 105 | ```json 106 | { 107 | "shields": [ 108 | { "url": "https://img.shields.io/badge/test-1.2.3--test-blue" }, 109 | { "left": "test", "right": "1.2.3-test", "color": "blue" }, 110 | { 111 | "left": "test", 112 | "right": "1.2.3-test", 113 | "color": "blue", 114 | "href": "https://www.example.com" 115 | } 116 | ] 117 | } 118 | ``` 119 | 120 | ### Build: Modern Mode 121 | By default, the build-chain produces "Modern Mode" output, shipping native ES2015 code to modern browsers that support it, with auto fallback to a legacy bundle. This can be turned off by setting `vueModernMode` parameter, afterwards the build-chain produces "Legacy" output for older browser compatibility. 122 | 123 | ```json 124 | { 125 | "vueModernMode": false 126 | } 127 | ``` 128 | 129 | ### Build: Reporting 130 | You can control the reporting output from the build-chain with the `vueReport` parameter. This defaults to `none` and can be set to `json`, `html` or `both`. With this enabled the build-chain will output a report file describing the webpack bundle. 131 | 132 | ```json 133 | { 134 | "vueReport": "html" 135 | } 136 | ``` 137 | 138 | ### Internationalization 139 | Most of the documentation text is taken directly from the OpenAPI schema file, however, there are various strings throughout the project that cannot be stored within the schema file. All of these strings are customizable by editing the `i18n` parameter. 140 | ```json 141 | { 142 | "i18n": { 143 | "API_SDK_DOCUMENTATION": "API and SDK Documentation", 144 | "VERSION": "version", 145 | "NO_INDEPTH_DOCS_AVAILABLE_ENDPOINT": "No in-depth API documentation is available for this endpoint.", 146 | "NO_INDEPTH_DOCS_AVAILABLE_TAG": "No in-depth API documentation is available for this section.", 147 | "CLICK_TO_COPY": "click to copy", 148 | "COPIED": "copied", 149 | "REQUEST_BODY": "Request Body", 150 | "REQUEST_RESPONSES": "Request Responses", 151 | "DEFINITION": "Definition", 152 | "DEFINITIONS": "Definitions", 153 | "SERVER": "Server", 154 | "LANGUAGE": "Language", 155 | "GENERATED_USING": "Generated using OpenDocumenter by $ourOpenCode", 156 | "HAVE_ANY_QUESTIONS_CONTACT": "Have any questions? Please contact", 157 | "US": "us", 158 | "OR": "or", 159 | "VIA_EMAIL": "via email", 160 | "VIA_OUR_WEBSITE": "via our website", 161 | "TERMS_OF_SERVICE": "Terms of Service", 162 | "SEND_US_AN_EMAIL": "Send us an email", 163 | "VISIT_OUR_WEBSITE": "Visit our website", 164 | "EXTERNAL_DOCUMENTATION": "External Documentation", 165 | "DOCUMENTATION": "Documentation" 166 | } 167 | } 168 | ``` 169 | 170 | > There is a single end-user visible string that isn't configurable in this config. The "Loading Documentation..." text in the page title, briefly shown during page load, is only configurable using the "Merge From Directory" parameter. 171 | 172 | ## License 173 | Licensed under the MIT license. Please see [LICENSE](LICENSE) for more details. 174 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | Only the latest version is supported and actively maintained while the project is pre-v1. 5 | 6 | | Version | Supported | 7 | | ------- | ------------------ | 8 | | 0.5.x | :white_check_mark: | 9 | | < 0.5 | :x: | 10 | 11 | ## Reporting a vulnerability 12 | To report any vulnerabilities please create an issue, you can expect to get an update on a reported vulnerability 13 | within a reasonable timeframe. If you have the time you can additionally create a patch for the vulnerability and 14 | create a pull request - this may speed up the process. 15 | -------------------------------------------------------------------------------- /example/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "mergeFromDirectory": null, 3 | "disableGeneratedUsingFooter": false, 4 | "abortOnInvalidSchema": false, 5 | "vueModernMode": true, 6 | "vueReport": "none", 7 | "shields": [], 8 | "i18n": { 9 | "API_SDK_DOCUMENTATION": "API and SDK Documentation", 10 | "VERSION": "version", 11 | "NO_INDEPTH_DOCS_AVAILABLE_ENDPOINT": "No in-depth API documentation is available for this endpoint.", 12 | "NO_INDEPTH_DOCS_AVAILABLE_TAG": "No in-depth API documentation is available for this section.", 13 | "CLICK_TO_COPY": "click to copy", 14 | "COPIED": "copied", 15 | "REQUEST_BODY": "Request Body", 16 | "REQUEST_RESPONSES": "Request Responses", 17 | "DEFINITION": "Definition", 18 | "DEFINITIONS": "Definitions", 19 | "SERVER": "Server", 20 | "LANGUAGE": "Language", 21 | "GENERATED_USING": "Generated using OpenDocumenter by $ourOpenCode", 22 | "HAVE_ANY_QUESTIONS_CONTACT": "Have any questions? Please contact", 23 | "US": "us", 24 | "OR": "or", 25 | "VIA_EMAIL": "via email", 26 | "VIA_OUR_WEBSITE": "via our website", 27 | "TERMS_OF_SERVICE": "Terms of Service", 28 | "SEND_US_AN_EMAIL": "Send us an email", 29 | "VISIT_OUR_WEBSITE": "Visit our website", 30 | "EXTERNAL_DOCUMENTATION": "External Documentation", 31 | "DOCUMENTATION": "Documentation" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /example/example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ouropencode/OpenDocumenter/8389fdab1341f5f12d37ed61b730de4ca656234f/example/example.gif -------------------------------------------------------------------------------- /example/openapi.yml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | 3 | info: 4 | version: 1.0.0 5 | title: Quisque API 6 | description: | 7 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc nec dui nec nisl dapibus ullamcorper. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut tincidunt vestibulum venenatis. Phasellus semper vehicula consectetur. Fusce convallis nulla id auctor interdum. Curabitur ut aliquam metus. Ut ac euismod elit. 8 | 9 | Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer auctor eu diam a convallis. Phasellus at maximus dui. Quisque imperdiet elementum volutpat. Integer molestie nisi hendrerit sem semper volutpat. Morbi nec porta eros. Morbi porttitor risus id massa convallis, at pharetra diam dictum. 10 | 11 | Nulla a nisl ullamcorper, scelerisque augue eu, suscipit massa. Vivamus consequat, nibh id porttitor rutrum, magna erat egestas ipsum, ac posuere neque lectus eu nibh. Etiam nunc tellus, lacinia eget luctus sit amet, rutrum et erat. Donec massa magna, finibus id vehicula vel, maximus sit amet mauris. Mauris pellentesque est a ex malesuada, a ultricies neque vulputate. Vivamus hendrerit felis a gravida tempor. Sed congue efficitur tellus, sed scelerisque lorem ultricies ac. Nullam aliquet facilisis tincidunt. Ut aliquam ipsum ac sem rutrum, ut vulputate elit porttitor. 12 | contact: 13 | name: Quisque support 14 | email: support@example.com 15 | url: https://example.com 16 | termsOfService: https://example.com/terms 17 | license: 18 | name: MIT 19 | url: https://github.com/ouropencode/OpenDocumenter/blob/master/LICENSE 20 | identifier: MIT 21 | 22 | servers: 23 | - url: https://fermentum.example.com 24 | description: Fermentum API 25 | - url: https://vestibulum.example.com 26 | description: Vestibulum API 27 | 28 | externalDocs: 29 | url: https://doc.example.com 30 | description: Curabitur eget porta 31 | 32 | tags: 33 | - name: Consectetur 34 | description: | 35 | Pellentesque sit amet metus lacinia, venenatis erat quis, gravida massa. Suspendisse facilisis porttitor nunc, in consequat augue. Vestibulum tincidunt quam vel mi dapibus, non convallis lacus porttitor. Etiam id purus nec diam vehicula feugiat ut sit amet metus. Nunc mattis dolor nec tristique ullamcorper. Integer malesuada in nisl eu consequat. Donec eu quam pellentesque, sodales neque ut, mattis ipsum. Nullam sit amet varius est. Duis et commodo leo, non suscipit eros. Nam tempor ultricies libero in bibendum. Curabitur eu bibendum erat. Morbi odio ipsum, imperdiet quis luctus at, venenatis sit amet nibh. 36 | - name: Posuere 37 | description: | 38 | Cras condimentum pulvinar venenatis. Nam sed mi in odio ornare imperdiet et eget dolor. Curabitur eros lorem, eleifend id finibus et, euismod a felis. Aliquam lobortis consectetur nisl in faucibus. In euismod consequat justo, eu auctor nunc semper in. Cras non tristique sem. Duis nec luctus augue. Etiam leo leo, malesuada in pellentesque rutrum, tempus ut sem. Duis neque velit, aliquet at malesuada congue, ultricies vehicula lacus. 39 | - name: Egestas 40 | description: | 41 | Phasellus eu felis hendrerit, volutpat odio sit amet, luctus augue. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean quis tempor lectus. Nam eu enim malesuada ex tristique congue id id orci. Duis pretium nisi a arcu efficitur, quis faucibus justo suscipit. Mauris consequat, eros eget cursus scelerisque, sapien nibh sodales elit, ut porttitor nisi lacus nec ante. Suspendisse libero leo, pharetra vel porta nec, gravida eu magna. Vivamus sit amet nisl elementum, euismod justo quis, volutpat nunc. 42 | - name: Convallis 43 | description: | 44 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam pulvinar felis eget leo porta ultricies. Integer fermentum diam at ex commodo molestie. Nulla facilisi. Donec egestas metus sodales ante vulputate dignissim. Etiam ut sollicitudin ligula. Ut molestie leo nec nisl elementum mattis. Vestibulum sollicitudin tellus ut felis aliquam, a pulvinar ipsum tempus. Etiam ac faucibus arcu. Donec non lectus faucibus odio pulvinar tempus. Duis convallis dictum eros ut varius. 45 | 46 | paths: 47 | /consectetur/venenatis: 48 | get: 49 | summary: Nullam vehicula enim 50 | description: | 51 | Nullam convallis leo tortor, at vestibulum libero hendrerit at. Vivamus posuere, velit eu consectetur commodo, lectus lorem dictum felis, at commodo sem nulla sed eros. Etiam in ligula non ligula condimentum rhoncus. Phasellus aliquam lacus nisl, non hendrerit magna accumsan et. Phasellus lacinia est quis faucibus fermentum. Nulla ac sem at massa vehicula porta ac laoreet ligula. Suspendisse lobortis molestie tincidunt. Sed faucibus neque at ex feugiat, ac consequat augue aliquam. 52 | tags: 53 | - Consectetur 54 | requestBody: 55 | required: true 56 | content: 57 | application/json: 58 | example: 59 | lorem: loremipsum 60 | ipsum: ipsumlorem 61 | schema: 62 | $ref: '#/components/schemas/LoremIpsum' 63 | responses: 64 | '200': 65 | description: Vestibulum vitae tempor lorem 66 | content: 67 | application/json: 68 | example: 69 | lorem: loremipsum 70 | ipsum: ipsumlorem 71 | schema: 72 | $ref: '#/components/schemas/LoremIpsum' 73 | '404': 74 | description: Nullam blandit faucibus vehicula 75 | content: 76 | application/json: 77 | example: 78 | error: iaculis arcu vitae urna accumsan 79 | schema: 80 | $ref: '#/components/schemas/Error' 81 | '409': 82 | description: Praesent vulputate vehicula urna 83 | content: 84 | application/json: 85 | example: 86 | error: scelerisque condimentum eleifend sed 87 | schema: 88 | $ref: '#/components/schemas/Error' 89 | '418': 90 | description: Nunc enim arcu mattis at rutrum 91 | content: 92 | application/json: 93 | example: 94 | error: arcu diam laoreet nulla in vestibulum 95 | schema: 96 | $ref: '#/components/schemas/Error' 97 | 98 | /consectetur/vitae: 99 | post: 100 | summary: Phasellus nec vestibulum 101 | description: | 102 | Etiam aliquam quam ac porta venenatis. Nulla facilisi. Curabitur mollis finibus mi, et congue nisi tincidunt non. Sed eleifend justo et turpis mollis sodales. In et tortor in augue mattis congue. Vestibulum imperdiet imperdiet auctor. Quisque sit amet consequat justo, in imperdiet sapien. Integer hendrerit vehicula lacus, eget vestibulum nisi consequat eget. Morbi dapibus neque aliquam mauris lacinia venenatis. Sed facilisis libero lacus, in egestas massa scelerisque molestie. Nunc tincidunt gravida sem in gravida. Duis facilisis ultrices massa, a malesuada purus ultrices nec. Cras ac urna at nulla interdum efficitur. Sed eu arcu at ante consectetur imperdiet ut vel sem. 103 | tags: 104 | - Consectetur 105 | requestBody: 106 | required: true 107 | content: 108 | application/json: 109 | example: 110 | lorem: loremipsum 111 | ipsum: ipsumlorem 112 | schema: 113 | $ref: '#/components/schemas/LoremIpsum' 114 | responses: 115 | '200': 116 | description: Vestibulum vitae tempor lorem 117 | content: 118 | application/json: 119 | example: 120 | lorem: loremipsum 121 | ipsum: ipsumlorem 122 | schema: 123 | $ref: '#/components/schemas/LoremIpsum' 124 | '409': 125 | description: Nullam blandit faucibus vehicula 126 | content: 127 | application/json: 128 | example: 129 | error: curabitur sed lectus ac felis 130 | schema: 131 | $ref: '#/components/schemas/Error' 132 | 133 | /consectetur/pulvinar: 134 | put: 135 | summary: In tincidunt nec nisl sed ornare 136 | description: | 137 | Nullam blandit faucibus vehicula. Cras ut lectus ornare sapien euismod mollis id pharetra nulla. Aliquam a libero faucibus, posuere mi ac, blandit eros. Maecenas aliquet ultricies urna, nec laoreet lacus fermentum ut. Sed ac magna sed risus facilisis accumsan. Fusce scelerisque erat quis vestibulum varius. Quisque pretium interdum leo, a tristique turpis maximus eu. 138 | tags: 139 | - Consectetur 140 | requestBody: 141 | required: true 142 | content: 143 | application/json: 144 | example: 145 | lorem: loremipsum 146 | ipsum: ipsumlorem 147 | schema: 148 | $ref: '#/components/schemas/LoremIpsum' 149 | responses: 150 | '200': 151 | description: Nunc malesuada scelerisque sem 152 | content: 153 | application/json: 154 | example: 155 | lorem: loremipsum 156 | ipsum: ipsumlorem 157 | schema: 158 | $ref: '#/components/schemas/LoremIpsum' 159 | '409': 160 | description: Integer suscipit lacus lorem 161 | content: 162 | application/json: 163 | example: 164 | error: curabitur sed lectus ac felis 165 | schema: 166 | $ref: '#/components/schemas/Error' 167 | 168 | /consectetur/condimentum/dolor: 169 | delete: 170 | summary: Cras sed augue varius 171 | description: | 172 | Fringilla elit eget, tincidunt odio. Curabitur ultrices eget purus ut rutrum. Ut vulputate pulvinar finibus. Quisque sollicitudin velit non dui suscipit dapibus. Duis non porta nisi. Mauris et magna vitae felis imperdiet finibus. Fusce accumsan sed urna vitae vehicula. Nulla id fermentum orci. Aenean egestas ac libero at sodales. Nam in aliquam orci. Cras volutpat placerat arcu, in gravida magna pharetra at. 173 | tags: 174 | - Consectetur 175 | responses: 176 | '200': 177 | description: In ut facilisis nulla 178 | content: 179 | application/json: 180 | example: 181 | lorem: loremipsum 182 | ipsum: ipsumlorem 183 | schema: 184 | $ref: '#/components/schemas/LoremIpsum' 185 | '404': 186 | description: Aenean imperdiet nisi ac mauris 187 | content: 188 | application/json: 189 | example: 190 | error: integer leo massa 191 | schema: 192 | $ref: '#/components/schemas/Error' 193 | 194 | /posuere/mollis: 195 | get: 196 | summary: Praesent consectetur pharetra 197 | description: | 198 | Aenean a justo vel erat condimentum sollicitudin at ut velit. Mauris volutpat lectus lorem, sed rutrum felis cursus nec. Praesent dapibus, augue vitae varius viverra, ante orci gravida risus, eu aliquet ex est non metus. In ultricies sem ac dictum feugiat. Sed accumsan pretium justo vitae rutrum. Nunc in efficitur lacus. Proin sodales condimentum blandit. Maecenas consectetur accumsan vestibulum. 199 | tags: 200 | - Posuere 201 | responses: 202 | '200': 203 | description: Sed a porttitor risus id dapibus purus 204 | content: 205 | application/json: 206 | example: 207 | lorem: loremipsum 208 | ipsum: ipsumlorem 209 | schema: 210 | $ref: '#/components/schemas/LoremIpsum' 211 | 212 | /posuere/imperdiet: 213 | get: 214 | summary: Mauris volutpat lectus 215 | description: | 216 | This endpoint provides a list of all the teams the logged in user has access to.Praesent dapibus, augue vitae varius viverra, ante orci gravida risus, eu aliquet ex est non metus. In ultricies sem ac dictum feugiat. Sed accumsan pretium justo vitae rutrum. Nunc in efficitur lacus. Proin sodales condimentum blandit. Maecenas consectetur accumsan vestibulum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean facilisis lectus et rutrum sagittis. 217 | tags: 218 | - Posuere 219 | responses: 220 | '200': 221 | description: Sed a porttitor risus id dapibus purus 222 | content: 223 | application/json: 224 | example: 225 | lorem: loremipsum 226 | ipsum: ipsumlorem 227 | schema: 228 | $ref: '#/components/schemas/LoremIpsum' 229 | 230 | /egestas/{{lorem}}/aliquam: 231 | get: 232 | summary: Duis ac egestas augue 233 | description: | 234 | Pellentesque aliquet pulvinar tellus eu varius. Quisque ultrices sit amet augue tristique lobortis. Mauris id libero a nulla dignissim sodales. Vestibulum bibendum, nunc eu finibus imperdiet, magna libero ultrices dolor, eu ullamcorper urna ipsum fringilla libero. In venenatis odio a velit vestibulum fringilla. Phasellus vulputate bibendum porta. Duis mattis quis tellus nec posuere. Nunc rutrum pharetra vulputate. Praesent et metus ultrices, tristique mauris id, consectetur erat. 235 | tags: 236 | - Egestas 237 | responses: 238 | '200': 239 | description: Sed a porttitor risus id dapibus purus 240 | content: 241 | application/json: 242 | example: 243 | lorem: loremipsum 244 | ipsum: ipsumlorem 245 | schema: 246 | $ref: '#/components/schemas/LoremIpsum' 247 | '403': 248 | description: Nulla tincidunt dapibus nisi 249 | content: 250 | application/json: 251 | example: 252 | error: sit amet semper nibh 253 | schema: 254 | $ref: '#/components/schemas/Error' 255 | 256 | /convallis: 257 | post: 258 | summary: Vivamus eget leo eget 259 | description: | 260 | Etiam pellentesque ligula urna, quis feugiat ante rhoncus nec. Praesent egestas, mi a venenatis rutrum, nisl eros lacinia libero, eget consectetur lectus enim ac risus. Vivamus at eleifend justo. Mauris cursus orci lacinia vehicula vestibulum. Integer accumsan accumsan felis vel ultricies. Phasellus gravida imperdiet urna, et varius mi pharetra eu. Interdum et malesuada fames ac ante ipsum primis in faucibus. Morbi ut nunc lacinia, euismod elit non, fermentum est. Aliquam sollicitudin sodales velit quis ultricies. Nulla sollicitudin posuere magna, eu interdum ipsum aliquet in. Vestibulum cursus at lorem non faucibus. Aliquam erat volutpat. Proin sit amet arcu ullamcorper, elementum magna a, dictum leo. Nunc lobortis cursus lacus a condimentum. 261 | tags: 262 | - Convallis 263 | responses: 264 | '200': 265 | description: Quisque ultrices sit amet 266 | content: 267 | application/json: 268 | example: 269 | lorem: loremipsum 270 | ipsum: ipsumlorem 271 | schema: 272 | $ref: '#/components/schemas/LoremIpsum' 273 | 274 | components: 275 | schemas: 276 | Error: 277 | type: object 278 | required: 279 | - error 280 | properties: 281 | error: 282 | type: string 283 | 284 | LoremIpsum: 285 | type: object 286 | required: 287 | - lorem 288 | - ipsum 289 | properties: 290 | lorem: 291 | type: string 292 | ipsum: 293 | type: string 294 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "opendocumenter", 3 | "version": "0.5.0", 4 | "author": "$ourOpenCode", 5 | "scripts": { 6 | "start": "npm-run-all dev-generate dev-serve", 7 | "dev": "nodemon -e js,vue,less,sass,css,html,json,yml --ignore tmp/ --ignore example/docs/ --exec npm start", 8 | "dev-generate": "node ./src-engine/cli.js --schema=./example/openapi.yml --output=./example/docs --config=./example/config.json", 9 | "dev-serve": "serve ./example/docs", 10 | "vue": "vue-cli-service build" 11 | }, 12 | "main": "./src-engine/index.js", 13 | "dependencies": { 14 | "@apidevtools/swagger-parser": "^10.0.1", 15 | "@fortawesome/fontawesome-svg-core": "^1.2.30", 16 | "@fortawesome/free-solid-svg-icons": "^5.14.0", 17 | "@fortawesome/vue-fontawesome": "^0.1.10", 18 | "@vue/cli-plugin-babel": "~4.5.3", 19 | "@vue/cli-plugin-vuex": "~4.5.3", 20 | "@vue/cli-service": "~4.5.2", 21 | "babel-plugin-root-import": "^6.5.0", 22 | "babel-runtime": "^6.26.0", 23 | "colors": "^1.4.0", 24 | "core-js": "^3.6.5", 25 | "exec-sh": "^0.3.4", 26 | "highlight.js": "9.18.3", 27 | "less": "^3.0.4", 28 | "less-loader": "^6.2.0", 29 | "markdown-it-vue": "^1.1.3", 30 | "minimist": "^1.2.5", 31 | "npm-run-all": "^4.1.5", 32 | "recursive-copy": "^2.0.10", 33 | "uuid": "^8.3.0", 34 | "vue": "^2.6.11", 35 | "vue-cli-plugin-webfontloader": "~0.1.1", 36 | "vue-head": "^2.2.0", 37 | "vue-highlight.js": "^3.1.0", 38 | "vue-template-compiler": "^2.6.11", 39 | "vuex": "^3.4.0", 40 | "webfontloader": "^1.6.28" 41 | }, 42 | "devDependencies": { 43 | "nodemon": "^2.0.4", 44 | "serve": "^11.3.2" 45 | }, 46 | "bin": { 47 | "opendocumenter": "src/cli.js" 48 | }, 49 | "engines": { 50 | "node": ">=14.6" 51 | }, 52 | "license": "MIT" 53 | } 54 | -------------------------------------------------------------------------------- /src-engine/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const Core = require('./index.js') 4 | const minimist = require("minimist") 5 | const colors = require("colors") 6 | const pkg = require('../package.json') 7 | 8 | const argv = minimist(process.argv.slice(2)) 9 | 10 | const start = async () => { 11 | const docgen = new Core(argv.schema, argv.output, argv.config) 12 | try { 13 | await docgen.prepare() 14 | await docgen.generate() 15 | await docgen.finish() 16 | } catch(e) { 17 | let message = e.message 18 | .toString() 19 | .replace(/Swagger/gi, "OpenAPI") 20 | .split('\n')[0] 21 | 22 | docgen._displayBanner("Aborting due to error: " + message, "err") 23 | } finally { 24 | await docgen.finalize() 25 | } 26 | } 27 | 28 | console.log(` 29 | _____ ____ _ 30 | | |___ ___ ___| \\ ___ ___ _ _ _____ ___ ___| |_ ___ ___ 31 | | | | . | -_| | | | . | _| | | | -_| | _| -_| _| 32 | |_____| _|___|_|_|____/|___|___|___|_|_|_|___|_|_|_| |___|_| 33 | |_| ${pkg.version} 34 | `.green) 35 | 36 | if(!argv.schema || !argv.output) { 37 | console.log(` 38 | OpenDocumenter is a automatic documentation generator for OpenAPI v3 schemas. 39 | Simply provide your schema file in JSON or YAML, then sit back and enjoy the documentation. 40 | 41 | Powered by nuxt.js and swagger-parser. 42 | 43 | Usage: 44 | 45 | opendocumenter --schema= --output= 46 | 47 | Arguments: 48 | 49 | --schema= (required) The OpenAPI v3 format file to generate documentation from. 50 | --output= (required) The output destination directory. 51 | --config= A configuration file to load advanced options from.`) 52 | } else { 53 | start() 54 | } 55 | -------------------------------------------------------------------------------- /src-engine/index.js: -------------------------------------------------------------------------------- 1 | const SwaggerParser = require("@apidevtools/swagger-parser") 2 | const copy = require("recursive-copy") 3 | const path = require("path") 4 | const fs = require("fs") 5 | const uuid = require("uuid") 6 | const colors = require("colors") 7 | const execShPromise = require("exec-sh").promise; 8 | 9 | module.exports = class Core { 10 | 11 | constructor(schema, outputDir, configFile) { 12 | this._schema = path.resolve(schema) 13 | this._outputDir = path.resolve(outputDir) 14 | this._configFile = configFile ? path.resolve(configFile) : undefined 15 | 16 | let config = {} 17 | if(this._configFile) 18 | config = JSON.parse(fs.readFileSync(this._configFile)) 19 | 20 | this._config = { 21 | "mergeFromDirectory": null, 22 | "disableGeneratedUsingFooter": false, 23 | "abortOnInvalidSchema": false, 24 | "vueModernMode": true, 25 | "vueReport": "none", 26 | "shields": [], 27 | "i18n": {}, 28 | ...config 29 | } 30 | 31 | this._config.i18n = { 32 | "API_SDK_DOCUMENTATION": "API and SDK Documentation", 33 | "VERSION": "version", 34 | "NO_INDEPTH_DOCS_AVAILABLE_ENDPOINT": "No in-depth API documentation is available for this endpoint.", 35 | "NO_INDEPTH_DOCS_AVAILABLE_TAG": "No in-depth API documentation is available for this section.", 36 | "CLICK_TO_COPY": "click to copy", 37 | "COPIED": "copied", 38 | "REQUEST_BODY": "Request Body", 39 | "REQUEST_RESPONSES": "Request Responses", 40 | "DEFINITION": "Definition", 41 | "DEFINITIONS": "Definitions", 42 | "SERVER": "Server", 43 | "LANGUAGE": "Language", 44 | "GENERATED_USING": "Generated using OpenDocumenter by $ourOpenCode", 45 | "HAVE_ANY_QUESTIONS_CONTACT": "Have any questions? Please contact", 46 | "US": "us", 47 | "OR": "or", 48 | "VIA_EMAIL": "via email", 49 | "VIA_OUR_WEBSITE": "via our website", 50 | "EXTERNAL_DOCUMENTATION": "External Documentation", 51 | "DOCUMENTATION": "Documentation", 52 | ...config.i18n 53 | } 54 | 55 | if(this._config.mergeFromDirectory != null) 56 | this._mrgPath = path.resolve(this._config.mergeFromDirectory) 57 | 58 | this._srcPath = path.join(__dirname, '..', 'src') 59 | this._tmpPath = path.join(__dirname, '..', 'tmp', uuid.v4(), 'src') 60 | this._cwd = process.cwd() 61 | } 62 | 63 | async prepare() { 64 | this._api = await this.loadAPI(this._schema) 65 | 66 | this._displayInfo() 67 | 68 | await copy(this._srcPath, this._tmpPath, { dot: true }) 69 | 70 | await copy( 71 | path.join(this._srcPath, "vue.config.js"), 72 | path.join(this._tmpPath, "..", "vue.config.js") 73 | ) 74 | 75 | await copy( 76 | "package.json", 77 | path.join(this._tmpPath, "..", "package.json") 78 | ) 79 | 80 | if(this._config.mergeFromDirectory != null) 81 | await copy(this._mrgPath, this._tmpPath, { overwrite: true, dot: true }) 82 | 83 | fs.writeFileSync(path.join(this._tmpPath, "environment.json"), JSON.stringify({ 84 | ...this._config, 85 | api: this._api, 86 | })) 87 | } 88 | 89 | async finish() { 90 | fs.rmdirSync(this._outputDir, { recursive: true }); 91 | 92 | const results = await copy(path.join(this._tmpPath, "..", "dist"), this._outputDir) 93 | 94 | this._displayBanner(`OpenDocumenter finished! ${results.length} files created.`) 95 | } 96 | 97 | async finalize() { 98 | process.chdir(this._cwd) 99 | fs.rmdirSync(this._tmpPath, { recursive: true }) 100 | } 101 | 102 | async generate(file) { 103 | process.chdir(path.resolve(this._tmpPath, '..')) 104 | 105 | let flags = [] 106 | 107 | if(this._config.vueModernMode == true) 108 | flags.push("--modern") 109 | 110 | if(this._config.vueReport == "html" || this._config.vueReport == "both") 111 | flags.push("--report") 112 | 113 | if(this._config.vueReport == "json" || this._config.vueReport == "both") 114 | flags.push("--report-json") 115 | 116 | flags = flags.join(' ') 117 | 118 | let flagStr = flags.length > 0 ? `with flags: ${flags}` : '' 119 | this._displayBanner(`Starting build ${flagStr}`) 120 | await execShPromise(`vue-cli-service build ${flags}`) 121 | } 122 | 123 | async loadAPI(file) { 124 | let api = await SwaggerParser.parse(file) 125 | 126 | try { 127 | api = await SwaggerParser.validate(file) 128 | } catch(e) { 129 | if(e.name != "SyntaxError") throw e 130 | this._displaySchemaSyntaxError(e.details) 131 | if(this._config.abortOnInvalidSchema == true) 132 | throw e 133 | } 134 | 135 | return api 136 | } 137 | 138 | async _displayBanner(text, type = "default") { 139 | let prefix = "---"; 140 | let color = "green" 141 | 142 | if(type == "warn" || type == "warning") { 143 | prefix = "???" 144 | color = "yellow" 145 | } 146 | 147 | if(type == "err" || type == "error") { 148 | prefix = "!!!" 149 | color = "red" 150 | } 151 | 152 | console.log() 153 | console.log(` ${prefix} ${text}`.bold[color]) 154 | console.log() 155 | } 156 | 157 | async _displayInfo() { 158 | this._displayBanner("API Details") 159 | console.log(` Name: ${this._api.info.title}`) 160 | console.log(` Version: ${this._api.info.version}`) 161 | console.log(` Schema: ${this._schema}`) 162 | console.log(` Output: ${this._outputDir}`) 163 | if(this._configFile) 164 | console.log(` Config: ${this._configFile}`) 165 | } 166 | 167 | async _displaySchemaSyntaxError(details) { 168 | this._displayBanner("OpenAPI schema validation failed", "warn") 169 | details.forEach((issue, idx) => { 170 | console.log(" - #/" + issue.path.join('/')) 171 | console.log(" " + issue.message) 172 | if(idx != details.length - 1) 173 | console.log() 174 | }) 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@vue/cli-plugin-babel/preset" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /src/assets/highlighter-theme-dark.less: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull 4 | 5 | */ 6 | 7 | .hljs { 8 | display: block; 9 | overflow-x: auto; 10 | padding: 0.5em; 11 | background: #002b36; 12 | color: #839496; 13 | } 14 | 15 | .hljs-comment, 16 | .hljs-quote { 17 | color: #586e75; 18 | } 19 | 20 | /* Solarized Green */ 21 | .hljs-keyword, 22 | .hljs-selector-tag, 23 | .hljs-addition { 24 | color: #859900; 25 | } 26 | 27 | /* Solarized Cyan */ 28 | .hljs-number, 29 | .hljs-string, 30 | .hljs-meta .hljs-meta-string, 31 | .hljs-literal, 32 | .hljs-doctag, 33 | .hljs-regexp { 34 | color: #2aa198; 35 | } 36 | 37 | /* Solarized Blue */ 38 | .hljs-title, 39 | .hljs-section, 40 | .hljs-name, 41 | .hljs-selector-id, 42 | .hljs-selector-class { 43 | color: #268bd2; 44 | } 45 | 46 | /* Solarized Yellow */ 47 | .hljs-attribute, 48 | .hljs-attr, 49 | .hljs-variable, 50 | .hljs-template-variable, 51 | .hljs-class .hljs-title, 52 | .hljs-type { 53 | color: #b58900; 54 | } 55 | 56 | /* Solarized Orange */ 57 | .hljs-symbol, 58 | .hljs-bullet, 59 | .hljs-subst, 60 | .hljs-meta, 61 | .hljs-meta .hljs-keyword, 62 | .hljs-selector-attr, 63 | .hljs-selector-pseudo, 64 | .hljs-link { 65 | color: #cb4b16; 66 | } 67 | 68 | /* Solarized Red */ 69 | .hljs-built_in, 70 | .hljs-deletion { 71 | color: #dc322f; 72 | } 73 | 74 | .hljs-formula { 75 | background: #073642; 76 | } 77 | 78 | .hljs-emphasis { 79 | font-style: italic; 80 | } 81 | 82 | .hljs-strong { 83 | font-weight: bold; 84 | } 85 | -------------------------------------------------------------------------------- /src/assets/highlighter-theme-light.less: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull 4 | 5 | */ 6 | 7 | .hljs { 8 | display: block; 9 | overflow-x: auto; 10 | padding: 0.5em; 11 | background: #fdf6e3; 12 | color: #657b83; 13 | } 14 | 15 | .hljs-comment, 16 | .hljs-quote { 17 | color: #93a1a1; 18 | } 19 | 20 | /* Solarized Green */ 21 | .hljs-keyword, 22 | .hljs-selector-tag, 23 | .hljs-addition { 24 | color: #859900; 25 | } 26 | 27 | /* Solarized Cyan */ 28 | .hljs-number, 29 | .hljs-string, 30 | .hljs-meta .hljs-meta-string, 31 | .hljs-literal, 32 | .hljs-doctag, 33 | .hljs-regexp { 34 | color: #2aa198; 35 | } 36 | 37 | /* Solarized Blue */ 38 | .hljs-title, 39 | .hljs-section, 40 | .hljs-name, 41 | .hljs-selector-id, 42 | .hljs-selector-class { 43 | color: #268bd2; 44 | } 45 | 46 | /* Solarized Yellow */ 47 | .hljs-attribute, 48 | .hljs-attr, 49 | .hljs-variable, 50 | .hljs-template-variable, 51 | .hljs-class .hljs-title, 52 | .hljs-type { 53 | color: #b58900; 54 | } 55 | 56 | /* Solarized Orange */ 57 | .hljs-symbol, 58 | .hljs-bullet, 59 | .hljs-subst, 60 | .hljs-meta, 61 | .hljs-meta .hljs-keyword, 62 | .hljs-selector-attr, 63 | .hljs-selector-pseudo, 64 | .hljs-link { 65 | color: #cb4b16; 66 | } 67 | 68 | /* Solarized Red */ 69 | .hljs-built_in, 70 | .hljs-deletion { 71 | color: #dc322f; 72 | } 73 | 74 | .hljs-formula { 75 | background: #eee8d5; 76 | } 77 | 78 | .hljs-emphasis { 79 | font-style: italic; 80 | } 81 | 82 | .hljs-strong { 83 | font-weight: bold; 84 | } 85 | -------------------------------------------------------------------------------- /src/assets/theme.less: -------------------------------------------------------------------------------- 1 | /** 2 | * Colors 3 | */ 4 | 5 | @color-body-background: white; 6 | @color-body-text: black; 7 | 8 | @color-body-link: #292b36; 9 | @color-body-link-hover: #8b8fa7; 10 | 11 | @color-sidebar-background: #f5f5f5; 12 | @color-sidebar-link: #16171d; 13 | @color-sidebar-link-hover: #8b8fa7; 14 | 15 | @color-split-background: #16171d; 16 | @color-split-text: white; 17 | 18 | @color-code-background: white; 19 | @color-code-text: black; 20 | @color-code-dark-background: #292b36; 21 | @color-code-dark-text: white; 22 | 23 | @color-toolbox-text: white; 24 | @color-toolbox-background: #292b36; 25 | @color-toolbox-active-background: #292b36; 26 | @color-toolbox-dropdown-background: white; 27 | @color-toolbox-link: #16171d; 28 | @color-toolbox-link-hover: #8b8fa7; 29 | 30 | @color-header-version: #808080; 31 | @color-header-intro: #808080; 32 | 33 | @color-help-text: #292b36; 34 | 35 | @color-endpoint-text: white; 36 | @color-endpoint-unk: #3387CC; 37 | @color-endpoint-get: green; 38 | @color-endpoint-put: #e5c500; 39 | @color-endpoint-post: #4070ec; 40 | @color-endpoint-delete: #ff4e3f; 41 | 42 | /** 43 | * Fonts 44 | */ 45 | 46 | @primary-font: Montserrat, sans-serif; 47 | @secondary-font: Roboto, sans-serif; 48 | @monospace-font: "Source Code Pro", monospace; 49 | 50 | .theme-font-title() { 51 | font-family: @primary-font; 52 | font-weight: 400; 53 | font-size: 2.5em; 54 | } 55 | 56 | .theme-font-subtitle() { 57 | font-family: @primary-font; 58 | font-weight: 400; 59 | font-size: 1.8em; 60 | } 61 | 62 | .theme-font-summary() { 63 | font-family: @primary-font; 64 | font-weight: 400; 65 | font-size: 1.2em; 66 | } 67 | 68 | .theme-font-tag-title() { 69 | font-family: @primary-font; 70 | font-weight: 400; 71 | font-size: 1.6em; 72 | } 73 | 74 | .theme-font-entry-title() { 75 | font-family: @primary-font; 76 | font-weight: 400; 77 | font-size: 1.2em; 78 | } 79 | 80 | .theme-font-entry-subtitle() { 81 | font-family: @primary-font; 82 | font-weight: 400; 83 | font-size: 1em; 84 | } 85 | 86 | .theme-font-body() { 87 | font-family: @secondary-font; 88 | font-weight: 400; 89 | font-size: 1em; 90 | } 91 | 92 | .theme-font-code() { 93 | font-family: @monospace-font; 94 | font-weight: 400; 95 | font-size: 1em; 96 | } 97 | -------------------------------------------------------------------------------- /src/assets/utils.less: -------------------------------------------------------------------------------- 1 | .truncated() { 2 | white-space: nowrap; 3 | overflow: hidden; 4 | text-overflow: ellipsis; 5 | } 6 | -------------------------------------------------------------------------------- /src/components/ContactTeam.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 30 | 31 | 40 | -------------------------------------------------------------------------------- /src/components/DocEntry.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 59 | 60 | 78 | -------------------------------------------------------------------------------- /src/components/DocEntryTitle.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 20 | 21 | 46 | -------------------------------------------------------------------------------- /src/components/DocFooter.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 21 | 22 | 58 | -------------------------------------------------------------------------------- /src/components/DocHeader.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 63 | 64 | 95 | -------------------------------------------------------------------------------- /src/components/DocLayout.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 34 | 35 | 64 | -------------------------------------------------------------------------------- /src/components/DocSidebar.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 35 | 36 | 43 | -------------------------------------------------------------------------------- /src/components/DocSidebarEntry.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 24 | 25 | 59 | -------------------------------------------------------------------------------- /src/components/DocSidebarTag.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 41 | 42 | 64 | -------------------------------------------------------------------------------- /src/components/DocTag.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 30 | 31 | 43 | -------------------------------------------------------------------------------- /src/components/DocToolbox.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 76 | 77 | 91 | -------------------------------------------------------------------------------- /src/components/DocToolboxDropdown.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 53 | 54 | 116 | -------------------------------------------------------------------------------- /src/components/DocToolboxIcon.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 27 | -------------------------------------------------------------------------------- /src/components/Endpoint.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 50 | 51 | 113 | -------------------------------------------------------------------------------- /src/components/Example.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 16 | 17 | 37 | -------------------------------------------------------------------------------- /src/components/Highlight.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 34 | 35 | 65 | -------------------------------------------------------------------------------- /src/components/Markdown.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 14 | 15 | 36 | -------------------------------------------------------------------------------- /src/components/ReqBody.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 16 | 23 | -------------------------------------------------------------------------------- /src/components/ReqResponse.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 22 | 23 | 45 | -------------------------------------------------------------------------------- /src/components/Shield.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 31 | 32 | 38 | -------------------------------------------------------------------------------- /src/components/SplitSection.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | 18 | 41 | -------------------------------------------------------------------------------- /src/components/StatusCode.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 62 | 63 | 74 | -------------------------------------------------------------------------------- /src/environment.json: -------------------------------------------------------------------------------- 1 | { 2 | "api": { 3 | "openapi": "0.0.0", 4 | "info": {}, 5 | "servers": [], 6 | "paths": {}, 7 | "components": {}, 8 | "security": [], 9 | "tags": [], 10 | "externalDocs": [] 11 | }, 12 | "i18n": {} 13 | } 14 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Loading Documentation... 8 | 11 | 12 | 13 |
14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | import './plugins/codegen' 4 | import envInject from './plugins/envInject' 5 | 6 | Vue.config.productionTip = false 7 | 8 | // Setup webfonts 9 | import WebFont from 'webfontloader' 10 | WebFont.load({ 11 | google: { 12 | families: ["Montserrat:200,400", "Roboto:400,700", "Source Code Pro:400"] 13 | } 14 | }) 15 | 16 | // Setup vue-head 17 | import VueHead from 'vue-head' 18 | Vue.use(VueHead) 19 | 20 | // Setup vue-highlight.js 21 | import VueHighlightJS from 'vue-highlight.js' 22 | import 'vue-highlight.js/lib/allLanguages' 23 | import 'highlight.js/styles/default.css' 24 | Vue.use(VueHighlightJS) 25 | 26 | // Setup fontawesome 27 | import { library } from '@fortawesome/fontawesome-svg-core' 28 | import { faChevronDown, faCog, faHeart, faQuestionCircle } from '@fortawesome/free-solid-svg-icons' 29 | import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' 30 | library.add(faChevronDown) 31 | library.add(faCog) 32 | library.add(faHeart) 33 | library.add(faQuestionCircle) 34 | Vue.component('font-awesome-icon', FontAwesomeIcon) 35 | 36 | // Smooth scrolling helper function 37 | Vue.prototype.$smoothScroll = id => { 38 | history.pushState({}, '', "#" + id) 39 | if(id == "") id = "top" 40 | let ele = document.getElementById(id) 41 | if(!ele) 42 | return console.log("cannot scroll to missing ele", id) 43 | document.getElementById(id).scrollIntoView({ 44 | behavior: "smooth" 45 | }) 46 | } 47 | 48 | // Path hashing function 49 | Vue.prototype.$hashPath = (path, method) => { 50 | path = path.replace(/[^a-zA-Z0-9_-]+/g, "-") 51 | let hash = `${method}-${path}` 52 | return hash.replace(/-+$/g, "").toLowerCase() 53 | } 54 | 55 | // Initialise Vue 56 | import store from './store' 57 | import index from './index.vue' 58 | ;(async () => { 59 | await envInject() 60 | new Vue({ 61 | store: store, 62 | render: h => h(index) 63 | }).$mount('#app') 64 | })() 65 | -------------------------------------------------------------------------------- /src/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 34 | 35 | 61 | -------------------------------------------------------------------------------- /src/plugins/codegen.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import store from '@/store' 3 | 4 | const generators = { 5 | 6 | curl(server, def) { 7 | let cmd = `curl -X ${def.method.toUpperCase()} "${server}${def.path}"` 8 | 9 | if (def.requestBody) { 10 | if(def.requestBody.content['application/json']) { 11 | let example = JSON.stringify(def.requestBody.content['application/json'].example || {}); 12 | cmd += ` -H "Content-Type: application/json" --data ${example}` 13 | } 14 | } 15 | 16 | return cmd 17 | }, 18 | 19 | } 20 | 21 | const codegen = (lang, def) => { 22 | const server = store.getters.currentServer 23 | if(!generators[lang]) 24 | return "" 25 | return generators[lang](server, def) 26 | } 27 | 28 | Vue.prototype.$codegen = codegen 29 | -------------------------------------------------------------------------------- /src/plugins/envInject.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import SwaggerParser from '@apidevtools/swagger-parser' 3 | import Environment from '../environment.json' 4 | 5 | const addSaneDefaults = api => { 6 | api.info = { 7 | title: "Untitled", 8 | version: "0.0.0", 9 | description: "", 10 | ...api.info 11 | } 12 | } 13 | 14 | const addPathMethodAndDefaultTags = api => { 15 | for(let path in api.paths) { 16 | for(let method in api.paths[path]) { 17 | const def = api.paths[path][method] 18 | def.path = path 19 | def.method = method 20 | if(!Array.isArray(def.tags) || def.tags.length == 0) { 21 | def.tags = ['default'] 22 | if(api.tags.filter(t => t.name == 'default').length == 0) 23 | api.tags.push({ name: "default" }) 24 | } 25 | } 26 | } 27 | } 28 | 29 | const addPathsToTags = api => { 30 | api.tags.forEach(tag => { 31 | tag.paths = {} 32 | for(let path in api.paths) { 33 | for(let method in api.paths[path]) { 34 | const def = api.paths[path][method] 35 | if(def.tags.indexOf(tag.name) !== -1) { 36 | if(tag.paths[path] == undefined) 37 | tag.paths[path] = {} 38 | tag.paths[path][method] = def 39 | } 40 | } 41 | } 42 | }) 43 | } 44 | 45 | export default async () => { 46 | let api = Environment.api 47 | addSaneDefaults(api) 48 | addPathMethodAndDefaultTags(api) 49 | addPathsToTags(api) 50 | api = await SwaggerParser.bundle(api) 51 | 52 | Vue.prototype.$api = api 53 | Vue.prototype.$config = Environment 54 | Vue.prototype.$i18n = key => Environment.i18n[key] || key 55 | 56 | console.log(`API name: ${api.info.title}, Version: ${api.info.version}`) 57 | } 58 | -------------------------------------------------------------------------------- /src/store.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | Vue.use(Vuex) 4 | 5 | const state = () => ({ 6 | currentServer: '', 7 | currentLang: '', 8 | showDefinition: false, 9 | }) 10 | 11 | const mutations = { 12 | setServer(state, server) { 13 | state.currentServer = server 14 | }, 15 | setLang(state, lang) { 16 | state.currentLang = lang 17 | }, 18 | setShowDefinition(state, show) { 19 | state.showDefinition = show 20 | }, 21 | } 22 | 23 | const getters = { 24 | currentServer: state => state.currentServer, 25 | currentLang: state => state.currentLang, 26 | showDefinition: state => state.showDefinition, 27 | } 28 | 29 | export default new Vuex.Store({ 30 | state, 31 | mutations, 32 | getters, 33 | }) 34 | -------------------------------------------------------------------------------- /src/vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "publicPath": "./", 3 | pages: { 4 | index: { 5 | entry: 'src/index.js', 6 | template: 'src/index.html', 7 | filename: 'index.html', 8 | title: 'Loading Documentation...', 9 | }, 10 | }, 11 | } 12 | --------------------------------------------------------------------------------