Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 94b6915

Browse filesBrowse files
authored
Merge branch 'master' into prefer-define-component
2 parents 58343cb + f8d0757 commit 94b6915
Copy full SHA for 94b6915
Expand file treeCollapse file tree

12 files changed

+828
-98
lines changed
+95Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/define-props-destructuring
5+
description: enforce consistent style for props destructuring
6+
---
7+
8+
# vue/define-props-destructuring
9+
10+
> enforce consistent style for props destructuring
11+
12+
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> _**This rule has not been released yet.**_ </badge>
13+
14+
## :book: Rule Details
15+
16+
This rule enforces a consistent style for handling Vue 3 Composition API props, allowing you to choose between requiring destructuring or prohibiting it.
17+
18+
By default, the rule requires you to use destructuring syntax when using `defineProps` instead of storing props in a variable and warns against combining `withDefaults` with destructuring.
19+
20+
<eslint-code-block :rules="{'vue/define-props-destructuring': ['error']}">
21+
22+
```vue
23+
<script setup>
24+
// ✓ GOOD
25+
const { foo } = defineProps(['foo'])
26+
const { bar = 'default' } = defineProps(['bar'])
27+
28+
// ✗ BAD
29+
const props = defineProps(['foo'])
30+
const propsWithDefaults = withDefaults(defineProps(['foo']), { foo: 'default' })
31+
32+
// ✗ BAD
33+
const { baz } = withDefaults(defineProps(['baz']), { baz: 'default' })
34+
</script>
35+
```
36+
37+
</eslint-code-block>
38+
39+
The rule applies to both JavaScript and TypeScript props:
40+
41+
<eslint-code-block :rules="{'vue/define-props-destructuring': ['error']}">
42+
43+
```vue
44+
<script setup lang="ts">
45+
// ✓ GOOD
46+
const { foo } = defineProps<{ foo?: string }>()
47+
const { bar = 'default' } = defineProps<{ bar?: string }>()
48+
49+
// ✗ BAD
50+
const props = defineProps<{ foo?: string }>()
51+
const propsWithDefaults = withDefaults(defineProps<{ foo?: string }>(), { foo: 'default' })
52+
</script>
53+
```
54+
55+
</eslint-code-block>
56+
57+
## :wrench: Options
58+
59+
```js
60+
{
61+
"vue/define-props-destructuring": ["error", {
62+
"destructure": "always" | "never"
63+
}]
64+
}
65+
```
66+
67+
- `destructure` - Sets the destructuring preference for props
68+
- `"always"` (default) - Requires destructuring when using `defineProps` and warns against using `withDefaults` with destructuring
69+
- `"never"` - Requires using a variable to store props and prohibits destructuring
70+
71+
### `"destructure": "never"`
72+
73+
<eslint-code-block :rules="{'vue/define-props-destructuring': ['error', { destructure: 'never' }]}">
74+
75+
```vue
76+
<script setup>
77+
// ✓ GOOD
78+
const props = defineProps(['foo'])
79+
const propsWithDefaults = withDefaults(defineProps(['foo']), { foo: 'default' })
80+
81+
// ✗ BAD
82+
const { foo } = defineProps(['foo'])
83+
</script>
84+
```
85+
86+
</eslint-code-block>
87+
88+
## :books: Further Reading
89+
90+
- [Reactive Props Destructure](https://vuejs.org/guide/components/props.html#reactive-props-destructure)
91+
92+
## :mag: Implementation
93+
94+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/define-props-destructuring.js)
95+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/define-props-destructuring.js)

‎docs/rules/index.md

Copy file name to clipboardExpand all lines: docs/rules/index.md
+85-83Lines changed: 85 additions & 83 deletions
Large diffs are not rendered by default.

‎docs/rules/no-bare-strings-in-template.md

Copy file name to clipboardExpand all lines: docs/rules/no-bare-strings-in-template.md
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ since: v7.0.0
1212
1313
## :book: Rule Details
1414

15-
This rule disallows the use of bare strings in `<template>`.
15+
This rule disallows the use of bare strings in `<template>`.
1616
In order to be able to internationalize your application, you will need to avoid using plain strings in your templates. Instead, you would need to use a template helper specializing in translation.
1717

