@adyen/eslint-plugin-bento 2.14.0 → 2.16.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 (19) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/package.json +1 -1
  3. package/rules/deprecation-bento-timeline-item-variant-prop/README.md +13 -5
  4. package/rules/deprecation-bento-timeline-item-variant-prop/__tests__/deprecation-bento-timeline-item-variant-prop.vue +2 -2
  5. package/rules/deprecation-bento-timeline-item-variant-prop/deprecation-bento-timeline-item-variant-prop.integration.test.ts +6 -2
  6. package/rules/deprecation-bento-timeline-item-variant-prop/deprecation-bento-timeline-item-variant-prop.js +61 -12
  7. package/rules/deprecation-bento-timeline-item-variant-prop/deprecation-bento-timeline-item-variant-prop.test.ts +87 -12
  8. package/rules/deprecation-components-error-prop/README.md +143 -7
  9. package/rules/deprecation-components-error-prop/__tests__/eslint.config.js +1 -1
  10. package/rules/deprecation-components-error-prop/deprecation-components-error-prop.integration.test.ts +14 -10
  11. package/rules/deprecation-components-error-prop/deprecation-components-error-prop.js +20 -30
  12. package/rules/deprecation-components-error-prop/deprecation-components-error-prop.test.ts +69 -36
  13. package/rules/deprecation-components-has-error-prop/README.md +107 -6
  14. package/rules/deprecation-components-has-error-prop/deprecation-components-has-error-prop.integration.test.ts +10 -2
  15. package/rules/deprecation-components-has-error-prop/deprecation-components-has-error-prop.js +18 -24
  16. package/rules/deprecation-components-has-error-prop/deprecation-components-has-error-prop.test.ts +58 -69
  17. package/rules/deprecation-components-input-emit/README.md +170 -3
  18. package/rules/deprecation-components-input-emit/deprecation-components-input-emit.integration.test.ts +9 -1
  19. package/rules/deprecation-components-input-emit/deprecation-components-input-emit.js +9 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.16.0 (2026-06-09)
