├── .gitignore ├── apps └── classic │ ├── .gitignore │ ├── README.md │ ├── babel.config.js │ ├── blog │ ├── 2019-05-28-first-blog-post.md │ ├── 2019-05-29-long-blog-post.md │ ├── 2021-08-01-mdx-blog-post.mdx │ ├── 2021-08-26-welcome │ │ ├── docusaurus-plushie-banner.jpeg │ │ └── index.md │ └── authors.yml │ ├── docs │ ├── assets │ │ ├── banner.png │ │ ├── docu.docx │ │ └── logo.svg │ ├── intro.md │ ├── test.md │ ├── tutorial-basics │ │ ├── _category_.json │ │ ├── congratulations.md │ │ ├── create-a-blog-post.md │ │ ├── create-a-document.md │ │ ├── create-a-page.md │ │ ├── deploy-your-site.md │ │ └── markdown-features.mdx │ └── tutorial-extras │ │ ├── _category_.json │ │ ├── manage-docs-versions.md │ │ └── translate-your-site.md │ ├── docusaurus.config.js │ ├── package.json │ ├── sidebars.js │ ├── src │ ├── components │ │ └── HomepageFeatures │ │ │ ├── index.js │ │ │ └── styles.module.css │ ├── css │ │ └── custom.css │ └── pages │ │ ├── index.js │ │ ├── index.module.css │ │ └── markdown-page.md │ └── static │ ├── .nojekyll │ └── img │ ├── docusaurus.png │ ├── favicon.ico │ ├── logo.svg │ ├── tutorial │ ├── docsVersionDropdown.png │ └── localeDropdown.png │ ├── undraw_docusaurus_mountain.svg │ ├── undraw_docusaurus_react.svg │ └── undraw_docusaurus_tree.svg ├── package.json ├── packages ├── mdx-loader │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── mdx-loader.d.ts │ │ └── remark │ │ │ ├── codeCompat │ │ │ └── index.ts │ │ │ ├── headings │ │ │ └── index.ts │ │ │ ├── toc │ │ │ └── index.ts │ │ │ ├── transformImage │ │ │ └── index.ts │ │ │ ├── transformLinks │ │ │ └── index.ts │ │ │ ├── unwrapMdxCodeBlocks │ │ │ └── index.ts │ │ │ └── utils │ │ │ └── index.ts │ └── tsconfig.json └── theme │ ├── index.js │ ├── package.json │ └── theme │ └── MDXContent │ └── index.js ├── readme.md └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | .docusaurus 107 | packages/mdx-loader/lib -------------------------------------------------------------------------------- /apps/classic/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /apps/classic/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | Using SSH: 30 | 31 | ``` 32 | $ USE_SSH=true yarn deploy 33 | ``` 34 | 35 | Not using SSH: 36 | 37 | ``` 38 | $ GIT_USER= yarn deploy 39 | ``` 40 | 41 | 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. 42 | -------------------------------------------------------------------------------- /apps/classic/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /apps/classic/blog/2019-05-28-first-blog-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: first-blog-post 3 | title: First Blog Post 4 | authors: 5 | name: Gao Wei 6 | title: Docusaurus Core Team 7 | url: https://github.com/wgao19 8 | image_url: https://github.com/wgao19.png 9 | tags: [hola, docusaurus] 10 | --- 11 | 12 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 13 | -------------------------------------------------------------------------------- /apps/classic/blog/2019-05-29-long-blog-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: long-blog-post 3 | title: Long Blog Post 4 | authors: endi 5 | tags: [hello, docusaurus] 6 | --- 7 | 8 | This is the summary of a very long blog post, 9 | 10 | Use a `` comment to limit blog post size in the list view. 11 | 12 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 13 | 14 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 15 | 16 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 17 | 18 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 19 | 20 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 21 | 22 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 23 | 24 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 25 | 26 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 27 | 28 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 29 | 30 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 31 | 32 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 33 | 34 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 35 | 36 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 37 | 38 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 39 | 40 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 41 | 42 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 43 | -------------------------------------------------------------------------------- /apps/classic/blog/2021-08-01-mdx-blog-post.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | slug: mdx-blog-post 3 | title: MDX Blog Post 4 | authors: [slorber] 5 | tags: [docusaurus] 6 | --- 7 | 8 | Blog posts support [Docusaurus Markdown features](https://docusaurus.io/docs/markdown-features), such as [MDX](https://mdxjs.com/). 9 | 10 | :::tip 11 | 12 | Use the power of React to create interactive blog posts. 13 | 14 | ```js 15 | 16 | ``` 17 | 18 | 19 | 20 | ::: 21 | -------------------------------------------------------------------------------- /apps/classic/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomber/docusaurus-mdx-2/b2ae743cae46751dfa3024faa02e8d42f93b0c7b/apps/classic/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg -------------------------------------------------------------------------------- /apps/classic/blog/2021-08-26-welcome/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: welcome 3 | title: Welcome 4 | authors: [slorber, yangshun] 5 | tags: [facebook, hello, docusaurus] 6 | --- 7 | 8 | [Docusaurus blogging features](https://docusaurus.io/docs/blog) are powered by the [blog plugin](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog). 9 | 10 | Simply add Markdown files (or folders) to the `blog` directory. 11 | 12 | Regular blog authors can be added to `authors.yml`. 13 | 14 | The blog post date can be extracted from filenames, such as: 15 | 16 | - `2019-05-30-welcome.md` 17 | - `2019-05-30-welcome/index.md` 18 | 19 | A blog post folder can be convenient to co-locate blog post images: 20 | 21 | ![Docusaurus Plushie](./docusaurus-plushie-banner.jpeg) 22 | 23 | The blog supports tags as well! 24 | 25 | **And if you don't want a blog**: just delete this directory, and use `blog: false` in your Docusaurus config. 26 | -------------------------------------------------------------------------------- /apps/classic/blog/authors.yml: -------------------------------------------------------------------------------- 1 | endi: 2 | name: Endilie Yacop Sucipto 3 | title: Maintainer of Docusaurus 4 | url: https://github.com/endiliey 5 | image_url: https://github.com/endiliey.png 6 | 7 | yangshun: 8 | name: Yangshun Tay 9 | title: Front End Engineer @ Facebook 10 | url: https://github.com/yangshun 11 | image_url: https://github.com/yangshun.png 12 | 13 | slorber: 14 | name: Sébastien Lorber 15 | title: Docusaurus maintainer 16 | url: https://sebastienlorber.com 17 | image_url: https://github.com/slorber.png 18 | -------------------------------------------------------------------------------- /apps/classic/docs/assets/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomber/docusaurus-mdx-2/b2ae743cae46751dfa3024faa02e8d42f93b0c7b/apps/classic/docs/assets/banner.png -------------------------------------------------------------------------------- /apps/classic/docs/assets/docu.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomber/docusaurus-mdx-2/b2ae743cae46751dfa3024faa02e8d42f93b0c7b/apps/classic/docs/assets/docu.docx -------------------------------------------------------------------------------- /apps/classic/docs/assets/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/classic/docs/intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # MDX v2 Demo 6 | 7 | `Math.PI` is {Math.PI} 8 | 9 | [Tests](test) 10 | -------------------------------------------------------------------------------- /apps/classic/docs/test.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # MDX v2 Test 6 | 7 | import Tabs from '@theme/Tabs'; 8 | import TabItem from '@theme/TabItem'; 9 | 10 | 11 | 12 | 13 | ```js 14 | function helloWorld() { 15 | console.log("Hello, world!"); 16 | } 17 | ``` 18 | 19 | 20 | 21 | 22 | ```py 23 | def hello_world(): 24 | print("Hello, world!") 25 | ``` 26 | 27 | 28 | 29 | 30 | ```java 31 | class HelloWorld { 32 | public static void main(String args[]) { 33 | System.out.println("Hello, World"); 34 | } 35 | } 36 | ``` 37 | 38 | 39 | 40 | 41 | `Math.PI` is {Math.PI} 42 | 43 | ```js title="/src/components/HelloCodeTitle.js" {1,4-6,11} 44 | import React from "react"; 45 | 46 | function MyComponent(props) { 47 | if (props.isBar) { 48 | return
Bar
; 49 | } 50 | 51 | return
Foo
; 52 | } 53 | 54 | export default MyComponent; 55 | ``` 56 | 57 | Lorem ipsum. 58 | 59 | ### Explicit Id Heading \{#my-explicit-id} 60 | 61 | Lorem ipsum. 62 | 63 | ## Admonitions 64 | 65 | :::note 66 | 67 | Some **content** with _markdown_ `syntax`. Check [this `api`](#). 68 | 69 | ::: 70 | 71 | ## Start your site 72 | 73 | Run the development server: 74 | 75 | ```bash 76 | cd my-website 77 | npm run start 78 | ``` 79 | 80 | The `cd` command changes the directory you're working with. 81 | 82 | [Link to explicit heading](#my-explicit-id) 83 | 84 | ## Image test 85 | 86 | ![Example banner](./assets/banner.png) 87 | 88 | ## Files assets test 89 | 90 | Download this png 91 | 92 | or 93 | 94 | [Download this png using Markdown](./assets/banner.png) 95 | 96 | ## Inline SVG test 97 | 98 | import DocusaurusSvg from './assets/logo.svg'; 99 | 100 | 101 | 102 | import Admonition from '@theme/Admonition' 103 | 104 | # MDX v2 Demo 105 | 106 | ## Admonitions 107 | 108 | 109 | 110 | Some **content** with _markdown_ `syntax`. Check [this `api`](#). 111 | 112 | 113 | 114 | :::note 115 | 116 | Some **content** with _markdown_ `syntax`. Check [this `api`](#). 117 | 118 | ::: 119 | 120 | :::note Your Title 121 | 122 | Some **content** with _markdown_ `syntax`. 123 | 124 | ::: 125 | 126 | :::info 127 | 128 | Some **content** with _markdown_ `syntax`. Check [this `api`](#). 129 | 130 | ::: 131 | 132 | :::caution 133 | 134 | Some **content** with _markdown_ `syntax`. Check [this `api`](#). 135 | 136 | ::: 137 | 138 | :::danger 139 | 140 | Some **content** with _markdown_ `syntax`. Check [this `api`](#). 141 | 142 | ::: 143 | 144 | # GFM 145 | 146 | ## Autolink literals 147 | 148 | www.example.com, https://example.com, and contact@example.com. 149 | 150 | ## Footnote 151 | 152 | A note[^1] 153 | 154 | [^1]: Big note. 155 | 156 | ## Strikethrough 157 | 158 | ~one~ or ~~two~~ tildes. 159 | 160 | ## Table 161 | 162 | | aaaaaa | bbbbbbbbb | ccccc | dddd | 163 | | ------ | :-------- | ----: | :--: | 164 | | 1 | 2 | 3 | 4 | 165 | 166 | ## Tasklist 167 | 168 | - [ ] to do 169 | - [x] done 170 | -------------------------------------------------------------------------------- /apps/classic/docs/tutorial-basics/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Tutorial - Basics", 3 | "position": 2 4 | } 5 | -------------------------------------------------------------------------------- /apps/classic/docs/tutorial-basics/congratulations.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 6 3 | --- 4 | 5 | # Congratulations! 6 | 7 | You have just learned the **basics of Docusaurus** and made some changes to the **initial template**. 8 | 9 | Docusaurus has **much more to offer**! 10 | 11 | Have **5 more minutes**? Take a look at **[versioning](../tutorial-extras/manage-docs-versions.md)** and **[i18n](../tutorial-extras/translate-your-site.md)**. 12 | 13 | Anything **unclear** or **buggy** in this tutorial? [Please report it!](https://github.com/facebook/docusaurus/discussions/4610) 14 | 15 | ## What's next? 16 | 17 | - Read the [official documentation](https://docusaurus.io/). 18 | - Add a custom [Design and Layout](https://docusaurus.io/docs/styling-layout) 19 | - Add a [search bar](https://docusaurus.io/docs/search) 20 | - Find inspirations in the [Docusaurus showcase](https://docusaurus.io/showcase) 21 | - Get involved in the [Docusaurus Community](https://docusaurus.io/community/support) 22 | -------------------------------------------------------------------------------- /apps/classic/docs/tutorial-basics/create-a-blog-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | --- 4 | 5 | # Create a Blog Post 6 | 7 | Docusaurus creates a **page for each blog post**, but also a **blog index page**, a **tag system**, an **RSS** feed... 8 | 9 | ## Create your first Post 10 | 11 | Create a file at `blog/2021-02-28-greetings.md`: 12 | 13 | ```md title="blog/2021-02-28-greetings.md" 14 | --- 15 | slug: greetings 16 | title: Greetings! 17 | authors: 18 | - name: Joel Marcey 19 | title: Co-creator of Docusaurus 1 20 | url: https://github.com/JoelMarcey 21 | image_url: https://github.com/JoelMarcey.png 22 | - name: Sébastien Lorber 23 | title: Docusaurus maintainer 24 | url: https://sebastienlorber.com 25 | image_url: https://github.com/slorber.png 26 | tags: [greetings] 27 | --- 28 | 29 | Congratulations, you have made your first post! 30 | 31 | Feel free to play around and edit this post as much you like. 32 | ``` 33 | 34 | A new blog post is now available at `http://localhost:3000/blog/greetings`. 35 | -------------------------------------------------------------------------------- /apps/classic/docs/tutorial-basics/create-a-document.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Create a Document 6 | 7 | Documents are **groups of pages** connected through: 8 | 9 | - a **sidebar** 10 | - **previous/next navigation** 11 | - **versioning** 12 | 13 | ## Create your first Doc 14 | 15 | Create a markdown file at `docs/hello.md`: 16 | 17 | ```md title="docs/hello.md" 18 | # Hello 19 | 20 | This is my **first Docusaurus document**! 21 | ``` 22 | 23 | A new document is now available at `http://localhost:3000/docs/hello`. 24 | 25 | ## Configure the Sidebar 26 | 27 | Docusaurus automatically **creates a sidebar** from the `docs` folder. 28 | 29 | Add metadata to customize the sidebar label and position: 30 | 31 | ```md title="docs/hello.md" {1-4} 32 | --- 33 | sidebar_label: 'Hi!' 34 | sidebar_position: 3 35 | --- 36 | 37 | # Hello 38 | 39 | This is my **first Docusaurus document**! 40 | ``` 41 | 42 | It is also possible to create your sidebar explicitly in `sidebars.js`: 43 | 44 | ```js title="sidebars.js" 45 | module.exports = { 46 | tutorialSidebar: [ 47 | { 48 | type: 'category', 49 | label: 'Tutorial', 50 | // highlight-next-line 51 | items: ['hello'], 52 | }, 53 | ], 54 | }; 55 | ``` 56 | -------------------------------------------------------------------------------- /apps/classic/docs/tutorial-basics/create-a-page.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Create a Page 6 | 7 | Add **Markdown or React** files to `src/pages` to create a **standalone page**: 8 | 9 | - `src/pages/index.js` -> `localhost:3000/` 10 | - `src/pages/foo.md` -> `localhost:3000/foo` 11 | - `src/pages/foo/bar.js` -> `localhost:3000/foo/bar` 12 | 13 | ## Create your first React Page 14 | 15 | Create a file at `src/pages/my-react-page.js`: 16 | 17 | ```jsx title="src/pages/my-react-page.js" 18 | import React from 'react'; 19 | import Layout from '@theme/Layout'; 20 | 21 | export default function MyReactPage() { 22 | return ( 23 | 24 |

