@db-ux/core-eslint-plugin 0.0.0 → 4.4.2-eslint-plugin2-696cb23

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 (53) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/README.md +843 -0
  3. package/build/index.d.ts +352 -0
  4. package/build/index.js +82 -0
  5. package/build/rules/accordion/accordion-item-headline-required.d.ts +16 -0
  6. package/build/rules/accordion/accordion-item-headline-required.js +61 -0
  7. package/build/rules/accordion/no-nested-accordion.d.ts +16 -0
  8. package/build/rules/accordion/no-nested-accordion.js +54 -0
  9. package/build/rules/badge/badge-corner-placement-rules.d.ts +18 -0
  10. package/build/rules/badge/badge-corner-placement-rules.js +114 -0
  11. package/build/rules/badge/badge-no-inline-in-interactive.d.ts +17 -0
  12. package/build/rules/badge/badge-no-inline-in-interactive.js +129 -0
  13. package/build/rules/button/button-no-text-requires-tooltip.d.ts +18 -0
  14. package/build/rules/button/button-no-text-requires-tooltip.js +109 -0
  15. package/build/rules/button/button-single-icon-attribute.d.ts +16 -0
  16. package/build/rules/button/button-single-icon-attribute.js +49 -0
  17. package/build/rules/button/button-type-required.d.ts +17 -0
  18. package/build/rules/button/button-type-required.js +58 -0
  19. package/build/rules/close-button/close-button-text-required.d.ts +16 -0
  20. package/build/rules/close-button/close-button-text-required.js +38 -0
  21. package/build/rules/content/text-or-children-required.d.ts +16 -0
  22. package/build/rules/content/text-or-children-required.js +47 -0
  23. package/build/rules/form/form-label-required.d.ts +16 -0
  24. package/build/rules/form/form-label-required.js +45 -0
  25. package/build/rules/form/form-validation-message-required.d.ts +16 -0
  26. package/build/rules/form/form-validation-message-required.js +91 -0
  27. package/build/rules/header/header-burger-menu-label-required.d.ts +16 -0
  28. package/build/rules/header/header-burger-menu-label-required.js +43 -0
  29. package/build/rules/icon/prefer-icon-attribute.d.ts +17 -0
  30. package/build/rules/icon/prefer-icon-attribute.js +82 -0
  31. package/build/rules/input/input-file-type-validation.d.ts +18 -0
  32. package/build/rules/input/input-file-type-validation.js +50 -0
  33. package/build/rules/input/input-type-required.d.ts +17 -0
  34. package/build/rules/input/input-type-required.js +54 -0
  35. package/build/rules/link/link-external-security.d.ts +19 -0
  36. package/build/rules/link/link-external-security.js +166 -0
  37. package/build/rules/navigation/navigation-item-back-button-text-required.d.ts +16 -0
  38. package/build/rules/navigation/navigation-item-back-button-text-required.js +43 -0
  39. package/build/rules/select/custom-select-tags-remove-text-required.d.ts +16 -0
  40. package/build/rules/select/custom-select-tags-remove-text-required.js +33 -0
  41. package/build/rules/select/select-requires-options.d.ts +16 -0
  42. package/build/rules/select/select-requires-options.js +45 -0
  43. package/build/rules/tag/tag-removable-remove-button-required.d.ts +16 -0
  44. package/build/rules/tag/tag-removable-remove-button-required.js +49 -0
  45. package/build/rules/tooltip/no-interactive-tooltip-content.d.ts +15 -0
  46. package/build/rules/tooltip/no-interactive-tooltip-content.js +49 -0
  47. package/build/rules/tooltip/tooltip-requires-interactive-parent.d.ts +15 -0
  48. package/build/rules/tooltip/tooltip-requires-interactive-parent.js +47 -0
  49. package/build/shared/constants.d.ts +58 -0
  50. package/build/shared/constants.js +98 -0
  51. package/build/shared/utils.d.ts +54 -0
  52. package/build/shared/utils.js +178 -0
  53. package/package.json +37 -1
