diff --git a/.eslintrc.js b/.eslintrc.js index 6a0b15228fd0..883afc7eacb8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -381,6 +381,7 @@ module.exports = { '@typescript-eslint/prefer-optional-chain': ERROR, '@docusaurus/no-html-links': ERROR, '@docusaurus/prefer-docusaurus-heading': ERROR, + '@docusaurus/prefer-ideal-image': OFF, '@docusaurus/no-untranslated-text': [ WARNING, { @@ -510,5 +511,11 @@ module.exports = { '@docusaurus/prefer-docusaurus-heading': OFF, }, }, + { + files: ['website/**'], + rules: { + '@docusaurus/prefer-ideal-image': ERROR, + }, + }, ], }; diff --git a/packages/eslint-plugin/src/index.ts b/packages/eslint-plugin/src/index.ts index 65c0b0ee3d2c..68c830817a10 100644 --- a/packages/eslint-plugin/src/index.ts +++ b/packages/eslint-plugin/src/index.ts @@ -25,6 +25,7 @@ export = { '@docusaurus/no-untranslated-text': 'warn', '@docusaurus/no-html-links': 'warn', '@docusaurus/prefer-docusaurus-heading': 'warn', + '@docusaurus/prefer-ideal-image': 'warn', }, }, }, diff --git a/packages/eslint-plugin/src/rules/__tests__/prefer-ideal-image.test.ts b/packages/eslint-plugin/src/rules/__tests__/prefer-ideal-image.test.ts new file mode 100644 index 000000000000..f7fdd0a706ef --- /dev/null +++ b/packages/eslint-plugin/src/rules/__tests__/prefer-ideal-image.test.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 rule from '../prefer-ideal-image'; +import {RuleTester} from './testUtils'; + +const errorsJSX = [{messageId: 'image'}] as const; + +const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, +}); + +ruleTester.run('prefer-ideal-image', rule, { + valid: [ + { + code: "", + }, + { + code: "", + }, + { + code: "", + }, + ], + invalid: [ + { + code: "some alt text", + errors: errorsJSX, + }, + { + code: "some alt text", + errors: errorsJSX, + }, + { + code: "some alt text", + errors: errorsJSX, + }, + ], +}); diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index caa726907faa..fdae7c245aac 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -7,6 +7,7 @@ import noHtmlLinks from './no-html-links'; import preferDocusaurusHeading from './prefer-docusaurus-heading'; +import preferIdealImage from './prefer-ideal-image'; import noUntranslatedText from './no-untranslated-text'; import stringLiteralI18nMessages from './string-literal-i18n-messages'; @@ -15,4 +16,5 @@ export default { 'string-literal-i18n-messages': stringLiteralI18nMessages, 'no-html-links': noHtmlLinks, 'prefer-docusaurus-heading': preferDocusaurusHeading, + 'prefer-ideal-image': preferIdealImage, }; diff --git a/packages/eslint-plugin/src/rules/prefer-ideal-image.ts b/packages/eslint-plugin/src/rules/prefer-ideal-image.ts new file mode 100644 index 000000000000..6b54910d62d0 --- /dev/null +++ b/packages/eslint-plugin/src/rules/prefer-ideal-image.ts @@ -0,0 +1,46 @@ +/** + * 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'; + +type Options = []; +type MessageIds = 'image'; + +const docsUrl = + 'https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-ideal-image'; + +export default createRule({ + name: 'prefer-ideal-image', + meta: { + type: 'suggestion', + docs: { + description: + 'enforce using Docusaurus IdealImage plugin component instead of tags', + recommended: false, + }, + schema: [], + messages: { + image: `Do not use an \`\` element to embed images. Use the \`\` component from \`@theme/IdealImage\` instead. See ${docsUrl}`, + }, + }, + defaultOptions: [], + + create(context) { + return { + JSXOpeningElement(node) { + const elementName = (node.name as TSESTree.JSXIdentifier).name; + + if (elementName !== 'img') { + return; + } + + context.report({node, messageId: 'image'}); + }, + }; + }, +}); diff --git a/website/docs/api/misc/eslint-plugin/README.mdx b/website/docs/api/misc/eslint-plugin/README.mdx index a0d41ee4d458..bdf355ec555c 100644 --- a/website/docs/api/misc/eslint-plugin/README.mdx +++ b/website/docs/api/misc/eslint-plugin/README.mdx @@ -54,6 +54,7 @@ For more fine-grained control, you can also enable the plugin manually and confi | [`@docusaurus/string-literal-i18n-messages`](./string-literal-i18n-messages.mdx) | Enforce translate APIs to be called on plain text labels | ✅ | | [`@docusaurus/no-html-links`](./no-html-links.mdx) | Ensures @docusaurus/Link is used instead of `` tags | ✅ | | [`@docusaurus/prefer-docusaurus-heading`](./prefer-docusaurus-heading.mdx) | Ensures @theme/Heading is used instead of `` tags for headings | ✅ | +| [`@docusaurus/prefer-ideal-image`](./prefer-ideal-image.mdx) | Ensures @theme/IdealImage is used instead of `` tags for embedding images | | ✅ = recommended diff --git a/website/docs/api/misc/eslint-plugin/prefer-ideal-image.mdx b/website/docs/api/misc/eslint-plugin/prefer-ideal-image.mdx new file mode 100644 index 000000000000..854cc6b5ecb2 --- /dev/null +++ b/website/docs/api/misc/eslint-plugin/prefer-ideal-image.mdx @@ -0,0 +1,46 @@ +--- +slug: /api/misc/@docusaurus/eslint-plugin/prefer-ideal-image +--- + +# prefer-ideal-image + +Ensure that the `` component provided by [`@theme/IdealImage`](../../plugins/plugin-ideal-image.mdx) Docusaurus plugin is used instead of `` tags. + +The `@theme/IdealImage` Docusaurus plugin generates an almost ideal image (responsive, lazy-loading, and low quality placeholder). + +## Rule Details {#details} + +Examples of **incorrect** code for this rule: + +```html +some alt text + +some alt text + +some alt text +``` + +Examples of **correct** code for this rule: + +```javascript +import Image from '@theme/IdealImage'; + + + + + + +``` diff --git a/website/src/components/ProductHuntCard.tsx b/website/src/components/ProductHuntCard.tsx index c7ecbcc9735b..f08a91ed0030 100644 --- a/website/src/components/ProductHuntCard.tsx +++ b/website/src/components/ProductHuntCard.tsx @@ -8,6 +8,7 @@ import type {ComponentProps} from 'react'; import React from 'react'; import Link from '@docusaurus/Link'; +import Image from '@theme/IdealImage'; export default function ProductHuntCard({ className, @@ -21,8 +22,8 @@ export default function ProductHuntCard({ to="https://www.producthunt.com/posts/docusaurus-2-0?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-docusaurus-2-0" className={className} style={{display: 'block', width: 250, height: 54, ...style}}> - Docusaurus 2.0 - Build optimized websites quickly, focus on your content. | Product Hunt
- {`${name}'s
diff --git a/website/src/components/Tweet/index.tsx b/website/src/components/Tweet/index.tsx index 57193b48b044..910402030035 100644 --- a/website/src/components/Tweet/index.tsx +++ b/website/src/components/Tweet/index.tsx @@ -10,6 +10,7 @@ import React, {type ReactNode} from 'react'; import clsx from 'clsx'; import Link from '@docusaurus/Link'; +import Image from '@theme/IdealImage'; import styles from './styles.module.css'; export interface Props { @@ -33,10 +34,10 @@ export default function Tweet({
- {name}
- {name}
diff --git a/website/src/pages/index.tsx b/website/src/pages/index.tsx index ecd3772d379f..e3c3774470ee 100644 --- a/website/src/pages/index.tsx +++ b/website/src/pages/index.tsx @@ -31,10 +31,10 @@ function HeroBanner() {
- {translate({message: @@ -190,12 +190,12 @@ function Feature({ return (
- {feature.title} diff --git a/website/src/plugins/changelog/theme/ChangelogItem/Header/Author/index.tsx b/website/src/plugins/changelog/theme/ChangelogItem/Header/Author/index.tsx index 185505530d24..5e4e00803859 100644 --- a/website/src/plugins/changelog/theme/ChangelogItem/Header/Author/index.tsx +++ b/website/src/plugins/changelog/theme/ChangelogItem/Header/Author/index.tsx @@ -9,6 +9,7 @@ import React from 'react'; import clsx from 'clsx'; import Link from '@docusaurus/Link'; import type {Props} from '@theme/BlogPostItem/Header/Author'; +import Image from '@theme/IdealImage'; import styles from './styles.module.css'; @@ -21,16 +22,25 @@ export default function ChangelogAuthor({
{imageURL && ( - {name} { - // Image returns 404 if the user's handle changes. We display a - // fallback instead. - e.currentTarget.src = - 'data:image/svg+xml,'; + img={{ + src: { + src: imageURL, + preSrc: + 'data:image/svg+xml,', + images: [], + }, + preSrc: + 'data:image/svg+xml,', }} + alt={name} + // onError={(e) => { + // // Image returns 404 if the user's handle changes. We display a + // // fallback instead. + // e.currentTarget.src = + // 'data:image/svg+xml,'; + // }} /> )}