From 52f00e463487ef99d0923e0ddf0ffc9cbb915631 Mon Sep 17 00:00:00 2001 From: Elias Papavasileiou Date: Tue, 19 Apr 2022 01:41:53 +0300 Subject: [PATCH 01/19] feat: add eslint plugin --- packages/eslint-plugin/.eslintrc.js | 13 +++ packages/eslint-plugin/README.md | 56 ++++++++++++ .../rules/no-dynamic-i18n-messages.test.js | 85 +++++++++++++++++++ .../lib/rules/no-untranslated-text.test.js | 72 ++++++++++++++++ .../docs/rules/no-dynamic-i18n-messages.md | 42 +++++++++ .../docs/rules/no-untranslated-text.md | 32 +++++++ packages/eslint-plugin/lib/index.js | 34 ++++++++ .../lib/rules/no-dynamic-i18n-messages.js | 69 +++++++++++++++ .../lib/rules/no-untranslated-text.js | 65 ++++++++++++++ packages/eslint-plugin/package.json | 33 +++++++ packages/eslint-plugin/util.js | 67 +++++++++++++++ project-words.txt | 1 + yarn.lock | 53 +++++++++++- 13 files changed, 618 insertions(+), 4 deletions(-) create mode 100644 packages/eslint-plugin/.eslintrc.js create mode 100644 packages/eslint-plugin/README.md create mode 100644 packages/eslint-plugin/__tests__/lib/rules/no-dynamic-i18n-messages.test.js create mode 100644 packages/eslint-plugin/__tests__/lib/rules/no-untranslated-text.test.js create mode 100644 packages/eslint-plugin/docs/rules/no-dynamic-i18n-messages.md create mode 100644 packages/eslint-plugin/docs/rules/no-untranslated-text.md create mode 100644 packages/eslint-plugin/lib/index.js create mode 100644 packages/eslint-plugin/lib/rules/no-dynamic-i18n-messages.js create mode 100644 packages/eslint-plugin/lib/rules/no-untranslated-text.js create mode 100644 packages/eslint-plugin/package.json create mode 100644 packages/eslint-plugin/util.js diff --git a/packages/eslint-plugin/.eslintrc.js b/packages/eslint-plugin/.eslintrc.js new file mode 100644 index 000000000000..81b5a7c02150 --- /dev/null +++ b/packages/eslint-plugin/.eslintrc.js @@ -0,0 +1,13 @@ +/** + * 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. + */ +module.exports = { + extends: [ + '../../.eslintrc.js', + 'plugin:eslint-plugin/recommended', + 'plugin:node/recommended', + ], +}; diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md new file mode 100644 index 000000000000..1f9533a15c3d --- /dev/null +++ b/packages/eslint-plugin/README.md @@ -0,0 +1,56 @@ +# `@docusaurus/eslint-plugin` + +Docusaurus specific linting rules for eslint + +## Installation + +You'll first need to install [ESLint](https://eslint.org/): + +```sh +npm i -D eslint +``` + +Next, install `@docusaurus/eslint-plugin`: + +```sh +npm i -D @docusaurus/eslint-plugin +``` + +## Usage + +Add `@docusaurus` to the plugins section of your `.eslintrc` configuration file: + +```json +{ + "plugins": ["@docusaurus"] +} +``` + +Then, you can extend one of the configs (e.g. the `recommended` config): + +```json +{ + "extends": ["plugin:@docusaurus/recommended"] +} +``` + +For more fine-grained control, you can also configure the rules you want to use: + +```json +{ + "rules": { + "@docusaurus/no-dynamic-i18n-messages": "error", + "@docusaurus/no-untranslated-text": "warn" + } +} +``` + +## Supported Configs + +- recommended +- all + +## Supported Rules + +- no-dynamic-i18n-messages +- no-untranslated-text diff --git a/packages/eslint-plugin/__tests__/lib/rules/no-dynamic-i18n-messages.test.js b/packages/eslint-plugin/__tests__/lib/rules/no-dynamic-i18n-messages.test.js new file mode 100644 index 000000000000..3db989e97e4e --- /dev/null +++ b/packages/eslint-plugin/__tests__/lib/rules/no-dynamic-i18n-messages.test.js @@ -0,0 +1,85 @@ +/** + * 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. + */ + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require('../../../lib/rules/no-dynamic-i18n-messages'); +const {RuleTester} = require('eslint'); +const {getCommonValidTests} = require('../../../util'); + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const errorsJSX = [{messageId: 'translateChildren', type: 'JSXElement'}]; +const errorsFunc = [{messageId: 'translateArg', type: 'Identifier'}]; + +const ruleTester = new RuleTester({ + parserOptions: { + ecmaVersion: 2022, + ecmaFeatures: {jsx: true}, + }, +}); +ruleTester.run('no-dynamic-i18n-messages', rule, { + valid: [ + ...getCommonValidTests(), + { + code: ` + Welcome to my website + `, + }, + { + code: ` + {'Welcome, {firstName}! How are you?'} + `, + }, + { + code: "translate({message: 'My page meta title'})", + }, + { + code: "translate({message: 'The logo of site {siteName}'}, {siteName: 'Docusaurus'})", + }, + { + code: 'translate({otherProp: metaTitle})', + }, + { + code: 'translate({otherProp: `My page meta title`})', + }, + ], + + invalid: [ + { + code: '{text}', + errors: errorsJSX, + }, + { + code: ' {text} ', + errors: errorsJSX, + }, + { + code: '`{text}`', + errors: errorsJSX, + }, + { + code: '{`${text}`}', + errors: errorsJSX, + }, + { + code: 'translate({message: metaTitle})', + errors: errorsFunc, + }, + { + code: 'translate({message: `My page meta title`})', + errors: errorsFunc, + }, + ], +}); diff --git a/packages/eslint-plugin/__tests__/lib/rules/no-untranslated-text.test.js b/packages/eslint-plugin/__tests__/lib/rules/no-untranslated-text.test.js new file mode 100644 index 000000000000..2e28bf59cb63 --- /dev/null +++ b/packages/eslint-plugin/__tests__/lib/rules/no-untranslated-text.test.js @@ -0,0 +1,72 @@ +/** + * 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. + */ + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require('../../../lib/rules/no-untranslated-text'); +const {RuleTester} = require('eslint'); +const {getCommonValidTests} = require('../../../util'); + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const errorsJSX = [{messageId: 'translateChildren', type: 'JSXElement'}]; +const errorsJSXFragment = [ + {messageId: 'translateChildren', type: 'JSXFragment'}, +]; + +const ruleTester = new RuleTester({ + parserOptions: { + ecmaVersion: 2022, + ecmaFeatures: {jsx: true}, + }, +}); +ruleTester.run('no-untranslated-text', rule, { + valid: [...getCommonValidTests()], + + invalid: [ + { + code: 'text', + errors: errorsJSX, + }, + { + code: ' text ', + errors: errorsJSX, + }, + { + code: '"text"', + errors: errorsJSX, + }, + { + code: "'text'", + errors: errorsJSX, + }, + { + code: '`text`', + errors: errorsJSX, + }, + { + code: '{"text"}', + errors: errorsJSX, + }, + { + code: "{'text'}", + errors: errorsJSX, + }, + { + code: '{`text`}', + errors: errorsJSX, + }, + { + code: '<>text', + errors: errorsJSXFragment, + }, + ], +}); diff --git a/packages/eslint-plugin/docs/rules/no-dynamic-i18n-messages.md b/packages/eslint-plugin/docs/rules/no-dynamic-i18n-messages.md new file mode 100644 index 000000000000..cf4dcb733a74 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/no-dynamic-i18n-messages.md @@ -0,0 +1,42 @@ +# enforce translate calls to be plain text labels (no-dynamic-i18n-messages) + +Ensures that `` children and the message attribute of `translate` function calls are hardcoded strings. + +This is to ensure that static extraction of the text will work so it can be translatable. In-string dynamic placeholders are also possible using the values object. + +## Rule Details + +Examples of **incorrect** code for this rule: + +```js +const text = 'Some text to be translated' + +// Invalid child +{text} + +// Invalid message attribute +translate({message: text}) +``` + +Examples of **correct** code for this rule: + +```js +// Valid child +Some text to be translated + +// Valid message attribute +translate({message: 'Some text to be translated'}) + +// Valid child using values object as prop + + {'Welcome, {firstName}! How are you?'} + + +// Valid message attribute using values object as second argument +translate({message: 'The logo of site {siteName}'}, {siteName: 'Docusaurus'}) +``` + +## Further Reading + +- https://docusaurus.io/docs/docusaurus-core#translate +- https://docusaurus.io/docs/docusaurus-core#translate-1 diff --git a/packages/eslint-plugin/docs/rules/no-untranslated-text.md b/packages/eslint-plugin/docs/rules/no-untranslated-text.md new file mode 100644 index 000000000000..96e8873fd84a --- /dev/null +++ b/packages/eslint-plugin/docs/rules/no-untranslated-text.md @@ -0,0 +1,32 @@ +# enforce text labels in JSX to be wrapped by translate calls (no-untranslated-text) + +Ensures that all text labels in JSX are wrapped by `` components. + +When the [i18n feature](https://docusaurus.io/docs/i18n/introduction) is used, this rule is to ensure that all strings appearing on the website are being translated, so no string accidentally slips through untranslated. + +## Rule Details + +Examples of **incorrect** code for this rule: + +```js +// Hello World is not translated +Hello World +``` + +Examples of **correct** code for this rule: + +```js +// Hello World is translated + + Hello World + +``` + +## When Not To Use It + +If you're not using the [i18n feature](https://docusaurus.io/docs/i18n/introduction) then you can disable this rule. + +## Further Reading + +- https://docusaurus.io/docs/docusaurus-core#translate +- https://docusaurus.io/docs/docusaurus-core#translate-1 diff --git a/packages/eslint-plugin/lib/index.js b/packages/eslint-plugin/lib/index.js new file mode 100644 index 000000000000..c30faa60eed3 --- /dev/null +++ b/packages/eslint-plugin/lib/index.js @@ -0,0 +1,34 @@ +/** + * 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. + */ + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const requireIndex = require('requireindex'); + +//------------------------------------------------------------------------------ +// Plugin Definition +//------------------------------------------------------------------------------ + +module.exports = { + // import all rules in lib/rules + rules: requireIndex(`${__dirname}/rules`), + configs: { + recommended: { + rules: { + 'docusaurus/no-dynamic-i18n-messages': 'error', + }, + }, + all: { + rules: { + 'docusaurus/no-dynamic-i18n-messages': 'error', + 'docusaurus/no-untranslated-text': 'warn', + }, + }, + }, +}; diff --git a/packages/eslint-plugin/lib/rules/no-dynamic-i18n-messages.js b/packages/eslint-plugin/lib/rules/no-dynamic-i18n-messages.js new file mode 100644 index 000000000000..5f7d6e6e2992 --- /dev/null +++ b/packages/eslint-plugin/lib/rules/no-dynamic-i18n-messages.js @@ -0,0 +1,69 @@ +/** + * 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. + */ + +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +const {isTextLabelChild, report} = require('../../util'); + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +/** + * @type {import('eslint').Rule.RuleModule} + */ +module.exports = { + meta: { + type: 'problem', + docs: { + description: 'enforce translate calls to be plain text labels', + category: 'Possible Problems', + url: 'https://github.com/facebook/docusaurus/tree/main/packages/eslint-plugin/docs/rules/no-dynamic-i18n-messages.md', + }, + schema: [], + messages: { + translateChildren: + ' children must be hardcoded strings. You can have in-string dynamic placeholders using the values prop.', + translateArg: + 'translation message must be a hardcoded string. You can have in-string dynamic placeholders using the values argument.', + }, + }, + + create(context) { + const isMessageTypeValid = (type) => type === 'Literal'; + + const isNodeValid = (node) => + node.children.every((child) => isTextLabelChild({child})); + + return { + "JSXElement[openingElement.name.name='Translate']": (node) => { + if (!isNodeValid(node)) { + report(context, node, 'translateChildren'); + } + }, + "CallExpression > Identifier[name='translate']": (node) => { + const messageProperty = node.parent.arguments[0].properties.find( + (property) => property.key.name === 'message', + ); + if (!messageProperty) { + return; + } + + const messageType = messageProperty.value.type; + if (!messageType) { + return; + } + + if (!isMessageTypeValid(messageType)) { + report(context, node, 'translateArg'); + } + }, + }; + }, +}; diff --git a/packages/eslint-plugin/lib/rules/no-untranslated-text.js b/packages/eslint-plugin/lib/rules/no-untranslated-text.js new file mode 100644 index 000000000000..086ce0fe366a --- /dev/null +++ b/packages/eslint-plugin/lib/rules/no-untranslated-text.js @@ -0,0 +1,65 @@ +/** + * 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. + */ + +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +const {isTextLabelChild, report} = require('../../util'); + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +/** + * @type {import('eslint').Rule.RuleModule} + */ +module.exports = { + meta: { + type: 'suggestion', + docs: { + description: + 'enforce text labels in JSX to be wrapped by translate calls', + category: 'Suggestions', + url: 'https://github.com/facebook/docusaurus/tree/main/packages/eslint-plugin/docs/rules/no-untranslated-text.md', + }, + schema: [], + messages: { + translateChildren: + 'All text labels in JSX should be wrapped by translate calls', + }, + }, + + create(context) { + const isParentTranslate = (child, isParentFragment) => + !isParentFragment && + child.parent.openingElement.name.name === 'Translate'; + + const isChildValid = (child, isParentFragment) => { + if (!isTextLabelChild({child, includeWhitespace: false})) { + return true; + } + return isParentTranslate(child, isParentFragment); + }; + + const isNodeValid = (node, isFragment) => + node.children.every((child) => isChildValid(child, isFragment)); + + return { + 'JSXElement[openingElement.selfClosing=false]': (node) => { + if (!isNodeValid(node)) { + report(context, node, 'translateChildren'); + } + }, + 'JSXFragment[openingFragment]': (node) => { + if (!isNodeValid(node, true)) { + report(context, node, 'translateChildren'); + } + }, + }; + }, +}; diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json new file mode 100644 index 000000000000..f67b3a4d8671 --- /dev/null +++ b/packages/eslint-plugin/package.json @@ -0,0 +1,33 @@ +{ + "name": "eslint-plugin-docusaurus", + "version": "2.0.0-beta.18", + "description": "Docusaurus specific linting rules for eslint", + "main": "lib/index.js", + "keywords": [ + "eslint", + "eslintplugin", + "eslint-plugin" + ], + "repository": { + "type": "git", + "url": "https://github.com/facebook/docusaurus.git", + "directory": "packages/eslint-plugin" + }, + "publishConfig": { + "access": "public" + }, + "license": "MIT", + "dependencies": { + "requireindex": "^1.2.0" + }, + "devDependencies": { + "eslint-plugin-eslint-plugin": "^4.1.0", + "eslint-plugin-node": "^11.1.0" + }, + "peerDependencies": { + "eslint": ">=6" + }, + "engines": { + "node": "12.x || 14.x || >= 16" + } +} diff --git a/packages/eslint-plugin/util.js b/packages/eslint-plugin/util.js new file mode 100644 index 000000000000..56dfdc8dc663 --- /dev/null +++ b/packages/eslint-plugin/util.js @@ -0,0 +1,67 @@ +/** + * 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. + */ +const isFalsyOrWhitespace = (value) => !value || !value.trim(); + +const isTextLabelChild = ({child, includeWhitespace = true} = {}) => { + switch (child.type) { + case 'JSXText': + return includeWhitespace || !isFalsyOrWhitespace(child.value); + case 'JSXExpressionContainer': + switch (child.expression.type) { + case 'Literal': + return true; + case 'TemplateLiteral': + return child.expression.expressions.length === 0; + default: + return false; + } + default: + return false; + } +}; + +const report = (context, node, messageId) => { + context.report({ + node, + messageId, + }); +}; + +const getCommonValidTests = () => [ + { + code: 'text', + }, + { + code: ' text ', + }, + { + code: '"text"', + }, + { + code: "'text'", + }, + { + code: '`text`', + }, + { + code: '{"text"}', + }, + { + code: "{'text'}", + }, + { + code: '{`text`}', + }, + { + code: '{text}', + }, + { + code: ' {text} ', + }, +]; + +module.exports = {isTextLabelChild, report, getCommonValidTests}; diff --git a/project-words.txt b/project-words.txt index 5bd78cccc06e..e403ec1046fa 100644 --- a/project-words.txt +++ b/project-words.txt @@ -244,6 +244,7 @@ refactorings regexes rehype reponame +requireindex retrocompatibility retrocompatible roadmap diff --git a/yarn.lock b/yarn.lock index cc8f2b73b8fd..5fb42a0b714f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8217,6 +8217,22 @@ eslint-module-utils@^2.7.3: debug "^3.2.7" find-up "^2.1.0" +eslint-plugin-es@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz#75a7cdfdccddc0589934aeeb384175f221c57893" + integrity sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ== + dependencies: + eslint-utils "^2.0.0" + regexpp "^3.0.0" + +eslint-plugin-eslint-plugin@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-eslint-plugin/-/eslint-plugin-eslint-plugin-4.1.0.tgz#40ae944d79e845dc9d4a85328eea3c5bf4ae0f7d" + integrity sha512-QJVw+WYXJuG2469gx5G929bz7crfxySDlK1i569FkuT6dpeHDeP7MmDrKaswCx17snG25LRFD6wmVX+AO5x7Qg== + dependencies: + eslint-utils "^3.0.0" + estraverse "^5.2.0" + eslint-plugin-header@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/eslint-plugin-header/-/eslint-plugin-header-3.1.1.tgz#6ce512432d57675265fac47292b50d1eff11acd6" @@ -8266,6 +8282,18 @@ eslint-plugin-jsx-a11y@^6.5.1: language-tags "^1.0.5" minimatch "^3.0.4" +eslint-plugin-node@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz#c95544416ee4ada26740a30474eefc5402dc671d" + integrity sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g== + dependencies: + eslint-plugin-es "^3.0.0" + eslint-utils "^2.0.0" + ignore "^5.1.1" + minimatch "^3.0.4" + resolve "^1.10.1" + semver "^6.1.0" + eslint-plugin-react-hooks@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.4.0.tgz#71c39e528764c848d8253e1aa2c7024ed505f6c4" @@ -8321,6 +8349,13 @@ eslint-scope@^7.1.1: esrecurse "^4.3.0" estraverse "^5.2.0" +eslint-utils@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== + dependencies: + eslint-visitor-keys "^1.1.0" + eslint-utils@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" @@ -8328,6 +8363,11 @@ eslint-utils@^3.0.0: dependencies: eslint-visitor-keys "^2.0.0" +eslint-visitor-keys@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + eslint-visitor-keys@^2.0.0, eslint-visitor-keys@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" @@ -10368,7 +10408,7 @@ ignore@^4.0.3: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.1.8, ignore@^5.1.9, ignore@^5.2.0: +ignore@^5.1.1, ignore@^5.1.8, ignore@^5.1.9, ignore@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== @@ -15978,7 +16018,7 @@ regexp.prototype.flags@^1.4.1: define-properties "^1.1.3" functions-have-names "^1.2.2" -regexpp@^3.2.0: +regexpp@^3.0.0, regexpp@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== @@ -16263,6 +16303,11 @@ require-package-name@^2.0.1: resolved "https://registry.yarnpkg.com/require-package-name/-/require-package-name-2.0.1.tgz#c11e97276b65b8e2923f75dabf5fb2ef0c3841b9" integrity sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q== +requireindex@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.2.0.tgz#3463cdb22ee151902635aa6c9535d4de9c2ef1ef" + integrity sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww== + requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" @@ -16312,7 +16357,7 @@ resolve.exports@^1.1.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.3.2: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.3.2: version "1.22.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== @@ -16648,7 +16693,7 @@ semver@7.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== -semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== From e6ac161f99382e9a32628bfc8052e2e84eb66f69 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Tue, 19 Apr 2022 10:23:33 +0800 Subject: [PATCH 02/19] refactor --- packages/eslint-plugin/.eslintrc.js | 7 +--- packages/eslint-plugin/lib/index.js | 9 ----- .../no-dynamic-i18n-messages.test.js | 12 +----- .../__tests__}/no-untranslated-text.test.js | 12 +----- .../lib/rules/no-dynamic-i18n-messages.js | 10 +---- .../lib/rules/no-untranslated-text.js | 10 +---- packages/eslint-plugin/{ => lib}/util.js | 1 + packages/eslint-plugin/package.json | 3 +- yarn.lock | 40 ++----------------- 9 files changed, 14 insertions(+), 90 deletions(-) rename packages/eslint-plugin/{__tests__/lib/rules => lib/rules/__tests__}/no-dynamic-i18n-messages.test.js (79%) rename packages/eslint-plugin/{__tests__/lib/rules => lib/rules/__tests__}/no-untranslated-text.test.js (74%) rename packages/eslint-plugin/{ => lib}/util.js (99%) diff --git a/packages/eslint-plugin/.eslintrc.js b/packages/eslint-plugin/.eslintrc.js index 81b5a7c02150..81bfbca20171 100644 --- a/packages/eslint-plugin/.eslintrc.js +++ b/packages/eslint-plugin/.eslintrc.js @@ -4,10 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ + module.exports = { - extends: [ - '../../.eslintrc.js', - 'plugin:eslint-plugin/recommended', - 'plugin:node/recommended', - ], + extends: ['../../.eslintrc.js', 'plugin:eslint-plugin/recommended'], }; diff --git a/packages/eslint-plugin/lib/index.js b/packages/eslint-plugin/lib/index.js index c30faa60eed3..7e7a2b814af0 100644 --- a/packages/eslint-plugin/lib/index.js +++ b/packages/eslint-plugin/lib/index.js @@ -5,18 +5,9 @@ * LICENSE file in the root directory of this source tree. */ -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - const requireIndex = require('requireindex'); -//------------------------------------------------------------------------------ -// Plugin Definition -//------------------------------------------------------------------------------ - module.exports = { - // import all rules in lib/rules rules: requireIndex(`${__dirname}/rules`), configs: { recommended: { diff --git a/packages/eslint-plugin/__tests__/lib/rules/no-dynamic-i18n-messages.test.js b/packages/eslint-plugin/lib/rules/__tests__/no-dynamic-i18n-messages.test.js similarity index 79% rename from packages/eslint-plugin/__tests__/lib/rules/no-dynamic-i18n-messages.test.js rename to packages/eslint-plugin/lib/rules/__tests__/no-dynamic-i18n-messages.test.js index 3db989e97e4e..3a69ea59b990 100644 --- a/packages/eslint-plugin/__tests__/lib/rules/no-dynamic-i18n-messages.test.js +++ b/packages/eslint-plugin/lib/rules/__tests__/no-dynamic-i18n-messages.test.js @@ -5,17 +5,9 @@ * LICENSE file in the root directory of this source tree. */ -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/no-dynamic-i18n-messages'); +const rule = require('../no-dynamic-i18n-messages'); const {RuleTester} = require('eslint'); -const {getCommonValidTests} = require('../../../util'); - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +const {getCommonValidTests} = require('../../util'); const errorsJSX = [{messageId: 'translateChildren', type: 'JSXElement'}]; const errorsFunc = [{messageId: 'translateArg', type: 'Identifier'}]; diff --git a/packages/eslint-plugin/__tests__/lib/rules/no-untranslated-text.test.js b/packages/eslint-plugin/lib/rules/__tests__/no-untranslated-text.test.js similarity index 74% rename from packages/eslint-plugin/__tests__/lib/rules/no-untranslated-text.test.js rename to packages/eslint-plugin/lib/rules/__tests__/no-untranslated-text.test.js index 2e28bf59cb63..abb4103d69f8 100644 --- a/packages/eslint-plugin/__tests__/lib/rules/no-untranslated-text.test.js +++ b/packages/eslint-plugin/lib/rules/__tests__/no-untranslated-text.test.js @@ -5,17 +5,9 @@ * LICENSE file in the root directory of this source tree. */ -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/no-untranslated-text'); +const rule = require('../no-untranslated-text'); const {RuleTester} = require('eslint'); -const {getCommonValidTests} = require('../../../util'); - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +const {getCommonValidTests} = require('../../util'); const errorsJSX = [{messageId: 'translateChildren', type: 'JSXElement'}]; const errorsJSXFragment = [ diff --git a/packages/eslint-plugin/lib/rules/no-dynamic-i18n-messages.js b/packages/eslint-plugin/lib/rules/no-dynamic-i18n-messages.js index 5f7d6e6e2992..2b9f5626f22b 100644 --- a/packages/eslint-plugin/lib/rules/no-dynamic-i18n-messages.js +++ b/packages/eslint-plugin/lib/rules/no-dynamic-i18n-messages.js @@ -5,15 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -//------------------------------------------------------------------------------ -// Helpers -//------------------------------------------------------------------------------ - -const {isTextLabelChild, report} = require('../../util'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ +const {isTextLabelChild, report} = require('../util'); /** * @type {import('eslint').Rule.RuleModule} diff --git a/packages/eslint-plugin/lib/rules/no-untranslated-text.js b/packages/eslint-plugin/lib/rules/no-untranslated-text.js index 086ce0fe366a..88a310d709b8 100644 --- a/packages/eslint-plugin/lib/rules/no-untranslated-text.js +++ b/packages/eslint-plugin/lib/rules/no-untranslated-text.js @@ -5,15 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -//------------------------------------------------------------------------------ -// Helpers -//------------------------------------------------------------------------------ - -const {isTextLabelChild, report} = require('../../util'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ +const {isTextLabelChild, report} = require('../util'); /** * @type {import('eslint').Rule.RuleModule} diff --git a/packages/eslint-plugin/util.js b/packages/eslint-plugin/lib/util.js similarity index 99% rename from packages/eslint-plugin/util.js rename to packages/eslint-plugin/lib/util.js index 56dfdc8dc663..f07392127c17 100644 --- a/packages/eslint-plugin/util.js +++ b/packages/eslint-plugin/lib/util.js @@ -4,6 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ + const isFalsyOrWhitespace = (value) => !value || !value.trim(); const isTextLabelChild = ({child, includeWhitespace = true} = {}) => { diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index f67b3a4d8671..8280e5b04663 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -21,8 +21,7 @@ "requireindex": "^1.2.0" }, "devDependencies": { - "eslint-plugin-eslint-plugin": "^4.1.0", - "eslint-plugin-node": "^11.1.0" + "eslint-plugin-eslint-plugin": "^4.1.0" }, "peerDependencies": { "eslint": ">=6" diff --git a/yarn.lock b/yarn.lock index 5fb42a0b714f..80bf7f7a4030 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8217,14 +8217,6 @@ eslint-module-utils@^2.7.3: debug "^3.2.7" find-up "^2.1.0" -eslint-plugin-es@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz#75a7cdfdccddc0589934aeeb384175f221c57893" - integrity sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ== - dependencies: - eslint-utils "^2.0.0" - regexpp "^3.0.0" - eslint-plugin-eslint-plugin@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/eslint-plugin-eslint-plugin/-/eslint-plugin-eslint-plugin-4.1.0.tgz#40ae944d79e845dc9d4a85328eea3c5bf4ae0f7d" @@ -8282,18 +8274,6 @@ eslint-plugin-jsx-a11y@^6.5.1: language-tags "^1.0.5" minimatch "^3.0.4" -eslint-plugin-node@^11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz#c95544416ee4ada26740a30474eefc5402dc671d" - integrity sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g== - dependencies: - eslint-plugin-es "^3.0.0" - eslint-utils "^2.0.0" - ignore "^5.1.1" - minimatch "^3.0.4" - resolve "^1.10.1" - semver "^6.1.0" - eslint-plugin-react-hooks@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.4.0.tgz#71c39e528764c848d8253e1aa2c7024ed505f6c4" @@ -8349,13 +8329,6 @@ eslint-scope@^7.1.1: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-utils@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" - integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== - dependencies: - eslint-visitor-keys "^1.1.0" - eslint-utils@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" @@ -8363,11 +8336,6 @@ eslint-utils@^3.0.0: dependencies: eslint-visitor-keys "^2.0.0" -eslint-visitor-keys@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" - integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== - eslint-visitor-keys@^2.0.0, eslint-visitor-keys@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" @@ -10408,7 +10376,7 @@ ignore@^4.0.3: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.1.1, ignore@^5.1.8, ignore@^5.1.9, ignore@^5.2.0: +ignore@^5.1.8, ignore@^5.1.9, ignore@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== @@ -16018,7 +15986,7 @@ regexp.prototype.flags@^1.4.1: define-properties "^1.1.3" functions-have-names "^1.2.2" -regexpp@^3.0.0, regexpp@^3.2.0: +regexpp@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== @@ -16357,7 +16325,7 @@ resolve.exports@^1.1.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.3.2: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.3.2: version "1.22.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== @@ -16693,7 +16661,7 @@ semver@7.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== -semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== From e48d6e1254ad7e2ac099c40adb8494e0bdcb9daf Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Tue, 19 Apr 2022 10:34:46 +0800 Subject: [PATCH 03/19] add tests --- .../lib/rules/__tests__/no-dynamic-i18n-messages.test.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/eslint-plugin/lib/rules/__tests__/no-dynamic-i18n-messages.test.js b/packages/eslint-plugin/lib/rules/__tests__/no-dynamic-i18n-messages.test.js index 3a69ea59b990..d067a4ab84f8 100644 --- a/packages/eslint-plugin/lib/rules/__tests__/no-dynamic-i18n-messages.test.js +++ b/packages/eslint-plugin/lib/rules/__tests__/no-dynamic-i18n-messages.test.js @@ -34,6 +34,9 @@ ruleTester.run('no-dynamic-i18n-messages', rule, { {'Welcome, {firstName}! How are you?'} `, }, + { + code: `{'This'} is {\`valid\`}`, + }, { code: "translate({message: 'My page meta title'})", }, @@ -53,6 +56,10 @@ ruleTester.run('no-dynamic-i18n-messages', rule, { code: '{text}', errors: errorsJSX, }, + { + code: 'Hi {text} my friend', + errors: errorsJSX, + }, { code: ' {text} ', errors: errorsJSX, From 5375bebe645e9bbc7bfb1cafca4464f385b3cb30 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Tue, 19 Apr 2022 10:51:31 +0800 Subject: [PATCH 04/19] fixups! --- .eslintrc.js | 11 ++++++++++- .../src/client/exports/__tests__/Translate.test.tsx | 1 + packages/eslint-plugin/lib/index.js | 6 +++--- packages/eslint-plugin/package.json | 2 +- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index e30864158b11..8854a53a53c4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -32,6 +32,7 @@ module.exports = { 'plugin:@typescript-eslint/recommended', 'plugin:regexp/recommended', 'prettier', + 'plugin:@docusaurus/all', ], settings: { 'import/resolver': { @@ -41,7 +42,14 @@ module.exports = { }, }, reportUnusedDisableDirectives: true, - plugins: ['react-hooks', 'header', 'jest', '@typescript-eslint', 'regexp'], + plugins: [ + 'react-hooks', + 'header', + 'jest', + '@typescript-eslint', + 'regexp', + '@docusaurus', + ], rules: { 'array-callback-return': WARNING, camelcase: WARNING, @@ -326,6 +334,7 @@ module.exports = { 'header/header': OFF, 'global-require': OFF, '@typescript-eslint/no-var-requires': OFF, + '@docusaurus/no-untranslated-text': OFF, }, }, { diff --git a/packages/docusaurus/src/client/exports/__tests__/Translate.test.tsx b/packages/docusaurus/src/client/exports/__tests__/Translate.test.tsx index 28a8f90fe672..7564385af7e4 100644 --- a/packages/docusaurus/src/client/exports/__tests__/Translate.test.tsx +++ b/packages/docusaurus/src/client/exports/__tests__/Translate.test.tsx @@ -67,6 +67,7 @@ describe('', () => { it('rejects when children is not a string', () => { expect(() => renderer.create( + // eslint-disable-next-line @docusaurus/no-dynamic-i18n-messages {/* @ts-expect-error: for test */} aaa diff --git a/packages/eslint-plugin/lib/index.js b/packages/eslint-plugin/lib/index.js index 7e7a2b814af0..cf7e05ae33b0 100644 --- a/packages/eslint-plugin/lib/index.js +++ b/packages/eslint-plugin/lib/index.js @@ -12,13 +12,13 @@ module.exports = { configs: { recommended: { rules: { - 'docusaurus/no-dynamic-i18n-messages': 'error', + '@docusaurus/no-dynamic-i18n-messages': 'error', }, }, all: { rules: { - 'docusaurus/no-dynamic-i18n-messages': 'error', - 'docusaurus/no-untranslated-text': 'warn', + '@docusaurus/no-dynamic-i18n-messages': 'error', + '@docusaurus/no-untranslated-text': 'warn', }, }, }, diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 8280e5b04663..8a7722529b1e 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,5 +1,5 @@ { - "name": "eslint-plugin-docusaurus", + "name": "@docusaurus/eslint-plugin", "version": "2.0.0-beta.18", "description": "Docusaurus specific linting rules for eslint", "main": "lib/index.js", From e33bcd25be1e32449f54fbaad36fac1a8852295e Mon Sep 17 00:00:00 2001 From: Elias Papavasileiou Date: Tue, 19 Apr 2022 19:20:08 +0300 Subject: [PATCH 05/19] fix(no-dynamic-i18n-messages): make translate() recognize template literals --- .../no-dynamic-i18n-messages.test.js | 5 +--- .../lib/rules/no-dynamic-i18n-messages.js | 15 ++++------ packages/eslint-plugin/lib/util.js | 30 +++++++++++++------ 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/packages/eslint-plugin/lib/rules/__tests__/no-dynamic-i18n-messages.test.js b/packages/eslint-plugin/lib/rules/__tests__/no-dynamic-i18n-messages.test.js index d067a4ab84f8..e5b696221442 100644 --- a/packages/eslint-plugin/lib/rules/__tests__/no-dynamic-i18n-messages.test.js +++ b/packages/eslint-plugin/lib/rules/__tests__/no-dynamic-i18n-messages.test.js @@ -69,6 +69,7 @@ ruleTester.run('no-dynamic-i18n-messages', rule, { errors: errorsJSX, }, { + // eslint-disable-next-line no-template-curly-in-string code: '{`${text}`}', errors: errorsJSX, }, @@ -76,9 +77,5 @@ ruleTester.run('no-dynamic-i18n-messages', rule, { code: 'translate({message: metaTitle})', errors: errorsFunc, }, - { - code: 'translate({message: `My page meta title`})', - errors: errorsFunc, - }, ], }); diff --git a/packages/eslint-plugin/lib/rules/no-dynamic-i18n-messages.js b/packages/eslint-plugin/lib/rules/no-dynamic-i18n-messages.js index 2b9f5626f22b..9bcf2f710aee 100644 --- a/packages/eslint-plugin/lib/rules/no-dynamic-i18n-messages.js +++ b/packages/eslint-plugin/lib/rules/no-dynamic-i18n-messages.js @@ -5,7 +5,11 @@ * LICENSE file in the root directory of this source tree. */ -const {isTextLabelChild, report} = require('../util'); +const { + isTextLabelChild, + report, + isStringWithoutExpressions, +} = require('../util'); /** * @type {import('eslint').Rule.RuleModule} @@ -28,8 +32,6 @@ module.exports = { }, create(context) { - const isMessageTypeValid = (type) => type === 'Literal'; - const isNodeValid = (node) => node.children.every((child) => isTextLabelChild({child})); @@ -47,12 +49,7 @@ module.exports = { return; } - const messageType = messageProperty.value.type; - if (!messageType) { - return; - } - - if (!isMessageTypeValid(messageType)) { + if (!isStringWithoutExpressions(messageProperty.value)) { report(context, node, 'translateArg'); } }, diff --git a/packages/eslint-plugin/lib/util.js b/packages/eslint-plugin/lib/util.js index f07392127c17..3da305a37bf3 100644 --- a/packages/eslint-plugin/lib/util.js +++ b/packages/eslint-plugin/lib/util.js @@ -7,19 +7,23 @@ const isFalsyOrWhitespace = (value) => !value || !value.trim(); +const isStringWithoutExpressions = (value) => { + switch (value.type) { + case 'Literal': + return true; + case 'TemplateLiteral': + return value.expressions.length === 0; + default: + return false; + } +}; + const isTextLabelChild = ({child, includeWhitespace = true} = {}) => { switch (child.type) { case 'JSXText': return includeWhitespace || !isFalsyOrWhitespace(child.value); case 'JSXExpressionContainer': - switch (child.expression.type) { - case 'Literal': - return true; - case 'TemplateLiteral': - return child.expression.expressions.length === 0; - default: - return false; - } + return isStringWithoutExpressions(child.expression); default: return false; } @@ -63,6 +67,14 @@ const getCommonValidTests = () => [ { code: ' {text} ', }, + { + code: 'translate({message: `My page meta title`})', + }, ]; -module.exports = {isTextLabelChild, report, getCommonValidTests}; +module.exports = { + isTextLabelChild, + report, + getCommonValidTests, + isStringWithoutExpressions, +}; From 19a7f8900aafcdc011d993b6f084ae46f970bd01 Mon Sep 17 00:00:00 2001 From: Elias Papavasileiou Date: Wed, 20 Apr 2022 13:09:27 +0300 Subject: [PATCH 06/19] refactor: rename rule no-dynamic-i18n-messages --> string-literal-i18n-messages --- .../src/client/exports/__tests__/Translate.test.tsx | 2 +- packages/eslint-plugin/README.md | 4 ++-- ...namic-i18n-messages.md => string-literal-i18n-messages.md} | 2 +- packages/eslint-plugin/lib/index.js | 4 ++-- ...-i18n-messages.test.js => string-literal-i18n-messages.js} | 4 ++-- ...namic-i18n-messages.js => string-literal-i18n-messages.js} | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) rename packages/eslint-plugin/docs/rules/{no-dynamic-i18n-messages.md => string-literal-i18n-messages.md} (93%) rename packages/eslint-plugin/lib/rules/__tests__/{no-dynamic-i18n-messages.test.js => string-literal-i18n-messages.js} (94%) rename packages/eslint-plugin/lib/rules/{no-dynamic-i18n-messages.js => string-literal-i18n-messages.js} (96%) diff --git a/packages/docusaurus/src/client/exports/__tests__/Translate.test.tsx b/packages/docusaurus/src/client/exports/__tests__/Translate.test.tsx index 7564385af7e4..9c164ae36739 100644 --- a/packages/docusaurus/src/client/exports/__tests__/Translate.test.tsx +++ b/packages/docusaurus/src/client/exports/__tests__/Translate.test.tsx @@ -67,7 +67,7 @@ describe('', () => { it('rejects when children is not a string', () => { expect(() => renderer.create( - // eslint-disable-next-line @docusaurus/no-dynamic-i18n-messages + // eslint-disable-next-line @docusaurus/string-literal-i18n-messages {/* @ts-expect-error: for test */} aaa diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index 1f9533a15c3d..63010730f262 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -39,7 +39,7 @@ For more fine-grained control, you can also configure the rules you want to use: ```json { "rules": { - "@docusaurus/no-dynamic-i18n-messages": "error", + "@docusaurus/string-literal-i18n-messages": "error", "@docusaurus/no-untranslated-text": "warn" } } @@ -52,5 +52,5 @@ For more fine-grained control, you can also configure the rules you want to use: ## Supported Rules -- no-dynamic-i18n-messages +- string-literal-i18n-messages - no-untranslated-text diff --git a/packages/eslint-plugin/docs/rules/no-dynamic-i18n-messages.md b/packages/eslint-plugin/docs/rules/string-literal-i18n-messages.md similarity index 93% rename from packages/eslint-plugin/docs/rules/no-dynamic-i18n-messages.md rename to packages/eslint-plugin/docs/rules/string-literal-i18n-messages.md index cf4dcb733a74..a4ef1389613d 100644 --- a/packages/eslint-plugin/docs/rules/no-dynamic-i18n-messages.md +++ b/packages/eslint-plugin/docs/rules/string-literal-i18n-messages.md @@ -1,4 +1,4 @@ -# enforce translate calls to be plain text labels (no-dynamic-i18n-messages) +# enforce translate calls to be plain text labels (string-literal-i18n-messages) Ensures that `` children and the message attribute of `translate` function calls are hardcoded strings. diff --git a/packages/eslint-plugin/lib/index.js b/packages/eslint-plugin/lib/index.js index cf7e05ae33b0..6ca71896088f 100644 --- a/packages/eslint-plugin/lib/index.js +++ b/packages/eslint-plugin/lib/index.js @@ -12,12 +12,12 @@ module.exports = { configs: { recommended: { rules: { - '@docusaurus/no-dynamic-i18n-messages': 'error', + '@docusaurus/string-literal-i18n-messages': 'error', }, }, all: { rules: { - '@docusaurus/no-dynamic-i18n-messages': 'error', + '@docusaurus/string-literal-i18n-messages': 'error', '@docusaurus/no-untranslated-text': 'warn', }, }, diff --git a/packages/eslint-plugin/lib/rules/__tests__/no-dynamic-i18n-messages.test.js b/packages/eslint-plugin/lib/rules/__tests__/string-literal-i18n-messages.js similarity index 94% rename from packages/eslint-plugin/lib/rules/__tests__/no-dynamic-i18n-messages.test.js rename to packages/eslint-plugin/lib/rules/__tests__/string-literal-i18n-messages.js index e5b696221442..567586bf0b33 100644 --- a/packages/eslint-plugin/lib/rules/__tests__/no-dynamic-i18n-messages.test.js +++ b/packages/eslint-plugin/lib/rules/__tests__/string-literal-i18n-messages.js @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -const rule = require('../no-dynamic-i18n-messages'); +const rule = require('../string-literal-i18n-messages'); const {RuleTester} = require('eslint'); const {getCommonValidTests} = require('../../util'); @@ -18,7 +18,7 @@ const ruleTester = new RuleTester({ ecmaFeatures: {jsx: true}, }, }); -ruleTester.run('no-dynamic-i18n-messages', rule, { +ruleTester.run('string-literal-i18n-messages', rule, { valid: [ ...getCommonValidTests(), { diff --git a/packages/eslint-plugin/lib/rules/no-dynamic-i18n-messages.js b/packages/eslint-plugin/lib/rules/string-literal-i18n-messages.js similarity index 96% rename from packages/eslint-plugin/lib/rules/no-dynamic-i18n-messages.js rename to packages/eslint-plugin/lib/rules/string-literal-i18n-messages.js index 9bcf2f710aee..fe317828b75b 100644 --- a/packages/eslint-plugin/lib/rules/no-dynamic-i18n-messages.js +++ b/packages/eslint-plugin/lib/rules/string-literal-i18n-messages.js @@ -20,7 +20,7 @@ module.exports = { docs: { description: 'enforce translate calls to be plain text labels', category: 'Possible Problems', - url: 'https://github.com/facebook/docusaurus/tree/main/packages/eslint-plugin/docs/rules/no-dynamic-i18n-messages.md', + url: 'https://github.com/facebook/docusaurus/tree/main/packages/eslint-plugin/docs/rules/string-literal-i18n-messages.md', }, schema: [], messages: { From 7ae0e64855cc7d5f59c7d688243de612b7246d66 Mon Sep 17 00:00:00 2001 From: Elias Papavasileiou Date: Wed, 20 Apr 2022 20:01:36 +0300 Subject: [PATCH 07/19] feat: add ignoreStrings option and refactor --- .eslintrc.js | 4 + .../__tests__/no-untranslated-text.test.js | 75 ++++++++++++++++- ...s => string-literal-i18n-messages.test.js} | 32 +------- .../lib/rules/no-untranslated-text.js | 32 +++++--- .../lib/rules/string-literal-i18n-messages.js | 6 +- packages/eslint-plugin/lib/util.js | 81 +++++++++++++++++-- project-words.txt | 1 + 7 files changed, 181 insertions(+), 50 deletions(-) rename packages/eslint-plugin/lib/rules/__tests__/{string-literal-i18n-messages.js => string-literal-i18n-messages.test.js} (61%) diff --git a/.eslintrc.js b/.eslintrc.js index 8854a53a53c4..e8e95ca38e31 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -312,6 +312,10 @@ 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-untranslated-text': [ + WARNING, + {ignoreStrings: ['·', '—', '×']}, + ], }, overrides: [ { diff --git a/packages/eslint-plugin/lib/rules/__tests__/no-untranslated-text.test.js b/packages/eslint-plugin/lib/rules/__tests__/no-untranslated-text.test.js index abb4103d69f8..b438040d9b8e 100644 --- a/packages/eslint-plugin/lib/rules/__tests__/no-untranslated-text.test.js +++ b/packages/eslint-plugin/lib/rules/__tests__/no-untranslated-text.test.js @@ -21,7 +21,45 @@ const ruleTester = new RuleTester({ }, }); ruleTester.run('no-untranslated-text', rule, { - valid: [...getCommonValidTests()], + valid: [ + ...getCommonValidTests(), + { + code: '·', + options: [{ignoreStrings: ['·', '—', '×']}], + }, + { + code: '· ', + options: [{ignoreStrings: ['·', '—', '×']}], + }, + { + code: ' · ', + options: [{ignoreStrings: ['·', '—', '×']}], + }, + { + code: '· ·', + options: [{ignoreStrings: ['·', '—', '×']}], + }, + { + code: '· — ×', + options: [{ignoreStrings: ['·', '—', '×']}], + }, + { + code: '{"·"}', + options: [{ignoreStrings: ['·', '—', '×']}], + }, + { + code: "{'·'}", + options: [{ignoreStrings: ['·', '—', '×']}], + }, + { + code: '{`·`}', + options: [{ignoreStrings: ['·', '—', '×']}], + }, + { + code: 'Docusaurus', + options: [{ignoreStrings: ['Docusaurus']}], + }, + ], invalid: [ { @@ -60,5 +98,40 @@ ruleTester.run('no-untranslated-text', rule, { code: '<>text', errors: errorsJSXFragment, }, + { + code: '· — ×', + errors: errorsJSX, + options: [{ignoreStrings: ['·', '—']}], + }, + { + code: '··', + errors: errorsJSX, + options: [{ignoreStrings: ['·', '—', '×']}], + }, + { + code: ' ·· ', + errors: errorsJSX, + options: [{ignoreStrings: ['·', '—', '×']}], + }, + { + code: '"·"', + errors: errorsJSX, + options: [{ignoreStrings: ['·', '—', '×']}], + }, + { + code: "'·'", + errors: errorsJSX, + options: [{ignoreStrings: ['·', '—', '×']}], + }, + { + code: '`·`', + errors: errorsJSX, + options: [{ignoreStrings: ['·', '—', '×']}], + }, + { + code: 'Docusaurus', + errors: errorsJSX, + options: [{ignoreStrings: ['Docu', 'saurus']}], + }, ], }); diff --git a/packages/eslint-plugin/lib/rules/__tests__/string-literal-i18n-messages.js b/packages/eslint-plugin/lib/rules/__tests__/string-literal-i18n-messages.test.js similarity index 61% rename from packages/eslint-plugin/lib/rules/__tests__/string-literal-i18n-messages.js rename to packages/eslint-plugin/lib/rules/__tests__/string-literal-i18n-messages.test.js index 567586bf0b33..8b381f4c1f96 100644 --- a/packages/eslint-plugin/lib/rules/__tests__/string-literal-i18n-messages.js +++ b/packages/eslint-plugin/lib/rules/__tests__/string-literal-i18n-messages.test.js @@ -19,37 +19,7 @@ const ruleTester = new RuleTester({ }, }); ruleTester.run('string-literal-i18n-messages', rule, { - valid: [ - ...getCommonValidTests(), - { - code: ` - Welcome to my website - `, - }, - { - code: ` - {'Welcome, {firstName}! How are you?'} - `, - }, - { - code: `{'This'} is {\`valid\`}`, - }, - { - code: "translate({message: 'My page meta title'})", - }, - { - code: "translate({message: 'The logo of site {siteName}'}, {siteName: 'Docusaurus'})", - }, - { - code: 'translate({otherProp: metaTitle})', - }, - { - code: 'translate({otherProp: `My page meta title`})', - }, - ], + valid: [...getCommonValidTests()], invalid: [ { diff --git a/packages/eslint-plugin/lib/rules/no-untranslated-text.js b/packages/eslint-plugin/lib/rules/no-untranslated-text.js index 88a310d709b8..dd4782586e0b 100644 --- a/packages/eslint-plugin/lib/rules/no-untranslated-text.js +++ b/packages/eslint-plugin/lib/rules/no-untranslated-text.js @@ -19,7 +19,17 @@ module.exports = { category: 'Suggestions', url: 'https://github.com/facebook/docusaurus/tree/main/packages/eslint-plugin/docs/rules/no-untranslated-text.md', }, - schema: [], + schema: [ + { + type: 'object', + properties: { + ignoreStrings: { + type: 'array', + }, + }, + additionalProperties: false, + }, + ], messages: { translateChildren: 'All text labels in JSX should be wrapped by translate calls', @@ -27,28 +37,32 @@ module.exports = { }, create(context) { - const isParentTranslate = (child, isParentFragment) => + const stringsToIgnore = context.options[0]?.ignoreStrings ?? []; + + const isParentTranslate = ({child, isParentFragment}) => !isParentFragment && child.parent.openingElement.name.name === 'Translate'; - const isChildValid = (child, isParentFragment) => { - if (!isTextLabelChild({child, includeWhitespace: false})) { + const isChildValid = ({child, isParentFragment}) => { + if (!isTextLabelChild({child, ignoreWhitespace: true, stringsToIgnore})) { return true; } - return isParentTranslate(child, isParentFragment); + return isParentTranslate({child, isParentFragment}); }; - const isNodeValid = (node, isFragment) => - node.children.every((child) => isChildValid(child, isFragment)); + const isNodeValid = ({node, isFragment = false} = {}) => + node.children.every((child) => + isChildValid({child, isParentFragment: isFragment}), + ); return { 'JSXElement[openingElement.selfClosing=false]': (node) => { - if (!isNodeValid(node)) { + if (!isNodeValid({node})) { report(context, node, 'translateChildren'); } }, 'JSXFragment[openingFragment]': (node) => { - if (!isNodeValid(node, true)) { + if (!isNodeValid({node, isFragment: true})) { report(context, node, 'translateChildren'); } }, diff --git a/packages/eslint-plugin/lib/rules/string-literal-i18n-messages.js b/packages/eslint-plugin/lib/rules/string-literal-i18n-messages.js index fe317828b75b..d6bea2a7a9cc 100644 --- a/packages/eslint-plugin/lib/rules/string-literal-i18n-messages.js +++ b/packages/eslint-plugin/lib/rules/string-literal-i18n-messages.js @@ -49,7 +49,11 @@ module.exports = { return; } - if (!isStringWithoutExpressions(messageProperty.value)) { + if ( + !isStringWithoutExpressions({ + text: messageProperty.value, + }) + ) { report(context, node, 'translateArg'); } }, diff --git a/packages/eslint-plugin/lib/util.js b/packages/eslint-plugin/lib/util.js index 3da305a37bf3..388cbb08196a 100644 --- a/packages/eslint-plugin/lib/util.js +++ b/packages/eslint-plugin/lib/util.js @@ -5,25 +5,62 @@ * LICENSE file in the root directory of this source tree. */ -const isFalsyOrWhitespace = (value) => !value || !value.trim(); +const isMadeOfIgnoredStrings = ({text, stringsToIgnore}) => + text + .trim() + .split(/\s+/) + .every((string) => stringsToIgnore.includes(string)); -const isStringWithoutExpressions = (value) => { - switch (value.type) { +const isWhitespace = (text) => !text || !text.trim(); + +const isTextValid = ({text, ignoreWhitespace, stringsToIgnore}) => + !!text && + !(ignoreWhitespace && isWhitespace(text)) && + !isMadeOfIgnoredStrings({ + text, + stringsToIgnore, + }); + +const isStringWithoutExpressions = ({ + text, + ignoreWhitespace = false, + stringsToIgnore = [], +} = {}) => { + switch (text.type) { case 'Literal': - return true; + return isTextValid({text: text.value, ignoreWhitespace, stringsToIgnore}); case 'TemplateLiteral': - return value.expressions.length === 0; + return ( + !text.expressions.length && + isTextValid({ + text: text.quasis[0].value.raw, + ignoreWhitespace, + stringsToIgnore, + }) + ); default: return false; } }; -const isTextLabelChild = ({child, includeWhitespace = true} = {}) => { +const isTextLabelChild = ({ + child, + ignoreWhitespace = false, + stringsToIgnore = [], +} = {}) => { switch (child.type) { case 'JSXText': - return includeWhitespace || !isFalsyOrWhitespace(child.value); + return isTextValid({ + text: child.value, + ignoreWhitespace, + stringsToIgnore, + }); case 'JSXExpressionContainer': - return isStringWithoutExpressions(child.expression); + return isStringWithoutExpressions({ + text: child.expression, + ignoreWhitespace, + stringsToIgnore, + }); default: return false; } @@ -70,6 +107,34 @@ const getCommonValidTests = () => [ { code: 'translate({message: `My page meta title`})', }, + { + code: ` + Welcome to my website + `, + }, + { + code: ` + {'Welcome, {firstName}! How are you?'} + `, + }, + { + code: `{'This'} is {\`valid\`}`, + }, + { + code: "translate({message: 'My page meta title'})", + }, + { + code: "translate({message: 'The logo of site {siteName}'}, {siteName: 'Docusaurus'})", + }, + { + code: 'translate({otherProp: metaTitle})', + }, + { + code: 'translate({otherProp: `My page meta title`})', + }, ]; module.exports = { diff --git a/project-words.txt b/project-words.txt index e403ec1046fa..9a13f6be9954 100644 --- a/project-words.txt +++ b/project-words.txt @@ -251,6 +251,7 @@ roadmap rocketvalidator rtcts rtlcss +saurus scaleway searchbar sebastien From aa58521ce4ad8b079ad2ace47262b5ccf9c66c50 Mon Sep 17 00:00:00 2001 From: Elias Papavasileiou Date: Wed, 20 Apr 2022 21:38:12 +0300 Subject: [PATCH 08/19] docs: migrate docs to /docs/api/plugins --- packages/eslint-plugin/README.md | 53 +----- .../docs/rules/no-untranslated-text.md | 32 ---- .../rules/string-literal-i18n-messages.md | 42 ---- website/docs/api/plugins/eslint-plugin.md | 179 ++++++++++++++++++ website/docs/api/plugins/overview.md | 6 + .../api/plugins/plugin-google-analytics.md | 2 +- .../docs/api/plugins/plugin-google-gtag.md | 2 +- .../docs/api/plugins/plugin-ideal-image.md | 2 +- website/docs/api/plugins/plugin-pwa.md | 2 +- website/docs/api/plugins/plugin-sitemap.md | 2 +- website/package.json | 1 + 11 files changed, 193 insertions(+), 130 deletions(-) delete mode 100644 packages/eslint-plugin/docs/rules/no-untranslated-text.md delete mode 100644 packages/eslint-plugin/docs/rules/string-literal-i18n-messages.md create mode 100644 website/docs/api/plugins/eslint-plugin.md diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index 63010730f262..30536fb852ae 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -1,56 +1,7 @@ # `@docusaurus/eslint-plugin` -Docusaurus specific linting rules for eslint - -## Installation - -You'll first need to install [ESLint](https://eslint.org/): - -```sh -npm i -D eslint -``` - -Next, install `@docusaurus/eslint-plugin`: - -```sh -npm i -D @docusaurus/eslint-plugin -``` +Docusaurus specific linting rules for eslint. ## Usage -Add `@docusaurus` to the plugins section of your `.eslintrc` configuration file: - -```json -{ - "plugins": ["@docusaurus"] -} -``` - -Then, you can extend one of the configs (e.g. the `recommended` config): - -```json -{ - "extends": ["plugin:@docusaurus/recommended"] -} -``` - -For more fine-grained control, you can also configure the rules you want to use: - -```json -{ - "rules": { - "@docusaurus/string-literal-i18n-messages": "error", - "@docusaurus/no-untranslated-text": "warn" - } -} -``` - -## Supported Configs - -- recommended -- all - -## Supported Rules - -- string-literal-i18n-messages -- no-untranslated-text +See [eslint-plugin documentation](https://docusaurus.io/docs/api/plugins/@docusaurus/eslint-plugin). diff --git a/packages/eslint-plugin/docs/rules/no-untranslated-text.md b/packages/eslint-plugin/docs/rules/no-untranslated-text.md deleted file mode 100644 index 96e8873fd84a..000000000000 --- a/packages/eslint-plugin/docs/rules/no-untranslated-text.md +++ /dev/null @@ -1,32 +0,0 @@ -# enforce text labels in JSX to be wrapped by translate calls (no-untranslated-text) - -Ensures that all text labels in JSX are wrapped by `` components. - -When the [i18n feature](https://docusaurus.io/docs/i18n/introduction) is used, this rule is to ensure that all strings appearing on the website are being translated, so no string accidentally slips through untranslated. - -## Rule Details - -Examples of **incorrect** code for this rule: - -```js -// Hello World is not translated -Hello World -``` - -Examples of **correct** code for this rule: - -```js -// Hello World is translated - - Hello World - -``` - -## When Not To Use It - -If you're not using the [i18n feature](https://docusaurus.io/docs/i18n/introduction) then you can disable this rule. - -## Further Reading - -- https://docusaurus.io/docs/docusaurus-core#translate -- https://docusaurus.io/docs/docusaurus-core#translate-1 diff --git a/packages/eslint-plugin/docs/rules/string-literal-i18n-messages.md b/packages/eslint-plugin/docs/rules/string-literal-i18n-messages.md deleted file mode 100644 index a4ef1389613d..000000000000 --- a/packages/eslint-plugin/docs/rules/string-literal-i18n-messages.md +++ /dev/null @@ -1,42 +0,0 @@ -# enforce translate calls to be plain text labels (string-literal-i18n-messages) - -Ensures that `` children and the message attribute of `translate` function calls are hardcoded strings. - -This is to ensure that static extraction of the text will work so it can be translatable. In-string dynamic placeholders are also possible using the values object. - -## Rule Details - -Examples of **incorrect** code for this rule: - -```js -const text = 'Some text to be translated' - -// Invalid child -{text} - -// Invalid message attribute -translate({message: text}) -``` - -Examples of **correct** code for this rule: - -```js -// Valid child -Some text to be translated - -// Valid message attribute -translate({message: 'Some text to be translated'}) - -// Valid child using values object as prop - - {'Welcome, {firstName}! How are you?'} - - -// Valid message attribute using values object as second argument -translate({message: 'The logo of site {siteName}'}, {siteName: 'Docusaurus'}) -``` - -## Further Reading - -- https://docusaurus.io/docs/docusaurus-core#translate -- https://docusaurus.io/docs/docusaurus-core#translate-1 diff --git a/website/docs/api/plugins/eslint-plugin.md b/website/docs/api/plugins/eslint-plugin.md new file mode 100644 index 000000000000..c2e3f6609a7f --- /dev/null +++ b/website/docs/api/plugins/eslint-plugin.md @@ -0,0 +1,179 @@ +--- +sidebar_position: 6 +id: eslint-plugin +title: '📦 eslint-plugin' +slug: '/api/plugins/@docusaurus/eslint-plugin' +--- + +import APITable from '@site/src/components/APITable'; + +Docusaurus eslint plugin to ensure best Docusaurus practices. + +# Installation {#installation} + +You'll first need to install [ESLint](https://eslint.org/): + +```sh +npm i -D eslint +``` + +Next, install `@docusaurus/eslint-plugin`: + +```bash npm2yarn +npm i -D @docusaurus/eslint-plugin +``` + +# Usage {#usage} + +Add `@docusaurus` to the plugins section of your `.eslintrc` configuration file: + +```json +{ + "plugins": ["@docusaurus"] +} +``` + +Then, you can extend one of the configs (e.g. the `recommended` config): + +```json +{ + "extends": ["plugin:@docusaurus/recommended"] +} +``` + +Each config contains a set of rules. For more fine-grained control, you can also configure the rules you want to use directly: + +```json +{ + "rules": { + "@docusaurus/string-literal-i18n-messages": "error", + "@docusaurus/no-untranslated-text": "warn" + } +} +``` + +# Supported Configs{#supported-configs} + +## recommended + +### Rules: + +- @docusaurus/string-literal-i18n-messages + +## all + +### Rules: + +- @docusaurus/string-literal-i18n-messages +- @docusaurus/no-untranslated-text + +# Supported Rules{#supported-rules} + +## string-literal-i18n-messages + +### enforce translate calls to be plain text labels (string-literal-i18n-messages) + +Ensures that `` children and the message attribute of `translate` function calls are hardcoded strings. + +This is to ensure that static extraction of the text will work so it can be translatable. In-string dynamic placeholders are also possible using the values object. + +### Rule Details + +Examples of **incorrect** code for this rule: + +```js +const text = 'Some text to be translated' + +// Invalid child +{text} + +// Invalid message attribute +translate({message: text}) +``` + +Examples of **correct** code for this rule: + +```js +// Valid child +Some text to be translated + +// Valid message attribute +translate({message: 'Some text to be translated'}) + +// Valid child using values object as prop + + {'Welcome, {firstName}! How are you?'} + + +// Valid message attribute using values object as second argument +translate({message: 'The logo of site {siteName}'}, {siteName: 'Docusaurus'}) +``` + +### Further Reading + +- https://docusaurus.io/docs/docusaurus-core#translate +- https://docusaurus.io/docs/docusaurus-core#translate-1 + +## no-untranslated-text + +### enforce text labels in JSX to be wrapped by translate calls (no-untranslated-text) + +Ensures that all text labels in JSX are wrapped by `` components. + +When the [i18n feature](https://docusaurus.io/docs/i18n/introduction) is used, this rule is to ensure that all strings appearing on the website are being translated, so no string accidentally slips through untranslated. + +### Rule Details + +Examples of **incorrect** code for this rule: + +```js +// Hello World is not translated +Hello World +``` + +Examples of **correct** code for this rule: + +```js +// Hello World is translated + + Hello World + +``` + +### Rule Configuration + +Accepted fields: + + + +| Option | Type | Default | Description | +| --- | --- | --- | --- | +| `ignoreStrings` | `string[]` | `['·', '—', '×']` | The strings to be ignored. | + + + +### When Not To Use It + +If you're not using the [i18n feature](https://docusaurus.io/docs/i18n/introduction) then you can disable this rule. + +### Further Reading + +- https://docusaurus.io/docs/docusaurus-core#translate +- https://docusaurus.io/docs/docusaurus-core#translate-1 + +# Example configuration {#ex-config} + +Here's an example configuration: + +```js title="[.eslintrc.js]" +module.exports = { + extends: ['plugin:@docusaurus/all'], + plugins: ['@docusaurus'], + rules: { + '@docusaurus/no-untranslated-text': [ + 'warn', + {ignoreStrings: ['·', '—', '×']}, + ], + }, +}; +``` diff --git a/website/docs/api/plugins/overview.md b/website/docs/api/plugins/overview.md index 31897e73e682..6a2e53416b2d 100644 --- a/website/docs/api/plugins/overview.md +++ b/website/docs/api/plugins/overview.md @@ -27,3 +27,9 @@ These plugins will add a useful behavior to your Docusaurus site. - [@docusaurus/plugin-ideal-image](./plugin-ideal-image.md) - [@docusaurus/plugin-google-analytics](./plugin-google-analytics.md) - [@docusaurus/plugin-google-gtag](./plugin-google-gtag.md) + +## Development plugins {#dev-plugins} + +These plugins are useful for developing a site with Docusaurus. + +- [@docusaurus/eslint-plugin](./eslint-plugin.md) diff --git a/website/docs/api/plugins/plugin-google-analytics.md b/website/docs/api/plugins/plugin-google-analytics.md index da8f938732c9..16b70a7089a1 100644 --- a/website/docs/api/plugins/plugin-google-analytics.md +++ b/website/docs/api/plugins/plugin-google-analytics.md @@ -1,5 +1,5 @@ --- -sidebar_position: 6 +sidebar_position: 7 id: plugin-google-analytics title: '📦 plugin-google-analytics' slug: '/api/plugins/@docusaurus/plugin-google-analytics' diff --git a/website/docs/api/plugins/plugin-google-gtag.md b/website/docs/api/plugins/plugin-google-gtag.md index c56343c14b0d..f1b1a62afef3 100644 --- a/website/docs/api/plugins/plugin-google-gtag.md +++ b/website/docs/api/plugins/plugin-google-gtag.md @@ -1,5 +1,5 @@ --- -sidebar_position: 7 +sidebar_position: 8 id: plugin-google-gtag title: '📦 plugin-google-gtag' slug: '/api/plugins/@docusaurus/plugin-google-gtag' diff --git a/website/docs/api/plugins/plugin-ideal-image.md b/website/docs/api/plugins/plugin-ideal-image.md index db40b29d7d7f..ab8c2cf4c49c 100644 --- a/website/docs/api/plugins/plugin-ideal-image.md +++ b/website/docs/api/plugins/plugin-ideal-image.md @@ -1,5 +1,5 @@ --- -sidebar_position: 8 +sidebar_position: 9 id: plugin-ideal-image title: '📦 plugin-ideal-image' slug: '/api/plugins/@docusaurus/plugin-ideal-image' diff --git a/website/docs/api/plugins/plugin-pwa.md b/website/docs/api/plugins/plugin-pwa.md index 1a7ab67bbe01..f9e25674c340 100644 --- a/website/docs/api/plugins/plugin-pwa.md +++ b/website/docs/api/plugins/plugin-pwa.md @@ -1,5 +1,5 @@ --- -sidebar_position: 9 +sidebar_position: 10 id: plugin-pwa title: '📦 plugin-pwa' slug: '/api/plugins/@docusaurus/plugin-pwa' diff --git a/website/docs/api/plugins/plugin-sitemap.md b/website/docs/api/plugins/plugin-sitemap.md index c8aec5da3079..39956e9e76fe 100644 --- a/website/docs/api/plugins/plugin-sitemap.md +++ b/website/docs/api/plugins/plugin-sitemap.md @@ -1,5 +1,5 @@ --- -sidebar_position: 10 +sidebar_position: 11 id: plugin-sitemap title: '📦 plugin-sitemap' slug: '/api/plugins/@docusaurus/plugin-sitemap' diff --git a/website/package.json b/website/package.json index fb2e68ec7840..16e3dbca6a88 100644 --- a/website/package.json +++ b/website/package.json @@ -80,6 +80,7 @@ }, "devDependencies": { "@tsconfig/docusaurus": "^1.0.5", + "@docusaurus/eslint-plugin": "2.0.0-beta.18", "@types/jest": "^27.4.1", "cross-env": "^7.0.3", "rimraf": "^3.0.2" From 87760878f72b3cca474870950c44934af540de90 Mon Sep 17 00:00:00 2001 From: Elias Papavasileiou Date: Wed, 20 Apr 2022 22:08:38 +0300 Subject: [PATCH 09/19] docs: fix anchor links in README.md --- website/docs/api/plugins/eslint-plugin.md | 48 ++++++++++------------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/website/docs/api/plugins/eslint-plugin.md b/website/docs/api/plugins/eslint-plugin.md index c2e3f6609a7f..821b925dcdd9 100644 --- a/website/docs/api/plugins/eslint-plugin.md +++ b/website/docs/api/plugins/eslint-plugin.md @@ -9,21 +9,13 @@ import APITable from '@site/src/components/APITable'; Docusaurus eslint plugin to ensure best Docusaurus practices. -# Installation {#installation} - -You'll first need to install [ESLint](https://eslint.org/): - -```sh -npm i -D eslint -``` - -Next, install `@docusaurus/eslint-plugin`: +## Installation ```bash npm2yarn -npm i -D @docusaurus/eslint-plugin +npm install --save-dev @docusaurus/eslint-plugin ``` -# Usage {#usage} +## Usage Add `@docusaurus` to the plugins section of your `.eslintrc` configuration file: @@ -52,32 +44,32 @@ Each config contains a set of rules. For more fine-grained control, you can also } ``` -# Supported Configs{#supported-configs} +## Supported Configs -## recommended +### recommended{#config-recommended} -### Rules: +#### Rules:{#config-recommended-rules} - @docusaurus/string-literal-i18n-messages -## all +### all{#config-all} -### Rules: +#### Rules:{#config-all-rules} - @docusaurus/string-literal-i18n-messages - @docusaurus/no-untranslated-text -# Supported Rules{#supported-rules} +## Supported Rules -## string-literal-i18n-messages +### string-literal-i18n-messages{#rule-string-literal-i18n-messages} -### enforce translate calls to be plain text labels (string-literal-i18n-messages) +enforce translate calls to be plain text labels (string-literal-i18n-messages) Ensures that `` children and the message attribute of `translate` function calls are hardcoded strings. This is to ensure that static extraction of the text will work so it can be translatable. In-string dynamic placeholders are also possible using the values object. -### Rule Details +#### Rule Details{#rule-string-literal-i18n-messages-details} Examples of **incorrect** code for this rule: @@ -109,20 +101,20 @@ translate({message: 'Some text to be translated'}) translate({message: 'The logo of site {siteName}'}, {siteName: 'Docusaurus'}) ``` -### Further Reading +#### Further Reading{#rule-string-literal-i18n-messages-further-reading} - https://docusaurus.io/docs/docusaurus-core#translate - https://docusaurus.io/docs/docusaurus-core#translate-1 -## no-untranslated-text +### no-untranslated-text{#rule-no-untranslated-text} -### enforce text labels in JSX to be wrapped by translate calls (no-untranslated-text) +enforce text labels in JSX to be wrapped by translate calls (no-untranslated-text) Ensures that all text labels in JSX are wrapped by `` components. When the [i18n feature](https://docusaurus.io/docs/i18n/introduction) is used, this rule is to ensure that all strings appearing on the website are being translated, so no string accidentally slips through untranslated. -### Rule Details +#### Rule Details{#rule-no-untranslated-text-details} Examples of **incorrect** code for this rule: @@ -140,7 +132,7 @@ Examples of **correct** code for this rule: ``` -### Rule Configuration +#### Rule Configuration{#rule-no-untranslated-text-configuration} Accepted fields: @@ -152,16 +144,16 @@ Accepted fields: -### When Not To Use It +#### When Not To Use It{#rule-no-untranslated-text-when-not-to-use} If you're not using the [i18n feature](https://docusaurus.io/docs/i18n/introduction) then you can disable this rule. -### Further Reading +#### Further Reading{#rule-no-untranslated-text-further-reading} - https://docusaurus.io/docs/docusaurus-core#translate - https://docusaurus.io/docs/docusaurus-core#translate-1 -# Example configuration {#ex-config} +## Example configuration Here's an example configuration: From 3d5ecd0f12bff48c420c548d4114094cb7c95080 Mon Sep 17 00:00:00 2001 From: Elias Papavasileiou Date: Wed, 20 Apr 2022 23:09:11 +0300 Subject: [PATCH 10/19] fix: add some ignored strings --- .eslintrc.js | 2 +- .../rules/__tests__/no-untranslated-text.test.js | 14 ++++++++++++-- packages/eslint-plugin/lib/util.js | 4 ++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index e8e95ca38e31..43cc1c0feb45 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -314,7 +314,7 @@ module.exports = { '@typescript-eslint/prefer-optional-chain': ERROR, '@docusaurus/no-untranslated-text': [ WARNING, - {ignoreStrings: ['·', '—', '×']}, + {ignoreStrings: ['·', '-', '—', "'", '"', '×', '@', '🏠', '​']}, ], }, overrides: [ diff --git a/packages/eslint-plugin/lib/rules/__tests__/no-untranslated-text.test.js b/packages/eslint-plugin/lib/rules/__tests__/no-untranslated-text.test.js index b438040d9b8e..61a18cf2be58 100644 --- a/packages/eslint-plugin/lib/rules/__tests__/no-untranslated-text.test.js +++ b/packages/eslint-plugin/lib/rules/__tests__/no-untranslated-text.test.js @@ -45,11 +45,11 @@ ruleTester.run('no-untranslated-text', rule, { }, { code: '{"·"}', - options: [{ignoreStrings: ['·', '—', '×']}], + options: [{ignoreStrings: ['"·"']}], }, { code: "{'·'}", - options: [{ignoreStrings: ['·', '—', '×']}], + options: [{ignoreStrings: ["'·'"]}], }, { code: '{`·`}', @@ -59,6 +59,16 @@ ruleTester.run('no-untranslated-text', rule, { code: 'Docusaurus', options: [{ignoreStrings: ['Docusaurus']}], }, + { + code: '', + options: [{ignoreStrings: ['​']}], + }, + { + code: `<> + {' · '} + `, + options: [{ignoreStrings: ['·', "'"]}], + }, ], invalid: [ diff --git a/packages/eslint-plugin/lib/util.js b/packages/eslint-plugin/lib/util.js index 388cbb08196a..19dbf93cebbc 100644 --- a/packages/eslint-plugin/lib/util.js +++ b/packages/eslint-plugin/lib/util.js @@ -28,7 +28,7 @@ const isStringWithoutExpressions = ({ } = {}) => { switch (text.type) { case 'Literal': - return isTextValid({text: text.value, ignoreWhitespace, stringsToIgnore}); + return isTextValid({text: text.raw, ignoreWhitespace, stringsToIgnore}); case 'TemplateLiteral': return ( !text.expressions.length && @@ -51,7 +51,7 @@ const isTextLabelChild = ({ switch (child.type) { case 'JSXText': return isTextValid({ - text: child.value, + text: child.raw, ignoreWhitespace, stringsToIgnore, }); From 9f04ae19ba1bb3e610216709d17749f452a7b3f4 Mon Sep 17 00:00:00 2001 From: Elias Papavasileiou Date: Thu, 21 Apr 2022 20:59:43 +0300 Subject: [PATCH 11/19] docs: update eslint-plugin docs --- .../lib/rules/no-untranslated-text.js | 2 +- .../lib/rules/string-literal-i18n-messages.js | 2 +- packages/eslint-plugin/package.json | 2 +- website/docs/api/misc/_category_.yml | 2 + website/docs/api/misc/eslint-plugin/README.md | 76 ++++++++ .../eslint-plugin/no-untranslated-text.md | 48 +++++ .../string-literal-i18n-messages.md | 46 +++++ .../docs/api/plugin-methods/_category_.yml | 2 +- website/docs/api/plugins/_category_.yml | 2 +- website/docs/api/plugins/eslint-plugin.md | 171 ------------------ website/docs/api/plugins/overview.md | 6 - .../api/plugins/plugin-google-analytics.md | 2 +- .../docs/api/plugins/plugin-google-gtag.md | 2 +- .../docs/api/plugins/plugin-ideal-image.md | 2 +- website/docs/api/plugins/plugin-pwa.md | 2 +- website/docs/api/plugins/plugin-sitemap.md | 2 +- website/docs/api/themes/_category_.yml | 2 +- 17 files changed, 183 insertions(+), 188 deletions(-) create mode 100644 website/docs/api/misc/_category_.yml create mode 100644 website/docs/api/misc/eslint-plugin/README.md create mode 100644 website/docs/api/misc/eslint-plugin/no-untranslated-text.md create mode 100644 website/docs/api/misc/eslint-plugin/string-literal-i18n-messages.md delete mode 100644 website/docs/api/plugins/eslint-plugin.md diff --git a/packages/eslint-plugin/lib/rules/no-untranslated-text.js b/packages/eslint-plugin/lib/rules/no-untranslated-text.js index dd4782586e0b..007996c84bca 100644 --- a/packages/eslint-plugin/lib/rules/no-untranslated-text.js +++ b/packages/eslint-plugin/lib/rules/no-untranslated-text.js @@ -17,7 +17,7 @@ module.exports = { description: 'enforce text labels in JSX to be wrapped by translate calls', category: 'Suggestions', - url: 'https://github.com/facebook/docusaurus/tree/main/packages/eslint-plugin/docs/rules/no-untranslated-text.md', + url: 'https://docusaurus.io/docs/api/misc/@docusaurus/eslint-plugin/no-untranslated-text', }, schema: [ { diff --git a/packages/eslint-plugin/lib/rules/string-literal-i18n-messages.js b/packages/eslint-plugin/lib/rules/string-literal-i18n-messages.js index d6bea2a7a9cc..692c9abcd13b 100644 --- a/packages/eslint-plugin/lib/rules/string-literal-i18n-messages.js +++ b/packages/eslint-plugin/lib/rules/string-literal-i18n-messages.js @@ -20,7 +20,7 @@ module.exports = { docs: { description: 'enforce translate calls to be plain text labels', category: 'Possible Problems', - url: 'https://github.com/facebook/docusaurus/tree/main/packages/eslint-plugin/docs/rules/string-literal-i18n-messages.md', + url: 'https://docusaurus.io/docs/api/misc/@docusaurus/eslint-plugin/string-literal-i18n-messages', }, schema: [], messages: { diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 8a7722529b1e..44dfe11043a4 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -27,6 +27,6 @@ "eslint": ">=6" }, "engines": { - "node": "12.x || 14.x || >= 16" + "node": ">=14" } } diff --git a/website/docs/api/misc/_category_.yml b/website/docs/api/misc/_category_.yml new file mode 100644 index 000000000000..5d91a13ac27e --- /dev/null +++ b/website/docs/api/misc/_category_.yml @@ -0,0 +1,2 @@ +label: Miscellaneous +position: 1 diff --git a/website/docs/api/misc/eslint-plugin/README.md b/website/docs/api/misc/eslint-plugin/README.md new file mode 100644 index 000000000000..3e986ebbd8ae --- /dev/null +++ b/website/docs/api/misc/eslint-plugin/README.md @@ -0,0 +1,76 @@ +--- +sidebar_position: 0 +id: eslint-plugin +title: '📦 eslint-plugin' +slug: '/api/misc/@docusaurus/eslint-plugin' +--- + +import APITable from '@site/src/components/APITable'; + +Docusaurus eslint plugin to ensure best Docusaurus practices. + +## Installation + +```bash npm2yarn +npm install --save-dev @docusaurus/eslint-plugin +``` + +## Usage + +Add `@docusaurus` to the plugins section of your `.eslintrc` configuration file: + +```json +{ + "plugins": ["@docusaurus"] +} +``` + +Then, you can extend one of the configs (e.g. the `recommended` config): + +```json +{ + "extends": ["plugin:@docusaurus/recommended"] +} +``` + +Each config contains a set of rules. For more fine-grained control, you can also configure the rules you want to use directly: + +```json +{ + "rules": { + "@docusaurus/string-literal-i18n-messages": "error", + "@docusaurus/no-untranslated-text": "warn" + } +} +``` + +## Supported Configs + +| Name | Rules | +| :-: | --- | +| recommended | [`@docusaurus/string-literal-i18n-messages`](./eslint-plugin/string-literal-i18n-messages) | +| all | [`@docusaurus/no-untranslated-text`](./eslint-plugin/no-untranslated-text)
[`@docusaurus/string-literal-i18n-messages`](./eslint-plugin/string-literal-i18n-messages) | + +## Supported Rules + +| Name | Description | +| --- | --- | +| [`@docusaurus/no-untranslated-text`](./eslint-plugin/no-untranslated-text) | Enforce text labels in JSX to be wrapped by translate calls | +| [`@docusaurus/string-literal-i18n-messages`](./eslint-plugin/string-literal-i18n-messages) | Enforce translate calls to be plain text labels | + +## Example configuration + +Here's an example configuration: + +```js title="[.eslintrc.js]" +module.exports = { + extends: ['plugin:@docusaurus/all'], + plugins: ['@docusaurus'], + rules: { + '@docusaurus/no-untranslated-text': [ + 'warn', + {ignoreStrings: ['·', '—', '×']}, + ], + }, +}; +``` diff --git a/website/docs/api/misc/eslint-plugin/no-untranslated-text.md b/website/docs/api/misc/eslint-plugin/no-untranslated-text.md new file mode 100644 index 000000000000..da3c0cac8af6 --- /dev/null +++ b/website/docs/api/misc/eslint-plugin/no-untranslated-text.md @@ -0,0 +1,48 @@ +--- +slug: '/api/misc/@docusaurus/eslint-plugin/no-untranslated-text' +--- + +enforce text labels in JSX to be wrapped by translate calls (no-untranslated-text) + +Ensures that all text labels in JSX are wrapped by `` components. + +When the [i18n feature](https://docusaurus.io/docs/i18n/introduction) is used, this rule is to ensure that all strings appearing on the website are being translated, so no string accidentally slips through untranslated. + +## Rule Details {#details} + +Examples of **incorrect** code for this rule: + +```js +// Hello World is not translated +Hello World +``` + +Examples of **correct** code for this rule: + +```js +// Hello World is translated + + Hello World + +``` + +## Rule Configuration {#configuration} + +Accepted fields: + + + +| Option | Type | Default | Description | +| --------------- | ---------- | ------- | -------------------------- | +| `ignoreStrings` | `string[]` | `[]` | The strings to be ignored. | + + + +## When Not To Use It {#when-not-to-use} + +If you're not using the [i18n feature](https://docusaurus.io/docs/i18n/introduction) then you can disable this rule. + +## Further Reading {#further-reading} + +- https://docusaurus.io/docs/docusaurus-core#translate +- https://docusaurus.io/docs/docusaurus-core#translate-1 diff --git a/website/docs/api/misc/eslint-plugin/string-literal-i18n-messages.md b/website/docs/api/misc/eslint-plugin/string-literal-i18n-messages.md new file mode 100644 index 000000000000..1a694c42e993 --- /dev/null +++ b/website/docs/api/misc/eslint-plugin/string-literal-i18n-messages.md @@ -0,0 +1,46 @@ +--- +slug: '/api/misc/@docusaurus/eslint-plugin/string-literal-i18n-messages' +--- + +enforce translate calls to be plain text labels (string-literal-i18n-messages) + +Ensures that `` children and the message attribute of `translate` function calls are hardcoded strings. + +This is to ensure that static extraction of the text will work so it can be translatable. In-string dynamic placeholders are also possible using the values object. + +## Rule Details {#details} + +Examples of **incorrect** code for this rule: + +```js +const text = 'Some text to be translated' + +// Invalid child +{text} + +// Invalid message attribute +translate({message: text}) +``` + +Examples of **correct** code for this rule: + +```js +// Valid child +Some text to be translated + +// Valid message attribute +translate({message: 'Some text to be translated'}) + +// Valid child using values object as prop + + {'Welcome, {firstName}! How are you?'} + + +// Valid message attribute using values object as second argument +translate({message: 'The logo of site {siteName}'}, {siteName: 'Docusaurus'}) +``` + +## Further Reading {#further-reading} + +- https://docusaurus.io/docs/docusaurus-core#translate +- https://docusaurus.io/docs/docusaurus-core#translate-1 diff --git a/website/docs/api/plugin-methods/_category_.yml b/website/docs/api/plugin-methods/_category_.yml index 86cb36c24614..f7c2e4636455 100644 --- a/website/docs/api/plugin-methods/_category_.yml +++ b/website/docs/api/plugin-methods/_category_.yml @@ -1,2 +1,2 @@ label: Plugin method references -position: 1 +position: 2 diff --git a/website/docs/api/plugins/_category_.yml b/website/docs/api/plugins/_category_.yml index cffabddbd5db..b7555fd25af0 100644 --- a/website/docs/api/plugins/_category_.yml +++ b/website/docs/api/plugins/_category_.yml @@ -1,5 +1,5 @@ label: Plugins -position: 2 +position: 3 link: type: doc id: api/plugins/plugins-overview # Dogfood using a "qualified id" diff --git a/website/docs/api/plugins/eslint-plugin.md b/website/docs/api/plugins/eslint-plugin.md deleted file mode 100644 index 821b925dcdd9..000000000000 --- a/website/docs/api/plugins/eslint-plugin.md +++ /dev/null @@ -1,171 +0,0 @@ ---- -sidebar_position: 6 -id: eslint-plugin -title: '📦 eslint-plugin' -slug: '/api/plugins/@docusaurus/eslint-plugin' ---- - -import APITable from '@site/src/components/APITable'; - -Docusaurus eslint plugin to ensure best Docusaurus practices. - -## Installation - -```bash npm2yarn -npm install --save-dev @docusaurus/eslint-plugin -``` - -## Usage - -Add `@docusaurus` to the plugins section of your `.eslintrc` configuration file: - -```json -{ - "plugins": ["@docusaurus"] -} -``` - -Then, you can extend one of the configs (e.g. the `recommended` config): - -```json -{ - "extends": ["plugin:@docusaurus/recommended"] -} -``` - -Each config contains a set of rules. For more fine-grained control, you can also configure the rules you want to use directly: - -```json -{ - "rules": { - "@docusaurus/string-literal-i18n-messages": "error", - "@docusaurus/no-untranslated-text": "warn" - } -} -``` - -## Supported Configs - -### recommended{#config-recommended} - -#### Rules:{#config-recommended-rules} - -- @docusaurus/string-literal-i18n-messages - -### all{#config-all} - -#### Rules:{#config-all-rules} - -- @docusaurus/string-literal-i18n-messages -- @docusaurus/no-untranslated-text - -## Supported Rules - -### string-literal-i18n-messages{#rule-string-literal-i18n-messages} - -enforce translate calls to be plain text labels (string-literal-i18n-messages) - -Ensures that `` children and the message attribute of `translate` function calls are hardcoded strings. - -This is to ensure that static extraction of the text will work so it can be translatable. In-string dynamic placeholders are also possible using the values object. - -#### Rule Details{#rule-string-literal-i18n-messages-details} - -Examples of **incorrect** code for this rule: - -```js -const text = 'Some text to be translated' - -// Invalid child -{text} - -// Invalid message attribute -translate({message: text}) -``` - -Examples of **correct** code for this rule: - -```js -// Valid child -Some text to be translated - -// Valid message attribute -translate({message: 'Some text to be translated'}) - -// Valid child using values object as prop - - {'Welcome, {firstName}! How are you?'} - - -// Valid message attribute using values object as second argument -translate({message: 'The logo of site {siteName}'}, {siteName: 'Docusaurus'}) -``` - -#### Further Reading{#rule-string-literal-i18n-messages-further-reading} - -- https://docusaurus.io/docs/docusaurus-core#translate -- https://docusaurus.io/docs/docusaurus-core#translate-1 - -### no-untranslated-text{#rule-no-untranslated-text} - -enforce text labels in JSX to be wrapped by translate calls (no-untranslated-text) - -Ensures that all text labels in JSX are wrapped by `` components. - -When the [i18n feature](https://docusaurus.io/docs/i18n/introduction) is used, this rule is to ensure that all strings appearing on the website are being translated, so no string accidentally slips through untranslated. - -#### Rule Details{#rule-no-untranslated-text-details} - -Examples of **incorrect** code for this rule: - -```js -// Hello World is not translated -Hello World -``` - -Examples of **correct** code for this rule: - -```js -// Hello World is translated - - Hello World - -``` - -#### Rule Configuration{#rule-no-untranslated-text-configuration} - -Accepted fields: - - - -| Option | Type | Default | Description | -| --- | --- | --- | --- | -| `ignoreStrings` | `string[]` | `['·', '—', '×']` | The strings to be ignored. | - - - -#### When Not To Use It{#rule-no-untranslated-text-when-not-to-use} - -If you're not using the [i18n feature](https://docusaurus.io/docs/i18n/introduction) then you can disable this rule. - -#### Further Reading{#rule-no-untranslated-text-further-reading} - -- https://docusaurus.io/docs/docusaurus-core#translate -- https://docusaurus.io/docs/docusaurus-core#translate-1 - -## Example configuration - -Here's an example configuration: - -```js title="[.eslintrc.js]" -module.exports = { - extends: ['plugin:@docusaurus/all'], - plugins: ['@docusaurus'], - rules: { - '@docusaurus/no-untranslated-text': [ - 'warn', - {ignoreStrings: ['·', '—', '×']}, - ], - }, -}; -``` diff --git a/website/docs/api/plugins/overview.md b/website/docs/api/plugins/overview.md index 6a2e53416b2d..31897e73e682 100644 --- a/website/docs/api/plugins/overview.md +++ b/website/docs/api/plugins/overview.md @@ -27,9 +27,3 @@ These plugins will add a useful behavior to your Docusaurus site. - [@docusaurus/plugin-ideal-image](./plugin-ideal-image.md) - [@docusaurus/plugin-google-analytics](./plugin-google-analytics.md) - [@docusaurus/plugin-google-gtag](./plugin-google-gtag.md) - -## Development plugins {#dev-plugins} - -These plugins are useful for developing a site with Docusaurus. - -- [@docusaurus/eslint-plugin](./eslint-plugin.md) diff --git a/website/docs/api/plugins/plugin-google-analytics.md b/website/docs/api/plugins/plugin-google-analytics.md index 16b70a7089a1..da8f938732c9 100644 --- a/website/docs/api/plugins/plugin-google-analytics.md +++ b/website/docs/api/plugins/plugin-google-analytics.md @@ -1,5 +1,5 @@ --- -sidebar_position: 7 +sidebar_position: 6 id: plugin-google-analytics title: '📦 plugin-google-analytics' slug: '/api/plugins/@docusaurus/plugin-google-analytics' diff --git a/website/docs/api/plugins/plugin-google-gtag.md b/website/docs/api/plugins/plugin-google-gtag.md index f1b1a62afef3..c56343c14b0d 100644 --- a/website/docs/api/plugins/plugin-google-gtag.md +++ b/website/docs/api/plugins/plugin-google-gtag.md @@ -1,5 +1,5 @@ --- -sidebar_position: 8 +sidebar_position: 7 id: plugin-google-gtag title: '📦 plugin-google-gtag' slug: '/api/plugins/@docusaurus/plugin-google-gtag' diff --git a/website/docs/api/plugins/plugin-ideal-image.md b/website/docs/api/plugins/plugin-ideal-image.md index ab8c2cf4c49c..db40b29d7d7f 100644 --- a/website/docs/api/plugins/plugin-ideal-image.md +++ b/website/docs/api/plugins/plugin-ideal-image.md @@ -1,5 +1,5 @@ --- -sidebar_position: 9 +sidebar_position: 8 id: plugin-ideal-image title: '📦 plugin-ideal-image' slug: '/api/plugins/@docusaurus/plugin-ideal-image' diff --git a/website/docs/api/plugins/plugin-pwa.md b/website/docs/api/plugins/plugin-pwa.md index f9e25674c340..1a7ab67bbe01 100644 --- a/website/docs/api/plugins/plugin-pwa.md +++ b/website/docs/api/plugins/plugin-pwa.md @@ -1,5 +1,5 @@ --- -sidebar_position: 10 +sidebar_position: 9 id: plugin-pwa title: '📦 plugin-pwa' slug: '/api/plugins/@docusaurus/plugin-pwa' diff --git a/website/docs/api/plugins/plugin-sitemap.md b/website/docs/api/plugins/plugin-sitemap.md index 39956e9e76fe..c8aec5da3079 100644 --- a/website/docs/api/plugins/plugin-sitemap.md +++ b/website/docs/api/plugins/plugin-sitemap.md @@ -1,5 +1,5 @@ --- -sidebar_position: 11 +sidebar_position: 10 id: plugin-sitemap title: '📦 plugin-sitemap' slug: '/api/plugins/@docusaurus/plugin-sitemap' diff --git a/website/docs/api/themes/_category_.yml b/website/docs/api/themes/_category_.yml index a0ceda5d5956..e50cf03368fb 100644 --- a/website/docs/api/themes/_category_.yml +++ b/website/docs/api/themes/_category_.yml @@ -1,5 +1,5 @@ label: Themes -position: 3 +position: 4 link: type: doc id: themes-overview # Dogfood using a "local id" From 05e2fedb16f076b650776e34449519449a2c9c75 Mon Sep 17 00:00:00 2001 From: Elias Papavasileiou Date: Thu, 21 Apr 2022 21:11:15 +0300 Subject: [PATCH 12/19] fix: update README link --- packages/eslint-plugin/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index 30536fb852ae..294068e505ce 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -4,4 +4,4 @@ Docusaurus specific linting rules for eslint. ## Usage -See [eslint-plugin documentation](https://docusaurus.io/docs/api/plugins/@docusaurus/eslint-plugin). +See [eslint-plugin documentation](https://docusaurus.io/docs/api/misc/@docusaurus/eslint-plugin). From 492615f3221e89170d8fd638fcd4a9d70b98d6c9 Mon Sep 17 00:00:00 2001 From: Elias Papavasileiou Date: Fri, 22 Apr 2022 11:21:33 +0300 Subject: [PATCH 13/19] docs: various updates - Reorder sidebar entries - Fix title size - Use Markdown file paths - Simplify relative links --- website/docs/api/misc/_category_.yml | 2 +- website/docs/api/misc/eslint-plugin/README.md | 8 ++++---- .../docs/api/misc/eslint-plugin/no-untranslated-text.md | 8 +++++--- .../misc/eslint-plugin/string-literal-i18n-messages.md | 4 +++- website/docs/api/plugin-methods/_category_.yml | 2 +- website/docs/api/plugins/_category_.yml | 2 +- website/docs/api/themes/_category_.yml | 2 +- 7 files changed, 16 insertions(+), 12 deletions(-) diff --git a/website/docs/api/misc/_category_.yml b/website/docs/api/misc/_category_.yml index 5d91a13ac27e..2fb307376467 100644 --- a/website/docs/api/misc/_category_.yml +++ b/website/docs/api/misc/_category_.yml @@ -1,2 +1,2 @@ label: Miscellaneous -position: 1 +position: 4 diff --git a/website/docs/api/misc/eslint-plugin/README.md b/website/docs/api/misc/eslint-plugin/README.md index 3e986ebbd8ae..df6140fdd228 100644 --- a/website/docs/api/misc/eslint-plugin/README.md +++ b/website/docs/api/misc/eslint-plugin/README.md @@ -48,15 +48,15 @@ Each config contains a set of rules. For more fine-grained control, you can also | Name | Rules | | :-: | --- | -| recommended | [`@docusaurus/string-literal-i18n-messages`](./eslint-plugin/string-literal-i18n-messages) | -| all | [`@docusaurus/no-untranslated-text`](./eslint-plugin/no-untranslated-text)
[`@docusaurus/string-literal-i18n-messages`](./eslint-plugin/string-literal-i18n-messages) | +| recommended | [`@docusaurus/string-literal-i18n-messages`](./string-literal-i18n-messages.md) | +| all | [`@docusaurus/no-untranslated-text`](./no-untranslated-text.md)
[`@docusaurus/string-literal-i18n-messages`](./string-literal-i18n-messages.md) | ## Supported Rules | Name | Description | | --- | --- | -| [`@docusaurus/no-untranslated-text`](./eslint-plugin/no-untranslated-text) | Enforce text labels in JSX to be wrapped by translate calls | -| [`@docusaurus/string-literal-i18n-messages`](./eslint-plugin/string-literal-i18n-messages) | Enforce translate calls to be plain text labels | +| [`@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 calls to be plain text labels | ## Example configuration diff --git a/website/docs/api/misc/eslint-plugin/no-untranslated-text.md b/website/docs/api/misc/eslint-plugin/no-untranslated-text.md index da3c0cac8af6..243a8073f811 100644 --- a/website/docs/api/misc/eslint-plugin/no-untranslated-text.md +++ b/website/docs/api/misc/eslint-plugin/no-untranslated-text.md @@ -2,11 +2,13 @@ slug: '/api/misc/@docusaurus/eslint-plugin/no-untranslated-text' --- -enforce text labels in JSX to be wrapped by translate calls (no-untranslated-text) +# no-untranslated-text + +# enforce text labels in JSX to be wrapped by translate calls Ensures that all text labels in JSX are wrapped by `` components. -When the [i18n feature](https://docusaurus.io/docs/i18n/introduction) is used, this rule is to ensure that all strings appearing on the website are being translated, so no string accidentally slips through untranslated. +When the [i18n feature](../../../i18n/i18n-introduction.md) is used, this rule is to ensure that all strings appearing on the website are being translated, so no string accidentally slips through untranslated. ## Rule Details {#details} @@ -40,7 +42,7 @@ Accepted fields: ## When Not To Use It {#when-not-to-use} -If you're not using the [i18n feature](https://docusaurus.io/docs/i18n/introduction) then you can disable this rule. +If you're not using the [i18n feature](../../../i18n/i18n-introduction.md) then you can disable this rule. ## Further Reading {#further-reading} diff --git a/website/docs/api/misc/eslint-plugin/string-literal-i18n-messages.md b/website/docs/api/misc/eslint-plugin/string-literal-i18n-messages.md index 1a694c42e993..9b97be29b84e 100644 --- a/website/docs/api/misc/eslint-plugin/string-literal-i18n-messages.md +++ b/website/docs/api/misc/eslint-plugin/string-literal-i18n-messages.md @@ -2,7 +2,9 @@ slug: '/api/misc/@docusaurus/eslint-plugin/string-literal-i18n-messages' --- -enforce translate calls to be plain text labels (string-literal-i18n-messages) +# string-literal-i18n-messages + +# enforce translate calls to be plain text labels Ensures that `` children and the message attribute of `translate` function calls are hardcoded strings. diff --git a/website/docs/api/plugin-methods/_category_.yml b/website/docs/api/plugin-methods/_category_.yml index f7c2e4636455..86cb36c24614 100644 --- a/website/docs/api/plugin-methods/_category_.yml +++ b/website/docs/api/plugin-methods/_category_.yml @@ -1,2 +1,2 @@ label: Plugin method references -position: 2 +position: 1 diff --git a/website/docs/api/plugins/_category_.yml b/website/docs/api/plugins/_category_.yml index b7555fd25af0..cffabddbd5db 100644 --- a/website/docs/api/plugins/_category_.yml +++ b/website/docs/api/plugins/_category_.yml @@ -1,5 +1,5 @@ label: Plugins -position: 3 +position: 2 link: type: doc id: api/plugins/plugins-overview # Dogfood using a "qualified id" diff --git a/website/docs/api/themes/_category_.yml b/website/docs/api/themes/_category_.yml index e50cf03368fb..a0ceda5d5956 100644 --- a/website/docs/api/themes/_category_.yml +++ b/website/docs/api/themes/_category_.yml @@ -1,5 +1,5 @@ label: Themes -position: 4 +position: 3 link: type: doc id: themes-overview # Dogfood using a "local id" From 35ef3b2f411d174141db57b746de1af24f9b8af3 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Fri, 29 Apr 2022 21:15:29 +0800 Subject: [PATCH 14/19] address reviews --- website/docs/api/misc/eslint-plugin/README.md | 2 +- website/docs/api/misc/eslint-plugin/no-untranslated-text.md | 2 +- .../docs/api/misc/eslint-plugin/string-literal-i18n-messages.md | 2 +- website/docs/docusaurus-core.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/website/docs/api/misc/eslint-plugin/README.md b/website/docs/api/misc/eslint-plugin/README.md index 27bba6268f9d..fe748173b4b7 100644 --- a/website/docs/api/misc/eslint-plugin/README.md +++ b/website/docs/api/misc/eslint-plugin/README.md @@ -7,7 +7,7 @@ slug: '/api/misc/@docusaurus/eslint-plugin' import APITable from '@site/src/components/APITable'; -Docusaurus eslint plugin to ensure best Docusaurus practices. +[ESLint](https://eslint.org/) is a tool that statically analyzes your code and reports problems or suggests best practices through editor hints and command line. Docusaurus provides an ESLint plugin to enforce best Docusaurus practices. ## Installation diff --git a/website/docs/api/misc/eslint-plugin/no-untranslated-text.md b/website/docs/api/misc/eslint-plugin/no-untranslated-text.md index 73a867084489..4943d48fd4c1 100644 --- a/website/docs/api/misc/eslint-plugin/no-untranslated-text.md +++ b/website/docs/api/misc/eslint-plugin/no-untranslated-text.md @@ -45,4 +45,4 @@ If you're not using the [i18n feature](../../../i18n/i18n-introduction.md) then ## Further Reading {#further-reading} - https://docusaurus.io/docs/docusaurus-core#translate -- https://docusaurus.io/docs/docusaurus-core#translate-1 +- https://docusaurus.io/docs/docusaurus-core#translate-imperative diff --git a/website/docs/api/misc/eslint-plugin/string-literal-i18n-messages.md b/website/docs/api/misc/eslint-plugin/string-literal-i18n-messages.md index 423a8677571a..a66bcc09e378 100644 --- a/website/docs/api/misc/eslint-plugin/string-literal-i18n-messages.md +++ b/website/docs/api/misc/eslint-plugin/string-literal-i18n-messages.md @@ -43,4 +43,4 @@ translate({message: 'The logo of site {siteName}'}, {siteName: 'Docusaurus'}) ## Further Reading {#further-reading} - https://docusaurus.io/docs/docusaurus-core#translate -- https://docusaurus.io/docs/docusaurus-core#translate-1 +- https://docusaurus.io/docs/docusaurus-core#translate-imperative diff --git a/website/docs/docusaurus-core.md b/website/docs/docusaurus-core.md index 63be189238f1..372c1a001fe9 100644 --- a/website/docs/docusaurus-core.md +++ b/website/docs/docusaurus-core.md @@ -621,7 +621,7 @@ import {interpolate} from '@docusaurus/Interpolate'; const message = interpolate('Welcome {firstName}', {firstName: 'Sébastien'}); ``` -### `translate` {#translate-1} +### `translate` {#translate-imperative} The imperative counterpart of the [``](#translate) component. Also supporting [placeholders interpolation](#interpolate). From beabcf47dff8e6ff104b716d07d5266c9bcc3a61 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Fri, 29 Apr 2022 21:21:57 +0800 Subject: [PATCH 15/19] wording polish --- website/docs/api/misc/eslint-plugin/no-untranslated-text.md | 4 ++-- .../api/misc/eslint-plugin/string-literal-i18n-messages.md | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/website/docs/api/misc/eslint-plugin/no-untranslated-text.md b/website/docs/api/misc/eslint-plugin/no-untranslated-text.md index 4943d48fd4c1..1032207d6d0a 100644 --- a/website/docs/api/misc/eslint-plugin/no-untranslated-text.md +++ b/website/docs/api/misc/eslint-plugin/no-untranslated-text.md @@ -6,7 +6,7 @@ slug: '/api/misc/@docusaurus/eslint-plugin/no-untranslated-text' Enforce text labels in JSX to be wrapped by translate calls. -When the [i18n feature](../../../i18n/i18n-introduction.md) is used, this rule is to ensure that all strings appearing on the website are being translated, so no string accidentally slips through untranslated. +When the [i18n feature](../../../i18n/i18n-introduction.md) is used, this rule ensures that all labels appearing on the website are translatable, so no string accidentally slips through untranslated. ## Rule Details {#details} @@ -40,7 +40,7 @@ Accepted fields: ## When Not To Use It {#when-not-to-use} -If you're not using the [i18n feature](../../../i18n/i18n-introduction.md) then you can disable this rule. +If you're not using the [i18n feature](../../../i18n/i18n-introduction.md), you can disable this rule. You can also disable this rule where the text is not supposed to be translated. ## Further Reading {#further-reading} diff --git a/website/docs/api/misc/eslint-plugin/string-literal-i18n-messages.md b/website/docs/api/misc/eslint-plugin/string-literal-i18n-messages.md index a66bcc09e378..9829a4d89bf7 100644 --- a/website/docs/api/misc/eslint-plugin/string-literal-i18n-messages.md +++ b/website/docs/api/misc/eslint-plugin/string-literal-i18n-messages.md @@ -6,7 +6,7 @@ slug: '/api/misc/@docusaurus/eslint-plugin/string-literal-i18n-messages' Enforce translate APIs to be called on plain text labels. -This is to ensure that static extraction of the text will work so it can be translatable. In-string dynamic placeholders are also possible using the values object. +Docusaurus offers the [`docusaurus write-translations`](../../../cli.md#docusaurus-write-translations-sitedir) API, which statically extracts the text labels marked as translatable. Dynamic values used in `` or `translate()` calls will fail to be extracted. This rule will ensure that all translate calls are statically extractable. ## Rule Details {#details} @@ -40,6 +40,10 @@ translate({message: 'Some text to be translated'}) translate({message: 'The logo of site {siteName}'}, {siteName: 'Docusaurus'}) ``` +## When Not To Use It {#when-not-to-use} + +If you're not using the [i18n feature](../../../i18n/i18n-introduction.md), you can disable this rule. + ## Further Reading {#further-reading} - https://docusaurus.io/docs/docusaurus-core#translate From ecdc3bd3a9f61fc29915f671617eed6346082b62 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Fri, 29 Apr 2022 21:36:47 +0800 Subject: [PATCH 16/19] add npmignore --- packages/eslint-plugin/.npmignore | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 packages/eslint-plugin/.npmignore diff --git a/packages/eslint-plugin/.npmignore b/packages/eslint-plugin/.npmignore new file mode 100644 index 000000000000..5f1c98c31f64 --- /dev/null +++ b/packages/eslint-plugin/.npmignore @@ -0,0 +1,6 @@ +copyUntypedFiles.mjs +.tsbuildinfo +tsconfig* +__tests__ + +.eslintrc.js From d1d9add9f509529198bbd28564bc23f2861e575a Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Fri, 29 Apr 2022 23:17:27 +0800 Subject: [PATCH 17/19] fix all internal warnings --- .eslintrc.js | 26 ++++- .../src/theme/LastUpdated/index.tsx | 1 + .../src/theme/Playground/index.tsx | 1 + .../src/client/theme-fallback/Error/index.tsx | 3 + .../client/theme-fallback/Loading/index.tsx | 3 + .../client/theme-fallback/NotFound/index.tsx | 3 + packages/eslint-plugin/lib/util.js | 4 +- .../src/components/ColorGenerator/index.tsx | 1 + website/src/components/UpgradeGuide/index.tsx | 1 + website/src/data/tweets.tsx | 2 + website/src/pages/index.tsx | 21 +++-- .../ShowcaseFilterToggle/index.tsx | 3 +- website/src/pages/versions.tsx | 10 +- .../changelog/theme/ChangelogList/index.tsx | 94 +++++++++++-------- .../changelog/theme/ChangelogPage/index.tsx | 5 +- .../theme/DocSidebar/Desktop/Content/index.js | 1 + 16 files changed, 126 insertions(+), 53 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 3aad774e9c46..bd36120efdef 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -315,7 +315,21 @@ module.exports = { '@typescript-eslint/prefer-optional-chain': ERROR, '@docusaurus/no-untranslated-text': [ WARNING, - {ignoreStrings: ['·', '-', '—', "'", '"', '×', '@', '🏠', '​']}, + { + ignoreStrings: [ + '·', + '-', + '—', + '×', + '​', + '@', + 'WebContainers', + 'Twitter', + 'GitHub', + 'Dev.to', + '1.x', + ], + }, ], }, overrides: [ @@ -363,6 +377,16 @@ module.exports = { '@typescript-eslint/explicit-module-boundary-types': OFF, }, }, + { + files: [ + '**/__tests__/**', + 'packages/docusaurus-plugin-debug/**', + 'website/_dogfooding/**', + ], + rules: { + '@docusaurus/no-untranslated-text': OFF, + }, + }, { // Internal files where extraneous deps don't matter much at long as // they run diff --git a/packages/docusaurus-theme-classic/src/theme/LastUpdated/index.tsx b/packages/docusaurus-theme-classic/src/theme/LastUpdated/index.tsx index 42e6d478fbdd..57937216285f 100644 --- a/packages/docusaurus-theme-classic/src/theme/LastUpdated/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/LastUpdated/index.tsx @@ -82,6 +82,7 @@ export default function LastUpdated({ {process.env.NODE_ENV === 'development' && (
+ {/* eslint-disable-next-line @docusaurus/no-untranslated-text */} (Simulated during dev for better perf)
)} diff --git a/packages/docusaurus-theme-live-codeblock/src/theme/Playground/index.tsx b/packages/docusaurus-theme-live-codeblock/src/theme/Playground/index.tsx index 62a11ab6ae2c..7ca4c62f326d 100644 --- a/packages/docusaurus-theme-live-codeblock/src/theme/Playground/index.tsx +++ b/packages/docusaurus-theme-live-codeblock/src/theme/Playground/index.tsx @@ -23,6 +23,7 @@ function Header({children}: {children: React.ReactNode}) { function LivePreviewLoader() { // Is it worth improving/translating? + // eslint-disable-next-line @docusaurus/no-untranslated-text return
Loading...
; } diff --git a/packages/docusaurus/src/client/theme-fallback/Error/index.tsx b/packages/docusaurus/src/client/theme-fallback/Error/index.tsx index de8ded15b7df..3376d906eaee 100644 --- a/packages/docusaurus/src/client/theme-fallback/Error/index.tsx +++ b/packages/docusaurus/src/client/theme-fallback/Error/index.tsx @@ -5,6 +5,9 @@ * LICENSE file in the root directory of this source tree. */ +// Should we translate theme-fallback? +/* eslint-disable @docusaurus/no-untranslated-text */ + import React from 'react'; import Layout from '@theme/Layout'; import ErrorBoundary from '@docusaurus/ErrorBoundary'; diff --git a/packages/docusaurus/src/client/theme-fallback/Loading/index.tsx b/packages/docusaurus/src/client/theme-fallback/Loading/index.tsx index 761c9c3a77b5..79a0823c3194 100644 --- a/packages/docusaurus/src/client/theme-fallback/Loading/index.tsx +++ b/packages/docusaurus/src/client/theme-fallback/Loading/index.tsx @@ -5,6 +5,9 @@ * LICENSE file in the root directory of this source tree. */ +// Should we translate theme-fallback? +/* eslint-disable @docusaurus/no-untranslated-text */ + import React from 'react'; import type {LoadingComponentProps} from 'react-loadable'; diff --git a/packages/docusaurus/src/client/theme-fallback/NotFound/index.tsx b/packages/docusaurus/src/client/theme-fallback/NotFound/index.tsx index 15ccfaefe6ab..78f83dcc2c05 100644 --- a/packages/docusaurus/src/client/theme-fallback/NotFound/index.tsx +++ b/packages/docusaurus/src/client/theme-fallback/NotFound/index.tsx @@ -5,6 +5,9 @@ * LICENSE file in the root directory of this source tree. */ +// Should we translate theme-fallback? +/* eslint-disable @docusaurus/no-untranslated-text */ + import React from 'react'; import Layout from '@theme/Layout'; import Head from '@docusaurus/Head'; diff --git a/packages/eslint-plugin/lib/util.js b/packages/eslint-plugin/lib/util.js index 19dbf93cebbc..388cbb08196a 100644 --- a/packages/eslint-plugin/lib/util.js +++ b/packages/eslint-plugin/lib/util.js @@ -28,7 +28,7 @@ const isStringWithoutExpressions = ({ } = {}) => { switch (text.type) { case 'Literal': - return isTextValid({text: text.raw, ignoreWhitespace, stringsToIgnore}); + return isTextValid({text: text.value, ignoreWhitespace, stringsToIgnore}); case 'TemplateLiteral': return ( !text.expressions.length && @@ -51,7 +51,7 @@ const isTextLabelChild = ({ switch (child.type) { case 'JSXText': return isTextValid({ - text: child.raw, + text: child.value, ignoreWhitespace, stringsToIgnore, }); diff --git a/website/src/components/ColorGenerator/index.tsx b/website/src/components/ColorGenerator/index.tsx index aedaf469e2bc..0ce0d508e358 100644 --- a/website/src/components/ColorGenerator/index.tsx +++ b/website/src/components/ColorGenerator/index.tsx @@ -279,6 +279,7 @@ export default function ColorGenerator(): JSX.Element {

src/css/custom.css}}> {'Replace the variables in {cssPath} with these new variables.'} diff --git a/website/src/components/UpgradeGuide/index.tsx b/website/src/components/UpgradeGuide/index.tsx index baf895fc775b..0ba1913aae74 100644 --- a/website/src/components/UpgradeGuide/index.tsx +++ b/website/src/components/UpgradeGuide/index.tsx @@ -82,6 +82,7 @@ function VersionNotice() { @canary}}> {'{canaryTag} release'} diff --git a/website/src/data/tweets.tsx b/website/src/data/tweets.tsx index 0a018cada6b2..1e6dd02ba46d 100644 --- a/website/src/data/tweets.tsx +++ b/website/src/data/tweets.tsx @@ -5,6 +5,8 @@ * LICENSE file in the root directory of this source tree. */ +/* eslint-disable @docusaurus/no-untranslated-text */ + import React from 'react'; import type {Props as Tweet} from '../components/Tweet'; diff --git a/website/src/pages/index.tsx b/website/src/pages/index.tsx index 02b5780aace6..86de91b0118a 100644 --- a/website/src/pages/index.tsx +++ b/website/src/pages/index.tsx @@ -90,9 +90,8 @@ function MigrationAnnouncement() { ), }}> - {`Coming from {docusaurusV1Link}? Check out our {migrationGuideLink}`} + {`Coming from {docusaurusV1Link}? Check out our {migrationGuideLink}.`} - . ); @@ -241,11 +240,19 @@ export default function Home(): JSX.Element {

- Support Ukraine 🇺🇦{' '} - - Help Provide Humanitarian Aid to Ukraine - - . + + + Help Provide Humanitarian Aid to Ukraine + + + ), + }}> + {'Support Ukraine 🇺🇦 {link}.'} +
diff --git a/website/src/pages/showcase/_components/ShowcaseFilterToggle/index.tsx b/website/src/pages/showcase/_components/ShowcaseFilterToggle/index.tsx index 931128ff7f01..8df064852eec 100644 --- a/website/src/pages/showcase/_components/ShowcaseFilterToggle/index.tsx +++ b/website/src/pages/showcase/_components/ShowcaseFilterToggle/index.tsx @@ -59,10 +59,11 @@ export default function ShowcaseFilterToggle(): JSX.Element { }} checked={operator} /> - {/* eslint-disable-next-line jsx-a11y/label-has-associated-control */} ); diff --git a/website/src/pages/versions.tsx b/website/src/pages/versions.tsx index 3a66f2307414..c9239d28edb6 100644 --- a/website/src/pages/versions.tsx +++ b/website/src/pages/versions.tsx @@ -170,9 +170,15 @@ export default function Version(): JSX.Element { )}
-