4
+
5
+ ### Features
6
+
7
+ - added eslint rule readme to mcp `get-deprecations` tool ([44b9ce380](https://github.com/Adyen/bento/commit/44b9ce380))
8
+
9
+ ### ❤️ Thank You
10
+
11
+ - erice
12
+
13
+
14
+ ## 2.15.0 (2026-06-03)
15
+
16
+ ### Bug Fixes
17
+
18
+ - fixed `deprecation-components-input-emit` error message wording ([5a22428fa](https://github.com/Adyen/bento/commit/5a22428fa))
19
+ - updated `deprecation-components-error-prop` rule from fix to suggest ([edcf472fd](https://github.com/Adyen/bento/commit/edcf472fd))
20
+ - updated `deprecation-components-has-error-prop` rule from fix to suggest ([f573b3643](https://github.com/Adyen/bento/commit/f573b3643))
21
+ - updated `deprecation-bento-timeline-item-variant-prop` to replace values correctly ([989c92838](https://github.com/Adyen/bento/commit/989c92838))
22
+
23
+ ### Code Refactoring
24
+
25
+ - extended `deprecation-components-input-emit` error message with race conditions example ([4030de20f](https://github.com/Adyen/bento/commit/4030de20f))
26
+
27
+ ### ❤️ Thank You
28
+
29
+ - daver
30
+ - erice
31
+ - gerald
32
+
33
+
3
34
  ## 2.14.0 (2026-05-27)
4
35
 
5
36
  This was a version bump only for eslint to align it with other projects, there were no code changes.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adyen/eslint-plugin-bento",
3
- "version": "2.14.0",
3
+ "version": "2.16.0",
4
4
  "description": "Adyen Bento ESLint rules",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -2,20 +2,28 @@
2
2
 
3
3
  ## Description
4
4
 
5
- The `variant` prop in the following components is deprecated:
5
+ The `variant` prop in `bento-timeline-item` is deprecated. Use `status` instead.
6
6
 
7
- - `bento-timeline-item`
7
+ The `variant` prop accepted `BentoTimelineItemVariant` enum values (icon names), while `status` accepts color strings.
8
+ The autofix remaps values automatically:
8
9
 
9
- Please use `status` instead.
10
+ | `variant` value | `status` value |
11
+ | ------------------------------- | -------------- |
12
+ | `SquareSmallIcon` / `DEFAULT` | `black` |
13
+ | `CheckedCircleIcon` / `SUCCESS` | `green` |
14
+ | `CrossCircleIcon` / `CRITICAL` | `red` |
15
+
16
+ > **Note:** If the variant value cannot be statically resolved (e.g. a variable reference), the autofix is skipped and
17
+ > only the deprecation warning is reported.
10
18
 
11
19
  ## Rule Details
12
20
 
13
- This rule reports usages of the deprecated item.
21
+ This rule reports usages of the deprecated `variant` prop and autofixes it to `status` with the correct color value.
14
22
 
15
23
  ### ❌ Incorrect
16
24
 
17
25
  ```html
18
- <bento-timeline-item variant="SUCCESS" /> <bento-timeline-item :variant="BentoTimelineItemVariant.CRITICAL" />
26
+ <bento-timeline-item variant="CheckedCircleIcon" /> <bento-timeline-item :variant="BentoTimelineItemVariant.CRITICAL" />
19
27
  ```
20
28
 
21
29
  ### ✅ Correct
@@ -1,8 +1,8 @@
1
1
  <template>
2
2
  <div>
3
- <bento-timeline-item variant="SUCCESS" />
3
+ <bento-timeline-item variant="CheckedCircleIcon" />
4
4
  <bento-timeline-item :variant="BentoTimelineItemVariant.CRITICAL" />
5
- <bento-timeline-item :variant="BentoTimelineItemVariant.SUCCESS" />
5
+ <bento-timeline-item :variant="BentoTimelineItemVariant.DEFAULT" />
6
6
  </div>
7
7
  </template>
8
8
 
@@ -8,8 +8,12 @@ describe('@adyen/bento/deprecation-bento-timeline-item-variant-prop', () => {
8
8
  // Load the test file generated alongside this one
9
9
  const vueFile = path.resolve(__dirname, '__tests__', 'deprecation-bento-timeline-item-variant-prop.vue');
10
10
 
11
+ // Strip any non-JSON preamble (e.g. pnpm warnings)
12
+ const rawOutput = execFileSyncEslintVueFiles(eslintrc, vueFile);
13
+ const jsonStart = rawOutput.indexOf('[');
14
+
11
15
  // Execute the linter
12
- const result = JSON.parse(execFileSyncEslintVueFiles(eslintrc, vueFile));
16
+ const result = JSON.parse(jsonStart >= 0 ? rawOutput.slice(jsonStart) : rawOutput);
13
17
 
14
18
  // 3 examples: static, shorthand, longhand
15
19
  expect(result[0].warningCount).toBe(3);
@@ -24,7 +28,7 @@ describe('@adyen/bento/deprecation-bento-timeline-item-variant-prop', () => {
24
28
  message: `The "variant" prop is deprecated. Please use "status" instead.`,
25
29
 
26
30
  fix: expect.objectContaining({
27
- text: expect.stringContaining('status'),
31
+ text: expect.stringContaining('status='),
28
32
  }),
29
33
  });
30
34
  });
@@ -7,6 +7,47 @@
7
7
 
8
8
  const VueUtils = require('eslint-plugin-vue/lib/utils');
9
9
 
10
+ // Maps BentoTimelineItemVariant enum values (runtime strings) to status colors
11
+ const VARIANT_VALUE_TO_STATUS = {
12
+ SquareSmallIcon: 'black',
13
+ CheckedCircleIcon: 'green',
14
+ CrossCircleIcon: 'red',
15
+ };
16
+
17
+ // Maps BentoTimelineItemVariant enum member names to status colors
18
+ const VARIANT_ENUM_KEY_TO_STATUS = {
19
+ DEFAULT: 'black',
20
+ SUCCESS: 'green',
21
+ CRITICAL: 'red',
22
+ };
23
+
24
+ /**
25
+ * Resolves a dynamic expression to a status color string.
26
+ * Returns null if the expression cannot be statically resolved.
27
+ */
28
+ function resolveExpressionToStatus(expression) {
29
+ if (!expression) {
30
+ return null;
31
+ }
32
+
33
+ // String literal: :variant="'CheckedCircleIcon'"
34
+ if (expression.type === 'Literal' && typeof expression.value === 'string') {
35
+ return VARIANT_VALUE_TO_STATUS[expression.value] ?? null;
36
+ }
37
+
38
+ // Enum member: :variant="BentoTimelineItemVariant.SUCCESS"
39
+ if (
40
+ expression.type === 'MemberExpression' &&
41
+ expression.object?.type === 'Identifier' &&
42
+ expression.object.name === 'BentoTimelineItemVariant' &&
43
+ expression.property?.type === 'Identifier'
44
+ ) {
45
+ return VARIANT_ENUM_KEY_TO_STATUS[expression.property.name] ?? null;
46
+ }
47
+
48
+ return null;
49
+ }
50
+
10
51
  /** @type {RuleModule} */
11
52
  module.exports = {
12
53
  meta: {
@@ -44,33 +85,41 @@ module.exports = {
44
85
  /* PROP DEPRECATION */
45
86
  /* ------------------------------------------------ */
46
87
  return VueUtils.defineTemplateBodyVisitor(context, {
47
- // 1. STATIC: value="foo"
88
+ // 1. STATIC: variant="CheckedCircleIcon"
48
89
  [`VElement:matches([name="bento-timeline-item"], [name="BentoTimelineItem"]) > VStartTag > VAttribute[directive=false][key.name="variant"]`](
49
90
  node
50
91
  ) {
92
+ const staticValue = node.value?.value;
93
+ const mappedStatus = staticValue ? VARIANT_VALUE_TO_STATUS[staticValue] : null;
94
+
51
95
  context.report({
52
96
  node,
53
97
  messageId: 'deprecation',
54
- ...(autofix && {
55
- fix(fixer) {
56
- return fixer.replaceText(node.key, 'status');
57
- },
58
- }),
98
+ ...(autofix &&
99
+ mappedStatus && {
100
+ fix(fixer) {
101
+ return fixer.replaceText(node, `status="${mappedStatus}"`);
102
+ },
103
+ }),
59
104
  });
60
105
  },
61
106
 
62
- // 2. DYNAMIC: :value="foo" or v-bind:value="foo"
107
+ // 2. DYNAMIC: :variant="BentoTimelineItemVariant.SUCCESS" or v-bind:variant="..."
63
108
  [`VElement:matches([name="bento-timeline-item"], [name="BentoTimelineItem"]) > VStartTag > VAttribute[directive=true][key.name.name="bind"][key.argument.name="variant"]`](
64
109
  node
65
110
  ) {
111
+ const expression = node.value?.expression;
112
+ const mappedStatus = resolveExpressionToStatus(expression);
113
+
66
114
  context.report({
67
115
  node,
68
116
  messageId: 'deprecation',
69
- ...(autofix && {
70
- fix(fixer) {
71
- return fixer.replaceText(node.key.argument, 'status');
72
- },
73
- }),
117
+ ...(autofix &&
118
+ mappedStatus && {
119
+ fix(fixer) {
120
+ return fixer.replaceText(node, `status="${mappedStatus}"`);
121
+ },
122
+ }),
74
123
  });
75
124
  },
76
125
  });
@@ -18,20 +18,21 @@ describe('@adyen/bento/deprecation-bento-timeline-item-variant-prop', () => {
18
18
  ruleTester.run('deprecation-bento-timeline-item-variant-prop', deprecationBentoTimelineItemVariantProp, {
19
19
  valid: [
20
20
  {
21
- name: 'Valid usage for bento-timeline-item',
21
+ name: 'Valid usage with status prop',
22
22
  filename: 'test.vue',
23
23
  code: `
24
24
  <template>
25
- <bento-timeline-item status="val" />
25
+ <bento-timeline-item status="green" />
26
26
  </template>
27
27
  `,
28
28
  },
29
29
  ],
30
30
  invalid: [
31
+ // No autofix when disabled
31
32
  {
32
33
  name: 'Invalid: autofix disabled (explicit false)',
33
34
  filename: 'test.vue',
34
- code: `<template><bento-timeline-item variant="val" /></template>`,
35
+ code: `<template><bento-timeline-item variant="CheckedCircleIcon" /></template>`,
35
36
  output: null,
36
37
  options: [{ autofix: false }],
37
38
  errors: [{ messageId: 'deprecation' }],
@@ -39,25 +40,99 @@ describe('@adyen/bento/deprecation-bento-timeline-item-variant-prop', () => {
39
40
  {
40
41
  name: 'Invalid: autofix disabled (no options)',
41
42
  filename: 'test.vue',
42
- code: `<template><bento-timeline-item :variant="val" /></template>`,
43
+ code: `<template><bento-timeline-item :variant="BentoTimelineItemVariant.SUCCESS" /></template>`,
43
44
  output: null,
44
45
  errors: [{ messageId: 'deprecation' }],
45
46
  },
46
- // bento-timeline-item: Static
47
+
48
+ // Static values — remaps enum value strings to colors
47
49
  {
48
- name: 'Invalid: Static prop on bento-timeline-item',
50
+ name: 'Invalid: Static variant="SquareSmallIcon" status="black"',
49
51
  filename: 'test.vue',
50
- code: `<template><bento-timeline-item variant="val" /></template>`,
51
- output: `<template><bento-timeline-item status="val" /></template>`,
52
+ code: `<template><bento-timeline-item variant="SquareSmallIcon" /></template>`,
53
+ output: `<template><bento-timeline-item status="black" /></template>`,
52
54
  options: [{ autofix: true }],
53
55
  errors: [{ messageId: 'deprecation' }],
54
56
  },
55
- // bento-timeline-item: Shorthand
56
57
  {
57
- name: 'Invalid: Dynamic prop on bento-timeline-item',
58
+ name: 'Invalid: Static variant="CheckedCircleIcon" status="green"',
58
59
  filename: 'test.vue',
59
- code: `<template><bento-timeline-item :variant="val" /></template>`,
60
- output: `<template><bento-timeline-item :status="val" /></template>`,
60
+ code: `<template><bento-timeline-item variant="CheckedCircleIcon" /></template>`,
61
+ output: `<template><bento-timeline-item status="green" /></template>`,
62
+ options: [{ autofix: true }],
63
+ errors: [{ messageId: 'deprecation' }],
64
+ },
65
+ {
66
+ name: 'Invalid: Static variant="CrossCircleIcon" → status="red"',
67
+ filename: 'test.vue',
68
+ code: `<template><bento-timeline-item variant="CrossCircleIcon" /></template>`,
69
+ output: `<template><bento-timeline-item status="red" /></template>`,
70
+ options: [{ autofix: true }],
71
+ errors: [{ messageId: 'deprecation' }],
72
+ },
73
+
74
+ // Static with unknown value — no fix applied
75
+ {
76
+ name: 'Invalid: Static variant with unknown value does not autofix',
77
+ filename: 'test.vue',
78
+ code: `<template><bento-timeline-item variant="unknownValue" /></template>`,
79
+ output: null,
80
+ options: [{ autofix: true }],
81
+ errors: [{ messageId: 'deprecation' }],
82
+ },
83
+
84
+ // Dynamic with enum member expression — remaps enum key to color
85
+ {
86
+ name: 'Invalid: Dynamic :variant="BentoTimelineItemVariant.DEFAULT" → status="black"',
87
+ filename: 'test.vue',
88
+ code: `<template><bento-timeline-item :variant="BentoTimelineItemVariant.DEFAULT" /></template>`,
89
+ output: `<template><bento-timeline-item status="black" /></template>`,
90
+ options: [{ autofix: true }],
91
+ errors: [{ messageId: 'deprecation' }],
92
+ },
93
+ {
94
+ name: 'Invalid: Dynamic :variant="BentoTimelineItemVariant.SUCCESS" → status="green"',
95
+ filename: 'test.vue',
96
+ code: `<template><bento-timeline-item :variant="BentoTimelineItemVariant.SUCCESS" /></template>`,
97
+ output: `<template><bento-timeline-item status="green" /></template>`,
98
+ options: [{ autofix: true }],
99
+ errors: [{ messageId: 'deprecation' }],
100
+ },
101
+ {
102
+ name: 'Invalid: Dynamic :variant="BentoTimelineItemVariant.CRITICAL" → status="red"',
103
+ filename: 'test.vue',
104
+ code: `<template><bento-timeline-item :variant="BentoTimelineItemVariant.CRITICAL" /></template>`,
105
+ output: `<template><bento-timeline-item status="red" /></template>`,
106
+ options: [{ autofix: true }],
107
+ errors: [{ messageId: 'deprecation' }],
108
+ },
109
+
110
+ // Dynamic with string literal — remaps value to color
111
+ {
112
+ name: 'Invalid: Dynamic :variant="\'CheckedCircleIcon\'" → status="green"',
113
+ filename: 'test.vue',
114
+ code: `<template><bento-timeline-item :variant="'CheckedCircleIcon'" /></template>`,
115
+ output: `<template><bento-timeline-item status="green" /></template>`,
116
+ options: [{ autofix: true }],
117
+ errors: [{ messageId: 'deprecation' }],
118
+ },
119
+
120
+ // Dynamic with unknown expression — no fix applied
121
+ {
122
+ name: 'Invalid: Dynamic :variant with unknown expression does not autofix',
123
+ filename: 'test.vue',
124
+ code: `<template><bento-timeline-item :variant="computedVariant" /></template>`,
125
+ output: null,
126
+ options: [{ autofix: true }],
127
+ errors: [{ messageId: 'deprecation' }],
128
+ },
129
+
130
+ // Dynamic with non-BentoTimelineItemVariant enum — no fix applied
131
+ {
132
+ name: 'Invalid: Dynamic :variant with unrelated enum does not autofix',
133
+ filename: 'test.vue',
134
+ code: `<template><bento-timeline-item :variant="customStatus.SUCCESS" /></template>`,
135
+ output: null,
61
136
  options: [{ autofix: true }],
62
137
  errors: [{ messageId: 'deprecation' }],
63
138
  },
@@ -10,34 +10,170 @@ The `error` prop in the following components is deprecated:
10
10
 
11
11
  Please use `error-message` instead.
12
12
 
13
+ > **Note:** The `error` prop is a `boolean` that only toggles error styling, while `error-message` is a `string` that
14
+ > both displays a message and applies error styling. A direct rename is not type-safe — you must also update the bound
15
+ > value from a boolean to an error message string.
16
+
17
+ | Deprecated prop | Replacement prop | Type change |
18
+ | --------------- | ---------------- | -------------------- |
19
+ | `error` | `error-message` | `boolean` → `string` |
20
+
13
21
  ## Rule Details
14
22
 
15
- This rule reports usages of the deprecated item.
23
+ This rule reports usages of the deprecated `error` prop and provides an IDE suggestion (not an autofix) to rename it.
16
24
 
17
25
  ### ❌ Incorrect
18
26
 
19
27
  ```html
20
- <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" />
21
33
  ```
22
34
 
23
35
  ```html
24
- <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" />
25
39
  ```
26
40
 
27
41
  ```html
28
- <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" />
29
45
  ```
30
46
 
31
47
  ### ✅ Correct
32
48
 
33
49
  ```html
34
- <bento-dropdown error-message="value" />
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" />
35
67
  ```
36
68
 
69
+ ## Migration Examples
70
+
71
+ ### Before — static boolean on `bento-dropdown`
72
+
37
73
  ```html
38
- <bento-input-field error-message="value" />
74
+ <template>
75
+ <bento-dropdown :error="true" :items="items" label="Country" />
76
+ </template>
39
77
  ```
40
78
 
79
+ ### After — static error message on `bento-dropdown`
80
+
41
81
  ```html
42
- <bento-input-field-phone-number error-message="value" />
82
+ <template>
83
+ <bento-dropdown error-message="Please select a country" :items="items" label="Country" />
84
+ </template>
85
+ ```
86
+
87
+ ### Before — dynamic validation on `bento-input-field`
88
+
89
+ ```html
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>
102
+ ```
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
+
123
+ ```html
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>
43
179
  ```
@@ -19,7 +19,7 @@ module.exports = defineConfig([
19
19
  },
20
20
  rules: {
21
21
  // Register the specific rule we are testing
22
- '@adyen/bento/deprecation-components-error-prop': ['warn', { autofix: true }],
22
+ '@adyen/bento/deprecation-components-error-prop': 'warn',
23
23
  },
24
24
  files: ['**/*.vue'],
25
25
  },
@@ -8,8 +8,10 @@ describe('@adyen/bento/deprecation-components-error-prop', () => {
8
8
  // Load the test file generated alongside this one
9
9
  const vueFile = path.resolve(__dirname, '__tests__', 'deprecation-components-error-prop.vue');
10
10
 
11
- // Execute the linter
12
- const result = JSON.parse(execFileSyncEslintVueFiles(eslintrc, vueFile));
11
+ // Execute the linter — strip any non-JSON preamble (e.g. pnpm warnings)
12
+ const rawOutput = execFileSyncEslintVueFiles(eslintrc, vueFile);
13
+ const jsonStart = rawOutput.indexOf('[{"');
14
+ const result = JSON.parse(jsonStart >= 0 ? rawOutput.slice(jsonStart) : rawOutput);
13
15
 
14
16
  /* ------------------------------------------------ */
15
17
  /* CASE 2: SINGLE REPLACEMENT */
@@ -20,18 +22,20 @@ describe('@adyen/bento/deprecation-components-error-prop', () => {
20
22
  const numberOfComponents = 3;
21
23
  expect(result[0].warningCount).toBe(numberOfComponents * 3);
22
24
 
23
- // Verify the message structure on the first error found.
24
- // Since the rule is the same for all components, checking the first one confirms the message is correct.
25
+ // Verify the message structure on the first warning found.
25
26
  expect(result[0].messages[0]).toMatchObject({
26
27
  ruleId: '@adyen/bento/deprecation-components-error-prop',
27
28
  severity: 1,
28
-
29
29
  messageId: 'deprecation',
30
- message: `The "error" prop is deprecated. Please use "error-message" instead.`,
31
-
32
- fix: expect.objectContaining({
33
- text: expect.stringContaining('error-message'),
34
- }),
30
+ message: `The "error" prop is deprecated. Use "error-message" instead. Note that "error" accepted a boolean, but "error-message" expects a string, so update the value to an error message.`,
31
+ suggestions: expect.arrayContaining([
32
+ expect.objectContaining({
33
+ messageId: 'suggestReplace',
34
+ fix: expect.objectContaining({
35
+ text: expect.stringContaining('error-message'),
36
+ }),
37
+ }),
38
+ ]),
35
39
  });
36
40
  });
37
41
  });
@@ -19,59 +19,49 @@ module.exports = {
19
19
  recommended: true,
20
20
  },
21
21
  messages: {
22
- deprecation: `The "error" prop is deprecated. Please use "error-message" instead.`,
22
+ deprecation: `The "error" prop is deprecated. Use "error-message" instead. Note that "error" accepted a boolean, but "error-message" expects a string, so update the value to an error message.`,
23
+ suggestReplace: `Replace "error" with "error-message". Remember to update the value from a boolean to a string error message.`,
23
24
  },
24
- fixable: 'code',
25
- schema: [
26
- {
27
- type: 'object',
28
- properties: {
29
- autofix: {
30
- type: 'boolean',
31
- default: false,
32
- },
33
- },
34
- additionalProperties: false,
35
- },
36
- ],
25
+ hasSuggestions: true,
26
+ schema: [],
37
27
  },
38
28
 
39
29
  /** @param {RuleContext} context */
40
30
  create(context) {
41
- const options = context.options[0] || {};
42
- const autofix = options.autofix === true;
43
-
44
- /* ------------------------------------------------ */
45
- /* CASE 2: PROP DEPRECATION (Multiple Components) */
46
- /* ------------------------------------------------ */
47
31
  return VueUtils.defineTemplateBodyVisitor(context, {
48
- // 1. STATIC: value="foo"
32
+ // 1. STATIC: error="foo"
49
33
  [`VElement:matches([name="bento-dropdown"], [name="BentoDropdown"], [name="bento-input-field"], [name="BentoInputField"], [name="bento-input-field-phone-number"], [name="BentoInputFieldPhoneNumber"]) > VStartTag > VAttribute[directive=false][key.name="error"]`](
50
34
  node
51
35
  ) {
52
36
  context.report({
53
37
  node,
54
38
  messageId: 'deprecation',
55
- ...(autofix && {
56
- fix(fixer) {
57
- return fixer.replaceText(node.key, 'error-message');
39
+ suggest: [
40
+ {
41
+ messageId: 'suggestReplace',
42
+ fix(fixer) {
43
+ return fixer.replaceText(node.key, 'error-message');
44
+ },
58
45
  },
59
- }),
46
+ ],
60
47
  });
61
48
  },
62
49
 
63
- // 2. DYNAMIC: :value="foo" or v-bind:value="foo"
50
+ // 2. DYNAMIC: :error="foo" or v-bind:error="foo"
64
51
  [`VElement:matches([name="bento-dropdown"], [name="BentoDropdown"], [name="bento-input-field"], [name="BentoInputField"], [name="bento-input-field-phone-number"], [name="BentoInputFieldPhoneNumber"]) > VStartTag > VAttribute[directive=true][key.name.name="bind"][key.argument.name="error"]`](
65
52
  node
66
53
  ) {
67
54
  context.report({
68
55
  node,
69
56
  messageId: 'deprecation',
70
- ...(autofix && {
71
- fix(fixer) {
72
- return fixer.replaceText(node.key.argument, 'error-message');
57
+ suggest: [
58
+ {
59
+ messageId: 'suggestReplace',
60
+ fix(fixer) {
61
+ return fixer.replaceText(node.key.argument, 'error-message');
62
+ },
73
63
  },
74
- }),
64
+ ],
75
65
  });
76
66
  },
77
67
  });