@docusaurus/eslint-plugin 2.4.1 → 3.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.d.ts CHANGED
@@ -10,12 +10,18 @@ declare const _default: {
10
10
  ignoredStrings: string[];
11
11
  }], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
12
12
  'string-literal-i18n-messages': import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"translateChildren" | "translateArg", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
13
+ 'no-html-links': import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"link", [{
14
+ ignoreFullyResolved: boolean;
15
+ }], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
16
+ 'prefer-docusaurus-heading': import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"headings", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
13
17
  };
14
18
  configs: {
15
19
  recommended: {
16
20
  plugins: string[];
17
21
  rules: {
18
22
  '@docusaurus/string-literal-i18n-messages': string;
23
+ '@docusaurus/no-html-links': string;
24
+ '@docusaurus/prefer-docusaurus-heading': string;
19
25
  };
20
26
  };
21
27
  all: {
@@ -23,6 +29,8 @@ declare const _default: {
23
29
  rules: {
24
30
  '@docusaurus/string-literal-i18n-messages': string;
25
31
  '@docusaurus/no-untranslated-text': string;
32
+ '@docusaurus/no-html-links': string;
33
+ '@docusaurus/prefer-docusaurus-heading': string;
26
34
  };
27
35
  };
28
36
  };
package/lib/index.js CHANGED
@@ -14,6 +14,8 @@ module.exports = {
14
14
  plugins: ['@docusaurus'],
15
15
  rules: {
16
16
  '@docusaurus/string-literal-i18n-messages': 'error',
17
+ '@docusaurus/no-html-links': 'warn',
18
+ '@docusaurus/prefer-docusaurus-heading': 'warn',
17
19
  },
18
20
  },
19
21
  all: {
@@ -21,6 +23,8 @@ module.exports = {
21
23
  rules: {
22
24
  '@docusaurus/string-literal-i18n-messages': 'error',
23
25
  '@docusaurus/no-untranslated-text': 'warn',
26
+ '@docusaurus/no-html-links': 'warn',
27
+ '@docusaurus/prefer-docusaurus-heading': 'warn',
24
28
  },
25
29
  },
26
30
  },
@@ -9,5 +9,9 @@ declare const _default: {
9
9
  ignoredStrings: string[];
10
10
  }], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
11
11
  'string-literal-i18n-messages': import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"translateChildren" | "translateArg", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
12
+ 'no-html-links': import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"link", [{
13
+ ignoreFullyResolved: boolean;
14
+ }], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
15
+ 'prefer-docusaurus-heading': import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"headings", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
12
16
  };
13
17
  export default _default;
@@ -7,9 +7,13 @@
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  const tslib_1 = require("tslib");
10
+ const no_html_links_1 = tslib_1.__importDefault(require("./no-html-links"));
11
+ const prefer_docusaurus_heading_1 = tslib_1.__importDefault(require("./prefer-docusaurus-heading"));
10
12
  const no_untranslated_text_1 = tslib_1.__importDefault(require("./no-untranslated-text"));
11
13
  const string_literal_i18n_messages_1 = tslib_1.__importDefault(require("./string-literal-i18n-messages"));
