diff --git a/.eslintrc.js b/.eslintrc.js index d5022179..4981f126 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -9,7 +9,7 @@ const tsExtensions = [".ts", ".tsx", ".d.ts"]; const allExtensions = [...tsExtensions, ".js", ".jsx"]; module.exports = { - ignorePatterns: ["demo/**"], + ignorePatterns: ["demo/**", "packages/docusaurus-template-*/**"], root: true, extends: [ "react-app", diff --git a/README.md b/README.md index 73f90c4b..c52008a7 100644 --- a/README.md +++ b/README.md @@ -22,125 +22,119 @@ OpenAPI plugin for generating API reference docs in Docusaurus v2.

-> ### πŸ’₯ [Breaking Changes](https://github.com/cloud-annotations/docusaurus-plugin-openapi/releases/tag/v0.2.0): v0.1.0 -> v0.2.0 +## Quick Overview -## Preset usage +```sh +npx create-docusaurus-openapi my-website +cd my-website +npm start +``` -Install the preset in your docusaurus project by running: +> Coming from `v0.1.0`? See the [migration guide](https://github.com/cloud-annotations/docusaurus-plugin-openapi/releases/tag/v0.2.0). -```sh -// with npm -npm install docusaurus-preset-openapi +_([npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b) comes with npm 5.2+ and higher)_ + +Then open [http://localhost:3000/](http://localhost:3000/) to see your site.
+When you’re ready to deploy to production, create a minified bundle with `npm run build`. + +## Creating a Site + +**You’ll need to have Node 14.0.0 or later version on your local development machine** (but it’s not required on the server). We recommend using the latest LTS version. You can use [nvm](https://github.com/creationix/nvm#installation) (macOS/Linux) or [nvm-windows](https://github.com/coreybutler/nvm-windows#node-version-manager-nvm-for-windows) to switch Node versions between different projects. + +To create a new site, you may choose one of the following methods: + +### npx -// with yarn -yarn add docusaurus-preset-openapi +```sh +npx create-docusaurus-openapi my-website ``` -The OpenAPI preset can be used as a drop-in replacement to `@docusaurus/preset-classic`: - -```js -/* docusaurus.config.js */ - -{ - presets: [ - [ - "docusaurus-preset-openapi", - { - // ... options - }, - ], - ], -} +_([npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b) is a package runner tool that comes with npm 5.2+ and higher)_ + +### npm + +```sh +npm init docusaurus-openapi my-website ``` -The default preset options will expose a route, `/api`, and look for an OpenAPI definition at `./openapi.json`. - -To explictly configure this, add an `api` stanza as follows: - -```js -/* docusaurus.config.js */ - -{ - presets: [ - [ - "docusaurus-preset-openapi", - { - api: { - path: 'openapi.json', - routeBasePath: 'api', - }, - // ... other options - }, - ], - ], -} +_`npm init ` is available in npm 6+_ + +### Yarn + +```sh +yarn create docusaurus-openapi my-website ``` -To add a link to the API page, add a new item to the navbar by updating `themeConfig.navbar.items`: - -```js -/* docusaurus.config.js */ - -{ - themeConfig: { - navbar: { - items: [ - // ... other items - { to: "/api", label: "API", position: "left" }, - // ... other items - ], - }, - }, -} +_[`yarn create `](https://yarnpkg.com/lang/en/docs/cli/create/) is available in Yarn 0.25+_ + +It will create a directory called `my-website` inside the current folder.
+Inside that directory, it will generate the initial project structure and install the transitive dependencies: + +``` +my-website +β”œβ”€β”€ blog +β”‚ β”œβ”€β”€ 2019-05-28-hola.md +β”‚ β”œβ”€β”€ 2019-05-29-hello-world.md +β”‚ └── 2020-05-30-welcome.md +β”œβ”€β”€ docs +β”‚ β”œβ”€β”€ doc1.md +β”‚ β”œβ”€β”€ doc2.md +β”‚ β”œβ”€β”€ doc3.md +β”‚ └── mdx.md +β”œβ”€β”€ src +β”‚ β”œβ”€β”€ css +β”‚ β”‚ └── custom.css +β”‚ └── pages +β”‚ β”œβ”€β”€ styles.module.css +β”‚ └── index.js +β”œβ”€β”€ static +β”‚ └── img +β”œβ”€β”€ .gitignore +β”œβ”€β”€ openapi.json +β”œβ”€β”€ docusaurus.config.js +β”œβ”€β”€ babel.config.js +β”œβ”€β”€ package.json +β”œβ”€β”€ sidebars.js +└── README.md ``` -## Multiple OpenAPI Definitions - -To have more than one OpenAPI pages, add additional OpenAPI plugin instances: - -```js -/* docusaurus.config.js */ - -{ - presets: [ - [ - 'docusaurus-preset-openapi', - { - api: { - // id: 'cars', // omitted => default instance - path: 'cars/openapi.json', - routeBasePath: 'cars', - // ... other options - }, - }, - ], - ], - plugins: [ - [ - 'docusaurus-plugin-openapi', - { - id: 'trains', - path: 'trains/openapi.json', - routeBasePath: 'trains', - // ... other options - }, - ], - [ - 'docusaurus-plugin-openapi', - { - id: 'bikes', - path: 'bikes/openapi.json', - routeBasePath: 'bikes', - // ... other options - }, - ], - ], -} +- `/docusaurus.config.js` - A config file containing the site configuration. This can be used to configure the OpenAPI preset +- `/openapi.json` - The default OpenAPI defination that will be served _(path can be configured in `docusaurus.config.js`)_. + +For more info see [project structure rundown](https://docusaurus.io/docs/installation#project-structure-rundown). + +Once the installation is done, you can open your project folder: + +```sh +cd my-website ``` -This will create routes for `/cars`, `/trains` and `/bikes`. +Inside the newly created project, you can run some built-in commands: + +### `npm start` or `yarn start` + +Runs the site in development mode.
+Open [http://localhost:3000](http://localhost:3000) to view it in the browser. + +The page will automatically reload if you make changes to the code. + +### `npm run build` or `yarn build` + +Builds the site for production to the `build` folder. + +Docusaurus is a modern static website generator that will build the website into a directory of static contents, which can be copied to any static file hosting service like [GitHub pages](https://pages.github.com/), [Vercel](https://vercel.com/) or [Netlify](https://www.netlify.com/). + +## Popular Alternatives + +Docusaurus OpenAPI is great for: + +- **Static generation** All OpenAPI operations will be rendered as static pages at build time, giving many performance benefits. +- **Demo panel** Allow users to try out your API with an interactive demo panel. +- **Native Docusaurus feel** Built from scratch to work with Docusaurus. + +Here are a few common cases where you might want to try something else: -> **Note:** One instance of the plugin is included in the preset. All additional plugin instances will require an `id`. +- If you need better support for more advanced OpenAPI features, check out [Redocusaurus](https://github.com/rohit-gohri/redocusaurus/). Redocusaurus embeds the much more mature OpenAPI documentation generator, [Redoc](https://github.com/Redocly/redoc), into Docusaurus as a React component. ## Contributing diff --git a/cypress/integration/test.spec.ts b/cypress/integration/test.spec.ts index 2e73ab9d..e9c8f876 100644 --- a/cypress/integration/test.spec.ts +++ b/cypress/integration/test.spec.ts @@ -6,30 +6,22 @@ * ========================================================================== */ describe("test", () => { - before(() => { - cy.visit("/api/recursive"); - }); - - it("renders query parameters", () => { - cy.findByText(/query parameters/i).should("exist"); - }); - - it("loads issue 21 tab", () => { - checkTab(/issue 21/i, [], /missing summary/i); - }); - - it("loads cos tab", () => { - checkTab(/cos/i, [], /generating an iam token/i); + it("loads Petstore page", () => { + cy.visit("/petstore"); + navTo( + [/pet/i, /add a new pet to the store/i], + /add a new pet to the store/i + ); }); - it("loads yaml tab", () => { - checkTab(/yaml/i, [/introduction/i], /yaml example/i); - checkTab(/yaml/i, [/^api$/i, /hello world/i], /hello world/i); + it("loads Cloud Object Storage page", () => { + cy.visit("/cos"); + navTo([], /generating an iam token/i); }); - it("loads mega tab", () => { - checkTab( - /mega/i, + it("loads Multi-spec page", () => { + cy.visit("/multi-spec"); + navTo( [ /foods/i, /burger store/i, @@ -41,18 +33,6 @@ describe("test", () => { ); }); - it("loads petstore tab", () => { - checkTab( - /petstore/i, - [/pet/i, /add a new pet to the store/i], - /add a new pet to the store/i - ); - }); - - it("loads api tab", () => { - checkTab(/^api$/i, [/^pet$/i, /recursive/i], /recursive/i); - }); - it("loads a page with authentication", () => { cy.visit("/cos/list-buckets"); cy.findByRole("button", { name: /authorize/i }).should("exist"); @@ -62,14 +42,15 @@ describe("test", () => { }); }); -function checkTab(tab: RegExp, links: RegExp[], heading: RegExp) { +/** + * Navigate to page using the sidebar + */ +function navTo(links: RegExp[], heading: RegExp) { cy.on("uncaught:exception", () => { // there is an uncaught error trying to load monaco in ci return false; }); - cy.get(".navbar").findByRole("link", { name: tab }).click(); - for (let link of links) { cy.get("nav.menu") .findByRole("link", { name: link }) diff --git a/demo/docs/intro.md b/demo/docs/intro.md index 8417aebe..96273969 100644 --- a/demo/docs/intro.md +++ b/demo/docs/intro.md @@ -10,14 +10,12 @@ Let's discover **Docusaurus in less than 5 minutes**. Get started by **creating a new site**. -Or **try Docusaurus immediately** with **[docusaurus.new](https://docusaurus.new)**. - ## Generate a new site -Generate a new Docusaurus site using the **classic template**: +Generate a new Docusaurus site using the **OpenAPI template**: ```shell -npm init docusaurus@latest my-website classic +npx create-docusaurus-openapi my-website ``` ## Start your site @@ -32,4 +30,4 @@ npx docusaurus start Your site starts at `http://localhost:3000`. -Open `docs/intro.md` and edit some lines: the site **reloads automatically** and displays your changes. +Open `openapi.json` and make some edits: the site **reloads automatically** and displays your changes. diff --git a/demo/docusaurus.config.js b/demo/docusaurus.config.js index c3aef5e4..39cd5aea 100644 --- a/demo/docusaurus.config.js +++ b/demo/docusaurus.config.js @@ -22,7 +22,8 @@ const config = { /** @type {import('docusaurus-preset-openapi').Options} */ ({ api: { - path: "examples/openapi.json", + path: "examples/petstore.yaml", + routeBasePath: "petstore", }, docs: { sidebarPath: require.resolve("./sidebars.js"), @@ -30,12 +31,7 @@ const config = { editUrl: "https://github.com/cloud-annotations/docusaurus-openapi/edit/main/demo/", }, - blog: { - showReadingTime: true, - // Please change this to your repo. - editUrl: - "https://github.com/cloud-annotations/docusaurus-openapi/edit/main/demo/", - }, + blog: false, theme: { customCss: require.resolve("./src/css/custom.css"), }, @@ -50,14 +46,6 @@ const config = { ], plugins: [ - [ - "docusaurus-plugin-openapi", - { - id: "issue", - path: "examples/openapi-issue-21.json", - routeBasePath: "issue-21", - }, - ], [ "docusaurus-plugin-openapi", { @@ -69,25 +57,9 @@ const config = { [ "docusaurus-plugin-openapi", { - id: "yaml", - path: "examples/openapi.yaml", - routeBasePath: "yaml", - }, - ], - [ - "docusaurus-plugin-openapi", - { - id: "petstore", - path: "examples/petstore.yaml", - routeBasePath: "petstore", - }, - ], - [ - "docusaurus-plugin-openapi", - { - id: "mega", + id: "multi-spec", path: "examples", - routeBasePath: "mega", + routeBasePath: "multi-spec", }, ], ], @@ -95,6 +67,10 @@ const config = { themeConfig: /** @type {import('docusaurus-preset-openapi').ThemeConfig} */ ({ + colorMode: { + disableSwitch: true, + defaultMode: "dark", + }, navbar: { title: "OpenAPI", logo: { @@ -106,65 +82,64 @@ const config = { type: "doc", docId: "intro", position: "left", - label: "Tutorial", + label: "Docs", + }, + { + label: "Examples", + position: "left", + items: [ + { to: "/petstore", label: "Petstore" }, + { to: "/cos", label: "Cloud Object Storage" }, + { to: "/multi-spec", label: "Multi-spec" }, + ], }, - { to: "/api", label: "API", position: "left" }, - { to: "/issue-21", label: "Issue 21", position: "left" }, - { to: "/cos", label: "COS", position: "left" }, - { to: "/yaml", label: "YAML", position: "left" }, - { to: "/petstore", label: "Petstore", position: "left" }, - { to: "/mega", label: "Mega", position: "left" }, - { to: "/blog", label: "Blog", position: "left" }, { href: "https://github.com/cloud-annotations/docusaurus-openapi", - label: "GitHub", position: "right", + className: "header-github-link", + "aria-label": "GitHub repository", }, ], }, footer: { style: "dark", - links: [ - { - title: "Docs", - items: [ - { - label: "Tutorial", - to: "/docs/intro", - }, - ], - }, - { - title: "Community", - items: [ - { - label: "Stack Overflow", - href: "https://stackoverflow.com/questions/tagged/docusaurus", - }, - { - label: "Discord", - href: "https://discordapp.com/invite/docusaurus", - }, - { - label: "Twitter", - href: "https://twitter.com/docusaurus", - }, - ], - }, - { - title: "More", - items: [ - { - label: "Blog", - to: "/blog", - }, - { - label: "GitHub", - href: "https://github.com/cloud-annotations/docusaurus-openapi", - }, - ], - }, - ], + // links: [ + // { + // title: "Docs", + // items: [ + // { + // label: "Tutorial", + // to: "/docs/intro", + // }, + // ], + // }, + // { + // title: "Community", + // items: [ + // { + // label: "Stack Overflow", + // href: "https://stackoverflow.com/questions/tagged/docusaurus", + // }, + // { + // label: "Discord", + // href: "https://discordapp.com/invite/docusaurus", + // }, + // { + // label: "Twitter", + // href: "https://twitter.com/docusaurus", + // }, + // ], + // }, + // { + // title: "More", + // items: [ + // { + // label: "GitHub", + // href: "https://github.com/cloud-annotations/docusaurus-openapi", + // }, + // ], + // }, + // ], copyright: `Copyright © ${new Date().getFullYear()} Cloud Annotations, Inc. Built with Docusaurus.`, }, prism: { diff --git a/demo/examples/openapi.json b/demo/examples/openapi.json deleted file mode 100644 index 89ae3307..00000000 --- a/demo/examples/openapi.json +++ /dev/null @@ -1,1061 +0,0 @@ -{ - "openapi": "3.0.3", - "info": { - "title": "Swagger Petstore JSON", - "version": "1.0.5", - "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.\n", - "termsOfService": "http://swagger.io/terms/", - "contact": { - "email": "bourdakos1@gmail.com" - }, - "license": { - "name": "Apache 2.0", - "url": "http://www.apache.org/licenses/LICENSE-2.0.html" - }, - "x-github": "https://github.com/bee-travels" - }, - "paths": { - "/pet/{petId}": { - "put": { - "tags": ["pet"], - "summary": "Recursive", - "description": "Advanced markdown description example.\n\n## H2 title\n\nCode snippet **Shell Example**:\n\n```shell\nyarn start\n```\n\n:::tip pro tip\n`remark-admonitions` is pretty great!\n:::", - "parameters": [ - { - "name": "currency", - "in": "query", - "description": "Advanced markdown description example.\n\n## H2 title\n\nCode snippet **Shell Example**:\n\n```shell\nyarn start\n```\n\n:::tip pro tip\n`remark-admonitions` is pretty great!\n:::", - "required": false, - "schema": { - "type": "string" - }, - "examples": { - "zero": { - "value": 0, - "summary": "A sample limit value" - }, - "max": { - "value": 50, - "summary": "A sample limit value" - } - } - }, - { - "name": "currency2", - "in": "query", - "description": "The 3 character currency code.", - "required": false, - "schema": { - "type": "string" - }, - "example": "approved" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/A" - } - } - } - }, - "responses": { - "200": { - "description": "successful operation" - } - } - }, - "get": { - "tags": ["pet"], - "summary": "Find pet by ID", - "description": "Returns a single pet", - "operationId": "getPetById", - "parameters": [ - { - "name": "petId", - "in": "path", - "description": "ID of pet to return", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - } - ], - "responses": { - "200": { - "description": "successful operation", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Pet" - } - }, - "application/xml": { - "schema": { - "$ref": "#/components/schemas/Pet" - } - } - } - }, - "400": { - "description": "Invalid ID supplied" - }, - "404": { - "description": "Pet not found" - } - }, - "security": [ - { - "ApiKey": [] - } - ] - }, - "post": { - "tags": ["pet"], - "summary": "Updates a pet in the store with form data", - "operationId": "updatePetWithForm", - "parameters": [ - { - "name": "petId", - "in": "path", - "description": "ID of pet that needs to be updated", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - } - ], - "requestBody": { - "content": { - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/UpdatePetObject" - } - } - } - }, - "responses": { - "405": { - "description": "Invalid input" - } - }, - "security": [ - { - "PetstoreAuth": ["write:pets", "read:pets"] - } - ] - }, - "delete": { - "tags": ["pet"], - "summary": "Deletes a pet", - "operationId": "deletePet", - "parameters": [ - { - "name": "apikey", - "in": "header", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "petId", - "in": "path", - "description": "Pet id to delete", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - } - ], - "responses": { - "400": { - "description": "Invalid ID supplied" - }, - "404": { - "description": "Pet not found" - } - }, - "security": [ - { - "PetstoreAuth": ["write:pets", "read:pets"] - } - ] - } - }, - "/pet/{petId}/uploadImage": { - "post": { - "tags": ["pet"], - "summary": "uploads an image", - "operationId": "uploadFile", - "parameters": [ - { - "name": "petId", - "in": "path", - "description": "ID of pet to update", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - } - ], - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/UploadPetImageObject" - } - } - } - }, - "responses": { - "200": { - "description": "successful operation", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - } - } - }, - "400": { - "description": "Invalid request due to user input, for example:\n\n* Bad header parameter\n* Invalid output language\n* No input images\n* The size of the image file in the request is larger than the maximum supported size" - } - }, - "security": [ - { - "PetstoreAuth": ["write:pets", "read:pets"] - } - ] - } - }, - "/pet": { - "post": { - "tags": ["pet"], - "summary": "Add a new pet to the store", - "operationId": "addPet", - "requestBody": { - "description": "Pet object that needs to be added to the store", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Pet" - } - }, - "application/xml": { - "schema": { - "$ref": "#/components/schemas/Pet" - } - } - }, - "required": true - }, - "responses": { - "405": { - "description": "Invalid input" - } - }, - "security": [ - { - "PetstoreAuth": ["write:pets", "read:pets"] - } - ] - }, - "put": { - "tags": ["pet"], - "summary": "Update an existing pet", - "operationId": "updatePet", - "requestBody": { - "description": "Pet object that needs to be added to the store", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Pet" - } - }, - "application/xml": { - "schema": { - "$ref": "#/components/schemas/Pet" - } - } - }, - "required": true - }, - "responses": { - "400": { - "description": "Invalid ID supplied" - }, - "404": { - "description": "Pet not found" - }, - "405": { - "description": "Validation exception" - } - }, - "security": [ - { - "PetstoreAuth": ["write:pets", "read:pets"] - } - ] - } - }, - "/pet/findByStatus": { - "get": { - "tags": ["pet"], - "summary": "Finds Pets by status", - "description": "Multiple status values can be provided with comma separated strings", - "operationId": "findPetsByStatus", - "parameters": [ - { - "name": "status", - "in": "query", - "description": "Status values that need to be considered for filter", - "required": true, - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/StatusEnum" - } - } - } - ], - "responses": { - "200": { - "description": "Invalid status value", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Pet" - } - } - }, - "application/xml": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Pet" - } - } - } - } - }, - "400": { - "description": "successful operation" - } - }, - "security": [ - { - "PetstoreAuth": ["write:pets", "read:pets"] - } - ] - } - }, - "/pet/findByTags": { - "get": { - "deprecated": true, - "x-deprecated-description": "The **Finds Pets by tags** endpoint has been deprecated and may be removed in future versions of the Petstore API.\n\nInstructions for updating: Use [Finds Pets by status](/api/finds-pets-by-status) instead.", - "tags": ["pet"], - "summary": "Finds Pets by tags", - "description": "'Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.'", - "operationId": "findPetsByTags", - "responses": { - "200": { - "description": "successful operation", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Pet" - } - } - }, - "application/xml": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Pet" - } - } - } - } - }, - "400": { - "description": "Invalid tag value" - } - }, - "security": [ - { - "PetstoreAuth": ["write:pets", "read:pets"] - } - ] - } - }, - "/store/inventory": { - "get": { - "tags": ["store"], - "summary": "Returns pet inventories by status", - "description": "Returns a map of status codes to quantities", - "operationId": "getInventory", - "responses": { - "200": { - "description": "successful operation", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Inventory" - } - } - } - } - }, - "security": [ - { - "ApiKey": [] - } - ] - } - }, - "/store/order/{orderId}": { - "get": { - "tags": ["store"], - "summary": "Find purchase order by ID", - "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", - "operationId": "getOrderById", - "parameters": [ - { - "name": "orderId", - "in": "path", - "description": "ID of pet that needs to be fetched", - "required": true, - "schema": { - "$ref": "#/components/schemas/OrderID" - } - } - ], - "responses": { - "200": { - "description": "successful operation", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Order" - } - }, - "application/xml": { - "schema": { - "$ref": "#/components/schemas/Order" - } - } - } - }, - "400": { - "description": "Invalid ID supplied" - }, - "404": { - "description": "Order not found" - } - } - }, - "delete": { - "tags": ["store"], - "summary": "Delete purchase order by ID", - "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", - "operationId": "deleteOrder", - "parameters": [ - { - "name": "orderId", - "in": "path", - "description": "ID of the order that needs to be deleted", - "required": true, - "schema": { - "$ref": "#/components/schemas/OrderID" - } - } - ], - "responses": { - "400": { - "description": "Invalid ID supplied" - }, - "404": { - "description": "Order not found" - } - } - } - }, - "/store/order": { - "post": { - "tags": ["store"], - "summary": "Place an order for a pet", - "operationId": "placeOrder", - "requestBody": { - "description": "order placed for purchasing the pet", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Order" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "successful operation", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Order" - } - }, - "application/xml": { - "schema": { - "$ref": "#/components/schemas/Order" - } - } - } - }, - "400": { - "description": "Invalid Order" - } - } - } - }, - "/user": { - "post": { - "tags": ["user"], - "summary": "Create user", - "description": "This can only be done by the logged in user.", - "operationId": "createUser", - "requestBody": { - "description": "Created user object", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User" - } - } - }, - "required": true - }, - "responses": { - "default": { - "description": "successful operation" - } - } - } - }, - "/user/createWithArray": { - "post": { - "tags": ["user"], - "summary": "Creates list of users with given input array", - "operationId": "createUsersWithArrayInput", - "requestBody": { - "description": "List of user object", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/User" - } - } - } - }, - "required": true - }, - "responses": { - "default": { - "description": "successful operation" - } - } - } - }, - "/user/createWithList": { - "post": { - "tags": ["user"], - "summary": "Creates list of users with given input array", - "operationId": "createUsersWithListInput", - "requestBody": { - "description": "List of user object", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/User" - } - } - } - }, - "required": true - }, - "responses": { - "default": { - "description": "successful operation" - } - } - } - }, - "/user/{username}": { - "get": { - "tags": ["user"], - "summary": "Get user by user name", - "operationId": "getUserByName", - "parameters": [ - { - "name": "username", - "in": "path", - "description": "The name that needs to be fetched. Use user1 for testing.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "successful operation", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User" - } - }, - "application/xml": { - "schema": { - "$ref": "#/components/schemas/User" - } - } - } - }, - "400": { - "description": "Invalid username supplied" - }, - "404": { - "description": "User not found" - } - } - }, - "put": { - "tags": ["user"], - "summary": "Updated user", - "description": "This can only be done by the logged in user.", - "operationId": "updateUser", - "parameters": [ - { - "name": "username", - "in": "path", - "description": "name that need to be updated", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "description": "Updated user object", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User" - } - } - }, - "required": true - }, - "responses": { - "400": { - "description": "Invalid user supplied" - }, - "404": { - "description": "User not found" - } - } - }, - "delete": { - "tags": ["user"], - "summary": "Delete user", - "description": "This can only be done by the logged in user.", - "operationId": "deleteUser", - "parameters": [ - { - "name": "username", - "in": "path", - "description": "The name that needs to be deleted", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "400": { - "description": "Invalid username supplied" - }, - "404": { - "description": "User not found" - } - } - } - }, - "/user/login": { - "get": { - "tags": ["user"], - "summary": "Logs user into the system", - "operationId": "loginUser", - "parameters": [ - { - "name": "username", - "in": "query", - "description": "The user name for login", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "password", - "in": "query", - "description": "The password for login in clear text", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "successful operation", - "headers": { - "X-Expires-After": { - "description": "date in UTC when token expires", - "schema": { - "type": "string", - "format": "date-time" - } - }, - "X-Rate-Limit": { - "description": "calls per hour allowed by the user", - "schema": { - "type": "integer", - "format": "int32" - } - } - }, - "content": { - "application/json": { - "schema": { - "type": "string" - } - }, - "application/xml": { - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "Invalid username/password supplied" - } - } - } - }, - "/user/logout": { - "get": { - "tags": ["user"], - "summary": "Logs out current logged in user session", - "operationId": "logoutUser", - "responses": { - "default": { - "description": "successful operation" - } - } - } - } - }, - "components": { - "schemas": { - "A": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "friend": { - "$ref": "#/components/schemas/A" - }, - "friend2": { - "$ref": "#/components/schemas/B" - } - } - }, - "B": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "friend": { - "$ref": "#/components/schemas/B" - } - } - }, - "Category": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string" - } - }, - "xml": { - "name": "Category" - } - }, - "Pet": { - "type": "object", - "required": ["name", "photoUrls"], - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "category": { - "$ref": "#/components/schemas/Category" - }, - "name": { - "type": "string", - "example": "doggie" - }, - "photoUrls": { - "type": "array", - "xml": { - "wrapped": true - }, - "items": { - "type": "string", - "xml": { - "name": "photoUrl" - } - } - }, - "tags": { - "type": "array", - "xml": { - "wrapped": true - }, - "items": { - "xml": { - "name": "tag" - }, - "$ref": "#/components/schemas/Tag" - } - } - } - }, - "Tag": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string" - } - }, - "xml": { - "name": "Tag" - } - }, - "StatusEnum": { - "type": "string", - "enum": ["available", "pending", "sold"], - "default": "available" - }, - "ApiResponse": { - "type": "object", - "properties": { - "code": { - "type": "integer", - "format": "int32" - }, - "type": { - "type": "string" - }, - "message": { - "type": "string" - } - } - }, - "UpdatePetObject": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Updated name of the pet" - }, - "status": { - "type": "string", - "description": "Updated status of the pet" - } - } - }, - "UploadPetImageObject": { - "type": "object", - "properties": { - "additionalMetadata": { - "type": "string", - "description": "Additional data to pass to server" - }, - "file": { - "type": "string", - "format": "binary", - "description": "file to upload" - } - } - }, - "Inventory": { - "type": "object", - "additionalProperties": { - "type": "integer", - "format": "int32" - } - }, - "OrderID": { - "type": "integer", - "maximum": 10, - "minimum": 1, - "format": "int64" - }, - "Order": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "petId": { - "type": "integer", - "format": "int64" - }, - "quantity": { - "type": "integer", - "format": "int32" - }, - "shipDate": { - "type": "string", - "format": "date-time" - }, - "status": { - "type": "string", - "description": "Order Status", - "enum": ["placed", "approved", "delivered"] - }, - "complete": { - "type": "boolean" - } - }, - "xml": { - "name": "Order" - } - }, - "User": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "username": { - "type": "string" - }, - "firstName": { - "type": "string" - }, - "lastName": { - "type": "string" - }, - "email": { - "type": "string" - }, - "password": { - "type": "string" - }, - "phone": { - "type": "string" - }, - "userStatus": { - "type": "integer", - "format": "int32", - "description": "User Status" - } - }, - "xml": { - "name": "User" - } - } - }, - "securitySchemes": { - "ApiKey": { - "type": "apiKey", - "name": "api_key", - "in": "header" - }, - "PetstoreAuth": { - "type": "oauth2", - "flows": { - "implicit": { - "authorizationUrl": "https://petstore.swagger.io/oauth/authorize", - "scopes": { - "read:pets": "read your pets", - "write:pets": "modify pets in your account" - } - } - } - } - } - }, - "tags": [ - { - "name": "bloop" - }, - { - "name": "pet", - "description": "Everything about your Pets", - "externalDocs": { - "description": "Find out more", - "url": "http://swagger.io" - } - }, - { - "name": "store", - "description": "Access to Petstore orders" - }, - { - "name": "user", - "description": "Operations about user", - "externalDocs": { - "description": "Find out more about our store", - "url": "http://swagger.io" - } - } - ], - "externalDocs": { - "description": "Find out more about Swagger", - "url": "http://swagger.io" - } -} diff --git a/demo/examples/openapi.yaml b/demo/examples/openapi.yaml deleted file mode 100644 index 03a7005e..00000000 --- a/demo/examples/openapi.yaml +++ /dev/null @@ -1,13 +0,0 @@ -openapi: 3.0.3 -info: - title: YAML Example - version: 1.0.0 - description: Sample description. -paths: - /hello: - get: - summary: Hello World - description: Example OpenApi definition with YAML. - responses: - 200: - description: OK diff --git a/demo/src/css/custom.css b/demo/src/css/custom.css index 51608a72..89f42981 100644 --- a/demo/src/css/custom.css +++ b/demo/src/css/custom.css @@ -4,18 +4,162 @@ * work well for content-centric websites. */ -/* You can override the default Infima variables here. */ :root { - --ifm-color-primary: #25c2a0; - --ifm-color-primary-dark: rgb(33, 175, 144); - --ifm-color-primary-darker: rgb(31, 165, 136); - --ifm-color-primary-darkest: rgb(26, 136, 112); - --ifm-color-primary-light: rgb(70, 203, 174); - --ifm-color-primary-lighter: rgb(102, 212, 189); - --ifm-color-primary-lightest: rgb(146, 224, 208); --ifm-code-font-size: 95%; } +html[data-theme="dark"] { + --ifm-color-primary: #58a6ff; + --ifm-background-color: #0d1117; + --ifm-navbar-background-color: #0d1117; + --ifm-color-emphasis-300: #30363d; + --ifm-card-background-color: #161b22; + --ifm-code-background: rgba(110, 118, 129, 0.4); + --ifm-table-stripe-background: var(--ifm-table-background); + --openapi-input-background: rgba(110, 118, 129, 0.4) !important; + --openapi-monaco-border-color: #21262d !important; + --openapi-monaco-background-color: #0d1117 !important; + --ifm-dropdown-background-color: #161b22; +} + +.navbar__link:hover { + color: rgb(245, 246, 247, 0.7); +} + +.navbar__link--active { + color: unset; +} + +.navbar__brand:hover { + color: rgb(245, 246, 247, 0.7); +} + +.navbar { + padding-right: 28px; +} + +.theme-api-markdown > table { + font-size: 90%; +} + +.theme-api-markdown table { + --ifm-code-background: #161b22; + --ifm-table-border-color: #21262d; +} + +.theme-api-markdown table table { + width: calc(100% - 16px) !important; + margin-left: 16px; +} + +.theme-api-markdown table table { + box-shadow: -15px 0px 0px 0px var(--ifm-background-color), + -16px 0px 0px 0px var(--ifm-table-border-color); +} + +.theme-api-markdown table table tr:first-child { + border-top: 0; +} + +.theme-api-markdown table th, +.theme-api-markdown table td { + padding: var(--ifm-table-cell-padding) 0; +} + +.theme-api-markdown table thead tr { + border-bottom-width: 3px; + border-top: 0; +} + +.theme-api-markdown table th { + border: 0; +} + +.theme-api-markdown table td { + border: 0; +} + +.button--primary:not(.button--outline) { + --ifm-button-color: white; + --ifm-button-background-color: #238636; +} + +.button--primary { + --ifm-button-border-color: #238636; +} + +.button--primary:not(.button--outline):hover { + --ifm-button-background-color: #2ea043; + --ifm-button-border-color: #2ea043; +} + +.button--primary:active, +.button--primary.button--active { + --ifm-button-border-color: #238636; + --ifm-button-background-color: #238636; + background-color: #238636; + border-color: #238636; +} + +.button--outline { + --ifm-button-background-color: #21262d; + --ifm-button-border-color: rgba(240, 246, 252, 0.1); + --ifm-button-color: #c9d1d9; +} + +.button--outline:hover { + --ifm-button-background-color: #30363d; + --ifm-button-border-color: #8b949e; + --ifm-button-color: #c9d1d9; +} + +.button--outline:active, +.button--outline.button--active { + --ifm-button-background-color: #282e33; + --ifm-button-border-color: #6e7681; + --ifm-button-color: #c9d1d9; + background-color: #282e33; + border-color: #6e7681; +} + +.api-code-tab { + padding: var(--ifm-spacing-vertical) var(--ifm-spacing-horizontal) !important; + margin: 0 !important; + border-radius: var(--openapi-card-border-radius) !important; + font-size: calc(0.875rem * var(--ifm-button-size-multiplier)) !important; + border-bottom: 3px solid transparent !important; +} + +.api-code-tab--active { + background-color: transparent !important; + border-bottom: 3px solid var(--ifm-color-primary) !important; + border-bottom-color: var(--ifm-tabs-color-active-border) !important; + border-bottom-left-radius: 0 !important; + border-bottom-right-radius: 0 !important; +} + +.api-code-tab--active:hover { + background-color: var(--ifm-hover-overlay) !important; +} + +.alert { + border-width: 1px; +} + +.navbar { + border-bottom: 1px solid var(--ifm-color-emphasis-300); +} + +.footer--dark { + border-top: 1px solid var(--ifm-color-emphasis-300); + --ifm-footer-background-color: var(--ifm-background-color); +} + +.hero--primary { + --ifm-hero-background-color: #161b22; + --ifm-hero-text-color: var(--ifm-heading-color); +} + .docusaurus-highlight-code-line { background-color: rgba(0, 0, 0, 0.1); display: block; @@ -26,3 +170,21 @@ html[data-theme="dark"] .docusaurus-highlight-code-line { background-color: rgba(0, 0, 0, 0.3); } + +.header-github-link:hover { + opacity: 0.6; +} + +.header-github-link:before { + content: ""; + width: 24px; + height: 24px; + display: flex; + background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") + no-repeat; +} + +html[data-theme="dark"] .header-github-link:before { + background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") + no-repeat; +} diff --git a/demo/src/pages/index.js b/demo/src/pages/index.js index ebfe6f0a..07a44b08 100644 --- a/demo/src/pages/index.js +++ b/demo/src/pages/index.js @@ -18,7 +18,7 @@ function HomepageHeader() { className="button button--secondary button--lg" to="/docs/intro" > - Docusaurus Tutorial - 5min ⏱️ + Get Started diff --git a/package.json b/package.json index 021a6693..c15e62ea 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "format": "prettier . --check --ignore-unknown --ignore-path .gitignore", "lint": "eslint . --ignore-path .gitignore --ext .ts,.tsx,.js", "test": "jest", - "test:cypress:dev": "start-server-and-test watch http://localhost:3000 cy:open", + "test:cypress:dev": "start-server-and-test watch:demo http://localhost:3000 cy:open", "test:cypress": "start-server-and-test serve http://localhost:3000 cy:run", "release:changelog": "scripts/changelog.ts", "release:version": "scripts/version.ts", diff --git a/packages/create-docusaurus-openapi/README.md b/packages/create-docusaurus-openapi/README.md new file mode 100644 index 00000000..d998f35d --- /dev/null +++ b/packages/create-docusaurus-openapi/README.md @@ -0,0 +1 @@ +# `create-docusaurus-openapi` diff --git a/packages/create-docusaurus-openapi/bin/index.js b/packages/create-docusaurus-openapi/bin/index.js new file mode 100755 index 00000000..1729c620 --- /dev/null +++ b/packages/create-docusaurus-openapi/bin/index.js @@ -0,0 +1,68 @@ +#!/usr/bin/env node +/* ============================================================================ + * Copyright (c) Cloud Annotations + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * ========================================================================== */ + +// @ts-check + +const path = require("path"); + +const logger = require("@docusaurus/logger").default; +const program = require("commander"); +const semver = require("semver"); + +const { default: init } = require("../lib"); +const requiredVersion = require("../package.json").engines.node; + +if (!semver.satisfies(process.version, requiredVersion)) { + logger.error("Minimum Node.js version not met :("); + logger.info`You are using Node.js number=${process.version}, Requirement: Node.js number=${requiredVersion}.`; + process.exit(1); +} + +function wrapCommand(fn) { + return (...args) => + fn(...args).catch((err) => { + logger.error(err.stack); + process.exitCode = 1; + }); +} + +program + .version(require("../package.json").version) + .usage(" [options]"); + +program + .command("init [siteName] [template] [rootDir]", { isDefault: true }) + .option("--use-npm") + .option("--skip-install") + .option("--typescript") + .description("Initialize website.") + .action( + ( + siteName, + template, + rootDir = ".", + { useNpm, skipInstall, typescript } = {} + ) => { + wrapCommand(init)(path.resolve(rootDir), siteName, template, { + useNpm, + skipInstall, + typescript, + }); + } + ); + +program.arguments("").action((cmd) => { + program.outputHelp(); + logger.error`Unknown command code=${cmd}.`; +}); + +program.parse(process.argv); + +if (!process.argv.slice(1).length) { + program.outputHelp(); +} diff --git a/packages/create-docusaurus-openapi/package.json b/packages/create-docusaurus-openapi/package.json new file mode 100755 index 00000000..ca1fcbfe --- /dev/null +++ b/packages/create-docusaurus-openapi/package.json @@ -0,0 +1,44 @@ +{ + "name": "create-docusaurus-openapi", + "version": "0.4.0", + "description": "Create Docusaurus apps easily.", + "repository": { + "type": "git", + "url": "https://github.com/cloud-annotations/docusaurus-plugin-openapi.git", + "directory": "packages/create-docusaurus-openapi" + }, + "bugs": { + "url": "https://github.com/cloud-annotations/docusaurus-plugin-openapi/issues" + }, + "scripts": { + "create-docusaurus-openapi": "create-docusaurus-openapi", + "build": "tsc", + "watch": "tsc --watch" + }, + "bin": { + "create-docusaurus-openapi": "bin/index.js" + }, + "publishConfig": { + "access": "public" + }, + "license": "MIT", + "dependencies": { + "@docusaurus/logger": "2.0.0-beta.14", + "commander": "^5.1.0", + "fs-extra": "^10.0.0", + "lodash": "^4.17.20", + "prompts": "^2.4.1", + "semver": "^7.3.4", + "shelljs": "^0.8.4", + "supports-color": "^8.1.1", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14" + }, + "devDependencies": { + "@types/prompts": "^2.0.9", + "@types/shelljs": "^0.8.6", + "@types/supports-color": "^8.1.1" + } +} diff --git a/packages/create-docusaurus-openapi/src/index.ts b/packages/create-docusaurus-openapi/src/index.ts new file mode 100755 index 00000000..846bc68c --- /dev/null +++ b/packages/create-docusaurus-openapi/src/index.ts @@ -0,0 +1,373 @@ +/* ============================================================================ + * Copyright (c) Cloud Annotations + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * ========================================================================== */ + +import { execSync } from "child_process"; +import os from "os"; +import path from "path"; + +import logger from "@docusaurus/logger"; +import fs from "fs-extra"; +import { kebabCase } from "lodash"; +import prompts from "prompts"; +import shell from "shelljs"; +import supportsColor from "supports-color"; + +const RecommendedTemplate = "openapi"; + +function hasYarn() { + try { + execSync("yarnpkg --version", { stdio: "ignore" }); + return true; + } catch (e) { + return false; + } +} + +function isValidGitRepoUrl(gitRepoUrl: string) { + return ["https://", "git@"].some((item) => gitRepoUrl.startsWith(item)); +} + +async function updatePkg(pkgPath: string, obj: Record) { + const content = await fs.readFile(pkgPath, "utf-8"); + const pkg = JSON.parse(content); + const newPkg = Object.assign(pkg, obj); + + await fs.outputFile(pkgPath, `${JSON.stringify(newPkg, null, 2)}\n`); +} + +function getTemplateInstallPackage( + template: string, + originalDirectory: string +) { + let templateToInstall = "docusaurus-template"; + if (template) { + const match = template.match(/^file:(.*)?$/); + if (match) { + templateToInstall = `file:${path.resolve(originalDirectory, match[1])}`; + } else if ( + template.includes("://") || + template.match(/^.+\.(tgz|tar\.gz)$/) + ) { + // for tar.gz or alternative paths + templateToInstall = template; + } else { + // Add prefix 'cra-template-' to non-prefixed templates, leaving any + // @scope/ and @version intact. + const packageMatch = template.match(/^(@[^/]+\/)?([^@]+)?(@.+)?$/); + const scope = packageMatch?.[1] || ""; + const templateName = packageMatch?.[2] || ""; + const version = packageMatch?.[3] || ""; + + if ( + templateName === templateToInstall || + templateName.startsWith(`${templateToInstall}-`) + ) { + // Covers: + // - cra-template + // - @SCOPE/cra-template + // - cra-template-NAME + // - @SCOPE/cra-template-NAME + templateToInstall = `${scope}${templateName}${version}`; + } else if (version && !scope && !templateName) { + // Covers using @SCOPE only + templateToInstall = `${version}/${templateToInstall}`; + } else { + // Covers templates without the `cra-template` prefix: + // - NAME + // - @SCOPE/NAME + templateToInstall = `${scope}${templateToInstall}-${templateName}${version}`; + } + } + } + + return templateToInstall; +} + +// Extract package name from tarball url or path. +function getPackageInfo(installPackage: string) { + const match = installPackage.match(/^file:(.*)?$/); + if (match) { + const installPackagePath = match[1]; + const { name, version } = require(path.join( + installPackagePath, + "package.json" + )); + return { name, version }; + } + return { name: installPackage }; +} + +function createPackageJson(appPath: string, templateName: string) { + const appPackage = require(path.join(appPath, "package.json")); + + const templatePath = path.dirname( + require.resolve(`${templateName}/package.json`, { paths: [appPath] }) + ); + + const templateJsonPath = path.join(templatePath, "template.json"); + + let templateJson: any = {}; + if (fs.existsSync(templateJsonPath)) { + templateJson = require(templateJsonPath); + } + + const templatePackage = templateJson.package || {}; + + // Keys to ignore in templatePackage + const templatePackageIgnorelist = [ + "name", + "version", + "description", + "keywords", + "bugs", + "license", + "author", + "contributors", + "files", + "browser", + "bin", + "man", + "directories", + "repository", + "peerDependencies", + "bundledDependencies", + "optionalDependencies", + "engineStrict", + "os", + "cpu", + "preferGlobal", + "private", + "publishConfig", + ]; + + // Keys from templatePackage that will be merged with appPackage + const templatePackageToMerge = ["dependencies"]; // "dependencies", "scripts" + + // Keys from templatePackage that will be added to appPackage, + // replacing any existing entries. + const templatePackageToReplace = Object.keys(templatePackage).filter( + (key) => { + return ( + !templatePackageIgnorelist.includes(key) && + !templatePackageToMerge.includes(key) + ); + } + ); + + // Copy over some of the devDependencies + appPackage.dependencies = appPackage.dependencies || {}; + const templateDependencies = templatePackage.dependencies || {}; + appPackage.dependencies = { + ...appPackage.dependencies, + ...templateDependencies, + }; + + // Add templatePackage keys/values to appPackage, replacing existing entries + templatePackageToReplace.forEach((key) => { + appPackage[key] = templatePackage[key]; + }); + + fs.writeFileSync( + path.join(appPath, "package.json"), + JSON.stringify(appPackage, null, 2) + os.EOL + ); +} + +export default async function init( + rootDir: string, + siteName?: string, + reqTemplate?: string, + cliOptions: Partial<{ + useNpm: boolean; + skipInstall: boolean; + typescript: boolean; + }> = {} +): Promise { + const useYarn = cliOptions.useNpm ? false : hasYarn(); + + let name = siteName; + + // Prompt if siteName is not passed from CLI. + if (!name) { + const prompt = await prompts({ + type: "text", + name: "name", + message: "What should we name this site?", + initial: "website", + }); + name = prompt.name; + } + + if (!name) { + logger.error("A website name is required."); + process.exit(1); + } + + const dest = path.resolve(rootDir, name); + if (fs.existsSync(dest)) { + logger.error`Directory already exists at path=${dest}!`; + process.exit(1); + } + + let template = reqTemplate ?? RecommendedTemplate; + + logger.info("Creating new Docusaurus project..."); + + if (isValidGitRepoUrl(template)) { + logger.info`Cloning Git template path=${template}...`; + if ( + shell.exec(`git clone --recursive ${template} ${dest}`, { silent: true }) + .code !== 0 + ) { + logger.error`Cloning Git template name=${template} failed!`; + process.exit(1); + } + } else if (fs.existsSync(path.resolve(process.cwd(), template))) { + const templateDir = path.resolve(process.cwd(), template); + try { + await fs.copy(templateDir, dest); + } catch (err) { + logger.error`Copying local template path=${templateDir} failed!`; + throw err; + } + } else { + const appName = path.basename(dest); + fs.ensureDirSync(name); + const packageJson = { + name: appName, + version: "0.1.0", + private: true, + }; + fs.writeFileSync( + path.join(dest, "package.json"), + JSON.stringify(packageJson, null, 2) + os.EOL + ); + + const originalDirectory = process.cwd(); + const templatePackageName = getTemplateInstallPackage( + template, + originalDirectory + ); + const templateInfo = getPackageInfo(templatePackageName); + const templateName = templateInfo.name; + + shell.exec( + `cd "${name}" && ${ + useYarn ? "yarn add" : "npm install --color always" + } ${templatePackageName}`, + { + env: { + ...process.env, + // Force coloring the output, since the command is invoked by shelljs, which is not the interactive shell + ...(supportsColor.stdout ? { FORCE_COLOR: "1" } : {}), + }, + } + ); + + const templatePath = path.dirname( + require.resolve(`${templateName}/package.json`, { paths: [dest] }) + ); + + createPackageJson(dest, templateName); + + const templateDir = path.join(templatePath, "template"); + if (fs.existsSync(templateDir)) { + fs.copySync(templateDir, dest); + } else { + logger.error("Could not locate supplied template."); + process.exit(1); + } + + shell.exec( + `cd "${name}" && ${ + useYarn ? "yarn remove" : "npm uninstall --color always" + } ${templateName}`, + { + env: { + ...process.env, + // Force coloring the output, since the command is invoked by shelljs, which is not the interactive shell + ...(supportsColor.stdout ? { FORCE_COLOR: "1" } : {}), + }, + } + ); + } + + // Update package.json info. + try { + await updatePkg(path.join(dest, "package.json"), { + name: kebabCase(name), + version: "0.0.0", + private: true, + }); + } catch (err) { + logger.error("Failed to update package.json."); + throw err; + } + + // We need to rename the gitignore file to .gitignore + if ( + !fs.pathExistsSync(path.join(dest, ".gitignore")) && + fs.pathExistsSync(path.join(dest, "gitignore")) + ) { + await fs.move(path.join(dest, "gitignore"), path.join(dest, ".gitignore")); + } + if (fs.pathExistsSync(path.join(dest, "gitignore"))) { + fs.removeSync(path.join(dest, "gitignore")); + } + + const pkgManager = useYarn ? "yarn" : "npm"; + // Display the most elegant way to cd. + const cdpath = + path.join(process.cwd(), name) === dest + ? name + : path.relative(process.cwd(), name); + if (!cliOptions.skipInstall) { + logger.info`Installing dependencies with name=${pkgManager}...`; + if ( + shell.exec( + `cd "${name}" && ${useYarn ? "yarn" : "npm install --color always"}`, + { + env: { + ...process.env, + // Force coloring the output, since the command is invoked by shelljs, which is not the interactive shell + ...(supportsColor.stdout ? { FORCE_COLOR: "1" } : {}), + }, + } + ).code !== 0 + ) { + logger.error("Dependency installation failed."); + logger.info`The site directory has already been created, and you can retry by typing: + + code=${`cd ${cdpath}`} + code=${`${pkgManager} install`}`; + process.exit(0); + } + } + + logger.success`Created path=${cdpath}.`; + logger.info`Inside that directory, you can run several commands: + + code=${`${pkgManager} start`} + Starts the development server. + + code=${`${pkgManager} ${useYarn ? "" : "run "}build`} + Bundles your website into static files for production. + + code=${`${pkgManager} ${useYarn ? "" : "run "}serve`} + Serves the built website locally. + + code=${`${pkgManager} deploy`} + Publishes the website to GitHub pages. + +We recommend that you begin by typing: + + code=${`cd ${cdpath}`} + code=${`${pkgManager} start`} + +Happy building awesome websites! +`; +} diff --git a/packages/create-docusaurus-openapi/tsconfig.json b/packages/create-docusaurus-openapi/tsconfig.json new file mode 100644 index 00000000..84575cb5 --- /dev/null +++ b/packages/create-docusaurus-openapi/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "lib" + }, + "include": ["src"] +} diff --git a/packages/docusaurus-plugin-openapi/README.md b/packages/docusaurus-plugin-openapi/README.md new file mode 100644 index 00000000..a479b124 --- /dev/null +++ b/packages/docusaurus-plugin-openapi/README.md @@ -0,0 +1,61 @@ +

Docusaurus OpenAPI

+ +
+ +OpenAPI plugin for generating API reference docs in Docusaurus v2. + +
+ +

+ +![](https://user-images.githubusercontent.com/4212769/85324376-b9e3d900-b497-11ea-9765-c42a8ad1ff61.png) + +

+ +## Multiple OpenAPI Definitions + +To have more than one OpenAPI pages, add additional OpenAPI plugin instances: + +```js +/* docusaurus.config.js */ + +{ + presets: [ + [ + 'docusaurus-preset-openapi', + { + api: { + // id: 'cars', // omitted => default instance + path: 'cars/openapi.json', + routeBasePath: 'cars', + // ... other options + }, + }, + ], + ], + plugins: [ + [ + 'docusaurus-plugin-openapi', + { + id: 'trains', + path: 'trains/openapi.json', + routeBasePath: 'trains', + // ... other options + }, + ], + [ + 'docusaurus-plugin-openapi', + { + id: 'bikes', + path: 'bikes/openapi.json', + routeBasePath: 'bikes', + // ... other options + }, + ], + ], +} +``` + +This will create routes for `/cars`, `/trains` and `/bikes`. + +> **Note:** One instance of the plugin is included in the preset. All additional plugin instances will require an `id`. diff --git a/packages/docusaurus-template-openapi/README.md b/packages/docusaurus-template-openapi/README.md new file mode 100644 index 00000000..b439ec88 --- /dev/null +++ b/packages/docusaurus-template-openapi/README.md @@ -0,0 +1 @@ +# docusaurus-template-openapi diff --git a/packages/docusaurus-template-openapi/package.json b/packages/docusaurus-template-openapi/package.json new file mode 100644 index 00000000..8929cb62 --- /dev/null +++ b/packages/docusaurus-template-openapi/package.json @@ -0,0 +1,27 @@ +{ + "name": "docusaurus-template-openapi", + "version": "0.4.0", + "keywords": [ + "react", + "create-docusaurus", + "template", + "openapi" + ], + "description": "OpenAPI template for Docusaurus.", + "repository": { + "type": "git", + "url": "https://github.com/cloud-annotations/docusaurus-openapi.git", + "directory": "packages/docusaurus-template-openapi" + }, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "bugs": { + "url": "https://github.com/cloud-annotations/docusaurus-openapi/issues" + }, + "files": [ + "template", + "template.json" + ] +} diff --git a/packages/docusaurus-template-openapi/template.json b/packages/docusaurus-template-openapi/template.json new file mode 100644 index 00000000..5f4fc496 --- /dev/null +++ b/packages/docusaurus-template-openapi/template.json @@ -0,0 +1,35 @@ +{ + "package": { + "name": "demo", + "version": "0.4.0", + "private": true, + "scripts": { + "docusaurus": "docusaurus", + "start": "docusaurus start", + "build": "docusaurus build", + "swizzle": "docusaurus swizzle", + "deploy": "docusaurus deploy", + "clear": "docusaurus clear", + "serve": "docusaurus serve", + "write-translations": "docusaurus write-translations", + "write-heading-ids": "docusaurus write-heading-ids" + }, + "dependencies": { + "@docusaurus/core": "2.0.0-beta.14", + "docusaurus-preset-openapi": "0.4.0", + "@mdx-js/react": "^1.6.21", + "clsx": "^1.1.1", + "prism-react-renderer": "^1.2.1", + "react": "^17.0.1", + "react-dom": "^17.0.1" + }, + "browserslist": { + "production": [">0.5%", "not dead", "not op_mini all"], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } + } +} diff --git a/packages/docusaurus-template-openapi/template/README.md b/packages/docusaurus-template-openapi/template/README.md new file mode 100644 index 00000000..aaba2fa1 --- /dev/null +++ b/packages/docusaurus-template-openapi/template/README.md @@ -0,0 +1,41 @@ +# Website + +This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. + +### Installation + +``` +$ yarn +``` + +### Local Development + +``` +$ yarn start +``` + +This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. + +### Build + +``` +$ yarn build +``` + +This command generates static content into the `build` directory and can be served using any static contents hosting service. + +### Deployment + +Using SSH: + +``` +$ USE_SSH=true yarn deploy +``` + +Not using SSH: + +``` +$ GIT_USER= yarn deploy +``` + +If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. diff --git a/packages/docusaurus-template-openapi/template/babel.config.js b/packages/docusaurus-template-openapi/template/babel.config.js new file mode 100644 index 00000000..bfd75dbd --- /dev/null +++ b/packages/docusaurus-template-openapi/template/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: [require.resolve("@docusaurus/core/lib/babel/preset")], +}; diff --git a/demo/blog/2019-05-28-first-blog-post.md b/packages/docusaurus-template-openapi/template/blog/2019-05-28-first-blog-post.md similarity index 100% rename from demo/blog/2019-05-28-first-blog-post.md rename to packages/docusaurus-template-openapi/template/blog/2019-05-28-first-blog-post.md diff --git a/demo/blog/2019-05-29-long-blog-post.md b/packages/docusaurus-template-openapi/template/blog/2019-05-29-long-blog-post.md similarity index 100% rename from demo/blog/2019-05-29-long-blog-post.md rename to packages/docusaurus-template-openapi/template/blog/2019-05-29-long-blog-post.md diff --git a/demo/blog/2021-08-01-mdx-blog-post.mdx b/packages/docusaurus-template-openapi/template/blog/2021-08-01-mdx-blog-post.mdx similarity index 100% rename from demo/blog/2021-08-01-mdx-blog-post.mdx rename to packages/docusaurus-template-openapi/template/blog/2021-08-01-mdx-blog-post.mdx diff --git a/demo/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg b/packages/docusaurus-template-openapi/template/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg similarity index 100% rename from demo/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg rename to packages/docusaurus-template-openapi/template/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg diff --git a/demo/blog/2021-08-26-welcome/index.md b/packages/docusaurus-template-openapi/template/blog/2021-08-26-welcome/index.md similarity index 100% rename from demo/blog/2021-08-26-welcome/index.md rename to packages/docusaurus-template-openapi/template/blog/2021-08-26-welcome/index.md diff --git a/demo/blog/authors.yml b/packages/docusaurus-template-openapi/template/blog/authors.yml similarity index 100% rename from demo/blog/authors.yml rename to packages/docusaurus-template-openapi/template/blog/authors.yml diff --git a/packages/docusaurus-template-openapi/template/docs/intro.md b/packages/docusaurus-template-openapi/template/docs/intro.md new file mode 100644 index 00000000..8417aebe --- /dev/null +++ b/packages/docusaurus-template-openapi/template/docs/intro.md @@ -0,0 +1,35 @@ +--- +sidebar_position: 1 +--- + +# Tutorial Intro + +Let's discover **Docusaurus in less than 5 minutes**. + +## Getting Started + +Get started by **creating a new site**. + +Or **try Docusaurus immediately** with **[docusaurus.new](https://docusaurus.new)**. + +## Generate a new site + +Generate a new Docusaurus site using the **classic template**: + +```shell +npm init docusaurus@latest my-website classic +``` + +## Start your site + +Run the development server: + +```shell +cd my-website + +npx docusaurus start +``` + +Your site starts at `http://localhost:3000`. + +Open `docs/intro.md` and edit some lines: the site **reloads automatically** and displays your changes. diff --git a/packages/docusaurus-template-openapi/template/docs/tutorial-basics/_category_.json b/packages/docusaurus-template-openapi/template/docs/tutorial-basics/_category_.json new file mode 100644 index 00000000..135e4a68 --- /dev/null +++ b/packages/docusaurus-template-openapi/template/docs/tutorial-basics/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Tutorial - Basics", + "position": 2 +} diff --git a/packages/docusaurus-template-openapi/template/docs/tutorial-basics/congratulations.md b/packages/docusaurus-template-openapi/template/docs/tutorial-basics/congratulations.md new file mode 100644 index 00000000..9ef99bba --- /dev/null +++ b/packages/docusaurus-template-openapi/template/docs/tutorial-basics/congratulations.md @@ -0,0 +1,21 @@ +--- +sidebar_position: 6 +--- + +# Congratulations! + +You have just learned the **basics of Docusaurus** and made some changes to the **initial template**. + +Docusaurus has **much more to offer**! + +Have **5 more minutes**? Take a look at **[versioning](../tutorial-extras/manage-docs-versions.md)** and **[i18n](../tutorial-extras/translate-your-site.md)**. + +Anything **unclear** or **buggy** in this tutorial? [Please report it!](https://github.com/facebook/docusaurus/discussions/4610) + +## What's next? + +- Read the [official documentation](https://docusaurus.io/). +- Add a custom [Design and Layout](https://docusaurus.io/docs/styling-layout) +- Add a [search bar](https://docusaurus.io/docs/search) +- Find inspirations in the [Docusaurus showcase](https://docusaurus.io/showcase) +- Get involved in the [Docusaurus Community](https://docusaurus.io/community/support) diff --git a/packages/docusaurus-template-openapi/template/docs/tutorial-basics/create-a-blog-post.md b/packages/docusaurus-template-openapi/template/docs/tutorial-basics/create-a-blog-post.md new file mode 100644 index 00000000..0d50aaf3 --- /dev/null +++ b/packages/docusaurus-template-openapi/template/docs/tutorial-basics/create-a-blog-post.md @@ -0,0 +1,34 @@ +--- +sidebar_position: 3 +--- + +# Create a Blog Post + +Docusaurus creates a **page for each blog post**, but also a **blog index page**, a **tag system**, an **RSS** feed... + +## Create your first Post + +Create a file at `blog/2021-02-28-greetings.md`: + +```md title="blog/2021-02-28-greetings.md" +--- +slug: greetings +title: Greetings! +authors: + - name: Joel Marcey + title: Co-creator of Docusaurus 1 + url: https://github.com/JoelMarcey + image_url: https://github.com/JoelMarcey.png + - name: SΓ©bastien Lorber + title: Docusaurus maintainer + url: https://sebastienlorber.com + image_url: https://github.com/slorber.png +tags: [greetings] +--- + +Congratulations, you have made your first post! + +Feel free to play around and edit this post as much you like. +``` + +A new blog post is now available at `http://localhost:3000/blog/greetings`. diff --git a/packages/docusaurus-template-openapi/template/docs/tutorial-basics/create-a-document.md b/packages/docusaurus-template-openapi/template/docs/tutorial-basics/create-a-document.md new file mode 100644 index 00000000..724449dc --- /dev/null +++ b/packages/docusaurus-template-openapi/template/docs/tutorial-basics/create-a-document.md @@ -0,0 +1,55 @@ +--- +sidebar_position: 2 +--- + +# Create a Document + +Documents are **groups of pages** connected through: + +- a **sidebar** +- **previous/next navigation** +- **versioning** + +## Create your first Doc + +Create a markdown file at `docs/hello.md`: + +```md title="docs/hello.md" +# Hello + +This is my **first Docusaurus document**! +``` + +A new document is now available at `http://localhost:3000/docs/hello`. + +## Configure the Sidebar + +Docusaurus automatically **creates a sidebar** from the `docs` folder. + +Add metadata to customize the sidebar label and position: + +```md title="docs/hello.md" {1-4} +--- +sidebar_label: "Hi!" +sidebar_position: 3 +--- + +# Hello + +This is my **first Docusaurus document**! +``` + +It is also possible to create your sidebar explicitly in `sidebars.js`: + +```diff title="sidebars.js" +module.exports = { + tutorialSidebar: [ + { + type: 'category', + label: 'Tutorial', +- items: [...], ++ items: ['hello'], + }, + ], +}; +``` diff --git a/packages/docusaurus-template-openapi/template/docs/tutorial-basics/create-a-page.md b/packages/docusaurus-template-openapi/template/docs/tutorial-basics/create-a-page.md new file mode 100644 index 00000000..da823040 --- /dev/null +++ b/packages/docusaurus-template-openapi/template/docs/tutorial-basics/create-a-page.md @@ -0,0 +1,43 @@ +--- +sidebar_position: 1 +--- + +# Create a Page + +Add **Markdown or React** files to `src/pages` to create a **standalone page**: + +- `src/pages/index.js` -> `localhost:3000/` +- `src/pages/foo.md` -> `localhost:3000/foo` +- `src/pages/foo/bar.js` -> `localhost:3000/foo/bar` + +## Create your first React Page + +Create a file at `src/pages/my-react-page.js`: + +```jsx title="src/pages/my-react-page.js" +import React from "react"; +import Layout from "@theme/Layout"; + +export default function MyReactPage() { + return ( + +

My React page

+

This is a React page

+
+ ); +} +``` + +A new page is now available at `http://localhost:3000/my-react-page`. + +## Create your first Markdown Page + +Create a file at `src/pages/my-markdown-page.md`: + +```mdx title="src/pages/my-markdown-page.md" +# My Markdown page + +This is a Markdown page +``` + +A new page is now available at `http://localhost:3000/my-markdown-page`. diff --git a/packages/docusaurus-template-openapi/template/docs/tutorial-basics/deploy-your-site.md b/packages/docusaurus-template-openapi/template/docs/tutorial-basics/deploy-your-site.md new file mode 100644 index 00000000..492eae02 --- /dev/null +++ b/packages/docusaurus-template-openapi/template/docs/tutorial-basics/deploy-your-site.md @@ -0,0 +1,31 @@ +--- +sidebar_position: 5 +--- + +# Deploy your site + +Docusaurus is a **static-site-generator** (also called **[Jamstack](https://jamstack.org/)**). + +It builds your site as simple **static HTML, JavaScript and CSS files**. + +## Build your site + +Build your site **for production**: + +```bash +npm run build +``` + +The static files are generated in the `build` folder. + +## Deploy your site + +Test your production build locally: + +```bash +npm run serve +``` + +The `build` folder is now served at `http://localhost:3000/`. + +You can now deploy the `build` folder **almost anywhere** easily, **for free** or very small cost (read the **[Deployment Guide](https://docusaurus.io/docs/deployment)**). diff --git a/packages/docusaurus-template-openapi/template/docs/tutorial-basics/markdown-features.mdx b/packages/docusaurus-template-openapi/template/docs/tutorial-basics/markdown-features.mdx new file mode 100644 index 00000000..193722c6 --- /dev/null +++ b/packages/docusaurus-template-openapi/template/docs/tutorial-basics/markdown-features.mdx @@ -0,0 +1,145 @@ +--- +sidebar_position: 4 +--- + +# Markdown Features + +Docusaurus supports **[Markdown](https://daringfireball.net/projects/markdown/syntax)** and a few **additional features**. + +## Front Matter + +Markdown documents have metadata at the top called [Front Matter](https://jekyllrb.com/docs/front-matter/): + +```text title="my-doc.md" +// highlight-start +--- +id: my-doc-id +title: My document title +description: My document description +slug: /my-custom-url +--- +// highlight-end + +## Markdown heading + +Markdown text with [links](./hello.md) +``` + +## Links + +Regular Markdown links are supported, using url paths or relative file paths. + +```md +Let's see how to [Create a page](/create-a-page). +``` + +```md +Let's see how to [Create a page](./create-a-page.md). +``` + +**Result:** Let's see how to [Create a page](./create-a-page.md). + +## Images + +Regular Markdown images are supported. + +Add an image at `static/img/docusaurus.png` and display it in Markdown: + +```md +![Docusaurus logo](/img/docusaurus.png) +``` + +![Docusaurus logo](/img/docusaurus.png) + +## Code Blocks + +Markdown code blocks are supported with Syntax highlighting. + + ```jsx title="src/components/HelloDocusaurus.js" + function HelloDocusaurus() { + return ( +

Hello, Docusaurus!

+ ) + } + ``` + +```jsx title="src/components/HelloDocusaurus.js" +function HelloDocusaurus() { + return

Hello, Docusaurus!

; +} +``` + +## Admonitions + +Docusaurus has a special syntax to create admonitions and callouts: + + :::tip My tip + + Use this awesome feature option + + ::: + + :::danger Take care + + This action is dangerous + + ::: + +:::tip My tip + +Use this awesome feature option + +::: + +:::danger Take care + +This action is dangerous + +::: + +## MDX and React Components + +[MDX](https://mdxjs.com/) can make your documentation more **interactive** and allows using any **React components inside Markdown**: + +```jsx +export const Highlight = ({children, color}) => ( + { + alert(`You clicked the color ${color} with label ${children}`) + }}> + {children} + +); + +This is Docusaurus green ! + +This is Facebook blue ! +``` + +export const Highlight = ({ children, color }) => ( + { + alert(`You clicked the color ${color} with label ${children}`); + }} + > + {children} + +); + +This is Docusaurus green ! + +This is Facebook blue ! diff --git a/packages/docusaurus-template-openapi/template/docs/tutorial-extras/_category_.json b/packages/docusaurus-template-openapi/template/docs/tutorial-extras/_category_.json new file mode 100644 index 00000000..ca3f8e06 --- /dev/null +++ b/packages/docusaurus-template-openapi/template/docs/tutorial-extras/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Tutorial - Extras", + "position": 3 +} diff --git a/packages/docusaurus-template-openapi/template/docs/tutorial-extras/manage-docs-versions.md b/packages/docusaurus-template-openapi/template/docs/tutorial-extras/manage-docs-versions.md new file mode 100644 index 00000000..ef0b41bd --- /dev/null +++ b/packages/docusaurus-template-openapi/template/docs/tutorial-extras/manage-docs-versions.md @@ -0,0 +1,55 @@ +--- +sidebar_position: 1 +--- + +# Manage Docs Versions + +Docusaurus can manage multiple versions of your docs. + +## Create a docs version + +Release a version 1.0 of your project: + +```bash +npm run docusaurus docs:version 1.0 +``` + +The `docs` folder is copied into `versioned_docs/version-1.0` and `versions.json` is created. + +Your docs now have 2 versions: + +- `1.0` at `http://localhost:3000/docs/` for the version 1.0 docs +- `current` at `http://localhost:3000/docs/next/` for the **upcoming, unreleased docs** + +## Add a Version Dropdown + +To navigate seamlessly across versions, add a version dropdown. + +Modify the `docusaurus.config.js` file: + +```js title="docusaurus.config.js" +module.exports = { + themeConfig: { + navbar: { + items: [ + // highlight-start + { + type: "docsVersionDropdown", + }, + // highlight-end + ], + }, + }, +}; +``` + +The docs version dropdown appears in your navbar: + +![Docs Version Dropdown](/img/tutorial/docsVersionDropdown.png) + +## Update an existing version + +It is possible to edit versioned docs in their respective folder: + +- `versioned_docs/version-1.0/hello.md` updates `http://localhost:3000/docs/hello` +- `docs/hello.md` updates `http://localhost:3000/docs/next/hello` diff --git a/packages/docusaurus-template-openapi/template/docs/tutorial-extras/translate-your-site.md b/packages/docusaurus-template-openapi/template/docs/tutorial-extras/translate-your-site.md new file mode 100644 index 00000000..2ae2d340 --- /dev/null +++ b/packages/docusaurus-template-openapi/template/docs/tutorial-extras/translate-your-site.md @@ -0,0 +1,88 @@ +--- +sidebar_position: 2 +--- + +# Translate your site + +Let's translate `docs/intro.md` to French. + +## Configure i18n + +Modify `docusaurus.config.js` to add support for the `fr` locale: + +```js title="docusaurus.config.js" +module.exports = { + i18n: { + defaultLocale: "en", + locales: ["en", "fr"], + }, +}; +``` + +## Translate a doc + +Copy the `docs/intro.md` file to the `i18n/fr` folder: + +```bash +mkdir -p i18n/fr/docusaurus-plugin-content-docs/current/ + +cp docs/intro.md i18n/fr/docusaurus-plugin-content-docs/current/intro.md +``` + +Translate `i18n/fr/docusaurus-plugin-content-docs/current/intro.md` in French. + +## Start your localized site + +Start your site on the French locale: + +```bash +npm run start -- --locale fr +``` + +Your localized site is accessible at `http://localhost:3000/fr/` and the `Getting Started` page is translated. + +:::caution + +In development, you can only use one locale at a same time. + +::: + +## Add a Locale Dropdown + +To navigate seamlessly across languages, add a locale dropdown. + +Modify the `docusaurus.config.js` file: + +```js title="docusaurus.config.js" +module.exports = { + themeConfig: { + navbar: { + items: [ + // highlight-start + { + type: "localeDropdown", + }, + // highlight-end + ], + }, + }, +}; +``` + +The locale dropdown now appears in your navbar: + +![Locale Dropdown](/img/tutorial/localeDropdown.png) + +## Build your localized site + +Build your site for a specific locale: + +```bash +npm run build -- --locale fr +``` + +Or build your site to include all the locales at once: + +```bash +npm run build +``` diff --git a/packages/docusaurus-template-openapi/template/docusaurus.config.js b/packages/docusaurus-template-openapi/template/docusaurus.config.js new file mode 100644 index 00000000..d0ca64b8 --- /dev/null +++ b/packages/docusaurus-template-openapi/template/docusaurus.config.js @@ -0,0 +1,120 @@ +// @ts-check +// Note: type annotations allow type checking and IDEs autocompletion + +const lightCodeTheme = require("prism-react-renderer/themes/github"); +const darkCodeTheme = require("prism-react-renderer/themes/dracula"); + +/** @type {import('@docusaurus/types').Config} */ +const config = { + title: "My Site", + tagline: "Dinosaurs are cool", + url: "https://your-docusaurus-test-site.com", + baseUrl: "/", + onBrokenLinks: "throw", + onBrokenMarkdownLinks: "warn", + favicon: "img/favicon.ico", + organizationName: "facebook", // Usually your GitHub org/user name. + projectName: "docusaurus", // Usually your repo name. + + presets: [ + [ + "docusaurus-preset-openapi", + /** @type {import('docusaurus-preset-openapi').Options} */ + ({ + docs: { + sidebarPath: require.resolve("./sidebars.js"), + // Please change this to your repo. + editUrl: + "https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/", + }, + blog: { + showReadingTime: true, + // Please change this to your repo. + editUrl: + "https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/", + }, + theme: { + customCss: require.resolve("./src/css/custom.css"), + }, + }), + ], + ], + + themeConfig: + /** @type {import('docusaurus-preset-openapi').ThemeConfig} */ + ({ + navbar: { + title: "My Site", + logo: { + alt: "My Site Logo", + src: "img/logo.svg", + }, + items: [ + { + type: "doc", + docId: "intro", + position: "left", + label: "Tutorial", + }, + { to: "/api", label: "API", position: "left" }, + { to: "/blog", label: "Blog", position: "left" }, + { + href: "https://github.com/facebook/docusaurus", + label: "GitHub", + position: "right", + }, + ], + }, + footer: { + style: "dark", + links: [ + { + title: "Docs", + items: [ + { + label: "Tutorial", + to: "/docs/intro", + }, + ], + }, + { + title: "Community", + items: [ + { + label: "Stack Overflow", + href: "https://stackoverflow.com/questions/tagged/docusaurus", + }, + { + label: "Discord", + href: "https://discordapp.com/invite/docusaurus", + }, + { + label: "Twitter", + href: "https://twitter.com/docusaurus", + }, + ], + }, + { + title: "More", + items: [ + { + label: "Blog", + to: "/blog", + }, + { + label: "GitHub", + href: "https://github.com/facebook/docusaurus", + }, + ], + }, + ], + copyright: `Copyright Β© ${new Date().getFullYear()} My Project, Inc. Built with Docusaurus.`, + }, + prism: { + theme: lightCodeTheme, + darkTheme: darkCodeTheme, + }, + }), +}; + +module.exports = config; diff --git a/packages/docusaurus-template-openapi/template/gitignore b/packages/docusaurus-template-openapi/template/gitignore new file mode 100644 index 00000000..b2d6de30 --- /dev/null +++ b/packages/docusaurus-template-openapi/template/gitignore @@ -0,0 +1,20 @@ +# Dependencies +/node_modules + +# Production +/build + +# Generated files +.docusaurus +.cache-loader + +# Misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/packages/docusaurus-template-openapi/template/openapi.json b/packages/docusaurus-template-openapi/template/openapi.json new file mode 100644 index 00000000..ece4f7be --- /dev/null +++ b/packages/docusaurus-template-openapi/template/openapi.json @@ -0,0 +1,165 @@ +{ + "openapi": "3.0.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "license": { + "name": "MIT" + } + }, + "servers": [ + { + "url": "http://petstore.swagger.io/v1" + } + ], + "paths": { + "/pets": { + "get": { + "summary": "List all pets", + "operationId": "listPets", + "tags": ["pets"], + "parameters": [ + { + "name": "limit", + "in": "query", + "description": "How many items to return at one time (max 100)", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "A paged array of pets", + "headers": { + "x-next": { + "description": "A link to the next page of responses", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pets" + } + } + } + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + }, + "post": { + "summary": "Create a pet", + "operationId": "createPets", + "tags": ["pets"], + "responses": { + "201": { + "description": "Null response" + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/pets/{petId}": { + "get": { + "summary": "Info for a specific pet", + "operationId": "showPetById", + "tags": ["pets"], + "parameters": [ + { + "name": "petId", + "in": "path", + "required": true, + "description": "The id of the pet to retrieve", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Expected response to a valid request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Pet": { + "type": "object", + "required": ["id", "name"], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "Pets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + }, + "Error": { + "type": "object", + "required": ["code", "message"], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + } +} diff --git a/packages/docusaurus-template-openapi/template/sidebars.js b/packages/docusaurus-template-openapi/template/sidebars.js new file mode 100644 index 00000000..96621506 --- /dev/null +++ b/packages/docusaurus-template-openapi/template/sidebars.js @@ -0,0 +1,31 @@ +/** + * Creating a sidebar enables you to: + - create an ordered group of docs + - render a sidebar for each doc of that group + - provide next/previous navigation + + The sidebars can be generated from the filesystem, or explicitly defined here. + + Create as many sidebars as you want. + */ + +// @ts-check + +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const sidebars = { + // By default, Docusaurus generates a sidebar from the docs folder structure + tutorialSidebar: [{ type: "autogenerated", dirName: "." }], + + // But you can create a sidebar manually + /* + tutorialSidebar: [ + { + type: 'category', + label: 'Tutorial', + items: ['hello'], + }, + ], + */ +}; + +module.exports = sidebars; diff --git a/packages/docusaurus-template-openapi/template/src/components/HomepageFeatures.js b/packages/docusaurus-template-openapi/template/src/components/HomepageFeatures.js new file mode 100644 index 00000000..b3039222 --- /dev/null +++ b/packages/docusaurus-template-openapi/template/src/components/HomepageFeatures.js @@ -0,0 +1,64 @@ +import React from "react"; +import clsx from "clsx"; +import styles from "./HomepageFeatures.module.css"; + +const FeatureList = [ + { + title: "Easy to Use", + Svg: require("../../static/img/undraw_docusaurus_mountain.svg").default, + description: ( + <> + Docusaurus was designed from the ground up to be easily installed and + used to get your website up and running quickly. + + ), + }, + { + title: "Focus on What Matters", + Svg: require("../../static/img/undraw_docusaurus_tree.svg").default, + description: ( + <> + Docusaurus lets you focus on your docs, and we'll do the chores. Go + ahead and move your docs into the docs directory. + + ), + }, + { + title: "Powered by React", + Svg: require("../../static/img/undraw_docusaurus_react.svg").default, + description: ( + <> + Extend or customize your website layout by reusing React. Docusaurus can + be extended while reusing the same header and footer. + + ), + }, +]; + +function Feature({ Svg, title, description }) { + return ( +
+
+ +
+
+

