├── .devcontainer └── devcontainer.json ├── .github └── workflows │ ├── build.yml │ ├── publish-pages.yml │ └── publish.yml ├── .gitignore ├── .prettierrc.cjs ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── RELEASE.md ├── SECURITY.md ├── SUPPORT.md ├── admin └── scripts │ └── copyUntypedFiles.mjs ├── nx.json ├── package.json ├── packages ├── docusaurus-plugin-application-insights │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ └── options.test.ts │ │ ├── analytics.ts │ │ ├── index.ts │ │ └── options.ts │ ├── tsconfig.client.json │ ├── tsconfig.json │ └── vitest.config.ts ├── docusaurus-plugin-rise4fun │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── options.ts │ │ └── rise4fun.css │ └── tsconfig.json ├── docusaurus-remark-plugin-code-element │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ ├── __fixtures__ │ │ │ │ └── compile.md │ │ │ ├── __snapshots__ │ │ │ │ └── index.test.ts.snap │ │ │ └── index.test.ts │ │ ├── index.ts │ │ └── types.d.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── docusaurus-remark-plugin-code-tabs │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ ├── __fixtures__ │ │ │ │ ├── codesandbox.md │ │ │ │ ├── groups.md │ │ │ │ ├── onetab.md │ │ │ │ └── twotabs.md │ │ │ ├── __snapshots__ │ │ │ │ └── index.test.ts.snap │ │ │ └── index.test.ts │ │ ├── index.ts │ │ └── types.d.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── docusaurus-remark-plugin-compile-code │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ ├── __fixtures__ │ │ │ │ ├── compile.md │ │ │ │ ├── meta.md │ │ │ │ ├── nodebin.md │ │ │ │ ├── nodes.md │ │ │ │ ├── puppet.html │ │ │ │ └── puppet.md │ │ │ ├── __snapshots__ │ │ │ │ └── index.test.ts.snap │ │ │ └── index.test.ts │ │ ├── hash.ts │ │ ├── index.ts │ │ └── types.d.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── docusaurus-remark-plugin-import-file │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ ├── __fixtures__ │ │ │ │ ├── compile.md │ │ │ │ └── test.md │ │ │ ├── __snapshots__ │ │ │ │ └── index.test.ts.snap │ │ │ └── index.test.ts │ │ ├── index.ts │ │ └── types.d.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── docusaurus-remark-plugin-side-editor │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ ├── __fixtures__ │ │ │ │ └── edit.md │ │ │ ├── __snapshots__ │ │ │ │ └── index.test.ts.snap │ │ │ └── index.test.ts │ │ ├── index.ts │ │ └── types.d.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── docusaurus-theme-codesandbox-button │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── demo.png │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── theme-codesandbox-button.ts │ │ └── theme │ │ │ └── CodeSandboxButton │ │ │ ├── index.tsx │ │ │ └── styles.module.css │ ├── tsconfig.client.json │ └── tsconfig.json ├── docusaurus-theme-side-editor │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── client │ │ │ ├── SideEditorContext.tsx │ │ │ ├── useHtmlDataTheme.ts │ │ │ └── useSideEditorConfig.ts │ │ ├── index.ts │ │ ├── theme-side-editor.ts │ │ └── theme │ │ │ ├── IFrameEditor │ │ │ ├── index.tsx │ │ │ └── styles.module.css │ │ │ ├── Root │ │ │ └── index.tsx │ │ │ ├── SideEditorButton │ │ │ ├── index.tsx │ │ │ └── styles.module.css │ │ │ ├── SideEditorCodePanel │ │ │ ├── index.tsx │ │ │ └── styles.module.css │ │ │ └── SideEditorRoot │ │ │ ├── ResizeHandle.module.css │ │ │ ├── ResizeHandle.tsx │ │ │ └── index.tsx │ ├── tsconfig.client.json │ └── tsconfig.json └── tsconfig.json ├── website ├── .gitignore ├── README.md ├── babel.config.js ├── docs │ ├── getting-started │ │ ├── analytics.mdx │ │ ├── configuration.mdx │ │ ├── deployment.mdx │ │ ├── index.mdx │ │ ├── maintenance.mdx │ │ ├── search.mdx │ │ └── search.png │ ├── intro.mdx │ ├── markdown-features │ │ ├── code-element.mdx │ │ ├── code-tabs.mdx │ │ ├── codesandbox-button.mdx │ │ ├── compile-code │ │ │ ├── custom-tool.mdx │ │ │ ├── html-page.mdx │ │ │ ├── index.mdx │ │ │ ├── node-script.mdx │ │ │ └── npm-tool.mdx │ │ ├── diagrams.mdx │ │ ├── docusaurus-extras.mdx │ │ ├── hello.mdp │ │ ├── import-file.mdx │ │ ├── index.mdx │ │ ├── math-equations.mdx │ │ └── side-editor.mdx │ └── plugins │ │ ├── docusaurus-plugin-application-insights.mdx │ │ ├── docusaurus-plugin-rise4fun.mdx │ │ ├── docusaurus-remark-plugin-code-element.mdx │ │ ├── docusaurus-remark-plugin-code-tabs.mdx │ │ ├── docusaurus-remark-plugin-compile-code.mdx │ │ ├── docusaurus-remark-plugin-import-file.mdx │ │ ├── docusaurus-remark-plugin-side-editor.mdx │ │ ├── docusaurus-theme-codesandbox-button.mdx │ │ ├── docusaurus-theme-side-editor.mdx │ │ └── index.mdx ├── docusaurus.config.js ├── langs │ ├── msagl.html │ ├── svgo.js │ └── z3.mjs ├── package.json ├── sidebars.js ├── src │ ├── components │ │ └── HomepageFeatures │ │ │ ├── index.tsx │ │ │ └── styles.module.css │ ├── css │ │ └── custom.css │ ├── pages │ │ ├── index.module.css │ │ └── index.tsx │ └── theme │ │ ├── BrowserWindow │ │ ├── index.tsx │ │ └── styles.module.css │ │ └── Cow │ │ └── index.tsx ├── static │ ├── .nojekyll │ ├── editors │ │ └── msagljs.html │ └── img │ │ └── logo.svg └── tsconfig.json └── yarn.lock /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node 3 | { 4 | "name": "Node.js & TypeScript", 5 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile 6 | "image": "mcr.microsoft.com/devcontainers/typescript-node:0-18", 7 | "features": { 8 | "ghcr.io/devcontainers/features/node:1": {} 9 | }, 10 | "customizations": { 11 | "vscode": { 12 | "extensions": ["unifiedjs.vscode-mdx"] 13 | } 14 | }, 15 | 16 | // Features to add to the dev container. More info: https://containers.dev/features. 17 | // "features": {}, 18 | 19 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 20 | // "forwardPorts": [], 21 | 22 | // Use 'postCreateCommand' to run commands after the container is created. 23 | "postCreateCommand": [ 24 | "which volta || curl https://get.volta.sh | bash", 25 | "yarn install --frozen-lockfile" 26 | ] 27 | 28 | // Configure tool-specific properties. 29 | // "customizations": {}, 30 | 31 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. 32 | // "remoteUser": "root" 33 | } 34 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | tags: 9 | - v* 10 | pull_request: {} 11 | 12 | jobs: 13 | build: 14 | name: Build and Test 15 | runs-on: ubuntu-latest 16 | env: 17 | GITHUB_REPOSITORY: ${{ github.repository }} 18 | GITHUB_REF: ${{ github.ref }} 19 | GITHUB_SHA: ${{ github.sha }} 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | - uses: volta-cli/action@v4 24 | 25 | - name: Cache ris4fun compile code 26 | id: docusaurus-rise4fun-compile-code 27 | uses: actions/cache@v3 28 | with: 29 | path: website/.docusaurus/docusaurus-remark-plugin-compile-code/ 30 | key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} 31 | 32 | - name: yarn install 33 | run: yarn install --frozen-lockfile 34 | 35 | - name: Build all 36 | run: yarn build 37 | 38 | - name: Test all 39 | run: yarn test 40 | -------------------------------------------------------------------------------- /.github/workflows/publish-pages.yml: -------------------------------------------------------------------------------- 1 | name: Publish Pages Site 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | tags: 7 | - v* 8 | 9 | jobs: 10 | build: 11 | name: Publish Pages Site 12 | runs-on: ubuntu-latest 13 | env: 14 | GITHUB_REPOSITORY: ${{ github.repository }} 15 | GITHUB_REF: ${{ github.ref }} 16 | GITHUB_SHA: ${{ github.sha }} 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | - uses: volta-cli/action@v4 21 | 22 | - name: Cache ris4fun compile code 23 | id: docusaurus-rise4fun-compile-code 24 | uses: actions/cache@v3 25 | with: 26 | path: website/.docusaurus/docusaurus-remark-plugin-compile-code/ 27 | key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} 28 | 29 | - name: yarn install 30 | run: yarn install --frozen-lockfile 31 | 32 | - name: Build all 33 | run: yarn build 34 | 35 | - name: Test all 36 | run: yarn test 37 | 38 | - name: Deploy to GitHub Pages 39 | uses: peaceiris/actions-gh-pages@v3 40 | with: 41 | github_token: ${{ secrets.GITHUB_TOKEN }} 42 | publish_dir: website/build 43 | force_orphan: true 44 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Node Packages 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | workflow_dispatch: {} 8 | 9 | jobs: 10 | publish: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v3 15 | - uses: volta-cli/action@v4 16 | 17 | - name: yarn install 18 | run: yarn install --frozen-lockfile 19 | 20 | - name: Build all 21 | run: yarn build 22 | 23 | - name: Publish Packages 24 | run: | 25 | cd packages 26 | for package in $(ls -d */); do 27 | echo "Publishing $package..." 28 | cd $package 29 | echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" >> .npmrc 30 | npm publish . --no-workspaces 31 | cd .. 32 | done 33 | env: 34 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store 4 | docusaurus 5 | .yarn/cache 6 | .yarn/install-state.gz 7 | -------------------------------------------------------------------------------- /.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | singleQuote: true, 3 | trailingComma: 'es5', 4 | printWidth: 100, 5 | }; 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["unifiedjs.vscode-mdx", "esbenp.prettier-vscode"] 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "docusaurus start", 11 | "skipFiles": ["/**"], 12 | "cwd": "${workspaceFolder}/website", 13 | "program": "node_modules/.bin/docusaurus", 14 | "args": ["start"], 15 | "runtimeVersion": "18", 16 | "nodeVersionHint": 18 17 | }, 18 | { 19 | "type": "node", 20 | "request": "launch", 21 | "name": "docusaurus build", 22 | "skipFiles": ["/**"], 23 | "cwd": "${workspaceFolder}/website", 24 | "program": "node_modules/.bin/docusaurus", 25 | "args": ["build"], 26 | "nodeVersionHint": 18 27 | }, 28 | { 29 | "type": "node", 30 | "request": "launch", 31 | "name": "code-tabs", 32 | "skipFiles": ["/**"], 33 | "cwd": "${workspaceFolder}/packages/docusaurus-remark-plugin-code-tabs", 34 | "program": "node_modules/.bin/jest", 35 | "args": [""] 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.mdp": "mdx" 4 | }, 5 | "cSpell.words": ["codesandbox", "codesandboxes", "descriptscript", "devicescript", "yacl"] 6 | } 7 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rise4fun Docusaurus plugins 2 | 3 | This repository contains plugins for Docusaurus to support creating rich documentation for programming languages tools. 4 | 5 | - [Read the documentation](https://microsoft.github.io/docusaurus-plugins). 6 | 7 | ## Development 8 | 9 | Install [Volta](https://docs.volta.sh/guide/getting-started), if you haven't got it installed already. 10 | 11 | Once installed, Volta will automatically install the correct version of Node.js and Yarn for this project - no need to install them manually. 12 | 13 | Install dependencies, 14 | 15 | ```bash 16 | yarn install 17 | ``` 18 | 19 | To build plugins and final web site, 20 | 21 | ```bash 22 | yarn build 23 | ``` 24 | 25 | To run a local development server, 26 | 27 | ```bash 28 | yarn start 29 | ``` 30 | 31 | To create releases, use the semantic release commit syntax 32 | 33 | - `feat: ...` for a minor version bump 34 | - `fix: ...` for a patch version bump 35 | 36 | ## Contributing 37 | 38 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 39 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 40 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 41 | 42 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 43 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 44 | provided by the bot. You will only need to do this once across all repos using our CLA. 45 | 46 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 47 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 48 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 49 | 50 | ## Trademarks 51 | 52 | This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft 53 | trademarks or logos is subject to and must follow 54 | [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). 55 | Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. 56 | Any use of third-party trademarks or logos are subject to those third-party's policies. 57 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release Process 2 | 3 | Releases are mostly automated using 4 | [release-it](https://github.com/release-it/release-it/) and 5 | [lerna-changelog](https://github.com/lerna/lerna-changelog/). 6 | 7 | ## Preparation 8 | 9 | Since the majority of the actual release process is automated, the primary 10 | remaining task prior to releasing is confirming that all pull requests that 11 | have been merged since the last release have been labeled with the appropriate 12 | `lerna-changelog` labels and the titles have been updated to ensure they 13 | represent something that would make sense to our users. Some great information 14 | on why this is important can be found at 15 | [keepachangelog.com](https://keepachangelog.com/en/1.0.0/), but the overall 16 | guiding principle here is that changelogs are for humans, not machines. 17 | 18 | When reviewing merged PR's the labels to be used are: 19 | 20 | - breaking - Used when the PR is considered a breaking change. 21 | - enhancement - Used when the PR adds a new feature or enhancement. 22 | - bug - Used when the PR fixes a bug included in a previous release. 23 | - documentation - Used when the PR adds or updates documentation. 24 | - internal - Used for internal changes that still require a mention in the 25 | changelog/release notes. 26 | 27 | ## Release 28 | 29 | Once the prep work is completed, the actual release is straight forward: 30 | 31 | - First, ensure that you have installed your projects dependencies: 32 | 33 | ```sh 34 | yarn install 35 | ``` 36 | 37 | - Second, ensure that you have obtained a 38 | [GitHub personal access token][generate-token] with the `repo` scope (no 39 | other permissions are needed). Make sure the token is available as the 40 | `GITHUB_AUTH` environment variable. 41 | 42 | For instance: 43 | 44 | ```bash 45 | export GITHUB_AUTH=abc123def456 46 | ``` 47 | 48 | [generate-token]: https://github.com/settings/tokens/new?scopes=repo&description=GITHUB_AUTH+env+variable 49 | 50 | - And last (but not least 😁) do your release. 51 | 52 | ```sh 53 | npx release-it 54 | ``` 55 | 56 | [release-it](https://github.com/release-it/release-it/) manages the actual 57 | release process. It will prompt you to to choose the version number after which 58 | you will have the chance to hand tweak the changelog to be used (for the 59 | `CHANGELOG.md` and GitHub release), then `release-it` continues on to tagging, 60 | pushing the tag and commits, etc. 61 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | - Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | - Full paths of source file(s) related to the manifestation of the issue 23 | - The location of the affected source code (tag/branch/commit or direct URL) 24 | - Any special configuration required to reproduce the issue 25 | - Step-by-step instructions to reproduce the issue 26 | - Proof-of-concept or exploit code (if possible) 27 | - Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # TODO: The maintainer of this repo has not yet edited this file 2 | 3 | **REPO OWNER**: Do you want Customer Service & Support (CSS) support for this product/project? 4 | 5 | - **No CSS support:** Fill out this template with information about how to file issues and get help. 6 | - **Yes CSS support:** Fill out an intake form at [aka.ms/onboardsupport](https://aka.ms/onboardsupport). CSS will work with/help you to determine next steps. 7 | - **Not sure?** Fill out an intake as though the answer were "Yes". CSS will help you decide. 8 | 9 | _Then remove this first heading from this SUPPORT.MD file before publishing your repo._ 10 | 11 | # Support 12 | 13 | ## How to file issues and get help 14 | 15 | This project uses GitHub Issues to track bugs and feature requests. Please search the existing 16 | issues before filing new issues to avoid duplicates. For new issues, file your bug or 17 | feature request as a new Issue. 18 | 19 | For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE 20 | FOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER 21 | CHANNEL. WHERE WILL YOU HELP PEOPLE?**. 22 | 23 | ## Microsoft Support Policy 24 | 25 | Support for this **PROJECT or PRODUCT** is limited to the resources listed above. 26 | -------------------------------------------------------------------------------- /admin/scripts/copyUntypedFiles.mjs: -------------------------------------------------------------------------------- 1 | // copied from facebook/docusaurus/admin/scripts/copyUntypedFiles.js 2 | 3 | import fs from 'fs-extra'; 4 | import path from 'path'; 5 | import chokidar from 'chokidar'; 6 | 7 | const srcDir = path.join(process.cwd(), 'src'); 8 | const libDir = path.join(process.cwd(), 'lib'); 9 | 10 | const ignoredPattern = /(?:__tests__|\.tsx?$)/; 11 | 12 | async function copy() { 13 | await fs.copy(srcDir, libDir, { 14 | filter(testedPath) { 15 | return !ignoredPattern.test(testedPath); 16 | }, 17 | }); 18 | } 19 | 20 | if (process.argv.includes('--watch')) { 21 | const watcher = chokidar.watch(srcDir, { 22 | ignored: ignoredPattern, 23 | ignoreInitial: true, 24 | persistent: true, 25 | }); 26 | ['add', 'change', 'unlink', 'addDir', 'unlinkDir'].forEach((event) => watcher.on(event, copy)); 27 | } else { 28 | await copy(); 29 | } 30 | -------------------------------------------------------------------------------- /nx.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasksRunnerOptions": { 3 | "default": { 4 | "runner": "nx/tasks-runners/default", 5 | "options": { 6 | "cacheableOperations": ["build", "test"] 7 | } 8 | } 9 | }, 10 | "targetDefaults": { 11 | "build": { 12 | "dependsOn": ["^build"], 13 | "outputs": ["{projectRoot}/lib"] 14 | }, 15 | "test": { 16 | "dependsOn": ["^test"] 17 | } 18 | }, 19 | "defaultBase": "main" 20 | } 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "4.0.0", 3 | "private": true, 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/microsoft/docusaurus-plugins.git" 7 | }, 8 | "workspaces": [ 9 | "packages/*", 10 | "website" 11 | ], 12 | "scripts": { 13 | "build": "nx run-many --target=build", 14 | "clear": "nx run-many --target=clear && nx reset", 15 | "patch": "release-it patch", 16 | "start": "cd website && yarn start", 17 | "test": "nx run-many --target=test", 18 | "watch": "nx run-many --target=watch" 19 | }, 20 | "release": { 21 | "branches": [ 22 | "main" 23 | ] 24 | }, 25 | "devDependencies": { 26 | "@release-it-plugins/lerna-changelog": "^5.0.0", 27 | "@release-it-plugins/workspaces": "^3.2.0", 28 | "nx": "15.8.7", 29 | "prettier": "^2.8.7", 30 | "release-it": "^15.9.1", 31 | "vitest": "^0.29.7" 32 | }, 33 | "volta": { 34 | "node": "18.15.0", 35 | "yarn": "1.22.19" 36 | }, 37 | "publishConfig": { 38 | "registry": "https://registry.npmjs.org" 39 | }, 40 | "release-it": { 41 | "plugins": { 42 | "@release-it-plugins/lerna-changelog": { 43 | "infile": "CHANGELOG.md", 44 | "launchEditor": true, 45 | "workspaces": [ 46 | "packages/*" 47 | ] 48 | }, 49 | "@release-it-plugins/workspaces": { 50 | "publish": false 51 | } 52 | }, 53 | "git": { 54 | "tagName": "v${version}" 55 | }, 56 | "github": { 57 | "release": true 58 | }, 59 | "npm": false 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /packages/docusaurus-plugin-application-insights/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store 4 | lib 5 | -------------------------------------------------------------------------------- /packages/docusaurus-plugin-application-insights/README.md: -------------------------------------------------------------------------------- 1 | # `@rise4fun/docusaurus-plugin-application-insights` 2 | 3 | Docusaurus plugin to support Microsoft Application Insights ([npm.js](https://www.npmjs.com/package/@rise4fun/docusaurus-plugin-application-insights)). 4 | 5 | ## Usage 6 | 7 | See [docusaurus-plugin-application-insights documentation](https://microsoft.github.io/docusaurus-plugins/docs/plugins/docusaurus-plugin-application-insights). 8 | -------------------------------------------------------------------------------- /packages/docusaurus-plugin-application-insights/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rise4fun/docusaurus-plugin-application-insights", 3 | "version": "4.0.0", 4 | "description": "Microsoft Application Insights plugin for Docusaurus.", 5 | "keywords": [ 6 | "Docusaurus", 7 | "rise4fun" 8 | ], 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/microsoft/docusaurus-plugins.git", 12 | "directory": "packages/docusaurus-plugin-application-insights" 13 | }, 14 | "license": "MIT", 15 | "main": "lib/index.js", 16 | "types": "lib/index.d.ts", 17 | "files": [ 18 | "lib" 19 | ], 20 | "scripts": { 21 | "build": "tsc --build", 22 | "clear": "rm -Rf lib", 23 | "test": "vitest run", 24 | "watch": "tsc --build --watch" 25 | }, 26 | "dependencies": { 27 | "@microsoft/applicationinsights-clickanalytics-js": "^3.0.2", 28 | "@microsoft/applicationinsights-web": "^3.0.2", 29 | "tslib": "^2.5.0", 30 | "validate-peer-dependencies": "^2.2.0" 31 | }, 32 | "devDependencies": { 33 | "@docusaurus/core": "^2.4.0", 34 | "@docusaurus/types": "^2.4.0", 35 | "@docusaurus/utils-validation": "^2.4.0", 36 | "typescript": "^4.9.5" 37 | }, 38 | "peerDependencies": { 39 | "@docusaurus/core": ">=2.4.0", 40 | "@docusaurus/types": ">=2.4.0", 41 | "@docusaurus/utils-validation": ">=2.4.0" 42 | }, 43 | "engines": { 44 | "node": ">=16.14" 45 | }, 46 | "publishConfig": { 47 | "access": "public" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/docusaurus-plugin-application-insights/src/__tests__/options.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from 'vitest'; 2 | import { normalizePluginOptions } from '@docusaurus/utils-validation'; 3 | import { validateOptions } from '../index'; 4 | import { Options, PluginOptions, normalizeConfig } from '../options'; 5 | import type { Validate } from '@docusaurus/types'; 6 | 7 | function testValidateOptions(options: Options) { 8 | return validateOptions({ 9 | validate: normalizePluginOptions as Validate, 10 | options, 11 | }); 12 | } 13 | 14 | describe('application-insights options', () => { 15 | it('throws for undefined options', () => { 16 | expect( 17 | // @ts-expect-error: TS should error 18 | () => testValidateOptions(undefined) 19 | ).toThrowErrorMatchingInlineSnapshot('"\\"config\\" is required"'); 20 | }); 21 | 22 | it('throws for null options', () => { 23 | expect( 24 | // @ts-expect-error: TS should error 25 | () => testValidateOptions(null) 26 | ).toThrowErrorMatchingInlineSnapshot('"\\"value\\" must be of type object"'); 27 | }); 28 | 29 | it('throws for empty object options', () => { 30 | expect(() => testValidateOptions({} as unknown as Options)).toThrowErrorMatchingInlineSnapshot( 31 | '"\\"config\\" is required"' 32 | ); 33 | }); 34 | 35 | it('throws for number options', () => { 36 | expect( 37 | // @ts-expect-error: TS should error 38 | () => testValidateOptions(42) 39 | ).toThrowErrorMatchingInlineSnapshot('"\\"value\\" must be of type object"'); 40 | }); 41 | 42 | it('throws for null instrumentationKey', () => { 43 | expect( 44 | // @ts-expect-error: TS should error 45 | () => testValidateOptions({ config: { instrumentationKey: null } }) 46 | ).toThrowErrorMatchingInlineSnapshot('"\\"config.instrumentationKey\\" must be a string"'); 47 | }); 48 | 49 | it('throws for number instrumentationKey', () => { 50 | expect( 51 | // @ts-expect-error: TS should error 52 | () => testValidateOptions({ config: { instrumentationKey: 42 } }) 53 | ).toThrowErrorMatchingInlineSnapshot('"\\"config.instrumentationKey\\" must be a string"'); 54 | }); 55 | 56 | it('throws for null connectionString', () => { 57 | expect( 58 | // @ts-expect-error: TS should error 59 | () => testValidateOptions({ config: { connectionString: null } }) 60 | ).toThrowErrorMatchingInlineSnapshot('"\\"config.connectionString\\" must be a string"'); 61 | }); 62 | 63 | it('throws for number connectionString', () => { 64 | expect( 65 | // @ts-expect-error: TS should error 66 | () => testValidateOptions({ config: { connectionString: 42 } }) 67 | ).toThrowErrorMatchingInlineSnapshot('"\\"config.connectionString\\" must be a string"'); 68 | }); 69 | 70 | it('validates missing options', () => { 71 | let options = { 72 | config: {}, 73 | }; 74 | 75 | expect(() => { 76 | testValidateOptions(options); 77 | }).toThrowErrorMatchingInlineSnapshot( 78 | '"\\"config\\" must contain at least one of [instrumentationKey, connectionString]"' 79 | ); 80 | }); 81 | 82 | it('validates if xor options are used together', () => { 83 | let options = { 84 | config: { 85 | instrumentationKey: 'KEY', 86 | connectionString: 'CONNECTION_STRING', 87 | }, 88 | }; 89 | 90 | expect(() => { 91 | testValidateOptions(options); 92 | }).toThrowErrorMatchingInlineSnapshot( 93 | '"\\"config\\" contains a conflict between exclusive peers [instrumentationKey, connectionString]"' 94 | ); 95 | }); 96 | 97 | it('normalizeConfig returns options when new configuration format used', () => { 98 | let options = { 99 | config: { 100 | instrumentationKey: 'KEY', 101 | }, 102 | enableClickAnalytics: true, 103 | }; 104 | 105 | expect(normalizeConfig(options)).toMatchInlineSnapshot(` 106 | { 107 | "config": { 108 | "instrumentationKey": "KEY", 109 | }, 110 | "enableClickAnalytics": true, 111 | } 112 | `); 113 | }); 114 | 115 | it('normalizeConfig returns new configuration format when legacy configuration format used', () => { 116 | let options = { 117 | instrumentationKey: 'KEY', 118 | }; 119 | 120 | expect(normalizeConfig(options)).toMatchInlineSnapshot(` 121 | { 122 | "config": { 123 | "instrumentationKey": "KEY", 124 | }, 125 | "enableClickAnalytics": false, 126 | } 127 | `); 128 | }); 129 | }); 130 | -------------------------------------------------------------------------------- /packages/docusaurus-plugin-application-insights/src/analytics.ts: -------------------------------------------------------------------------------- 1 | import { ClickAnalyticsPlugin } from '@microsoft/applicationinsights-clickanalytics-js'; 2 | import { ApplicationInsights } from '@microsoft/applicationinsights-web'; 3 | import type { ClientModule } from '@docusaurus/types'; 4 | 5 | let appInsights: ApplicationInsights; 6 | 7 | if (typeof window !== 'undefined') { 8 | const pluginConfig = (window as any).appInsightsPluginConfig; 9 | 10 | if (pluginConfig) { 11 | if (pluginConfig.enableClickAnalytics) { 12 | const clickPluginInstance = new ClickAnalyticsPlugin(); 13 | 14 | pluginConfig.config = { 15 | ...pluginConfig.config, 16 | extensions: [clickPluginInstance], 17 | extensionConfig: { 18 | [clickPluginInstance.identifier]: { 19 | autoCapture: true, 20 | dataTags: { 21 | useDefaultContentNameOrId: true, 22 | }, 23 | urlCollectQuery: true, 24 | }, 25 | }, 26 | }; 27 | } 28 | 29 | appInsights = new ApplicationInsights({ 30 | config: pluginConfig.config, 31 | }); 32 | appInsights.loadAppInsights(); 33 | appInsights.trackPageView(); 34 | } 35 | } 36 | 37 | const clientModule: ClientModule = { 38 | onRouteDidUpdate({ location, previousLocation }) { 39 | if ( 40 | previousLocation && 41 | (location.pathname !== previousLocation.pathname || 42 | location.search !== previousLocation.search || 43 | location.hash !== previousLocation.hash) 44 | ) { 45 | // don't log hash, leave for client side data 46 | appInsights?.trackPageView({ 47 | name: location.pathname + location.search, 48 | }); 49 | } 50 | }, 51 | }; 52 | 53 | export default clientModule; 54 | -------------------------------------------------------------------------------- /packages/docusaurus-plugin-application-insights/src/index.ts: -------------------------------------------------------------------------------- 1 | import type { LoadContext, Plugin } from '@docusaurus/types'; 2 | import { PluginOptions, Options, normalizeConfig } from './options'; 3 | import { resolve } from 'node:path'; 4 | import validatePeerDependencies from 'validate-peer-dependencies'; 5 | 6 | validatePeerDependencies(__dirname); 7 | 8 | export default function pluginApplicationInsights( 9 | _context: LoadContext, 10 | options: PluginOptions 11 | ): Plugin { 12 | const isProd = process.env.NODE_ENV === 'production'; 13 | return { 14 | name: '@rise4fun/docusaurus-plugin-application-insights', 15 | 16 | getClientModules() { 17 | return isProd ? [resolve(__dirname, './analytics')] : []; 18 | }, 19 | 20 | injectHtmlTags() { 21 | if (!isProd) { 22 | return {}; 23 | } 24 | 25 | options = normalizeConfig(options); 26 | 27 | return { 28 | headTags: [ 29 | { 30 | tagName: 'script', 31 | innerHTML: `window.appInsightsPluginConfig = ${JSON.stringify(options)};`, 32 | }, 33 | ], 34 | }; 35 | }, 36 | }; 37 | } 38 | 39 | export { validateOptions } from './options'; 40 | 41 | export type { PluginOptions, Options }; 42 | -------------------------------------------------------------------------------- /packages/docusaurus-plugin-application-insights/src/options.ts: -------------------------------------------------------------------------------- 1 | import { deprecate } from 'node:util'; 2 | import { Joi } from '@docusaurus/utils-validation'; 3 | import type { OptionValidationContext } from '@docusaurus/types'; 4 | import { Snippet } from '@microsoft/applicationinsights-web'; 5 | 6 | type Prettify = { 7 | [K in keyof T]: T[K]; 8 | } & {}; 9 | 10 | export type ApplicationInsightsConfig = Prettify; 11 | 12 | export type PluginOptions = { 13 | config: ApplicationInsightsConfig; 14 | enableClickAnalytics: boolean; 15 | }; 16 | 17 | export type Options = { 18 | config: ApplicationInsightsConfig; 19 | enableClickAnalytics?: boolean; 20 | }; 21 | 22 | export const DEPRECATED_CONFIG_MESSAGE = `You passed the Application Insights configuration using the legacy configuration structure. Please use the new configuration structure in the future. 23 | 24 | Example: 25 | 26 | { 27 | config: , 28 | enableClickAnalytics: 29 | }`; 30 | 31 | export function normalizeConfig( 32 | maybeLegacyOptions: ApplicationInsightsConfig | PluginOptions 33 | ): PluginOptions { 34 | // If we have a `config` property, we're using the new configuration API 35 | if ('config' in maybeLegacyOptions) { 36 | return maybeLegacyOptions; 37 | } 38 | 39 | let options: PluginOptions; 40 | 41 | // If we don't have a `config` property, we're using the legacy configuration API, so we normalize it 42 | const deprecateConfig = deprecate(() => { 43 | options = { 44 | config: maybeLegacyOptions as ApplicationInsightsConfig, 45 | enableClickAnalytics: false, 46 | }; 47 | }, DEPRECATED_CONFIG_MESSAGE); 48 | 49 | deprecateConfig(); 50 | 51 | return options!; 52 | } 53 | 54 | const pluginOptionsSchema = Joi.object({ 55 | config: Joi.object({ 56 | instrumentationKey: Joi.string().empty(''), 57 | connectionString: Joi.string().empty(''), 58 | diagnosticLogInterval: Joi.number().optional(), 59 | maxMessageLimit: Joi.number().optional(), 60 | loggingLevelConsole: Joi.number().optional(), 61 | loggingLevelTelemetry: Joi.number().optional(), 62 | enableDebug: Joi.boolean().optional(), 63 | enableDebugExceptions: Joi.boolean().optional(), 64 | endpointUrl: Joi.string().optional(), 65 | extensionConfig: Joi.object().optional(), 66 | extensions: Joi.array().items(Joi.object()).optional(), 67 | channels: Joi.array().items(Joi.array().items(Joi.object())).optional(), 68 | disableInstrumentationKeyValidation: Joi.boolean().optional(), 69 | enablePerfMgr: Joi.boolean().optional(), 70 | createPerfMgr: Joi.func().optional(), 71 | perfEvtsSendAll: Joi.boolean().optional(), 72 | idLength: Joi.number().optional(), 73 | cookieDomain: Joi.string().optional(), 74 | cookiePath: Joi.string().optional(), 75 | disableCookiesUsage: Joi.boolean().optional(), 76 | cookieCfg: Joi.object().optional(), 77 | disablePageUnloadEvents: Joi.array().items(Joi.string()).optional(), 78 | disablePageShowEvents: Joi.array().items(Joi.string()).optional(), 79 | disableDbgExt: Joi.boolean().optional(), 80 | emitLineDelimitedJson: Joi.boolean().optional(), 81 | accountId: Joi.string().optional(), 82 | sessionRenewalMs: Joi.number().optional(), 83 | sessionExpirationMs: Joi.number().optional(), 84 | maxBatchSizeInBytes: Joi.number().optional(), 85 | maxBatchInterval: Joi.number().optional(), 86 | disableExceptionTracking: Joi.boolean().optional(), 87 | disableTelemetry: Joi.boolean().optional(), 88 | samplingPercentage: Joi.number().optional(), 89 | autoTrackPageVisitTime: Joi.boolean().optional(), 90 | enableAutoRouteTracking: Joi.boolean().optional(), 91 | disableAjaxTracking: Joi.boolean().optional(), 92 | disableFetchTracking: Joi.boolean().optional(), 93 | excludeRequestFromAutoTrackingPatterns: Joi.array() 94 | .items(Joi.alternatives().try(Joi.string(), Joi.object().instance(RegExp))) 95 | .optional(), 96 | addRequestContext: Joi.func().optional(), 97 | overridePageViewDuration: Joi.boolean().optional(), 98 | maxAjaxCallsPerView: Joi.number().optional(), 99 | disableDataLossAnalysis: Joi.boolean().optional(), 100 | disableCorrelationHeaders: Joi.boolean().optional(), 101 | distributedTracingMode: Joi.string().optional(), 102 | correlationHeaderExcludedDomains: Joi.array().items(Joi.string()).optional(), 103 | disableFlushOnBeforeUnload: Joi.boolean().optional(), 104 | disableFlushOnUnload: Joi.boolean().optional(), 105 | enableSessionStorageBuffer: Joi.boolean().optional(), 106 | bufferOverride: Joi.object().optional(), 107 | isCookieUseDisabled: Joi.boolean().optional(), 108 | isRetryDisabled: Joi.boolean().optional(), 109 | url: Joi.string().optional(), 110 | isStorageUseDisabled: Joi.boolean().optional(), 111 | isBeaconApiDisabled: Joi.boolean().optional(), 112 | disableXhr: Joi.boolean().optional(), 113 | onunloadDisableFetch: Joi.boolean().optional(), 114 | sdkExtension: Joi.string().optional(), 115 | isBrowserLinkTrackingEnabled: Joi.boolean().optional(), 116 | appId: Joi.string().optional(), 117 | enableCorsCorrelation: Joi.boolean().optional(), 118 | namePrefix: Joi.string().optional(), 119 | sessionCookiePostfix: Joi.string().optional(), 120 | userCookiePostfix: Joi.string().optional(), 121 | enableRequestHeaderTracking: Joi.boolean().optional(), 122 | enableResponseHeaderTracking: Joi.boolean().optional(), 123 | enableAjaxErrorStatusText: Joi.boolean().optional(), 124 | enableAjaxPerfTracking: Joi.boolean().optional(), 125 | maxAjaxPerfLookupAttempts: Joi.number().optional(), 126 | ajaxPerfLookupDelay: Joi.number().optional(), 127 | onunloadDisableBeacon: Joi.boolean().optional(), 128 | autoExceptionInstrumented: Joi.boolean().optional(), 129 | correlationHeaderDomains: Joi.array().items(Joi.string()).optional(), 130 | autoUnhandledPromiseInstrumented: Joi.boolean().optional(), 131 | enableUnhandledPromiseRejectionTracking: Joi.boolean().optional(), 132 | correlationHeaderExcludePatterns: Joi.array().items(Joi.object().instance(RegExp)).optional(), 133 | customHeaders: Joi.array() 134 | .items( 135 | Joi.object({ 136 | header: Joi.string().required(), 137 | value: Joi.string().required(), 138 | }) 139 | ) 140 | .optional(), 141 | convertUndefined: Joi.any().optional(), 142 | eventsLimitInMem: Joi.number().optional(), 143 | disableIkeyDeprecationMessage: Joi.boolean().optional(), 144 | addIntEndpoints: Joi.boolean().optional(), 145 | }) 146 | .required() 147 | .xor('instrumentationKey', 'connectionString'), 148 | 149 | enableClickAnalytics: Joi.boolean().optional(), 150 | }); 151 | 152 | export function validateOptions({ 153 | validate, 154 | options, 155 | }: OptionValidationContext): PluginOptions { 156 | return validate(pluginOptionsSchema, options); 157 | } 158 | -------------------------------------------------------------------------------- /packages/docusaurus-plugin-application-insights/tsconfig.client.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "noEmit": false, 5 | "composite": true, 6 | "incremental": true, 7 | "tsBuildInfoFile": "./lib/.tsbuildinfo-client", 8 | "module": "esnext", 9 | "target": "esnext", 10 | "rootDir": "src", 11 | "outDir": "lib", 12 | "lib": ["ESNext", "dom"] 13 | }, 14 | "include": ["src/analytics.ts", "src/*.d.ts"], 15 | "exclude": ["**/__tests__/**"] 16 | } 17 | -------------------------------------------------------------------------------- /packages/docusaurus-plugin-application-insights/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "references": [{ "path": "./tsconfig.client.json" }], 4 | "compilerOptions": { 5 | "noEmit": false, 6 | "incremental": true, 7 | "tsBuildInfoFile": "./lib/.tsbuildinfo", 8 | "rootDir": "src", 9 | "outDir": "lib" 10 | }, 11 | "include": ["src"], 12 | "exclude": ["src/analytics.ts", "**/__tests__/**"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/docusaurus-plugin-application-insights/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | include: ['**/__tests__/**/*.test.ts'], 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /packages/docusaurus-plugin-rise4fun/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store 4 | lib 5 | -------------------------------------------------------------------------------- /packages/docusaurus-plugin-rise4fun/README.md: -------------------------------------------------------------------------------- 1 | # `@rise4fun/docusaurus-plugins` 2 | 3 | Docusaurus plugin for Microsoft Research Rise4fun web sites ([npm.js](https://www.npmjs.com/package/@rise4fun/docusaurus-plugin-rise4fun)). 4 | 5 | ## Usage 6 | 7 | See [docusaurus-plugin-rise4fun documentation](https://microsoft.github.io/docusaurus-plugins/docs/plugins/docusaurus-plugin-rise4fun). 8 | -------------------------------------------------------------------------------- /packages/docusaurus-plugin-rise4fun/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rise4fun/docusaurus-plugin-rise4fun", 3 | "version": "4.0.0", 4 | "description": "Docusaurus plugin for Microsoft projects.", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/microsoft/docusaurus-plugins.git", 8 | "directory": "packages/docusaurus-plugin-rise4fun" 9 | }, 10 | "license": "MIT", 11 | "main": "lib/index.js", 12 | "types": "lib/index.d.ts", 13 | "scripts": { 14 | "build": "tsc --build && node ../../admin/scripts/copyUntypedFiles.mjs", 15 | "clear": "rm -Rf lib", 16 | "watch": "tsc --build --watch" 17 | }, 18 | "dependencies": { 19 | "@rise4fun/docusaurus-plugin-application-insights": "^4.0.0", 20 | "@rise4fun/docusaurus-remark-plugin-code-element": "^4.0.0", 21 | "@rise4fun/docusaurus-remark-plugin-code-tabs": "^4.0.0", 22 | "@rise4fun/docusaurus-remark-plugin-compile-code": "^4.0.0", 23 | "@rise4fun/docusaurus-remark-plugin-import-file": "^4.0.0", 24 | "@rise4fun/docusaurus-remark-plugin-side-editor": "^4.0.0", 25 | "@rise4fun/docusaurus-theme-codesandbox-button": "^4.0.0", 26 | "@rise4fun/docusaurus-theme-side-editor": "^4.0.0", 27 | "fs-extra": "^11.1.0", 28 | "hast-util-is-element": "1.1.0", 29 | "rehype-katex": "5", 30 | "remark-math": "3", 31 | "tslib": "^2.5.0", 32 | "validate-peer-dependencies": "^2.2.0" 33 | }, 34 | "devDependencies": { 35 | "@docusaurus/core": "^2.4.0", 36 | "@docusaurus/remark-plugin-npm2yarn": "^2.4.0", 37 | "@docusaurus/theme-mermaid": "^2.4.0", 38 | "@docusaurus/types": "^2.4.0", 39 | "@docusaurus/utils-validation": "^2.4.0", 40 | "typescript": "^4.9.5" 41 | }, 42 | "peerDependencies": { 43 | "@docusaurus/core": ">=2.4.0", 44 | "@docusaurus/remark-plugin-npm2yarn": ">=2.4.0", 45 | "@docusaurus/theme-mermaid": ">=2.4.0", 46 | "@docusaurus/types": ">=2.4.0", 47 | "@docusaurus/utils-validation": ">=2.4.0" 48 | }, 49 | "engines": { 50 | "node": ">=16.14" 51 | }, 52 | "publishConfig": { 53 | "access": "public" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /packages/docusaurus-plugin-rise4fun/src/options.ts: -------------------------------------------------------------------------------- 1 | import type { PluginOptions as ApplicationInsightsOptions } from '@rise4fun/docusaurus-plugin-application-insights'; 2 | import type { PluginOptions as CompileCodePluginOptions } from '@rise4fun/docusaurus-remark-plugin-compile-code'; 3 | import type { PluginOptions as CodeTabsPluginOptions } from '@rise4fun/docusaurus-remark-plugin-code-tabs'; 4 | import type { PluginOptions as CodeElementPluginOptions } from '@rise4fun/docusaurus-remark-plugin-code-element'; 5 | import type { PluginOptions as SideEditorRemarkPluginOptions } from '@rise4fun/docusaurus-remark-plugin-side-editor'; 6 | import type { SideEditorThemeConfig } from '@rise4fun/docusaurus-theme-side-editor'; 7 | import type { CodeSandboxButtonThemeConfig } from '@rise4fun/docusaurus-theme-codesandbox-button'; 8 | import type { PluginOptions as ImportFilePluginOptions } from '@rise4fun/docusaurus-remark-plugin-import-file'; 9 | 10 | export interface AlgoliaOptions { 11 | /** 12 | * The application ID provided by Algolia 13 | */ 14 | appId: string; 15 | /** 16 | * Public API key: it is safe to commit it 17 | */ 18 | apiKey: string; 19 | /** 20 | * Index name provided by algolia 21 | */ 22 | indexName: string; 23 | } 24 | 25 | export type PluginOptions = { 26 | /** 27 | * Disable injecting Microsoft legal links 28 | */ 29 | legal?: false; 30 | 31 | /** 32 | * Application Insights configuration to enable analytics 33 | */ 34 | appInsights?: ApplicationInsightsOptions; 35 | 36 | /** 37 | * Precompiled snippets configuration 38 | */ 39 | compileCode?: CompileCodePluginOptions; 40 | 41 | /** 42 | * Set as false to disable injecting `npm2yarn` plugin 43 | */ 44 | npm2yarn?: false; 45 | /** 46 | * Set as false to disable injecting `math`, `katex` plugins 47 | */ 48 | math?: false; 49 | /** 50 | * Set as false to disable code tabs 51 | */ 52 | codeTabs?: false | CodeTabsPluginOptions; 53 | /** 54 | * Sets as false to disable mermaid diagrams. Set as mermaid configuration for customizations. 55 | */ 56 | mermaid?: false | object; 57 | 58 | /** 59 | * Sets as false to disable CodeSandbox button theme. 60 | */ 61 | codeSandbox?: false | CodeSandboxButtonThemeConfig; 62 | 63 | /** 64 | * Sets the side editor options 65 | */ 66 | sideEditor?: SideEditorRemarkPluginOptions & SideEditorThemeConfig; 67 | 68 | /** 69 | * Configure code to MDX plugin 70 | */ 71 | codeElement?: CodeElementPluginOptions; 72 | 73 | /** 74 | * Limited Algolia options 75 | */ 76 | algolia?: AlgoliaOptions; 77 | 78 | /** 79 | * Set to false to disable button 80 | */ 81 | githubButton?: false; 82 | 83 | /** 84 | * Set to false to disable partial imports 85 | */ 86 | importFile?: false | ImportFilePluginOptions; 87 | }; 88 | 89 | export type Options = Partial; 90 | -------------------------------------------------------------------------------- /packages/docusaurus-plugin-rise4fun/src/rise4fun.css: -------------------------------------------------------------------------------- 1 | .markdown a { 2 | --ifm-link-decoration: underline; 3 | } 4 | 5 | .header-github-link:hover { 6 | opacity: 0.6; 7 | } 8 | 9 | .header-github-link::before { 10 | content: ''; 11 | width: 24px; 12 | height: 24px; 13 | display: flex; 14 | 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") 15 | no-repeat; 16 | } 17 | 18 | [data-theme='dark'] .header-github-link::before { 19 | 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") 20 | no-repeat; 21 | } 22 | 23 | [data-theme='light'] .DocSearch { 24 | /* --docsearch-primary-color: var(--ifm-color-primary); */ 25 | /* --docsearch-text-color: var(--ifm-font-color-base); */ 26 | --docsearch-muted-color: var(--ifm-color-emphasis-700); 27 | --docsearch-container-background: rgb(94 100 112 / 70%); 28 | /* Modal */ 29 | --docsearch-modal-background: var(--ifm-color-secondary-lighter); 30 | /* Search box */ 31 | --docsearch-searchbox-background: var(--ifm-color-secondary); 32 | --docsearch-searchbox-focus-background: var(--ifm-color-white); 33 | /* Hit */ 34 | --docsearch-hit-color: var(--ifm-font-color-base); 35 | --docsearch-hit-active-color: var(--ifm-color-white); 36 | --docsearch-hit-background: var(--ifm-color-white); 37 | /* Footer */ 38 | --docsearch-footer-background: var(--ifm-color-white); 39 | } 40 | 41 | [data-theme='dark'] .DocSearch { 42 | --docsearch-text-color: var(--ifm-font-color-base); 43 | --docsearch-muted-color: var(--ifm-color-secondary-darkest); 44 | --docsearch-container-background: rgb(47 55 69 / 70%); 45 | /* Modal */ 46 | --docsearch-modal-background: var(--ifm-background-color); 47 | /* Search box */ 48 | --docsearch-searchbox-background: var(--ifm-background-color); 49 | --docsearch-searchbox-focus-background: var(--ifm-color-black); 50 | /* Hit */ 51 | --docsearch-hit-color: var(--ifm-font-color-base); 52 | --docsearch-hit-active-color: var(--ifm-color-white); 53 | --docsearch-hit-background: var(--ifm-color-emphasis-100); 54 | /* Footer */ 55 | --docsearch-footer-background: var(--ifm-background-surface-color); 56 | --docsearch-key-gradient: linear-gradient( 57 | -26.5deg, 58 | var(--ifm-color-emphasis-200) 0%, 59 | var(--ifm-color-emphasis-100) 100% 60 | ); 61 | } 62 | -------------------------------------------------------------------------------- /packages/docusaurus-plugin-rise4fun/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "noEmit": false, 5 | "incremental": true, 6 | "tsBuildInfoFile": "./lib/.tsbuildinfo", 7 | "rootDir": "src", 8 | "outDir": "lib" 9 | }, 10 | "include": ["src"], 11 | "exclude": ["**/__tests__/**"] 12 | } 13 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-code-element/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store 4 | lib 5 | snippets 6 | .docusaurus -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-code-element/.npmignore: -------------------------------------------------------------------------------- 1 | .tsbuildinfo* 2 | tsconfig* 3 | __tests__ 4 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-code-element/README.md: -------------------------------------------------------------------------------- 1 | # `@rise4fun/docusaurus-remark-plugin-code-element` 2 | 3 | Docusaurus plugin to convert fenced code section into MDX snippets ([npm.js](https://www.npmjs.com/package/@rise4fun/docusaurus-remark-plugin-code-element)). 4 | 5 | ## Usage 6 | 7 | See [docusaurus-remark-plugin-code-element documentation](https://microsoft.github.io/docusaurus-plugins/docs/plugins/docusaurus-remark-plugin-code-element). 8 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-code-element/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rise4fun/docusaurus-remark-plugin-code-element", 3 | "version": "4.0.0", 4 | "description": "Run tool and show output.", 5 | "keywords": [ 6 | "Docusaurus", 7 | "remark-plugin", 8 | "rise4fun" 9 | ], 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/microsoft/docusaurus-plugins.git", 13 | "directory": "packages/docusaurus-remark-plugin-code-element" 14 | }, 15 | "license": "MIT", 16 | "main": "lib/index.js", 17 | "types": "src/types.d.ts", 18 | "scripts": { 19 | "build": "tsc", 20 | "clear": "rm -Rf lib && rm -Rf .docusaurus", 21 | "test": "vitest run", 22 | "watch": "tsc --watch" 23 | }, 24 | "dependencies": { 25 | "fs-extra": "^11.1.0", 26 | "minimatch": "^6.1.6", 27 | "p-all": "^4.0.0", 28 | "tslib": "^2.4.0", 29 | "typescript": "^4.9.4", 30 | "unist-util-visit": "^2.0.3" 31 | }, 32 | "devDependencies": { 33 | "@types/fs-extra": "^11.0.0", 34 | "@types/mdast": "^3.0.10", 35 | "remark": "^12.0.1", 36 | "remark-mdx": "^1.6.21", 37 | "to-vfile": "^6.1.0" 38 | }, 39 | "engines": { 40 | "node": ">=16.14" 41 | }, 42 | "publishConfig": { 43 | "access": "public" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-code-element/src/__tests__/__fixtures__/compile.md: -------------------------------------------------------------------------------- 1 | Some snippets 2 | 3 | ```dot 4 | foo 5 | ``` 6 | 7 | ```dot meta meta1=0 meta2="hello" metaf=false metat=true 8 | foo 9 | ``` 10 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-code-element/src/__tests__/__snapshots__/index.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`compile-code plugin > works on compile file 1`] = ` 4 | "import Dot from '@theme/Dot'; 5 | 6 | Some snippets 7 | 8 | 9 | 10 | 11 | " 12 | `; 13 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-code-element/src/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from 'vitest'; 2 | import path from 'path'; 3 | import vfile from 'to-vfile'; 4 | import mdx from 'remark-mdx'; 5 | import remark from 'remark'; 6 | import plugin from '../index'; 7 | import type { PluginOptions } from '../types'; 8 | 9 | const processFixture = async (name: string, options: PluginOptions) => { 10 | const filePath = path.join(__dirname, '__fixtures__', `${name}.md`); 11 | const file = await vfile.read(filePath); 12 | const result = await remark().use(mdx).use(plugin, options).process(file); 13 | 14 | return result.toString(); 15 | }; 16 | 17 | const options: PluginOptions = { 18 | langs: [ 19 | { 20 | lang: 'dot', 21 | element: 'Dot', 22 | }, 23 | ], 24 | }; 25 | 26 | describe('compile-code plugin', () => { 27 | it('works on compile file', async () => { 28 | const result = await processFixture('compile', options); 29 | expect(result).toMatchSnapshot(); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-code-element/src/index.ts: -------------------------------------------------------------------------------- 1 | import visit from 'unist-util-visit'; 2 | import type { Code, Literal, Root } from 'mdast'; 3 | import type { Plugin } from 'unified'; 4 | import { parse } from 'node:querystring'; 5 | import { PluginOptions } from './types'; 6 | 7 | function parseMeta(node: Code) { 8 | const r = parse(node.meta || '', ' '); 9 | Object.keys(r).forEach((k) => (r[k] = fromAttributeValue(r[k]))); 10 | return r; 11 | } 12 | 13 | function injectThemeImport(root: Root, element: string) { 14 | if ( 15 | !root.children.find( 16 | (n: { type: string; value?: string }) => 17 | n.type === 'import' && n.value && n.value?.includes(`@theme/${element}`) 18 | ) 19 | ) { 20 | root.children.unshift({ 21 | type: 'import', 22 | value: `import ${element} from '@theme/${element}';`, 23 | } as any); 24 | } 25 | } 26 | 27 | function fromAttributeValue(s: string | string[] | undefined) { 28 | if (s === undefined) return s; 29 | if (s === '') return true; 30 | if (s === 'true') return true; 31 | if (s === 'false') return false; 32 | if (typeof s === 'string') { 33 | const i = parseInt(s); 34 | if (!isNaN(i)) return i; 35 | const f = parseFloat(s); 36 | if (!isNaN(f)) return f; 37 | const x = /^0x/.test(s) && parseInt(s.slice(2), 16); 38 | if (x !== false && !isNaN(x)) return x; 39 | } 40 | if (typeof s === 'string' && s[0] === '"') { 41 | try { 42 | return JSON.parse(s); 43 | } catch {} 44 | } 45 | return s; 46 | } 47 | 48 | function toAttributeValue(s: string | undefined) { 49 | if (!s) return s; 50 | try { 51 | const j = JSON.parse(s); 52 | if (typeof j === 'string') s = j; 53 | } catch {} 54 | return JSON.stringify(s); 55 | } 56 | 57 | const plugin: Plugin<[PluginOptions?]> = (options = undefined) => { 58 | const { langs = [] } = options || {}; 59 | 60 | return async (root, vfile) => { 61 | const needsImport = new Set(); 62 | const visited = new Set(); // visit called twice on async 63 | // collect all nodes 64 | visit(root, 'code', (node: Code, nodeIndex: number, parent) => { 65 | const { lang, meta, value } = node; 66 | if (!parent || !lang || visited.has(node)) return; 67 | visited.add(node); 68 | 69 | const langOptions = langs.find((o) => o.lang === lang); 70 | if (!langOptions) return; 71 | 72 | const { element } = langOptions; 73 | const parsedMeta = parseMeta(node); 74 | needsImport.add(element); 75 | parent.children.splice(nodeIndex, 1, { 76 | type: 'jsx', 77 | value: `<${element} lang={${toAttributeValue(lang)}} meta={${JSON.stringify( 78 | parsedMeta 79 | )}} value={${toAttributeValue(value)}} />`, 80 | }); 81 | }); 82 | 83 | needsImport.forEach((element) => injectThemeImport(root as Root, element)); 84 | }; 85 | }; 86 | 87 | // To continue supporting `require('...')` without the `.default` ㄟ(▔,▔)ㄏ 88 | // TODO change to export default after migrating to ESM 89 | export = plugin; 90 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-code-element/src/types.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Plugin options. 3 | */ 4 | export interface PluginOptions { 5 | langs: [ 6 | { 7 | /** 8 | * Code section identifier 9 | */ 10 | lang: string; 11 | /** 12 | * React element name to be imported from `@theme/element` 13 | */ 14 | element: string; 15 | } 16 | ]; 17 | } 18 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-code-element/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "noEmit": false, 5 | "incremental": true, 6 | "tsBuildInfoFile": "./lib/.tsbuildinfo", 7 | "sourceMap": true, 8 | "declarationMap": true, 9 | "rootDir": "src", 10 | "outDir": "lib" 11 | }, 12 | "include": ["src"], 13 | "exclude": ["**/__tests__/**"] 14 | } 15 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-code-element/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | include: ['**/__tests__/**/*.test.ts'], 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-code-tabs/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store 4 | lib 5 | snippets 6 | .docusaurus -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-code-tabs/.npmignore: -------------------------------------------------------------------------------- 1 | .tsbuildinfo* 2 | tsconfig* 3 | __tests__ 4 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-code-tabs/README.md: -------------------------------------------------------------------------------- 1 | # `@rise4fun/docusaurus-remark-plugin-code-tabs` 2 | 3 | Docusaurus remark plugin that assembles code sections into tabs, with optional CodeSandbox button ([npm.js](https://www.npmjs.com/package/@rise4fun/docusaurus-remark-plugin-code-tabs)). 4 | 5 | ## Usage 6 | 7 | See [docusaurus-remark-plugin-code-tabs documentation](https://microsoft.github.io/docusaurus-plugins/docs/plugins/docusaurus-remark-plugin-code-tabs). 8 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-code-tabs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rise4fun/docusaurus-remark-plugin-code-tabs", 3 | "version": "4.0.0", 4 | "description": "Remark docusaurus plugin that assembles code sections in tabs.", 5 | "keywords": [ 6 | "Docusaurus", 7 | "remark-plugin", 8 | "rise4fun" 9 | ], 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/microsoft/docusaurus-plugins.git", 13 | "directory": "packages/docusaurus-remark-plugin-code-tabs" 14 | }, 15 | "license": "MIT", 16 | "main": "lib/index.js", 17 | "types": "src/types.d.ts", 18 | "scripts": { 19 | "build": "tsc", 20 | "clear": "rm -Rf lib && rm -Rf .docusaurus", 21 | "test": "vitest run", 22 | "watch": "tsc --watch" 23 | }, 24 | "dependencies": { 25 | "fs-extra": "^11.1.0", 26 | "p-all": "^4.0.0", 27 | "tslib": "^2.4.0", 28 | "typescript": "^4.9.4", 29 | "unist-util-visit": "^2.0.3" 30 | }, 31 | "devDependencies": { 32 | "@types/fs-extra": "^11.0.0", 33 | "@types/mdast": "^3.0.10", 34 | "remark": "^12.0.1", 35 | "remark-mdx": "^1.6.21", 36 | "to-vfile": "^6.1.0" 37 | }, 38 | "engines": { 39 | "node": ">=16.14" 40 | }, 41 | "publishConfig": { 42 | "access": "public" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-code-tabs/src/__tests__/__fixtures__/codesandbox.md: -------------------------------------------------------------------------------- 1 | Tabs with codesandbox 2 | 3 | ```js tabs codesandbox title="main.js" 4 | js; 5 | ``` 6 | 7 | ```ts title="main.ts" 8 | ts; 9 | ``` 10 | 11 | Codesandbox alone 12 | 13 | ```js codesandbox 14 | console.log('show codesandbox button'); 15 | ``` 16 | 17 | ```js 18 | console.log("don't showc codesandbox button) 19 | ``` 20 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-code-tabs/src/__tests__/__fixtures__/groups.md: -------------------------------------------------------------------------------- 1 | Some snippets 2 | 3 | ```js tabs=group 4 | jsvalue; 5 | ``` 6 | 7 | ```ts 8 | tsvalue; 9 | ``` 10 | 11 | And a paragraph and another tabs group 12 | 13 | ```yml tabs=group 14 | jsvalue 15 | ``` 16 | 17 | ```cs 18 | tsvalue 19 | ``` 20 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-code-tabs/src/__tests__/__fixtures__/onetab.md: -------------------------------------------------------------------------------- 1 | Single snippets 2 | 3 | ```js tabs title="main.js" 4 | js; 5 | ``` 6 | 7 | And now with codesandbox 8 | 9 | ```js tabs title="main.js" codesandbox 10 | ts; 11 | ``` 12 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-code-tabs/src/__tests__/__fixtures__/twotabs.md: -------------------------------------------------------------------------------- 1 | Some snippets 2 | 3 | ```js tabs title="JavaScript!" 4 | js; 5 | ``` 6 | 7 | ```ts 8 | ts; 9 | ``` 10 | 11 | and lazy 12 | 13 | ```js tabs title="JavaScript!" lazy 14 | js; 15 | ``` 16 | 17 | ```ts 18 | ts; 19 | ``` 20 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-code-tabs/src/__tests__/__snapshots__/index.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`code-tabs plugin > works on codesandbox file 1`] = ` 4 | "import CodeSandboxButton from '@theme/CodeSandboxButton'; 5 | 6 | import Tabs from '@theme/Tabs'; 7 | 8 | import TabItem from '@theme/TabItem'; 9 | 10 | Tabs with codesandbox 11 | 12 | 13 | 14 | 15 | 16 | \`\`\`js tabs codesandbox title=\\"main.js\\" 17 | js; 18 | \`\`\` 19 | 20 | 21 | 22 | 23 | 24 | \`\`\`ts title=\\"main.ts\\" 25 | ts; 26 | \`\`\` 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | Codesandbox alone 35 | 36 | \`\`\`js codesandbox 37 | console.log('show codesandbox button'); 38 | \`\`\` 39 | 40 | 41 | 42 | \`\`\`js 43 | console.log(\\"don't showc codesandbox button) 44 | \`\`\` 45 | " 46 | `; 47 | 48 | exports[`code-tabs plugin > works on groups file 1`] = ` 49 | "import Tabs from '@theme/Tabs'; 50 | 51 | import TabItem from '@theme/TabItem'; 52 | 53 | Some snippets 54 | 55 | 56 | 57 | 58 | 59 | \`\`\`js tabs=group 60 | jsvalue; 61 | \`\`\` 62 | 63 | 64 | 65 | 66 | 67 | \`\`\`ts 68 | tsvalue; 69 | \`\`\` 70 | 71 | 72 | 73 | 74 | 75 | And a paragraph and another tabs group 76 | 77 | 78 | 79 | 80 | 81 | \`\`\`yml tabs=group 82 | jsvalue 83 | \`\`\` 84 | 85 | 86 | 87 | 88 | 89 | \`\`\`cs 90 | tsvalue 91 | \`\`\` 92 | 93 | 94 | 95 | 96 | " 97 | `; 98 | 99 | exports[`code-tabs plugin > works on onetab file 1`] = ` 100 | "import CodeSandboxButton from '@theme/CodeSandboxButton'; 101 | 102 | Single snippets 103 | 104 | \`\`\`js tabs title=\\"main.js\\" 105 | js; 106 | \`\`\` 107 | 108 | And now with codesandbox 109 | 110 | \`\`\`js tabs title=\\"main.js\\" codesandbox 111 | ts; 112 | \`\`\` 113 | 114 | 115 | " 116 | `; 117 | 118 | exports[`code-tabs plugin > works on twotabs file 1`] = ` 119 | "import Tabs from '@theme/Tabs'; 120 | 121 | import TabItem from '@theme/TabItem'; 122 | 123 | Some snippets 124 | 125 | 126 | 127 | 128 | 129 | \`\`\`js tabs title=\\"JavaScript!\\" 130 | js; 131 | \`\`\` 132 | 133 | 134 | 135 | 136 | 137 | \`\`\`ts 138 | ts; 139 | \`\`\` 140 | 141 | 142 | 143 | 144 | 145 | and lazy 146 | 147 | 148 | 149 | 150 | 151 | \`\`\`js tabs title=\\"JavaScript!\\" lazy 152 | js; 153 | \`\`\` 154 | 155 | 156 | 157 | 158 | 159 | \`\`\`ts 160 | ts; 161 | \`\`\` 162 | 163 | 164 | 165 | 166 | " 167 | `; 168 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-code-tabs/src/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from 'vitest'; 2 | import path from 'path'; 3 | import vfile from 'to-vfile'; 4 | import mdx from 'remark-mdx'; 5 | import remark from 'remark'; 6 | import plugin from '../index'; 7 | 8 | const processFixture = async (name: string, options: any) => { 9 | const filePath = path.join(__dirname, '__fixtures__', `${name}.md`); 10 | const file = await vfile.read(filePath); 11 | const result = await remark().use(mdx).use(plugin, options).process(file); 12 | 13 | return result.toString(); 14 | }; 15 | 16 | const options = {}; 17 | 18 | describe('code-tabs plugin', () => { 19 | it('works on onetab file', async () => { 20 | const result = await processFixture('onetab', options); 21 | expect(result).toMatchSnapshot(); 22 | }); 23 | it('works on twotabs file', async () => { 24 | const result = await processFixture('twotabs', options); 25 | expect(result).toMatchSnapshot(); 26 | }); 27 | it('works on groups file', async () => { 28 | const result = await processFixture('groups', options); 29 | expect(result).toMatchSnapshot(); 30 | }); 31 | it('works on codesandbox file', async () => { 32 | const result = await processFixture('codesandbox', options); 33 | expect(result).toMatchSnapshot(); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-code-tabs/src/types.d.ts: -------------------------------------------------------------------------------- 1 | export type PluginOptions = { 2 | /** 3 | * language code to title map 4 | */ 5 | langTitles?: Record; 6 | }; 7 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-code-tabs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "noEmit": false, 5 | "incremental": true, 6 | "tsBuildInfoFile": "./lib/.tsbuildinfo", 7 | "sourceMap": true, 8 | "declarationMap": true, 9 | "rootDir": "src", 10 | "outDir": "lib" 11 | }, 12 | "include": ["src"], 13 | "exclude": ["**/__tests__/**"] 14 | } 15 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-code-tabs/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | include: ['**/__tests__/**/*.test.ts'], 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-compile-code/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store 4 | lib 5 | snippets 6 | .docusaurus -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-compile-code/.npmignore: -------------------------------------------------------------------------------- 1 | .tsbuildinfo* 2 | tsconfig* 3 | __tests__ 4 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-compile-code/README.md: -------------------------------------------------------------------------------- 1 | # `@rise4fun/docusaurus-remark-plugin-compile-code` 2 | 3 | Docusaurus plugin to run a tool against code snippets at build time ([npm.js](https://www.npmjs.com/package/@rise4fun/docusaurus-remark-plugin-compile-code)). 4 | 5 | ## Usage 6 | 7 | See [docusaurus-remark-plugin-compile-code documentation](https://microsoft.github.io/docusaurus-plugins/docs/plugins/docusaurus-remark-plugin-compile-code/). 8 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-compile-code/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rise4fun/docusaurus-remark-plugin-compile-code", 3 | "version": "4.0.0", 4 | "description": "Run tool and show output.", 5 | "keywords": [ 6 | "Docusaurus", 7 | "remark-plugin", 8 | "rise4fun" 9 | ], 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/microsoft/docusaurus-plugins.git", 13 | "directory": "packages/docusaurus-remark-plugin-compile-code" 14 | }, 15 | "license": "MIT", 16 | "main": "lib/index.js", 17 | "types": "src/types.d.ts", 18 | "scripts": { 19 | "build": "tsc", 20 | "clear": "rm -Rf lib && rm -Rf .docusaurus", 21 | "test": "vitest run", 22 | "watch": "tsc --watch" 23 | }, 24 | "dependencies": { 25 | "fs-extra": "^11.1.0", 26 | "minimatch": "^6.1.6", 27 | "p-all": "^4.0.0", 28 | "puppeteer": "^19.6.3", 29 | "tslib": "^2.4.0", 30 | "typescript": "^4.9.4", 31 | "unist-util-visit": "^2.0.3" 32 | }, 33 | "devDependencies": { 34 | "@types/fs-extra": "^11.0.0", 35 | "@types/mdast": "^3.0.10", 36 | "@types/puppeteer": "^7.0.4", 37 | "remark": "^12.0.1", 38 | "remark-mdx": "^1.6.21", 39 | "to-vfile": "^6.1.0" 40 | }, 41 | "engines": { 42 | "node": ">=16.14" 43 | }, 44 | "publishConfig": { 45 | "access": "public" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-compile-code/src/__tests__/__fixtures__/compile.md: -------------------------------------------------------------------------------- 1 | Some snippets 2 | 3 | ```echo 4 | foo 5 | ``` 6 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-compile-code/src/__tests__/__fixtures__/meta.md: -------------------------------------------------------------------------------- 1 | Testing meta 2 | 3 | ```fail skip foo 4 | don't run this 5 | ``` 6 | 7 | next, broken 8 | 9 | ```fail ignore-errors foo 10 | ignore errors 11 | ``` 12 | 13 | missing lang meta 14 | 15 | ```fail 16 | fail 17 | ``` 18 | 19 | prefix 20 | 21 | ```echo prefix 22 | 23 | ``` 24 | 25 | ```echo prefix 26 | prefix 27 | ``` 28 | 29 | ```echonull 30 | remove me 31 | ``` 32 | 33 | ```meta bar 34 | baz 35 | ``` 36 | 37 | ```meta 38 | don't run this 39 | ``` 40 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-compile-code/src/__tests__/__fixtures__/nodebin.md: -------------------------------------------------------------------------------- 1 | Some snippets 2 | 3 | ```ts 4 | let x = 0; 5 | ``` 6 | 7 | next, broken 8 | 9 | ```ts ignore-errors 10 | sadfj asd fasdf 11 | ``` 12 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-compile-code/src/__tests__/__fixtures__/nodes.md: -------------------------------------------------------------------------------- 1 | Some snippets reflected in files 2 | 3 | ```echonode 4 | foo 5 | ``` 6 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-compile-code/src/__tests__/__fixtures__/puppet.html: -------------------------------------------------------------------------------- 1 | 2 | Offscreen renderer 3 | 4 | 5 | 29 | 30 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-compile-code/src/__tests__/__fixtures__/puppet.md: -------------------------------------------------------------------------------- 1 | ```puppet 2 | hello world 3 | ``` 4 | 5 | ```puppet 6 | hello world 2 7 | ``` 8 | 9 | ```puppet 10 | hello 11 | world 12 | foo 13 | bar 14 | ``` 15 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-compile-code/src/__tests__/__snapshots__/index.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`compile-code plugin > works on compile file 1`] = ` 4 | "Some snippets 5 | 6 | \`\`\`lisp 7 | foo 8 | \`\`\` 9 | 10 | prefix 11 | 12 | foo 13 | " 14 | `; 15 | 16 | exports[`compile-code plugin > works on meta file 1`] = ` 17 | "Testing meta 18 | 19 | \`\`\`fail skip foo 20 | don't run this 21 | \`\`\` 22 | 23 | next, broken 24 | 25 | \`\`\`fail ignore-errors foo 26 | ignore errors 27 | \`\`\` 28 | 29 | \`\`\`console title=\\"Error\\" 30 | Error: fail 31 | \`\`\` 32 | 33 | missing lang meta 34 | 35 | \`\`\`fail 36 | fail 37 | \`\`\` 38 | 39 | prefix 40 | 41 | \`\`\`lisp prefix 42 | 43 | \`\`\` 44 | 45 | prefix 46 | 47 | \`\`\`lisp prefix 48 | prefix 49 | \`\`\` 50 | 51 | prefix 52 | 53 | \`\`\`meta bar 54 | baz 55 | \`\`\` 56 | 57 | baz 58 | 59 | \`\`\`meta 60 | don't run this 61 | \`\`\` 62 | " 63 | `; 64 | 65 | exports[`compile-code plugin > works on nodebin file 1`] = ` 66 | "Some snippets 67 | 68 | \`\`\`ts 69 | let x = 0; 70 | \`\`\` 71 | 72 | next, broken 73 | 74 | \`\`\`ts ignore-errors 75 | sadfj asd fasdf 76 | \`\`\` 77 | 78 | input.ts(1,1): error TS1434: Unexpected keyword or identifier. 79 | input.ts(1,7): error TS1434: Unexpected keyword or identifier. 80 | " 81 | `; 82 | 83 | exports[`compile-code plugin > works on nodes file 1`] = ` 84 | "Some snippets reflected in files 85 | 86 | \`\`\`echonode 87 | foo 88 | \`\`\` 89 | 90 | extra --> foo 91 | 92 | \`\`\`json 93 | { 94 | \\"lang\\": \\"echonode\\", 95 | \\"meta\\": \\"\\", 96 | \\"cwd\\": \\".docusaurus/docusaurus-remark-plugin-compile-code/cache/echonode/185f635fea6671cf955bf4b541beb834e8a1ecc581a9af9ad9cd151bf2f70e63\\" 97 | } 98 | \`\`\` 99 | " 100 | `; 101 | 102 | exports[`compile-code plugin > works on puppet file 1`] = ` 103 | "\`\`\`puppet 104 | hello world 105 | \`\`\` 106 | 107 | browser says: hello world 108 | 109 | \`\`\`puppet 110 | hello world 2 111 | \`\`\` 112 | 113 | browser says: hello world 2 114 | 115 | \`\`\`puppet 116 | hello 117 | world 118 | foo 119 | bar 120 | \`\`\` 121 | 122 | browser says: hello 123 | world 124 | foo 125 | bar 126 | " 127 | `; 128 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-compile-code/src/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from 'vitest'; 2 | import path from 'path'; 3 | import vfile from 'to-vfile'; 4 | import mdx from 'remark-mdx'; 5 | import remark from 'remark'; 6 | import plugin from '../index'; 7 | import type { PluginOptions } from '../types'; 8 | import { readFileSync } from 'fs-extra'; 9 | 10 | const processFixture = async (name: string, options: PluginOptions) => { 11 | const filePath = path.join(__dirname, '__fixtures__', `${name}.md`); 12 | const file = await vfile.read(filePath); 13 | const result = await remark().use(mdx).use(plugin, options).process(file); 14 | 15 | return result.toString(); 16 | }; 17 | 18 | const options: PluginOptions = { 19 | langs: [ 20 | { 21 | lang: 'ts', 22 | nodeBin: 'tsc', 23 | inputFiles: { 24 | 'tsconfig.json': { 25 | compilerOptions: { 26 | target: 'esnext', 27 | skipLibCheck: true, 28 | lib: ['esnext', 'dom'], 29 | }, 30 | include: ['input.ts'], 31 | }, 32 | }, 33 | }, 34 | { 35 | lang: 'meta', 36 | meta: 'bar', 37 | compile: async (source, langOptions) => ({ 38 | stdout: source, 39 | }), 40 | }, 41 | { 42 | lang: 'fail', 43 | errorLang: 'console', 44 | errorMeta: 'console', 45 | meta: 'foo', 46 | compile: async (source, langOptions) => { 47 | throw new Error('fail'); 48 | }, 49 | }, 50 | { 51 | lang: 'echo', 52 | prefix: 'prefix', 53 | inputLang: 'lisp', 54 | compile: async (source, langOptions) => ({ 55 | stdout: source, 56 | }), 57 | }, 58 | { 59 | lang: 'echonull', 60 | inputLang: null, 61 | outputLang: null, 62 | compile: async (source, langOptions) => ({ 63 | stdout: source, 64 | }), 65 | }, 66 | { 67 | lang: 'echonode', 68 | compile: async (source, langOptions) => ({ 69 | nodes: [ 70 | { 71 | type: 'code', 72 | meta: 'extra', 73 | value: 'extra --> ' + source, 74 | }, 75 | { 76 | type: 'code', 77 | lang: 'json', 78 | value: JSON.stringify(langOptions, null, 2), 79 | }, 80 | ], 81 | }), 82 | }, 83 | ], 84 | }; 85 | 86 | describe('compile-code plugin', () => { 87 | it('works on compile file', async () => { 88 | const result = await processFixture('compile', options); 89 | expect(result).toMatchSnapshot(); 90 | }); 91 | it('works on nodebin file', async () => { 92 | const result = await processFixture('nodebin', options); 93 | expect(result).toMatchSnapshot(); 94 | }); 95 | it('works on meta file', async () => { 96 | const result = await processFixture('meta', options); 97 | expect(result).toMatchSnapshot(); 98 | }); 99 | it('works on nodes file', async () => { 100 | const result = await processFixture('nodes', options); 101 | expect(result).toMatchSnapshot(); 102 | }); 103 | it('works on puppet file', async () => { 104 | const options: PluginOptions = { 105 | langs: [ 106 | { 107 | lang: 'puppet', 108 | createDriverHtml: (options) => { 109 | const filePath = path.join(__dirname, '__fixtures__', `puppet.html`); 110 | return readFileSync(filePath, { encoding: 'utf-8' }); 111 | }, 112 | }, 113 | ], 114 | }; 115 | const result = await processFixture('puppet', options); 116 | expect(result).toMatchSnapshot(); 117 | }); 118 | }); 119 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-compile-code/src/hash.ts: -------------------------------------------------------------------------------- 1 | import { createHash } from 'crypto'; 2 | import { LangOptions } from './types'; 3 | import { readFileSync } from 'fs-extra'; 4 | 5 | export default function hashCode(source: string, meta: string, options: LangOptions) { 6 | const hash = createHash('sha256'); 7 | hash.update(source).update(meta).update(JSON.stringify(options)); 8 | options.hashFiles?.forEach((file) => { 9 | hash.update(readFileSync(file)); 10 | }); 11 | return hash.digest('hex'); 12 | } 13 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-compile-code/src/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { Node } from 'unist'; 2 | 3 | export interface LangResult { 4 | /** 5 | * console standard output 6 | */ 7 | stdout?: string; 8 | /** 9 | * console standard error 10 | */ 11 | stderr?: string; 12 | /** 13 | * Tool runtime exception or invalid status code 14 | */ 15 | error?: string; 16 | /** 17 | * Extra markdown node to be added after the output 18 | */ 19 | nodes?: Node[]; 20 | /** 21 | * In memory generated files 22 | */ 23 | outputFiles?: Record; 24 | } 25 | 26 | export interface SnippetOptions { 27 | meta: string; 28 | cwd: string; 29 | } 30 | 31 | export type CompileFunction = ( 32 | source: string, 33 | options: LangOptions & SnippetOptions 34 | ) => Promise; 35 | 36 | export interface OutputFile { 37 | name: string; 38 | title?: string; 39 | lang?: string; 40 | meta?: string; 41 | } 42 | 43 | export interface LangOptions { 44 | /** 45 | * Markdown language code identifier in the markdown fenced regions. 46 | */ 47 | lang: string; 48 | /** 49 | * Required string in meta to filter snippets within a language 50 | */ 51 | meta?: string; 52 | /** 53 | * Prefix all sources with this string (if not present) 54 | */ 55 | prefix?: string; 56 | /** 57 | * Replace language with the `prism` language for syntax coloring 58 | * after processing. Set to 'null' to remove code. 59 | */ 60 | inputLang?: string | null; 61 | /** 62 | * Version of the tool, used when hashing pre-compiled solution 63 | */ 64 | version?: string; 65 | /** 66 | * Tool timeout in milliseconds 67 | */ 68 | timeout?: number; 69 | /** 70 | * Output markdown language code identifier, default `code`. Set to `null` to hide standard output. 71 | */ 72 | outputLang?: string | null; 73 | /** 74 | * Error markdown language code identifier, default `console`. Set to `null` to hide standard output. 75 | */ 76 | errorLang?: string; 77 | /** 78 | * Output meta, default empty. 79 | */ 80 | outputMeta?: string; 81 | /** 82 | * Error meta, default empty. 83 | */ 84 | errorMeta?: string; 85 | /** 86 | * Do not fail build when tools return errors 87 | */ 88 | ignoreErrors?: boolean; 89 | /** 90 | * minimatch filter to exclude files from generation 91 | */ 92 | excludedFiles?: string[]; 93 | /** 94 | * Sets of files that should be included in the final output 95 | */ 96 | outputFiles?: OutputFile[]; 97 | /** 98 | * Optional hash files to consider when computing the hash of the source 99 | */ 100 | hashFiles?: string[]; 101 | } 102 | 103 | export interface ToolLangOptions extends LangOptions { 104 | /** 105 | * Path to the executable 106 | */ 107 | command?: string; 108 | /** 109 | * Additional arguments passed when invoking tool 110 | */ 111 | args?: string[]; 112 | /** 113 | * File name extension for sources, default is `lang`. 114 | */ 115 | extension: string?; 116 | /** 117 | * name of the tool alias installed by npm in `node_modules/.bin`. Overrides command. 118 | */ 119 | nodeBin?: string; 120 | /** 121 | * npm package containing the node tool 122 | */ 123 | npmPackage?: string; 124 | /** 125 | * Expected success return code, default `0`. 126 | */ 127 | successReturnCode?: number; 128 | /** 129 | * Ignore tool return code. 130 | */ 131 | ignoreReturnCode?: boolean; 132 | /** 133 | * Additional files to write in the folder before running tool 134 | */ 135 | inputFiles?: Record; 136 | } 137 | 138 | /** 139 | * A tool that runs in puppeteer 140 | */ 141 | export interface PuppeteerLangOptions extends LangOptions { 142 | /** 143 | * File path to the HTML driver 144 | */ 145 | html?: string; 146 | /** 147 | * Generates the HTML that will drive puppeteer 148 | * @param options 149 | * @returns 150 | */ 151 | createDriverHtml?: (options: PuppeteerLangOptions) => string; 152 | /** 153 | * Creates a message that will make the driver do a compilation request 154 | * @param msg 155 | * @returns 156 | */ 157 | createCompileRequest?: (msg: { 158 | id: string; 159 | source: string; 160 | options: LangOptions & SnippetOptions; 161 | }) => object; 162 | /** 163 | * Give a received message from puppeteer, convert to a render rest if any 164 | * @param msg 165 | * @returns 166 | */ 167 | resolveCompileResponse?: (msg: object) => { 168 | id: string; 169 | } & LangResult; 170 | } 171 | 172 | export interface CustomLangOptions extends LangOptions { 173 | /** 174 | * Custom function that compiles the source and returns a result object 175 | */ 176 | compile?: CompileFunction; 177 | } 178 | 179 | export type PluginOptions = { 180 | /** 181 | * List of compilers 182 | */ 183 | langs: (ToolLangOptions | CustomLangOptions | PuppeteerLangOptions)[]; 184 | 185 | /** 186 | * Use cache folder, default is true. 187 | */ 188 | cache?: boolean; 189 | 190 | /** 191 | * Stop compiling after first failed snippet 192 | */ 193 | failFast?: boolean; 194 | }; 195 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-compile-code/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "noEmit": false, 5 | "incremental": true, 6 | "tsBuildInfoFile": "./lib/.tsbuildinfo", 7 | "sourceMap": true, 8 | "declarationMap": true, 9 | "rootDir": "src", 10 | "outDir": "lib", 11 | "lib": ["dom"] 12 | }, 13 | "include": ["src"], 14 | "exclude": ["**/__tests__/**"] 15 | } 16 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-compile-code/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | include: ['**/__tests__/**/*.test.ts'], 6 | testTimeout: 10000, 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-import-file/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store 4 | lib 5 | snippets 6 | .docusaurus -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-import-file/.npmignore: -------------------------------------------------------------------------------- 1 | .tsbuildinfo* 2 | tsconfig* 3 | __tests__ 4 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-import-file/README.md: -------------------------------------------------------------------------------- 1 | # `@rise4fun/docusaurus-remark-plugin-import-file` 2 | 3 | Docusaurus plugin to import markdown files ([npm.js](https://www.npmjs.com/package/@rise4fun/docusaurus-remark-plugin-import-files)). 4 | 5 | ## Usage 6 | 7 | See [docusaurus-remark-plugin-import-file documentation](https://microsoft.github.io/docusaurus-plugins/docs/plugins/docusaurus-remark-plugin-import-file). 8 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-import-file/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rise4fun/docusaurus-remark-plugin-import-file", 3 | "version": "4.0.0", 4 | "description": "Import markdown file", 5 | "keywords": [ 6 | "Docusaurus", 7 | "remark-plugin", 8 | "rise4fun" 9 | ], 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/microsoft/docusaurus-plugins.git", 13 | "directory": "packages/docusaurus-remark-plugin-import-file" 14 | }, 15 | "license": "MIT", 16 | "main": "lib/index.js", 17 | "types": "src/types.d.ts", 18 | "scripts": { 19 | "build": "tsc", 20 | "clear": "rm -Rf lib && rm -Rf .docusaurus", 21 | "test": "vitest run", 22 | "watch": "tsc --watch" 23 | }, 24 | "dependencies": { 25 | "fs-extra": "^11.1.0", 26 | "minimatch": "^6.1.6", 27 | "p-all": "^4.0.0", 28 | "tslib": "^2.4.0", 29 | "typescript": "^4.9.4", 30 | "unist-util-visit": "^2.0.3" 31 | }, 32 | "devDependencies": { 33 | "@types/fs-extra": "^11.0.0", 34 | "@types/mdast": "^3.0.10", 35 | "remark": "^12.0.1", 36 | "remark-mdx": "^1.6.21", 37 | "to-vfile": "^6.1.0" 38 | }, 39 | "engines": { 40 | "node": ">=16.14" 41 | }, 42 | "publishConfig": { 43 | "access": "public" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-import-file/src/__tests__/__fixtures__/compile.md: -------------------------------------------------------------------------------- 1 | Some snippets 2 | 3 | test.md 4 | 5 | {@import ./test.md} 6 | 7 | Missing 8 | 9 | {@import optional ./missing.md} 10 | 11 | More snippets 12 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-import-file/src/__tests__/__fixtures__/test.md: -------------------------------------------------------------------------------- 1 | this is imported. 2 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-import-file/src/__tests__/__snapshots__/index.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`compile-code plugin > works on compile file 1`] = ` 4 | "Some snippets 5 | 6 | test.md 7 | 8 | this is imported. 9 | 10 | Missing 11 | 12 | 13 | 14 | More snippets 15 | " 16 | `; 17 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-import-file/src/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from 'vitest'; 2 | import path from 'path'; 3 | import vfile from 'to-vfile'; 4 | import mdx from 'remark-mdx'; 5 | import remark from 'remark'; 6 | import plugin from '../index'; 7 | import type { PluginOptions } from '../types'; 8 | 9 | const processFixture = async (name: string, options: PluginOptions) => { 10 | const filePath = path.join(__dirname, '__fixtures__', `${name}.md`); 11 | const file = await vfile.read(filePath); 12 | const result = await remark().use(mdx).use(plugin, options).process(file); 13 | 14 | return result.toString(); 15 | }; 16 | 17 | const options: PluginOptions = {}; 18 | 19 | describe('compile-code plugin', () => { 20 | it('works on compile file', async () => { 21 | const result = await processFixture('compile', options); 22 | expect(result).toMatchSnapshot(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-import-file/src/index.ts: -------------------------------------------------------------------------------- 1 | const { readFileSync, existsSync } = require('fs'); 2 | const { resolve } = require('path'); 3 | const visit = require('unist-util-visit'); 4 | 5 | // inspired from https://github.com/dotansimha/remark-import-partial 6 | // DO NOT convert to ES6 module, it will break the plugin 7 | module.exports = function () { 8 | // @ts-ignore 9 | const unified: any = this; 10 | 11 | return function transformer(tree: any, file: any) { 12 | visit(tree, 'paragraph', (node: any, index: number, parent: any) => { 13 | if (parent && node.children && node.children[0] && node.children[0].type === 'text') { 14 | const m = /{@import(?\s+optional)?\s+(?.+)}/.exec( 15 | node.children[0].value || '' 16 | ); 17 | if (m) { 18 | const { filePath, optional } = m.groups || {}; 19 | const fileAbsPath = resolve(file.dirname, filePath); 20 | 21 | if (existsSync(fileAbsPath)) { 22 | const rawMd = readFileSync(fileAbsPath, 'utf-8'); 23 | parent.children.splice(index, 1, ...unified.parse(rawMd).children); 24 | } else { 25 | if (!optional) throw new Error(`import error: ${fileAbsPath} not found`); 26 | else node.children = []; 27 | } 28 | } 29 | } 30 | }); 31 | }; 32 | }; 33 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-import-file/src/types.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Plugin options. 3 | */ 4 | export interface PluginOptions {} 5 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-import-file/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "noEmit": false, 5 | "incremental": true, 6 | "tsBuildInfoFile": "./lib/.tsbuildinfo", 7 | "sourceMap": true, 8 | "declarationMap": true, 9 | "rootDir": "src", 10 | "outDir": "lib" 11 | }, 12 | "include": ["src"], 13 | "exclude": ["**/__tests__/**"] 14 | } 15 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-import-file/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | include: ['**/__tests__/**/*.test.ts'], 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-side-editor/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store 4 | lib 5 | snippets 6 | .docusaurus -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-side-editor/.npmignore: -------------------------------------------------------------------------------- 1 | .tsbuildinfo* 2 | tsconfig* 3 | __tests__ 4 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-side-editor/README.md: -------------------------------------------------------------------------------- 1 | # `@rise4fun/docusaurus-remark-plugin-side-editor` 2 | 3 | Docusaurus remark plugin that injects `Edit` button after code section that open snippet in side editor ([npm.js](https://www.npmjs.com/package/@rise4fun/docusaurus-theme-side-editor)). 4 | 5 | ## Usage 6 | 7 | See [docusaurus-remark-plugin-side-editor documentation](https://microsoft.github.io/docusaurus-plugins/docs/plugins/docusaurus-remark-plugin-side-editor). 8 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-side-editor/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rise4fun/docusaurus-remark-plugin-side-editor", 3 | "version": "4.0.0", 4 | "description": "Remark docusaurus plugin that assembles code sections in tabs.", 5 | "keywords": [ 6 | "Docusaurus", 7 | "remark-plugin", 8 | "rise4fun" 9 | ], 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/microsoft/docusaurus-plugins.git", 13 | "directory": "packages/docusaurus-remark-plugin-side-editor" 14 | }, 15 | "license": "MIT", 16 | "main": "lib/index.js", 17 | "types": "src/types.d.ts", 18 | "scripts": { 19 | "build": "tsc", 20 | "clear": "rm -Rf lib && rm -Rf .docusaurus", 21 | "test": "vitest run", 22 | "watch": "tsc --watch" 23 | }, 24 | "dependencies": { 25 | "fs-extra": "^11.1.0", 26 | "p-all": "^4.0.0", 27 | "tslib": "^2.4.0", 28 | "typescript": "^4.9.4", 29 | "unist-util-visit": "^2.0.3" 30 | }, 31 | "devDependencies": { 32 | "@types/fs-extra": "^11.0.0", 33 | "@types/mdast": "^3.0.10", 34 | "remark": "^12.0.1", 35 | "remark-mdx": "^1.6.21", 36 | "to-vfile": "^6.1.0" 37 | }, 38 | "engines": { 39 | "node": ">=16.14" 40 | }, 41 | "publishConfig": { 42 | "access": "public" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-side-editor/src/__tests__/__fixtures__/edit.md: -------------------------------------------------------------------------------- 1 | Show edit button 2 | 3 | ```js edit 4 | console.log('edit this!'); 5 | ``` 6 | 7 | and that 8 | 9 | ```js edit=someeditor 10 | console.log('edit this!'); 11 | ``` 12 | 13 | ```ts edit 14 | console.log('edit this!'); 15 | ``` 16 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-side-editor/src/__tests__/__snapshots__/index.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`code-tabs plugin > works on edit file 1`] = ` 4 | "import SideEditorButton from '@theme/SideEditorButton'; 5 | 6 | Show edit button 7 | 8 | \`\`\`js edit 9 | console.log('edit this!'); 10 | \`\`\` 11 | 12 | 13 | 14 | and that 15 | 16 | \`\`\`js edit=someeditor 17 | console.log('edit this!'); 18 | \`\`\` 19 | 20 | 21 | 22 | \`\`\`ts edit 23 | console.log('edit this!'); 24 | \`\`\` 25 | 26 | 27 | " 28 | `; 29 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-side-editor/src/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from 'vitest'; 2 | import path from 'path'; 3 | import vfile from 'to-vfile'; 4 | import mdx from 'remark-mdx'; 5 | import remark from 'remark'; 6 | import plugin from '../index'; 7 | 8 | const processFixture = async (name: string, options: any) => { 9 | const filePath = path.join(__dirname, '__fixtures__', `${name}.md`); 10 | const file = await vfile.read(filePath); 11 | const result = await remark().use(mdx).use(plugin, options).process(file); 12 | 13 | return result.toString(); 14 | }; 15 | 16 | const options = { 17 | languages: { 18 | ts: 'typescript', 19 | }, 20 | }; 21 | 22 | describe('code-tabs plugin', () => { 23 | it('works on edit file', async () => { 24 | const result = await processFixture('edit', options); 25 | expect(result).toMatchSnapshot(); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-side-editor/src/index.ts: -------------------------------------------------------------------------------- 1 | /// (node: Code) { 9 | const r = parse(node.meta || '', ' '); 10 | Object.keys(r).forEach((k) => (r[k] = fromAttributeValue(r[k]))); 11 | return r as T; 12 | } 13 | 14 | function injectThemeImport(root: Root, element: string) { 15 | if ( 16 | !root.children.find( 17 | (n: { type: string; value?: string }) => 18 | n.type === 'import' && n.value && n.value?.indexOf(`@theme/${element}`) > -1 19 | ) 20 | ) { 21 | root.children.unshift( 22 | ...[ 23 | { 24 | type: 'import', 25 | value: `import ${element} from '@theme/${element}';`, 26 | } as any, 27 | ] 28 | ); 29 | } 30 | } 31 | 32 | function fromAttributeValue(s: string | string[] | undefined) { 33 | if (!s) return s; 34 | if (typeof s === 'string' && s[0] === '"') { 35 | try { 36 | return JSON.parse(s); 37 | } catch {} 38 | } 39 | return s; 40 | } 41 | 42 | function toAttributeValue(s: string | undefined | null) { 43 | if (!s) return s; 44 | try { 45 | const j = JSON.parse(s); 46 | if (typeof j === 'string') s = j; 47 | } catch {} 48 | return JSON.stringify(s); 49 | } 50 | 51 | const plugin: Plugin<[PluginOptions?]> = (options = undefined) => { 52 | const { languages } = options || {}; 53 | 54 | return async (root, vfile) => { 55 | let needsSideEditorButtonImport = false; 56 | 57 | const visited = new Set(); // visit called twice on async 58 | // collect all nodes 59 | visit(root, 'code', (node: Code, nodeIndex: number, parent) => { 60 | if (!parent || visited.has(node)) return; 61 | visited.add(node); 62 | 63 | const { edit } = parseMeta<{ 64 | edit: string; 65 | }>(node); 66 | if (edit === undefined) return; 67 | const { lang = '', value } = node; 68 | const editorId = edit || languages?.[lang || ''] || lang; 69 | parent.children.splice(nodeIndex + 1, 0, { 70 | type: 'jsx', 71 | value: ``, 74 | } as Literal); 75 | needsSideEditorButtonImport = true; 76 | return nodeIndex + 2; 77 | }); 78 | 79 | // add import as final step 80 | if (needsSideEditorButtonImport) { 81 | injectThemeImport(root as Root, 'SideEditorButton'); 82 | } 83 | }; 84 | }; 85 | 86 | // To continue supporting `require('...')` without the `.default` ㄟ(▔,▔)ㄏ 87 | // TODO change to export default after migrating to ESM 88 | export = plugin; 89 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-side-editor/src/types.d.ts: -------------------------------------------------------------------------------- 1 | export type PluginOptions = { 2 | /** 3 | * language code to side editor editor ids 4 | */ 5 | languages?: Record; 6 | }; 7 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-side-editor/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "noEmit": false, 5 | "incremental": true, 6 | "tsBuildInfoFile": "./lib/.tsbuildinfo", 7 | "sourceMap": true, 8 | "declarationMap": true, 9 | "rootDir": "src", 10 | "outDir": "lib" 11 | }, 12 | "include": ["src"], 13 | "exclude": ["**/__tests__/**"] 14 | } 15 | -------------------------------------------------------------------------------- /packages/docusaurus-remark-plugin-side-editor/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | include: ['**/__tests__/**/*.test.ts'], 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /packages/docusaurus-theme-codesandbox-button/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store 4 | lib 5 | snippets 6 | .docusaurus 7 | -------------------------------------------------------------------------------- /packages/docusaurus-theme-codesandbox-button/.npmignore: -------------------------------------------------------------------------------- 1 | .tsbuildinfo* 2 | tsconfig* 3 | __tests__ 4 | -------------------------------------------------------------------------------- /packages/docusaurus-theme-codesandbox-button/README.md: -------------------------------------------------------------------------------- 1 | # `@rise4fun/docusaurus-theme-codesandbox-button` 2 | 3 | Docusaurus theme component that displays a button to create/open a CodeSandbox project ([npm.js](https://www.npmjs.com/package/@rise4fun/docusaurus-theme-codesandbox-button)). 4 | 5 | ![Demo of the feature in Docusaurus](./demo.png) 6 | 7 | ## Usage 8 | 9 | See [documentation](https://microsoft.github.io/docusaurus-plugins/docs/plugins/docusaurus-theme-codesandbox-button). 10 | -------------------------------------------------------------------------------- /packages/docusaurus-theme-codesandbox-button/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/docusaurus-plugins/9491281645fed491d317e859932c263d29f94a82/packages/docusaurus-theme-codesandbox-button/demo.png -------------------------------------------------------------------------------- /packages/docusaurus-theme-codesandbox-button/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rise4fun/docusaurus-theme-codesandbox-button", 3 | "version": "4.0.0", 4 | "description": "CodeSandbox components for Docusaurus.", 5 | "keywords": [ 6 | "Docusaurus", 7 | "rise4fun" 8 | ], 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/microsoft/docusaurus-plugins.git", 12 | "directory": "packages/docusaurus-theme-codesandbox-button" 13 | }, 14 | "license": "MIT", 15 | "sideEffects": false, 16 | "exports": { 17 | "./lib/*": "./lib/*", 18 | "./src/*": "./src/*", 19 | "./client": { 20 | "type": "./lib/client/index.d.ts", 21 | "default": "./lib/client/index.js" 22 | }, 23 | ".": { 24 | "types": "./src/theme-codesandbox-button.d.ts", 25 | "default": "./lib/index.js" 26 | } 27 | }, 28 | "main": "lib/index.js", 29 | "types": "src/theme-codesandbox-button.d.ts", 30 | "scripts": { 31 | "build": "tsc --build && node ../../admin/scripts/copyUntypedFiles.mjs", 32 | "build:watch": "tsc --build --watch", 33 | "clear": "rm -Rf ./lib", 34 | "watch": "run-p -c copy:watch build:watch" 35 | }, 36 | "dependencies": { 37 | "@mdx-js/react": "^1.6.22", 38 | "clsx": "^1.2.1", 39 | "tslib": "^2.5.0", 40 | "validate-peer-dependencies": "^2.2.0" 41 | }, 42 | "devDependencies": { 43 | "@docusaurus/core": "^2.4.0", 44 | "@docusaurus/types": "^2.4.0", 45 | "@docusaurus/utils-validation": "^2.4.0", 46 | "@types/mdx-js__react": "^1.5.5" 47 | }, 48 | "peerDependencies": { 49 | "@docusaurus/core": ">=2.4.0", 50 | "@docusaurus/types": ">=2.4.0", 51 | "@docusaurus/utils-validation": ">=2.4.0", 52 | "react": "^16.8.4 || ^17.0.0", 53 | "react-dom": "^16.8.4 || ^17.0.0" 54 | }, 55 | "engines": { 56 | "node": ">=16.14" 57 | }, 58 | "publishConfig": { 59 | "access": "public" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /packages/docusaurus-theme-codesandbox-button/src/index.ts: -------------------------------------------------------------------------------- 1 | import type { Plugin } from '@docusaurus/types'; 2 | import type { 3 | ThemeConfig, 4 | CodeSandboxButtonThemeConfig, 5 | } from '@rise4fun/docusaurus-theme-codesandbox-button'; 6 | import validatePeerDependencies from 'validate-peer-dependencies'; 7 | 8 | validatePeerDependencies(__dirname); 9 | 10 | export default function themeCodeSandboxButton(): Plugin { 11 | return { 12 | name: '@rise4fun/docusaurus-theme-codesandbox-button', 13 | 14 | getThemePath() { 15 | return '../lib/theme'; 16 | }, 17 | getTypeScriptThemePath() { 18 | return '../src/theme'; 19 | }, 20 | }; 21 | } 22 | 23 | export function getSwizzleComponentList() { 24 | return undefined; 25 | } 26 | 27 | export { ThemeConfig, CodeSandboxButtonThemeConfig }; 28 | -------------------------------------------------------------------------------- /packages/docusaurus-theme-codesandbox-button/src/theme-codesandbox-button.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '@rise4fun/docusaurus-theme-codesandbox-button' { 4 | export interface CodeSandboxFileOptions { 5 | content: string | object; 6 | } 7 | export interface CodeSandboxOptions { 8 | files: Record; 9 | } 10 | export interface CodeSandboxButtonThemeConfig { 11 | templates?: Record; 12 | defaultTemplate?: string; 13 | } 14 | export interface ThemeConfig { 15 | codeSandbox?: CodeSandboxButtonThemeConfig; 16 | } 17 | export default function themeCodeSandboxButton(): object; 18 | } 19 | 20 | declare module '@theme/CodeSandboxButton' { 21 | export interface Props { 22 | className?: string; 23 | files: Record; 24 | startFile?: string; 25 | label?: string; 26 | template?: string; 27 | } 28 | 29 | export default function CodeSandboxButton(props: Props): JSX.Element; 30 | } 31 | -------------------------------------------------------------------------------- /packages/docusaurus-theme-codesandbox-button/src/theme/CodeSandboxButton/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import type { Props } from '@theme/CodeSandboxButton'; 3 | import clsx from 'clsx'; 4 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; 5 | import styles from './styles.module.css'; 6 | import type { 7 | CodeSandboxOptions, 8 | ThemeConfig, 9 | } from '@rise4fun/docusaurus-theme-codesandbox-button'; 10 | 11 | const DEFAULT_CODESANDBOX: CodeSandboxOptions = { 12 | files: { 13 | 'package.json': { 14 | content: { 15 | dependencies: {}, 16 | }, 17 | }, 18 | 'sandbox.config.json': { 19 | content: { 20 | template: 'node', 21 | view: 'terminal', 22 | container: { 23 | node: '18', 24 | }, 25 | }, 26 | }, 27 | }, 28 | }; 29 | 30 | export default function CodeSandboxButton(props: Props) { 31 | const { siteConfig } = useDocusaurusContext(); 32 | const { themeConfig } = siteConfig; 33 | const { codeSandbox = {} } = themeConfig as ThemeConfig; 34 | const { templates, defaultTemplate } = codeSandbox; 35 | const { className, files, startFile, template = defaultTemplate, label = 'CodeSandbox' } = props; 36 | const sandbox = templates?.[template || ''] || DEFAULT_CODESANDBOX; 37 | 38 | const [error, setError] = useState(); 39 | const [importing, setImporting] = useState(false); 40 | 41 | const handleClick = async () => { 42 | const f = files; 43 | const body = { 44 | files: { 45 | ...(sandbox?.files || {}), 46 | ...f, 47 | }, 48 | }; 49 | try { 50 | setError(undefined); 51 | setImporting(true); 52 | const x = await fetch('https://codesandbox.io/api/v1/sandboxes/define?json=1', { 53 | method: 'POST', 54 | headers: { 55 | 'Content-Type': 'application/json', 56 | Accept: 'application/json', 57 | }, 58 | body: JSON.stringify(body), 59 | }); 60 | if (!x.ok) throw new Error('codesandbox.io api call failed'); 61 | const data = await x.json(); 62 | const { sandbox_id } = data; 63 | if (sandbox_id === undefined) throw new Error('failed to create new sandbox'); 64 | let url = `https://codesandbox.io/s/${data.sandbox_id}?`; 65 | if (startFile) url += `file=/${encodeURIComponent(startFile)}`; 66 | window.open(url, '_blank', 'noreferrer'); 67 | } catch (error: any) { 68 | console.error(error); 69 | setError(error?.message || error + ''); 70 | } finally { 71 | setImporting(false); 72 | } 73 | }; 74 | 75 | return ( 76 | 87 | ); 88 | } 89 | -------------------------------------------------------------------------------- /packages/docusaurus-theme-codesandbox-button/src/theme/CodeSandboxButton/styles.module.css: -------------------------------------------------------------------------------- 1 | .mr1 { 2 | margin-right: 0.5rem; 3 | margin-bottom: 1rem; 4 | } 5 | 6 | .hidemobile { 7 | } 8 | @media screen and (max-width: 996px) { 9 | .hidemobile { 10 | display: none !important; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/docusaurus-theme-codesandbox-button/tsconfig.client.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "noEmit": false, 5 | "composite": true, 6 | "incremental": true, 7 | "tsBuildInfoFile": "./lib/.tsbuildinfo-client", 8 | "rootDir": "src", 9 | "outDir": "lib", 10 | "module": "esnext", 11 | "target": "esnext", 12 | "isolatedModules": false, 13 | "lib": ["DOM", "ESNext"] 14 | }, 15 | "include": ["src/client", "src/theme", "src/*.d.ts", "src/theme-codesandbox-button.ts"], 16 | "exclude": ["**/__tests__/**"] 17 | } 18 | -------------------------------------------------------------------------------- /packages/docusaurus-theme-codesandbox-button/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "references": [{ "path": "./tsconfig.client.json" }], 4 | "compilerOptions": { 5 | "noEmit": false, 6 | "incremental": true, 7 | "isolatedModules": false, 8 | "tsBuildInfoFile": "./lib/.tsbuildinfo", 9 | "module": "commonjs", 10 | "rootDir": "src", 11 | "outDir": "lib" 12 | }, 13 | "include": ["src"], 14 | "exclude": ["src/client", "src/theme", "**/__tests__/**"] 15 | } 16 | -------------------------------------------------------------------------------- /packages/docusaurus-theme-side-editor/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store 4 | lib 5 | snippets 6 | .docusaurus 7 | -------------------------------------------------------------------------------- /packages/docusaurus-theme-side-editor/.npmignore: -------------------------------------------------------------------------------- 1 | .tsbuildinfo* 2 | tsconfig* 3 | __tests__ 4 | -------------------------------------------------------------------------------- /packages/docusaurus-theme-side-editor/README.md: -------------------------------------------------------------------------------- 1 | # `@rise4fun/docusaurus-theme-side-editor` 2 | 3 | A Docusaurus theme component that creates a side editor ([npm.js](https://www.npmjs.com/package/@rise4fun/docusaurus-theme-side-editor)). 4 | 5 | ## Usage 6 | 7 | See [documentation](https://microsoft.github.io/docusaurus-plugins/docs/plugins/docusaurus-theme-side-editor). 8 | -------------------------------------------------------------------------------- /packages/docusaurus-theme-side-editor/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rise4fun/docusaurus-theme-side-editor", 3 | "version": "4.0.0", 4 | "description": "Side editor components for Docusaurus.", 5 | "keywords": [ 6 | "Docusaurus", 7 | "rise4fun" 8 | ], 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/microsoft/docusaurus-plugins.git", 12 | "directory": "packages/docusaurus-theme-side-editor" 13 | }, 14 | "license": "MIT", 15 | "sideEffects": false, 16 | "exports": { 17 | "./lib/*": "./lib/*", 18 | "./src/*": "./src/*", 19 | "./client": { 20 | "type": "./lib/client/index.d.ts", 21 | "default": "./lib/client/index.js" 22 | }, 23 | ".": { 24 | "types": "./src/theme-side-editor.d.ts", 25 | "default": "./lib/index.js" 26 | } 27 | }, 28 | "main": "lib/index.js", 29 | "types": "src/theme-side-editor.d.ts", 30 | "scripts": { 31 | "build": "tsc --build && node ../../admin/scripts/copyUntypedFiles.mjs", 32 | "build:watch": "tsc --build --watch", 33 | "clear": "rm -Rf ./lib", 34 | "copy:watch": "node ../../admin/scripts/copyUntypedFiles.mjs --watch", 35 | "watch": "run-p -c copy:watch build:watch" 36 | }, 37 | "dependencies": { 38 | "@mdx-js/react": "^1.6.22", 39 | "@monaco-editor/react": "^4.4.6", 40 | "clsx": "^1.2.1", 41 | "monaco-editor": "^0.34.1", 42 | "react-resizable-panels": "^0.0.36", 43 | "tslib": "^2.5.0", 44 | "validate-peer-dependencies": "^2.2.0" 45 | }, 46 | "devDependencies": { 47 | "@docusaurus/core": "^2.4.0", 48 | "@docusaurus/types": "^2.4.0", 49 | "@docusaurus/utils-validation": "^2.4.0", 50 | "@types/mdx-js__react": "^1.5.5" 51 | }, 52 | "peerDependencies": { 53 | "@docusaurus/core": ">=2.4.0", 54 | "@docusaurus/types": ">=2.4.0", 55 | "@docusaurus/utils-validation": ">=2.4.0", 56 | "react": "^16.8.4 || ^17.0.0", 57 | "react-dom": "^16.8.4 || ^17.0.0" 58 | }, 59 | "engines": { 60 | "node": ">=16.14" 61 | }, 62 | "publishConfig": { 63 | "access": "public" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /packages/docusaurus-theme-side-editor/src/client/SideEditorContext.tsx: -------------------------------------------------------------------------------- 1 | import SideEditorRoot from '@theme/SideEditorRoot'; 2 | import React, { createContext, ReactNode, useContext, useState } from 'react'; 3 | import type { SideEditorConfig } from '@rise4fun/docusaurus-theme-side-editor'; 4 | import useSideEditorConfig from './useSideEditorConfig'; 5 | 6 | export interface SideEditorSource { 7 | editorId: string; 8 | text: string | undefined; 9 | config: SideEditorConfig; 10 | } 11 | 12 | export interface SideEditorProps { 13 | setSource: (editorId: string | undefined, text: string | undefined) => void; 14 | source?: SideEditorSource; 15 | } 16 | 17 | const dummySetSource = () => {}; 18 | export const SideEditorContext = createContext({ 19 | setSource: dummySetSource, 20 | }); 21 | SideEditorContext.displayName = 'SideEditor'; 22 | 23 | export function SplitEditorProvider(props: { children: ReactNode }) { 24 | const { children } = props; 25 | const { editors } = useSideEditorConfig(); 26 | 27 | const [source, setSource_] = useState(); 28 | const setSource = (editorId: string | undefined, text: string | undefined) => { 29 | const editorConfig = editors.find(({ id }) => id === editorId); 30 | if (!editorId || !editorConfig) setSource_(undefined); 31 | else { 32 | const newSource = { editorId, text, config: editorConfig }; 33 | setSource_(newSource); 34 | } 35 | }; 36 | 37 | return ( 38 | 39 | {source !== undefined ? : <>{children}} 40 | 41 | ); 42 | } 43 | 44 | export default function useSideEditorContext() { 45 | const ctx = useContext(SideEditorContext); 46 | if (ctx.setSource === dummySetSource) 47 | throw new Error('SideEditor not properly configured. Did you swizzle Root?'); 48 | return ctx; 49 | } 50 | -------------------------------------------------------------------------------- /packages/docusaurus-theme-side-editor/src/client/useHtmlDataTheme.ts: -------------------------------------------------------------------------------- 1 | import useIsBrowser from '@docusaurus/useIsBrowser'; 2 | export default function useHtmlDataTheme() { 3 | const isBrowser = useIsBrowser(); 4 | const colorMode = isBrowser ? (document.firstElementChild as HTMLElement).dataset.theme : 'dark'; 5 | return colorMode; 6 | } 7 | -------------------------------------------------------------------------------- /packages/docusaurus-theme-side-editor/src/client/useSideEditorConfig.ts: -------------------------------------------------------------------------------- 1 | import type { ThemeConfig } from '@rise4fun/docusaurus-theme-side-editor'; 2 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; 3 | 4 | export default function useSideEditorConfig() { 5 | const { siteConfig } = useDocusaurusContext(); 6 | const { themeConfig } = siteConfig; 7 | const { sideEditor: config } = themeConfig as ThemeConfig; 8 | if (!config) throw new Error('themeConfig.sideEditor not configured'); 9 | return config; 10 | } 11 | -------------------------------------------------------------------------------- /packages/docusaurus-theme-side-editor/src/index.ts: -------------------------------------------------------------------------------- 1 | import type { Plugin } from '@docusaurus/types'; 2 | import type { ThemeConfig, SideEditorThemeConfig } from '@rise4fun/docusaurus-theme-side-editor'; 3 | import validatePeerDependencies from 'validate-peer-dependencies'; 4 | 5 | validatePeerDependencies(__dirname); 6 | 7 | export default function themeSideEditor(): Plugin { 8 | return { 9 | name: '@rise4fun/docusaurus-theme-side-editor', 10 | 11 | getThemePath() { 12 | return '../lib/theme'; 13 | }, 14 | getTypeScriptThemePath() { 15 | return '../src/theme'; 16 | }, 17 | }; 18 | } 19 | 20 | export function getSwizzleComponentList() { 21 | return undefined; 22 | } 23 | 24 | export { ThemeConfig, SideEditorThemeConfig }; 25 | -------------------------------------------------------------------------------- /packages/docusaurus-theme-side-editor/src/theme-side-editor.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '@rise4fun/docusaurus-theme-side-editor' { 4 | export type ThemeConfig = { 5 | sideEditor: SideEditorThemeConfig; 6 | }; 7 | export interface SideEditorThemeConfig { 8 | editors: IFrameEditorConfig[]; 9 | persistenceId?: string; 10 | } 11 | export interface SideEditorConfig { 12 | id: string; 13 | type: string; 14 | 15 | language?: string; 16 | } 17 | 18 | export interface IFrameEditorConfig extends SideEditorConfig { 19 | title?: string; 20 | lightUrl: string; 21 | darkUrl: string; 22 | type: 'iframe'; 23 | className?: string; 24 | allow?: string; 25 | sandbox?: string; 26 | 27 | message?: object; 28 | messageIdFieldName?: string; 29 | messageTextFieldName?: string; 30 | readyMessage: Record; 31 | } 32 | 33 | export default function themeSideEditor(): object; 34 | } 35 | 36 | declare module '@theme/SideEditorRoot' { 37 | export interface Props { 38 | children: React.ReactNode; 39 | } 40 | 41 | export default function SideEditorRoot(props: Props): JSX.Element; 42 | } 43 | 44 | declare module '@theme/IFrameEditor' { 45 | import type { IFrameEditorConfig } from '@rise4fun/docusaurus-theme-side-editor'; 46 | export interface Props { 47 | source?: { text?: string }; 48 | config: IFrameEditorConfig; 49 | } 50 | 51 | export default function IFrameEditor(props: Props): JSX.Element; 52 | } 53 | 54 | declare module '@theme/SideEditorButton' { 55 | export interface Props { 56 | editorId: string; 57 | text: string; 58 | className?: string; 59 | title?: string; 60 | label?: string; 61 | } 62 | 63 | export default function SideEditorButton(props: Props): JSX.Element; 64 | } 65 | 66 | declare module '@theme/SideEditorCodePanel' { 67 | export interface Props { 68 | className?: string; 69 | } 70 | 71 | export default function SideEditorCodePanel(props: Props): JSX.Element; 72 | } 73 | -------------------------------------------------------------------------------- /packages/docusaurus-theme-side-editor/src/theme/IFrameEditor/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useRef, useEffect } from 'react'; 2 | import type { Props } from '@theme/IFrameEditor'; 3 | import styles from './styles.module.css'; 4 | import clsx from 'clsx'; 5 | import useHtmlDataTheme from '../../client/useHtmlDataTheme'; 6 | import useBaseUrl from '@docusaurus/useBaseUrl'; 7 | 8 | export default function IFrameEditor(props: Props) { 9 | const { config, source = {} } = props; 10 | const { text } = source; 11 | const { 12 | id, 13 | title, 14 | lightUrl, 15 | darkUrl, 16 | message = {}, 17 | readyMessage, 18 | className, 19 | messageTextFieldName = 'text', 20 | messageIdFieldName = 'mid', 21 | allow = 'accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; usb; xr-spatial-tracking; serial; bluetooth', 22 | sandbox = 'allow-forms allow-scripts allow-downloads allow-modals allow-popups allow-presentation allow-same-origin allow-scripts', 23 | } = config; 24 | const colorMode = useHtmlDataTheme(); 25 | const url = useBaseUrl(colorMode === 'dark' ? darkUrl : lightUrl); 26 | const frameId = `${id}-editor-frame`; 27 | const iframeRef = useRef(null); 28 | 29 | const postSource = () => { 30 | const iframe = iframeRef.current; 31 | const editorWindow = iframe?.contentWindow; 32 | if (!editorWindow) return; 33 | 34 | const id = Math.random() + ''; 35 | const msg = { 36 | ...message, 37 | [messageIdFieldName]: id, 38 | [messageTextFieldName]: text, 39 | }; 40 | editorWindow.postMessage(msg, '*'); 41 | }; 42 | 43 | // when source changes 44 | useEffect(() => postSource(), [url, source]); 45 | 46 | // sniff for a ready message from the iframe 47 | useEffect(() => { 48 | if (!readyMessage || typeof window === 'undefined') return; 49 | 50 | const iframe = iframeRef.current; 51 | const editorWindow = iframe?.contentWindow; 52 | if (!editorWindow) return; 53 | 54 | const handleMessage = (ev: MessageEvent) => { 55 | const { data } = ev; 56 | if (Object.entries(readyMessage).every(([key, value]) => data[key] === readyMessage[key])) { 57 | window.removeEventListener('message', handleMessage); 58 | postSource(); 59 | } 60 | }; 61 | window.addEventListener('message', handleMessage); 62 | return () => { 63 | window.removeEventListener('message', handleMessage); 64 | }; 65 | }, [url]); 66 | 67 | return ( 68 |