My React page

25 |

This is a React page

26 |
27 | ); 28 | } 29 | ``` 30 | 31 | A new page is now available at `http://localhost:3000/my-react-page`. 32 | 33 | ## Create your first Markdown Page 34 | 35 | Create a file at `src/pages/my-markdown-page.md`: 36 | 37 | ```mdx title="src/pages/my-markdown-page.md" 38 | # My Markdown page 39 | 40 | This is a Markdown page 41 | ``` 42 | 43 | A new page is now available at `http://localhost:3000/my-markdown-page`. 44 | -------------------------------------------------------------------------------- /apps/classic/docs/tutorial-basics/deploy-your-site.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 5 3 | --- 4 | 5 | # Deploy your site 6 | 7 | Docusaurus is a **static-site-generator** (also called **[Jamstack](https://jamstack.org/)**). 8 | 9 | It builds your site as simple **static HTML, JavaScript and CSS files**. 10 | 11 | ## Build your site 12 | 13 | Build your site **for production**: 14 | 15 | ```bash 16 | npm run build 17 | ``` 18 | 19 | The static files are generated in the `build` folder. 20 | 21 | ## Deploy your site 22 | 23 | Test your production build locally: 24 | 25 | ```bash 26 | npm run serve 27 | ``` 28 | 29 | The `build` folder is now served at `http://localhost:3000/`. 30 | 31 | 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)**). 32 | -------------------------------------------------------------------------------- /apps/classic/docs/tutorial-basics/markdown-features.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 4 3 | --- 4 | 5 | # Markdown Features 6 | 7 | Docusaurus supports **[Markdown](https://daringfireball.net/projects/markdown/syntax)** and a few **additional features**. 8 | 9 | ## Front Matter 10 | 11 | Markdown documents have metadata at the top called [Front Matter](https://jekyllrb.com/docs/front-matter/): 12 | 13 | ```text title="my-doc.md" 14 | // highlight-start 15 | --- 16 | id: my-doc-id 17 | title: My document title 18 | description: My document description 19 | slug: /my-custom-url 20 | --- 21 | // highlight-end 22 | 23 | ## Markdown heading 24 | 25 | Markdown text with [links](./hello.md) 26 | ``` 27 | 28 | ## Links 29 | 30 | Regular Markdown links are supported, using url paths or relative file paths. 31 | 32 | ```md 33 | Let's see how to [Create a page](/create-a-page). 34 | ``` 35 | 36 | ```md 37 | Let's see how to [Create a page](./create-a-page.md). 38 | ``` 39 | 40 | **Result:** Let's see how to [Create a page](./create-a-page.md). 41 | 42 | ## Images 43 | 44 | Regular Markdown images are supported. 45 | 46 | Add an image at `static/img/docusaurus.png` and display it in Markdown: 47 | 48 | ```md 49 | ![Docusaurus logo](/img/docusaurus.png) 50 | ``` 51 | 52 | ![Docusaurus logo](/img/docusaurus.png) 53 | 54 | ## Code Blocks 55 | 56 | Markdown code blocks are supported with Syntax highlighting. 57 | 58 | ```jsx title="src/components/HelloDocusaurus.js" 59 | function HelloDocusaurus() { 60 | return ( 61 |

Hello, Docusaurus!