@@ -0,0 +1,114 @@
1
+ import { COMPONENTS, MESSAGES, MESSAGE_IDS } from '../../shared/constants.js';
2
+ import { createAngularFix, createAngularVisitors, defineTemplateBodyVisitor, getAttributeValue, isDBComponent } from '../../shared/utils.js';
3
+ function getTextContent(node) {
4
+ if (node.children) {
5
+ for (const child of node.children) {
6
+ if (child.type === 'JSXText') {
7
+ return child.value.trim();
8
+ }
9
+ if (child.type === 'Text') {
10
+ return child.value?.trim() || null;
11
+ }
12
+ if (child.type === 'VText') {
13
+ return child.value?.trim() || null;
14
+ }
15
+ }
16
+ }
17
+ return null;
18
+ }
19
+ export default {
20
+ meta: {
21
+ type: 'problem',
22
+ docs: {
23
+ description: 'Ensure DBBadge with corner placement has max 3 characters and label',
24
+ url: 'https://github.com/db-ux-design-system/core-web/blob/main/packages/eslint-plugin/README.md#badge-corner-placement-rules'
25
+ },
26
+ fixable: 'code',
27
+ messages: {
28
+ [MESSAGE_IDS.BADGE_CORNER_TEXT_TOO_LONG]: MESSAGES.BADGE_CORNER_TEXT_TOO_LONG,
29
+ [MESSAGE_IDS.BADGE_CORNER_MISSING_LABEL]: MESSAGES.BADGE_CORNER_MISSING_LABEL
30
+ },
31
+ schema: []
32
+ },
33
+ create(context) {
34
+ const angularHandler = (node, parserServices) => {
35
+ const placement = getAttributeValue(node, 'placement');
36
+ if (!placement || placement === 'inline')
37
+ return;
38
+ const text = getAttributeValue(node, 'text');
39
+ const children = getTextContent(node);
40
+ const content = (typeof text === 'string' ? text : children) || '';
41
+ const label = getAttributeValue(node, 'label');
42
+ const loc = parserServices.convertNodeSourceSpanToLoc(node.sourceSpan);
43
+ if (content.length > 3) {
44
+ context.report({
45
+ loc,
46
+ messageId: MESSAGE_IDS.BADGE_CORNER_TEXT_TOO_LONG
47
+ });
48
+ }
49
+ if (!label) {
50
+ context.report({
51
+ loc,
52
+ messageId: MESSAGE_IDS.BADGE_CORNER_MISSING_LABEL,
53
+ fix(fixer) {
54
+ const fixData = createAngularFix(context, node, ` label="${content || 'Badge'}"`);
55
+ if (!fixData)
56
+ return null;
57
+ return fixer.insertTextBeforeRange([fixData.insertPos, fixData.insertPos], fixData.attributeText);
58
+ }
59
+ });
60
+ }
61
+ };
62
+ const angularVisitors = createAngularVisitors(context, COMPONENTS.DBBadge, angularHandler);
63
+ if (angularVisitors)
64
+ return angularVisitors;
65
+ const checkBadge = (node) => {
66
+ const openingElement = node.openingElement || node;
67
+ if (!isDBComponent(openingElement, COMPONENTS.DBBadge))
68
+ return;
69
+ const placement = getAttributeValue(openingElement, 'placement');
70
+ if (!placement || placement === 'inline')
71
+ return;
72
+ const text = getAttributeValue(openingElement, 'text');
73
+ const children = getTextContent(node);
74
+ const content = (typeof text === 'string' ? text : children) || '';
75
+ const label = getAttributeValue(openingElement, 'label');
76
+ if (content.length > 3) {
77
+ context.report({
78
+ node: openingElement,
79
+ messageId: MESSAGE_IDS.BADGE_CORNER_TEXT_TOO_LONG,
80
+ fix(fixer) {
81
+ const fixes = [];
82
+ const shortText = content.slice(0, 3);
83
+ if (text && typeof text === 'string') {
84
+ const textAttr = openingElement.attributes.find((a) => a.type === 'JSXAttribute' &&
85
+ a.name.name === 'text');
86
+ if (textAttr) {
87
+ fixes.push(fixer.replaceText(textAttr, `text="${shortText}" label="${content}"`));
88
+ }
89
+ }
90
+ else if (children) {
91
+ const textChild = node.children.find((c) => c.type === 'JSXText');
92
+ if (textChild) {
93
+ fixes.push(fixer.replaceText(textChild, shortText));
94
+ const lastAttr = openingElement.attributes[openingElement.attributes.length - 1];
95
+ const insertPos = lastAttr
96
+ ? lastAttr.range[1]
97
+ : openingElement.name.range[1];
98
+ fixes.push(fixer.insertTextAfterRange([insertPos, insertPos], ` label="${content}"`));
99
+ }
100
+ }
101
+ return fixes;
102
+ }
103
+ });
104
+ }
105
+ if (!label) {
106
+ context.report({
107
+ node: openingElement,
108
+ messageId: MESSAGE_IDS.BADGE_CORNER_MISSING_LABEL
109
+ });
110
+ }
111
+ };
112
+ return defineTemplateBodyVisitor(context, { VElement: checkBadge, Element: checkBadge }, { JSXElement: checkBadge });
113
+ }
114
+ };
@@ -0,0 +1,17 @@
1
+ import { MESSAGE_IDS } from '../../shared/constants.js';
2
+ declare const _default: {
3
+ meta: {
4
+ type: string;
5
+ docs: {
6
+ description: string;
7
+ url: string;
8
+ };
9
+ fixable: string;
10
+ messages: {
11
+ [MESSAGE_IDS.BADGE_NO_INLINE_IN_INTERACTIVE]: string;
12
+ };
13
+ schema: never[];
14
+ };
15
+ create(context: any): any;
16
+ };
17
+ export default _default;
@@ -0,0 +1,129 @@
1
+ import { COMPONENTS, MESSAGES, MESSAGE_IDS } from '../../shared/constants.js';
2
+ import { createAngularFix, createAngularVisitors, defineTemplateBodyVisitor, getAttributeValue, isDBComponent } from '../../shared/utils.js';
3
+ const INTERACTIVE_PARENTS = ['DBButton', 'DBLink', 'button', 'a'];
4
+ export default {
5
+ meta: {
6
+ type: 'problem',
7
+ docs: {
8
+ description: 'Prevent inline placement for DBBadge inside interactive elements',
9
+ url: 'https://github.com/db-ux-design-system/core-web/blob/main/packages/eslint-plugin/README.md#badge-no-inline-in-interactive'
10
+ },
11
+ fixable: 'code',
12
+ messages: {
13
+ [MESSAGE_IDS.BADGE_NO_INLINE_IN_INTERACTIVE]: MESSAGES.BADGE_NO_INLINE_IN_INTERACTIVE
14
+ },
15
+ schema: []
16
+ },
17
+ create(context) {
18
+ const angularHandler = (node, parserServices) => {
19
+ const placement = getAttributeValue(node, 'placement');
20
+ if (placement && placement !== 'inline')
21
+ return;
22
+ let parent = node.parent;
23
+ while (parent) {
24
+ if (parent.type === 'Element') {
25
+ const matchedParent = INTERACTIVE_PARENTS.find((p) => parent.name === p ||
26
+ parent.name === p.toLowerCase().replace('db', 'db-'));
27
+ if (matchedParent) {
28
+ const loc = parserServices.convertNodeSourceSpanToLoc(node.sourceSpan);
29
+ context.report({
30
+ loc,
31
+ messageId: MESSAGE_IDS.BADGE_NO_INLINE_IN_INTERACTIVE,
32
+ data: { parent: matchedParent },
33
+ fix(fixer) {
34
+ const fixData = createAngularFix(context, node, ' placement="corner-top-right"');
35
+ if (!fixData)
36
+ return null;
37
+ return fixer.insertTextBeforeRange([fixData.insertPos, fixData.insertPos], fixData.attributeText);
38
+ }
39
+ });
40
+ return;
41
+ }
42
+ }
43
+ parent = parent.parent;
44
+ }
45
+ };
46
+ const angularVisitors = createAngularVisitors(context, COMPONENTS.DBBadge, angularHandler);
47
+ if (angularVisitors)
48
+ return angularVisitors;
49
+ const checkBadge = (node) => {
50
+ const openingElement = node.openingElement || node;
51
+ if (!isDBComponent(openingElement, COMPONENTS.DBBadge))
52
+ return;
53
+ const placement = getAttributeValue(openingElement, 'placement');
54
+ if (placement && placement !== 'inline')
55
+ return;
56
+ let parent = node.parent;
57
+ while (parent) {
58
+ if (parent.type === 'JSXElement' ||
59
+ parent.type === 'VElement') {
60
+ const parentOpening = parent.openingElement || parent;
61
+ const parentName = parentOpening.name || parentOpening.rawName;
62
+ const name = typeof parentName === 'string'
63
+ ? parentName
64
+ : parentName.type === 'JSXIdentifier'
65
+ ? parentName.name
66
+ : null;
67
+ if (name) {
68
+ const matchedParent = INTERACTIVE_PARENTS.find((p) => name === p ||
69
+ name === p.toLowerCase().replace('db', 'db-'));
70
+ if (matchedParent) {
71
+ context.report({
72
+ node: openingElement,
73
+ messageId: MESSAGE_IDS.BADGE_NO_INLINE_IN_INTERACTIVE,
74
+ data: { parent: matchedParent },
75
+ fix(fixer) {
76
+ if (node.openingElement) {
77
+ // JSX
78
+ const placementAttr = openingElement.attributes.find((a) => a.type === 'JSXAttribute' &&
79
+ a.name.name === 'placement');
80
+ if (placementAttr) {
81
+ return fixer.replaceText(placementAttr, 'placement="corner-top-right"');
82
+ }
83
+ else {
84
+ const lastAttr = openingElement.attributes[openingElement.attributes
85
+ .length - 1];
86
+ const insertPos = lastAttr
87
+ ? lastAttr.range[1]
88
+ : openingElement.name.range[1];
89
+ return fixer.insertTextAfterRange([insertPos, insertPos], ' placement="corner-top-right"');
90
+ }
91
+ }
92
+ else {
93
+ // Vue
94
+ const placementAttr = openingElement.startTag.attributes.find((a) => a.key.name === 'placement');
95
+ if (placementAttr) {
96
+ return fixer.replaceText(placementAttr, 'placement="corner-top-right"');
97
+ }
98
+ else {
99
+ const attrs = openingElement.startTag
100
+ .attributes;
101
+ if (attrs.length > 0) {
102
+ const lastAttr = attrs[attrs.length - 1];
103
+ return fixer.insertTextAfterRange([
104
+ lastAttr.range[1],
105
+ lastAttr.range[1]
106
+ ], ' placement="corner-top-right"');
107
+ }
108
+ else {
109
+ const insertPos = openingElement.startTag
110
+ .range[0] +
111
+ 1 +
112
+ openingElement.rawName
113
+ .length;
114
+ return fixer.insertTextAfterRange([insertPos, insertPos], ' placement="corner-top-right"');
115
+ }
116
+ }
117
+ }
118
+ }
119
+ });
120
+ return;
121
+ }
122
+ }
123
+ }
124
+ parent = parent.parent;
125
+ }
126
+ };
127
+ return defineTemplateBodyVisitor(context, { VElement: checkBadge, Element: checkBadge }, { JSXElement: checkBadge });
128
+ }
129
+ };
@@ -0,0 +1,18 @@
1
+ import { MESSAGE_IDS } from '../../shared/constants.js';
2
+ declare const _default: {
3
+ meta: {
4
+ type: string;
5
+ docs: {
6
+ description: string;
7
+ url: string;
8
+ };
9
+ fixable: string;
10
+ messages: {
11
+ [MESSAGE_IDS.BUTTON_NO_TEXT_MISSING_ICON]: string;
12
+ [MESSAGE_IDS.BUTTON_NO_TEXT_MISSING_TOOLTIP]: string;
13
+ };
14
+ schema: never[];
15
+ };
16
+ create(context: any): any;
17
+ };
18
+ export default _default;
@@ -0,0 +1,109 @@
1
+ import { COMPONENTS, MESSAGES, MESSAGE_IDS } from '../../shared/constants.js';
2
+ import { createAngularVisitors, defineTemplateBodyVisitor, getAttributeValue, hasChildOfType, isDBComponent } from '../../shared/utils.js';
3
+ export default {
4
+ meta: {
5
+ type: 'problem',
6
+ docs: {
7
+ description: 'Ensure DBButton with noText has icon and DBTooltip child',
8
+ url: 'https://github.com/db-ux-design-system/core-web/blob/main/packages/eslint-plugin/README.md#button-no-text-requires-tooltip'
9
+ },
10
+ fixable: 'code',
11
+ messages: {
12
+ [MESSAGE_IDS.BUTTON_NO_TEXT_MISSING_ICON]: MESSAGES.BUTTON_NO_TEXT_MISSING_ICON,
13
+ [MESSAGE_IDS.BUTTON_NO_TEXT_MISSING_TOOLTIP]: MESSAGES.BUTTON_NO_TEXT_MISSING_TOOLTIP
14
+ },
15
+ schema: []
16
+ },
17
+ create(context) {
18
+ const angularHandler = (node, parserServices) => {
19
+ const noText = getAttributeValue(node, 'noText');
20
+ if (!noText)
21
+ return;
22
+ const icon = getAttributeValue(node, 'icon') ||
23
+ getAttributeValue(node, 'iconLeading') ||
24
+ getAttributeValue(node, 'iconTrailing');
25
+ const loc = parserServices.convertNodeSourceSpanToLoc(node.sourceSpan);
26
+ if (!icon) {
27
+ context.report({
28
+ loc,
29
+ messageId: MESSAGE_IDS.BUTTON_NO_TEXT_MISSING_ICON
30
+ });
31
+ }
32
+ const hasTooltip = hasChildOfType(node, COMPONENTS.DBTooltip);
33
+ if (!hasTooltip) {
34
+ context.report({
35
+ loc,
36
+ messageId: MESSAGE_IDS.BUTTON_NO_TEXT_MISSING_TOOLTIP,
37
+ fix(fixer) {
38
+ const sourceCode = context.sourceCode || context.getSourceCode();
39
+ const text = sourceCode.getText();
40
+ const startOffset = node.sourceSpan.start.offset;
41
+ const endOffset = node.sourceSpan.end.offset;
42
+ const tagText = text.substring(startOffset, endOffset);
43
+ const closeTagIndex = tagText.lastIndexOf('</db-button>');
44
+ if (closeTagIndex === -1)
45
+ return null;
46
+ const insertPos = startOffset + closeTagIndex;
47
+ return fixer.insertTextBeforeRange([insertPos, insertPos], '\n <db-tooltip>Describe action</db-tooltip>');
48
+ }
49
+ });
50
+ }
51
+ };
52
+ const angularVisitors = createAngularVisitors(context, COMPONENTS.DBButton, angularHandler);
53
+ if (angularVisitors)
54
+ return angularVisitors;
55
+ const checkButton = (node) => {
56
+ const openingElement = node.openingElement || node;
57
+ if (!isDBComponent(openingElement, COMPONENTS.DBButton))
58
+ return;
59
+ const noText = getAttributeValue(openingElement, 'noText');
60
+ if (!noText)
61
+ return;
62
+ const icon = getAttributeValue(openingElement, 'icon') ||
63
+ getAttributeValue(openingElement, 'iconLeading') ||
64
+ getAttributeValue(openingElement, 'iconTrailing');
65
+ if (!icon) {
66
+ context.report({
67
+ node: openingElement,
68
+ messageId: MESSAGE_IDS.BUTTON_NO_TEXT_MISSING_ICON
69
+ });
70
+ }
71
+ const hasTooltip = hasChildOfType(node, COMPONENTS.DBTooltip);
72
+ if (!hasTooltip) {
73
+ context.report({
74
+ node: openingElement,
75
+ messageId: MESSAGE_IDS.BUTTON_NO_TEXT_MISSING_TOOLTIP,
76
+ fix(fixer) {
77
+ if (node.openingElement) {
78
+ // JSX
79
+ const closingTag = node.closingElement;
80
+ if (!closingTag)
81
+ return null;
82
+ const componentName = openingElement.name.type === 'JSXIdentifier'
83
+ ? openingElement.name.name
84
+ : 'DBButton';
85
+ const tooltipName = componentName.includes('-')
86
+ ? 'db-tooltip'
87
+ : 'DBTooltip';
88
+ return fixer.insertTextBefore(closingTag, `\n <${tooltipName}>Describe action</${tooltipName}>`);
89
+ }
90
+ else {
91
+ // Vue
92
+ if (!node.endTag || !node.startTag?.range)
93
+ return null;
94
+ const componentName = openingElement.rawName;
95
+ const tooltipName = componentName.includes('-')
96
+ ? 'db-tooltip'
97
+ : 'DBTooltip';
98
+ return fixer.insertTextAfterRange([
99
+ node.startTag.range[1],
100
+ node.startTag.range[1]
101
+ ], `\n <${tooltipName}>Describe action</${tooltipName}>`);
102
+ }
103
+ }
104
+ });
105
+ }
106
+ };
107
+ return defineTemplateBodyVisitor(context, { VElement: checkButton, Element: checkButton }, { JSXElement: checkButton });
108
+ }
109
+ };
@@ -0,0 +1,16 @@
1
+ import { MESSAGE_IDS } from '../../shared/constants.js';
2
+ declare const _default: {
3
+ meta: {
4
+ type: string;
5
+ docs: {
6
+ description: string;
7
+ url: string;
8
+ };
9
+ messages: {
10
+ [MESSAGE_IDS.BUTTON_MULTIPLE_ICONS]: string;
11
+ };
12
+ schema: never[];
13
+ };
14
+ create(context: any): any;
15
+ };
16
+ export default _default;
@@ -0,0 +1,49 @@
1
+ import { createAngularVisitors, defineTemplateBodyVisitor, getAttributeValue, isDBComponent } from '../../shared/utils.js';
2
+ import { COMPONENTS, MESSAGES, MESSAGE_IDS } from '../../shared/constants.js';
3
+ export default {
4
+ meta: {
5
+ type: 'problem',
6
+ docs: {
7
+ description: 'Ensure DBButton uses only one icon attribute',
8
+ url: 'https://github.com/db-ux-design-system/core-web/blob/main/packages/eslint-plugin/README.md#button-single-icon-attribute'
9
+ },
10
+ messages: {
11
+ [MESSAGE_IDS.BUTTON_MULTIPLE_ICONS]: MESSAGES.BUTTON_MULTIPLE_ICONS
12
+ },
13
+ schema: []
14
+ },
15
+ create(context) {
16
+ const angularHandler = (node, parserServices) => {
17
+ const icon = getAttributeValue(node, 'icon');
18
+ const iconLeading = getAttributeValue(node, 'iconLeading');
19
+ const iconTrailing = getAttributeValue(node, 'iconTrailing');
20
+ const iconCount = [icon, iconLeading, iconTrailing].filter(Boolean).length;
21
+ if (iconCount > 1) {
22
+ const loc = parserServices.convertNodeSourceSpanToLoc(node.sourceSpan);
23
+ context.report({
24
+ loc,
25
+ messageId: MESSAGE_IDS.BUTTON_MULTIPLE_ICONS
26
+ });
27
+ }
28
+ };
29
+ const angularVisitors = createAngularVisitors(context, COMPONENTS.DBButton, angularHandler);
30
+ if (angularVisitors)
31
+ return angularVisitors;
32
+ const checkButton = (node) => {
33
+ const openingElement = node.openingElement || node;
34
+ if (!isDBComponent(openingElement, COMPONENTS.DBButton))
35
+ return;
36
+ const icon = getAttributeValue(openingElement, 'icon');
37
+ const iconLeading = getAttributeValue(openingElement, 'iconLeading');
38
+ const iconTrailing = getAttributeValue(openingElement, 'iconTrailing');
39
+ const iconCount = [icon, iconLeading, iconTrailing].filter(Boolean).length;
40
+ if (iconCount > 1) {
41
+ context.report({
42
+ node: openingElement,
43
+ messageId: MESSAGE_IDS.BUTTON_MULTIPLE_ICONS
44
+ });
45
+ }
46
+ };
47
+ return defineTemplateBodyVisitor(context, { VElement: checkButton, Element: checkButton }, { JSXElement: checkButton });
48
+ }
49
+ };
@@ -0,0 +1,17 @@
1
+ import { MESSAGE_IDS } from '../../shared/constants.js';
2
+ declare const _default: {
3
+ meta: {
4
+ type: string;
5
+ docs: {
6
+ description: string;
7
+ url: string;
8
+ };
9
+ fixable: string;
10
+ messages: {
11
+ [MESSAGE_IDS.BUTTON_TYPE_REQUIRED]: string;
12
+ };
13
+ schema: never[];
14
+ };
15
+ create(context: any): any;
16
+ };
17
+ export default _default;
@@ -0,0 +1,58 @@
1
+ import { COMPONENTS, MESSAGES, MESSAGE_IDS } from '../../shared/constants.js';
2
+ import { createAngularFix, createAngularVisitors, createJsxVueFix, defineTemplateBodyVisitor, getAttributeValue, isDBComponent } from '../../shared/utils.js';
3
+ export default {
4
+ meta: {
5
+ type: 'problem',
6
+ docs: {
7
+ description: 'Ensure DBButton has explicit type attribute',
8
+ url: 'https://github.com/db-ux-design-system/core-web/blob/main/packages/eslint-plugin/README.md#button-type-required'
9
+ },
10
+ fixable: 'code',
11
+ messages: {
12
+ [MESSAGE_IDS.BUTTON_TYPE_REQUIRED]: MESSAGES.BUTTON_TYPE_REQUIRED
13
+ },
14
+ schema: []
15
+ },
16
+ create(context) {
17
+ const angularHandler = (node, parserServices) => {
18
+ const type = getAttributeValue(node, 'type');
19
+ if (!type) {
20
+ const loc = parserServices.convertNodeSourceSpanToLoc(node.sourceSpan);
21
+ context.report({
22
+ loc,
23
+ messageId: MESSAGE_IDS.BUTTON_TYPE_REQUIRED,
24
+ fix(fixer) {
25
+ const fixData = createAngularFix(context, node, ' type="button"');
26
+ if (!fixData)
27
+ return null;
28
+ return fixer.insertTextBeforeRange([fixData.insertPos, fixData.insertPos], fixData.attributeText);
29
+ }
30
+ });
31
+ }
32
+ };
33
+ const angularVisitors = createAngularVisitors(context, COMPONENTS.DBButton, angularHandler);
34
+ if (angularVisitors)
35
+ return angularVisitors;
36
+ const checkButton = (node) => {
37
+ const openingElement = node.openingElement || node;
38
+ if (!isDBComponent(openingElement, COMPONENTS.DBButton))
39
+ return;
40
+ const type = getAttributeValue(openingElement, 'type');
41
+ if (type)
42
+ return;
43
+ const hasClickHandler = getAttributeValue(openingElement, 'onClick') ||
44
+ getAttributeValue(openingElement, '(click)') ||
45
+ getAttributeValue(openingElement, '@click');
46
+ const typeValue = hasClickHandler ? 'button' : 'submit';
47
+ context.report({
48
+ node: openingElement,
49
+ messageId: MESSAGE_IDS.BUTTON_TYPE_REQUIRED,
50
+ fix(fixer) {
51
+ const fixData = createJsxVueFix(node, openingElement, ` type="${typeValue}"`);
52
+ return fixer.insertTextAfterRange([fixData.insertPos, fixData.insertPos], fixData.attributeText);
53
+ }
54
+ });
55
+ };
56
+ return defineTemplateBodyVisitor(context, { VElement: checkButton, Element: checkButton }, { JSXElement: checkButton });
57
+ }
58
+ };
@@ -0,0 +1,16 @@
1
+ import { MESSAGE_IDS } from '../../shared/constants.js';
2
+ declare const _default: {
3
+ meta: {
4
+ type: string;
5
+ docs: {
6
+ description: string;
7
+ url: string;
8
+ };
9
+ messages: {
10
+ [MESSAGE_IDS.CLOSE_BUTTON_TEXT_REQUIRED]: string;
11
+ };
12
+ schema: never[];
13
+ };
14
+ create(context: any): any;
15
+ };
16
+ export default _default;
@@ -0,0 +1,38 @@
1
+ import { defineTemplateBodyVisitor, getAttributeValue, isDBComponent } from '../../shared/utils.js';
2
+ import { MESSAGES, MESSAGE_IDS } from '../../shared/constants.js';
3
+ const COMPONENTS_WITH_CLOSE_BUTTON = {
4
+ DBNotification: 'closeButtonText',
5
+ DBDrawer: 'closeButtonText',
6
+ DBCustomSelect: 'mobileCloseButtonText'
7
+ };
8
+ export default {
9
+ meta: {
10
+ type: 'problem',
11
+ docs: {
12
+ description: 'Ensure components have close button text for accessibility',
13
+ url: 'https://github.com/db-ux-design-system/core-web/blob/main/packages/eslint-plugin/README.md#close-button-text-required'
14
+ },
15
+ messages: {
16
+ [MESSAGE_IDS.CLOSE_BUTTON_TEXT_REQUIRED]: MESSAGES.CLOSE_BUTTON_TEXT_REQUIRED
17
+ },
18
+ schema: []
19
+ },
20
+ create(context) {
21
+ const checkComponent = (node) => {
22
+ const openingElement = node.openingElement || node;
23
+ const component = Object.keys(COMPONENTS_WITH_CLOSE_BUTTON).find((comp) => isDBComponent(openingElement, comp));
24
+ if (!component)
25
+ return;
26
+ const attribute = COMPONENTS_WITH_CLOSE_BUTTON[component];
27
+ const value = getAttributeValue(openingElement, attribute);
28
+ if (!value) {
29
+ context.report({
30
+ node: openingElement,
31
+ messageId: MESSAGE_IDS.CLOSE_BUTTON_TEXT_REQUIRED,
32
+ data: { component, attribute }
33
+ });
34
+ }
35
+ };
36
+ return defineTemplateBodyVisitor(context, { VElement: checkComponent, Element: checkComponent }, { JSXElement: checkComponent });
37
+ }
38
+ };
@@ -0,0 +1,16 @@
1
+ import { MESSAGE_IDS } from '../../shared/constants.js';
2
+ declare const _default: {
3
+ meta: {
4
+ type: string;
5
+ docs: {
6
+ description: string;
7
+ url: string;
8
+ };
9
+ messages: {
10
+ [MESSAGE_IDS.TEXT_OR_CHILDREN_REQUIRED]: string;
11
+ };
12
+ schema: never[];
13
+ };
14
+ create(context: any): any;
15
+ };
16
+ export default _default;
@@ -0,0 +1,47 @@
1
+ import { MESSAGES, MESSAGE_IDS } from '../../shared/constants.js';
2
+ import { defineTemplateBodyVisitor, getAttributeValue, isDBComponent } from '../../shared/utils.js';
3
+ const COMPONENTS_REQUIRING_CONTENT = [
4
+ 'DBAccordionItem',
5
+ 'DBButton',
6
+ 'DBLink',
7
+ 'DBIcon',
8
+ 'DBInfotext',
9
+ 'DBNavigationItem',
10
+ 'DBNotification'
11
+ ];
12
+ export default {
13
+ meta: {
14
+ type: 'problem',
15
+ docs: {
16
+ description: 'Ensure components have text property or children content',
17
+ url: 'https://github.com/db-ux-design-system/core-web/blob/main/packages/eslint-plugin/README.md#text-or-children-required'
18
+ },
19
+ messages: {
20
+ [MESSAGE_IDS.TEXT_OR_CHILDREN_REQUIRED]: MESSAGES.TEXT_OR_CHILDREN_REQUIRED
21
+ },
22
+ schema: []
23
+ },
24
+ create(context) {
25
+ const checkComponent = (node) => {
26
+ const openingElement = node.openingElement || node;
27
+ const component = COMPONENTS_REQUIRING_CONTENT.find((comp) => isDBComponent(openingElement, comp));
28
+ if (!component)
29
+ return;
30
+ const text = getAttributeValue(openingElement, 'text');
31
+ const hasChildren = node.children?.some((child) => (child.type === 'JSXText' && child.value.trim() !== '') ||
32
+ (child.type === 'VText' && child.value.trim() !== '') ||
33
+ child.type === 'JSXElement' ||
34
+ child.type === 'VElement' ||
35
+ child.type === 'JSXExpressionContainer' ||
36
+ child.type === 'VExpressionContainer');
37
+ if (!text && !hasChildren) {
38
+ context.report({
39
+ node: openingElement,
40
+ messageId: MESSAGE_IDS.TEXT_OR_CHILDREN_REQUIRED,
41
+ data: { component }
42
+ });
43
+ }
44
+ };
45
+ return defineTemplateBodyVisitor(context, { VElement: checkComponent, Element: checkComponent }, { JSXElement: checkComponent });
46
+ }
47
+ };