@adyen/eslint-plugin-bento 2.11.0 → 2.13.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 +16 -0
- package/configs/vue-recommended.js +1 -0
- package/configs/vue-strict.js +1 -0
- package/index.js +1 -0
- package/package.json +1 -1
- package/rules/deprecation-bento-date-range-picker-has-actions-prop/README.md +50 -0
- package/rules/deprecation-bento-date-range-picker-has-actions-prop/__tests__/deprecation-bento-date-range-picker-has-actions-prop.vue +10 -0
- package/rules/deprecation-bento-date-range-picker-has-actions-prop/__tests__/eslint.config.js +29 -0
- package/rules/deprecation-bento-date-range-picker-has-actions-prop/__tests__/remove/deprecation-bento-date-range-picker-has-actions-prop.vue +11 -0
- package/rules/deprecation-bento-date-range-picker-has-actions-prop/__tests__/remove/eslint.config.js +29 -0
- package/rules/deprecation-bento-date-range-picker-has-actions-prop/deprecation-bento-date-range-picker-has-actions-prop.integration.test.ts +47 -0
- package/rules/deprecation-bento-date-range-picker-has-actions-prop/deprecation-bento-date-range-picker-has-actions-prop.js +205 -0
- package/rules/deprecation-bento-date-range-picker-has-actions-prop/deprecation-bento-date-range-picker-has-actions-prop.test.ts +176 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.13.0 (2026-05-20)
|
|
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.12.0 (2026-05-12)
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
- added rule to deprecate `bento-date-range-picker` prop `has-actions` in eslint ([d594fcb72](https://github.com/Adyen/bento/commit/d594fcb72))
|
|
13
|
+
|
|
14
|
+
### ❤️ Thank You
|
|
15
|
+
|
|
16
|
+
- erice
|
|
17
|
+
|
|
18
|
+
|
|
3
19
|
## 2.11.0 (2026-05-06)
|
|
4
20
|
|
|
5
21
|
### Features
|
|
@@ -12,6 +12,7 @@ module.exports = [
|
|
|
12
12
|
},
|
|
13
13
|
rules: {
|
|
14
14
|
'@adyen/bento/deprecation-bento-data-grid-cell-action-menu-item-handler': 'warn',
|
|
15
|
+
'@adyen/bento/deprecation-bento-date-range-picker-has-actions-prop': 'warn',
|
|
15
16
|
'@adyen/bento/deprecation-input-filter-dropdown-options': 'warn',
|
|
16
17
|
'@adyen/bento/deprecation-bento-dropdown-size-prop': 'warn',
|
|
17
18
|
'@adyen/bento/deprecation-bento-timeline-item-variant-prop': 'warn',
|
package/configs/vue-strict.js
CHANGED
|
@@ -12,6 +12,7 @@ module.exports = [
|
|
|
12
12
|
},
|
|
13
13
|
rules: {
|
|
14
14
|
'@adyen/bento/deprecation-bento-data-grid-cell-action-menu-item-handler': 'error',
|
|
15
|
+
'@adyen/bento/deprecation-bento-date-range-picker-has-actions-prop': 'error',
|
|
15
16
|
'@adyen/bento/deprecation-input-filter-dropdown-options': 'error',
|
|
16
17
|
'@adyen/bento/deprecation-bento-dropdown-size-prop': 'error',
|
|
17
18
|
'@adyen/bento/deprecation-bento-timeline-item-variant-prop': 'error',
|
package/index.js
CHANGED
|
@@ -13,6 +13,7 @@ const plugin = {
|
|
|
13
13
|
},
|
|
14
14
|
rules: {
|
|
15
15
|
'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
|
+
'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'),
|
|
16
17
|
'deprecation-input-filter-dropdown-options': require('./rules/deprecation-input-filter-dropdown-options/deprecation-input-filter-dropdown-options'),
|
|
17
18
|
'deprecation-bento-dropdown-size-prop': require('./rules/deprecation-bento-dropdown-size-prop/deprecation-bento-dropdown-size-prop'),
|
|
18
19
|
'deprecation-bento-timeline-item-variant-prop': require('./rules/deprecation-bento-timeline-item-variant-prop/deprecation-bento-timeline-item-variant-prop'),
|
package/package.json
CHANGED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# deprecation-bento-date-range-picker-has-actions-prop
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
Manages the **temporary** `has-actions` prop on `bento-date-range-picker`.
|
|
6
|
+
|
|
7
|
+
The `has-actions` prop is a temporary opt-in flag for the new actions behavior. This rule helps teams adopt it during
|
|
8
|
+
the transition period, and later remove it once the behavior becomes the default.
|
|
9
|
+
|
|
10
|
+
## Options
|
|
11
|
+
|
|
12
|
+
| Option | Type | Default | Description |
|
|
13
|
+
| :------------ | :-------------------- | :------ | :------------------------------------------------------------- |
|
|
14
|
+
| **`action`** | `'add'` \| `'remove'` | `'add'` | `add` reports missing `has-actions`. `remove` reports present. |
|
|
15
|
+
| **`autofix`** | `boolean` | `false` | Enables auto-fixing. |
|
|
16
|
+
|
|
17
|
+
## Rule Details
|
|
18
|
+
|
|
19
|
+
### Action: `add` (default)
|
|
20
|
+
|
|
21
|
+
Reports `bento-date-range-picker` instances that are **missing** the `has-actions` prop.
|
|
22
|
+
|
|
23
|
+
#### ❌ Incorrect
|
|
24
|
+
|
|
25
|
+
```html
|
|
26
|
+
<bento-date-range-picker label="Pick" />
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
#### ✅ Correct
|
|
30
|
+
|
|
31
|
+
```html
|
|
32
|
+
<bento-date-range-picker has-actions label="Pick" />
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Action: `remove`
|
|
36
|
+
|
|
37
|
+
Reports `bento-date-range-picker` instances that **have** the `has-actions` prop, so it can be cleaned up once the
|
|
38
|
+
behavior becomes permanent.
|
|
39
|
+
|
|
40
|
+
#### ❌ Incorrect
|
|
41
|
+
|
|
42
|
+
```html
|
|
43
|
+
<bento-date-range-picker has-actions label="Pick" />
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
#### ✅ Correct
|
|
47
|
+
|
|
48
|
+
```html
|
|
49
|
+
<bento-date-range-picker label="Pick" />
|
|
50
|
+
```
|
|
@@ -0,0 +1,10 @@
|
|
|
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>
|
|
@@ -0,0 +1,29 @@
|
|
|
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
|
+
]);
|
|
@@ -0,0 +1,11 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
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
|
+
]);
|
|
@@ -0,0 +1,47 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1,205 @@
|
|
|
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 COMPONENT_SELECTOR = `VElement:matches([name="bento-date-range-picker"], [name="BentoDateRangePicker"])`;
|
|
11
|
+
const STATIC_ATTR = `${COMPONENT_SELECTOR} > VStartTag > VAttribute[directive=false][key.name="has-actions"]`;
|
|
12
|
+
const DYNAMIC_ATTR = `${COMPONENT_SELECTOR} > VStartTag > VAttribute[directive=true][key.name.name="bind"][key.argument.name="has-actions"]`;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Checks whether a VElement already has the `has-actions` attribute set to a truthy value.
|
|
16
|
+
* Returns false if the prop is missing or explicitly set to false.
|
|
17
|
+
*/
|
|
18
|
+
function hasHasActionsTruthy(startTag) {
|
|
19
|
+
return startTag.attributes.some(attr => {
|
|
20
|
+
if (!attr.directive) {
|
|
21
|
+
// Static: has-actions (boolean) or has-actions="something"
|
|
22
|
+
// has-actions="false" is the string "false", still counts as explicitly false
|
|
23
|
+
if (attr.key.name !== 'has-actions') {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
return !attr.value || attr.value.value !== 'false';
|
|
27
|
+
}
|
|
28
|
+
// Dynamic: :has-actions="expr" or v-bind:has-actions="expr"
|
|
29
|
+
if (
|
|
30
|
+
attr.key.name &&
|
|
31
|
+
attr.key.name.name === 'bind' &&
|
|
32
|
+
attr.key.argument &&
|
|
33
|
+
attr.key.argument.name === 'has-actions'
|
|
34
|
+
) {
|
|
35
|
+
// :has-actions="false" → expression is a Literal with value false
|
|
36
|
+
const expr = attr.value && attr.value.expression;
|
|
37
|
+
if (expr && expr.type === 'Literal' && expr.value === false) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Removes an attribute node. If the attribute is alone on its line,
|
|
48
|
+
* removes the entire line (including leading whitespace and trailing newline)
|
|
49
|
+
* to avoid leaving a blank line.
|
|
50
|
+
*/
|
|
51
|
+
function removeAttribute(fixer, node, sourceCode) {
|
|
52
|
+
const fullText = sourceCode.getText();
|
|
53
|
+
const lineStart = fullText.lastIndexOf('\n', node.range[0] - 1) + 1;
|
|
54
|
+
const lineEnd = node.range[1];
|
|
55
|
+
|
|
56
|
+
// Check if the attribute is the only non-whitespace content on its line
|
|
57
|
+
const textBefore = fullText.slice(lineStart, node.range[0]);
|
|
58
|
+
const textAfter = fullText.slice(node.range[1], fullText.indexOf('\n', node.range[1]));
|
|
59
|
+
|
|
60
|
+
if (textBefore.trim() === '' && textAfter.trim() === '') {
|
|
61
|
+
// Remove from end of previous line to end of this line
|
|
62
|
+
const removeEnd = fullText.indexOf('\n', lineEnd);
|
|
63
|
+
return fixer.removeRange([lineStart - 1, removeEnd >= 0 ? removeEnd : lineEnd]);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return fixer.remove(node);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** @type {RuleModule} */
|
|
70
|
+
module.exports = {
|
|
71
|
+
meta: {
|
|
72
|
+
type: 'suggestion',
|
|
73
|
+
docs: {
|
|
74
|
+
description:
|
|
75
|
+
'Manages the temporary "has-actions" prop on bento-date-range-picker. Use action "add" to add it or "remove" to remove it.',
|
|
76
|
+
category: 'Deprecations',
|
|
77
|
+
url: 'https://github.com/Adyen/bento/blob/main/packages/eslint/rules/deprecation-bento-date-range-picker-has-actions-prop/README.md',
|
|
78
|
+
recommended: true,
|
|
79
|
+
},
|
|
80
|
+
messages: {
|
|
81
|
+
missingProp: `The temporary "has-actions" prop is missing on bento-date-range-picker. Add it to opt in to the new actions behavior.`,
|
|
82
|
+
removeProp: `The temporary "has-actions" prop should be removed from bento-date-range-picker.`,
|
|
83
|
+
},
|
|
84
|
+
fixable: 'code',
|
|
85
|
+
schema: [
|
|
86
|
+
{
|
|
87
|
+
type: 'object',
|
|
88
|
+
properties: {
|
|
89
|
+
action: {
|
|
90
|
+
type: 'string',
|
|
91
|
+
enum: ['add', 'remove'],
|
|
92
|
+
default: 'add',
|
|
93
|
+
},
|
|
94
|
+
autofix: {
|
|
95
|
+
type: 'boolean',
|
|
96
|
+
default: false,
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
additionalProperties: false,
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
/** @param {RuleContext} context */
|
|
105
|
+
create(context) {
|
|
106
|
+
const options = context.options[0] || {};
|
|
107
|
+
const action = options.action || 'add';
|
|
108
|
+
const autofix = options.autofix === true;
|
|
109
|
+
|
|
110
|
+
if (action === 'remove') {
|
|
111
|
+
/* ------------------------------------------------ */
|
|
112
|
+
/* REMOVE: report and optionally remove has-actions */
|
|
113
|
+
/* ------------------------------------------------ */
|
|
114
|
+
return VueUtils.defineTemplateBodyVisitor(context, {
|
|
115
|
+
[STATIC_ATTR](node) {
|
|
116
|
+
context.report({
|
|
117
|
+
node,
|
|
118
|
+
messageId: 'removeProp',
|
|
119
|
+
...(autofix && {
|
|
120
|
+
fix(fixer) {
|
|
121
|
+
return removeAttribute(fixer, node, context.sourceCode);
|
|
122
|
+
},
|
|
123
|
+
}),
|
|
124
|
+
});
|
|
125
|
+
},
|
|
126
|
+
[DYNAMIC_ATTR](node) {
|
|
127
|
+
context.report({
|
|
128
|
+
node,
|
|
129
|
+
messageId: 'removeProp',
|
|
130
|
+
...(autofix && {
|
|
131
|
+
fix(fixer) {
|
|
132
|
+
return removeAttribute(fixer, node, context.sourceCode);
|
|
133
|
+
},
|
|
134
|
+
}),
|
|
135
|
+
});
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* ------------------------------------------------ */
|
|
141
|
+
/* ADD: report components missing has-actions */
|
|
142
|
+
/* ------------------------------------------------ */
|
|
143
|
+
return VueUtils.defineTemplateBodyVisitor(context, {
|
|
144
|
+
[COMPONENT_SELECTOR](node) {
|
|
145
|
+
if (hasHasActionsTruthy(node.startTag)) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Find if has-actions exists but is set to false
|
|
150
|
+
const falseAttr = node.startTag.attributes.find(attr => {
|
|
151
|
+
if (!attr.directive) {
|
|
152
|
+
return attr.key.name === 'has-actions' && attr.value && attr.value.value === 'false';
|
|
153
|
+
}
|
|
154
|
+
if (
|
|
155
|
+
attr.key.name &&
|
|
156
|
+
attr.key.name.name === 'bind' &&
|
|
157
|
+
attr.key.argument &&
|
|
158
|
+
attr.key.argument.name === 'has-actions'
|
|
159
|
+
) {
|
|
160
|
+
const expr = attr.value && attr.value.expression;
|
|
161
|
+
return expr && expr.type === 'Literal' && expr.value === false;
|
|
162
|
+
}
|
|
163
|
+
return false;
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
context.report({
|
|
167
|
+
node: falseAttr || node.startTag,
|
|
168
|
+
messageId: 'missingProp',
|
|
169
|
+
...(autofix && {
|
|
170
|
+
fix(fixer) {
|
|
171
|
+
// If has-actions="false" or :has-actions="false", replace with boolean has-actions
|
|
172
|
+
if (falseAttr) {
|
|
173
|
+
return fixer.replaceText(falseAttr, 'has-actions');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Prop is completely missing – insert it
|
|
177
|
+
const sourceCode = context.sourceCode;
|
|
178
|
+
const fullText = sourceCode.getText();
|
|
179
|
+
const firstAttr = node.startTag.attributes[0];
|
|
180
|
+
if (firstAttr) {
|
|
181
|
+
const isMultiLine = firstAttr.loc.start.line !== node.startTag.loc.start.line;
|
|
182
|
+
if (isMultiLine) {
|
|
183
|
+
// Derive indentation from the actual whitespace before the first attribute
|
|
184
|
+
const lineStart = fullText.lastIndexOf('\n', firstAttr.range[0] - 1) + 1;
|
|
185
|
+
const indent = fullText.slice(lineStart, firstAttr.range[0]);
|
|
186
|
+
return fixer.insertTextBefore(firstAttr, `has-actions\n${indent}`);
|
|
187
|
+
}
|
|
188
|
+
return fixer.insertTextBefore(firstAttr, 'has-actions ');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// No attributes – insert before the closing > or />
|
|
192
|
+
const startTagText = sourceCode.getText(node.startTag);
|
|
193
|
+
const selfClosing = startTagText.endsWith('/>');
|
|
194
|
+
const insertPos = node.startTag.range[1] - (selfClosing ? 2 : 1);
|
|
195
|
+
// Only add a leading space if there isn't already whitespace before the insert position
|
|
196
|
+
const charBefore = fullText[insertPos - 1];
|
|
197
|
+
const leadingSpace = charBefore && /\s/.test(charBefore) ? '' : ' ';
|
|
198
|
+
return fixer.insertTextBeforeRange([insertPos, insertPos], `${leadingSpace}has-actions `);
|
|
199
|
+
},
|
|
200
|
+
}),
|
|
201
|
+
});
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
},
|
|
205
|
+
};
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { RuleTester } from 'eslint';
|
|
2
|
+
import vueParser from 'vue-eslint-parser';
|
|
3
|
+
import tsParser from '@typescript-eslint/parser';
|
|
4
|
+
import deprecationBentoDateRangePickerHasActionsProp from './deprecation-bento-date-range-picker-has-actions-prop';
|
|
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-date-range-picker-has-actions-prop', () => {
|
|
18
|
+
describe('action: add (default)', () => {
|
|
19
|
+
ruleTester.run(
|
|
20
|
+
'deprecation-bento-date-range-picker-has-actions-prop (add)',
|
|
21
|
+
deprecationBentoDateRangePickerHasActionsProp,
|
|
22
|
+
{
|
|
23
|
+
valid: [
|
|
24
|
+
{
|
|
25
|
+
name: 'Valid: component already has has-actions (static boolean)',
|
|
26
|
+
filename: 'test.vue',
|
|
27
|
+
code: `<template><bento-date-range-picker has-actions /></template>`,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'Valid: component already has has-actions (dynamic)',
|
|
31
|
+
filename: 'test.vue',
|
|
32
|
+
code: `<template><bento-date-range-picker :has-actions="true" /></template>`,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'Valid: unrelated component without has-actions',
|
|
36
|
+
filename: 'test.vue',
|
|
37
|
+
code: `<template><bento-button label="click" /></template>`,
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
invalid: [
|
|
41
|
+
{
|
|
42
|
+
name: 'Invalid: missing has-actions (no autofix)',
|
|
43
|
+
filename: 'test.vue',
|
|
44
|
+
code: `<template><bento-date-range-picker label="Pick" /></template>`,
|
|
45
|
+
output: null,
|
|
46
|
+
errors: [{ messageId: 'missingProp' }],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: 'Invalid: missing has-actions (autofix adds prop)',
|
|
50
|
+
filename: 'test.vue',
|
|
51
|
+
code: `<template><bento-date-range-picker label="Pick" /></template>`,
|
|
52
|
+
output: `<template><bento-date-range-picker has-actions label="Pick" /></template>`,
|
|
53
|
+
options: [{ action: 'add', autofix: true }],
|
|
54
|
+
errors: [{ messageId: 'missingProp' }],
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: 'Invalid: missing has-actions on self-closing tag without attrs (autofix)',
|
|
58
|
+
filename: 'test.vue',
|
|
59
|
+
code: `<template><bento-date-range-picker /></template>`,
|
|
60
|
+
output: `<template><bento-date-range-picker has-actions /></template>`,
|
|
61
|
+
options: [{ autofix: true }],
|
|
62
|
+
errors: [{ messageId: 'missingProp' }],
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'Invalid: missing has-actions on self-closing tag without space before /> (autofix)',
|
|
66
|
+
filename: 'test.vue',
|
|
67
|
+
code: `<template><bento-date-range-picker/></template>`,
|
|
68
|
+
output: `<template><bento-date-range-picker has-actions /></template>`,
|
|
69
|
+
options: [{ autofix: true }],
|
|
70
|
+
errors: [{ messageId: 'missingProp' }],
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: 'Invalid: has-actions="false" (static string false, no autofix)',
|
|
74
|
+
filename: 'test.vue',
|
|
75
|
+
code: `<template><bento-date-range-picker has-actions="false" /></template>`,
|
|
76
|
+
output: null,
|
|
77
|
+
errors: [{ messageId: 'missingProp' }],
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'Invalid: has-actions="false" (static string false, autofix replaces)',
|
|
81
|
+
filename: 'test.vue',
|
|
82
|
+
code: `<template><bento-date-range-picker has-actions="false" label="Pick" /></template>`,
|
|
83
|
+
output: `<template><bento-date-range-picker has-actions label="Pick" /></template>`,
|
|
84
|
+
options: [{ autofix: true }],
|
|
85
|
+
errors: [{ messageId: 'missingProp' }],
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: 'Invalid: :has-actions="false" (dynamic false, autofix replaces)',
|
|
89
|
+
filename: 'test.vue',
|
|
90
|
+
code: `<template><bento-date-range-picker :has-actions="false" label="Pick" /></template>`,
|
|
91
|
+
output: `<template><bento-date-range-picker has-actions label="Pick" /></template>`,
|
|
92
|
+
options: [{ autofix: true }],
|
|
93
|
+
errors: [{ messageId: 'missingProp' }],
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: 'Invalid: multi-line component missing has-actions (autofix preserves indentation)',
|
|
97
|
+
filename: 'test.vue',
|
|
98
|
+
code: `<template>
|
|
99
|
+
<bento-date-range-picker
|
|
100
|
+
label="Pick"
|
|
101
|
+
/>
|
|
102
|
+
</template>`,
|
|
103
|
+
output: `<template>
|
|
104
|
+
<bento-date-range-picker
|
|
105
|
+
has-actions
|
|
106
|
+
label="Pick"
|
|
107
|
+
/>
|
|
108
|
+
</template>`,
|
|
109
|
+
options: [{ autofix: true }],
|
|
110
|
+
errors: [{ messageId: 'missingProp' }],
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe('action: remove', () => {
|
|
118
|
+
ruleTester.run(
|
|
119
|
+
'deprecation-bento-date-range-picker-has-actions-prop (remove)',
|
|
120
|
+
deprecationBentoDateRangePickerHasActionsProp,
|
|
121
|
+
{
|
|
122
|
+
valid: [
|
|
123
|
+
{
|
|
124
|
+
name: 'Valid: component without has-actions',
|
|
125
|
+
filename: 'test.vue',
|
|
126
|
+
code: `<template><bento-date-range-picker label="Pick" /></template>`,
|
|
127
|
+
options: [{ action: 'remove' }],
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
invalid: [
|
|
131
|
+
{
|
|
132
|
+
name: 'Invalid: has-actions present (static boolean, no autofix)',
|
|
133
|
+
filename: 'test.vue',
|
|
134
|
+
code: `<template><bento-date-range-picker has-actions /></template>`,
|
|
135
|
+
output: null,
|
|
136
|
+
options: [{ action: 'remove' }],
|
|
137
|
+
errors: [{ messageId: 'removeProp' }],
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
name: 'Invalid: has-actions present (static boolean, autofix removes)',
|
|
141
|
+
filename: 'test.vue',
|
|
142
|
+
code: `<template><bento-date-range-picker has-actions label="Pick" /></template>`,
|
|
143
|
+
output: `<template><bento-date-range-picker label="Pick" /></template>`,
|
|
144
|
+
options: [{ action: 'remove', autofix: true }],
|
|
145
|
+
errors: [{ messageId: 'removeProp' }],
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: 'Invalid: has-actions present (dynamic, autofix removes)',
|
|
149
|
+
filename: 'test.vue',
|
|
150
|
+
code: `<template><bento-date-range-picker :has-actions="true" /></template>`,
|
|
151
|
+
output: `<template><bento-date-range-picker /></template>`,
|
|
152
|
+
options: [{ action: 'remove', autofix: true }],
|
|
153
|
+
errors: [{ messageId: 'removeProp' }],
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: 'Invalid: multi-line has-actions on its own line (autofix removes entire line)',
|
|
157
|
+
filename: 'test.vue',
|
|
158
|
+
code: `<template>
|
|
159
|
+
<bento-date-range-picker
|
|
160
|
+
has-actions
|
|
161
|
+
label="Pick"
|
|
162
|
+
/>
|
|
163
|
+
</template>`,
|
|
164
|
+
output: `<template>
|
|
165
|
+
<bento-date-range-picker
|
|
166
|
+
label="Pick"
|
|
167
|
+
/>
|
|
168
|
+
</template>`,
|
|
169
|
+
options: [{ action: 'remove', autofix: true }],
|
|
170
|
+
errors: [{ messageId: 'removeProp' }],
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
}
|
|
174
|
+
);
|
|
175
|
+
});
|
|
176
|
+
});
|