62 | ) 63 | } 64 | ``` 65 | 66 | ```jsx title="src/components/HelloDocusaurus.js" 67 | function HelloDocusaurus() { 68 | return

Hello, Docusaurus!

; 69 | } 70 | ``` 71 | 72 | ## Admonitions 73 | 74 | Docusaurus has a special syntax to create admonitions and callouts: 75 | 76 | :::tip My tip 77 | 78 | Use this awesome feature option 79 | 80 | ::: 81 | 82 | :::danger Take care 83 | 84 | This action is dangerous 85 | 86 | ::: 87 | 88 | :::tip My tip 89 | 90 | Use this awesome feature option 91 | 92 | ::: 93 | 94 | :::danger Take care 95 | 96 | This action is dangerous 97 | 98 | ::: 99 | 100 | ## MDX and React Components 101 | 102 | [MDX](https://mdxjs.com/) can make your documentation more **interactive** and allows using any **React components inside Markdown**: 103 | 104 | ```jsx 105 | export const Highlight = ({children, color}) => ( 106 | { 115 | alert(`You clicked the color ${color} with label ${children}`) 116 | }}> 117 | {children} 118 | 119 | ); 120 | 121 | This is Docusaurus green ! 122 | 123 | This is Facebook blue ! 124 | ``` 125 | 126 | export const Highlight = ({children, color}) => ( 127 | { 136 | alert(`You clicked the color ${color} with label ${children}`); 137 | }}> 138 | {children} 139 | 140 | ); 141 | 142 | This is Docusaurus green ! 143 | 144 | This is Facebook blue ! 145 | -------------------------------------------------------------------------------- /apps/classic/docs/tutorial-extras/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Tutorial - Extras", 3 | "position": 3 4 | } 5 | -------------------------------------------------------------------------------- /apps/classic/docs/tutorial-extras/manage-docs-versions.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Manage Docs Versions 6 | 7 | Docusaurus can manage multiple versions of your docs. 8 | 9 | ## Create a docs version 10 | 11 | Release a version 1.0 of your project: 12 | 13 | ```bash 14 | npm run docusaurus docs:version 1.0 15 | ``` 16 | 17 | The `docs` folder is copied into `versioned_docs/version-1.0` and `versions.json` is created. 18 | 19 | Your docs now have 2 versions: 20 | 21 | - `1.0` at `http://localhost:3000/docs/` for the version 1.0 docs 22 | - `current` at `http://localhost:3000/docs/next/` for the **upcoming, unreleased docs** 23 | 24 | ## Add a Version Dropdown 25 | 26 | To navigate seamlessly across versions, add a version dropdown. 27 | 28 | Modify the `docusaurus.config.js` file: 29 | 30 | ```js title="docusaurus.config.js" 31 | module.exports = { 32 | themeConfig: { 33 | navbar: { 34 | items: [ 35 | // highlight-start 36 | { 37 | type: 'docsVersionDropdown', 38 | }, 39 | // highlight-end 40 | ], 41 | }, 42 | }, 43 | }; 44 | ``` 45 | 46 | The docs version dropdown appears in your navbar: 47 | 48 | ![Docs Version Dropdown](/img/tutorial/docsVersionDropdown.png) 49 | 50 | ## Update an existing version 51 | 52 | It is possible to edit versioned docs in their respective folder: 53 | 54 | - `versioned_docs/version-1.0/hello.md` updates `http://localhost:3000/docs/hello` 55 | - `docs/hello.md` updates `http://localhost:3000/docs/next/hello` 56 | -------------------------------------------------------------------------------- /apps/classic/docs/tutorial-extras/translate-your-site.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Translate your site 6 | 7 | Let's translate `docs/intro.md` to French. 8 | 9 | ## Configure i18n 10 | 11 | Modify `docusaurus.config.js` to add support for the `fr` locale: 12 | 13 | ```js title="docusaurus.config.js" 14 | module.exports = { 15 | i18n: { 16 | defaultLocale: 'en', 17 | locales: ['en', 'fr'], 18 | }, 19 | }; 20 | ``` 21 | 22 | ## Translate a doc 23 | 24 | Copy the `docs/intro.md` file to the `i18n/fr` folder: 25 | 26 | ```bash 27 | mkdir -p i18n/fr/docusaurus-plugin-content-docs/current/ 28 | 29 | cp docs/intro.md i18n/fr/docusaurus-plugin-content-docs/current/intro.md 30 | ``` 31 | 32 | Translate `i18n/fr/docusaurus-plugin-content-docs/current/intro.md` in French. 33 | 34 | ## Start your localized site 35 | 36 | Start your site on the French locale: 37 | 38 | ```bash 39 | npm run start -- --locale fr 40 | ``` 41 | 42 | Your localized site is accessible at `http://localhost:3000/fr/` and the `Getting Started` page is translated. 43 | 44 | :::caution 45 | 46 | In development, you can only use one locale at a same time. 47 | 48 | ::: 49 | 50 | ## Add a Locale Dropdown 51 | 52 | To navigate seamlessly across languages, add a locale dropdown. 53 | 54 | Modify the `docusaurus.config.js` file: 55 | 56 | ```js title="docusaurus.config.js" 57 | module.exports = { 58 | themeConfig: { 59 | navbar: { 60 | items: [ 61 | // highlight-start 62 | { 63 | type: 'localeDropdown', 64 | }, 65 | // highlight-end 66 | ], 67 | }, 68 | }, 69 | }; 70 | ``` 71 | 72 | The locale dropdown now appears in your navbar: 73 | 74 | ![Locale Dropdown](/img/tutorial/localeDropdown.png) 75 | 76 | ## Build your localized site 77 | 78 | Build your site for a specific locale: 79 | 80 | ```bash 81 | npm run build -- --locale fr 82 | ``` 83 | 84 | Or build your site to include all the locales at once: 85 | 86 | ```bash 87 | npm run build 88 | ``` 89 | -------------------------------------------------------------------------------- /apps/classic/docusaurus.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Note: type annotations allow type checking and IDEs autocompletion 3 | 4 | const lightCodeTheme = require("prism-react-renderer/themes/github"); 5 | const darkCodeTheme = require("prism-react-renderer/themes/dracula"); 6 | 7 | /** @type {import('@docusaurus/types').Config} */ 8 | const config = { 9 | title: "My Site", 10 | tagline: "MDX v2 Demo", 11 | url: "https://your-docusaurus-test-site.com", 12 | baseUrl: "/", 13 | onBrokenLinks: "throw", 14 | onBrokenMarkdownLinks: "warn", 15 | favicon: "img/favicon.ico", 16 | organizationName: "pomber", // Usually your GitHub org/user name. 17 | projectName: "docusaurus-mdx-2", // Usually your repo name. 18 | 19 | presets: [ 20 | [ 21 | "classic", 22 | /** @type {import('@docusaurus/preset-classic').Options} */ 23 | ({ 24 | docs: { 25 | sidebarPath: require.resolve("./sidebars.js"), 26 | // Please change this to your repo. 27 | editUrl: 28 | "https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/", 29 | }, 30 | blog: { 31 | showReadingTime: true, 32 | // Please change this to your repo. 33 | editUrl: 34 | "https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/", 35 | }, 36 | theme: { 37 | customCss: require.resolve("./src/css/custom.css"), 38 | }, 39 | }), 40 | ], 41 | ], 42 | 43 | themes: ["mdx-v2"], 44 | themeConfig: 45 | /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ 46 | ({ 47 | navbar: { 48 | title: "My Site", 49 | logo: { 50 | alt: "My Site Logo", 51 | src: "img/logo.svg", 52 | }, 53 | items: [ 54 | { 55 | type: "doc", 56 | docId: "intro", 57 | position: "left", 58 | label: "Tutorial", 59 | }, 60 | { to: "/blog", label: "Blog", position: "left" }, 61 | { 62 | href: "https://github.com/facebook/docusaurus", 63 | label: "GitHub", 64 | position: "right", 65 | }, 66 | ], 67 | }, 68 | footer: { 69 | style: "dark", 70 | links: [ 71 | { 72 | title: "Docs", 73 | items: [ 74 | { 75 | label: "Tutorial", 76 | to: "/docs/intro", 77 | }, 78 | ], 79 | }, 80 | { 81 | title: "Community", 82 | items: [ 83 | { 84 | label: "Stack Overflow", 85 | href: "https://stackoverflow.com/questions/tagged/docusaurus", 86 | }, 87 | { 88 | label: "Discord", 89 | href: "https://discordapp.com/invite/docusaurus", 90 | }, 91 | { 92 | label: "Twitter", 93 | href: "https://twitter.com/docusaurus", 94 | }, 95 | ], 96 | }, 97 | { 98 | title: "More", 99 | items: [ 100 | { 101 | label: "Blog", 102 | to: "/blog", 103 | }, 104 | { 105 | label: "GitHub", 106 | href: "https://github.com/facebook/docusaurus", 107 | }, 108 | ], 109 | }, 110 | ], 111 | copyright: `Copyright © ${new Date().getFullYear()} My Project, Inc. Built with Docusaurus.`, 112 | }, 113 | prism: { 114 | theme: lightCodeTheme, 115 | darkTheme: darkCodeTheme, 116 | }, 117 | }), 118 | }; 119 | 120 | module.exports = config; 121 | -------------------------------------------------------------------------------- /apps/classic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "classic", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids" 15 | }, 16 | "dependencies": { 17 | "@docusaurus/core": "2.0.0-beta.18", 18 | "@docusaurus/preset-classic": "2.0.0-beta.18", 19 | "@mdx-js/react": "^2.0.0", 20 | "clsx": "^1.1.1", 21 | "docusaurus-theme-mdx-v2": "0.1.2", 22 | "prism-react-renderer": "^1.3.1", 23 | "react": "^17.0.2", 24 | "react-dom": "^17.0.2" 25 | }, 26 | "browserslist": { 27 | "production": [ 28 | ">0.5%", 29 | "not dead", 30 | "not op_mini all" 31 | ], 32 | "development": [ 33 | "last 1 chrome version", 34 | "last 1 firefox version", 35 | "last 1 safari version" 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /apps/classic/sidebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creating a sidebar enables you to: 3 | - create an ordered group of docs 4 | - render a sidebar for each doc of that group 5 | - provide next/previous navigation 6 | 7 | The sidebars can be generated from the filesystem, or explicitly defined here. 8 | 9 | Create as many sidebars as you want. 10 | */ 11 | 12 | // @ts-check 13 | 14 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ 15 | const sidebars = { 16 | // By default, Docusaurus generates a sidebar from the docs folder structure 17 | tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], 18 | 19 | // But you can create a sidebar manually 20 | /* 21 | tutorialSidebar: [ 22 | { 23 | type: 'category', 24 | label: 'Tutorial', 25 | items: ['hello'], 26 | }, 27 | ], 28 | */ 29 | }; 30 | 31 | module.exports = sidebars; 32 | -------------------------------------------------------------------------------- /apps/classic/src/components/HomepageFeatures/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import styles from './styles.module.css'; 4 | 5 | const FeatureList = [ 6 | { 7 | title: 'Easy to Use', 8 | Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default, 9 | description: ( 10 | <> 11 | Docusaurus was designed from the ground up to be easily installed and 12 | used to get your website up and running quickly. 13 | 14 | ), 15 | }, 16 | { 17 | title: 'Focus on What Matters', 18 | Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default, 19 | description: ( 20 | <> 21 | Docusaurus lets you focus on your docs, and we'll do the chores. Go 22 | ahead and move your docs into the docs directory. 23 | 24 | ), 25 | }, 26 | { 27 | title: 'Powered by React', 28 | Svg: require('@site/static/img/undraw_docusaurus_react.svg').default, 29 | description: ( 30 | <> 31 | Extend or customize your website layout by reusing React. Docusaurus can 32 | be extended while reusing the same header and footer. 33 | 34 | ), 35 | }, 36 | ]; 37 | 38 | function Feature({Svg, title, description}) { 39 | return ( 40 |
41 |
42 | 43 |
44 |
45 |

