@adyen/eslint-plugin-bento 2.22.0 → 2.24.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/CHANGELOG.md +33 -0
- package/README.md +31 -15
- package/configs/vue-recommended.js +16 -0
- package/configs/vue-strict.js +17 -1
- package/index.js +3 -0
- package/package.json +3 -3
- package/rules/deprecation-bento-tag-variant-enum/README.md +69 -0
- package/rules/deprecation-bento-tag-variant-enum/deprecation-bento-tag-variant-enum.js +167 -0
- package/rules/deprecation-filter-bar-deprecated-props/README.md +7 -0
- package/rules/deprecation-filter-bar-deprecated-props/deprecation-filter-bar-deprecated-props.js +14 -3
- package/rules/vue-no-redundant-dialog-v-if/README.md +55 -0
- package/rules/vue-no-redundant-dialog-v-if/vue-no-redundant-dialog-v-if.js +150 -0
- package/rules/vue-no-use-bento-translation-overrides/README.md +14 -0
- package/rules/vue-no-use-bento-translation-overrides/vue-no-use-bento-translation-overrides.js +93 -0
- package/utils/component-slot-migration.js +22 -4
- package/rules/deprecation-bento-data-grid-cell-action-menu-item-handler/__tests__/deprecation-bento-data-grid-cell-action-menu-item-handler.vue +0 -63
- package/rules/deprecation-bento-data-grid-cell-action-menu-item-handler/__tests__/eslint.config.js +0 -26
- package/rules/deprecation-bento-data-grid-cell-action-menu-item-handler/deprecation-bento-data-grid-cell-action-menu-item-handler.integration.test.ts +0 -24
- package/rules/deprecation-bento-data-grid-cell-action-menu-item-handler/deprecation-bento-data-grid-cell-action-menu-item-handler.test.ts +0 -116
- package/rules/deprecation-bento-date-range-picker-has-actions-prop/__tests__/deprecation-bento-date-range-picker-has-actions-prop.vue +0 -10
- package/rules/deprecation-bento-date-range-picker-has-actions-prop/__tests__/eslint.config.js +0 -29
- package/rules/deprecation-bento-date-range-picker-has-actions-prop/__tests__/remove/deprecation-bento-date-range-picker-has-actions-prop.vue +0 -11
- package/rules/deprecation-bento-date-range-picker-has-actions-prop/__tests__/remove/eslint.config.js +0 -29
- package/rules/deprecation-bento-date-range-picker-has-actions-prop/deprecation-bento-date-range-picker-has-actions-prop.integration.test.ts +0 -47
- package/rules/deprecation-bento-date-range-picker-has-actions-prop/deprecation-bento-date-range-picker-has-actions-prop.test.ts +0 -176
- package/rules/deprecation-bento-dropdown-size-prop/__tests__/deprecation-bento-dropdown-size-prop.vue +0 -9
- package/rules/deprecation-bento-dropdown-size-prop/__tests__/eslint.config.js +0 -26
- package/rules/deprecation-bento-dropdown-size-prop/deprecation-bento-dropdown-size-prop.integration.test.ts +0 -33
- package/rules/deprecation-bento-dropdown-size-prop/deprecation-bento-dropdown-size-prop.test.ts +0 -101
- package/rules/deprecation-bento-popover-modifiers-prop/__tests__/deprecation-bento-popover-modifiers-prop.vue +0 -9
- package/rules/deprecation-bento-popover-modifiers-prop/__tests__/eslint.config.js +0 -26
- package/rules/deprecation-bento-popover-modifiers-prop/deprecation-bento-popover-modifiers-prop.integration.test.ts +0 -24
- package/rules/deprecation-bento-popover-modifiers-prop/deprecation-bento-popover-modifiers-prop.test.ts +0 -109
- package/rules/deprecation-bento-timeline-item-variant-prop/__tests__/deprecation-bento-timeline-item-variant-prop.vue +0 -9
- package/rules/deprecation-bento-timeline-item-variant-prop/__tests__/eslint.config.js +0 -26
- package/rules/deprecation-bento-timeline-item-variant-prop/deprecation-bento-timeline-item-variant-prop.integration.test.ts +0 -35
- package/rules/deprecation-bento-timeline-item-variant-prop/deprecation-bento-timeline-item-variant-prop.test.ts +0 -141
- package/rules/deprecation-components-aria-label-prop/__tests__/deprecation-components-aria-label-prop.vue +0 -25
- package/rules/deprecation-components-aria-label-prop/__tests__/eslint.config.js +0 -26
- package/rules/deprecation-components-aria-label-prop/deprecation-components-aria-label-prop.integration.test.ts +0 -36
- package/rules/deprecation-components-aria-label-prop/deprecation-components-aria-label-prop.test.ts +0 -194
- package/rules/deprecation-components-error-prop/__tests__/deprecation-components-error-prop.vue +0 -17
- package/rules/deprecation-components-error-prop/__tests__/eslint.config.js +0 -26
- package/rules/deprecation-components-error-prop/deprecation-components-error-prop.integration.test.ts +0 -41
- package/rules/deprecation-components-error-prop/deprecation-components-error-prop.test.ts +0 -153
- package/rules/deprecation-components-has-error-prop/__tests__/deprecation-components-has-error-prop.vue +0 -13
- package/rules/deprecation-components-has-error-prop/__tests__/eslint.config.js +0 -26
- package/rules/deprecation-components-has-error-prop/deprecation-components-has-error-prop.integration.test.ts +0 -37
- package/rules/deprecation-components-has-error-prop/deprecation-components-has-error-prop.test.ts +0 -122
- package/rules/deprecation-components-input-emit/__tests__/deprecation-components-input-emit.vue +0 -22
- package/rules/deprecation-components-input-emit/__tests__/eslint.config.js +0 -25
- package/rules/deprecation-components-input-emit/deprecation-components-input-emit.integration.test.ts +0 -43
- package/rules/deprecation-components-input-emit/deprecation-components-input-emit.test.ts +0 -320
- package/rules/deprecation-components-value-prop/__tests__/deprecation-components-value-prop.vue +0 -69
- package/rules/deprecation-components-value-prop/__tests__/eslint.config.js +0 -25
- package/rules/deprecation-components-value-prop/deprecation-components-value-prop.integration.test.ts +0 -37
- package/rules/deprecation-components-value-prop/deprecation-components-value-prop.test.ts +0 -480
- package/rules/deprecation-country-custom-usage/__tests__/deprecation-country-custom-usage.vue +0 -93
- package/rules/deprecation-country-custom-usage/__tests__/eslint.config.js +0 -25
- package/rules/deprecation-country-custom-usage/deprecation-country-custom-usage.integration.test.ts +0 -22
- package/rules/deprecation-country-custom-usage/deprecation-country-custom-usage.test.ts +0 -380
- package/rules/deprecation-currency-default-slot/__tests__/deprecation-currency-default-slot.vue +0 -7
- package/rules/deprecation-currency-default-slot/__tests__/eslint.config.js +0 -24
- package/rules/deprecation-currency-default-slot/deprecation-currency-default-slot.integration.test.ts +0 -29
- package/rules/deprecation-currency-default-slot/deprecation-currency-default-slot.test.ts +0 -136
- package/rules/deprecation-filter-bar-deprecated-props/__tests__/deprecation-filter-bar-deprecated-props.vue +0 -62
- package/rules/deprecation-filter-bar-deprecated-props/__tests__/eslint.config.js +0 -25
- package/rules/deprecation-filter-bar-deprecated-props/deprecation-filter-bar-deprecated-props.integration.test.ts +0 -22
- package/rules/deprecation-filter-bar-deprecated-props/deprecation-filter-bar-deprecated-props.test.ts +0 -467
- package/rules/deprecation-input-filter-dropdown-options/__tests__/deprecation-input-filter-dropdown-options.vue +0 -46
- package/rules/deprecation-input-filter-dropdown-options/__tests__/eslint.config.js +0 -26
- package/rules/deprecation-input-filter-dropdown-options/deprecation-input-filter-dropdown-options.integration.test.ts +0 -27
- package/rules/deprecation-input-filter-dropdown-options/deprecation-input-filter-dropdown-options.test.ts +0 -392
- package/rules/deprecation-tag-default-slot/__tests__/deprecation-tag-default-slot.vue +0 -5
- package/rules/deprecation-tag-default-slot/__tests__/eslint.config.js +0 -24
- package/rules/deprecation-tag-default-slot/deprecation-tag-default-slot.integration.test.ts +0 -29
- package/rules/deprecation-tag-default-slot/deprecation-tag-default-slot.test.ts +0 -145
- package/rules/tailwind-v3-to-bento/__tests__/eslint.config.js +0 -24
- package/rules/tailwind-v3-to-bento/__tests__/tailwind-v3-to-bento.vue +0 -15
- package/rules/tailwind-v3-to-bento/tailwind-v3-to-bento.integration.test.ts +0 -29
- package/rules/tailwind-v3-to-bento/tailwind-v3-to-bento.test.ts +0 -403
- package/rules/vue-old-spacer-tokens/__tests__/eslint.config.js +0 -24
- package/rules/vue-old-spacer-tokens/__tests__/vue-old-spacer-tokens.vue +0 -5
- package/rules/vue-old-spacer-tokens/vue-old-spacer-tokens.integration.test.ts +0 -29
- package/rules/vue-old-spacer-tokens/vue-old-spacer-tokens.test.ts +0 -77
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('eslint').Rule.RuleContext} RuleContext
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {import('vue-eslint-parser').AST.VDirective} VDirective
|
|
6
|
+
* @typedef {import('vue-eslint-parser').AST.VElement} VElement
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {import('eslint-plugin-vue/lib/utils').RuleModule} RuleModule
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const VueUtils = require('eslint-plugin-vue/lib/utils');
|
|
13
|
+
|
|
14
|
+
// Components rendering a native "<dialog>" element, which is hidden by the browser while closed
|
|
15
|
+
const DIALOG_COMPONENTS = ['bento-modal', 'bento-modal-fullscreen', 'bento-sidepanel'];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Normalizes a tag or attribute name so "BentoModal" and "bento-modal" are treated alike.
|
|
19
|
+
* @param {string} name
|
|
20
|
+
* @returns {string}
|
|
21
|
+
*/
|
|
22
|
+
function toKebabCase(name) {
|
|
23
|
+
return name.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Collapses whitespace so expressions spanning several lines can be compared.
|
|
28
|
+
* @param {string} expression
|
|
29
|
+
* @returns {string}
|
|
30
|
+
*/
|
|
31
|
+
function normalizeExpression(expression) {
|
|
32
|
+
return expression.replace(/\s+/g, ' ').trim();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Finds a directive by name, e.g. "if" for "v-if" or "bind" for ":is-open".
|
|
37
|
+
* @param {VElement} node
|
|
38
|
+
* @param {string} directiveName
|
|
39
|
+
* @returns {VDirective | undefined}
|
|
40
|
+
*/
|
|
41
|
+
function findDirective(node, directiveName) {
|
|
42
|
+
return node.startTag.attributes.find(attribute => attribute.directive && attribute.key.name.name === directiveName);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Finds the "is-open" binding, either as ":is-open" or as "v-bind:isOpen".
|
|
47
|
+
* @param {VElement} node
|
|
48
|
+
* @returns {VDirective | undefined}
|
|
49
|
+
*/
|
|
50
|
+
function findIsOpenBinding(node) {
|
|
51
|
+
return node.startTag.attributes.find(
|
|
52
|
+
attribute =>
|
|
53
|
+
attribute.directive &&
|
|
54
|
+
attribute.key.name.name === 'bind' &&
|
|
55
|
+
attribute.key.argument &&
|
|
56
|
+
attribute.key.argument.type === 'VIdentifier' &&
|
|
57
|
+
toKebabCase(attribute.key.argument.rawName) === 'is-open'
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Checks whether the element right after the given one relies on its "v-if",
|
|
63
|
+
* in which case removing the directive would break the template.
|
|
64
|
+
* @param {VElement} node
|
|
65
|
+
* @returns {boolean}
|
|
66
|
+
*/
|
|
67
|
+
function hasElseBranch(node) {
|
|
68
|
+
const siblings = node.parent.children;
|
|
69
|
+
const nextElement = siblings.slice(siblings.indexOf(node) + 1).find(sibling => sibling.type === 'VElement');
|
|
70
|
+
|
|
71
|
+
if (!nextElement) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return !!(findDirective(nextElement, 'else') || findDirective(nextElement, 'else-if'));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** @type {RuleModule} */
|
|
79
|
+
module.exports = {
|
|
80
|
+
meta: {
|
|
81
|
+
type: 'suggestion',
|
|
82
|
+
docs: {
|
|
83
|
+
description: 'Disallow a redundant "v-if" next to "is-open" on components rendering a native dialog',
|
|
84
|
+
category: 'Best Practices',
|
|
85
|
+
url: 'https://github.com/Adyen/bento/blob/main/packages/eslint/rules/vue-no-redundant-dialog-v-if/README.md',
|
|
86
|
+
recommended: true,
|
|
87
|
+
},
|
|
88
|
+
messages: {
|
|
89
|
+
redundantVIf: `"v-if" is redundant on "{{ component }}" and unmounts it before the closing animation can run. The underlying "<dialog>" element stays hidden while closed, so "is-open" alone controls its visibility.`,
|
|
90
|
+
suggestRemoveVIf: `Remove the redundant "v-if" directive`,
|
|
91
|
+
},
|
|
92
|
+
hasSuggestions: true,
|
|
93
|
+
schema: [],
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
/** @param {RuleContext} context */
|
|
97
|
+
create(context) {
|
|
98
|
+
const sourceCode = context.sourceCode;
|
|
99
|
+
const template =
|
|
100
|
+
sourceCode.parserServices.getTemplateBodyTokenStore &&
|
|
101
|
+
sourceCode.parserServices.getTemplateBodyTokenStore();
|
|
102
|
+
|
|
103
|
+
return VueUtils.defineTemplateBodyVisitor(context, {
|
|
104
|
+
/** @param {VElement} node */
|
|
105
|
+
VElement(node) {
|
|
106
|
+
if (!DIALOG_COMPONENTS.includes(toKebabCase(node.rawName))) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const vIf = findDirective(node, 'if');
|
|
111
|
+
const isOpen = findIsOpenBinding(node);
|
|
112
|
+
const vIfExpression = vIf && vIf.value && vIf.value.expression;
|
|
113
|
+
const isOpenExpression = isOpen && isOpen.value && isOpen.value.expression;
|
|
114
|
+
|
|
115
|
+
if (!vIfExpression || !isOpenExpression) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Only a "v-if" driven by the very same value as "is-open" is provably redundant
|
|
120
|
+
if (
|
|
121
|
+
normalizeExpression(sourceCode.getText(vIfExpression)) !==
|
|
122
|
+
normalizeExpression(sourceCode.getText(isOpenExpression))
|
|
123
|
+
) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (hasElseBranch(node)) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
context.report({
|
|
132
|
+
node: vIf,
|
|
133
|
+
messageId: 'redundantVIf',
|
|
134
|
+
data: { component: toKebabCase(node.rawName) },
|
|
135
|
+
suggest: [
|
|
136
|
+
{
|
|
137
|
+
messageId: 'suggestRemoveVIf',
|
|
138
|
+
fix(fixer) {
|
|
139
|
+
// Removing from the previous token takes the leading whitespace
|
|
140
|
+
// or line break with it, keeping the start tag tidy
|
|
141
|
+
const tokenBefore = template.getTokenBefore(vIf);
|
|
142
|
+
return fixer.removeRange([tokenBefore.range[1], vIf.range[1]]);
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
});
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
},
|
|
150
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Disallow unapproved translation overrides
|
|
2
|
+
|
|
3
|
+
The `@adyen/bento/vue-no-use-bento-translation-overrides` rule prevents importing `useBentoTranslationOverrides` from
|
|
4
|
+
`@adyen/bento-vue3`.
|
|
5
|
+
|
|
6
|
+
This restricted composable is only intended for teams building SDKs or component libraries for external customers. It
|
|
7
|
+
must not be used in Adyen products. Teams must request approval from the Bento team before using it.
|
|
8
|
+
|
|
9
|
+
After approval, suppress the rule only around the approved import and include the reason:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
// eslint-disable-next-line @adyen/bento/vue-no-use-bento-translation-overrides -- Approved by the Bento team for the customer SDK.
|
|
13
|
+
import { useBentoTranslationOverrides } from '@adyen/bento-vue3';
|
|
14
|
+
```
|
package/rules/vue-no-use-bento-translation-overrides/vue-no-use-bento-translation-overrides.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('eslint').Rule.RuleContext} RuleContext
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {import('eslint').Rule.RuleModule} RuleModule
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const BENTO_VUE3_PACKAGE = '@adyen/bento-vue3';
|
|
9
|
+
const RESTRICTED_COMPOSABLE = 'useBentoTranslationOverrides';
|
|
10
|
+
|
|
11
|
+
/** @type {RuleModule} */
|
|
12
|
+
module.exports = {
|
|
13
|
+
meta: {
|
|
14
|
+
type: 'problem',
|
|
15
|
+
docs: {
|
|
16
|
+
description: `Restricts use of ${RESTRICTED_COMPOSABLE} to approved customer-facing SDKs and component libraries.`,
|
|
17
|
+
category: 'Restrictions',
|
|
18
|
+
url: 'https://github.com/Adyen/bento/blob/main/packages/eslint/rules/vue-no-use-bento-translation-overrides/README.md',
|
|
19
|
+
recommended: true,
|
|
20
|
+
},
|
|
21
|
+
messages: {
|
|
22
|
+
restricted:
|
|
23
|
+
'`useBentoTranslationOverrides` is restricted to teams building SDKs or component libraries for external customers and must not be used in Adyen products. Request approval from the Bento team before using this composable.',
|
|
24
|
+
},
|
|
25
|
+
schema: [],
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
/** @param {RuleContext} context */
|
|
29
|
+
create(context) {
|
|
30
|
+
const namespaceImports = new Set();
|
|
31
|
+
|
|
32
|
+
const getImportedName = specifier => {
|
|
33
|
+
if (specifier.type !== 'ImportSpecifier' && specifier.type !== 'ExportSpecifier') {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const imported = specifier.type === 'ImportSpecifier' ? specifier.imported : specifier.local;
|
|
38
|
+
return imported.type === 'Identifier' ? imported.name : imported.value;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const reportRestrictedSpecifier = specifier => {
|
|
42
|
+
if (getImportedName(specifier) === RESTRICTED_COMPOSABLE) {
|
|
43
|
+
context.report({
|
|
44
|
+
node: specifier,
|
|
45
|
+
messageId: 'restricted',
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
ImportDeclaration(node) {
|
|
52
|
+
if (node.source.value !== BENTO_VUE3_PACKAGE) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
for (const specifier of node.specifiers) {
|
|
57
|
+
if (specifier.type === 'ImportNamespaceSpecifier') {
|
|
58
|
+
namespaceImports.add(specifier.local.name);
|
|
59
|
+
} else {
|
|
60
|
+
reportRestrictedSpecifier(specifier);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
ExportNamedDeclaration(node) {
|
|
65
|
+
if (node.source?.value !== BENTO_VUE3_PACKAGE) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
node.specifiers.forEach(reportRestrictedSpecifier);
|
|
70
|
+
},
|
|
71
|
+
MemberExpression(node) {
|
|
72
|
+
if (node.object.type !== 'Identifier' || !namespaceImports.has(node.object.name)) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const propertyName = node.computed
|
|
77
|
+
? node.property.type === 'Literal'
|
|
78
|
+
? node.property.value
|
|
79
|
+
: null
|
|
80
|
+
: node.property.type === 'Identifier'
|
|
81
|
+
? node.property.name
|
|
82
|
+
: null;
|
|
83
|
+
|
|
84
|
+
if (propertyName === RESTRICTED_COMPOSABLE) {
|
|
85
|
+
context.report({
|
|
86
|
+
node,
|
|
87
|
+
messageId: 'restricted',
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
};
|
|
@@ -6,6 +6,7 @@ const j = require('jscodeshift');
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
+
* @typedef {import('vue-eslint-parser').AST.VElement} VElement
|
|
9
10
|
* @typedef {import('vue-eslint-parser').AST.VText} VText
|
|
10
11
|
* @typedef {import('vue-eslint-parser').AST.VExpressionContainer} VExpressionContainer
|
|
11
12
|
*/
|
|
@@ -26,10 +27,23 @@ module.exports = {
|
|
|
26
27
|
*/
|
|
27
28
|
componentSlotMigration({ propertyName, context, template, alwaysExpression = false }) {
|
|
28
29
|
return node => {
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
/**
|
|
31
|
+
* @param {VElement | VText | VExpressionContainer} child
|
|
32
|
+
* @returns {boolean}
|
|
33
|
+
*/
|
|
34
|
+
function isNamedSlot(child) {
|
|
35
|
+
return (
|
|
36
|
+
child.type === 'VElement' &&
|
|
37
|
+
child.name === 'template' &&
|
|
38
|
+
child.startTag.attributes.some(
|
|
39
|
+
attribute => attribute.directive && attribute.key.name.name === 'slot'
|
|
40
|
+
)
|
|
41
|
+
);
|
|
31
42
|
}
|
|
32
43
|
|
|
44
|
+
// Named slots are not default slot content and should not trigger migration.
|
|
45
|
+
const contentNodes = node.children.filter(child => !isNamedSlot(child));
|
|
46
|
+
|
|
33
47
|
// Turned true if one of the children is an expression
|
|
34
48
|
let hasExpression = false;
|
|
35
49
|
|
|
@@ -72,7 +86,11 @@ module.exports = {
|
|
|
72
86
|
|
|
73
87
|
// Remove empty nodes
|
|
74
88
|
/** @type { Array<VText | VExpressionContainer> } */
|
|
75
|
-
const filteredNodes =
|
|
89
|
+
const filteredNodes = contentNodes.filter(removeEmptyVTextNodes);
|
|
90
|
+
|
|
91
|
+
if (!filteredNodes.length) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
76
94
|
|
|
77
95
|
// Get all content and concat
|
|
78
96
|
const slotContent = (() => {
|
|
@@ -80,7 +98,7 @@ module.exports = {
|
|
|
80
98
|
return transformNodeToText(filteredNodes[0])?.trim();
|
|
81
99
|
}
|
|
82
100
|
|
|
83
|
-
const content =
|
|
101
|
+
const content = contentNodes
|
|
84
102
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
85
103
|
.map((/** @type {VText | VExpressionContainer} */ node) => transformNodeToText(node, true))
|
|
86
104
|
.join('')
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div>
|
|
3
|
-
<!-- Inline expression: should warn (2 handlers) -->
|
|
4
|
-
<bento-data-grid
|
|
5
|
-
:cell-actions="[
|
|
6
|
-
{
|
|
7
|
-
variant: 'more',
|
|
8
|
-
menuData: [
|
|
9
|
-
{
|
|
10
|
-
text: 'Cancel payment',
|
|
11
|
-
handler: () => {
|
|
12
|
-
return;
|
|
13
|
-
},
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
text: 'Transfer payment',
|
|
17
|
-
handler: () => {
|
|
18
|
-
return;
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
|
-
],
|
|
22
|
-
},
|
|
23
|
-
]"
|
|
24
|
-
:data="[]"
|
|
25
|
-
:columns="[]"
|
|
26
|
-
/>
|
|
27
|
-
|
|
28
|
-
<!-- Identifier reference: should warn (1 handler) -->
|
|
29
|
-
<bento-data-grid :cell-actions="rowActions" :data="[]" :columns="[]" />
|
|
30
|
-
|
|
31
|
-
<!-- Non-data-grid component: should NOT warn -->
|
|
32
|
-
<test-component
|
|
33
|
-
:cell-actions="[
|
|
34
|
-
{
|
|
35
|
-
menuData: [
|
|
36
|
-
{
|
|
37
|
-
text: 'Action',
|
|
38
|
-
handler: () => {
|
|
39
|
-
return;
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
],
|
|
43
|
-
},
|
|
44
|
-
]"
|
|
45
|
-
/>
|
|
46
|
-
</div>
|
|
47
|
-
</template>
|
|
48
|
-
|
|
49
|
-
<script setup>
|
|
50
|
-
const rowActions = [
|
|
51
|
-
{
|
|
52
|
-
variant: 'more',
|
|
53
|
-
menuData: [
|
|
54
|
-
{
|
|
55
|
-
text: 'Refund',
|
|
56
|
-
handler: () => {
|
|
57
|
-
return;
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
],
|
|
61
|
-
},
|
|
62
|
-
];
|
|
63
|
-
</script>
|
package/rules/deprecation-bento-data-grid-cell-action-menu-item-handler/__tests__/eslint.config.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
// eslint.config.js
|
|
2
|
-
const { defineConfig } = require('eslint/config');
|
|
3
|
-
const vueParser = require('vue-eslint-parser');
|
|
4
|
-
const tsParser = require('@typescript-eslint/parser');
|
|
5
|
-
const adyenBento = require('@adyen/eslint-plugin-bento');
|
|
6
|
-
|
|
7
|
-
module.exports = defineConfig([
|
|
8
|
-
{
|
|
9
|
-
languageOptions: {
|
|
10
|
-
ecmaVersion: 'latest',
|
|
11
|
-
sourceType: 'module',
|
|
12
|
-
parser: vueParser,
|
|
13
|
-
parserOptions: {
|
|
14
|
-
parser: tsParser,
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
plugins: {
|
|
18
|
-
'@adyen/bento': adyenBento,
|
|
19
|
-
},
|
|
20
|
-
rules: {
|
|
21
|
-
// Register the specific rule we are testing
|
|
22
|
-
'@adyen/bento/deprecation-bento-data-grid-cell-action-menu-item-handler': ['warn', { autofix: true }],
|
|
23
|
-
},
|
|
24
|
-
files: ['**/*.vue'],
|
|
25
|
-
},
|
|
26
|
-
]);
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { execFileSyncEslintVueFiles } from '../../utils/exec-file-sync-eslint-vue-files';
|
|
3
|
-
|
|
4
|
-
describe('@adyen/bento/deprecation-bento-data-grid-cell-action-menu-item-handler', () => {
|
|
5
|
-
it('should lint with warning when handler is used in menuData', () => {
|
|
6
|
-
const eslintrc = path.resolve(__dirname, '__tests__', 'eslint.config.js');
|
|
7
|
-
const vueFile = path.resolve(
|
|
8
|
-
__dirname,
|
|
9
|
-
'__tests__',
|
|
10
|
-
'deprecation-bento-data-grid-cell-action-menu-item-handler.vue'
|
|
11
|
-
);
|
|
12
|
-
|
|
13
|
-
const result = JSON.parse(execFileSyncEslintVueFiles(eslintrc, vueFile));
|
|
14
|
-
|
|
15
|
-
expect(result[0].warningCount).toBe(3);
|
|
16
|
-
|
|
17
|
-
expect(result[0].messages[0]).toMatchObject({
|
|
18
|
-
ruleId: '@adyen/bento/deprecation-bento-data-grid-cell-action-menu-item-handler',
|
|
19
|
-
severity: 1,
|
|
20
|
-
messageId: 'deprecation',
|
|
21
|
-
message: `The "handler" property is deprecated in bento-data-grid cell action menu items. Use "event" instead.`,
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
});
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { RuleTester } from 'eslint';
|
|
2
|
-
import vueParser from 'vue-eslint-parser';
|
|
3
|
-
import tsParser from '@typescript-eslint/parser';
|
|
4
|
-
import deprecationRule from './deprecation-bento-data-grid-cell-action-menu-item-handler';
|
|
5
|
-
|
|
6
|
-
const ruleTester = new RuleTester({
|
|
7
|
-
languageOptions: {
|
|
8
|
-
ecmaVersion: 'latest',
|
|
9
|
-
sourceType: 'module',
|
|
10
|
-
parser: vueParser,
|
|
11
|
-
parserOptions: {
|
|
12
|
-
parser: tsParser,
|
|
13
|
-
},
|
|
14
|
-
},
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
describe('@adyen/bento/deprecation-bento-data-grid-cell-action-menu-item-handler', () => {
|
|
18
|
-
ruleTester.run('deprecation-bento-data-grid-cell-action-menu-item-handler', deprecationRule, {
|
|
19
|
-
valid: [
|
|
20
|
-
{
|
|
21
|
-
name: 'Valid: menuData with event instead of handler',
|
|
22
|
-
filename: 'test.vue',
|
|
23
|
-
code: `<template>
|
|
24
|
-
<bento-data-grid :cell-actions="[{ menuData: [{ text: 'Edit', event: fn }] }]" />
|
|
25
|
-
</template>`,
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
name: 'Valid: handler in menuData on a non-data-grid component (template)',
|
|
29
|
-
filename: 'test.vue',
|
|
30
|
-
code: `<template>
|
|
31
|
-
<test-component :cell-actions="[{ menuData: [{ text: 'Edit', handler: fn }] }]" />
|
|
32
|
-
</template>`,
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
name: 'Valid: handler outside of menuData context',
|
|
36
|
-
filename: 'test.vue',
|
|
37
|
-
code: `<script>const obj = { handler: () => {} };</script>`,
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
name: 'Valid: handler in unrelated array',
|
|
41
|
-
filename: 'test.vue',
|
|
42
|
-
code: `<script>const arr = [{ handler: () => {} }];</script>`,
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
name: 'Valid: menuData with event in script',
|
|
46
|
-
filename: 'test.vue',
|
|
47
|
-
code: `<script>
|
|
48
|
-
const actions = [{ menuData: [{ text: 'Edit', event: () => {} }] }];
|
|
49
|
-
</script>`,
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
name: 'Valid: handler in menuData in script not passed to bento-data-grid',
|
|
53
|
-
filename: 'test.vue',
|
|
54
|
-
code: `<template>
|
|
55
|
-
<test-component :cell-actions="actions" />
|
|
56
|
-
</template>
|
|
57
|
-
<script>
|
|
58
|
-
const actions = [{ menuData: [{ text: 'Cancel', handler: () => {} }] }];
|
|
59
|
-
</script>`,
|
|
60
|
-
},
|
|
61
|
-
],
|
|
62
|
-
invalid: [
|
|
63
|
-
{
|
|
64
|
-
name: 'Invalid: handler in menuData (template, no autofix)',
|
|
65
|
-
filename: 'test.vue',
|
|
66
|
-
code: `<template>
|
|
67
|
-
<bento-data-grid :cell-actions="[{ menuData: [{ text: 'Cancel', handler: fn }] }]" />
|
|
68
|
-
</template>`,
|
|
69
|
-
output: null,
|
|
70
|
-
errors: [{ messageId: 'deprecation' }],
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
name: 'Invalid: handler in menuData (template, autofix renames to event)',
|
|
74
|
-
filename: 'test.vue',
|
|
75
|
-
code: `<template>
|
|
76
|
-
<bento-data-grid :cell-actions="[{ menuData: [{ text: 'Cancel', handler: fn }] }]" />
|
|
77
|
-
</template>`,
|
|
78
|
-
output: `<template>
|
|
79
|
-
<bento-data-grid :cell-actions="[{ menuData: [{ text: 'Cancel', event: fn }] }]" />
|
|
80
|
-
</template>`,
|
|
81
|
-
options: [{ autofix: true }],
|
|
82
|
-
errors: [{ messageId: 'deprecation' }],
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
name: 'Invalid: handler resolved from script variable via identifier',
|
|
86
|
-
filename: 'test.vue',
|
|
87
|
-
code: `<template>
|
|
88
|
-
<bento-data-grid :cell-actions="actions" />
|
|
89
|
-
</template>
|
|
90
|
-
<script>
|
|
91
|
-
const actions = [{ menuData: [{ text: 'Cancel', handler: () => {} }] }];
|
|
92
|
-
</script>`,
|
|
93
|
-
output: `<template>
|
|
94
|
-
<bento-data-grid :cell-actions="actions" />
|
|
95
|
-
</template>
|
|
96
|
-
<script>
|
|
97
|
-
const actions = [{ menuData: [{ text: 'Cancel', event: () => {} }] }];
|
|
98
|
-
</script>`,
|
|
99
|
-
options: [{ autofix: true }],
|
|
100
|
-
errors: [{ messageId: 'deprecation' }],
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
name: 'Invalid: multiple handlers in menuData (template, autofix)',
|
|
104
|
-
filename: 'test.vue',
|
|
105
|
-
code: `<template>
|
|
106
|
-
<bento-data-grid :cell-actions="[{ menuData: [{ text: 'Cancel', handler: a }, { text: 'Delete', handler: b }] }]" />
|
|
107
|
-
</template>`,
|
|
108
|
-
output: `<template>
|
|
109
|
-
<bento-data-grid :cell-actions="[{ menuData: [{ text: 'Cancel', event: a }, { text: 'Delete', event: b }] }]" />
|
|
110
|
-
</template>`,
|
|
111
|
-
options: [{ autofix: true }],
|
|
112
|
-
errors: [{ messageId: 'deprecation' }, { messageId: 'deprecation' }],
|
|
113
|
-
},
|
|
114
|
-
],
|
|
115
|
-
});
|
|
116
|
-
});
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div>
|
|
3
|
-
<!-- These should trigger the "add" action (missing has-actions) -->
|
|
4
|
-
<bento-date-range-picker label="Pick a date" />
|
|
5
|
-
<bento-date-range-picker label="Another picker" :min="minDate" />
|
|
6
|
-
<bento-date-range-picker v-model="value" />
|
|
7
|
-
</div>
|
|
8
|
-
</template>
|
|
9
|
-
|
|
10
|
-
<script setup></script>
|
package/rules/deprecation-bento-date-range-picker-has-actions-prop/__tests__/eslint.config.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
// eslint.config.js
|
|
2
|
-
const { defineConfig } = require('eslint/config');
|
|
3
|
-
const vueParser = require('vue-eslint-parser');
|
|
4
|
-
const tsParser = require('@typescript-eslint/parser');
|
|
5
|
-
const adyenBento = require('@adyen/eslint-plugin-bento');
|
|
6
|
-
|
|
7
|
-
module.exports = defineConfig([
|
|
8
|
-
{
|
|
9
|
-
languageOptions: {
|
|
10
|
-
ecmaVersion: 'latest',
|
|
11
|
-
sourceType: 'module',
|
|
12
|
-
parser: vueParser,
|
|
13
|
-
parserOptions: {
|
|
14
|
-
parser: tsParser,
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
plugins: {
|
|
18
|
-
'@adyen/bento': adyenBento,
|
|
19
|
-
},
|
|
20
|
-
rules: {
|
|
21
|
-
// Register the specific rule we are testing
|
|
22
|
-
'@adyen/bento/deprecation-bento-date-range-picker-has-actions-prop': [
|
|
23
|
-
'warn',
|
|
24
|
-
{ action: 'add', autofix: true },
|
|
25
|
-
],
|
|
26
|
-
},
|
|
27
|
-
files: ['**/*.vue'],
|
|
28
|
-
},
|
|
29
|
-
]);
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div>
|
|
3
|
-
<!-- These should trigger the "remove" action (has-actions present) -->
|
|
4
|
-
<bento-date-range-picker has-actions />
|
|
5
|
-
<bento-date-range-picker has-actions label="Pick a date" />
|
|
6
|
-
<bento-date-range-picker :has-actions="true" />
|
|
7
|
-
<bento-date-range-picker :has-actions="false" />
|
|
8
|
-
</div>
|
|
9
|
-
</template>
|
|
10
|
-
|
|
11
|
-
<script setup></script>
|
package/rules/deprecation-bento-date-range-picker-has-actions-prop/__tests__/remove/eslint.config.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
// eslint.config.js
|
|
2
|
-
const { defineConfig } = require('eslint/config');
|
|
3
|
-
const vueParser = require('vue-eslint-parser');
|
|
4
|
-
const tsParser = require('@typescript-eslint/parser');
|
|
5
|
-
const adyenBento = require('@adyen/eslint-plugin-bento');
|
|
6
|
-
|
|
7
|
-
module.exports = defineConfig([
|
|
8
|
-
{
|
|
9
|
-
languageOptions: {
|
|
10
|
-
ecmaVersion: 'latest',
|
|
11
|
-
sourceType: 'module',
|
|
12
|
-
parser: vueParser,
|
|
13
|
-
parserOptions: {
|
|
14
|
-
parser: tsParser,
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
plugins: {
|
|
18
|
-
'@adyen/bento': adyenBento,
|
|
19
|
-
},
|
|
20
|
-
rules: {
|
|
21
|
-
// Register the specific rule we are testing
|
|
22
|
-
'@adyen/bento/deprecation-bento-date-range-picker-has-actions-prop': [
|
|
23
|
-
'warn',
|
|
24
|
-
{ action: 'remove', autofix: true },
|
|
25
|
-
],
|
|
26
|
-
},
|
|
27
|
-
files: ['**/*.vue'],
|
|
28
|
-
},
|
|
29
|
-
]);
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { execFileSyncEslintVueFiles } from '../../utils/exec-file-sync-eslint-vue-files';
|
|
3
|
-
|
|
4
|
-
describe('@adyen/bento/deprecation-bento-date-range-picker-has-actions-prop', () => {
|
|
5
|
-
it('should lint with warning when has-actions is missing (add action)', () => {
|
|
6
|
-
const eslintrc = path.resolve(__dirname, '__tests__', 'eslint.config.js');
|
|
7
|
-
const vueFile = path.resolve(
|
|
8
|
-
__dirname,
|
|
9
|
-
'__tests__',
|
|
10
|
-
'deprecation-bento-date-range-picker-has-actions-prop.vue'
|
|
11
|
-
);
|
|
12
|
-
|
|
13
|
-
const result = JSON.parse(execFileSyncEslintVueFiles(eslintrc, vueFile));
|
|
14
|
-
|
|
15
|
-
// The test .vue file has 3 bento-date-range-picker instances without has-actions
|
|
16
|
-
expect(result[0].warningCount).toBe(3);
|
|
17
|
-
|
|
18
|
-
expect(result[0].messages[0]).toMatchObject({
|
|
19
|
-
ruleId: '@adyen/bento/deprecation-bento-date-range-picker-has-actions-prop',
|
|
20
|
-
severity: 1,
|
|
21
|
-
messageId: 'missingProp',
|
|
22
|
-
message: `The temporary "has-actions" prop is missing on bento-date-range-picker. Add it to opt in to the new actions behavior.`,
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('should lint with warning when has-actions is present (remove action)', () => {
|
|
27
|
-
const eslintrc = path.resolve(__dirname, '__tests__', 'remove', 'eslint.config.js');
|
|
28
|
-
const vueFile = path.resolve(
|
|
29
|
-
__dirname,
|
|
30
|
-
'__tests__',
|
|
31
|
-
'remove',
|
|
32
|
-
'deprecation-bento-date-range-picker-has-actions-prop.vue'
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
const result = JSON.parse(execFileSyncEslintVueFiles(eslintrc, vueFile));
|
|
36
|
-
|
|
37
|
-
// The test .vue file has 4 bento-date-range-picker instances with has-actions
|
|
38
|
-
expect(result[0].warningCount).toBe(4);
|
|
39
|
-
|
|
40
|
-
expect(result[0].messages[0]).toMatchObject({
|
|
41
|
-
ruleId: '@adyen/bento/deprecation-bento-date-range-picker-has-actions-prop',
|
|
42
|
-
severity: 1,
|
|
43
|
-
messageId: 'removeProp',
|
|
44
|
-
message: `The temporary "has-actions" prop should be removed from bento-date-range-picker.`,
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
});
|