├── example ├── static │ ├── .nojekyll │ └── img │ │ ├── favicon.ico │ │ ├── logo.svg │ │ ├── undraw_docusaurus_tree.svg │ │ ├── undraw_docusaurus_mountain.svg │ │ └── undraw_docusaurus_react.svg ├── sidebars.js ├── docs │ ├── doc2.md │ ├── mdx.md │ ├── doc3.md │ └── doc1.md ├── babel.config.js ├── .gitignore ├── blog │ ├── 2019-05-28-hola.md │ ├── 2019-05-29-hello-world.md │ └── 2019-05-30-welcome.md ├── src │ ├── pages │ │ ├── styles.module.css │ │ └── index.js │ └── css │ │ └── custom.css ├── README.md ├── package.json └── docusaurus.config.js ├── tests ├── __mocks__ │ ├── react.ts │ └── @theme-init │ │ └── CodeBlock.ts ├── ReferenceCodeBlock.test.ts └── __snapshots__ │ └── ReferenceCodeBlock.test.ts.snap ├── .github ├── assets │ └── example.png ├── dependabot.yml └── workflows │ ├── test.yml │ └── publish.yml ├── .npmignore ├── src ├── types │ └── @theme-init.d.ts ├── index.ts └── theme │ ├── types.ts │ ├── CodeBlock │ └── index.tsx │ └── ReferenceCodeBlock │ └── index.tsx ├── jest.config.js ├── .editorconfig ├── LICENSE ├── package.json ├── README.md ├── .gitignore ├── CONTRIBUTING.md └── tsconfig.json /example/static/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/__mocks__/react.ts: -------------------------------------------------------------------------------- 1 | export default {} 2 | -------------------------------------------------------------------------------- /tests/__mocks__/@theme-init/CodeBlock.ts: -------------------------------------------------------------------------------- 1 | export default {} 2 | -------------------------------------------------------------------------------- /.github/assets/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saucelabs/docusaurus-theme-github-codeblock/HEAD/.github/assets/example.png -------------------------------------------------------------------------------- /example/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saucelabs/docusaurus-theme-github-codeblock/HEAD/example/static/img/favicon.ico -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | example 2 | coverage 3 | node_modules 4 | src 5 | tests 6 | tsconfig.json 7 | .github 8 | .editorconfig 9 | *.tgz 10 | /*.js 11 | -------------------------------------------------------------------------------- /example/sidebars.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | someSidebar: { 3 | Docusaurus: ['doc1', 'doc2', 'doc3'], 4 | Features: ['mdx'], 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /example/docs/doc2.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: doc2 3 | title: Document Number 2 4 | --- 5 | 6 | This is a link to [another document.](doc3.md) This is a link to an [external page.](http://www.example.com/) 7 | -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | plugins: [ 4 | '@babel/plugin-transform-modules-commonjs' 5 | ] 6 | }; 7 | -------------------------------------------------------------------------------- /src/types/@theme-init.d.ts: -------------------------------------------------------------------------------- 1 | declare module '@theme-init/CodeBlock' { 2 | export type Props = { 3 | readonly children: string; 4 | readonly className?: string; 5 | readonly metastring?: string; 6 | }; 7 | 8 | const CodeBlock: (props: Props) => JSX.Element; 9 | export default CodeBlock; 10 | } 11 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | collectCoverage: true, 4 | testMatch: ['/tests/**/*.test.ts'], 5 | transform: { 6 | '^.+\\.(ts|js|tsx)$': 'ts-jest' 7 | }, 8 | coverageThreshold: { 9 | global: { 10 | branches: 20, 11 | functions: 42, 12 | lines: 47, 13 | statements: 47 14 | } 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | indent_style = space 10 | indent_size = 4 11 | 12 | end_of_line = lf 13 | charset = utf-8 14 | trim_trailing_whitespace = true 15 | insert_final_newline = true 16 | 17 | [{*.json,.eslintrc.js,*.yml}] 18 | indent_size = 2 19 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Sauce Labs, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import path from 'path' 9 | 10 | export default () => ({ 11 | name: 'docusaurus-theme-github-codeblock', 12 | 13 | getThemePath() { 14 | return path.resolve(__dirname, './theme') 15 | } 16 | }) 17 | -------------------------------------------------------------------------------- /example/blog/2019-05-28-hola.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: hola 3 | title: Hola 4 | author: Gao Wei 5 | author_title: Docusaurus Core Team 6 | author_url: https://github.com/wgao19 7 | author_image_url: https://avatars1.githubusercontent.com/u/2055384?v=4 8 | tags: [hola, docusaurus] 9 | --- 10 | 11 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 12 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: npm 9 | directory: "/" 10 | schedule: 11 | interval: weekly 12 | open-pull-requests-limit: 10 13 | -------------------------------------------------------------------------------- /example/blog/2019-05-29-hello-world.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: hello-world 3 | title: Hello 4 | author: Endilie Yacop Sucipto 5 | author_title: Maintainer of Docusaurus 6 | author_url: https://github.com/endiliey 7 | author_image_url: https://avatars1.githubusercontent.com/u/17883920?s=460&v=4 8 | tags: [hello, docusaurus] 9 | --- 10 | 11 | Welcome to this blog. This blog is created with [**Docusaurus 2 alpha**](https://v2.docusaurus.io/). 12 | 13 | 14 | 15 | This is a test post. 16 | 17 | A whole bunch of other information. 18 | -------------------------------------------------------------------------------- /example/blog/2019-05-30-welcome.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: welcome 3 | title: Welcome 4 | author: Yangshun Tay 5 | author_title: Front End Engineer @ Facebook 6 | author_url: https://github.com/yangshun 7 | author_image_url: https://avatars0.githubusercontent.com/u/1315101?s=400&v=4 8 | tags: [facebook, hello, docusaurus] 9 | --- 10 | 11 | Blog features are powered by the blog plugin. Simply add files to the `blog` directory. It supports tags as well! 12 | 13 | Delete the whole directory if you don't want the blog features. As simple as that! 14 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test Changes 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | pull_request: 7 | 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Clone Repository 13 | uses: actions/checkout@v3 14 | - name: Setup Node version 15 | uses: actions/setup-node@v3 16 | with: 17 | node-version: 18 18 | - name: Install dependencies 19 | run: npm ci 20 | - run: npm run build 21 | - name: Run tests 22 | run: npm test 23 | -------------------------------------------------------------------------------- /example/docs/mdx.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: mdx 3 | title: Powered by MDX 4 | --- 5 | 6 | You can write JSX and use React components within your Markdown thanks to [MDX](https://mdxjs.com/). 7 | 8 | export const Highlight = ({children, color}) => ( {children} ); 14 | 15 | Docusaurus green and Facebook blue are my favorite colors. 16 | 17 | I can write **Markdown** alongside my _JSX_! 18 | -------------------------------------------------------------------------------- /src/theme/types.ts: -------------------------------------------------------------------------------- 1 | import type { Props } from '@theme-init/CodeBlock' 2 | 3 | export interface GitHubReference { 4 | url: string 5 | fromLine: number 6 | toLine: number 7 | title: string 8 | } 9 | 10 | export interface ReferenceCodeBlockProps extends Props { 11 | reference: string 12 | } 13 | 14 | export type DispatchTypes = 'reset' | 'loading' | 'loaded' | 'error' 15 | 16 | export interface DispatchMessage { 17 | type: DispatchTypes 18 | value: string | Error 19 | } 20 | 21 | export interface CustomizedLinkOptions { 22 | title: string | undefined 23 | linkText: string 24 | noteStyling: React.CSSProperties 25 | useCustomStyling: boolean 26 | } -------------------------------------------------------------------------------- /example/src/pages/styles.module.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable docusaurus/copyright-header */ 2 | 3 | /** 4 | * CSS files with the .module.css suffix will be treated as CSS modules 5 | * and scoped locally. 6 | */ 7 | 8 | .heroBanner { 9 | padding: 4rem 0; 10 | text-align: center; 11 | position: relative; 12 | overflow: hidden; 13 | } 14 | 15 | @media screen and (max-width: 966px) { 16 | .heroBanner { 17 | padding: 2rem; 18 | } 19 | } 20 | 21 | .buttons { 22 | display: flex; 23 | align-items: center; 24 | justify-content: center; 25 | } 26 | 27 | .features { 28 | display: flex; 29 | align-items: center; 30 | padding: 2rem 0; 31 | width: 100%; 32 | } 33 | 34 | .featureImage { 35 | height: 200px; 36 | width: 200px; 37 | } 38 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://v2.docusaurus.io/), a modern static website generator. 4 | 5 | ## Installation 6 | 7 | ```console 8 | yarn install 9 | ``` 10 | 11 | ## Local Development 12 | 13 | ```console 14 | yarn start 15 | ``` 16 | 17 | This command starts a local development server and open up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ## Build 20 | 21 | ```console 22 | yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ## Deployment 28 | 29 | ```console 30 | GIT_USER= USE_SSH=true yarn deploy 31 | ``` 32 | 33 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 34 | -------------------------------------------------------------------------------- /src/theme/CodeBlock/index.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import React from 'react' 9 | import ReferenceCodeBlock from '../ReferenceCodeBlock' 10 | import CodeBlock from '@theme-init/CodeBlock' 11 | 12 | import type { ReferenceCodeBlockProps } from '../types' 13 | 14 | const componentWrapper = (Component: typeof CodeBlock) => { 15 | const WrappedComponent = (props: ReferenceCodeBlockProps) => { 16 | if (props.reference || props.metastring?.split(' ').includes('reference')) { 17 | return ( 18 | 19 | ); 20 | } 21 | 22 | return 23 | }; 24 | 25 | return WrappedComponent; 26 | }; 27 | 28 | module.exports = componentWrapper(CodeBlock) 29 | -------------------------------------------------------------------------------- /example/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable docusaurus/copyright-header */ 2 | /** 3 | * Any CSS included here will be global. The classic template 4 | * bundles Infima by default. Infima is a CSS framework designed to 5 | * work well for content-centric websites. 6 | */ 7 | 8 | /* You can override the default Infima variables here. */ 9 | :root { 10 | --ifm-color-primary: #25c2a0; 11 | --ifm-color-primary-dark: rgb(33, 175, 144); 12 | --ifm-color-primary-darker: rgb(31, 165, 136); 13 | --ifm-color-primary-darkest: rgb(26, 136, 112); 14 | --ifm-color-primary-light: rgb(70, 203, 174); 15 | --ifm-color-primary-lighter: rgb(102, 212, 189); 16 | --ifm-color-primary-lightest: rgb(146, 224, 208); 17 | --ifm-code-font-size: 95%; 18 | } 19 | 20 | .docusaurus-highlight-code-line { 21 | background-color: rgb(72, 77, 91); 22 | display: block; 23 | margin: 0 calc(-1 * var(--ifm-pre-padding)); 24 | padding: 0 var(--ifm-pre-padding); 25 | } 26 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "serve": "docusaurus serve" 12 | }, 13 | "dependencies": { 14 | "@babel/plugin-transform-modules-commonjs": "^7.12.1", 15 | "@docusaurus/core": "2.0.0-alpha.65", 16 | "@docusaurus/preset-classic": "2.0.0-alpha.65", 17 | "@mdx-js/react": "^1.5.8", 18 | "clsx": "^1.1.1", 19 | "react": "^16.8.4", 20 | "react-dom": "^16.8.4" 21 | }, 22 | "browserslist": { 23 | "production": [ 24 | ">0.2%", 25 | "not dead", 26 | "not op_mini all" 27 | ], 28 | "development": [ 29 | "last 1 chrome version", 30 | "last 1 firefox version", 31 | "last 1 safari version" 32 | ] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Sauce Labs 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 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@saucelabs/theme-github-codeblock", 3 | "version": "0.3.0", 4 | "description": "A Docusaurus v2 plugin that supports referencing code examples from public GitHub repositories.", 5 | "main": "build/index.js", 6 | "directories": { 7 | "example": "example" 8 | }, 9 | "scripts": { 10 | "build": "tsc", 11 | "release": "release-it --github.release --ci --npm.skipChecks --no-git.requireCleanWorkingDir", 12 | "test": "jest", 13 | "watch": "tsc --watch" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/saucelabs/docusaurus-code-references.git" 18 | }, 19 | "keywords": [ 20 | "docusarus" 21 | ], 22 | "author": "Open Source Program Office Sauce Labs ", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/saucelabs/docusaurus-code-references/issues" 26 | }, 27 | "homepage": "https://github.com/saucelabs/docusaurus-code-references#readme", 28 | "devDependencies": { 29 | "@types/jest": "^27.0.0", 30 | "@types/node": "^20.2.3", 31 | "@types/react": "^18.2.7", 32 | "@types/recursive-readdir": "^2.2.0", 33 | "jest": "^26.6.3", 34 | "release-it": "^17.0.0", 35 | "ts-jest": "^26.4.4", 36 | "typescript": "^4.0.3" 37 | }, 38 | "publishConfig": { 39 | "access": "public" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Manual NPM Publish 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | releaseType: 7 | description: "Release Type" 8 | required: true 9 | type: choice 10 | default: "patch" 11 | options: 12 | - patch 13 | - minor 14 | - major 15 | 16 | jobs: 17 | publish: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Clone Repository 21 | uses: actions/checkout@v3 22 | - name: Setup Node version 23 | uses: actions/setup-node@v3 24 | with: 25 | node-version: 18 26 | - name: Install dependencies 27 | run: npm ci 28 | - name: Run tests 29 | run: npm test 30 | - name: Build package 31 | run: npm run build 32 | - name: Setup Git 33 | run: | 34 | git config --global user.name "oss-sauce-bot" 35 | git config --global user.email "66365630+oss-sauce-bot@users.noreply.github.com" 36 | - name: NPM Setup 37 | run: | 38 | npm set registry "https://registry.npmjs.org/" 39 | npm set //registry.npmjs.org/:_authToken $NPM_TOKEN 40 | npm whoami 41 | env: 42 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 43 | - name: Release 44 | run: npm run release -- ${{github.event.inputs.releaseType}} 45 | env: 46 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 47 | -------------------------------------------------------------------------------- /tests/ReferenceCodeBlock.test.ts: -------------------------------------------------------------------------------- 1 | import { parseCustomization, parseReference, codeReducer } from '../src/theme/ReferenceCodeBlock/index' 2 | 3 | test('should parse GitHub reference properly', () => { 4 | expect(parseReference('https://github.com/saucelabs/docusaurus-theme-github-codeblock/blob/main/src/theme/ReferenceCodeBlock/index.tsx')) 5 | .toMatchSnapshot() 6 | expect(parseReference('https://github.com/saucelabs/docusaurus-theme-github-codeblock/blob/main/src/theme/ReferenceCodeBlock/index.tsx#L105-L108')) 7 | .toMatchSnapshot() 8 | }) 9 | 10 | test('should use custom reference link text', () => { 11 | expect(parseCustomization('referenceLinkText="Sample text"')) 12 | .toMatchSnapshot() 13 | expect(parseCustomization('referenceLinkText="Sample text" title="Sample title"')) 14 | .toMatchSnapshot() 15 | expect(parseCustomization('referenceLinkText="Sample text" title="Sample title" customStyling')) 16 | .toMatchSnapshot() 17 | }) 18 | 19 | test('codeReducer', () => { 20 | const prevState = { foo: 'bar' } 21 | expect(codeReducer(prevState, { type: 'reset', value: '' })).toMatchSnapshot() 22 | expect(codeReducer(prevState, { type: 'loading', value: '' })).toMatchSnapshot() 23 | expect(codeReducer(prevState, { type: 'loaded', value: 'foobar' })).toMatchSnapshot() 24 | expect(codeReducer(prevState, { type: 'error', value: 'ups' })).toMatchSnapshot() 25 | // @ts-expect-error 26 | expect(codeReducer(prevState, { type: 'unknown', value: '' })).toEqual(prevState) 27 | }) 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Docusaurus Theme GitHub Codeblock ![Test Changes](https://github.com/saucelabs/docusaurus-theme-github-codeblock/workflows/Test%20Changes/badge.svg?branch=main) 2 | ================================= 3 | 4 | A Docusaurus v2 plugin that supports referencing code examples from public GitHub repositories. 5 | 6 | > Note: this theme plugin requires [Docusaurus v2](https://v2.docusaurus.io/) 7 | 8 | ## Install 9 | 10 | First, add the theme plugin to your dependencies: 11 | 12 | ```sh 13 | # NPM 14 | npm install --save @saucelabs/theme-github-codeblock 15 | 16 | # Yarn 17 | yarn add @saucelabs/theme-github-codeblock 18 | ``` 19 | 20 | ## Usage 21 | 22 | Add the theme plugin to your list of themes in the `docusaurus.config.js`: 23 | 24 | ```js 25 | // ... 26 | themes: [ 27 | '@saucelabs/theme-github-codeblock' 28 | ], 29 | // ... 30 | ``` 31 | 32 | In order to reference GitHub snippets in your markdown, create code blocks with a `reference` attached to the language meta string and put the link to your GitHub reference in the code block, e.g.: 33 | 34 | ```js reference 35 | https://github.com/saucelabs/docusaurus-theme-github-codeblock/blob/main/src/theme/ReferenceCodeBlock/index.tsx#L105-L108 36 | ``` 37 | 38 | You can also set a custom title: 39 | 40 | ```js reference title="Example" 41 | https://github.com/saucelabs/docusaurus-theme-github-codeblock/blob/main/src/theme/ReferenceCodeBlock/index.tsx#L105-L108 42 | ``` 43 | 44 | The plugin will download the code and display the desired lines: 45 | 46 | ![Plugin Example](https://github.com/saucelabs/docusaurus-theme-github-codeblock/raw/main/.github/assets/example.png 'Plugin Example') 47 | 48 | --- 49 | 50 | If you are interested contributing to this project, see [CONTRIBUTING.md](CONTRIBUTING.md). 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | build -------------------------------------------------------------------------------- /example/docs/doc3.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: doc3 3 | title: This is Document Number 3 4 | --- 5 | 6 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. In ac euismod odio, eu consequat dui. Nullam molestie consectetur risus id imperdiet. Proin sodales ornare turpis, non mollis massa ultricies id. Nam at nibh scelerisque, feugiat ante non, dapibus tortor. Vivamus volutpat diam quis tellus elementum bibendum. Praesent semper gravida velit quis aliquam. Etiam in cursus neque. Nam lectus ligula, malesuada et mauris a, bibendum faucibus mi. Phasellus ut interdum felis. Phasellus in odio pulvinar, porttitor urna eget, fringilla lectus. Aliquam sollicitudin est eros. Mauris consectetur quam vitae mauris interdum hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. 7 | 8 | Duis et egestas libero, imperdiet faucibus ipsum. Sed posuere eget urna vel feugiat. Vivamus a arcu sagittis, fermentum urna dapibus, congue lectus. Fusce vulputate porttitor nisl, ac cursus elit volutpat vitae. Nullam vitae ipsum egestas, convallis quam non, porta nibh. Morbi gravida erat nec neque bibendum, eu pellentesque velit posuere. Fusce aliquam erat eu massa eleifend tristique. 9 | 10 | Sed consequat sollicitudin ipsum eget tempus. Integer a aliquet velit. In justo nibh, pellentesque non suscipit eget, gravida vel lacus. Donec odio ante, malesuada in massa quis, pharetra tristique ligula. Donec eros est, tristique eget finibus quis, semper non nisl. Vivamus et elit nec enim ornare placerat. Sed posuere odio a elit cursus sagittis. 11 | 12 | Phasellus feugiat purus eu tortor ultrices finibus. Ut libero nibh, lobortis et libero nec, dapibus posuere eros. Sed sagittis euismod justo at consectetur. Nulla finibus libero placerat, cursus sapien at, eleifend ligula. Vivamus elit nisl, hendrerit ac nibh eu, ultrices tempus dui. Nam tellus neque, commodo non rhoncus eu, gravida in risus. Nullam id iaculis tortor. 13 | 14 | Nullam at odio in sem varius tempor sit amet vel lorem. Etiam eu hendrerit nisl. Fusce nibh mauris, vulputate sit amet ex vitae, congue rhoncus nisl. Sed eget tellus purus. Nullam tempus commodo erat ut tristique. Cras accumsan massa sit amet justo consequat eleifend. Integer scelerisque vitae tellus id consectetur. 15 | -------------------------------------------------------------------------------- /tests/__snapshots__/ReferenceCodeBlock.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`codeReducer 1`] = ` 4 | Object { 5 | "code": "loading...", 6 | "error": null, 7 | "loading": null, 8 | } 9 | `; 10 | 11 | exports[`codeReducer 2`] = ` 12 | Object { 13 | "foo": "bar", 14 | "loading": true, 15 | } 16 | `; 17 | 18 | exports[`codeReducer 3`] = ` 19 | Object { 20 | "code": "foobar", 21 | "foo": "bar", 22 | "loading": false, 23 | } 24 | `; 25 | 26 | exports[`codeReducer 4`] = ` 27 | Object { 28 | "error": "ups", 29 | "foo": "bar", 30 | "loading": false, 31 | } 32 | `; 33 | 34 | exports[`should parse GitHub reference properly 1`] = ` 35 | Object { 36 | "fromLine": 0, 37 | "title": "src/theme/ReferenceCodeBlock/index.ts", 38 | "toLine": Infinity, 39 | "url": "https://raw.githubusercontent.com/saucelabs/docusaurus-theme-github-codeblock/main/src/theme/ReferenceCodeBlock/index.ts", 40 | } 41 | `; 42 | 43 | exports[`should parse GitHub reference properly 2`] = ` 44 | Object { 45 | "fromLine": 104, 46 | "title": "src/theme/ReferenceCodeBlock/index.tsx", 47 | "toLine": 9, 48 | "url": "https://raw.githubusercontent.com/saucelabs/docusaurus-theme-github-codeblock/main/src/theme/ReferenceCodeBlock/index.tsx", 49 | } 50 | `; 51 | 52 | exports[`should use custom reference link text 1`] = ` 53 | Object { 54 | "linkText": "Sample text", 55 | "noteStyling": Object { 56 | "color": "#0E75DD", 57 | "fontSize": ".9em", 58 | "fontWeight": 600, 59 | "paddingBottom": "13px", 60 | "textAlign": "center", 61 | "textDecoration": "underline", 62 | }, 63 | "title": undefined, 64 | "useCustomStyling": false, 65 | } 66 | `; 67 | 68 | exports[`should use custom reference link text 2`] = ` 69 | Object { 70 | "linkText": "Sample text", 71 | "noteStyling": Object { 72 | "color": "#0E75DD", 73 | "fontSize": ".9em", 74 | "fontWeight": 600, 75 | "paddingBottom": "13px", 76 | "textAlign": "center", 77 | "textDecoration": "underline", 78 | }, 79 | "title": "Sample title", 80 | "useCustomStyling": false, 81 | } 82 | `; 83 | 84 | exports[`should use custom reference link text 3`] = ` 85 | Object { 86 | "linkText": "Sample text", 87 | "noteStyling": Object {}, 88 | "title": "Sample title", 89 | "useCustomStyling": true, 90 | } 91 | `; 92 | -------------------------------------------------------------------------------- /example/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import Layout from '@theme/Layout'; 4 | import Link from '@docusaurus/Link'; 5 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; 6 | import useBaseUrl from '@docusaurus/useBaseUrl'; 7 | import styles from './styles.module.css'; 8 | 9 | const features = [ 10 | { 11 | title: 'Easy to Use', 12 | imageUrl: 'img/undraw_docusaurus_mountain.svg', 13 | description: ( 14 | <> 15 | Docusaurus was designed from the ground up to be easily installed and 16 | used to get your website up and running quickly. 17 | 18 | ), 19 | }, 20 | { 21 | title: 'Focus on What Matters', 22 | imageUrl: 'img/undraw_docusaurus_tree.svg', 23 | description: ( 24 | <> 25 | Docusaurus lets you focus on your docs, and we'll do the chores. Go 26 | ahead and move your docs into the docs directory. 27 | 28 | ), 29 | }, 30 | { 31 | title: 'Powered by React', 32 | imageUrl: 'img/undraw_docusaurus_react.svg', 33 | description: ( 34 | <> 35 | Extend or customize your website layout by reusing React. Docusaurus can 36 | be extended while reusing the same header and footer. 37 | 38 | ), 39 | }, 40 | ]; 41 | 42 | function Feature({imageUrl, title, description}) { 43 | const imgUrl = useBaseUrl(imageUrl); 44 | return ( 45 |
46 | {imgUrl && ( 47 |
48 | {title} 49 |
50 | )} 51 |

{title}

52 |

{description}

53 |
54 | ); 55 | } 56 | 57 | function Home() { 58 | const context = useDocusaurusContext(); 59 | const {siteConfig = {}} = context; 60 | return ( 61 | 64 |
65 |
66 |

{siteConfig.title}

67 |

{siteConfig.tagline}

68 |
69 | 75 | Get Started 76 | 77 |
78 |
79 |
80 |
81 | {features && features.length > 0 && ( 82 |
83 |
84 |
85 | {features.map((props, idx) => ( 86 | 87 | ))} 88 |
89 |
90 |
91 | )} 92 |
93 |
94 | ); 95 | } 96 | 97 | export default Home; 98 | -------------------------------------------------------------------------------- /example/docusaurus.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | title: 'My Site', 5 | tagline: 'The tagline of my site', 6 | url: 'https://your-docusaurus-test-site.com', 7 | baseUrl: '/', 8 | onBrokenLinks: 'throw', 9 | favicon: 'img/favicon.ico', 10 | organizationName: 'facebook', // Usually your GitHub org/user name. 11 | projectName: 'docusaurus', // Usually your repo name. 12 | themes: [ 13 | path.resolve(__dirname, '..', 'build') 14 | ], 15 | themeConfig: { 16 | navbar: { 17 | title: 'My Site', 18 | logo: { 19 | alt: 'My Site Logo', 20 | src: 'img/logo.svg', 21 | }, 22 | items: [ 23 | { 24 | to: 'docs/', 25 | activeBasePath: 'docs', 26 | label: 'Docs', 27 | position: 'left', 28 | }, 29 | {to: 'blog', label: 'Blog', position: 'left'}, 30 | { 31 | href: 'https://github.com/facebook/docusaurus', 32 | label: 'GitHub', 33 | position: 'right', 34 | }, 35 | ], 36 | }, 37 | footer: { 38 | style: 'dark', 39 | links: [ 40 | { 41 | title: 'Docs', 42 | items: [ 43 | { 44 | label: 'Style Guide', 45 | to: 'docs/', 46 | }, 47 | { 48 | label: 'Second Doc', 49 | to: 'docs/doc2/', 50 | }, 51 | ], 52 | }, 53 | { 54 | title: 'Community', 55 | items: [ 56 | { 57 | label: 'Stack Overflow', 58 | href: 'https://stackoverflow.com/questions/tagged/docusaurus', 59 | }, 60 | { 61 | label: 'Discord', 62 | href: 'https://discordapp.com/invite/docusaurus', 63 | }, 64 | { 65 | label: 'Twitter', 66 | href: 'https://twitter.com/docusaurus', 67 | }, 68 | ], 69 | }, 70 | { 71 | title: 'More', 72 | items: [ 73 | { 74 | label: 'Blog', 75 | to: 'blog', 76 | }, 77 | { 78 | label: 'GitHub', 79 | href: 'https://github.com/facebook/docusaurus', 80 | }, 81 | ], 82 | }, 83 | ], 84 | copyright: `Copyright © ${new Date().getFullYear()} My Project, Inc. Built with Docusaurus.`, 85 | }, 86 | }, 87 | presets: [ 88 | [ 89 | '@docusaurus/preset-classic', 90 | { 91 | docs: { 92 | sidebarPath: require.resolve('./sidebars.js'), 93 | // Please change this to your repo. 94 | editUrl: 95 | 'https://github.com/facebook/docusaurus/edit/master/website/', 96 | }, 97 | blog: { 98 | showReadingTime: true, 99 | // Please change this to your repo. 100 | editUrl: 101 | 'https://github.com/facebook/docusaurus/edit/master/website/blog/', 102 | }, 103 | theme: { 104 | customCss: require.resolve('./src/css/custom.css'), 105 | }, 106 | }, 107 | ], 108 | ], 109 | }; 110 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to `docusaurus-theme-github-codeblock` 2 | 3 | **Thank you for your interest in `docusaurus-theme-github-codeblock`. Your contributions are highly welcome.** 4 | 5 | There are multiple ways of getting involved: 6 | 7 | - [Report a bug](#report-a-bug) 8 | - [Suggest a feature](#suggest-a-feature) 9 | - [Contribute code](#contribute-code) 10 | 11 | Below are a few guidelines we would like you to follow. 12 | If you need help, please reach out to us by opening an issue. 13 | 14 | ## Report a bug 15 | Reporting bugs is one of the best ways to contribute. Before creating a bug report, please check that an [issue](/issues) reporting the same problem does not already exist. If there is such an issue, you may add your information as a comment. 16 | 17 | To report a new bug you should open an issue that summarizes the bug and set the label to "bug". 18 | 19 | If you want to provide a fix along with your bug report: That is great! In this case please send us a pull request as described in section [Contribute Code](#contribute-code). 20 | 21 | ## Suggest a feature 22 | To request a new feature you should open an [issue](../../issues/new) and summarize the desired functionality and its use case. Set the issue label to "feature". 23 | 24 | ## Contribute code 25 | This is an outline of what the workflow for code contributions looks like 26 | 27 | - Check the list of open [issues](../../issues). Either assign an existing issue to yourself, or 28 | create a new one that you would like work on and discuss your ideas and use cases. 29 | 30 | It is always best to discuss your plans beforehand, to ensure that your contribution is in line with our goals. 31 | 32 | - Fork the repository on GitHub 33 | - Create a topic branch from where you want to base your work. This is usually master. 34 | - Open a new pull request, label it `work in progress` and outline what you will be contributing 35 | - Make commits of logical units. 36 | - Make sure you sign-off on your commits `git commit -s -m "adding X to change Y"` 37 | - Write good commit messages (see below). 38 | - Push your changes to a topic branch in your fork of the repository. 39 | - As you push your changes, update the pull request with new information and tasks as you complete them 40 | - Project maintainers might comment on your work as you progress 41 | - When you are done, remove the `work in progess` label and ping the maintainers for a review 42 | - Your pull request must receive a :thumbsup: from two [maintainers](MAINTAINERS) 43 | 44 | Thanks for your contributions! 45 | 46 | ### Commit messages 47 | Your commit messages ideally can answer two questions: what changed and why. The subject line should feature the “what” and the body of the commit should describe the “why”. 48 | 49 | When creating a pull request, its description should reference the corresponding issue id. 50 | 51 | ### Sign your work / Developer certificate of origin 52 | All contributions (including pull requests) must agree to the Developer Certificate of Origin (DCO) version 1.1. This is exactly the same one created and used by the Linux kernel developers and posted on http://developercertificate.org/. This is a developer's certification that he or she has the right to submit the patch for inclusion into the project. Simply submitting a contribution implies this agreement, however, please include a "Signed-off-by" tag in every patch (this tag is a conventional way to confirm that you agree to the DCO) - you can automate this with a [Git hook](https://stackoverflow.com/questions/15015894/git-add-signed-off-by-line-using-format-signoff-not-working) 53 | 54 | ``` 55 | git commit -s -m "adding X to change Y" 56 | ``` 57 | 58 | **Have fun, and happy hacking!** 59 | -------------------------------------------------------------------------------- /src/theme/ReferenceCodeBlock/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useReducer } from 'react' 2 | import CodeBlock from '@theme-init/CodeBlock' 3 | 4 | import type { ReferenceCodeBlockProps, GitHubReference, DispatchMessage, CustomizedLinkOptions } from '../types' 5 | 6 | const DEFAULT_LINK_TEXT = 'See full example on GitHub' 7 | 8 | const initialFetchResultState = { 9 | code: 'loading...', 10 | error: null, 11 | loading: null, 12 | } 13 | 14 | const noteStyle: React.CSSProperties = { 15 | fontSize: '.9em', 16 | fontWeight: 600, 17 | color: '#0E75DD', 18 | textAlign: 'center', 19 | paddingBottom: '13px', 20 | textDecoration: 'underline', 21 | } 22 | 23 | /** 24 | * parses GitHub reference 25 | * @param {string} ref url to github file 26 | */ 27 | export function parseReference (ref: string): GitHubReference { 28 | const fullUrl = ref.slice(ref.indexOf('https'), -1) 29 | const [url, loc] = fullUrl.split('#') 30 | 31 | /** 32 | * webpack causes failures when it tries to render this page 33 | */ 34 | const global = globalThis || {} 35 | if (!global.URL) { 36 | // @ts-ignore 37 | global.URL = URL 38 | } 39 | 40 | const [org, repo, blob, branch, ...pathSeg] = new global.URL(url).pathname.split('/').slice(1) 41 | const [fromLine, toLine] = loc 42 | ? loc.split('-').map((lineNr) => parseInt(lineNr.slice(1), 10) - 1) 43 | : [0, Infinity] 44 | 45 | return { 46 | url: `https://raw.githubusercontent.com/${org}/${repo}/${branch}/${pathSeg.join('/')}`, 47 | fromLine, 48 | toLine, 49 | title: pathSeg.join('/') 50 | } 51 | } 52 | 53 | export function parseCustomization (metastring: string | undefined): CustomizedLinkOptions { 54 | 55 | const refTitle = metastring?.match(/title="(?.*?)"/)?.groups?.title; 56 | 57 | const refLinkMatch = metastring?.match(/referenceLinkText="(?<referenceLinkText>.*?)"/); 58 | const refLinkText = refLinkMatch?.groups?.referenceLinkText ?? DEFAULT_LINK_TEXT; 59 | 60 | const customStylingMatch = metastring?.match(/customStyling/); 61 | const refUseCustomStyling = customStylingMatch?.length === 1; 62 | const refNoteStyling = customStylingMatch?.length === 1 ? {} : noteStyle; 63 | 64 | return { 65 | title: refTitle, 66 | linkText: refLinkText, 67 | noteStyling: refNoteStyling, 68 | useCustomStyling: refUseCustomStyling 69 | } 70 | } 71 | 72 | async function fetchCode ({ url, fromLine, toLine }: GitHubReference, fetchResultStateDispatcher: React.Dispatch<DispatchMessage>) { 73 | let res: Response 74 | 75 | try { 76 | res = await fetch(url) 77 | } catch (err) { 78 | return fetchResultStateDispatcher({ type: 'error', value: err as Error }) 79 | } 80 | 81 | if (res.status !== 200) { 82 | const error = await res.text() 83 | return fetchResultStateDispatcher({ type: 'error', value: error }) 84 | } 85 | 86 | const body = (await res.text()).split('\n').slice(fromLine, (toLine || fromLine) + 1) 87 | const preceedingSpace = body.reduce((prev: number, line: string) => { 88 | if (line.length === 0) { 89 | return prev 90 | } 91 | 92 | const spaces = line.match(/^\s+/) 93 | if (spaces) { 94 | return Math.min(prev, spaces[0].length) 95 | } 96 | 97 | return 0 98 | }, Infinity) 99 | 100 | return fetchResultStateDispatcher({ 101 | type: 'loaded', 102 | value: body.map((line) => line.slice(preceedingSpace)).join('\n') 103 | }) 104 | } 105 | 106 | export function codeReducer (prevState: any, { type, value }: DispatchMessage) { 107 | switch (type) { 108 | case 'reset': { 109 | return initialFetchResultState; 110 | } 111 | case 'loading': { 112 | return {...prevState, loading: true}; 113 | } 114 | case 'loaded': { 115 | return {...prevState, code: value, loading: false}; 116 | } 117 | case 'error': { 118 | return {...prevState, error: value, loading: false}; 119 | } 120 | default: 121 | return prevState; 122 | } 123 | } 124 | 125 | function ReferenceCode(props: ReferenceCodeBlockProps) { 126 | const [fetchResultState, fetchResultStateDispatcher] = useReducer( 127 | codeReducer, 128 | initialFetchResultState, 129 | ) 130 | 131 | const codeSnippetDetails = parseReference(props.children) 132 | if (fetchResultState.loading !== false) { 133 | fetchCode(codeSnippetDetails, fetchResultStateDispatcher) 134 | } 135 | 136 | const parsedCustomization = parseCustomization(props.metastring) 137 | 138 | const customProps = { 139 | ...props, 140 | metastring: parsedCustomization.title 141 | ? ` title="${parsedCustomization.title}"` 142 | : ` title="${codeSnippetDetails.title}"`, 143 | children: initialFetchResultState.code, 144 | }; 145 | 146 | return ( 147 | <div> 148 | <CodeBlock {...customProps}>{fetchResultState.code}</CodeBlock> 149 | <div style={parsedCustomization.noteStyling} className={parsedCustomization.useCustomStyling ? 'github-codeblock-reference-link' : ''}> 150 | <a href={props.children} target="_blank">{parsedCustomization.linkText}</a> 151 | </div> 152 | </div> 153 | ); 154 | } 155 | 156 | export default ReferenceCode; 157 | -------------------------------------------------------------------------------- /example/docs/doc1.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: doc1 3 | title: Style Guide 4 | sidebar_label: Style Guide 5 | slug: / 6 | --- 7 | 8 | This theme provides the following: 9 | 10 | ```js reference 11 | https://github.com/saucelabs/docusaurus-theme-github-codeblock/blob/main/src/theme/ReferenceCodeBlock/index.tsx#L104-L107 12 | ``` 13 | 14 | ```js reference title="Custom Title" 15 | https://github.com/saucelabs/docusaurus-theme-github-codeblock/blob/main/src/theme/ReferenceCodeBlock/index.tsx#L104-L107 16 | ``` 17 | 18 | You can write content using [GitHub-flavored Markdown syntax](https://github.github.com/gfm/). 19 | 20 | ## Markdown Syntax 21 | 22 | To serve as an example page when styling markdown based Docusaurus sites. 23 | 24 | ## Headers 25 | 26 | # H1 - Create the best documentation 27 | 28 | ## H2 - Create the best documentation 29 | 30 | ### H3 - Create the best documentation 31 | 32 | #### H4 - Create the best documentation 33 | 34 | ##### H5 - Create the best documentation 35 | 36 | ###### H6 - Create the best documentation 37 | 38 | --- 39 | 40 | ## Emphasis 41 | 42 | Emphasis, aka italics, with *asterisks* or _underscores_. 43 | 44 | Strong emphasis, aka bold, with **asterisks** or __underscores__. 45 | 46 | Combined emphasis with **asterisks and _underscores_**. 47 | 48 | Strikethrough uses two tildes. ~~Scratch this.~~ 49 | 50 | --- 51 | 52 | ## Lists 53 | 54 | 1. First ordered list item 55 | 1. Another item 56 | - Unordered sub-list. 57 | 1. Actual numbers don't matter, just that it's a number 58 | 1. Ordered sub-list 59 | 1. And another item. 60 | 61 | * Unordered list can use asterisks 62 | 63 | - Or minuses 64 | 65 | + Or pluses 66 | 67 | --- 68 | 69 | ## Links 70 | 71 | [I'm an inline-style link](https://www.google.com/) 72 | 73 | [I'm an inline-style link with title](https://www.google.com/ "Google's Homepage") 74 | 75 | [I'm a reference-style link][arbitrary case-insensitive reference text] 76 | 77 | [You can use numbers for reference-style link definitions][1] 78 | 79 | Or leave it empty and use the [link text itself]. 80 | 81 | URLs and URLs in angle brackets will automatically get turned into links. http://www.example.com/ or <http://www.example.com/> and sometimes example.com (but not on GitHub, for example). 82 | 83 | Some text to show that the reference links can follow later. 84 | 85 | [arbitrary case-insensitive reference text]: https://www.mozilla.org/ 86 | [1]: http://slashdot.org/ 87 | [link text itself]: http://www.reddit.com/ 88 | 89 | --- 90 | 91 | ## Images 92 | 93 | Here's our logo (hover to see the title text): 94 | 95 | Inline-style: ![alt text](https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png 'Logo Title Text 1') 96 | 97 | Reference-style: ![alt text][logo] 98 | 99 | [logo]: https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png 'Logo Title Text 2' 100 | 101 | Images from any folder can be used by providing path to file. Path should be relative to markdown file. 102 | 103 | ![img](../static/img/logo.svg) 104 | 105 | --- 106 | 107 | ## Code 108 | 109 | ```javascript 110 | var s = 'JavaScript syntax highlighting'; 111 | alert(s); 112 | ``` 113 | 114 | ```python 115 | s = "Python syntax highlighting" 116 | print(s) 117 | ``` 118 | 119 | ``` 120 | No language indicated, so no syntax highlighting. 121 | But let's throw in a <b>tag</b>. 122 | ``` 123 | 124 | ```js {2} 125 | function highlightMe() { 126 | console.log('This line can be highlighted!'); 127 | } 128 | ``` 129 | 130 | --- 131 | 132 | ## Tables 133 | 134 | Colons can be used to align columns. 135 | 136 | | Tables | Are | Cool | 137 | | ------------- | :-----------: | -----: | 138 | | col 3 is | right-aligned | \$1600 | 139 | | col 2 is | centered | \$12 | 140 | | zebra stripes | are neat | \$1 | 141 | 142 | There must be at least 3 dashes separating each header cell. The outer pipes (|) are optional, and you don't need to make the raw Markdown line up prettily. You can also use inline Markdown. 143 | 144 | | Markdown | Less | Pretty | 145 | | -------- | --------- | ---------- | 146 | | _Still_ | `renders` | **nicely** | 147 | | 1 | 2 | 3 | 148 | 149 | --- 150 | 151 | ## Blockquotes 152 | 153 | > Blockquotes are very handy in email to emulate reply text. This line is part of the same quote. 154 | 155 | Quote break. 156 | 157 | > This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can _put_ **Markdown** into a blockquote. 158 | 159 | --- 160 | 161 | ## Inline HTML 162 | 163 | <dl> 164 | <dt>Definition list</dt> 165 | <dd>Is something people use sometimes.</dd> 166 | 167 | <dt>Markdown in HTML</dt> 168 | <dd>Does *not* work **very** well. Use HTML <em>tags</em>.</dd> 169 | </dl> 170 | 171 | --- 172 | 173 | ## Line Breaks 174 | 175 | Here's a line for us to start with. 176 | 177 | This line is separated from the one above by two newlines, so it will be a _separate paragraph_. 178 | 179 | This line is also a separate paragraph, but... This line is only separated by a single newline, so it's a separate line in the _same paragraph_. 180 | 181 | --- 182 | 183 | ## Admonitions 184 | 185 | :::note 186 | 187 | This is a note 188 | 189 | ::: 190 | 191 | :::tip 192 | 193 | This is a tip 194 | 195 | ::: 196 | 197 | :::important 198 | 199 | This is important 200 | 201 | ::: 202 | 203 | :::caution 204 | 205 | This is a caution 206 | 207 | ::: 208 | 209 | :::warning 210 | 211 | This is a warning 212 | 213 | ::: 214 | -------------------------------------------------------------------------------- /example/static/img/logo.svg: -------------------------------------------------------------------------------- 1 | <svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path fill="#FFF" d="M99 52h84v34H99z"/><path d="M23 163c-7.398 0-13.843-4.027-17.303-10A19.886 19.886 0 0 0 3 163c0 11.046 8.954 20 20 20h20v-20H23z" fill="#3ECC5F"/><path d="M112.98 57.376L183 53V43c0-11.046-8.954-20-20-20H73l-2.5-4.33c-1.112-1.925-3.889-1.925-5 0L63 23l-2.5-4.33c-1.111-1.925-3.889-1.925-5 0L53 23l-2.5-4.33c-1.111-1.925-3.889-1.925-5 0L43 23c-.022 0-.042.003-.065.003l-4.142-4.141c-1.57-1.571-4.252-.853-4.828 1.294l-1.369 5.104-5.192-1.392c-2.148-.575-4.111 1.389-3.535 3.536l1.39 5.193-5.102 1.367c-2.148.576-2.867 3.259-1.296 4.83l4.142 4.142c0 .021-.003.042-.003.064l-4.33 2.5c-1.925 1.111-1.925 3.889 0 5L23 53l-4.33 2.5c-1.925 1.111-1.925 3.889 0 5L23 63l-4.33 2.5c-1.925 1.111-1.925 3.889 0 5L23 73l-4.33 2.5c-1.925 1.111-1.925 3.889 0 5L23 83l-4.33 2.5c-1.925 1.111-1.925 3.889 0 5L23 93l-4.33 2.5c-1.925 1.111-1.925 3.889 0 5L23 103l-4.33 2.5c-1.925 1.111-1.925 3.889 0 5L23 113l-4.33 2.5c-1.925 1.111-1.925 3.889 0 5L23 123l-4.33 2.5c-1.925 1.111-1.925 3.889 0 5L23 133l-4.33 2.5c-1.925 1.111-1.925 3.889 0 5L23 143l-4.33 2.5c-1.925 1.111-1.925 3.889 0 5L23 153l-4.33 2.5c-1.925 1.111-1.925 3.889 0 5L23 163c0 11.046 8.954 20 20 20h120c11.046 0 20-8.954 20-20V83l-70.02-4.376A10.645 10.645 0 0 1 103 68c0-5.621 4.37-10.273 9.98-10.624" fill="#3ECC5F"/><path fill="#3ECC5F" d="M143 183h30v-40h-30z"/><path d="M193 158c-.219 0-.428.037-.639.064-.038-.15-.074-.301-.116-.451A5 5 0 0 0 190.32 148a4.96 4.96 0 0 0-3.016 1.036 26.531 26.531 0 0 0-.335-.336 4.955 4.955 0 0 0 1.011-2.987 5 5 0 0 0-9.599-1.959c-.148-.042-.297-.077-.445-.115.027-.211.064-.42.064-.639a5 5 0 0 0-5-5 5 5 0 0 0-5 5c0 .219.037.428.064.639-.148.038-.297.073-.445.115a4.998 4.998 0 0 0-9.599 1.959c0 1.125.384 2.151 1.011 2.987-3.717 3.632-6.031 8.693-6.031 14.3 0 11.046 8.954 20 20 20 9.339 0 17.16-6.41 19.361-15.064.211.027.42.064.639.064a5 5 0 0 0 5-5 5 5 0 0 0-5-5" fill="#44D860"/><path fill="#3ECC5F" d="M153 123h30v-20h-30z"/><path d="M193 115.5a2.5 2.5 0 1 0 0-5c-.109 0-.214.019-.319.032-.02-.075-.037-.15-.058-.225a2.501 2.501 0 0 0-.963-4.807c-.569 0-1.088.197-1.508.518a6.653 6.653 0 0 0-.168-.168c.314-.417.506-.931.506-1.494a2.5 2.5 0 0 0-4.8-.979A9.987 9.987 0 0 0 183 103c-5.522 0-10 4.478-10 10s4.478 10 10 10c.934 0 1.833-.138 2.69-.377a2.5 2.5 0 0 0 4.8-.979c0-.563-.192-1.077-.506-1.494.057-.055.113-.111.168-.168.42.321.939.518 1.508.518a2.5 2.5 0 0 0 .963-4.807c.021-.074.038-.15.058-.225.105.013.21.032.319.032" fill="#44D860"/><path d="M63 55.5a2.5 2.5 0 0 1-2.5-2.5c0-4.136-3.364-7.5-7.5-7.5s-7.5 3.364-7.5 7.5a2.5 2.5 0 1 1-5 0c0-6.893 5.607-12.5 12.5-12.5S65.5 46.107 65.5 53a2.5 2.5 0 0 1-2.5 2.5" fill="#000"/><path d="M103 183h60c11.046 0 20-8.954 20-20V93h-60c-11.046 0-20 8.954-20 20v70z" fill="#FFFF50"/><path d="M168.02 124h-50.04a1 1 0 1 1 0-2h50.04a1 1 0 1 1 0 2m0 20h-50.04a1 1 0 1 1 0-2h50.04a1 1 0 1 1 0 2m0 20h-50.04a1 1 0 1 1 0-2h50.04a1 1 0 1 1 0 2m0-49.814h-50.04a1 1 0 1 1 0-2h50.04a1 1 0 1 1 0 2m0 19.814h-50.04a1 1 0 1 1 0-2h50.04a1 1 0 1 1 0 2m0 20h-50.04a1 1 0 1 1 0-2h50.04a1 1 0 1 1 0 2M183 61.611c-.012 0-.022-.006-.034-.005-3.09.105-4.552 3.196-5.842 5.923-1.346 2.85-2.387 4.703-4.093 4.647-1.889-.068-2.969-2.202-4.113-4.46-1.314-2.594-2.814-5.536-5.963-5.426-3.046.104-4.513 2.794-5.807 5.167-1.377 2.528-2.314 4.065-4.121 3.994-1.927-.07-2.951-1.805-4.136-3.813-1.321-2.236-2.848-4.75-5.936-4.664-2.994.103-4.465 2.385-5.763 4.4-1.373 2.13-2.335 3.428-4.165 3.351-1.973-.07-2.992-1.51-4.171-3.177-1.324-1.873-2.816-3.993-5.895-3.89-2.928.1-4.399 1.97-5.696 3.618-1.232 1.564-2.194 2.802-4.229 2.724a1 1 0 0 0-.072 2c3.017.101 4.545-1.8 5.872-3.487 1.177-1.496 2.193-2.787 4.193-2.855 1.926-.082 2.829 1.115 4.195 3.045 1.297 1.834 2.769 3.914 5.731 4.021 3.103.104 4.596-2.215 5.918-4.267 1.182-1.834 2.202-3.417 4.15-3.484 1.793-.067 2.769 1.35 4.145 3.681 1.297 2.197 2.766 4.686 5.787 4.796 3.125.108 4.634-2.62 5.949-5.035 1.139-2.088 2.214-4.06 4.119-4.126 1.793-.042 2.728 1.595 4.111 4.33 1.292 2.553 2.757 5.445 5.825 5.556l.169.003c3.064 0 4.518-3.075 5.805-5.794 1.139-2.41 2.217-4.68 4.067-4.773v-2z" fill="#000"/><path fill="#3ECC5F" d="M83 183h40v-40H83z"/><path d="M143 158c-.219 0-.428.037-.639.064-.038-.15-.074-.301-.116-.451A5 5 0 0 0 140.32 148a4.96 4.96 0 0 0-3.016 1.036 26.531 26.531 0 0 0-.335-.336 4.955 4.955 0 0 0 1.011-2.987 5 5 0 0 0-9.599-1.959c-.148-.042-.297-.077-.445-.115.027-.211.064-.42.064-.639a5 5 0 0 0-5-5 5 5 0 0 0-5 5c0 .219.037.428.064.639-.148.038-.297.073-.445.115a4.998 4.998 0 0 0-9.599 1.959c0 1.125.384 2.151 1.011 2.987-3.717 3.632-6.031 8.693-6.031 14.3 0 11.046 8.954 20 20 20 9.339 0 17.16-6.41 19.361-15.064.211.027.42.064.639.064a5 5 0 0 0 5-5 5 5 0 0 0-5-5" fill="#44D860"/><path fill="#3ECC5F" d="M83 123h40v-20H83z"/><path d="M133 115.5a2.5 2.5 0 1 0 0-5c-.109 0-.214.019-.319.032-.02-.075-.037-.15-.058-.225a2.501 2.501 0 0 0-.963-4.807c-.569 0-1.088.197-1.508.518a6.653 6.653 0 0 0-.168-.168c.314-.417.506-.931.506-1.494a2.5 2.5 0 0 0-4.8-.979A9.987 9.987 0 0 0 123 103c-5.522 0-10 4.478-10 10s4.478 10 10 10c.934 0 1.833-.138 2.69-.377a2.5 2.5 0 0 0 4.8-.979c0-.563-.192-1.077-.506-1.494.057-.055.113-.111.168-.168.42.321.939.518 1.508.518a2.5 2.5 0 0 0 .963-4.807c.021-.074.038-.15.058-.225.105.013.21.032.319.032" fill="#44D860"/><path d="M143 41.75c-.16 0-.33-.02-.49-.05a2.52 2.52 0 0 1-.47-.14c-.15-.06-.29-.14-.431-.23-.13-.09-.259-.2-.38-.31-.109-.12-.219-.24-.309-.38s-.17-.28-.231-.43a2.619 2.619 0 0 1-.189-.96c0-.16.02-.33.05-.49.03-.16.08-.31.139-.47.061-.15.141-.29.231-.43.09-.13.2-.26.309-.38.121-.11.25-.22.38-.31.141-.09.281-.17.431-.23.149-.06.31-.11.47-.14.32-.07.65-.07.98 0 .159.03.32.08.47.14.149.06.29.14.43.23.13.09.259.2.38.31.11.12.22.25.31.38.09.14.17.28.23.43.06.16.11.31.14.47.029.16.05.33.05.49 0 .66-.271 1.31-.73 1.77-.121.11-.25.22-.38.31-.14.09-.281.17-.43.23a2.565 2.565 0 0 1-.96.19m20-1.25c-.66 0-1.3-.27-1.771-.73a3.802 3.802 0 0 1-.309-.38c-.09-.14-.17-.28-.231-.43a2.619 2.619 0 0 1-.189-.96c0-.66.27-1.3.729-1.77.121-.11.25-.22.38-.31.141-.09.281-.17.431-.23.149-.06.31-.11.47-.14.32-.07.66-.07.98 0 .159.03.32.08.47.14.149.06.29.14.43.23.13.09.259.2.38.31.459.47.73 1.11.73 1.77 0 .16-.021.33-.05.49-.03.16-.08.32-.14.47-.07.15-.14.29-.23.43-.09.13-.2.26-.31.38-.121.11-.25.22-.38.31-.14.09-.281.17-.43.23a2.565 2.565 0 0 1-.96.19" fill="#000"/></g></svg> -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Basic Options */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | "target": "ES2018", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ 8 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ 9 | "lib": ["es2018","es2019.array", "DOM"], /* Specify library files to be included in the compilation. */ 10 | // "allowJs": true, /* Allow javascript files to be compiled. */ 11 | // "checkJs": true, /* Report errors in .js files. */ 12 | "jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 13 | "declaration": true, /* Generates corresponding '.d.ts' file. */ 14 | "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 15 | "sourceMap": true, /* Generates corresponding '.map' file. */ 16 | // "outFile": "./", /* Concatenate and emit output to single file. */ 17 | "outDir": "./build", /* Redirect output structure to the directory. */ 18 | "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 19 | // "composite": true, /* Enable project compilation */ 20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 21 | // "removeComments": true, /* Do not emit comments to output. */ 22 | // "noEmit": true, /* Do not emit outputs. */ 23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 26 | 27 | /* Strict Type-Checking Options */ 28 | "strict": true, /* Enable all strict type-checking options. */ 29 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 30 | // "strictNullChecks": true, /* Enable strict null checks. */ 31 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 32 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 33 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 34 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 35 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 36 | 37 | /* Additional Checks */ 38 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 39 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 40 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 41 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 42 | 43 | /* Module Resolution Options */ 44 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 45 | "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 46 | "paths": { 47 | "react": [ "./examples/node_modules/react" ] 48 | }, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 49 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 50 | // "typeRoots": [], /* List of folders to include type definitions from. */ 51 | "types": ["node", "jest"], /* Type declaration files to be included in compilation. */ 52 | "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 53 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 54 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 55 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 56 | 57 | /* Source Map Options */ 58 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 59 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 60 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 61 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 62 | 63 | /* Experimental Options */ 64 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 65 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 66 | 67 | /* Advanced Options */ 68 | "skipLibCheck": true, /* Skip type checking of declaration files. */ 69 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 70 | }, 71 | "exclude": [ 72 | "tests", 73 | "build" 74 | ] 75 | } 76 | -------------------------------------------------------------------------------- /example/static/img/undraw_docusaurus_tree.svg: -------------------------------------------------------------------------------- 1 | <svg id="ac356da0-b129-4ca5-aecc-4700531dd101" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="1129" height="663" viewBox="0 0 1129 663"><title>docu_tree -------------------------------------------------------------------------------- /example/static/img/undraw_docusaurus_mountain.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /example/static/img/undraw_docusaurus_react.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | --------------------------------------------------------------------------------