{title}

+

{description}

+
+
+ ); +} + +export default function HomepageFeatures() { + return ( +
+
+
+ {FeatureList.map((props, idx) => ( + + ))} +
+
+
+ ); +} diff --git a/packages/docusaurus-template-openapi/template/src/components/HomepageFeatures.module.css b/packages/docusaurus-template-openapi/template/src/components/HomepageFeatures.module.css new file mode 100644 index 00000000..b248eb2e --- /dev/null +++ b/packages/docusaurus-template-openapi/template/src/components/HomepageFeatures.module.css @@ -0,0 +1,11 @@ +.features { + display: flex; + align-items: center; + padding: 2rem 0; + width: 100%; +} + +.featureSvg { + height: 200px; + width: 200px; +} diff --git a/packages/docusaurus-template-openapi/template/src/css/custom.css b/packages/docusaurus-template-openapi/template/src/css/custom.css new file mode 100644 index 00000000..51608a72 --- /dev/null +++ b/packages/docusaurus-template-openapi/template/src/css/custom.css @@ -0,0 +1,28 @@ +/** + * Any CSS included here will be global. The classic template + * bundles Infima by default. Infima is a CSS framework designed to + * work well for content-centric websites. + */ + +/* You can override the default Infima variables here. */ +:root { + --ifm-color-primary: #25c2a0; + --ifm-color-primary-dark: rgb(33, 175, 144); + --ifm-color-primary-darker: rgb(31, 165, 136); + --ifm-color-primary-darkest: rgb(26, 136, 112); + --ifm-color-primary-light: rgb(70, 203, 174); + --ifm-color-primary-lighter: rgb(102, 212, 189); + --ifm-color-primary-lightest: rgb(146, 224, 208); + --ifm-code-font-size: 95%; +} + +.docusaurus-highlight-code-line { + background-color: rgba(0, 0, 0, 0.1); + display: block; + margin: 0 calc(-1 * var(--ifm-pre-padding)); + padding: 0 var(--ifm-pre-padding); +} + +html[data-theme="dark"] .docusaurus-highlight-code-line { + background-color: rgba(0, 0, 0, 0.3); +} diff --git a/packages/docusaurus-template-openapi/template/src/pages/index.js b/packages/docusaurus-template-openapi/template/src/pages/index.js new file mode 100644 index 00000000..ebfe6f0a --- /dev/null +++ b/packages/docusaurus-template-openapi/template/src/pages/index.js @@ -0,0 +1,42 @@ +import React from "react"; +import clsx from "clsx"; +import Layout from "@theme/Layout"; +import Link from "@docusaurus/Link"; +import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; +import styles from "./index.module.css"; +import HomepageFeatures from "../components/HomepageFeatures"; + +function HomepageHeader() { + const { siteConfig } = useDocusaurusContext(); + return ( +
+
+

{siteConfig.title}

+

{siteConfig.tagline}

+
+ + Docusaurus Tutorial - 5min ⏱️ + +
+
+
+ ); +} + +export default function Home() { + const { siteConfig } = useDocusaurusContext(); + return ( + + +
+ +
+
+ ); +} diff --git a/packages/docusaurus-template-openapi/template/src/pages/index.module.css b/packages/docusaurus-template-openapi/template/src/pages/index.module.css new file mode 100644 index 00000000..666feb6a --- /dev/null +++ b/packages/docusaurus-template-openapi/template/src/pages/index.module.css @@ -0,0 +1,23 @@ +/** + * CSS files with the .module.css suffix will be treated as CSS modules + * and scoped locally. + */ + +.heroBanner { + padding: 4rem 0; + text-align: center; + position: relative; + overflow: hidden; +} + +@media screen and (max-width: 966px) { + .heroBanner { + padding: 2rem; + } +} + +.buttons { + display: flex; + align-items: center; + justify-content: center; +} diff --git a/packages/docusaurus-template-openapi/template/src/pages/markdown-page.md b/packages/docusaurus-template-openapi/template/src/pages/markdown-page.md new file mode 100644 index 00000000..9756c5b6 --- /dev/null +++ b/packages/docusaurus-template-openapi/template/src/pages/markdown-page.md @@ -0,0 +1,7 @@ +--- +title: Markdown page example +--- + +# Markdown page example + +You don't need React to write simple standalone pages. diff --git a/packages/docusaurus-template-openapi/template/static/.nojekyll b/packages/docusaurus-template-openapi/template/static/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/packages/docusaurus-template-openapi/template/static/img/docusaurus.png b/packages/docusaurus-template-openapi/template/static/img/docusaurus.png new file mode 100644 index 00000000..f458149e Binary files /dev/null and b/packages/docusaurus-template-openapi/template/static/img/docusaurus.png differ diff --git a/packages/docusaurus-template-openapi/template/static/img/favicon.ico b/packages/docusaurus-template-openapi/template/static/img/favicon.ico new file mode 100644 index 00000000..c01d54bc Binary files /dev/null and b/packages/docusaurus-template-openapi/template/static/img/favicon.ico differ diff --git a/packages/docusaurus-template-openapi/template/static/img/logo.svg b/packages/docusaurus-template-openapi/template/static/img/logo.svg new file mode 100644 index 00000000..9db6d0d0 --- /dev/null +++ b/packages/docusaurus-template-openapi/template/static/img/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/docusaurus-template-openapi/template/static/img/tutorial/docsVersionDropdown.png b/packages/docusaurus-template-openapi/template/static/img/tutorial/docsVersionDropdown.png new file mode 100644 index 00000000..ff1cbe68 Binary files /dev/null and b/packages/docusaurus-template-openapi/template/static/img/tutorial/docsVersionDropdown.png differ diff --git a/packages/docusaurus-template-openapi/template/static/img/tutorial/localeDropdown.png b/packages/docusaurus-template-openapi/template/static/img/tutorial/localeDropdown.png new file mode 100644 index 00000000..d7163f96 Binary files /dev/null and b/packages/docusaurus-template-openapi/template/static/img/tutorial/localeDropdown.png differ diff --git a/packages/docusaurus-template-openapi/template/static/img/undraw_docusaurus_mountain.svg b/packages/docusaurus-template-openapi/template/static/img/undraw_docusaurus_mountain.svg new file mode 100644 index 00000000..431cef2f --- /dev/null +++ b/packages/docusaurus-template-openapi/template/static/img/undraw_docusaurus_mountain.svg @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/docusaurus-template-openapi/template/static/img/undraw_docusaurus_react.svg b/packages/docusaurus-template-openapi/template/static/img/undraw_docusaurus_react.svg new file mode 100644 index 00000000..e4170504 --- /dev/null +++ b/packages/docusaurus-template-openapi/template/static/img/undraw_docusaurus_react.svg @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/docusaurus-template-openapi/template/static/img/undraw_docusaurus_tree.svg b/packages/docusaurus-template-openapi/template/static/img/undraw_docusaurus_tree.svg new file mode 100644 index 00000000..a05cc03d --- /dev/null +++ b/packages/docusaurus-template-openapi/template/static/img/undraw_docusaurus_tree.svg @@ -0,0 +1 @@ +docu_tree \ No newline at end of file diff --git a/packages/docusaurus-theme-openapi/src/theme/ApiDemoPanel/Curl/index.js b/packages/docusaurus-theme-openapi/src/theme/ApiDemoPanel/Curl/index.js index 18c34ed2..178c0bf3 100644 --- a/packages/docusaurus-theme-openapi/src/theme/ApiDemoPanel/Curl/index.js +++ b/packages/docusaurus-theme-openapi/src/theme/ApiDemoPanel/Curl/index.js @@ -8,6 +8,7 @@ import React, { useRef, useState, useEffect } from "react"; import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; +import clsx from "clsx"; import codegen from "postman-code-generators"; import Highlight, { defaultProps } from "prism-react-renderer"; import { useSelector } from "react-redux"; @@ -206,12 +207,16 @@ function Curl() { return ( <> -
+
{langs.map((lang) => { return (