1818
This rule was inspired by [no-bare-strings rule in ember-template-lint](https://github.com/ember-template-lint/ember-template-lint/blob/master/docs/rule/no-bare-strings.md).
@@ -50,7 +50,7 @@ This rule was inspired by [no-bare-strings rule in ember-template-lint](https://
5050
</eslint-code-block>
5151

5252
:::tip
53-
This rule does not check for string literals, in bindings and mustaches interpolation. This is because it looks like a conscious decision.
53+
This rule does not check for string literals, in bindings and mustaches interpolation. This is because it looks like a conscious decision.
5454
If you want to report these string literals, enable the [vue/no-useless-v-bind] and [vue/no-useless-mustaches] rules and fix the useless string literals.
5555
:::
5656

@@ -72,7 +72,7 @@ If you want to report these string literals, enable the [vue/no-useless-v-bind]
7272
}
7373
```
7474

75-
- `allowlist` ... An array of allowed strings.
75+
- `allowlist` ... An array of allowed strings or regular expression patterns (e.g. `/\d+/` to allow numbers).
7676
- `attributes` ... An object whose keys are tag name or patterns and value is an array of attributes to check for that tag name.
7777
- `directives` ... An array of directive names to check literal value.
7878

‎docs/rules/no-multiple-template-root.md

Copy file name to clipboardExpand all lines: docs/rules/no-multiple-template-root.md
+26-1Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,32 @@ This rule checks whether template contains single root element valid for Vue 2.
6161

6262
## :wrench: Options
6363

64-
Nothing.
64+
```json
65+
{
66+
"vue/no-multiple-template-root": ["error", {
67+
"disallowComments": false
68+
}]
69+
}
70+
```
71+
72+
- "disallowComments" (`boolean`) Enables there should not be any comments in the template root. Default is `false`.
73+
74+
### "disallowComments": true
75+
76+
<eslint-code-block :rules="{'vue/no-multiple-template-root': ['error', {disallowComments: true}]}">
77+
78+
```vue
79+
/* ✗ BAD */
80+
<template>
81+
<!-- root comment -->
82+
<div>
83+
vue eslint plugin
84+
</div>
85+
<!-- root comment -->
86+
</template>
87+
```
88+
89+
</eslint-code-block>
6590

6691
## :rocket: Version
6792

‎lib/index.js

Copy file name to clipboardExpand all lines: lib/index.js
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ const plugin = {
5858
'define-emits-declaration': require('./rules/define-emits-declaration'),
5959
'define-macros-order': require('./rules/define-macros-order'),
6060
'define-props-declaration': require('./rules/define-props-declaration'),
61+
'define-props-destructuring': require('./rules/define-props-destructuring'),
6162
'dot-location': require('./rules/dot-location'),
6263
'dot-notation': require('./rules/dot-notation'),
6364
'enforce-style-attribute': require('./rules/enforce-style-attribute'),
+79Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/**
2+
* @author Wayne Zhang
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
const utils = require('../utils')
8+
9+
module.exports = {
10+
meta: {
11+
type: 'suggestion',
12+
docs: {
13+
description: 'enforce consistent style for props destructuring',
14+
categories: undefined,
15+
url: 'https://eslint.vuejs.org/rules/define-props-destructuring.html'
16+
},
17+
fixable: null,
18+
schema: [
19+
{
20+
type: 'object',
21+
properties: {
22+
destructure: {
23+
enum: ['always', 'never']
24+
}
25+
},
26+
additionalProperties: false
27+
}
28+
],
29+
messages: {
30+
preferDestructuring: 'Prefer destructuring from `defineProps` directly.',
31+
avoidDestructuring: 'Avoid destructuring from `defineProps`.',
32+
avoidWithDefaults: 'Avoid using `withDefaults` with destructuring.'
33+
}
34+
},
35+
/** @param {RuleContext} context */
36+
create(context) {
37+
const options = context.options[0] || {}
38+
const destructurePreference = options.destructure || 'always'
39+
40+
return utils.compositingVisitors(
41+
utils.defineScriptSetupVisitor(context, {
42+
onDefinePropsEnter(node, props) {
43+
const hasNoArgs = props.filter((prop) => prop.propName).length === 0
44+
if (hasNoArgs) {
45+
return
46+
}
47+
48+
const hasDestructure = utils.isUsingPropsDestructure(node)
49+
const hasWithDefaults = utils.hasWithDefaults(node)
50+
51+
if (destructurePreference === 'never') {
52+
if (hasDestructure) {
53+
context.report({
54+
node,
55+
messageId: 'avoidDestructuring'
56+
})
57+
}
58+
return
59+
}
60+
61+
if (!hasDestructure) {
62+
context.report({
63+
node,
64+
messageId: 'preferDestructuring'
65+
})
66+
return
67+
}
68+
69+
if (hasWithDefaults) {
70+
context.report({
71+
node: node.parent.callee,
72+
messageId: 'avoidWithDefaults'
73+
})
74+
}
75+
}
76+
})
77+
)
78+
}
79+
}

‎lib/rules/no-bare-strings-in-template.js

Copy file name to clipboardExpand all lines: lib/rules/no-bare-strings-in-template.js
+39-9Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -149,17 +149,33 @@ module.exports = {
149149
*/
150150
const opts = context.options[0] || {}
151151
/** @type {string[]} */
152-
const allowlist = opts.allowlist || DEFAULT_ALLOWLIST
152+
const rawAllowlist = opts.allowlist || DEFAULT_ALLOWLIST
153153
const attributes = parseTargetAttrs(opts.attributes || DEFAULT_ATTRIBUTES)
154154
const directives = opts.directives || DEFAULT_DIRECTIVES
155155

156-
const allowlistRe = new RegExp(
157-
allowlist
158-
.map((w) => regexp.escape(w))
159-
.sort((a, b) => b.length - a.length)
160-
.join('|'),
161-
'gu'
162-
)
156+
/** @type {string[]} */
157+
const stringAllowlist = []
158+
/** @type {RegExp[]} */
159+
const regexAllowlist = []
160+
161+
for (const item of rawAllowlist) {
162+
if (regexp.isRegExp(item)) {
163+
regexAllowlist.push(regexp.toRegExp(item))
164+
} else {
165+
stringAllowlist.push(item)
166+
}
167+
}
168+
169+
const allowlistRe =
170+
stringAllowlist.length > 0
171+
? new RegExp(
172+
stringAllowlist
173+
.map((w) => regexp.escape(w))
174+
.sort((a, b) => b.length - a.length)
175+
.join('|'),
176+
'gu'
177+
)
178+
: null
163179

164180
/** @type {ElementStack | null} */
165181
let elementStack = null
@@ -168,7 +184,21 @@ module.exports = {
168184
* @param {string} str
169185
*/
170186
function getBareString(str) {
171-
return str.trim().replace(allowlistRe, '').trim()
187+
let result = str.trim()
188+
189+
if (allowlistRe) {
190+
result = result.replace(allowlistRe, '')
191+
}
192+
193+
for (const regex of regexAllowlist) {
194+
const flags = regex.flags.includes('g')
195+
? regex.flags
196+
: `${regex.flags}g`
197+
const globalRegex = new RegExp(regex.source, flags)
198+
result = result.replace(globalRegex, '')
199+
}
200+
201+
return result.trim()
172202
}
173203

174204
/**

‎lib/rules/no-multiple-template-root.js

Copy file name to clipboardExpand all lines: lib/rules/no-multiple-template-root.js
+41-1Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,21 @@
66

77
const utils = require('../utils')
88

9+
/**
10+
* Get all comments that need to be reported
11+
* @param {(HTMLComment | HTMLBogusComment | Comment)[]} comments
12+
* @param {Range[]} elementRanges
13+
* @returns {(HTMLComment | HTMLBogusComment | Comment)[]}
14+
*/
15+
function getReportComments(comments, elementRanges) {
16+
return comments.filter(
17+
(comment) =>
18+
!elementRanges.some(
19+
(range) => range[0] <= comment.range[0] && comment.range[1] <= range[1]
20+
)
21+
)
22+
}
23+
924
module.exports = {
1025
meta: {
1126
type: 'problem',
@@ -15,8 +30,19 @@ module.exports = {
1530
url: 'https://eslint.vuejs.org/rules/no-multiple-template-root.html'
1631
},
1732
fixable: null,
18-
schema: [],
33+
schema: [
34+
{
35+
type: 'object',
36+
properties: {
37+
disallowComments: {
38+
type: 'boolean'
39+
}
40+
},
41+
additionalProperties: false
42+
}
43+
],
1944
messages: {
45+
commentRoot: 'The template root disallows comments.',
2046
multipleRoot: 'The template root requires exactly one element.',
2147
textRoot: 'The template root requires an element rather than texts.',
2248
disallowedElement: "The template root disallows '<{{name}}>' elements.",
@@ -28,6 +54,8 @@ module.exports = {
2854
* @returns {RuleListener} AST event handlers.
2955
*/
3056
create(context) {
57+
const options = context.options[0] || {}
58+
const disallowComments = options.disallowComments
3159
const sourceCode = context.getSourceCode()
3260

3361
return {
@@ -37,6 +65,18 @@ module.exports = {
3765
return
3866
}
3967

68+
const comments = element.comments
69+
const elementRanges = element.children.map((child) => child.range)
70+
if (disallowComments && comments.length > 0) {
71+
for (const comment of getReportComments(comments, elementRanges)) {
72+
context.report({
73+
node: comment,
74+
loc: comment.loc,
75+
messageId: 'commentRoot'
76+
})
77+
}
78+
}
79+
4080
const rootElements = []
4181
let extraText = null
4282
let extraElement = null

‎package.json

Copy file name to clipboardExpand all lines: package.json
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "eslint-plugin-vue",
3-
"version": "10.0.0",
3+
"version": "10.0.1",
44
"description": "Official ESLint plugin for Vue.js",
55
"main": "lib/index.js",
66
"types": "lib/index.d.ts",

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.