{title}

46 |

{description}

47 |
48 |
49 | ); 50 | } 51 | 52 | export default function HomepageFeatures() { 53 | return ( 54 |
55 |
56 |
57 | {FeatureList.map((props, idx) => ( 58 | 59 | ))} 60 |
61 |
62 |
63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /apps/classic/src/components/HomepageFeatures/styles.module.css: -------------------------------------------------------------------------------- 1 | .features { 2 | display: flex; 3 | align-items: center; 4 | padding: 2rem 0; 5 | width: 100%; 6 | } 7 | 8 | .featureSvg { 9 | height: 200px; 10 | width: 200px; 11 | } 12 | -------------------------------------------------------------------------------- /apps/classic/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #2e8555; 10 | --ifm-color-primary-dark: #29784c; 11 | --ifm-color-primary-darker: #277148; 12 | --ifm-color-primary-darkest: #205d3b; 13 | --ifm-color-primary-light: #33925d; 14 | --ifm-color-primary-lighter: #359962; 15 | --ifm-color-primary-lightest: #3cad6e; 16 | --ifm-code-font-size: 95%; 17 | } 18 | 19 | /* For readability concerns, you should choose a lighter palette in dark mode. */ 20 | [data-theme='dark'] { 21 | --ifm-color-primary: #25c2a0; 22 | --ifm-color-primary-dark: #21af90; 23 | --ifm-color-primary-darker: #1fa588; 24 | --ifm-color-primary-darkest: #1a8870; 25 | --ifm-color-primary-light: #29d5b0; 26 | --ifm-color-primary-lighter: #32d8b4; 27 | --ifm-color-primary-lightest: #4fddbf; 28 | } 29 | 30 | .docusaurus-highlight-code-line { 31 | background-color: rgba(0, 0, 0, 0.1); 32 | display: block; 33 | margin: 0 calc(-1 * var(--ifm-pre-padding)); 34 | padding: 0 var(--ifm-pre-padding); 35 | } 36 | 37 | [data-theme='dark'] .docusaurus-highlight-code-line { 38 | background-color: rgba(0, 0, 0, 0.3); 39 | } 40 | -------------------------------------------------------------------------------- /apps/classic/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import clsx from "clsx"; 3 | import Layout from "@theme/Layout"; 4 | import Link from "@docusaurus/Link"; 5 | import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; 6 | import styles from "./index.module.css"; 7 | import HomepageFeatures from "@site/src/components/HomepageFeatures"; 8 | 9 | function HomepageHeader() { 10 | const { siteConfig } = useDocusaurusContext(); 11 | return ( 12 |
13 |
14 |

{siteConfig.title}

15 |

{siteConfig.tagline}

16 |
17 | 21 | Demo page using MDX v2 22 | 23 |
24 |
25 |
26 | ); 27 | } 28 | 29 | export default function Home() { 30 | const { siteConfig } = useDocusaurusContext(); 31 | return ( 32 | 36 | 37 |
38 | 39 |
40 |
41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /apps/classic/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 996px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | -------------------------------------------------------------------------------- /apps/classic/src/pages/markdown-page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Markdown page example 3 | --- 4 | 5 | # Markdown page example 6 | 7 | You don't need React to write simple standalone pages. 8 | -------------------------------------------------------------------------------- /apps/classic/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomber/docusaurus-mdx-2/b2ae743cae46751dfa3024faa02e8d42f93b0c7b/apps/classic/static/.nojekyll -------------------------------------------------------------------------------- /apps/classic/static/img/docusaurus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomber/docusaurus-mdx-2/b2ae743cae46751dfa3024faa02e8d42f93b0c7b/apps/classic/static/img/docusaurus.png -------------------------------------------------------------------------------- /apps/classic/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomber/docusaurus-mdx-2/b2ae743cae46751dfa3024faa02e8d42f93b0c7b/apps/classic/static/img/favicon.ico -------------------------------------------------------------------------------- /apps/classic/static/img/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/classic/static/img/tutorial/docsVersionDropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomber/docusaurus-mdx-2/b2ae743cae46751dfa3024faa02e8d42f93b0c7b/apps/classic/static/img/tutorial/docsVersionDropdown.png -------------------------------------------------------------------------------- /apps/classic/static/img/tutorial/localeDropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomber/docusaurus-mdx-2/b2ae743cae46751dfa3024faa02e8d42f93b0c7b/apps/classic/static/img/tutorial/localeDropdown.png -------------------------------------------------------------------------------- /apps/classic/static/img/undraw_docusaurus_mountain.svg: -------------------------------------------------------------------------------- 1 | 2 | Easy to Use 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /apps/classic/static/img/undraw_docusaurus_react.svg: -------------------------------------------------------------------------------- 1 | 2 | Powered by React 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /apps/classic/static/img/undraw_docusaurus_tree.svg: -------------------------------------------------------------------------------- 1 | 2 | Focus on What Matters 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "workspaces": [ 4 | "packages/*", 5 | "apps/*" 6 | ], 7 | "scripts": { 8 | "build": "yarn workspaces run build", 9 | "dev": "yarn workspaces run dev" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/mdx-loader/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docusaurus-mdx-loader-v2", 3 | "version": "0.1.2", 4 | "description": "Docusaurus Loader for MDX v2", 5 | "main": "lib/index.js", 6 | "types": "src/mdx-loader.d.ts", 7 | "publishConfig": { 8 | "access": "public" 9 | }, 10 | "scripts": { 11 | "build": "tsc", 12 | "dev": "tsc --watch" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/pomber/docusaurus-mdx-2", 17 | "directory": "packages/mdx-loader" 18 | }, 19 | "license": "MIT", 20 | "dependencies": { 21 | "@babel/parser": "^7.17.3", 22 | "@babel/traverse": "^7.17.3", 23 | "@docusaurus/logger": "2.0.0-beta.18", 24 | "@docusaurus/utils": "2.0.0-beta.18", 25 | "@mdx-js/mdx": "^2.1.0", 26 | "escape-html": "^1.0.3", 27 | "estree-util-value-to-estree": "^1.3.0", 28 | "file-loader": "^6.2.0", 29 | "fs-extra": "^10.0.1", 30 | "image-size": "^1.0.1", 31 | "lz-string": "^1.4.4", 32 | "mdast-util-to-string": "^2.0.0", 33 | "remark-admonitions": "^1.2.1", 34 | "remark-emoji": "^2.1.0", 35 | "remark-gfm": "1.0.0", 36 | "stringify-object": "^3.3.0", 37 | "tslib": "^2.3.1", 38 | "unist-util-visit": "^2.0.2", 39 | "url-loader": "^4.1.1", 40 | "webpack": "^5.69.1" 41 | }, 42 | "devDependencies": { 43 | "@docusaurus/types": "2.0.0-beta.18", 44 | "@types/babel__traverse": "^7.14.2", 45 | "@types/escape-html": "^1.0.1", 46 | "@types/fs-extra": "^9.0.13", 47 | "@types/mdast": "^3.0.7", 48 | "@types/stringify-object": "^3.3.1", 49 | "@types/unist": "^2.0.6", 50 | "mdx-debugger": "^0.2.0", 51 | "remark": "^12.0.0", 52 | "remark-mdx": "^1.6.21", 53 | "to-vfile": "^6.0.0", 54 | "typescript": "^4.6.3", 55 | "unified": "^9.2.1", 56 | "unist-builder": "^2.0.3", 57 | "unist-util-remove-position": "^3.0.0" 58 | }, 59 | "peerDependencies": { 60 | "react": "^16.8.4 || ^17.0.0", 61 | "react-dom": "^16.8.4 || ^17.0.0" 62 | }, 63 | "engines": { 64 | "node": ">=14" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /packages/mdx-loader/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import fs from "fs-extra"; 9 | import logger from "@docusaurus/logger"; 10 | import emoji from "remark-emoji"; 11 | import { 12 | parseFrontMatter, 13 | parseMarkdownContentTitle, 14 | escapePath, 15 | getFileLoaderUtils, 16 | } from "@docusaurus/utils"; 17 | import stringifyObject from "stringify-object"; 18 | import headings from "./remark/headings"; 19 | import toc from "./remark/toc"; 20 | import unwrapMdxCodeBlocks from "./remark/unwrapMdxCodeBlocks"; 21 | import transformImage from "./remark/transformImage"; 22 | import transformLinks from "./remark/transformLinks"; 23 | import codeCompat from "./remark/codeCompat"; 24 | import type { LoaderContext } from "webpack"; 25 | import type { Plugin } from "unified"; 26 | // @ts-ignore 27 | import admonitions from "remark-admonitions"; 28 | import gfm from "remark-gfm"; 29 | import { VFile } from "@mdx-js/mdx/lib/compile"; 30 | 31 | export type RemarkOrRehypePlugin = 32 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 33 | [Plugin, Record] | Plugin; 34 | export type RemarkAndRehypePluginOptions = { 35 | remarkPlugins: RemarkOrRehypePlugin[]; 36 | rehypePlugins: RemarkOrRehypePlugin[]; 37 | beforeDefaultRemarkPlugins: RemarkOrRehypePlugin[]; 38 | beforeDefaultRehypePlugins: RemarkOrRehypePlugin[]; 39 | }; 40 | 41 | const { 42 | loaders: { inlineMarkdownImageFileLoader }, 43 | } = getFileLoaderUtils(); 44 | 45 | const DEFAULT_OPTIONS: RemarkAndRehypePluginOptions = { 46 | rehypePlugins: [], 47 | remarkPlugins: [unwrapMdxCodeBlocks, emoji as any, headings, toc], 48 | beforeDefaultRemarkPlugins: [], 49 | beforeDefaultRehypePlugins: [], 50 | }; 51 | 52 | type Options = RemarkAndRehypePluginOptions & { 53 | staticDirs: string[]; 54 | siteDir: string; 55 | isMDXPartial?: (filePath: string) => boolean; 56 | isMDXPartialFrontMatterWarningDisabled?: boolean; 57 | removeContentTitle?: boolean; 58 | metadataPath?: string | ((filePath: string) => string); 59 | createAssets?: (metadata: { 60 | frontMatter: Record; 61 | metadata: Record; 62 | }) => Record; 63 | filepath: string; 64 | }; 65 | 66 | /** 67 | * When this throws, it generally means that there's no metadata file associated 68 | * with this MDX document. It can happen when using MDX partials (usually 69 | * starting with _). That's why it's important to provide the `isMDXPartial` 70 | * function in config 71 | */ 72 | async function readMetadataPath(metadataPath: string) { 73 | try { 74 | return await fs.readFile(metadataPath, "utf8"); 75 | } catch (err) { 76 | logger.error`MDX loader can't read MDX metadata file path=${metadataPath}. Maybe the isMDXPartial option function was not provided?`; 77 | throw err; 78 | } 79 | } 80 | 81 | /** 82 | * Converts assets an object with Webpack require calls code. 83 | * This is useful for mdx files to reference co-located assets using relative 84 | * paths. Those assets should enter the Webpack assets pipeline and be hashed. 85 | * For now, we only handle that for images and paths starting with `./`: 86 | * 87 | * `{image: "./myImage.png"}` => `{image: require("./myImage.png")}` 88 | */ 89 | function createAssetsExportCode(assets: Record) { 90 | if (Object.keys(assets).length === 0) { 91 | return "undefined"; 92 | } 93 | 94 | // TODO implementation can be completed/enhanced 95 | function createAssetValueCode(assetValue: unknown): string | undefined { 96 | if (Array.isArray(assetValue)) { 97 | const arrayItemCodes = assetValue.map( 98 | (item) => createAssetValueCode(item) ?? "undefined" 99 | ); 100 | return `[${arrayItemCodes.join(", ")}]`; 101 | } 102 | // Only process string values starting with ./ 103 | // We could enhance this logic and check if file exists on disc? 104 | if (typeof assetValue === "string" && assetValue.startsWith("./")) { 105 | // TODO do we have other use-cases than image assets? 106 | // Probably not worth adding more support, as we want to move to Webpack 5 new asset system (https://github.com/facebook/docusaurus/pull/4708) 107 | const inlineLoader = inlineMarkdownImageFileLoader; 108 | return `require("${inlineLoader}${escapePath(assetValue)}").default`; 109 | } 110 | return undefined; 111 | } 112 | 113 | const assetEntries = Object.entries(assets); 114 | 115 | const codeLines = assetEntries 116 | .map(([key, value]) => { 117 | const assetRequireCode = createAssetValueCode(value); 118 | return assetRequireCode ? `"${key}": ${assetRequireCode},` : undefined; 119 | }) 120 | .filter(Boolean); 121 | 122 | return `{\n${codeLines.join("\n")}\n}`; 123 | } 124 | 125 | export default async function mdxLoader( 126 | this: LoaderContext, 127 | fileString: string 128 | ): Promise { 129 | const callback = this.async(); 130 | const filePath = this.resourcePath; 131 | const reqOptions = this.getOptions() || {}; 132 | 133 | const { frontMatter, content: contentWithTitle } = 134 | parseFrontMatter(fileString); 135 | 136 | const { content, contentTitle } = parseMarkdownContentTitle( 137 | contentWithTitle, 138 | { 139 | removeContentTitle: reqOptions.removeContentTitle, 140 | } 141 | ); 142 | 143 | const hasFrontMatter = Object.keys(frontMatter).length > 0; 144 | 145 | // console.log((reqOptions.remarkPlugins as any)[0][0].toString()); 146 | // remove admonitions from remark plugins 147 | const optionsRemarkPlugins = (reqOptions.remarkPlugins || []).filter( 148 | (maybePlugin) => { 149 | const plugin = Array.isArray(maybePlugin) ? maybePlugin[0] : maybePlugin; 150 | const isAdmonitions = plugin === admonitions; 151 | return !isAdmonitions; 152 | } 153 | ); 154 | 155 | const options: Options = { 156 | ...reqOptions, 157 | remarkPlugins: [ 158 | ...(reqOptions.beforeDefaultRemarkPlugins || []), 159 | ...DEFAULT_OPTIONS.remarkPlugins, 160 | [ 161 | transformImage, 162 | { 163 | staticDirs: reqOptions.staticDirs, 164 | siteDir: reqOptions.siteDir, 165 | }, 166 | ], 167 | [ 168 | transformLinks, 169 | { 170 | staticDirs: reqOptions.staticDirs, 171 | siteDir: reqOptions.siteDir, 172 | }, 173 | ], 174 | gfm as any, 175 | codeCompat, 176 | ...optionsRemarkPlugins, 177 | ], 178 | rehypePlugins: [ 179 | ...(reqOptions.beforeDefaultRehypePlugins || []), 180 | ...DEFAULT_OPTIONS.rehypePlugins, 181 | ...(reqOptions.rehypePlugins || []), 182 | ], 183 | }; 184 | 185 | let result: VFile; 186 | try { 187 | // typescript doesn't like: 188 | // const { compile } = await import("@mdx-js/mdx"); 189 | // from https://github.com/microsoft/TypeScript/issues/43329#issuecomment-922544562 190 | const dynamicImport = Function('return import("@mdx-js/mdx")')() as Promise< 191 | typeof import("@mdx-js/mdx") 192 | >; 193 | const { compile } = await dynamicImport; 194 | 195 | // const { withDebugger } = await (Function( 196 | // 'return import("mdx-debugger")' 197 | // )() as Promise); 198 | // const newCompiler = withDebugger(compile, { 199 | // filter: (file: any) => file.path.includes("intro.md"), 200 | // log: (path: string, url: string) => logger.info(`${path}: ${url}`), 201 | // }); 202 | 203 | const newCompiler = compile; 204 | result = await newCompiler({ path: filePath, value: content }, { 205 | format: "mdx", 206 | providerImportSource: "@mdx-js/react", 207 | ...options, 208 | } as any); 209 | } catch (err) { 210 | return callback(err as Error); 211 | } 212 | 213 | // MDX partials are MDX files starting with _ or in a folder starting with _ 214 | // Partial are not expected to have associated metadata files or front matter 215 | const isMDXPartial = options.isMDXPartial && options.isMDXPartial(filePath); 216 | if (isMDXPartial && hasFrontMatter) { 217 | const errorMessage = `Docusaurus MDX partial files should not contain FrontMatter. 218 | Those partial files use the _ prefix as a convention by default, but this is configurable. 219 | File at ${filePath} contains FrontMatter that will be ignored: 220 | ${JSON.stringify(frontMatter, null, 2)}`; 221 | 222 | if (!options.isMDXPartialFrontMatterWarningDisabled) { 223 | const shouldError = process.env.NODE_ENV === "test" || process.env.CI; 224 | if (shouldError) { 225 | return callback(new Error(errorMessage)); 226 | } 227 | logger.warn(errorMessage); 228 | } 229 | } 230 | 231 | function getMetadataPath(): string | undefined { 232 | if (!isMDXPartial) { 233 | // Read metadata for this MDX and export it. 234 | if (options.metadataPath && typeof options.metadataPath === "function") { 235 | return options.metadataPath(filePath); 236 | } 237 | } 238 | return undefined; 239 | } 240 | 241 | const metadataPath = getMetadataPath(); 242 | if (metadataPath) { 243 | this.addDependency(metadataPath); 244 | } 245 | 246 | const metadataJsonString = metadataPath 247 | ? await readMetadataPath(metadataPath) 248 | : undefined; 249 | 250 | const metadata = metadataJsonString 251 | ? JSON.parse(metadataJsonString) 252 | : undefined; 253 | 254 | const assets = 255 | reqOptions.createAssets && metadata 256 | ? reqOptions.createAssets({ frontMatter, metadata }) 257 | : undefined; 258 | 259 | const exportsCode = ` 260 | export const frontMatter = ${stringifyObject(frontMatter)}; 261 | export const contentTitle = ${stringifyObject(contentTitle)}; 262 | ${metadataJsonString ? `export const metadata = ${metadataJsonString};` : ""} 263 | ${assets ? `export const assets = ${createAssetsExportCode(assets)};` : ""} 264 | `; 265 | 266 | const code = ` 267 | import React from 'react'; 268 | import { mdx } from '@mdx-js/react'; 269 | 270 | ${exportsCode} 271 | ${result.value} 272 | `; 273 | 274 | return callback(null, code); 275 | } 276 | -------------------------------------------------------------------------------- /packages/mdx-loader/src/mdx-loader.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import type { Plugin } from "unified"; 9 | 10 | export type RemarkOrRehypePlugin = 11 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 12 | [Plugin, Record] | Plugin; 13 | export type RemarkAndRehypePluginOptions = { 14 | remarkPlugins: RemarkOrRehypePlugin[]; 15 | rehypePlugins: RemarkOrRehypePlugin[]; 16 | beforeDefaultRemarkPlugins: RemarkOrRehypePlugin[]; 17 | beforeDefaultRehypePlugins: RemarkOrRehypePlugin[]; 18 | }; 19 | -------------------------------------------------------------------------------- /packages/mdx-loader/src/remark/codeCompat/index.ts: -------------------------------------------------------------------------------- 1 | import visit from "unist-util-visit"; 2 | import type { Transformer, Processor } from "unified"; 3 | import type { Code } from "mdast"; 4 | 5 | // To make theme-classic/src/theme/MDXComponents/Pre work 6 | // we need to fill two properties that mdx v2 doesn't provide anymore 7 | export default function plugin(this: Processor): Transformer { 8 | return (root) => { 9 | visit(root, "code", (node: Code) => { 10 | node.data = node.data || {}; 11 | node.data.hProperties = node.data.hProperties || {}; 12 | (node.data.hProperties as any)["originalType"] = "code"; 13 | (node.data.hProperties as any)["metastring"] = node.meta; 14 | }); 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /packages/mdx-loader/src/remark/headings/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | /* Based on remark-slug (https://github.com/remarkjs/remark-slug) and gatsby-remark-autolink-headers (https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-remark-autolink-headers) */ 9 | 10 | import { parseMarkdownHeadingId, createSlugger } from "@docusaurus/utils"; 11 | import visit from "unist-util-visit"; 12 | import toString from "mdast-util-to-string"; 13 | import type { Transformer } from "unified"; 14 | import type { Parent } from "unist"; 15 | import type { Heading, Text } from "mdast"; 16 | 17 | export default function plugin(): Transformer { 18 | return (root) => { 19 | const slugs = createSlugger(); 20 | // console.log("before", root); 21 | visit(root, "heading", (headingNode: Heading) => { 22 | const data = headingNode.data || (headingNode.data = {}); 23 | const properties = (data.hProperties || (data.hProperties = {})) as { 24 | id: string; 25 | }; 26 | let { id } = properties; 27 | 28 | if (id) { 29 | id = slugs.slug(id, { maintainCase: true }); 30 | } else { 31 | const headingTextNodes = headingNode.children.filter( 32 | ({ type }) => !["html", "jsx"].includes(type) 33 | ); 34 | const heading = toString( 35 | headingTextNodes.length > 0 36 | ? ({ children: headingTextNodes } as Parent) 37 | : headingNode 38 | ); 39 | 40 | // Support explicit heading IDs 41 | const parsedHeading = parseMarkdownHeadingId(heading); 42 | 43 | id = parsedHeading.id || slugs.slug(heading); 44 | 45 | if (parsedHeading.id) { 46 | // When there's an id, it is always in the last child node 47 | // Sometimes heading is in multiple "parts" (** syntax creates a child 48 | // node): 49 | // ## part1 *part2* part3 {#id} 50 | const lastNode = headingNode.children[ 51 | headingNode.children.length - 1 52 | ] as Text; 53 | 54 | if (headingNode.children.length > 1) { 55 | const lastNodeText = parseMarkdownHeadingId(lastNode.value).text; 56 | // When last part contains test+id, remove the id 57 | if (lastNodeText) { 58 | lastNode.value = lastNodeText; 59 | } 60 | // When last part contains only the id: completely remove that node 61 | else { 62 | headingNode.children.pop(); 63 | } 64 | } else { 65 | lastNode.value = parsedHeading.text; 66 | } 67 | } 68 | } 69 | 70 | data.id = id; 71 | properties.id = id; 72 | }); 73 | // console.log("after", root); 74 | }; 75 | } 76 | -------------------------------------------------------------------------------- /packages/mdx-loader/src/remark/toc/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import { parse, type ParserOptions } from "@babel/parser"; 9 | import type { Identifier } from "@babel/types"; 10 | import traverse from "@babel/traverse"; 11 | import stringifyObject from "stringify-object"; 12 | import toString from "mdast-util-to-string"; 13 | import visit from "unist-util-visit"; 14 | import { toValue } from "../utils"; 15 | 16 | import type { TOCItem } from "@docusaurus/types"; 17 | import type { Node, Parent } from "unist"; 18 | import type { Heading, Literal } from "mdast"; 19 | import type { Transformer } from "unified"; 20 | import { valueToEstree } from "estree-util-value-to-estree"; 21 | 22 | const parseOptions: ParserOptions = { 23 | plugins: ["jsx"], 24 | sourceType: "module", 25 | }; 26 | 27 | const isImport = (child: any): child is Literal => 28 | child.type === "mdxjsEsm" && child.value.startsWith("import"); 29 | const hasImports = (index: number) => index > -1; 30 | const isExport = (child: any): child is Literal => 31 | child.type === "mdxjsEsm" && child.value.startsWith("export"); 32 | 33 | interface PluginOptions { 34 | name?: string; 35 | } 36 | 37 | const isTarget = (child: Literal, name: string) => { 38 | let found = false; 39 | const ast = parse(child.value, parseOptions); 40 | traverse(ast, { 41 | VariableDeclarator: (path) => { 42 | if ((path.node.id as Identifier).name === name) { 43 | found = true; 44 | } 45 | }, 46 | }); 47 | return found; 48 | }; 49 | 50 | const getOrCreateExistingTargetIndex = (children: Node[], name: string) => { 51 | let importsIndex = -1; 52 | let targetIndex = -1; 53 | 54 | children.forEach((child, index) => { 55 | if (isImport(child)) { 56 | importsIndex = index; 57 | } else if (isExport(child) && isTarget(child, name)) { 58 | targetIndex = index; 59 | } 60 | }); 61 | 62 | if (targetIndex === -1) { 63 | const target = createExportNode(name, []); 64 | 65 | targetIndex = hasImports(importsIndex) ? importsIndex + 1 : 0; 66 | children.splice(targetIndex, 0, target); 67 | } 68 | 69 | return targetIndex; 70 | }; 71 | 72 | export default function plugin(options: PluginOptions = {}): Transformer { 73 | const name = options.name || "toc"; 74 | 75 | return (root) => { 76 | const headings: TOCItem[] = []; 77 | 78 | visit(root, "heading", (child: Heading, _index, parent) => { 79 | const value = toString(child); 80 | 81 | // depth:1 headings are titles and not included in the TOC 82 | if (!value || child.depth < 2) { 83 | return; 84 | } 85 | 86 | headings.push({ 87 | value: toValue(child), 88 | id: child.data!.id as string, 89 | level: child.depth, 90 | }); 91 | }); 92 | const { children } = root as Parent; 93 | const targetIndex = getOrCreateExistingTargetIndex(children, name); 94 | 95 | if (headings && headings.length) { 96 | children[targetIndex] = createExportNode(name, headings); 97 | } 98 | }; 99 | } 100 | 101 | function createExportNode(name: string, object: any) { 102 | return { 103 | type: "mdxjsEsm", 104 | value: `export const ${name} = ${stringifyObject(object)}`, 105 | data: { 106 | estree: { 107 | type: "Program", 108 | body: [ 109 | { 110 | type: "ExportNamedDeclaration", 111 | declaration: { 112 | type: "VariableDeclaration", 113 | declarations: [ 114 | { 115 | type: "VariableDeclarator", 116 | id: { 117 | type: "Identifier", 118 | name: name, 119 | }, 120 | init: valueToEstree(object), 121 | }, 122 | ], 123 | kind: "const", 124 | }, 125 | specifiers: [], 126 | source: null, 127 | }, 128 | ], 129 | sourceType: "module", 130 | }, 131 | }, 132 | }; 133 | } 134 | -------------------------------------------------------------------------------- /packages/mdx-loader/src/remark/transformImage/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import { 9 | toMessageRelativeFilePath, 10 | posixPath, 11 | escapePath, 12 | getFileLoaderUtils, 13 | findAsyncSequential, 14 | } from "@docusaurus/utils"; 15 | import visit from "unist-util-visit"; 16 | import path from "path"; 17 | import url from "url"; 18 | import fs from "fs-extra"; 19 | import escapeHtml from "escape-html"; 20 | import sizeOf from "image-size"; 21 | import { promisify } from "util"; 22 | import type { Transformer } from "unified"; 23 | import type { Image, Literal } from "mdast"; 24 | import logger from "@docusaurus/logger"; 25 | import { assetRequireAttributeValue } from "../utils"; 26 | 27 | const { 28 | loaders: { inlineMarkdownImageFileLoader }, 29 | } = getFileLoaderUtils(); 30 | 31 | type PluginOptions = { 32 | staticDirs: string[]; 33 | siteDir: string; 34 | }; 35 | 36 | type Context = PluginOptions & { 37 | filePath: string; 38 | }; 39 | 40 | async function toImageRequireNode( 41 | node: Image, 42 | imagePath: string, 43 | filePath: string 44 | ) { 45 | const jsxNode = node as unknown as { 46 | type: string; 47 | name: string; 48 | attributes: any[]; 49 | children: any[]; 50 | }; 51 | const attributes = []; 52 | let relativeImagePath = posixPath( 53 | path.relative(path.dirname(filePath), imagePath) 54 | ); 55 | relativeImagePath = `./${relativeImagePath}`; 56 | 57 | const parsedUrl = url.parse(node.url); 58 | const hash = parsedUrl.hash ?? ""; 59 | const search = parsedUrl.search ?? ""; 60 | const requireString = `${inlineMarkdownImageFileLoader}${ 61 | escapePath(relativeImagePath) + search 62 | }`; 63 | attributes.push({ 64 | type: "mdxJsxAttribute", 65 | name: "src", 66 | value: assetRequireAttributeValue(requireString, hash), 67 | }); 68 | 69 | attributes.push({ 70 | type: "mdxJsxAttribute", 71 | name: "loading", 72 | value: "lazy", 73 | }); 74 | if (node.alt) { 75 | attributes.push({ 76 | type: "mdxJsxAttribute", 77 | name: "alt", 78 | value: escapeHtml(node.alt), 79 | }); 80 | } 81 | if (node.title) { 82 | attributes.push({ 83 | type: "mdxJsxAttribute", 84 | name: "title", 85 | value: escapeHtml(node.title), 86 | }); 87 | } 88 | 89 | try { 90 | const size = (await promisify(sizeOf)(imagePath))!; 91 | if (size.width) { 92 | attributes.push({ 93 | type: "mdxJsxAttribute", 94 | name: "width", 95 | value: size.width, 96 | }); 97 | } 98 | if (size.height) { 99 | attributes.push({ 100 | type: "mdxJsxAttribute", 101 | name: "height", 102 | value: size.height, 103 | }); 104 | } 105 | } catch (err) { 106 | // Workaround for https://github.com/yarnpkg/berry/pull/3889#issuecomment-1034469784 107 | // TODO remove this check once fixed in Yarn PnP 108 | if (!process.versions.pnp) { 109 | logger.warn`The image at path=${imagePath} can't be read correctly. Please ensure it's a valid image. 110 | ${(err as Error).message}`; 111 | } 112 | } 113 | 114 | Object.keys(jsxNode).forEach( 115 | (key) => delete jsxNode[key as keyof typeof jsxNode] 116 | ); 117 | 118 | jsxNode.type = "mdxJsxFlowElement"; 119 | jsxNode.name = "img"; 120 | jsxNode.attributes = attributes; 121 | jsxNode.children = []; 122 | } 123 | 124 | async function ensureImageFileExist(imagePath: string, sourceFilePath: string) { 125 | const imageExists = await fs.pathExists(imagePath); 126 | if (!imageExists) { 127 | throw new Error( 128 | `Image ${toMessageRelativeFilePath( 129 | imagePath 130 | )} used in ${toMessageRelativeFilePath(sourceFilePath)} not found.` 131 | ); 132 | } 133 | } 134 | 135 | async function getImageAbsolutePath( 136 | imagePath: string, 137 | { siteDir, filePath, staticDirs }: Context 138 | ) { 139 | if (imagePath.startsWith("@site/")) { 140 | const imageFilePath = path.join(siteDir, imagePath.replace("@site/", "")); 141 | await ensureImageFileExist(imageFilePath, filePath); 142 | return imageFilePath; 143 | } else if (path.isAbsolute(imagePath)) { 144 | // absolute paths are expected to exist in the static folder 145 | const possiblePaths = staticDirs.map((dir) => path.join(dir, imagePath)); 146 | const imageFilePath = await findAsyncSequential( 147 | possiblePaths, 148 | fs.pathExists 149 | ); 150 | if (!imageFilePath) { 151 | throw new Error( 152 | `Image ${possiblePaths 153 | .map((p) => toMessageRelativeFilePath(p)) 154 | .join(" or ")} used in ${toMessageRelativeFilePath( 155 | filePath 156 | )} not found.` 157 | ); 158 | } 159 | return imageFilePath; 160 | } 161 | // relative paths are resolved against the source file's folder 162 | const imageFilePath = path.join( 163 | path.dirname(filePath), 164 | decodeURIComponent(imagePath) 165 | ); 166 | await ensureImageFileExist(imageFilePath, filePath); 167 | return imageFilePath; 168 | } 169 | 170 | async function processImageNode(node: Image, context: Context) { 171 | if (!node.url) { 172 | throw new Error( 173 | `Markdown image URL is mandatory in "${toMessageRelativeFilePath( 174 | context.filePath 175 | )}" file` 176 | ); 177 | } 178 | 179 | const parsedUrl = url.parse(node.url); 180 | if (parsedUrl.protocol || !parsedUrl.pathname) { 181 | // pathname:// is an escape hatch, in case user does not want her images to 182 | // be converted to require calls going through webpack loader 183 | if (parsedUrl.protocol === "pathname:") { 184 | node.url = node.url.replace("pathname://", ""); 185 | } 186 | return; 187 | } 188 | 189 | // We try to convert image urls without protocol to images with require calls 190 | // going through webpack ensures that image assets exist at build time 191 | const imagePath = await getImageAbsolutePath(parsedUrl.pathname, context); 192 | await toImageRequireNode(node, imagePath, context.filePath); 193 | } 194 | 195 | export default function plugin(options: PluginOptions): Transformer { 196 | return async (root, vfile) => { 197 | const promises: Promise[] = []; 198 | visit(root, "image", (node: Image) => { 199 | promises.push( 200 | processImageNode(node, { ...options, filePath: vfile.path! }) 201 | ); 202 | }); 203 | await Promise.all(promises); 204 | }; 205 | } 206 | -------------------------------------------------------------------------------- /packages/mdx-loader/src/remark/transformLinks/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import { 9 | toMessageRelativeFilePath, 10 | posixPath, 11 | escapePath, 12 | getFileLoaderUtils, 13 | findAsyncSequential, 14 | } from "@docusaurus/utils"; 15 | import visit from "unist-util-visit"; 16 | import path from "path"; 17 | import url from "url"; 18 | import fs from "fs-extra"; 19 | import escapeHtml from "escape-html"; 20 | import { assetRequireAttributeValue, stringifyContent } from "../utils"; 21 | import type { Transformer } from "unified"; 22 | import type { Link, Literal } from "mdast"; 23 | 24 | const { 25 | loaders: { inlineMarkdownLinkFileLoader }, 26 | } = getFileLoaderUtils(); 27 | 28 | type PluginOptions = { 29 | staticDirs: string[]; 30 | siteDir: string; 31 | }; 32 | 33 | type Context = PluginOptions & { 34 | filePath: string; 35 | }; 36 | 37 | // transform the link node to a jsx link with a require() call 38 | function toAssetRequireNode(node: Link, assetPath: string, filePath: string) { 39 | const jsxNode = node as unknown as { 40 | type: string; 41 | name: string; 42 | attributes: any[]; 43 | children: any[]; 44 | }; 45 | const attributes = []; 46 | 47 | let relativeAssetPath = posixPath( 48 | path.relative(path.dirname(filePath), assetPath) 49 | ); 50 | // require("assets/file.pdf") means requiring from a package called assets 51 | relativeAssetPath = `./${relativeAssetPath}`; 52 | 53 | const parsedUrl = url.parse(node.url); 54 | const hash = parsedUrl.hash ?? ""; 55 | const search = parsedUrl.search ?? ""; 56 | 57 | const requireString = `${ 58 | // A hack to stop Webpack from using its built-in loader to parse JSON 59 | path.extname(relativeAssetPath) === ".json" 60 | ? `${relativeAssetPath.replace(".json", ".raw")}!=` 61 | : "" 62 | }${inlineMarkdownLinkFileLoader}${escapePath(relativeAssetPath) + search}`; 63 | 64 | attributes.push({ 65 | type: "mdxJsxAttribute", 66 | name: "href", 67 | value: assetRequireAttributeValue(requireString, hash), 68 | }); 69 | 70 | attributes.push({ 71 | type: "mdxJsxAttribute", 72 | name: "target", 73 | value: "_blank", 74 | }); 75 | 76 | if (node.title) { 77 | attributes.push({ 78 | type: "mdxJsxAttribute", 79 | name: "title", 80 | value: escapeHtml(node.title), 81 | }); 82 | } 83 | 84 | const children = stringifyContent(node); 85 | 86 | Object.keys(jsxNode).forEach( 87 | (key) => delete jsxNode[key as keyof typeof jsxNode] 88 | ); 89 | 90 | jsxNode.type = "mdxJsxFlowElement"; 91 | jsxNode.name = "a"; 92 | jsxNode.attributes = attributes; 93 | jsxNode.children = [{ type: "text", value: children }]; 94 | } 95 | 96 | async function ensureAssetFileExist(assetPath: string, sourceFilePath: string) { 97 | const assetExists = await fs.pathExists(assetPath); 98 | if (!assetExists) { 99 | throw new Error( 100 | `Asset ${toMessageRelativeFilePath( 101 | assetPath 102 | )} used in ${toMessageRelativeFilePath(sourceFilePath)} not found.` 103 | ); 104 | } 105 | } 106 | 107 | async function getAssetAbsolutePath( 108 | assetPath: string, 109 | { siteDir, filePath, staticDirs }: Context 110 | ) { 111 | if (assetPath.startsWith("@site/")) { 112 | const assetFilePath = path.join(siteDir, assetPath.replace("@site/", "")); 113 | // The @site alias is the only way to believe that the user wants an asset. 114 | // Everything else can just be a link URL 115 | await ensureAssetFileExist(assetFilePath, filePath); 116 | return assetFilePath; 117 | } else if (path.isAbsolute(assetPath)) { 118 | const assetFilePath = await findAsyncSequential( 119 | staticDirs.map((dir) => path.join(dir, assetPath)), 120 | fs.pathExists 121 | ); 122 | if (assetFilePath) { 123 | return assetFilePath; 124 | } 125 | } else { 126 | const assetFilePath = path.join(path.dirname(filePath), assetPath); 127 | if (await fs.pathExists(assetFilePath)) { 128 | return assetFilePath; 129 | } 130 | } 131 | return null; 132 | } 133 | 134 | async function processLinkNode(node: Link, context: Context) { 135 | if (!node.url) { 136 | // try to improve error feedback 137 | // see https://github.com/facebook/docusaurus/issues/3309#issuecomment-690371675 138 | const title = node.title || (node.children[0] as Literal)?.value || "?"; 139 | const line = node?.position?.start?.line || "?"; 140 | throw new Error( 141 | `Markdown link URL is mandatory in "${toMessageRelativeFilePath( 142 | context.filePath 143 | )}" file (title: ${title}, line: ${line}).` 144 | ); 145 | } 146 | 147 | const parsedUrl = url.parse(node.url); 148 | if (parsedUrl.protocol || !parsedUrl.pathname) { 149 | // Don't process pathname:// here, it's used by the component 150 | return; 151 | } 152 | const hasSiteAlias = parsedUrl.pathname.startsWith("@site/"); 153 | const hasAssetLikeExtension = 154 | path.extname(parsedUrl.pathname) && 155 | !parsedUrl.pathname.match(/\.(?:mdx?|html)(?:#|$)/); 156 | if (!hasSiteAlias && !hasAssetLikeExtension) { 157 | return; 158 | } 159 | 160 | const assetPath = await getAssetAbsolutePath( 161 | decodeURIComponent(parsedUrl.pathname), 162 | context 163 | ); 164 | if (assetPath) { 165 | toAssetRequireNode(node, assetPath, context.filePath); 166 | } 167 | } 168 | 169 | export default function plugin(options: PluginOptions): Transformer { 170 | return async (root, vfile) => { 171 | const promises: Promise[] = []; 172 | visit(root, "link", (node: Link) => { 173 | promises.push( 174 | processLinkNode(node, { ...options, filePath: vfile.path! }) 175 | ); 176 | }); 177 | await Promise.all(promises); 178 | }; 179 | } 180 | -------------------------------------------------------------------------------- /packages/mdx-loader/src/remark/unwrapMdxCodeBlocks/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import visit from "unist-util-visit"; 9 | import type { Transformer, Processor } from "unified"; 10 | import type { Code, Parent } from "mdast"; 11 | 12 | // This plugin is mostly to help integrating Docusaurus with translation systems 13 | // that do not support well MDX embedded JSX syntax (like Crowdin). 14 | // We wrap the JSX syntax in code blocks so that translation tools don't mess up 15 | // with the markup, but the JSX inside such code blocks should still be 16 | // evaluated as JSX 17 | // See https://github.com/facebook/docusaurus/pull/4278 18 | export default function plugin(this: Processor): Transformer { 19 | return (root) => { 20 | visit(root, "code", (node: Code, _index, parent) => { 21 | if (node.lang === "mdx-code-block") { 22 | const newChildren = (this.parse(node.value) as Parent).children; 23 | 24 | // Replace the mdx code block by its content, parsed 25 | parent!.children.splice( 26 | parent!.children.indexOf(node), 27 | 1, 28 | ...newChildren 29 | ); 30 | } 31 | }); 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /packages/mdx-loader/src/remark/utils/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import escapeHtml from "escape-html"; 9 | import toString from "mdast-util-to-string"; 10 | import type { Parent } from "unist"; 11 | import type { PhrasingContent, Heading } from "mdast"; 12 | 13 | export function stringifyContent(node: Parent): string { 14 | return (node.children as PhrasingContent[]).map(toValue).join(""); 15 | } 16 | 17 | export function toValue(node: PhrasingContent | Heading): string { 18 | switch (node?.type) { 19 | case "text": 20 | return escapeHtml(node.value); 21 | case "heading": 22 | return stringifyContent(node); 23 | case "inlineCode": 24 | return `${escapeHtml(node.value)}`; 25 | case "emphasis": 26 | return `${stringifyContent(node)}`; 27 | case "strong": 28 | return `${stringifyContent(node)}`; 29 | case "delete": 30 | return `${stringifyContent(node)}`; 31 | case "link": 32 | return stringifyContent(node); 33 | default: 34 | } 35 | 36 | return toString(node); 37 | } 38 | 39 | export function assetRequireAttributeValue( 40 | requireString: string, 41 | hash: string 42 | ) { 43 | return { 44 | type: "mdxJsxAttributeValueExpression", 45 | value: `require("${requireString}").default + '${hash}'`, 46 | data: { 47 | estree: { 48 | type: "Program", 49 | body: [ 50 | { 51 | type: "ExpressionStatement", 52 | expression: { 53 | type: "BinaryExpression", 54 | left: { 55 | type: "MemberExpression", 56 | object: { 57 | type: "CallExpression", 58 | callee: { 59 | type: "Identifier", 60 | name: "require", 61 | }, 62 | arguments: [ 63 | { 64 | type: "Literal", 65 | value: requireString, 66 | raw: `"${requireString}"`, 67 | }, 68 | ], 69 | optional: false, 70 | }, 71 | property: { 72 | type: "Identifier", 73 | name: "default", 74 | }, 75 | computed: false, 76 | optional: false, 77 | }, 78 | operator: "+", 79 | right: { 80 | type: "Literal", 81 | value: hash, 82 | raw: `"${hash}"`, 83 | }, 84 | }, 85 | }, 86 | ], 87 | sourceType: "module", 88 | comments: [], 89 | }, 90 | }, 91 | }; 92 | } 93 | -------------------------------------------------------------------------------- /packages/mdx-loader/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Emit */ 4 | "target": "ES2020", 5 | "module": "commonjs", 6 | "lib": ["ESNext", "DOM"], 7 | "declaration": true, 8 | "declarationMap": false, 9 | "jsx": "react", 10 | "importHelpers": true, 11 | "noEmitHelpers": true, 12 | 13 | /* Strict Type-Checking Options */ 14 | "allowUnreachableCode": false, 15 | // Too hard to turn on 16 | "allowJs": true, 17 | "exactOptionalPropertyTypes": false, 18 | "noFallthroughCasesInSwitch": true, 19 | "noImplicitOverride": true, 20 | "noImplicitReturns": true, 21 | // `process.env` is usually accessed as property 22 | "noPropertyAccessFromIndexSignature": false, 23 | "noUncheckedIndexedAccess": true, 24 | /* strict family */ 25 | "strict": true, 26 | "alwaysStrict": true, 27 | "noImplicitAny": true, 28 | "noImplicitThis": true, 29 | "strictBindCallApply": true, 30 | "strictFunctionTypes": true, 31 | "strictNullChecks": true, 32 | "strictPropertyInitialization": true, 33 | "useUnknownInCatchVariables": true, 34 | /* Handled by ESLint */ 35 | "noUnusedLocals": false, 36 | "noUnusedParameters": false, 37 | "importsNotUsedAsValues": "remove", 38 | 39 | /* Module Resolution */ 40 | "moduleResolution": "node", 41 | "resolveJsonModule": true, 42 | "allowSyntheticDefaultImports": true, 43 | "esModuleInterop": true, 44 | "forceConsistentCasingInFileNames": true, 45 | "isolatedModules": true, 46 | "skipLibCheck": true, // @types/webpack and webpack/types.d.ts are not the same thing 47 | "incremental": true, 48 | "tsBuildInfoFile": "./lib/.tsbuildinfo", 49 | "rootDir": "src", 50 | "outDir": "lib" 51 | }, 52 | "exclude": ["node_modules", "**/__tests__/**/*", "**/lib/**/*"] 53 | } 54 | -------------------------------------------------------------------------------- /packages/theme/index.js: -------------------------------------------------------------------------------- 1 | module.exports = function themeMDXv2() { 2 | return { 3 | name: "docusaurus-theme-mdx-v2", 4 | 5 | getThemePath() { 6 | return "./theme"; 7 | }, 8 | configureWebpack(config) { 9 | config.module.rules.forEach((rule) => { 10 | if (String(rule.test) === String(/\.mdx?$/i)) { 11 | rule.use = rule.use.map((use) => { 12 | if (use.loader.includes("mdx-loader")) { 13 | return { 14 | ...use, 15 | // loader: require.resolve("@docusaurus/mdx-loader"), 16 | loader: require.resolve("docusaurus-mdx-loader-v2"), 17 | }; 18 | } else { 19 | return use; 20 | } 21 | }); 22 | } 23 | }); 24 | 25 | return { 26 | mergeStrategy: { "module.rules": "replace" }, 27 | module: { rules: config.module.rules }, 28 | }; 29 | }, 30 | }; 31 | }; 32 | -------------------------------------------------------------------------------- /packages/theme/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docusaurus-theme-mdx-v2", 3 | "version": "0.1.2", 4 | "description": "Docusaurus Theme for MDX v2", 5 | "main": "index.js", 6 | "publishConfig": { 7 | "access": "public" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/pomber/docusaurus-mdx-2", 12 | "directory": "packages/theme" 13 | }, 14 | "license": "MIT", 15 | "dependencies": { 16 | "@mdx-js/react": "^2.1.0", 17 | "docusaurus-mdx-loader-v2": "0.1.2" 18 | }, 19 | "scripts": { 20 | "build": "cd .", 21 | "dev": "cd ." 22 | }, 23 | "engines": { 24 | "node": ">=14" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/theme/theme/MDXContent/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { MDXProvider } from "@mdx-js/react"; 3 | import MDXComponents from "@theme/MDXComponents"; 4 | 5 | export default function MDXContent({ children }) { 6 | return {children}; 7 | } 8 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Docusaurus Theme MDX v2 2 | 3 | This is a Docusaurus theme to add support for MDX v2 [until it is officially supported](https://github.com/facebook/docusaurus/issues/4029). 4 | 5 | ## Installation 6 | 7 | 1. Upgrade Docusaurus to version `2.0.0-beta.18` or later 8 | 2. Upgrade your `@mdx-js/react` dependency to version `^2.0.0` 9 | 3. Install `docusaurus-theme-mdx-v2` 10 | 11 | ``` 12 | npm install docusaurus-theme-mdx-v2 13 | ``` 14 | 15 | 4. Add `mdx-v2` theme to your `docusaurus.config.js` 16 | 17 | ```js 18 | // ... 19 | const config = { 20 | //... 21 | presets: [...], 22 | themes: ["mdx-v2"], 23 | themeConfig: ... 24 | //... 25 | } 26 | // ... 27 | ``` 28 | 29 | 5. Migrate your .md and .mdx files to MDX v2 if you need. The most common breaking changes are: 30 | 31 | - You cannot use comments like this `` anymore. Now you should use something like `{/* prettier-ignore */}` 32 | - You need to escape `{` in your prose like this `\{` 33 | - If you are using components that aren't imported or part of the `MDXComponents`, MDX v2 will throw an error (v1 only showed a warning) 34 | 35 | ## Known Issues 36 | 37 | ### Admonitions syntax doesn't work with MDX v2 38 | 39 | Instead of using the markdown syntax for admonitions, you should use the 40 | `` component. 41 | 42 | Instead of this: 43 | 44 | ```md 45 | :::note Your Title 46 | 47 | Some **content** with _markdown_ `syntax`. 48 | 49 | ::: 50 | ``` 51 | 52 | Use this: 53 | 54 | ```md 55 | import Admonition from '@theme/Admonition' 56 | 57 | 58 | 59 | Some **content** with _markdown_ `syntax`. 60 | 61 | 62 | ``` 63 | 64 | ## License 65 | 66 | MIT 67 | --------------------------------------------------------------------------------