Docusaurus v1 (Legacy)

+

+ + Docusaurus v1 (Legacy) + +

- Here you can find documentation for legacy version of Docusaurus. + + Here you can find documentation for legacy version of Docusaurus. +

diff --git a/website/src/plugins/changelog/theme/ChangelogList/index.tsx b/website/src/plugins/changelog/theme/ChangelogList/index.tsx index f05400ab5756..cf0d7b7297a3 100644 --- a/website/src/plugins/changelog/theme/ChangelogList/index.tsx +++ b/website/src/plugins/changelog/theme/ChangelogList/index.tsx @@ -8,6 +8,7 @@ import React from 'react'; import BlogLayout from '@theme/BlogLayout'; import BlogListPaginator from '@theme/BlogListPaginator'; +import Translate from '@docusaurus/Translate'; import type {Props} from '@theme/BlogListPage'; import { PageMetadata, @@ -41,45 +42,60 @@ function ChangelogListContent(props: Props): JSX.Element {

{blogTitle}

- Subscribe through{' '} - - RSS feeds - - - - {' '} - or follow us on{' '} - - Twitter - - - - {' '} - to stay up-to-date with new releases! + + Twitter + + + + + ), + rssLink: ( + + + + RSS feeds + + + + + + + ), + }}> + { + 'Subscribe through {rssLink} or follow us on {twitterLink} to stay up-to-date with new releases!' + } +

