From 2c8ff51b98213a0406b892067ef0a2d63619a354 Mon Sep 17 00:00:00 2001 From: Viktor Malmedal Date: Sat, 1 Oct 2022 18:55:54 +0200 Subject: [PATCH 01/15] feat: add eslint plugin no-html-links --- packages/eslint-plugin/src/index.ts | 2 + .../src/rules/__tests__/no-html-links.test.ts | 45 +++++++++++++++++ packages/eslint-plugin/src/rules/index.ts | 2 + .../eslint-plugin/src/rules/no-html-links.ts | 48 +++++++++++++++++++ 4 files changed, 97 insertions(+) create mode 100644 packages/eslint-plugin/src/rules/__tests__/no-html-links.test.ts create mode 100644 packages/eslint-plugin/src/rules/no-html-links.ts diff --git a/packages/eslint-plugin/src/index.ts b/packages/eslint-plugin/src/index.ts index 1274bc31dfee..8422998de9f1 100644 --- a/packages/eslint-plugin/src/index.ts +++ b/packages/eslint-plugin/src/index.ts @@ -13,12 +13,14 @@ export = { recommended: { rules: { '@docusaurus/string-literal-i18n-messages': 'error', + '@docusaurus/no-html-links': 'warn', }, }, all: { rules: { '@docusaurus/string-literal-i18n-messages': 'error', '@docusaurus/no-untranslated-text': 'warn', + '@docusaurus/no-html-links': 'warn', }, }, }, diff --git a/packages/eslint-plugin/src/rules/__tests__/no-html-links.test.ts b/packages/eslint-plugin/src/rules/__tests__/no-html-links.test.ts new file mode 100644 index 000000000000..915793dfae3d --- /dev/null +++ b/packages/eslint-plugin/src/rules/__tests__/no-html-links.test.ts @@ -0,0 +1,45 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import rule from '../no-html-links'; +import {RuleTester} from './testUtils'; + +const errorsJSX = [{messageId: 'link'}] as const; + +const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, +}); + +ruleTester.run('prefer-docusaurus-link', rule, { + valid: [ + { + code: 'test', + }, + { + code: 'Twitter', + }, + ], + invalid: [ + { + code: 'test', + errors: errorsJSX, + }, + { + code: 'test', + errors: errorsJSX, + }, + { + code: 'test', + errors: errorsJSX, + }, + ], +}); diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index 70a83f55a79b..85409ddfb473 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -5,10 +5,12 @@ * LICENSE file in the root directory of this source tree. */ +import noHtmlLinks from './no-html-links'; import noUntranslatedText from './no-untranslated-text'; import stringLiteralI18nMessages from './string-literal-i18n-messages'; export default { 'no-untranslated-text': noUntranslatedText, 'string-literal-i18n-messages': stringLiteralI18nMessages, + 'no-html-links': noHtmlLinks, }; diff --git a/packages/eslint-plugin/src/rules/no-html-links.ts b/packages/eslint-plugin/src/rules/no-html-links.ts new file mode 100644 index 000000000000..b14c65c97754 --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-html-links.ts @@ -0,0 +1,48 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {createRule} from '../util'; +import type {TSESTree} from '@typescript-eslint/types/dist/ts-estree'; + +const docsUrl = 'https://docusaurus.io/docs/docusaurus-core#link'; + +type Options = []; + +type MessageIds = 'link'; + +export default createRule({ + name: 'no-html-links', + meta: { + type: 'problem', + docs: { + description: 'enforce using Docusaurus Link component instead of tag', + recommended: false, + }, + schema: [ + { + type: 'object', + additionalProperties: false, + }, + ], + messages: { + link: `Do not use an \`\` element to navigate. Use \`\` from \`@docusaurus/Link\` instead. See: ${docsUrl}`, + }, + }, + defaultOptions: [], + + create(context) { + return { + JSXOpeningElement(node) { + if ((node.name as TSESTree.JSXIdentifier).name !== 'a') { + return; + } + + context.report({node, messageId: 'link'}); + }, + }; + }, +}); From 0ec09b0264fdaae1a8b3b9eff5038204e2c16ea1 Mon Sep 17 00:00:00 2001 From: Viktor Malmedal Date: Sat, 1 Oct 2022 18:56:36 +0200 Subject: [PATCH 02/15] docs: add docs for no-html-links --- website/docs/api/misc/eslint-plugin/README.md | 1 + .../api/misc/eslint-plugin/no-html-links.md | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 website/docs/api/misc/eslint-plugin/no-html-links.md diff --git a/website/docs/api/misc/eslint-plugin/README.md b/website/docs/api/misc/eslint-plugin/README.md index 9d0d2236846c..a4d0a6ea06ea 100644 --- a/website/docs/api/misc/eslint-plugin/README.md +++ b/website/docs/api/misc/eslint-plugin/README.md @@ -53,6 +53,7 @@ Each config contains a set of rules. For more fine-grained control, you can also | --- | --- | --- | | [`@docusaurus/no-untranslated-text`](./no-untranslated-text.md) | Enforce text labels in JSX to be wrapped by translate calls | | | [`@docusaurus/string-literal-i18n-messages`](./string-literal-i18n-messages.md) | Enforce translate APIs to be called on plain text labels | ✅ | +| [`@docusaurus/no-html-links`](./no-html-links.md) | Ensures @docusaurus/Link is used instead of a tags | ✅ | ✅ = recommended diff --git a/website/docs/api/misc/eslint-plugin/no-html-links.md b/website/docs/api/misc/eslint-plugin/no-html-links.md new file mode 100644 index 000000000000..3075f7964be4 --- /dev/null +++ b/website/docs/api/misc/eslint-plugin/no-html-links.md @@ -0,0 +1,24 @@ +--- +slug: /api/misc/@docusaurus/eslint-plugin/no-html-links +--- + +# no-html-links + +Ensure that docusaurus `` is used instead of `` tags. + +## Rule Details {#details} + +Examples of **incorrect** code for this rule: + +```html +go to page! +``` + +Examples of **correct** code for this rule: + +```js +// +import Link from '@docusaurus/Link'; + +go to page!; +``` From 6e80680b2025d8aaf0dbfd2f0427380584c62205 Mon Sep 17 00:00:00 2001 From: Viktor Malmedal Date: Sat, 1 Oct 2022 19:09:34 +0200 Subject: [PATCH 03/15] fix: eslint no-html-links errors --- .eslintrc.js | 1 + .../src/theme/EditThisPage/index.tsx | 9 +++----- .../src/theme/Heading/index.tsx | 7 ++++--- .../src/theme/SkipToContent/index.tsx | 5 +++-- .../src/theme/TOCItems/Tree.tsx | 7 +++---- .../src/theme/SearchPage/index.tsx | 8 +++---- .../docusaurus/src/client/exports/Link.tsx | 2 +- .../_pages tests/hydration-tests.tsx | 21 +++++-------------- website/src/components/HackerNewsIcon.tsx | 9 ++++---- website/src/components/ProductHuntCard.tsx | 9 ++++---- .../src/components/TeamProfileCards/index.tsx | 8 +++---- website/src/components/Tweet/index.tsx | 5 +++-- website/src/components/TweetQuote/index.tsx | 9 ++++---- website/src/components/Versions.tsx | 5 +++-- website/src/pages/showcase/index.tsx | 9 +++----- website/src/pages/versions.tsx | 4 ++-- 16 files changed, 50 insertions(+), 68 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index c76cc234dc9f..56851d0247c3 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -374,6 +374,7 @@ module.exports = { // locals must be justified with a disable comment. '@typescript-eslint/no-unused-vars': [ERROR, {ignoreRestSiblings: true}], '@typescript-eslint/prefer-optional-chain': ERROR, + '@docusaurus/no-html-links': ERROR, '@docusaurus/no-untranslated-text': [ WARNING, { diff --git a/packages/docusaurus-theme-classic/src/theme/EditThisPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/EditThisPage/index.tsx index ede0b5cbed62..1fbfb173ab5f 100644 --- a/packages/docusaurus-theme-classic/src/theme/EditThisPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/EditThisPage/index.tsx @@ -8,22 +8,19 @@ import React from 'react'; import Translate from '@docusaurus/Translate'; import {ThemeClassNames} from '@docusaurus/theme-common'; +import Link from '@docusaurus/Link'; import IconEdit from '@theme/Icon/Edit'; import type {Props} from '@theme/EditThisPage'; export default function EditThisPage({editUrl}: Props): JSX.Element { return ( - + Edit this page - + ); } diff --git a/packages/docusaurus-theme-classic/src/theme/Heading/index.tsx b/packages/docusaurus-theme-classic/src/theme/Heading/index.tsx index 9969fdbb6e55..fb1b66fa9af4 100644 --- a/packages/docusaurus-theme-classic/src/theme/Heading/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Heading/index.tsx @@ -9,6 +9,7 @@ import React from 'react'; import clsx from 'clsx'; import {translate} from '@docusaurus/Translate'; import {useThemeConfig} from '@docusaurus/theme-common'; +import Link from '@docusaurus/Link'; import type {Props} from '@theme/Heading'; import styles from './styles.module.css'; @@ -33,16 +34,16 @@ export default function Heading({as: As, id, ...props}: Props): JSX.Element { )} id={id}> {props.children} - ​ - + ); } diff --git a/packages/docusaurus-theme-classic/src/theme/SkipToContent/index.tsx b/packages/docusaurus-theme-classic/src/theme/SkipToContent/index.tsx index 174f813a3ae8..15ef981a78b3 100644 --- a/packages/docusaurus-theme-classic/src/theme/SkipToContent/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/SkipToContent/index.tsx @@ -9,6 +9,7 @@ import React from 'react'; import Translate, {translate} from '@docusaurus/Translate'; import {useSkipToContent} from '@docusaurus/theme-common/internal'; +import Link from '@docusaurus/Link'; import styles from './styles.module.css'; export default function SkipToContent(): JSX.Element { @@ -19,13 +20,13 @@ export default function SkipToContent(): JSX.Element { role="region" aria-label={translate({id: 'theme.common.skipToMainContent'})}> {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */} - + Skip to main content - + ); } diff --git a/packages/docusaurus-theme-classic/src/theme/TOCItems/Tree.tsx b/packages/docusaurus-theme-classic/src/theme/TOCItems/Tree.tsx index 04ab3fc93bad..266feaba2c1b 100644 --- a/packages/docusaurus-theme-classic/src/theme/TOCItems/Tree.tsx +++ b/packages/docusaurus-theme-classic/src/theme/TOCItems/Tree.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import Link from '@docusaurus/Link'; import type {Props} from '@theme/TOCItems/Tree'; // Recursive component rendering the toc tree @@ -22,12 +23,10 @@ function TOCItemTree({