@adyen/eslint-plugin-bento 2.23.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.
Files changed (82) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +31 -15
  3. package/configs/vue-recommended.js +15 -0
  4. package/configs/vue-strict.js +15 -0
  5. package/index.js +3 -0
  6. package/package.json +1 -1
  7. package/rules/deprecation-bento-tag-variant-enum/README.md +69 -0
  8. package/rules/deprecation-bento-tag-variant-enum/deprecation-bento-tag-variant-enum.js +167 -0
  9. package/rules/vue-no-redundant-dialog-v-if/README.md +55 -0
  10. package/rules/vue-no-redundant-dialog-v-if/vue-no-redundant-dialog-v-if.js +150 -0
  11. package/rules/vue-no-use-bento-translation-overrides/README.md +14 -0
  12. package/rules/vue-no-use-bento-translation-overrides/vue-no-use-bento-translation-overrides.js +93 -0
  13. package/rules/deprecation-bento-data-grid-cell-action-menu-item-handler/__tests__/deprecation-bento-data-grid-cell-action-menu-item-handler.vue +0 -63
  14. package/rules/deprecation-bento-data-grid-cell-action-menu-item-handler/__tests__/eslint.config.js +0 -26
  15. 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
  16. package/rules/deprecation-bento-data-grid-cell-action-menu-item-handler/deprecation-bento-data-grid-cell-action-menu-item-handler.test.ts +0 -116
  17. package/rules/deprecation-bento-date-range-picker-has-actions-prop/__tests__/deprecation-bento-date-range-picker-has-actions-prop.vue +0 -10
  18. package/rules/deprecation-bento-date-range-picker-has-actions-prop/__tests__/eslint.config.js +0 -29
  19. package/rules/deprecation-bento-date-range-picker-has-actions-prop/__tests__/remove/deprecation-bento-date-range-picker-has-actions-prop.vue +0 -11
  20. package/rules/deprecation-bento-date-range-picker-has-actions-prop/__tests__/remove/eslint.config.js +0 -29
  21. package/rules/deprecation-bento-date-range-picker-has-actions-prop/deprecation-bento-date-range-picker-has-actions-prop.integration.test.ts +0 -47
  22. package/rules/deprecation-bento-date-range-picker-has-actions-prop/deprecation-bento-date-range-picker-has-actions-prop.test.ts +0 -176
  23. package/rules/deprecation-bento-dropdown-size-prop/__tests__/deprecation-bento-dropdown-size-prop.vue +0 -9
  24. package/rules/deprecation-bento-dropdown-size-prop/__tests__/eslint.config.js +0 -26
  25. package/rules/deprecation-bento-dropdown-size-prop/deprecation-bento-dropdown-size-prop.integration.test.ts +0 -33
  26. package/rules/deprecation-bento-dropdown-size-prop/deprecation-bento-dropdown-size-prop.test.ts +0 -101
  27. package/rules/deprecation-bento-popover-modifiers-prop/__tests__/deprecation-bento-popover-modifiers-prop.vue +0 -9
  28. package/rules/deprecation-bento-popover-modifiers-prop/__tests__/eslint.config.js +0 -26
  29. package/rules/deprecation-bento-popover-modifiers-prop/deprecation-bento-popover-modifiers-prop.integration.test.ts +0 -24
  30. package/rules/deprecation-bento-popover-modifiers-prop/deprecation-bento-popover-modifiers-prop.test.ts +0 -109
  31. package/rules/deprecation-bento-timeline-item-variant-prop/__tests__/deprecation-bento-timeline-item-variant-prop.vue +0 -9
  32. package/rules/deprecation-bento-timeline-item-variant-prop/__tests__/eslint.config.js +0 -26
  33. package/rules/deprecation-bento-timeline-item-variant-prop/deprecation-bento-timeline-item-variant-prop.integration.test.ts +0 -35
  34. package/rules/deprecation-bento-timeline-item-variant-prop/deprecation-bento-timeline-item-variant-prop.test.ts +0 -141
  35. package/rules/deprecation-components-aria-label-prop/__tests__/deprecation-components-aria-label-prop.vue +0 -25
  36. package/rules/deprecation-components-aria-label-prop/__tests__/eslint.config.js +0 -26
  37. package/rules/deprecation-components-aria-label-prop/deprecation-components-aria-label-prop.integration.test.ts +0 -36
  38. package/rules/deprecation-components-aria-label-prop/deprecation-components-aria-label-prop.test.ts +0 -194
  39. package/rules/deprecation-components-error-prop/__tests__/deprecation-components-error-prop.vue +0 -17
  40. package/rules/deprecation-components-error-prop/__tests__/eslint.config.js +0 -26
  41. package/rules/deprecation-components-error-prop/deprecation-components-error-prop.integration.test.ts +0 -41
  42. package/rules/deprecation-components-error-prop/deprecation-components-error-prop.test.ts +0 -153
  43. package/rules/deprecation-components-has-error-prop/__tests__/deprecation-components-has-error-prop.vue +0 -13
  44. package/rules/deprecation-components-has-error-prop/__tests__/eslint.config.js +0 -26
  45. package/rules/deprecation-components-has-error-prop/deprecation-components-has-error-prop.integration.test.ts +0 -37
  46. package/rules/deprecation-components-has-error-prop/deprecation-components-has-error-prop.test.ts +0 -122
  47. package/rules/deprecation-components-input-emit/__tests__/deprecation-components-input-emit.vue +0 -22
  48. package/rules/deprecation-components-input-emit/__tests__/eslint.config.js +0 -25
  49. package/rules/deprecation-components-input-emit/deprecation-components-input-emit.integration.test.ts +0 -43
  50. package/rules/deprecation-components-input-emit/deprecation-components-input-emit.test.ts +0 -320
  51. package/rules/deprecation-components-value-prop/__tests__/deprecation-components-value-prop.vue +0 -69
  52. package/rules/deprecation-components-value-prop/__tests__/eslint.config.js +0 -25
  53. package/rules/deprecation-components-value-prop/deprecation-components-value-prop.integration.test.ts +0 -37
  54. package/rules/deprecation-components-value-prop/deprecation-components-value-prop.test.ts +0 -480
  55. package/rules/deprecation-country-custom-usage/__tests__/deprecation-country-custom-usage.vue +0 -93
  56. package/rules/deprecation-country-custom-usage/__tests__/eslint.config.js +0 -25
  57. package/rules/deprecation-country-custom-usage/deprecation-country-custom-usage.integration.test.ts +0 -22
  58. package/rules/deprecation-country-custom-usage/deprecation-country-custom-usage.test.ts +0 -380
  59. package/rules/deprecation-currency-default-slot/__tests__/deprecation-currency-default-slot.vue +0 -7
  60. package/rules/deprecation-currency-default-slot/__tests__/eslint.config.js +0 -24
  61. package/rules/deprecation-currency-default-slot/deprecation-currency-default-slot.integration.test.ts +0 -29
  62. package/rules/deprecation-currency-default-slot/deprecation-currency-default-slot.test.ts +0 -136
  63. package/rules/deprecation-filter-bar-deprecated-props/__tests__/deprecation-filter-bar-deprecated-props.vue +0 -62
  64. package/rules/deprecation-filter-bar-deprecated-props/__tests__/eslint.config.js +0 -25
  65. package/rules/deprecation-filter-bar-deprecated-props/deprecation-filter-bar-deprecated-props.integration.test.ts +0 -22
  66. package/rules/deprecation-filter-bar-deprecated-props/deprecation-filter-bar-deprecated-props.test.ts +0 -492
  67. package/rules/deprecation-input-filter-dropdown-options/__tests__/deprecation-input-filter-dropdown-options.vue +0 -46
  68. package/rules/deprecation-input-filter-dropdown-options/__tests__/eslint.config.js +0 -26
  69. package/rules/deprecation-input-filter-dropdown-options/deprecation-input-filter-dropdown-options.integration.test.ts +0 -27
  70. package/rules/deprecation-input-filter-dropdown-options/deprecation-input-filter-dropdown-options.test.ts +0 -392
  71. package/rules/deprecation-tag-default-slot/__tests__/deprecation-tag-default-slot.vue +0 -5
  72. package/rules/deprecation-tag-default-slot/__tests__/eslint.config.js +0 -24
  73. package/rules/deprecation-tag-default-slot/deprecation-tag-default-slot.integration.test.ts +0 -29
  74. package/rules/deprecation-tag-default-slot/deprecation-tag-default-slot.test.ts +0 -163
  75. package/rules/tailwind-v3-to-bento/__tests__/eslint.config.js +0 -24
  76. package/rules/tailwind-v3-to-bento/__tests__/tailwind-v3-to-bento.vue +0 -15
  77. package/rules/tailwind-v3-to-bento/tailwind-v3-to-bento.integration.test.ts +0 -29
  78. package/rules/tailwind-v3-to-bento/tailwind-v3-to-bento.test.ts +0 -403
  79. package/rules/vue-old-spacer-tokens/__tests__/eslint.config.js +0 -24
  80. package/rules/vue-old-spacer-tokens/__tests__/vue-old-spacer-tokens.vue +0 -5
  81. package/rules/vue-old-spacer-tokens/vue-old-spacer-tokens.integration.test.ts +0 -29
  82. package/rules/vue-old-spacer-tokens/vue-old-spacer-tokens.test.ts +0 -77
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.24.0 (2026-09-09)
4
+
5
+ ### Features
6
+
7
+ - added `useBentoTranslationOverrides` composable to override translated strings in vue 3 ([e0cb22fc0](https://github.com/Adyen/bento/commit/e0cb22fc0))
8
+ - added rule to deprecate `bento-tag` white variant ([20867ed09](https://github.com/Adyen/bento/commit/20867ed09))
9
+
10
+ ### Miscellaneous Chores
11
+
12
+ - added `vue-prefer-no-redundant-v-if-for-dialog` eslint rule ([f21cf44a6](https://github.com/Adyen/bento/commit/f21cf44a6))
13
+
14
+ ### ❤️ Thank You
15
+
16
+ - erice
17
+ - gerald
18
+ - mafalda
19
+
20
+
3
21
  ## 2.23.0 (2026-08-17)
4
22
 
5
23
  ### Features
package/README.md CHANGED
@@ -19,7 +19,7 @@ npm install --save-dev eslint@9 vue-eslint-parser@10 @adyen/eslint-plugin-bento@
19
19
 
20
20
  ### Flat config recommended (`eslint.config.js`)
21
21
 
22
- All rules are set to `warn`
22
+ Deprecation rules are set to `warn`. Restricted APIs remain errors.
23
23
 
24
24
  ```javascript
25
25
  const { defineConfig } = require('eslint/config');
@@ -97,6 +97,12 @@ module.exports = defineConfig([
97
97
 
98
98
  ## Rules
99
99
 
100
+ ### `@adyen/bento/vue-no-use-bento-translation-overrides`
101
+
102
+ Disallow importing the restricted `useBentoTranslationOverrides` composable without approval from the Bento team. This
103
+ API is only intended for teams building SDKs or component libraries for external customers and must not be used in Adyen
104
+ products.
105
+
100
106
  ### `@adyen/bento/vue-old-spacer-tokens`
101
107
 
102
108
  Disallow the use of ADL space utility classes in favor of Bento _spacer_ utility classes.
@@ -109,6 +115,12 @@ Disallow the use of the `default` slot in `bento-currency` instances.
109
115
 
110
116
  Disallow the use of the `default` slot in `bento-tag` instances.
111
117
 
118
+ ### `@adyen/bento/vue-no-redundant-dialog-v-if`
119
+
120
+ Disallow a `v-if` that duplicates the `is-open` binding on `bento-modal`, `bento-modal-fullscreen` and
121
+ `bento-sidepanel`. These components render a native `<dialog>`, so the `v-if` only unmounts them before their closing
122
+ animation can run. Reported with an IDE suggestion to remove the directive, not an autofix.
123
+
112
124
  ### `@adyen/bento/tailwind-v3-to-bento`
113
125
 
114
126
  Replace Tailwind CSS v3 utility classes with Bento design token equivalents. Covers spacing, border radius, border
@@ -119,23 +131,27 @@ warning — use the `bento-typography` component instead.
119
131
 
120
132
  ### Recommended (`vue-recommended`)
121
133
 
122
- | Rule | Severity |
123
- | ------------------------------------------------ | -------- |
124
- | `@adyen/bento/deprecation-components-input-emit` | `warn` |
125
- | `@adyen/bento/deprecation-components-value-prop` | `warn` |
126
- | `@adyen/bento/deprecation-currency-default-slot` | `warn` |
127
- | `@adyen/bento/deprecation-tag-default-slot` | `warn` |
128
- | `@adyen/bento/vue-old-spacer-tokens` | `warn` |
134
+ | Rule | Severity |
135
+ | ----------------------------------------------------- | -------- |
136
+ | `@adyen/bento/deprecation-components-input-emit` | `warn` |
137
+ | `@adyen/bento/deprecation-components-value-prop` | `warn` |
138
+ | `@adyen/bento/deprecation-currency-default-slot` | `warn` |
139
+ | `@adyen/bento/deprecation-tag-default-slot` | `warn` |
140
+ | `@adyen/bento/vue-no-use-bento-translation-overrides` | `error` |
141
+ | `@adyen/bento/vue-no-redundant-dialog-v-if` | `warn` |
142
+ | `@adyen/bento/vue-old-spacer-tokens` | `warn` |
129
143
 
130
144
  ### Strict (`vue-strict`)
131
145
 
132
- | Rule | Severity |
133
- | ------------------------------------------------ | -------- |
134
- | `@adyen/bento/deprecation-components-input-emit` | `error` |
135
- | `@adyen/bento/deprecation-components-value-prop` | `error` |
136
- | `@adyen/bento/deprecation-currency-default-slot` | `error` |
137
- | `@adyen/bento/deprecation-tag-default-slot` | `error` |
138
- | `@adyen/bento/vue-old-spacer-tokens` | `error` |
146
+ | Rule | Severity |
147
+ | ----------------------------------------------------- | -------- |
148
+ | `@adyen/bento/deprecation-components-input-emit` | `error` |
149
+ | `@adyen/bento/deprecation-components-value-prop` | `error` |
150
+ | `@adyen/bento/deprecation-currency-default-slot` | `error` |
151
+ | `@adyen/bento/deprecation-tag-default-slot` | `error` |
152
+ | `@adyen/bento/vue-no-use-bento-translation-overrides` | `error` |
153
+ | `@adyen/bento/vue-no-redundant-dialog-v-if` | `error` |
154
+ | `@adyen/bento/vue-old-spacer-tokens` | `error` |
139
155
 
140
156
  ### Tailwind (`tailwind`)
141
157
 
@@ -1,5 +1,17 @@
1
1
  /** @type {import('eslint').Linter.Config[]} */
2
2
  module.exports = [
3
+ {
4
+ name: '@bento/adyen/recommended-restricted-apis',
5
+ files: ['*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}', '**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}'],
6
+ plugins: {
7
+ get '@adyen/bento'() {
8
+ return require('../index');
9
+ },
10
+ },
11
+ rules: {
12
+ '@adyen/bento/vue-no-use-bento-translation-overrides': 'error',
13
+ },
14
+ },
3
15
  {
4
16
  name: '@bento/adyen/recommended',
5
17
  files: ['*.vue', '**/*.vue'],
@@ -12,6 +24,7 @@ module.exports = [
12
24
  },
13
25
  },
14
26
  rules: {
27
+ '@adyen/bento/deprecation-bento-tag-variant-enum': 'warn',
15
28
  '@adyen/bento/deprecation-bento-data-grid-cell-action-menu-item-handler': 'warn',
16
29
  '@adyen/bento/deprecation-bento-date-range-picker-has-actions-prop': 'warn',
17
30
  '@adyen/bento/deprecation-input-filter-dropdown-options': 'warn',
@@ -27,6 +40,8 @@ module.exports = [
27
40
  '@adyen/bento/deprecation-components-value-prop': 'warn',
28
41
  '@adyen/bento/deprecation-currency-default-slot': 'warn',
29
42
  '@adyen/bento/deprecation-tag-default-slot': 'warn',
43
+ '@adyen/bento/vue-no-use-bento-translation-overrides': 'error',
44
+ '@adyen/bento/vue-no-redundant-dialog-v-if': 'warn',
30
45
  '@adyen/bento/vue-old-spacer-tokens': 'warn',
31
46
  },
32
47
  },
@@ -1,5 +1,17 @@
1
1
  /** @type {import('eslint').Linter.Config[]} */
2
2
  module.exports = [
3
+ {
4
+ name: '@bento/adyen/strict-restricted-apis',
5
+ files: ['*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}', '**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}'],
6
+ plugins: {
7
+ get '@adyen/bento'() {
8
+ return require('../index');
9
+ },
10
+ },
11
+ rules: {
12
+ '@adyen/bento/vue-no-use-bento-translation-overrides': 'error',
13
+ },
14
+ },
3
15
  {
4
16
  name: '@bento/adyen/strict',
5
17
  files: ['*.vue', '**/*.vue'],
@@ -12,6 +24,7 @@ module.exports = [
12
24
  },
13
25
  },
14
26
  rules: {
27
+ '@adyen/bento/deprecation-bento-tag-variant-enum': 'error',
15
28
  '@adyen/bento/deprecation-bento-data-grid-cell-action-menu-item-handler': 'error',
16
29
  '@adyen/bento/deprecation-bento-date-range-picker-has-actions-prop': ['error', { action: 'remove' }],
17
30
  '@adyen/bento/deprecation-input-filter-dropdown-options': 'error',
@@ -27,6 +40,8 @@ module.exports = [
27
40
  '@adyen/bento/deprecation-components-value-prop': 'error',
28
41
  '@adyen/bento/deprecation-currency-default-slot': 'error',
29
42
  '@adyen/bento/deprecation-tag-default-slot': 'error',
43
+ '@adyen/bento/vue-no-use-bento-translation-overrides': 'error',
44
+ '@adyen/bento/vue-no-redundant-dialog-v-if': 'error',
30
45
  '@adyen/bento/vue-old-spacer-tokens': 'error',
31
46
  },
32
47
  },
package/index.js CHANGED
@@ -12,6 +12,7 @@ const plugin = {
12
12
  tailwind: require('./configs/tailwind'),
13
13
  },
14
14
  rules: {
15
+ 'deprecation-bento-tag-variant-enum': require('./rules/deprecation-bento-tag-variant-enum/deprecation-bento-tag-variant-enum'),
15
16
  'deprecation-bento-data-grid-cell-action-menu-item-handler': require('./rules/deprecation-bento-data-grid-cell-action-menu-item-handler/deprecation-bento-data-grid-cell-action-menu-item-handler'),
16
17
  'deprecation-bento-date-range-picker-has-actions-prop': require('./rules/deprecation-bento-date-range-picker-has-actions-prop/deprecation-bento-date-range-picker-has-actions-prop'),
17
18
  'deprecation-input-filter-dropdown-options': require('./rules/deprecation-input-filter-dropdown-options/deprecation-input-filter-dropdown-options'),
@@ -27,6 +28,8 @@ const plugin = {
27
28
  'deprecation-components-value-prop': require('./rules/deprecation-components-value-prop/deprecation-components-value-prop'),
28
29
  'deprecation-currency-default-slot': require('./rules/deprecation-currency-default-slot/deprecation-currency-default-slot'),
29
30
  'deprecation-tag-default-slot': require('./rules/deprecation-tag-default-slot/deprecation-tag-default-slot'),
31
+ 'vue-no-use-bento-translation-overrides': require('./rules/vue-no-use-bento-translation-overrides/vue-no-use-bento-translation-overrides'),
32
+ 'vue-no-redundant-dialog-v-if': require('./rules/vue-no-redundant-dialog-v-if/vue-no-redundant-dialog-v-if'),
30
33
  'vue-old-spacer-tokens': require('./rules/vue-old-spacer-tokens/vue-old-spacer-tokens'),
31
34
  'tailwind-v3-to-bento': require('./rules/tailwind-v3-to-bento/tailwind-v3-to-bento'),
32
35
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adyen/eslint-plugin-bento",
3
- "version": "2.23.0",
3
+ "version": "2.24.0",
4
4
  "description": "Adyen Bento ESLint rules",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -0,0 +1,69 @@
1
+ # deprecation-bento-tag-variant-enum
2
+
3
+ ## Description
4
+
5
+ The white `bento-tag` variant and the `BentoTagVariant.WHITE` enum member are deprecated. Use the `grey` variant or the
6
+ `'grey'` string literal instead.
7
+
8
+ ## Rule details
9
+
10
+ The rule reports and can autofix the following usages:
11
+
12
+ - `BentoTagVariant.WHITE` in scripts and template expressions.
13
+ - Static `variant="white"` attributes on `bento-tag` and `BentoTag`.
14
+ - Named bindings such as `:variant="'white'"` and `v-bind:variant="'white'"`.
15
+ - Object bindings such as `v-bind="{ variant: 'white' }"`.
16
+ - `BentoTagProps` object literals whose `variant` is `'white'` or `BentoTagVariant.WHITE`.
17
+
18
+ The rule does not report `variant="white"` on unrelated components or other `BentoTagVariant` members. Values that can
19
+ only be determined at runtime cannot be safely autofixed.
20
+
21
+ ## Incorrect
22
+
23
+ ```vue
24
+ <template>
25
+ <bento-tag variant="white" />
26
+ <bento-tag :variant="'white'" />
27
+ <bento-tag v-bind="{ variant: 'white' }" />
28
+ <bento-tag :variant="BentoTagVariant.WHITE" />
29
+ </template>
30
+
31
+ <script setup lang="ts">
32
+ import { type BentoTagProps, BentoTagVariant } from '@adyen/bento-vue2';
33
+
34
+ const tagProps: BentoTagProps = { variant: 'white' };
35
+ const enumTagProps: BentoTagProps = { variant: BentoTagVariant.WHITE };
36
+ </script>
37
+ ```
38
+
39
+ ## Correct
40
+
41
+ ```vue
42
+ <template>
43
+ <bento-tag variant="grey" />
44
+ <bento-tag :variant="'grey'" />
45
+ <bento-tag v-bind="{ variant: 'grey' }" />
46
+ </template>
47
+
48
+ <script setup lang="ts">
49
+ import type { BentoTagProps } from '@adyen/bento-vue2';
50
+
51
+ const tagProps: BentoTagProps = { variant: 'grey' };
52
+ </script>
53
+ ```
54
+
55
+ ## Configuration
56
+
57
+ The rule is enabled as a warning in the recommended Bento Vue config and as an error in the strict config.
58
+
59
+ To enable autofix explicitly:
60
+
61
+ ```javascript
62
+ export default [
63
+ {
64
+ rules: {
65
+ '@adyen/bento/deprecation-bento-tag-variant-enum': ['warn', { autofix: true }],
66
+ },
67
+ },
68
+ ];
69
+ ```
@@ -0,0 +1,167 @@
1
+ /**
2
+ * @typedef {import('eslint').Rule.RuleContext} RuleContext
3
+ */
4
+ /**
5
+ * @typedef {import('eslint-plugin-vue/lib/utils').RuleModule} RuleModule
6
+ */
7
+
8
+ const VueUtils = require('eslint-plugin-vue/lib/utils');
9
+
10
+ const findWhiteVariantProperty = properties =>
11
+ properties.find(
12
+ property =>
13
+ property.type === 'Property' &&
14
+ !property.computed &&
15
+ ((property.key.type === 'Identifier' && property.key.name === 'variant') ||
16
+ (property.key.type === 'Literal' && property.key.value === 'variant')) &&
17
+ property.value.type === 'Literal' &&
18
+ property.value.value === 'white'
19
+ );
20
+
21
+ /** @type {RuleModule} */
22
+ module.exports = {
23
+ meta: {
24
+ type: 'suggestion',
25
+ docs: {
26
+ description: 'Deprecates the white bento-tag variant and BentoTagVariant.WHITE',
27
+ category: 'Deprecations',
28
+ url: 'https://github.com/Adyen/bento/blob/main/packages/eslint/rules/deprecation-bento-tag-variant-enum/README.md',
29
+ recommended: true,
30
+ },
31
+ messages: {
32
+ deprecationWithReplacement: `The "{{name}}" enum member is deprecated. Please use "{{replacement}}" instead.`,
33
+ whiteVariant: `The "white" bento-tag variant is deprecated. Please use "grey" instead.`,
34
+ },
35
+ fixable: 'code',
36
+ schema: [
37
+ {
38
+ type: 'object',
39
+ properties: {
40
+ autofix: {
41
+ type: 'boolean',
42
+ default: false,
43
+ },
44
+ },
45
+ additionalProperties: false,
46
+ },
47
+ ],
48
+ },
49
+
50
+ /** @param {RuleContext} context */
51
+ create(context) {
52
+ const options = context.options[0] || {};
53
+ const autofix = options.autofix === true;
54
+
55
+ const REPLACEMENT_MAP = {
56
+ WHITE: "'grey'",
57
+ };
58
+
59
+ const enumName = 'BentoTagVariant';
60
+
61
+ const visitor = {
62
+ [`MemberExpression[object.name="${enumName}"]`](node) {
63
+ if (node.computed && node.property.type !== 'Literal') {
64
+ return;
65
+ }
66
+
67
+ const memberName = node.computed ? node.property.value : node.property.name;
68
+ const replacement = REPLACEMENT_MAP[memberName];
69
+ if (replacement) {
70
+ context.report({
71
+ node,
72
+ messageId: 'deprecationWithReplacement',
73
+ data: { name: `${enumName}.${memberName}`, replacement },
74
+ ...(autofix && {
75
+ fix(fixer) {
76
+ return fixer.replaceText(node, replacement);
77
+ },
78
+ }),
79
+ });
80
+ }
81
+ },
82
+ VariableDeclarator(node) {
83
+ const typeName = node.id?.typeAnnotation?.typeAnnotation?.typeName;
84
+ if (
85
+ typeName?.type !== 'Identifier' ||
86
+ typeName.name !== 'BentoTagProps' ||
87
+ node.init?.type !== 'ObjectExpression'
88
+ ) {
89
+ return;
90
+ }
91
+
92
+ const variantProperty = findWhiteVariantProperty(node.init.properties);
93
+ if (variantProperty?.type !== 'Property') {
94
+ return;
95
+ }
96
+
97
+ context.report({
98
+ node: variantProperty.value,
99
+ messageId: 'whiteVariant',
100
+ ...(autofix && {
101
+ fix(fixer) {
102
+ return fixer.replaceText(variantProperty.value, "'grey'");
103
+ },
104
+ }),
105
+ });
106
+ },
107
+ [`VElement:matches([name="bento-tag"], [name="BentoTag"]) > VStartTag > VAttribute[directive=false][key.name="variant"]`](
108
+ node
109
+ ) {
110
+ if (node.value?.value !== 'white') {
111
+ return;
112
+ }
113
+
114
+ context.report({
115
+ node,
116
+ messageId: 'whiteVariant',
117
+ ...(autofix && {
118
+ fix(fixer) {
119
+ return fixer.replaceText(node, 'variant="grey"');
120
+ },
121
+ }),
122
+ });
123
+ },
124
+ [`VElement:matches([name="bento-tag"], [name="BentoTag"]) > VStartTag > VAttribute[directive=true][key.name.name="bind"][key.argument.name="variant"]`](
125
+ node
126
+ ) {
127
+ const expression = node.value?.expression;
128
+ if (expression?.type !== 'Literal' || expression.value !== 'white') {
129
+ return;
130
+ }
131
+
132
+ context.report({
133
+ node,
134
+ messageId: 'whiteVariant',
135
+ ...(autofix && {
136
+ fix(fixer) {
137
+ return fixer.replaceText(expression, "'grey'");
138
+ },
139
+ }),
140
+ });
141
+ },
142
+ [`VElement:matches([name="bento-tag"], [name="BentoTag"]) > VStartTag > VAttribute[directive=true][key.name.name="bind"]`](
143
+ node
144
+ ) {
145
+ if (node.key.argument || node.value?.expression?.type !== 'ObjectExpression') {
146
+ return;
147
+ }
148
+
149
+ const variantProperty = findWhiteVariantProperty(node.value.expression.properties);
150
+ if (variantProperty?.type !== 'Property') {
151
+ return;
152
+ }
153
+
154
+ context.report({
155
+ node: variantProperty.value,
156
+ messageId: 'whiteVariant',
157
+ ...(autofix && {
158
+ fix(fixer) {
159
+ return fixer.replaceText(variantProperty.value, "'grey'");
160
+ },
161
+ }),
162
+ });
163
+ },
164
+ };
165
+ return VueUtils.defineTemplateBodyVisitor(context, visitor, visitor);
166
+ },
167
+ };
@@ -0,0 +1,55 @@
1
+ # @adyen/bento/vue-no-redundant-dialog-v-if
2
+
3
+ > Disallow a redundant `v-if` next to `is-open` on components rendering a native dialog
4
+
5
+ - This rule is included in the `vue-recommended` (`warn`) and `vue-strict` (`error`) configurations
6
+ - 💡 This rule provides an IDE suggestion (not an autofix), so `--fix` leaves the code untouched
7
+
8
+ The following components render a native HTML `<dialog>` element:
9
+
10
+ - `bento-modal`
11
+ - `bento-modal-fullscreen`
12
+ - `bento-sidepanel`
13
+
14
+ A closed `<dialog>` is hidden by the browser and its content is not rendered, so `is-open` alone is enough to control
15
+ visibility. Pairing it with a `v-if` on the same value unmounts the component the moment it closes, which cancels the
16
+ closing animation.
17
+
18
+ This rule only reports a `v-if` whose expression is identical to the `is-open` binding, because that is the only case
19
+ where removing the directive provably keeps the same behaviour. A `v-if` driven by another condition (for example
20
+ `v-if="hasPermission"`) is left alone, as is an element whose `v-if` is followed by a `v-else` or `v-else-if` branch.
21
+
22
+ ## ✗ BAD
23
+
24
+ <!-- prettier-ignore -->
25
+ ```html
26
+ <template>
27
+ <bento-modal v-if="isOpen" :is-open="isOpen" />
28
+
29
+ <bento-modal-fullscreen v-if="isOpen" :is-open="isOpen" />
30
+
31
+ <bento-sidepanel v-if="isOpen" :is-open="isOpen" />
32
+ </template>
33
+ ```
34
+
35
+ ## ✓ Good
36
+
37
+ <!-- prettier-ignore -->
38
+ ```html
39
+ <template>
40
+ <bento-modal :is-open="isOpen" />
41
+
42
+ <bento-modal-fullscreen :is-open="isOpen" />
43
+
44
+ <bento-sidepanel :is-open="isOpen" />
45
+ </template>
46
+ ```
47
+
48
+ A `v-if` on an unrelated condition is still valid, since it does not duplicate `is-open`:
49
+
50
+ <!-- prettier-ignore -->
51
+ ```html
52
+ <template>
53
+ <bento-modal v-if="hasPermission" :is-open="isOpen" />
54
+ </template>
55
+ ```
@@ -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
+ ```