Skip to content

Commit 57058ce

Browse files
rzzfwaynzhFloEdelmann
authored
feat(define-props-destructuring): add only-when-assigned option (#3009)
Co-authored-by: waynzh <waynzh19@gmail.com> Co-authored-by: Flo Edelmann <git@flo-edelmann.de>
1 parent 72fdd6e commit 57058ce

File tree

5 files changed

+177
-5
lines changed

5 files changed

+177
-5
lines changed

.changeset/shy-ends-help.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'eslint-plugin-vue': minor
3+
---
4+
5+
Added new `destructure: "only-when-assigned"` option to `vue/define-props-destructuring` and changed default value from `destructure: "always"` to `destructure: "only-when-assigned"`

docs/rules/define-props-destructuring.md

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@ By default, the rule requires you to use destructuring syntax when using `define
2323
// ✓ GOOD
2424
const { foo } = defineProps(['foo'])
2525
const { bar = 'default' } = defineProps(['bar'])
26+
defineProps(['bar'])
2627
2728
// ✗ BAD
2829
const props = defineProps(['foo'])
2930
const propsWithDefaults = withDefaults(defineProps(['foo']), { foo: 'default' })
31+
withDefaults(defineProps(['foo']), { foo: 'default' })
3032
3133
// ✗ BAD
3234
const { baz } = withDefaults(defineProps(['baz']), { baz: 'default' })
@@ -44,10 +46,12 @@ The rule applies to both JavaScript and TypeScript props:
4446
// ✓ GOOD
4547
const { foo } = defineProps<{ foo?: string }>()
4648
const { bar = 'default' } = defineProps<{ bar?: string }>()
49+
defineProps<{ bar?: string }>()
4750
4851
// ✗ BAD
4952
const props = defineProps<{ foo?: string }>()
5053
const propsWithDefaults = withDefaults(defineProps<{ foo?: string }>(), { foo: 'default' })
54+
withDefaults(defineProps<{ foo?: string }>(), { foo: 'default' })
5155
</script>
5256
```
5357

@@ -58,15 +62,39 @@ The rule applies to both JavaScript and TypeScript props:
5862
```js
5963
{
6064
"vue/define-props-destructuring": ["error", {
61-
"destructure": "always" | "never"
65+
"destructure": "only-when-assigned" | "always" | "never"
6266
}]
6367
}
6468
```
6569

6670
- `destructure` - Sets the destructuring preference for props
67-
- `"always"` (default) - Requires destructuring when using `defineProps` and warns against using `withDefaults` with destructuring
71+
- `"only-when-assigned"` (default) - Requires destructuring when `defineProps` is assigned to a variable, and warns against using `withDefaults` with destructuring
72+
- `"always"` - Requires destructuring when using `defineProps` and warns against using `withDefaults` with destructuring
6873
- `"never"` - Requires using a variable to store props and prohibits destructuring
6974

75+
### `"destructure": "always"`
76+
77+
<eslint-code-block :rules="{'vue/define-props-destructuring': ['error', { destructure: 'always' }]}">
78+
79+
```vue
80+
<script setup>
81+
// ✓ GOOD
82+
const { foo } = defineProps(['foo'])
83+
const { bar = 'default' } = defineProps(['bar'])
84+
85+
// ✗ BAD
86+
const props = defineProps(['foo'])
87+
const propsWithDefaults = withDefaults(defineProps(['foo']), { foo: 'default' })
88+
defineProps(['bar'])
89+
withDefaults(defineProps(['foo']), { foo: 'default' })
90+
91+
// ✗ BAD
92+
const { baz } = withDefaults(defineProps(['baz']), { baz: 'default' })
93+
</script>
94+
```
95+
96+
</eslint-code-block>
97+
7098
### `"destructure": "never"`
7199

72100
<eslint-code-block :rules="{'vue/define-props-destructuring': ['error', { destructure: 'never' }]}">

lib/rules/define-props-destructuring.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ module.exports = {
2020
type: 'object',
2121
properties: {
2222
destructure: {
23-
enum: ['always', 'never']
23+
enum: ['only-when-assigned', 'always', 'never']
2424
}
2525
},
2626
additionalProperties: false
@@ -35,7 +35,7 @@ module.exports = {
3535
/** @param {RuleContext} context */
3636
create(context) {
3737
const options = context.options[0] || {}
38-
const destructurePreference = options.destructure || 'always'
38+
const destructurePreference = options.destructure || 'only-when-assigned'
3939

4040
return utils.compositingVisitors(
4141
utils.defineScriptSetupVisitor(context, {
@@ -49,6 +49,7 @@ module.exports = {
4949

5050
const hasDestructure = utils.isUsingPropsDestructure(node)
5151
const hasWithDefaults = utils.hasWithDefaults(node)
52+
const hasAssigned = !!utils.getLeftOfDefineProps(node)
5253

5354
if (destructurePreference === 'never') {
5455
if (hasDestructure) {
@@ -60,7 +61,10 @@ module.exports = {
6061
return
6162
}
6263

63-
if (!hasDestructure) {
64+
if (
65+
!hasDestructure &&
66+
(destructurePreference === 'always' || hasAssigned)
67+
) {
6468
context.report({
6569
node,
6670
messageId: 'preferDestructuring'

lib/utils/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1557,6 +1557,7 @@ module.exports = {
15571557
* @returns { Record<string, {prop: AssignmentProperty , expression: Expression} | undefined> }
15581558
*/
15591559
getDefaultPropExpressionsForPropsDestructure,
1560+
getLeftOfDefineProps,
15601561
/**
15611562
* Checks whether the given defineProps node is using Props Destructure.
15621563
* @param {CallExpression} node The node of defineProps

tests/lib/rules/define-props-destructuring.js

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,25 @@ tester.run('define-props-destructuring', rule, {
123123
languageOptions: {
124124
parserOptions: { parser: require.resolve('@typescript-eslint/parser') }
125125
}
126+
},
127+
{
128+
filename: 'test.vue',
129+
code: `
130+
<script setup>
131+
defineProps(['foo'])
132+
</script>
133+
`
134+
},
135+
{
136+
filename: 'test.vue',
137+
code: `
138+
<script setup lang="ts">
139+
defineProps<{ foo?: string }>()
140+
</script>
141+
`,
142+
languageOptions: {
143+
parserOptions: { parser: require.resolve('@typescript-eslint/parser') }
144+
}
126145
}
127146
],
128147
invalid: [
@@ -359,6 +378,121 @@ tester.run('define-props-destructuring', rule, {
359378
endColumn: 44
360379
}
361380
]
381+
},
382+
{
383+
filename: 'test.vue',
384+
code: `
385+
<script setup lang="ts">
386+
withDefaults(defineProps(['foo']), { foo: 'default' })
387+
</script>
388+
`,
389+
errors: [
390+
{
391+
messageId: 'avoidWithDefaults',
392+
line: 3,
393+
column: 7,
394+
endLine: 3,
395+
endColumn: 19
396+
}
397+
]
398+
},
399+
{
400+
filename: 'test.vue',
401+
code: `
402+
<script setup lang="ts">
403+
withDefaults(defineProps<{ foo?: string }>(), { foo: 'default' })
404+
</script>
405+
`,
406+
languageOptions: {
407+
parserOptions: { parser: require.resolve('@typescript-eslint/parser') }
408+
},
409+
errors: [
410+
{
411+
messageId: 'avoidWithDefaults',
412+
line: 3,
413+
column: 7,
414+
endLine: 3,
415+
endColumn: 19
416+
}
417+
]
418+
},
419+
{
420+
filename: 'test.vue',
421+
code: `
422+
<script setup lang="ts">
423+
withDefaults(defineProps(['foo']), { foo: 'default' })
424+
</script>
425+
`,
426+
options: [{ destructure: 'always' }],
427+
errors: [
428+
{
429+
messageId: 'preferDestructuring',
430+
line: 3,
431+
column: 20,
432+
endLine: 3,
433+
endColumn: 40
434+
}
435+
]
436+
},
437+
{
438+
filename: 'test.vue',
439+
code: `
440+
<script setup lang="ts">
441+
withDefaults(defineProps<{ foo?: string }>(), { foo: 'default' })
442+
</script>
443+
`,
444+
options: [{ destructure: 'always' }],
445+
languageOptions: {
446+
parserOptions: { parser: require.resolve('@typescript-eslint/parser') }
447+
},
448+
errors: [
449+
{
450+
messageId: 'preferDestructuring',
451+
line: 3,
452+
column: 20,
453+
endLine: 3,
454+
endColumn: 51
455+
}
456+
]
457+
},
458+
{
459+
filename: 'test.vue',
460+
code: `
461+
<script setup lang="ts">
462+
defineProps(['foo'])
463+
</script>
464+
`,
465+
options: [{ destructure: 'always' }],
466+
errors: [
467+
{
468+
messageId: 'preferDestructuring',
469+
line: 3,
470+
column: 7,
471+
endLine: 3,
472+
endColumn: 27
473+
}
474+
]
475+
},
476+
{
477+
filename: 'test.vue',
478+
code: `
479+
<script setup lang="ts">
480+
defineProps<{ foo?: string }>()
481+
</script>
482+
`,
483+
options: [{ destructure: 'always' }],
484+
languageOptions: {
485+
parserOptions: { parser: require.resolve('@typescript-eslint/parser') }
486+
},
487+
errors: [
488+
{
489+
messageId: 'preferDestructuring',
490+
line: 3,
491+
column: 7,
492+
endLine: 3,
493+
endColumn: 38
494+
}
495+
]
362496
}
363497
]
364498
})

0 commit comments

Comments
 (0)