@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.
- package/CHANGELOG.md +1 -0
- package/README.md +843 -0
- package/build/index.d.ts +352 -0
- package/build/index.js +82 -0
- package/build/rules/accordion/accordion-item-headline-required.d.ts +16 -0
- package/build/rules/accordion/accordion-item-headline-required.js +61 -0
- package/build/rules/accordion/no-nested-accordion.d.ts +16 -0
- package/build/rules/accordion/no-nested-accordion.js +54 -0
- package/build/rules/badge/badge-corner-placement-rules.d.ts +18 -0
- package/build/rules/badge/badge-corner-placement-rules.js +114 -0
- package/build/rules/badge/badge-no-inline-in-interactive.d.ts +17 -0
- package/build/rules/badge/badge-no-inline-in-interactive.js +129 -0
- package/build/rules/button/button-no-text-requires-tooltip.d.ts +18 -0
- package/build/rules/button/button-no-text-requires-tooltip.js +109 -0
- package/build/rules/button/button-single-icon-attribute.d.ts +16 -0
- package/build/rules/button/button-single-icon-attribute.js +49 -0
- package/build/rules/button/button-type-required.d.ts +17 -0
- package/build/rules/button/button-type-required.js +58 -0
- package/build/rules/close-button/close-button-text-required.d.ts +16 -0
- package/build/rules/close-button/close-button-text-required.js +38 -0
- package/build/rules/content/text-or-children-required.d.ts +16 -0
- package/build/rules/content/text-or-children-required.js +47 -0
- package/build/rules/form/form-label-required.d.ts +16 -0
- package/build/rules/form/form-label-required.js +45 -0
- package/build/rules/form/form-validation-message-required.d.ts +16 -0
- package/build/rules/form/form-validation-message-required.js +91 -0
- package/build/rules/header/header-burger-menu-label-required.d.ts +16 -0
- package/build/rules/header/header-burger-menu-label-required.js +43 -0
- package/build/rules/icon/prefer-icon-attribute.d.ts +17 -0
- package/build/rules/icon/prefer-icon-attribute.js +82 -0
- package/build/rules/input/input-file-type-validation.d.ts +18 -0
- package/build/rules/input/input-file-type-validation.js +50 -0
- package/build/rules/input/input-type-required.d.ts +17 -0
- package/build/rules/input/input-type-required.js +54 -0
- package/build/rules/link/link-external-security.d.ts +19 -0
- package/build/rules/link/link-external-security.js +166 -0
- package/build/rules/navigation/navigation-item-back-button-text-required.d.ts +16 -0
- package/build/rules/navigation/navigation-item-back-button-text-required.js +43 -0
- package/build/rules/select/custom-select-tags-remove-text-required.d.ts +16 -0
- package/build/rules/select/custom-select-tags-remove-text-required.js +33 -0
- package/build/rules/select/select-requires-options.d.ts +16 -0
- package/build/rules/select/select-requires-options.js +45 -0
- package/build/rules/tag/tag-removable-remove-button-required.d.ts +16 -0
- package/build/rules/tag/tag-removable-remove-button-required.js +49 -0
- package/build/rules/tooltip/no-interactive-tooltip-content.d.ts +15 -0
- package/build/rules/tooltip/no-interactive-tooltip-content.js +49 -0
- package/build/rules/tooltip/tooltip-requires-interactive-parent.d.ts +15 -0
- package/build/rules/tooltip/tooltip-requires-interactive-parent.js +47 -0
- package/build/shared/constants.d.ts +58 -0
- package/build/shared/constants.js +98 -0
- package/build/shared/utils.d.ts +54 -0
- package/build/shared/utils.js +178 -0
- package/package.json +37 -1
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
function isVElement(node) {
|
|
2
|
+
return node.type === 'VElement';
|
|
3
|
+
}
|
|
4
|
+
function isAngularElement(node) {
|
|
5
|
+
return (node &&
|
|
6
|
+
typeof node.name === 'string' &&
|
|
7
|
+
(node.attributes || node.inputs));
|
|
8
|
+
}
|
|
9
|
+
export function getAttributeValue(node, attrName) {
|
|
10
|
+
const kebabAttrName = attrName
|
|
11
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
12
|
+
.toLowerCase();
|
|
13
|
+
if (isAngularElement(node)) {
|
|
14
|
+
const attr = node.attributes.find((a) => a.name === attrName || a.name === kebabAttrName);
|
|
15
|
+
if (attr) {
|
|
16
|
+
return attr.value === undefined || attr.value === ''
|
|
17
|
+
? true
|
|
18
|
+
: attr.value;
|
|
19
|
+
}
|
|
20
|
+
const input = node.inputs.find((i) => i.name === attrName || i.name === kebabAttrName);
|
|
21
|
+
if (input)
|
|
22
|
+
return true;
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
if (isVElement(node)) {
|
|
26
|
+
const attr = node.startTag.attributes.find((a) => {
|
|
27
|
+
if (a.key.name === 'bind' && a.key.argument?.name === attrName)
|
|
28
|
+
return true;
|
|
29
|
+
return (a.key.name === attrName ||
|
|
30
|
+
a.key.name === kebabAttrName ||
|
|
31
|
+
a.key.name === `:${attrName}` ||
|
|
32
|
+
a.key.name === `:${kebabAttrName}`);
|
|
33
|
+
});
|
|
34
|
+
if (!attr)
|
|
35
|
+
return null;
|
|
36
|
+
if (!attr.value)
|
|
37
|
+
return true;
|
|
38
|
+
return attr.value.value;
|
|
39
|
+
}
|
|
40
|
+
const variants = [attrName, `[${attrName}]`, `:${attrName}`];
|
|
41
|
+
const attr = node.attributes.find((a) => a.type === 'JSXAttribute' &&
|
|
42
|
+
variants.includes(a.name.name));
|
|
43
|
+
if (!attr)
|
|
44
|
+
return null;
|
|
45
|
+
if (!attr.value)
|
|
46
|
+
return true;
|
|
47
|
+
if (attr.value.type === 'Literal')
|
|
48
|
+
return attr.value.value;
|
|
49
|
+
if (attr.value.type === 'JSXExpressionContainer')
|
|
50
|
+
return true;
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
export function hasChildOfType(node, componentName) {
|
|
54
|
+
const kebabName = componentName
|
|
55
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
56
|
+
.toLowerCase();
|
|
57
|
+
if (isAngularElement(node)) {
|
|
58
|
+
return (node.children || []).some((child) => {
|
|
59
|
+
if (child.type === 'Element') {
|
|
60
|
+
return child.name === componentName || child.name === kebabName;
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if (isVElement(node)) {
|
|
66
|
+
return (node.children || []).some((child) => {
|
|
67
|
+
if (child.type === 'VElement') {
|
|
68
|
+
return (child.rawName === componentName ||
|
|
69
|
+
child.rawName === kebabName);
|
|
70
|
+
}
|
|
71
|
+
return false;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return node.children.some((child) => {
|
|
75
|
+
if (child.type === 'JSXElement') {
|
|
76
|
+
const openingElement = child.openingElement;
|
|
77
|
+
if (openingElement.name.type === 'JSXIdentifier') {
|
|
78
|
+
const name = openingElement.name.name;
|
|
79
|
+
return name === componentName || name === kebabName;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
export function isDBComponent(node, componentName) {
|
|
86
|
+
const kebabName = componentName
|
|
87
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
88
|
+
.toLowerCase();
|
|
89
|
+
if (isAngularElement(node)) {
|
|
90
|
+
return node.name === componentName || node.name === kebabName;
|
|
91
|
+
}
|
|
92
|
+
if (isVElement(node)) {
|
|
93
|
+
return node.rawName === componentName || node.rawName === kebabName;
|
|
94
|
+
}
|
|
95
|
+
if (node.name.type !== 'JSXIdentifier')
|
|
96
|
+
return false;
|
|
97
|
+
const name = node.name.name;
|
|
98
|
+
return name === componentName || name === kebabName;
|
|
99
|
+
}
|
|
100
|
+
export function defineTemplateBodyVisitor(context, templateVisitor, scriptVisitor) {
|
|
101
|
+
const sourceCode = context.sourceCode || context.getSourceCode();
|
|
102
|
+
// Vue templates
|
|
103
|
+
if (sourceCode.parserServices?.defineTemplateBodyVisitor) {
|
|
104
|
+
return sourceCode.parserServices.defineTemplateBodyVisitor(templateVisitor, scriptVisitor || {});
|
|
105
|
+
}
|
|
106
|
+
// Angular templates
|
|
107
|
+
if (sourceCode.parserServices?.convertNodeSourceSpanToLoc) {
|
|
108
|
+
const angularVisitors = {};
|
|
109
|
+
for (const [key, handler] of Object.entries(templateVisitor)) {
|
|
110
|
+
if (key === 'VElement' || key === 'Element') {
|
|
111
|
+
angularVisitors['Element'] = handler;
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
angularVisitors[key] = handler;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return angularVisitors;
|
|
118
|
+
}
|
|
119
|
+
// JSX
|
|
120
|
+
return scriptVisitor || {};
|
|
121
|
+
}
|
|
122
|
+
export function createAngularVisitors(context, componentName, handler) {
|
|
123
|
+
const sourceCode = context.sourceCode || context.getSourceCode();
|
|
124
|
+
const parserServices = sourceCode?.parserServices;
|
|
125
|
+
const isAngular = parserServices?.convertNodeSourceSpanToLoc;
|
|
126
|
+
if (!isAngular) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
// For DB components, convert DBComponentName -> db-component-name
|
|
130
|
+
const kebabName = componentName.startsWith('DB')
|
|
131
|
+
? 'db-' +
|
|
132
|
+
componentName
|
|
133
|
+
.slice(2)
|
|
134
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
135
|
+
.toLowerCase()
|
|
136
|
+
: componentName.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
|
137
|
+
const wrappedHandler = (node) => handler(node, parserServices);
|
|
138
|
+
return {
|
|
139
|
+
[`Element[name="${kebabName}"]`]: wrappedHandler,
|
|
140
|
+
[`Element[name="${componentName}"]`]: wrappedHandler
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
export function createAngularFix(context, node, attributeText) {
|
|
144
|
+
const sourceCode = context.sourceCode || context.getSourceCode();
|
|
145
|
+
const text = sourceCode.getText();
|
|
146
|
+
const startOffset = node.sourceSpan.start.offset;
|
|
147
|
+
const endOffset = node.sourceSpan.end.offset;
|
|
148
|
+
const tagText = text.substring(startOffset, endOffset);
|
|
149
|
+
const closeTagIndex = tagText.indexOf('>');
|
|
150
|
+
if (closeTagIndex === -1)
|
|
151
|
+
return null;
|
|
152
|
+
const insertPos = startOffset + closeTagIndex;
|
|
153
|
+
return { insertPos, attributeText };
|
|
154
|
+
}
|
|
155
|
+
export function createJsxVueFix(node, openingElement, attributeText) {
|
|
156
|
+
if (node.openingElement) {
|
|
157
|
+
// JSX
|
|
158
|
+
const lastAttr = openingElement.attributes[openingElement.attributes.length - 1];
|
|
159
|
+
const insertPos = lastAttr
|
|
160
|
+
? lastAttr.range[1]
|
|
161
|
+
: openingElement.name.range[1];
|
|
162
|
+
return { insertPos, attributeText };
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
// Vue
|
|
166
|
+
const attrs = openingElement.startTag.attributes;
|
|
167
|
+
if (attrs.length > 0) {
|
|
168
|
+
const lastAttr = attrs[attrs.length - 1];
|
|
169
|
+
return { insertPos: lastAttr.range[1], attributeText };
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
const insertPos = openingElement.startTag.range[0] +
|
|
173
|
+
1 +
|
|
174
|
+
openingElement.rawName.length;
|
|
175
|
+
return { insertPos, attributeText };
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@db-ux/core-eslint-plugin",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.4.2-eslint-plugin2-696cb23",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "ESLint plugin for DB UX Design System components",
|
|
6
6
|
"repository": {
|
|
@@ -8,11 +8,47 @@
|
|
|
8
8
|
"url": "git+https://github.com/db-ux-design-system/core-web.git"
|
|
9
9
|
},
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./build/index.d.ts",
|
|
14
|
+
"default": "./build/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"CHANGELOG.md",
|
|
19
|
+
"build"
|
|
20
|
+
],
|
|
11
21
|
"keywords": [
|
|
12
22
|
"eslint",
|
|
13
23
|
"eslint-plugin",
|
|
14
24
|
"db-ux"
|
|
15
25
|
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc",
|
|
28
|
+
"copy-output": "npm-run-all copy:*",
|
|
29
|
+
"copy:changelog": "cpr CHANGELOG.md ../../build-outputs/eslint-plugin/CHANGELOG.md --overwrite",
|
|
30
|
+
"copy:outputs": "cpr build ../../build-outputs/eslint-plugin/build -o",
|
|
31
|
+
"copy:package.json": "cpr package.json ../../build-outputs/eslint-plugin/package.json -o",
|
|
32
|
+
"copy:readme": "cpr README.md ../../build-outputs/eslint-plugin/README.md -o",
|
|
33
|
+
"test": "vitest run --config vitest.config.ts",
|
|
34
|
+
"test:update": "vitest run --config vitest.config.ts --update"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"eslint": "^9.0.0 || ^10.0.0"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@angular-eslint/utils": "20.7.0",
|
|
41
|
+
"@typescript-eslint/utils": "8.54.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@angular-eslint/bundled-angular-compiler": "20.7.0",
|
|
45
|
+
"@angular-eslint/template-parser": "20.3.0",
|
|
46
|
+
"@typescript-eslint/parser": "8.54.0",
|
|
47
|
+
"@typescript-eslint/rule-tester": "8.54.0",
|
|
48
|
+
"typescript": "5.9.3",
|
|
49
|
+
"vitest": "3.2.4",
|
|
50
|
+
"vue-eslint-parser": "9.4.3"
|
|
51
|
+
},
|
|
16
52
|
"publishConfig": {
|
|
17
53
|
"registry": "https://registry.npmjs.org/",
|
|
18
54
|
"access": "public"
|