From 331ff3be0e8dc95c910e409a6c02d28ce8ddf162 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Fri, 10 Jun 2022 12:18:26 +0800 Subject: [PATCH] docs: use custom diff code blocks (#5099) --- .cspell.json | 2 + docs/linting/MONOREPO.md | 34 ++--- docs/linting/README.md | 82 ++++++------ docs/linting/TROUBLESHOOTING.md | 15 ++- docs/linting/TYPED_LINTING.md | 33 ++--- packages/website/docusaurusConfig.ts | 17 +++ packages/website/package.json | 1 + packages/website/src/css/custom.css | 38 +++++- .../src/theme/CodeBlock/Content/String.tsx | 121 ++++++++++++++++++ .../theme/CodeBlock/Content/styles.module.css | 81 ++++++++++++ 10 files changed, 344 insertions(+), 80 deletions(-) create mode 100644 packages/website/src/theme/CodeBlock/Content/String.tsx create mode 100644 packages/website/src/theme/CodeBlock/Content/styles.module.css diff --git a/.cspell.json b/.cspell.json index 09466a9d300..4691cd152db 100644 --- a/.cspell.json +++ b/.cspell.json @@ -73,8 +73,10 @@ "linebreaks", "lzstring", "markdownlint", + "metastring", "necroing", "nocheck", + "noninteractive", "nullish", "OOM", "OOMs", diff --git a/docs/linting/MONOREPO.md b/docs/linting/MONOREPO.md index 2f8cbe15f34..4ec2df16136 100644 --- a/docs/linting/MONOREPO.md +++ b/docs/linting/MONOREPO.md @@ -18,22 +18,24 @@ Earlier in our docs on [typed linting](./TYPED_LINTING.md), we showed you how to For example, this is how we specify all of our `tsconfig.json` within this repo. -```diff title=".eslintrc.js" - module.exports = { - root: true, - parser: '@typescript-eslint/parser', - parserOptions: { - tsconfigRootDir: __dirname, -- project: ['./tsconfig.json'], -+ project: ['./tsconfig.eslint.json', './packages/*/tsconfig.json'], - }, - plugins: ['@typescript-eslint'], - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:@typescript-eslint/recommended-requiring-type-checking', - ], - }; +```js title=".eslintrc.js" +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + parserOptions: { + tsconfigRootDir: __dirname, + // Remove this line + project: ['./tsconfig.json'], + // Add this line + project: ['./tsconfig.eslint.json', './packages/*/tsconfig.json'], + }, + plugins: ['@typescript-eslint'], + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/recommended-requiring-type-checking', + ], +}; ``` If you're looking for an example of what the `.eslintrc.js`, and referenced `tsconfig.json` might look like in a real example, look no further than this very repo. We're a multi-package monorepo that uses one `tsconfig.json` per package, that also uses typed linting. diff --git a/docs/linting/README.md b/docs/linting/README.md index dbf08d1e780..23f0a2b1981 100644 --- a/docs/linting/README.md +++ b/docs/linting/README.md @@ -136,19 +136,18 @@ If you use [`prettier`](https://www.npmjs.com/package/prettier), there is also a Using this config by adding it to the end of your `extends`: -```diff title=".eslintrc.js" - module.exports = { - root: true, - parser: '@typescript-eslint/parser', - plugins: [ - '@typescript-eslint', - ], - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', -+ 'prettier', - ], - }; +```js title=".eslintrc.js" +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint'], + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + // Add this line + 'prettier', + ], +}; ``` ### Community Configs @@ -163,19 +162,20 @@ A few popular all-in-one configs are: To use one of these complete config packages, you would replace the `extends` with the package name. For example: -```diff title=".eslintrc.js" - module.exports = { - root: true, - parser: '@typescript-eslint/parser', - plugins: [ - '@typescript-eslint', - ], - extends: [ -- 'eslint:recommended', -- 'plugin:@typescript-eslint/recommended', -+ 'airbnb-typescript', - ], - }; +```js title=".eslintrc.js" +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint'], + extends: [ + // Removed lines start + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + // Removed lines end + // Add this line + 'airbnb-typescript', + ], +}; ``` @@ -196,20 +196,22 @@ Below are just a few examples: Every plugin that is out there includes documentation on the various configurations and rules they offer. A typical plugin might be used like: -```diff title=".eslintrc.js" - module.exports = { - root: true, - parser: '@typescript-eslint/parser', - plugins: [ - '@typescript-eslint', -+ 'jest', - ], - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', -+ 'plugin:jest/recommended', - ], - }; +```js title=".eslintrc.js" +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + plugins: [ + '@typescript-eslint', + // Add this line + 'jest', + ], + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + // Add this line + 'plugin:jest/recommended', + ], +}; ``` diff --git a/docs/linting/TROUBLESHOOTING.md b/docs/linting/TROUBLESHOOTING.md index d6cb97486f5..d9c447b4d0e 100644 --- a/docs/linting/TROUBLESHOOTING.md +++ b/docs/linting/TROUBLESHOOTING.md @@ -42,12 +42,15 @@ See our docs on [type aware linting](./TYPED_LINTING.md#i-get-errors-telling-me- You can use `parserOptions.extraFileExtensions` to specify an array of non-TypeScript extensions to allow, for example: -```diff - parserOptions: { - tsconfigRootDir: __dirname, - project: ['./tsconfig.json'], -+ extraFileExtensions: ['.vue'], - }, +```js title=".eslintrc.js" +module.exports = { + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./tsconfig.json'], + // Add this line + extraFileExtensions: ['.vue'], + }, +}; ``` ## One of my lint rules isn't working correctly on a pure JavaScript file diff --git a/docs/linting/TYPED_LINTING.md b/docs/linting/TYPED_LINTING.md index 376a0334c17..ae58028e442 100644 --- a/docs/linting/TYPED_LINTING.md +++ b/docs/linting/TYPED_LINTING.md @@ -8,21 +8,24 @@ Under the hood, the typescript-eslint parser uses TypeScript's compiler APIs to To tap into TypeScript's additional powers, there are two small changes you need to make to your config file: -```diff title=".eslintrc.js" - module.exports = { - root: true, - parser: '@typescript-eslint/parser', -+ parserOptions: { -+ tsconfigRootDir: __dirname, -+ project: ['./tsconfig.json'], -+ }, - plugins: ['@typescript-eslint'], - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', -+ 'plugin:@typescript-eslint/recommended-requiring-type-checking', - ], - }; +```js title=".eslintrc.js" +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + // Added lines start + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./tsconfig.json'], + }, + // Added lines end + plugins: ['@typescript-eslint'], + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + // Add this line + 'plugin:@typescript-eslint/recommended-requiring-type-checking', + ], +}; ``` In more detail: diff --git a/packages/website/docusaurusConfig.ts b/packages/website/docusaurusConfig.ts index eab99e5d2eb..ed8d4deaa0c 100644 --- a/packages/website/docusaurusConfig.ts +++ b/packages/website/docusaurusConfig.ts @@ -139,6 +139,23 @@ const themeConfig: ThemeCommonConfig & AlgoliaThemeConfig = { styles: [], }, additionalLanguages: ['ignore'], + magicComments: [ + { + className: 'theme-code-block-highlighted-line', + line: 'highlight-next-line', + block: { start: 'highlight-start', end: 'highlight-end' }, + }, + { + className: 'code-block-removed-line', + line: 'Remove this line', + block: { start: 'Removed lines start', end: 'Removed lines end' }, + }, + { + className: 'code-block-added-line', + line: 'Add this line', + block: { start: 'Added lines start', end: 'Added lines end' }, + }, + ], }, tableOfContents: { maxHeadingLevel: 4, diff --git a/packages/website/package.json b/packages/website/package.json index ffa80744244..a15565d0932 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -26,6 +26,7 @@ "json5": "^2.2.1", "konamimojisplosion": "^0.5.1", "lzstring.ts": "^2.0.2", + "prism-react-renderer": "^1.3.3", "react": "^18.1.0", "react-dom": "^18.1.0", "remark-docusaurus-tabs": "^0.2.0", diff --git a/packages/website/src/css/custom.css b/packages/website/src/css/custom.css index 2324486fc7e..dcda800b7ab 100644 --- a/packages/website/src/css/custom.css +++ b/packages/website/src/css/custom.css @@ -109,7 +109,39 @@ h6 { background-color: var(--code-line-decoration); } -/* indent the nested checklist for the rule doc attributes */ -ul.contains-task-list > li > ul.contains-task-list { - padding-left: 24px; +.code-block-removed-line::before { + content: '-'; + display: inline-block; + width: 0px; + position: relative; + left: -0.7em; + color: red; +} + +.code-block-removed-line { + background-color: #ff000020; + display: block; + margin: 0 calc(-1 * var(--ifm-pre-padding)); + padding: 0 var(--ifm-pre-padding); + user-select: none; +} + +.code-block-added-line::before { + content: '+'; + display: inline-block; + width: 0px; + position: relative; + left: -0.7em; + color: rgb(2, 164, 113); +} + +.code-block-added-line { + background-color: #00ff9540; + display: block; + margin: 0 calc(-1 * var(--ifm-pre-padding)); + padding: 0 var(--ifm-pre-padding); +} + +[data-theme='dark'] .code-block-added-line { + background-color: #00ff9510; } diff --git a/packages/website/src/theme/CodeBlock/Content/String.tsx b/packages/website/src/theme/CodeBlock/Content/String.tsx new file mode 100644 index 00000000000..f26a6ec5b9c --- /dev/null +++ b/packages/website/src/theme/CodeBlock/Content/String.tsx @@ -0,0 +1,121 @@ +// Change: added `copiedCode` which filters out the removed lines + +import React from 'react'; +import clsx from 'clsx'; +import { + useThemeConfig, + parseCodeBlockTitle, + parseLanguage, + parseLines, + containsLineNumbers, + usePrismTheme, + useCodeWordWrap, +} from '@docusaurus/theme-common'; +import Highlight, { defaultProps, type Language } from 'prism-react-renderer'; +import Line from '@theme/CodeBlock/Line'; +import CopyButton from '@theme/CodeBlock/CopyButton'; +import WordWrapButton from '@theme/CodeBlock/WordWrapButton'; +import Container from '@theme/CodeBlock/Container'; +import type { Props } from '@theme/CodeBlock/Content/String'; + +import styles from './styles.module.css'; + +// eslint-disable-next-line import/no-default-export +export default function CodeBlockString({ + children, + className: blockClassName = '', + metastring, + title: titleProp, + showLineNumbers: showLineNumbersProp, + language: languageProp, +}: Props): JSX.Element { + const { + prism: { defaultLanguage, magicComments }, + } = useThemeConfig(); + const language = + languageProp ?? parseLanguage(blockClassName) ?? defaultLanguage; + const prismTheme = usePrismTheme(); + const wordWrap = useCodeWordWrap(); + + // We still parse the metastring in case we want to support more syntax in the + // future. Note that MDX doesn't strip quotes when parsing metastring: + // "title=\"xyz\"" => title: "\"xyz\"" + const title = parseCodeBlockTitle(metastring) || titleProp; + + const { lineClassNames, code } = parseLines(children, { + metastring, + language, + magicComments, + }); + const showLineNumbers = + showLineNumbersProp ?? containsLineNumbers(metastring); + + const copiedCode = code + .split('\n') + .filter((c, i) => !lineClassNames[i]?.includes('code-block-removed-line')) + .join('\n'); + + return ( + + {title &&
{title}
} +
+ + {({ + className, + tokens, + getLineProps, + getTokenProps, + }): JSX.Element => ( +
+              
+                {tokens.map((line, i) => (
+                  
+                ))}
+              
+            
+ )} +
+
+ {(wordWrap.isEnabled || wordWrap.isCodeScrollable) && ( + wordWrap.toggle()} + isEnabled={wordWrap.isEnabled} + /> + )} + +
+
+
+ ); +} diff --git a/packages/website/src/theme/CodeBlock/Content/styles.module.css b/packages/website/src/theme/CodeBlock/Content/styles.module.css new file mode 100644 index 00000000000..e75d80399a6 --- /dev/null +++ b/packages/website/src/theme/CodeBlock/Content/styles.module.css @@ -0,0 +1,81 @@ +/* No change; copied over so that the .tsx file can get its CSS */ + +.codeBlockContent { + position: relative; + /* rtl:ignore */ + direction: ltr; + border-radius: inherit; +} + +.codeBlockTitle { + border-bottom: 1px solid var(--ifm-color-emphasis-300); + font-size: var(--ifm-code-font-size); + font-weight: 500; + padding: 0.75rem var(--ifm-pre-padding); + border-top-left-radius: inherit; + border-top-right-radius: inherit; +} + +.codeBlock { + --ifm-pre-background: var(--prism-background-color); + margin: 0; + padding: 0; +} + +.codeBlockTitle + .codeBlockContent .codeBlock { + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.codeBlockStandalone { + padding: 0; +} + +.codeBlockLines { + font: inherit; + /* rtl:ignore */ + float: left; + min-width: 100%; + padding: var(--ifm-pre-padding); +} + +.codeBlockLinesWithNumbering { + display: table; + padding: var(--ifm-pre-padding) 0; +} + +@media print { + .codeBlockLines { + white-space: pre-wrap; + } +} + +.buttonGroup { + display: flex; + column-gap: 0.2rem; + position: absolute; + right: calc(var(--ifm-pre-padding) / 2); + top: calc(var(--ifm-pre-padding) / 2); +} + +.buttonGroup button { + display: flex; + align-items: center; + background: var(--prism-background-color); + color: var(--prism-color); + border: 1px solid var(--ifm-color-emphasis-300); + border-radius: var(--ifm-global-radius); + padding: 0.4rem; + line-height: 0; + transition: opacity 200ms ease-in-out; + opacity: 0; +} + +.buttonGroup button:focus-visible, +.buttonGroup button:hover { + opacity: 1 !important; +} + +:global(.theme-code-block:hover) .buttonGroup button { + opacity: 0.4; +}