{items.map(({content: BlogPostContent}) => ( diff --git a/website/src/plugins/changelog/theme/ChangelogPage/index.tsx b/website/src/plugins/changelog/theme/ChangelogPage/index.tsx index 21aaf20dcbd4..ef45aa304e6b 100644 --- a/website/src/plugins/changelog/theme/ChangelogPage/index.tsx +++ b/website/src/plugins/changelog/theme/ChangelogPage/index.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import Translate from '@docusaurus/Translate'; import BlogLayout from '@theme/BlogLayout'; import ChangelogItem from '@theme/ChangelogItem'; import ChangelogPaginator from '@theme/ChangelogPaginator'; @@ -86,7 +87,9 @@ function ChangelogPageContent(props: Props): JSX.Element { /> ) : undefined }> - ← Back to index page + + ← Back to index page + Sidebar Ad From 50f0de4fa358527bcbf48301dd06bfb67212e90e Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Fri, 29 Apr 2022 23:26:01 +0800 Subject: [PATCH 18/19] doc improvements --- packages/eslint-plugin/README.md | 2 +- packages/eslint-plugin/package.json | 2 +- website/docs/api/misc/eslint-plugin/README.md | 28 +++++++++---------- .../eslint-plugin/no-untranslated-text.md | 6 ++-- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index 294068e505ce..842c2dd41410 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -1,6 +1,6 @@ # `@docusaurus/eslint-plugin` -Docusaurus specific linting rules for eslint. +ESLint plugin to enforce best Docusaurus practices. ## Usage diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 44dfe11043a4..3fb79ea135b4 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,7 +1,7 @@ { "name": "@docusaurus/eslint-plugin", "version": "2.0.0-beta.18", - "description": "Docusaurus specific linting rules for eslint", + "description": "ESLint plugin to enforce best Docusaurus practices.", "main": "lib/index.js", "keywords": [ "eslint", diff --git a/website/docs/api/misc/eslint-plugin/README.md b/website/docs/api/misc/eslint-plugin/README.md index fe748173b4b7..c42ba573e722 100644 --- a/website/docs/api/misc/eslint-plugin/README.md +++ b/website/docs/api/misc/eslint-plugin/README.md @@ -5,8 +5,6 @@ title: '📦 eslint-plugin' slug: '/api/misc/@docusaurus/eslint-plugin' --- -import APITable from '@site/src/components/APITable'; - [ESLint](https://eslint.org/) is a tool that statically analyzes your code and reports problems or suggests best practices through editor hints and command line. Docusaurus provides an ESLint plugin to enforce best Docusaurus practices. ## Installation @@ -19,7 +17,7 @@ npm install --save-dev @docusaurus/eslint-plugin Add `@docusaurus` to the plugins section of your `.eslintrc` configuration file: -```json +```json title=".eslintrc" { "plugins": ["@docusaurus"] } @@ -27,7 +25,7 @@ Add `@docusaurus` to the plugins section of your `.eslintrc` configuration file: Then, you can extend one of the configs (e.g. the `recommended` config): -```json +```json title=".eslintrc" { "extends": ["plugin:@docusaurus/recommended"] } @@ -35,7 +33,7 @@ Then, you can extend one of the configs (e.g. the `recommended` config): Each config contains a set of rules. For more fine-grained control, you can also configure the rules you want to use directly: -```json +```json title=".eslintrc" { "rules": { "@docusaurus/string-literal-i18n-messages": "error", @@ -46,25 +44,25 @@ Each config contains a set of rules. For more fine-grained control, you can also ## Supported Configs -| Name | Rules | -| :-: | --- | -| recommended | [`@docusaurus/string-literal-i18n-messages`](./string-literal-i18n-messages.md) | -| all | [`@docusaurus/no-untranslated-text`](./no-untranslated-text.md)
[`@docusaurus/string-literal-i18n-messages`](./string-literal-i18n-messages.md) | +- Recommended: recommended rule set for most Docusaurus sites that should be extended from. +- All: **all** rules enabled. This will change between minor versions, so you should not use this if you want to avoid unexpected breaking changes. ## Supported Rules -| Name | Description | -| --- | --- | -| [`@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 | +| Name | Description | | +| --- | --- | --- | +| [`@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 | ✅ | + +✅ = recommended ## Example configuration Here's an example configuration: -```js title="[.eslintrc.js]" +```js title=".eslintrc.js" module.exports = { - extends: ['plugin:@docusaurus/all'], + extends: ['plugin:@docusaurus/recommended'], plugins: ['@docusaurus'], rules: { '@docusaurus/no-untranslated-text': [ diff --git a/website/docs/api/misc/eslint-plugin/no-untranslated-text.md b/website/docs/api/misc/eslint-plugin/no-untranslated-text.md index 1032207d6d0a..66dbc2d76497 100644 --- a/website/docs/api/misc/eslint-plugin/no-untranslated-text.md +++ b/website/docs/api/misc/eslint-plugin/no-untranslated-text.md @@ -32,9 +32,9 @@ Accepted fields: -| Option | Type | Default | Description | -| --------------- | ---------- | ------- | -------------------------- | -| `ignoreStrings` | `string[]` | `[]` | The strings to be ignored. | +| Option | Type | Default | Description | +| --- | --- | --- | --- | +| `ignoreStrings` | `string[]` | `[]` | Text labels that only contain strings in this list will not be reported. | From 40cfd23ca5e97a55da2a7ae2a19ec4254a121afa Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Fri, 29 Apr 2022 23:28:36 +0800 Subject: [PATCH 19/19] fix test --- .eslintrc.js | 2 +- .../lib/rules/__tests__/no-untranslated-text.test.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index bd36120efdef..110a6d688601 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -321,7 +321,7 @@ module.exports = { '-', '—', '×', - '​', + '​', // zwj: ​ '@', 'WebContainers', 'Twitter', diff --git a/packages/eslint-plugin/lib/rules/__tests__/no-untranslated-text.test.js b/packages/eslint-plugin/lib/rules/__tests__/no-untranslated-text.test.js index 61a18cf2be58..12e83856bc59 100644 --- a/packages/eslint-plugin/lib/rules/__tests__/no-untranslated-text.test.js +++ b/packages/eslint-plugin/lib/rules/__tests__/no-untranslated-text.test.js @@ -45,11 +45,11 @@ ruleTester.run('no-untranslated-text', rule, { }, { code: '{"·"}', - options: [{ignoreStrings: ['"·"']}], + options: [{ignoreStrings: ['·']}], }, { code: "{'·'}", - options: [{ignoreStrings: ["'·'"]}], + options: [{ignoreStrings: ['·']}], }, { code: '{`·`}', @@ -61,7 +61,7 @@ ruleTester.run('no-untranslated-text', rule, { }, { code: '', - options: [{ignoreStrings: ['​']}], + options: [{ignoreStrings: ['​']}], }, { code: `<>