@db-ux/core-eslint-plugin 0.0.0 → 4.4.2-eslint-plugin-28ea614

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/README.md +754 -0
  3. package/build/index.d.ts +99 -0
  4. package/build/index.js +79 -0
  5. package/build/rules/accordion/no-nested-accordion.d.ts +5 -0
  6. package/build/rules/accordion/no-nested-accordion.js +37 -0
  7. package/build/rules/badge/badge-corner-placement-rules.d.ts +5 -0
  8. package/build/rules/badge/badge-corner-placement-rules.js +76 -0
  9. package/build/rules/badge/badge-no-inline-in-interactive.d.ts +5 -0
  10. package/build/rules/badge/badge-no-inline-in-interactive.js +67 -0
  11. package/build/rules/button/button-no-text-requires-tooltip.d.ts +5 -0
  12. package/build/rules/button/button-no-text-requires-tooltip.js +59 -0
  13. package/build/rules/button/button-single-icon-attribute.d.ts +5 -0
  14. package/build/rules/button/button-single-icon-attribute.js +35 -0
  15. package/build/rules/button/button-type-required.d.ts +5 -0
  16. package/build/rules/button/button-type-required.js +45 -0
  17. package/build/rules/close-button/close-button-text-required.d.ts +5 -0
  18. package/build/rules/close-button/close-button-text-required.js +40 -0
  19. package/build/rules/content/text-or-children-required.d.ts +5 -0
  20. package/build/rules/content/text-or-children-required.js +49 -0
  21. package/build/rules/form/form-label-required.d.ts +5 -0
  22. package/build/rules/form/form-label-required.js +47 -0
  23. package/build/rules/form/form-validation-message-required.d.ts +5 -0
  24. package/build/rules/form/form-validation-message-required.js +93 -0
  25. package/build/rules/header/header-burger-menu-label-required.d.ts +5 -0
  26. package/build/rules/header/header-burger-menu-label-required.js +32 -0
  27. package/build/rules/icon/prefer-icon-attribute.d.ts +5 -0
  28. package/build/rules/icon/prefer-icon-attribute.js +67 -0
  29. package/build/rules/input/input-file-type-validation.d.ts +5 -0
  30. package/build/rules/input/input-file-type-validation.js +52 -0
  31. package/build/rules/input/input-type-required.d.ts +5 -0
  32. package/build/rules/input/input-type-required.js +40 -0
  33. package/build/rules/link/link-external-security.d.ts +5 -0
  34. package/build/rules/link/link-external-security.js +50 -0
  35. package/build/rules/navigation/navigation-item-back-button-text-required.d.ts +5 -0
  36. package/build/rules/navigation/navigation-item-back-button-text-required.js +32 -0
  37. package/build/rules/select/custom-select-tags-remove-text-required.d.ts +5 -0
  38. package/build/rules/select/custom-select-tags-remove-text-required.js +35 -0
  39. package/build/rules/select/select-requires-options.d.ts +5 -0
  40. package/build/rules/select/select-requires-options.js +44 -0
  41. package/build/rules/tag/tag-removable-remove-button-required.d.ts +5 -0
  42. package/build/rules/tag/tag-removable-remove-button-required.js +35 -0
  43. package/build/rules/tooltip/no-interactive-tooltip-content.d.ts +5 -0
  44. package/build/rules/tooltip/no-interactive-tooltip-content.js +47 -0
  45. package/build/rules/tooltip/tooltip-requires-interactive-parent.d.ts +5 -0
  46. package/build/rules/tooltip/tooltip-requires-interactive-parent.js +47 -0
  47. package/build/shared/utils.d.ts +5 -0
  48. package/build/shared/utils.js +61 -0
  49. package/package.json +32 -1
