@adyen/eslint-plugin-bento 2.15.0 → 2.17.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 CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.17.0 (2026-06-16)
4
+
5
+ This was a version bump only for eslint to align it with other projects, there were no code changes.
6
+
7
+
8
+ ## 2.16.0 (2026-06-09)
9
+
10
+ ### Features
11
+
12
+ - added eslint rule readme to mcp `get-deprecations` tool ([44b9ce380](https://github.com/Adyen/bento/commit/44b9ce380))
13
+
14
+ ### ❤️ Thank You
15
+
16
+ - erice
17
+
18
+
3
19
  ## 2.15.0 (2026-06-03)
4
20
 
5
21
  ### Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adyen/eslint-plugin-bento",
3
- "version": "2.15.0",
3
+ "version": "2.17.0",
4
4
  "description": "Adyen Bento ESLint rules",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -14,6 +14,10 @@ Please use `error-message` instead.
14
14
  > both displays a message and applies error styling. A direct rename is not type-safe — you must also update the bound
15
15
  > value from a boolean to an error message string.
16
16
 
17
+ | Deprecated prop | Replacement prop | Type change |
18
+ | --------------- | ---------------- | -------------------- |
19
+ | `error` | `error-message` | `boolean` → `string` |
20
+
17
21
  ## Rule Details
18
22
 
19
23
  This rule reports usages of the deprecated `error` prop and provides an IDE suggestion (not an autofix) to rename it.
@@ -21,27 +25,155 @@ This rule reports usages of the deprecated `error` prop and provides an IDE sugg
21
25
  ### ❌ Incorrect
22
26
 
23
27
  ```html
24
- <bento-dropdown error="value" /> <bento-dropdown :error="value" />
28
+ <!-- Static boolean -->
29
+ <bento-dropdown :error="true" :items="items" label="Country" />
30
+
31
+ <!-- Dynamic binding -->
32
+ <bento-dropdown :error="hasError" :items="items" label="Country" />
25
33
  ```
26
34
 
27
35
  ```html
28
- <bento-input-field error="value" /> <bento-input-field :error="value" />
36
+ <bento-input-field :error="true" label="Email" />
37
+
38
+ <bento-input-field :error="hasError" label="Email" />
29
39
  ```
30
40
 
31
41
  ```html
32
- <bento-input-field-phone-number error="value" /> <bento-input-field-phone-number :error="value" />
42
+ <bento-input-field-phone-number :error="true" label="Phone number" />
43
+
44
+ <bento-input-field-phone-number :error="hasError" label="Phone number" />
33
45
  ```
34
46
 
35
47
  ### ✅ Correct
36
48
 
37
49
  ```html
38
- <bento-dropdown error-message="Error message text" />
50
+ <!-- Static error message -->
51
+ <bento-dropdown error-message="Please select a country" :items="items" label="Country" />
52
+
53
+ <!-- Dynamic error message driven by validation -->
54
+ <bento-dropdown :error-message="errorMessage" :items="items" label="Country" />
55
+ ```
56
+
57
+ ```html
58
+ <bento-input-field error-message="Enter a valid email address" label="Email" />
59
+
60
+ <bento-input-field :error-message="errorMessage" label="Email" />
61
+ ```
62
+
63
+ ```html
64
+ <bento-input-field-phone-number error-message="Enter a valid phone number" label="Phone number" />
65
+
66
+ <bento-input-field-phone-number :error-message="errorMessage" label="Phone number" />
67
+ ```
68
+
69
+ ## Migration Examples
70
+
71
+ ### Before — static boolean on `bento-dropdown`
72
+
73
+ ```html
74
+ <template>
75
+ <bento-dropdown :error="true" :items="items" label="Country" />
76
+ </template>
77
+ ```
78
+
79
+ ### After — static error message on `bento-dropdown`
80
+
81
+ ```html
82
+ <template>
83
+ <bento-dropdown error-message="Please select a country" :items="items" label="Country" />
84
+ </template>
39
85
  ```
40
86
 
87
+ ### Before — dynamic validation on `bento-input-field`
88
+
41
89
  ```html
42
- <bento-input-field error-message="Error message text" />
90
+ <template>
91
+ <bento-input-field :error="hasError" v-model="email" label="Email" />
92
+ </template>
93
+
94
+ <script setup lang="ts">
95
+ const email = ref('');
96
+ const hasError = ref(false);
97
+
98
+ const validate = () => {
99
+ hasError.value = !email.value.includes('@');
100
+ };
101
+ </script>
43
102
  ```
44
103
 
104
+ ### After — dynamic validation with error message on `bento-input-field`
105
+
106
+ ```html
107
+ <template>
108
+ <bento-input-field :error-message="errorMessage" v-model="email" label="Email" />
109
+ </template>
110
+
111
+ <script setup lang="ts">
112
+ const email = ref('');
113
+ const errorMessage = ref<string | null>(null);
114
+
115
+ const validate = () => {
116
+ errorMessage.value = !email.value.includes('@') ? 'Enter a valid email address' : null;
117
+ };
118
+ </script>
119
+ ```
120
+
121
+ ### Before — phone number validation
122
+
45
123
  ```html
46
- <bento-input-field-phone-number error-message="Error message text" />
124
+ <template>
125
+ <bento-input-field-phone-number
126
+ :error="hasError"
127
+ v-model="phone"
128
+ :selected-country="country"
129
+ label="Phone number"
130
+ @input:valid="onValid"
131
+ @blur="validate"
132
+ />
133
+ </template>
134
+
135
+ <script setup lang="ts">
136
+ const phone = ref('');
137
+ const country = ref('US');
138
+ const hasError = ref(false);
139
+ const isValid = ref(false);
140
+
141
+ const onValid = (validity: boolean) => {
142
+ isValid.value = validity;
143
+ };
144
+
145
+ const validate = () => {
146
+ hasError.value = !isValid.value && !!phone.value;
147
+ };
148
+ </script>
149
+ ```
150
+
151
+ ### After — phone number validation with error message
152
+
153
+ ```html
154
+ <template>
155
+ <bento-input-field-phone-number
156
+ :error-message="errorMessage"
157
+ v-model="phone"
158
+ :selected-country="country"
159
+ label="Phone number"
160
+ @input:valid="onValid"
161
+ @blur="validate"
162
+ />
163
+ </template>
164
+
165
+ <script setup lang="ts">
166
+ const phone = ref('');
167
+ const country = ref('US');
168
+ const errorMessage = ref<string | null>(null);
169
+ const isValid = ref(false);
170
+
171
+ const onValid = (validity: boolean) => {
172
+ isValid.value = validity;
173
+ };
174
+
175
+ const validate = () => {
176
+ errorMessage.value = !isValid.value && !!phone.value ? 'Enter a valid phone number' : null;
177
+ };
178
+ </script>
47
179
  ```
@@ -7,32 +7,129 @@ The `hasError` prop in the following components is deprecated:
7
7
  - `bento-checkbox-group`
8
8
  - `bento-radio-group`
9
9
 
10
- Please use `errorMessage` instead.
10
+ Please use `error-message` instead.
11
11
 
12
- > **Note:** The `hasError` prop is a `boolean` that only toggles error styling, while `errorMessage` is a `string` that
13
- > both displays a message and applies error styling. A direct rename is not type-safe — you must also update the bound
14
- > value from a boolean to an error message string.
12
+ > **Note:** The `has-error` prop is a `boolean` that only toggles error styling, while `error-message` is a `string`
13
+ > that both displays a message and applies error styling. A direct rename is not type-safe — you must also update the
14
+ > bound value from a boolean to an error message string.
15
+
16
+ | Deprecated prop | Replacement prop | Type change |
17
+ | --------------- | ---------------- | -------------------- |
18
+ | `has-error` | `error-message` | `boolean` → `string` |
15
19
 
16
20
  ## Rule Details
17
21
 
18
- This rule reports usages of the deprecated `hasError` prop and provides an IDE suggestion (not an autofix) to rename it.
22
+ This rule reports usages of the deprecated `has-error` prop and provides an IDE suggestion (not an autofix) to rename
23
+ it.
19
24
 
20
25
  ### ❌ Incorrect
21
26
 
22
27
  ```html
23
- <bento-checkbox-group has-error="value" /> <bento-checkbox-group :has-error="value" />
28
+ <!-- Static boolean -->
29
+ <bento-checkbox-group :has-error="true" :items="items" label="Permissions" />
30
+
31
+ <!-- Dynamic binding -->
32
+ <bento-checkbox-group :has-error="hasError" :items="items" label="Permissions" />
24
33
  ```
25
34
 
26
35
  ```html
27
- <bento-radio-group has-error="value" /> <bento-radio-group :has-error="value" />
36
+ <bento-radio-group :has-error="true" :items="items" label="Priority" />
37
+
38
+ <bento-radio-group :has-error="hasError" :items="items" label="Priority" />
28
39
  ```
29
40
 
30
41
  ### ✅ Correct
31
42
 
32
43
  ```html
33
- <bento-checkbox-group errorMessage="Error message text" />
44
+ <!-- Group-level error: pass a string message to error-message -->
45
+ <bento-checkbox-group error-message="Select at least one option" :items="items" label="Permissions" />
46
+
47
+ <!-- Dynamic error message driven by validation -->
48
+ <bento-checkbox-group :error-message="errorMessage" :items="items" label="Permissions" />
49
+ ```
50
+
51
+ ```html
52
+ <bento-radio-group error-message="Please select an option" :items="items" label="Priority" />
53
+
54
+ <bento-radio-group :error-message="errorMessage" :items="items" label="Priority" />
55
+ ```
56
+
57
+ ## Migration Examples
58
+
59
+ ### Before — static boolean
60
+
61
+ ```html
62
+ <template>
63
+ <bento-checkbox-group :has-error="true" :items="items" label="Toppings" />
64
+ </template>
65
+ ```
66
+
67
+ ### After — static error message
68
+
69
+ ```html
70
+ <template>
71
+ <bento-checkbox-group error-message="Select at least one topping" :items="items" label="Toppings" />
72
+ </template>
73
+ ```
74
+
75
+ ### Before — dynamic validation
76
+
77
+ ```html
78
+ <template>
79
+ <bento-checkbox-group :has-error="hasError" :items="items" label="Toppings" />
80
+ </template>
81
+
82
+ <script setup lang="ts">
83
+ const hasError = ref(false);
84
+
85
+ const validate = () => {
86
+ hasError.value = selectedToppings.value.length === 0;
87
+ };
88
+ </script>
89
+ ```
90
+
91
+ ### After — dynamic validation with error message
92
+
93
+ ```html
94
+ <template>
95
+ <bento-checkbox-group :error-message="errorMessage" :items="items" label="Toppings" />
96
+ </template>
97
+
98
+ <script setup lang="ts">
99
+ const errorMessage = ref<string | null>(null);
100
+
101
+ const validate = () => {
102
+ errorMessage.value = selectedToppings.value.length === 0 ? 'Select at least one topping' : null;
103
+ };
104
+ </script>
34
105
  ```
35
106
 
107
+ ### Before — item-level `hasError`
108
+
36
109
  ```html
37
- <bento-radio-group errorMessage="Error message text" />
110
+ <template>
111
+ <bento-checkbox-group :items="items" label="Permissions" />
112
+ </template>
113
+
114
+ <script setup lang="ts">
115
+ const items = [
116
+ { value: 'read', label: 'Read', hasError: true },
117
+ { value: 'write', label: 'Write' },
118
+ ];
119
+ </script>
120
+ ```
121
+
122
+ ### After — item-level `errorMessage`
123
+
124
+ ```html
125
+ <template>
126
+ <bento-checkbox-group :items="items" label="Permissions" />
127
+ </template>
128
+
129
+ <script setup lang="ts">
130
+ const items = [
131
+ { value: 'read', label: 'Read', errorMessage: 'Read permission is required' },
132
+ { value: 'write', label: 'Write' },
133
+ ];
134
+ </script>
38
135
  ```
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Description
4
4
 
5
- The `input` emit in the following components is deprecated:
5
+ The `@input` event listener in the following components is deprecated:
6
6
 
7
7
  - `bento-date-picker`
8
8
  - `bento-date-range-picker`
@@ -21,11 +21,28 @@ The `input` emit in the following components is deprecated:
21
21
  - `bento-selection-card-group`
22
22
  - `bento-textarea`
23
23
 
24
- Please use `update:model-value` instead.
24
+ Please use `@update:model-value` instead.
25
+
26
+ | Deprecated event | Replacement event | Autofix available |
27
+ | ---------------- | --------------------- | -------------------------- |
28
+ | `@input` | `@update:model-value` | Yes (opt-in via `autofix`) |
29
+
30
+ > **⚠️ Caveat — `v-model` combined with `@input`**
31
+ >
32
+ > Bento Vue 2 components emit **both** `input` (deprecated) and `update:model-value` on every value change for backward
33
+ > compatibility. In Vue 2, `v-model` internally listens to `@input`. If you autofix `@input` to `@update:model-value`
34
+ > while keeping `v-model`, you end up with `v-model` listening to `@input` **and** an explicit `@update:model-value`
35
+ > handler — both of which fire on every change. This causes race conditions because the handler may read the model value
36
+ > before `v-model`'s `@input` listener has propagated the update.
37
+ >
38
+ > In this case, **do not rely on autofix alone**. You must also replace `v-model` with an explicit `:model-value`
39
+ > binding so that a single `@update:model-value` handler controls both the model update and any side effects. See the
40
+ > [Migration Examples](#migration-examples) below.
25
41
 
26
42
  ## Rule Details
27
43
 
28
- This rule reports usages of the deprecated item.
44
+ This rule reports usages of the deprecated `@input` event and can optionally autofix them to `@update:model-value`.
45
+ Autofix is disabled by default and can be enabled with the `autofix` option.
29
46
 
30
47
  ### ❌ Incorrect
31
48
 
@@ -93,6 +110,18 @@ This rule reports usages of the deprecated item.
93
110
  <bento-textarea @input="handler" />
94
111
  ```
95
112
 
113
+ ```html
114
+ <!-- v-on:input directive syntax is also reported -->
115
+ <bento-input-field v-on:input="handler" />
116
+ ```
117
+
118
+ ```html
119
+ <!-- ⚠️ v-model + @input — autofix alone is NOT safe -->
120
+ <bento-input-field v-model="email" @input="onEmailChange" />
121
+
122
+ <bento-dropdown v-model="country" @input="onCountryChange" :items="items" />
123
+ ```
124
+
96
125
  ### ✅ Correct
97
126
 
98
127
  ```html
@@ -158,3 +187,141 @@ This rule reports usages of the deprecated item.
158
187
  ```html
159
188
  <bento-textarea @update:model-value="handler" />
160
189
  ```
190
+
191
+ ```html
192
+ <!-- v-model without a separate handler — no change needed -->
193
+ <bento-input-field v-model="email" />
194
+ ```
195
+
196
+ ```html
197
+ <!-- v-model + @input migrated: split v-model into explicit binding + handler -->
198
+ <bento-input-field :model-value="email" @update:model-value="onEmailChange" />
199
+
200
+ <bento-dropdown :model-value="country" @update:model-value="onCountryChange" :items="items" />
201
+ ```
202
+
203
+ ## Migration Examples
204
+
205
+ If `v-model` is used alongside `@input`, you must also replace `v-model` with `:model-value` — see Case 2. Otherwise,
206
+ renaming `@input` to `@update:model-value` is sufficient — see Cases 1 and 3.
207
+
208
+ ### Case 1 — standalone `@input` (safe to autofix)
209
+
210
+ The simplest case: `@input` is used without `v-model`. Rename it to `@update:model-value`.
211
+
212
+ #### Before
213
+
214
+ ```html
215
+ <template>
216
+ <bento-dropdown @input="onSelect" :items="countries" label="Country" />
217
+ </template>
218
+
219
+ <script setup lang="ts">
220
+ const onSelect = (value: string) => {
221
+ console.log('Selected:', value);
222
+ };
223
+ </script>
224
+ ```
225
+
226
+ #### After
227
+
228
+ ```html
229
+ <template>
230
+ <bento-dropdown @update:model-value="onSelect" :items="countries" label="Country" />
231
+ </template>
232
+
233
+ <script setup lang="ts">
234
+ const onSelect = (value: string) => {
235
+ console.log('Selected:', value);
236
+ };
237
+ </script>
238
+ ```
239
+
240
+ ### Case 2 — `v-model` + `@input` (autofix NOT safe — requires manual split)
241
+
242
+ When `v-model` is used alongside `@input`, autofix alone will cause a dual-emission race condition. You must replace
243
+ `v-model` with an explicit `:model-value` binding and merge the update logic into a single `@update:model-value`
244
+ handler.
245
+
246
+ #### Before — `v-model` + `@input`
247
+
248
+ ```html
249
+ <template>
250
+ <bento-dropdown v-model="country" @input="onCountryChange" :items="countries" label="Country" />
251
+ </template>
252
+
253
+ <script setup lang="ts">
254
+ const country = ref('');
255
+
256
+ const onCountryChange = (value: string) => {
257
+ fetchCities(value);
258
+ };
259
+ </script>
260
+ ```
261
+
262
+ #### ❌ Broken — after autofix only (dual-emission race condition)
263
+
264
+ ```html
265
+ <template>
266
+ <!-- v-model internally listens to @input, while @update:model-value listens to the new event.
267
+ The component emits BOTH events on every change, causing a race condition:
268
+ onCountryChange may read `country` before v-model's @input handler has updated it. -->
269
+ <bento-dropdown v-model="country" @update:model-value="onCountryChange" :items="countries" label="Country" />
270
+ </template>
271
+ ```
272
+
273
+ #### ✅ Correct — split `v-model` and merge handlers
274
+
275
+ Replace `v-model` with `:model-value` and merge the update logic into one handler:
276
+
277
+ ```html
278
+ <template>
279
+ <bento-dropdown :model-value="country" @update:model-value="onCountryChange" :items="countries" label="Country" />
280
+ </template>
281
+
282
+ <script setup lang="ts">
283
+ const country = ref('');
284
+
285
+ const onCountryChange = (value: string) => {
286
+ country.value = value;
287
+ fetchCities(value);
288
+ };
289
+ </script>
290
+ ```
291
+
292
+ ### Case 3 — explicit `:model-value` + `@input` (safe to autofix)
293
+
294
+ When `:model-value` is already used instead of `v-model`, there is no dual-emission conflict. Rename `@input` to
295
+ `@update:model-value`.
296
+
297
+ #### Before — `:model-value` + `@input`
298
+
299
+ ```html
300
+ <template>
301
+ <bento-checkbox-group :model-value="selected" @input="onSelect" :items="items" label="Toppings" />
302
+ </template>
303
+
304
+ <script setup lang="ts">
305
+ const selected = ref([]);
306
+
307
+ const onSelect = (value: string[]) => {
308
+ selected.value = value;
309
+ };
310
+ </script>
311
+ ```
312
+
313
+ #### After — rename to `@update:model-value`
314
+
315
+ ```html
316
+ <template>
317
+ <bento-checkbox-group :model-value="selected" @update:model-value="onSelect" :items="items" label="Toppings" />
318
+ </template>
319
+
320
+ <script setup lang="ts">
321
+ const selected = ref([]);
322
+
323
+ const onSelect = (value: string[]) => {
324
+ selected.value = value;
325
+ };
326
+ </script>
327
+ ```