12
14
  exports.default = {
13
15
  'no-untranslated-text': no_untranslated_text_1.default,
14
16
  'string-literal-i18n-messages': string_literal_i18n_messages_1.default,
17
+ 'no-html-links': no_html_links_1.default,
18
+ 'prefer-docusaurus-heading': prefer_docusaurus_heading_1.default,
15
19
  };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ type Options = [
8
+ {
9
+ ignoreFullyResolved: boolean;
10
+ }
11
+ ];
12
+ declare const _default: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"link", Options, import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
13
+ export default _default;
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Facebook, Inc. and its affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ const util_1 = require("../util");
10
+ const docsUrl = 'https://docusaurus.io/docs/docusaurus-core#link';
11
+ function isFullyResolvedUrl(urlString) {
12
+ try {
13
+ // href gets coerced to a string when it gets rendered anyway
14
+ const url = new URL(String(urlString));
15
+ if (url.protocol) {
16
+ return true;
17
+ }
18
+ }
19
+ catch (e) { }
20
+ return false;
21
+ }
22
+ exports.default = (0, util_1.createRule)({
23
+ name: 'no-html-links',
24
+ meta: {
25
+ type: 'problem',
26
+ docs: {
27
+ description: 'enforce using Docusaurus Link component instead of <a> tag',
28
+ recommended: false,
29
+ },
30
+ schema: [
31
+ {
32
+ type: 'object',
33
+ properties: {
34
+ ignoreFullyResolved: {
35
+ type: 'boolean',
36
+ },
37
+ },
38
+ additionalProperties: false,
39
+ },
40
+ ],
41
+ messages: {
42
+ link: `Do not use an \`<a>\` element to navigate. Use the \`<Link />\` component from \`@docusaurus/Link\` instead. See: ${docsUrl}`,
43
+ },
44
+ },
45
+ defaultOptions: [
46
+ {
47
+ ignoreFullyResolved: false,
48
+ },
49
+ ],
50
+ create(context, [options]) {
51
+ const { ignoreFullyResolved } = options;
52
+ return {
53
+ JSXOpeningElement(node) {
54
+ if (node.name.name !== 'a') {
55
+ return;
56
+ }
57
+ if (ignoreFullyResolved) {
58
+ const hrefAttr = node.attributes.find((attr) => attr.type === 'JSXAttribute' && attr.name.name === 'href');
59
+ if (hrefAttr?.value?.type === 'Literal') {
60
+ if (isFullyResolvedUrl(String(hrefAttr.value.value))) {
61
+ return;
62
+ }
63
+ }
64
+ if (hrefAttr?.value?.type === 'JSXExpressionContainer') {
65
+ const container = hrefAttr.value;
66
+ const { expression } = container;
67
+ if (expression.type === 'TemplateLiteral') {
68
+ // Simple static string template literals
69
+ if (expression.expressions.length === 0 &&
70
+ expression.quasis.length === 1 &&
71
+ expression.quasis[0]?.type === 'TemplateElement' &&
72
+ isFullyResolvedUrl(String(expression.quasis[0].value.raw))) {
73
+ return;
74
+ }
75
+ // TODO add more complex TemplateLiteral cases here
76
+ }
77
+ }
78
+ }
79
+ context.report({ node, messageId: 'link' });
80
+ },
81
+ };
82
+ },
83
+ });
@@ -4,7 +4,7 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
- declare type Options = [
7
+ type Options = [
8
8
  {
9
9
  ignoredStrings: string[];
10
10
  }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ declare const _default: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"headings", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
8
+ export default _default;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Facebook, Inc. and its affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ const util_1 = require("../util");
10
+ exports.default = (0, util_1.createRule)({
11
+ name: 'prefer-docusaurus-heading',
12
+ meta: {
13
+ type: 'problem',
14
+ docs: {
15
+ description: 'enforce using Docusaurus theme Heading component instead of any <hn> tag',
16
+ recommended: false,
17
+ },
18
+ schema: [],
19
+ messages: {
20
+ headings: 'Do not use any of the `<hn>` tags for headings. Use the `<Heading />` component from `@theme/Heading` instead.',
21
+ },
22
+ },
23
+ defaultOptions: [],
24
+ create(context) {
25
+ const headingTypes = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
26
+ return {
27
+ JSXOpeningElement(node) {
28
+ const elementName = node.name.name;
29
+ if (!headingTypes.includes(elementName)) {
30
+ return;
31
+ }
32
+ context.report({ node, messageId: 'headings' });
33
+ },
34
+ };
35
+ },
36
+ });
@@ -4,6 +4,6 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
- declare type MessageIds = 'translateChildren' | 'translateArg';
7
+ type MessageIds = 'translateChildren' | 'translateArg';
8
8
  declare const _default: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<MessageIds, [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
9
9
  export default _default;
package/lib/util.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import { ESLintUtils } from '@typescript-eslint/utils';
8
8
  import type { TSESTree } from '@typescript-eslint/types/dist/ts-estree';
9
- declare type CheckTranslateChildOptions = {
9
+ type CheckTranslateChildOptions = {
10
10
  ignoredStrings?: string[];
11
11
  };
12
12
  export declare function isStringWithoutExpressions(text: TSESTree.Node, options?: CheckTranslateChildOptions): boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docusaurus/eslint-plugin",
3
- "version": "2.4.1",
3
+ "version": "3.0.0-beta.0",
4
4
  "description": "ESLint plugin to enforce best Docusaurus practices.",
5
5
  "main": "lib/index.js",
6
6
  "keywords": [
@@ -21,11 +21,11 @@
21
21
  "build": "tsc"
22
22
  },
23
23
  "dependencies": {
24
- "@typescript-eslint/utils": "^5.30.5",
25
- "tslib": "^2.4.0"
24
+ "@typescript-eslint/utils": "^5.62.0",
25
+ "tslib": "^2.6.0"
26
26
  },
27
27
  "devDependencies": {
28
- "eslint-plugin-eslint-plugin": "^4.4.0"
28
+ "eslint-plugin-eslint-plugin": "^5.1.0"
29
29
  },
30
30
  "peerDependencies": {
31
31
  "eslint": ">=6"
@@ -33,5 +33,5 @@
33
33
  "engines": {
34
34
  "node": ">=16.14"
35
35
  },
36
- "gitHead": "60e657d8ae5a4a9ed1c2d777f9defd882cc12681"
36
+ "gitHead": "27a1e90d9fff88af90ecad35bea16d4d7230482a"
37
37
  }
package/src/index.ts CHANGED
@@ -14,6 +14,8 @@ export = {
14
14
  plugins: ['@docusaurus'],
15
15
  rules: {
16
16
  '@docusaurus/string-literal-i18n-messages': 'error',
17
+ '@docusaurus/no-html-links': 'warn',
18
+ '@docusaurus/prefer-docusaurus-heading': 'warn',
17
19
  },
18
20
  },
19
21
  all: {
@@ -21,6 +23,8 @@ export = {
21
23
  rules: {
22
24
  '@docusaurus/string-literal-i18n-messages': 'error',
23
25
  '@docusaurus/no-untranslated-text': 'warn',
26
+ '@docusaurus/no-html-links': 'warn',
27
+ '@docusaurus/prefer-docusaurus-heading': 'warn',
24
28
  },
25
29
  },
26
30
  },
@@ -5,10 +5,14 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
+ import noHtmlLinks from './no-html-links';
9
+ import preferDocusaurusHeading from './prefer-docusaurus-heading';
8
10
  import noUntranslatedText from './no-untranslated-text';
9
11
  import stringLiteralI18nMessages from './string-literal-i18n-messages';
10
12
 
11
13
  export default {
12
14
  'no-untranslated-text': noUntranslatedText,
13
15
  'string-literal-i18n-messages': stringLiteralI18nMessages,
16
+ 'no-html-links': noHtmlLinks,
17
+ 'prefer-docusaurus-heading': preferDocusaurusHeading,
14
18
  };
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import {createRule} from '../util';
9
+ import type {TSESTree} from '@typescript-eslint/types/dist/ts-estree';
10
+
11
+ const docsUrl = 'https://docusaurus.io/docs/docusaurus-core#link';
12
+
13
+ type Options = [
14
+ {
15
+ ignoreFullyResolved: boolean;
16
+ },
17
+ ];
18
+
19
+ type MessageIds = 'link';
20
+
21
+ function isFullyResolvedUrl(urlString: string): boolean {
22
+ try {
23
+ // href gets coerced to a string when it gets rendered anyway
24
+ const url = new URL(String(urlString));
25
+ if (url.protocol) {
26
+ return true;
27
+ }
28
+ } catch (e) {}
29
+ return false;
30
+ }
31
+
32
+ export default createRule<Options, MessageIds>({
33
+ name: 'no-html-links',
34
+ meta: {
35
+ type: 'problem',
36
+ docs: {
37
+ description: 'enforce using Docusaurus Link component instead of <a> tag',
38
+ recommended: false,
39
+ },
40
+ schema: [
41
+ {
42
+ type: 'object',
43
+ properties: {
44
+ ignoreFullyResolved: {
45
+ type: 'boolean',
46
+ },
47
+ },
48
+ additionalProperties: false,
49
+ },
50
+ ],
51
+ messages: {
52
+ link: `Do not use an \`<a>\` element to navigate. Use the \`<Link />\` component from \`@docusaurus/Link\` instead. See: ${docsUrl}`,
53
+ },
54
+ },
55
+ defaultOptions: [
56
+ {
57
+ ignoreFullyResolved: false,
58
+ },
59
+ ],
60
+
61
+ create(context, [options]) {
62
+ const {ignoreFullyResolved} = options;
63
+
64
+ return {
65
+ JSXOpeningElement(node) {
66
+ if ((node.name as TSESTree.JSXIdentifier).name !== 'a') {
67
+ return;
68
+ }
69
+
70
+ if (ignoreFullyResolved) {
71
+ const hrefAttr = node.attributes.find(
72
+ (attr): attr is TSESTree.JSXAttribute =>
73
+ attr.type === 'JSXAttribute' && attr.name.name === 'href',
74
+ );
75
+
76
+ if (hrefAttr?.value?.type === 'Literal') {
77
+ if (isFullyResolvedUrl(String(hrefAttr.value.value))) {
78
+ return;
79
+ }
80
+ }
81
+ if (hrefAttr?.value?.type === 'JSXExpressionContainer') {
82
+ const container: TSESTree.JSXExpressionContainer = hrefAttr.value;
83
+ const {expression} = container;
84
+ if (expression.type === 'TemplateLiteral') {
85
+ // Simple static string template literals
86
+ if (
87
+ expression.expressions.length === 0 &&
88
+ expression.quasis.length === 1 &&
89
+ expression.quasis[0]?.type === 'TemplateElement' &&
90
+ isFullyResolvedUrl(String(expression.quasis[0].value.raw))
91
+ ) {
92
+ return;
93
+ }
94
+ // TODO add more complex TemplateLiteral cases here
95
+ }
96
+ }
97
+ }
98
+
99
+ context.report({node, messageId: 'link'});
100
+ },
101
+ };
102
+ },
103
+ });
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import {createRule} from '../util';
9
+ import type {TSESTree} from '@typescript-eslint/types/dist/ts-estree';
10
+
11
+ type Options = [];
12
+ type MessageIds = 'headings';
13
+
14
+ export default createRule<Options, MessageIds>({
15
+ name: 'prefer-docusaurus-heading',
16
+ meta: {
17
+ type: 'problem',
18
+ docs: {
19
+ description:
20
+ 'enforce using Docusaurus theme Heading component instead of any <hn> tag',
21
+ recommended: false,
22
+ },
23
+ schema: [],
24
+ messages: {
25
+ headings:
26
+ 'Do not use any of the `<hn>` tags for headings. Use the `<Heading />` component from `@theme/Heading` instead.',
27
+ },
28
+ },
29
+ defaultOptions: [],
30
+
31
+ create(context) {
32
+ const headingTypes = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
33
+
34
+ return {
35
+ JSXOpeningElement(node) {
36
+ const elementName = (node.name as TSESTree.JSXIdentifier).name;
37
+
38
+ if (!headingTypes.includes(elementName)) {
39
+ return;
40
+ }
41
+
42
+ context.report({node, messageId: 'headings'});
43
+ },
44
+ };
45
+ },
46
+ });