@@ -0,0 +1,99 @@
1
+ declare const plugin: {
2
+ meta: {
3
+ name: string;
4
+ version: string;
5
+ };
6
+ rules: {
7
+ 'button-no-text-requires-tooltip': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingIcon" | "missingTooltip", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
8
+ name: string;
9
+ };
10
+ 'button-type-required': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingType", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
11
+ name: string;
12
+ };
13
+ 'button-single-icon-attribute': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"multipleIcons", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
14
+ name: string;
15
+ };
16
+ 'form-label-required': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingLabel", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
17
+ name: string;
18
+ };
19
+ 'form-validation-message-required': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingInvalidMessage", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
20
+ name: string;
21
+ };
22
+ 'prefer-icon-attribute': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"preferAttribute", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
23
+ name: string;
24
+ };
25
+ 'text-or-children-required': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingContent", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
26
+ name: string;
27
+ };
28
+ 'no-interactive-tooltip-content': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"noInteractive", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
29
+ name: string;
30
+ };
31
+ 'tooltip-requires-interactive-parent': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"requiresInteractive", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
32
+ name: string;
33
+ };
34
+ 'no-nested-accordion': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"noNested", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
35
+ name: string;
36
+ };
37
+ 'badge-corner-placement-rules': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"textTooLong" | "missingLabel", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
38
+ name: string;
39
+ };
40
+ 'badge-no-inline-in-interactive': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"noInline", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
41
+ name: string;
42
+ };
43
+ 'link-external-security': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingTargetBlank" | "missingReferrerPolicy" | "missingContentExternal", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
44
+ name: string;
45
+ };
46
+ 'select-requires-options': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingOptions", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
47
+ name: string;
48
+ };
49
+ 'custom-select-tags-remove-text-required': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingRemoveTagsTexts", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
50
+ name: string;
51
+ };
52
+ 'close-button-text-required': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingCloseButtonText", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
53
+ name: string;
54
+ };
55
+ 'header-burger-menu-label-required': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingBurgerMenuLabel", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
56
+ name: string;
57
+ };
58
+ 'navigation-item-back-button-text-required': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingBackButtonText", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
59
+ name: string;
60
+ };
61
+ 'tag-removable-remove-button-required': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingRemoveButton", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
62
+ name: string;
63
+ };
64
+ 'input-type-required': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingType", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
65
+ name: string;
66
+ };
67
+ 'input-file-type-validation': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingAccept" | "invalidMultiple" | "invalidAccept", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
68
+ name: string;
69
+ };
70
+ };
71
+ configs: {
72
+ recommended: {
73
+ rules: {
74
+ 'db-ux/button-no-text-requires-tooltip': string;
75
+ 'db-ux/button-type-required': string;
76
+ 'db-ux/button-single-icon-attribute': string;
77
+ 'db-ux/form-label-required': string;
78
+ 'db-ux/form-validation-message-required': string;
79
+ 'db-ux/prefer-icon-attribute': string;
80
+ 'db-ux/text-or-children-required': string;
81
+ 'db-ux/no-interactive-tooltip-content': string;
82
+ 'db-ux/tooltip-requires-interactive-parent': string;
83
+ 'db-ux/no-nested-accordion': string;
84
+ 'db-ux/badge-corner-placement-rules': string;
85
+ 'db-ux/badge-no-inline-in-interactive': string;
86
+ 'db-ux/link-external-security': string;
87
+ 'db-ux/select-requires-options': string;
88
+ 'db-ux/custom-select-tags-remove-text-required': string;
89
+ 'db-ux/close-button-text-required': string;
90
+ 'db-ux/header-burger-menu-label-required': string;
91
+ 'db-ux/navigation-item-back-button-text-required': string;
92
+ 'db-ux/tag-removable-remove-button-required': string;
93
+ 'db-ux/input-type-required': string;
94
+ 'db-ux/input-file-type-validation': string;
95
+ };
96
+ };
97
+ };
98
+ };
99
+ export default plugin;
package/build/index.js ADDED
@@ -0,0 +1,79 @@
1
+ import noNestedAccordion from './rules/accordion/no-nested-accordion.js';
2
+ import badgeCornerPlacementRules from './rules/badge/badge-corner-placement-rules.js';
3
+ import badgeNoInlineInInteractive from './rules/badge/badge-no-inline-in-interactive.js';
4
+ import buttonNoTextRequiresTooltip from './rules/button/button-no-text-requires-tooltip.js';
5
+ import buttonSingleIconAttribute from './rules/button/button-single-icon-attribute.js';
6
+ import buttonTypeRequired from './rules/button/button-type-required.js';
7
+ import closeButtonTextRequired from './rules/close-button/close-button-text-required.js';
8
+ import textOrChildrenRequired from './rules/content/text-or-children-required.js';
9
+ import formLabelRequired from './rules/form/form-label-required.js';
10
+ import formValidationMessageRequired from './rules/form/form-validation-message-required.js';
11
+ import headerBurgerMenuLabelRequired from './rules/header/header-burger-menu-label-required.js';
12
+ import preferIconAttribute from './rules/icon/prefer-icon-attribute.js';
13
+ import inputFileTypeValidation from './rules/input/input-file-type-validation.js';
14
+ import inputTypeRequired from './rules/input/input-type-required.js';
15
+ import linkExternalSecurity from './rules/link/link-external-security.js';
16
+ import navigationItemBackButtonTextRequired from './rules/navigation/navigation-item-back-button-text-required.js';
17
+ import customSelectTagsRemoveTextRequired from './rules/select/custom-select-tags-remove-text-required.js';
18
+ import selectRequiresOptions from './rules/select/select-requires-options.js';
19
+ import tagRemovableRemoveButtonRequired from './rules/tag/tag-removable-remove-button-required.js';
20
+ import noInteractiveTooltipContent from './rules/tooltip/no-interactive-tooltip-content.js';
21
+ import tooltipRequiresInteractiveParent from './rules/tooltip/tooltip-requires-interactive-parent.js';
22
+ const recommended = {
23
+ rules: {
24
+ 'db-ux/button-no-text-requires-tooltip': 'error',
25
+ 'db-ux/button-type-required': 'error',
26
+ 'db-ux/button-single-icon-attribute': 'error',
27
+ 'db-ux/form-label-required': 'error',
28
+ 'db-ux/form-validation-message-required': 'error',
29
+ 'db-ux/prefer-icon-attribute': 'warn',
30
+ 'db-ux/text-or-children-required': 'error',
31
+ 'db-ux/no-interactive-tooltip-content': 'error',
32
+ 'db-ux/tooltip-requires-interactive-parent': 'error',
33
+ 'db-ux/no-nested-accordion': 'error',
34
+ 'db-ux/badge-corner-placement-rules': 'error',
35
+ 'db-ux/badge-no-inline-in-interactive': 'error',
36
+ 'db-ux/link-external-security': 'warn',
37
+ 'db-ux/select-requires-options': 'error',
38
+ 'db-ux/custom-select-tags-remove-text-required': 'error',
39
+ 'db-ux/close-button-text-required': 'error',
40
+ 'db-ux/header-burger-menu-label-required': 'error',
41
+ 'db-ux/navigation-item-back-button-text-required': 'error',
42
+ 'db-ux/tag-removable-remove-button-required': 'error',
43
+ 'db-ux/input-type-required': 'warn',
44
+ 'db-ux/input-file-type-validation': 'error'
45
+ }
46
+ };
47
+ const plugin = {
48
+ meta: {
49
+ name: '@db-ux/eslint-plugin',
50
+ version: '0.0.1'
51
+ },
52
+ rules: {
53
+ 'button-no-text-requires-tooltip': buttonNoTextRequiresTooltip,
54
+ 'button-type-required': buttonTypeRequired,
55
+ 'button-single-icon-attribute': buttonSingleIconAttribute,
56
+ 'form-label-required': formLabelRequired,
57
+ 'form-validation-message-required': formValidationMessageRequired,
58
+ 'prefer-icon-attribute': preferIconAttribute,
59
+ 'text-or-children-required': textOrChildrenRequired,
60
+ 'no-interactive-tooltip-content': noInteractiveTooltipContent,
61
+ 'tooltip-requires-interactive-parent': tooltipRequiresInteractiveParent,
62
+ 'no-nested-accordion': noNestedAccordion,
63
+ 'badge-corner-placement-rules': badgeCornerPlacementRules,
64
+ 'badge-no-inline-in-interactive': badgeNoInlineInInteractive,
65
+ 'link-external-security': linkExternalSecurity,
66
+ 'select-requires-options': selectRequiresOptions,
67
+ 'custom-select-tags-remove-text-required': customSelectTagsRemoveTextRequired,
68
+ 'close-button-text-required': closeButtonTextRequired,
69
+ 'header-burger-menu-label-required': headerBurgerMenuLabelRequired,
70
+ 'navigation-item-back-button-text-required': navigationItemBackButtonTextRequired,
71
+ 'tag-removable-remove-button-required': tagRemovableRemoveButtonRequired,
72
+ 'input-type-required': inputTypeRequired,
73
+ 'input-file-type-validation': inputFileTypeValidation
74
+ },
75
+ configs: {
76
+ recommended
77
+ }
78
+ };
79
+ export default plugin;
@@ -0,0 +1,5 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ declare const _default: ESLintUtils.RuleModule<"noNested", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
5
+ export default _default;
@@ -0,0 +1,37 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ import { isDBComponent } from '../../shared/utils.js';
3
+ const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/db-ux-design-system/core-web/blob/main/packages/eslint-plugin/README.md#${name}`);
4
+ export default createRule({
5
+ name: 'no-nested-accordion',
6
+ meta: {
7
+ type: 'problem',
8
+ docs: {
9
+ description: 'Prevent nesting DBAccordion components'
10
+ },
11
+ messages: {
12
+ noNested: 'DBAccordion must not be nested inside another DBAccordion as it confuses users'
13
+ },
14
+ schema: []
15
+ },
16
+ defaultOptions: [],
17
+ create(context) {
18
+ return {
19
+ JSXElement(node) {
20
+ if (!isDBComponent(node.openingElement, 'DBAccordion'))
21
+ return;
22
+ let parent = node.parent;
23
+ while (parent) {
24
+ if (parent.type === 'JSXElement' &&
25
+ isDBComponent(parent.openingElement, 'DBAccordion')) {
26
+ context.report({
27
+ node: node.openingElement,
28
+ messageId: 'noNested'
29
+ });
30
+ return;
31
+ }
32
+ parent = parent.parent;
33
+ }
34
+ }
35
+ };
36
+ }
37
+ });
@@ -0,0 +1,5 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ declare const _default: ESLintUtils.RuleModule<"textTooLong" | "missingLabel", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
5
+ export default _default;
@@ -0,0 +1,76 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ import { getAttributeValue, isDBComponent } from '../../shared/utils.js';
3
+ const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/db-ux-design-system/core-web/blob/main/packages/eslint-plugin/README.md#${name}`);
4
+ function getTextContent(node) {
5
+ const textChild = node.children.find((child) => child.type === 'JSXText');
6
+ return textChild && textChild.type === 'JSXText'
7
+ ? textChild.value.trim()
8
+ : null;
9
+ }
10
+ export default createRule({
11
+ name: 'badge-corner-placement-rules',
12
+ meta: {
13
+ type: 'problem',
14
+ docs: {
15
+ description: 'Ensure DBBadge with corner placement has max 3 characters and label'
16
+ },
17
+ fixable: 'code',
18
+ messages: {
19
+ textTooLong: 'DBBadge with corner placement must have max 3 characters in text/children',
20
+ missingLabel: 'DBBadge with corner placement must have a label attribute for accessibility'
21
+ },
22
+ schema: []
23
+ },
24
+ defaultOptions: [],
25
+ create(context) {
26
+ return {
27
+ JSXElement(node) {
28
+ if (!isDBComponent(node.openingElement, 'DBBadge'))
29
+ return;
30
+ const placement = getAttributeValue(node.openingElement, 'placement');
31
+ if (!placement || placement === 'inline')
32
+ return;
33
+ const text = getAttributeValue(node.openingElement, 'text');
34
+ const children = getTextContent(node);
35
+ const content = (typeof text === 'string' ? text : children) || '';
36
+ const label = getAttributeValue(node.openingElement, 'label');
37
+ if (content.length > 3) {
38
+ context.report({
39
+ node: node.openingElement,
40
+ messageId: 'textTooLong',
41
+ fix(fixer) {
42
+ const fixes = [];
43
+ const shortText = content.slice(0, 3);
44
+ if (text && typeof text === 'string') {
45
+ const textAttr = node.openingElement.attributes.find((a) => a.type === 'JSXAttribute' &&
46
+ a.name.name === 'text');
47
+ if (textAttr) {
48
+ fixes.push(fixer.replaceText(textAttr, `text="${shortText}" label="${content}"`));
49
+ }
50
+ }
51
+ else if (children) {
52
+ const textChild = node.children.find((c) => c.type === 'JSXText');
53
+ if (textChild) {
54
+ fixes.push(fixer.replaceText(textChild, shortText));
55
+ const lastAttr = node.openingElement.attributes[node.openingElement.attributes
56
+ .length - 1];
57
+ const insertPos = lastAttr
58
+ ? lastAttr.range[1]
59
+ : node.openingElement.name.range[1];
60
+ fixes.push(fixer.insertTextAfterRange([insertPos, insertPos], ` label="${content}"`));
61
+ }
62
+ }
63
+ return fixes;
64
+ }
65
+ });
66
+ }
67
+ if (!label) {
68
+ context.report({
69
+ node: node.openingElement,
70
+ messageId: 'missingLabel'
71
+ });
72
+ }
73
+ }
74
+ };
75
+ }
76
+ });
@@ -0,0 +1,5 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ declare const _default: ESLintUtils.RuleModule<"noInline", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
5
+ export default _default;
@@ -0,0 +1,67 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ import { getAttributeValue, isDBComponent } from '../../shared/utils.js';
3
+ const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/db-ux-design-system/core-web/blob/main/packages/eslint-plugin/README.md#${name}`);
4
+ const INTERACTIVE_PARENTS = ['DBButton', 'DBLink', 'button', 'a'];
5
+ export default createRule({
6
+ name: 'badge-no-inline-in-interactive',
7
+ meta: {
8
+ type: 'problem',
9
+ docs: {
10
+ description: 'Prevent inline placement for DBBadge inside interactive elements'
11
+ },
12
+ fixable: 'code',
13
+ messages: {
14
+ noInline: 'DBBadge inside {{parent}} cannot have placement="inline". Use corner placement instead'
15
+ },
16
+ schema: []
17
+ },
18
+ defaultOptions: [],
19
+ create(context) {
20
+ return {
21
+ JSXElement(node) {
22
+ if (!isDBComponent(node.openingElement, 'DBBadge'))
23
+ return;
24
+ const placement = getAttributeValue(node.openingElement, 'placement');
25
+ if (placement && placement !== 'inline')
26
+ return;
27
+ let parent = node.parent;
28
+ while (parent) {
29
+ if (parent.type === 'JSXElement') {
30
+ const parentName = parent.openingElement.name;
31
+ if (parentName.type === 'JSXIdentifier') {
32
+ const name = parentName.name;
33
+ const matchedParent = INTERACTIVE_PARENTS.find((p) => name === p ||
34
+ name ===
35
+ p.toLowerCase().replace('db', 'db-'));
36
+ if (matchedParent) {
37
+ context.report({
38
+ node: node.openingElement,
39
+ messageId: 'noInline',
40
+ data: { parent: matchedParent },
41
+ fix(fixer) {
42
+ const placementAttr = node.openingElement.attributes.find((a) => a.type === 'JSXAttribute' &&
43
+ a.name.name === 'placement');
44
+ if (placementAttr) {
45
+ return fixer.replaceText(placementAttr, 'placement="corner-top-right"');
46
+ }
47
+ else {
48
+ const lastAttr = node.openingElement.attributes[node.openingElement
49
+ .attributes.length - 1];
50
+ const insertPos = lastAttr
51
+ ? lastAttr.range[1]
52
+ : node.openingElement.name
53
+ .range[1];
54
+ return fixer.insertTextAfterRange([insertPos, insertPos], ' placement="corner-top-right"');
55
+ }
56
+ }
57
+ });
58
+ return;
59
+ }
60
+ }
61
+ }
62
+ parent = parent.parent;
63
+ }
64
+ }
65
+ };
66
+ }
67
+ });
@@ -0,0 +1,5 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ declare const _default: ESLintUtils.RuleModule<"missingIcon" | "missingTooltip", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
5
+ export default _default;
@@ -0,0 +1,59 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ import { getAttributeValue, hasChildOfType, isDBComponent } from '../../shared/utils.js';
3
+ const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/db-ux-design-system/core-web/blob/main/packages/eslint-plugin/README.md#${name}`);
4
+ export default createRule({
5
+ name: 'button-no-text-requires-tooltip',
6
+ meta: {
7
+ type: 'problem',
8
+ docs: {
9
+ description: 'Ensure DBButton with noText has icon and DBTooltip child'
10
+ },
11
+ fixable: 'code',
12
+ messages: {
13
+ missingIcon: 'DBButton with noText must have an icon prop',
14
+ missingTooltip: 'DBButton with noText must have a DBTooltip child for accessibility'
15
+ },
16
+ schema: []
17
+ },
18
+ defaultOptions: [],
19
+ create(context) {
20
+ return {
21
+ JSXElement(node) {
22
+ const openingElement = node.openingElement;
23
+ if (!isDBComponent(openingElement, 'DBButton'))
24
+ return;
25
+ const noText = getAttributeValue(openingElement, 'noText');
26
+ if (!noText)
27
+ return;
28
+ const icon = getAttributeValue(openingElement, 'icon') ||
29
+ getAttributeValue(openingElement, 'iconLeading') ||
30
+ getAttributeValue(openingElement, 'iconTrailing');
31
+ if (!icon) {
32
+ context.report({
33
+ node: openingElement,
34
+ messageId: 'missingIcon'
35
+ });
36
+ }
37
+ const hasTooltip = hasChildOfType(node, 'DBTooltip');
38
+ if (!hasTooltip) {
39
+ context.report({
40
+ node: openingElement,
41
+ messageId: 'missingTooltip',
42
+ fix(fixer) {
43
+ const closingTag = node.closingElement;
44
+ if (!closingTag)
45
+ return null;
46
+ const componentName = openingElement.name.type === 'JSXIdentifier'
47
+ ? openingElement.name.name
48
+ : 'DBButton';
49
+ const tooltipName = componentName.includes('-')
50
+ ? 'db-tooltip'
51
+ : 'DBTooltip';
52
+ return fixer.insertTextBefore(closingTag, `\n <${tooltipName}>Describe action</${tooltipName}>`);
53
+ }
54
+ });
55
+ }
56
+ }
57
+ };
58
+ }
59
+ });
@@ -0,0 +1,5 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ declare const _default: ESLintUtils.RuleModule<"multipleIcons", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
5
+ export default _default;
@@ -0,0 +1,35 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ import { getAttributeValue, isDBComponent } from '../../shared/utils.js';
3
+ const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/db-ux-design-system/core-web/blob/main/packages/eslint-plugin/README.md#${name}`);
4
+ export default createRule({
5
+ name: 'button-single-icon-attribute',
6
+ meta: {
7
+ type: 'problem',
8
+ docs: {
9
+ description: 'Ensure DBButton uses only one icon attribute'
10
+ },
11
+ messages: {
12
+ multipleIcons: 'DBButton can only use one of: icon, iconLeading, or iconTrailing'
13
+ },
14
+ schema: []
15
+ },
16
+ defaultOptions: [],
17
+ create(context) {
18
+ return {
19
+ JSXElement(node) {
20
+ if (!isDBComponent(node.openingElement, 'DBButton'))
21
+ return;
22
+ const icon = getAttributeValue(node.openingElement, 'icon');
23
+ const iconLeading = getAttributeValue(node.openingElement, 'iconLeading');
24
+ const iconTrailing = getAttributeValue(node.openingElement, 'iconTrailing');
25
+ const iconCount = [icon, iconLeading, iconTrailing].filter(Boolean).length;
26
+ if (iconCount > 1) {
27
+ context.report({
28
+ node: node.openingElement,
29
+ messageId: 'multipleIcons'
30
+ });
31
+ }
32
+ }
33
+ };
34
+ }
35
+ });
@@ -0,0 +1,5 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ declare const _default: ESLintUtils.RuleModule<"missingType", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
5
+ export default _default;
@@ -0,0 +1,45 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ import { getAttributeValue, isDBComponent } from '../../shared/utils.js';
3
+ const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/db-ux-design-system/core-web/blob/main/packages/eslint-plugin/README.md#${name}`);
4
+ export default createRule({
5
+ name: 'button-type-required',
6
+ meta: {
7
+ type: 'problem',
8
+ docs: {
9
+ description: 'Ensure DBButton has explicit type attribute'
10
+ },
11
+ fixable: 'code',
12
+ messages: {
13
+ missingType: 'DBButton must have an explicit type attribute (submit, button, or reset)'
14
+ },
15
+ schema: []
16
+ },
17
+ defaultOptions: [],
18
+ create(context) {
19
+ return {
20
+ JSXElement(node) {
21
+ const openingElement = node.openingElement;
22
+ if (!isDBComponent(openingElement, 'DBButton'))
23
+ return;
24
+ const type = getAttributeValue(openingElement, 'type');
25
+ if (!type) {
26
+ const hasClickHandler = getAttributeValue(openingElement, 'onClick') ||
27
+ getAttributeValue(openingElement, '(click)') ||
28
+ getAttributeValue(openingElement, '@click');
29
+ const typeValue = hasClickHandler ? 'button' : 'submit';
30
+ context.report({
31
+ node: openingElement,
32
+ messageId: 'missingType',
33
+ fix(fixer) {
34
+ const lastAttr = openingElement.attributes[openingElement.attributes.length - 1];
35
+ const insertPos = lastAttr
36
+ ? lastAttr.range[1]
37
+ : openingElement.name.range[1];
38
+ return fixer.insertTextAfterRange([insertPos, insertPos], ` type="${typeValue}"`);
39
+ }
40
+ });
41
+ }
42
+ }
43
+ };
44
+ }
45
+ });
@@ -0,0 +1,5 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ declare const _default: ESLintUtils.RuleModule<"missingCloseButtonText", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
5
+ export default _default;
@@ -0,0 +1,40 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ import { getAttributeValue, isDBComponent } from '../../shared/utils.js';
3
+ const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/db-ux-design-system/core-web/blob/main/packages/eslint-plugin/README.md#${name}`);
4
+ const COMPONENTS_WITH_CLOSE_BUTTON = {
5
+ DBNotification: 'closeButtonText',
6
+ DBDrawer: 'closeButtonText',
7
+ DBCustomSelect: 'mobileCloseButtonText'
8
+ };
9
+ export default createRule({
10
+ name: 'close-button-text-required',
11
+ meta: {
12
+ type: 'problem',
13
+ docs: {
14
+ description: 'Ensure components have close button text for accessibility'
15
+ },
16
+ messages: {
17
+ missingCloseButtonText: '{{component}} must have {{attribute}} attribute for accessibility'
18
+ },
19
+ schema: []
20
+ },
21
+ defaultOptions: [],
22
+ create(context) {
23
+ return {
24
+ JSXElement(node) {
25
+ const component = Object.keys(COMPONENTS_WITH_CLOSE_BUTTON).find((comp) => isDBComponent(node.openingElement, comp));
26
+ if (!component)
27
+ return;
28
+ const attribute = COMPONENTS_WITH_CLOSE_BUTTON[component];
29
+ const value = getAttributeValue(node.openingElement, attribute);
30
+ if (!value) {
31
+ context.report({
32
+ node: node.openingElement,
33
+ messageId: 'missingCloseButtonText',
34
+ data: { component, attribute }
35
+ });
36
+ }
37
+ }
38
+ };
39
+ }
40
+ });
@@ -0,0 +1,5 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ declare const _default: ESLintUtils.RuleModule<"missingContent", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
5
+ export default _default;
@@ -0,0 +1,49 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ import { getAttributeValue, isDBComponent } from '../../shared/utils.js';
3
+ const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/db-ux-design-system/core-web/blob/main/packages/eslint-plugin/README.md#${name}`);
4
+ const COMPONENTS_REQUIRING_CONTENT = [
5
+ 'DBAccordionItem',
6
+ 'DBBadge',
7
+ 'DBButton',
8
+ 'DBLink',
9
+ 'DBIcon',
10
+ 'DBInfotext',
11
+ 'DBNavigationItem',
12
+ 'DBNotification'
13
+ ];
14
+ export default createRule({
15
+ name: 'text-or-children-required',
16
+ meta: {
17
+ type: 'problem',
18
+ docs: {
19
+ description: 'Ensure components have text property or children content'
20
+ },
21
+ messages: {
22
+ missingContent: '{{component}} must have either a text property or children content'
23
+ },
24
+ schema: []
25
+ },
26
+ defaultOptions: [],
27
+ create(context) {
28
+ return {
29
+ JSXElement(node) {
30
+ const openingElement = node.openingElement;
31
+ const component = COMPONENTS_REQUIRING_CONTENT.find((comp) => isDBComponent(openingElement, comp));
32
+ if (!component)
33
+ return;
34
+ const text = getAttributeValue(openingElement, 'text');
35
+ const hasChildren = node.children.some((child) => (child.type === 'JSXText' &&
36
+ child.value.trim() !== '') ||
37
+ child.type === 'JSXElement' ||
38
+ child.type === 'JSXExpressionContainer');
39
+ if (!text && !hasChildren) {
40
+ context.report({
41
+ node: openingElement,
42
+ messageId: 'missingContent',
43
+ data: { component }
44
+ });
45
+ }
46
+ }
47
+ };
48
+ }
49
+ });