Skip to content

Commit cac3beb

Browse files
authored
Add vue/require-expose rule (#1568)
1 parent 88d8d1b commit cac3beb

File tree

8 files changed

+916
-52
lines changed

8 files changed

+916
-52
lines changed

Diff for: docs/rules/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ For example:
325325
| [vue/padding-line-between-blocks](./padding-line-between-blocks.md) | require or disallow padding lines between blocks | :wrench: |
326326
| [vue/require-direct-export](./require-direct-export.md) | require the component to be directly exported | |
327327
| [vue/require-emit-validator](./require-emit-validator.md) | require type definitions in emits | |
328+
| [vue/require-expose](./require-expose.md) | require declare public properties using `expose` | |
328329
| [vue/require-name-property](./require-name-property.md) | require a name property in Vue components | |
329330
| [vue/script-indent](./script-indent.md) | enforce consistent indentation in `<script>` | :wrench: |
330331
| [vue/sort-keys](./sort-keys.md) | enforce sort-keys in a manner that is compatible with order-in-components | |

Diff for: docs/rules/require-expose.md

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/require-expose
5+
description: require declare public properties using `expose`
6+
---
7+
# vue/require-expose
8+
9+
> require declare public properties using `expose`
10+
11+
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
12+
13+
## :book: Rule Details
14+
15+
This rule enforces the component to explicitly declare the exposed properties to the component using `expose`. You can use `expose` to control the internal properties of a component so that they cannot be referenced externally.
16+
17+
The `expose` API was officially introduced in Vue 3.2.
18+
19+
<eslint-code-block :rules="{'vue/require-expose': ['error']}">
20+
21+
```vue
22+
<script>
23+
/* ✓ GOOD */
24+
export default {
25+
expose: ['increment'],
26+
data() {
27+
return { count: 0 }
28+
},
29+
methods: {
30+
increment() {
31+
this.count++
32+
}
33+
}
34+
}
35+
</script>
36+
```
37+
38+
</eslint-code-block>
39+
40+
<eslint-code-block :rules="{'vue/require-expose': ['error']}">
41+
42+
```vue
43+
<script>
44+
/* ✗ BAD */
45+
export default {
46+
data() {
47+
return { count: 0 }
48+
},
49+
methods: {
50+
increment() {
51+
this.count++
52+
}
53+
}
54+
}
55+
</script>
56+
```
57+
58+
</eslint-code-block>
59+
60+
<eslint-code-block :rules="{'vue/require-expose': ['error']}">
61+
62+
```vue
63+
<script>
64+
/* ✓ GOOD */
65+
import { ref } from 'vue'
66+
67+
export default {
68+
setup(props, { expose }) {
69+
const count = ref(0)
70+
71+
function increment() {
72+
count.value++
73+
}
74+
// public
75+
expose({
76+
increment
77+
})
78+
// private
79+
return { count }
80+
}
81+
}
82+
</script>
83+
```
84+
85+
</eslint-code-block>
86+
87+
<eslint-code-block :rules="{'vue/require-expose': ['error']}">
88+
89+
```vue
90+
<script>
91+
/* ✗ BAD */
92+
import { ref } from 'vue'
93+
94+
export default {
95+
setup(props) {
96+
const count = ref(0)
97+
98+
function increment() {
99+
count.value++
100+
}
101+
return { increment, count }
102+
}
103+
}
104+
</script>
105+
```
106+
107+
</eslint-code-block>
108+
109+
## :wrench: Options
110+
111+
Nothing.
112+
113+
## :books: Further Reading
114+
115+
- [Vue RFCs - 0042-expose-api](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0042-expose-api.md)
116+
117+
## :mag: Implementation
118+
119+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/require-expose.js)
120+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/require-expose.js)

Diff for: lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ module.exports = {
145145
'require-direct-export': require('./rules/require-direct-export'),
146146
'require-emit-validator': require('./rules/require-emit-validator'),
147147
'require-explicit-emits': require('./rules/require-explicit-emits'),
148+
'require-expose': require('./rules/require-expose'),
148149
'require-name-property': require('./rules/require-name-property'),
149150
'require-prop-type-constructor': require('./rules/require-prop-type-constructor'),
150151
'require-prop-types': require('./rules/require-prop-types'),

Diff for: lib/rules/require-explicit-emits.js

+12-35
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@
1818
// Requirements
1919
// ------------------------------------------------------------------------------
2020

21-
const { findVariable } = require('eslint-utils')
21+
const {
22+
findVariable,
23+
isOpeningBraceToken,
24+
isClosingBraceToken,
25+
isOpeningBracketToken
26+
} = require('eslint-utils')
2227
const utils = require('../utils')
2328
const { capitalize } = require('../utils/casing')
2429

@@ -53,34 +58,6 @@ const FIX_EMITS_AFTER_OPTIONS = [
5358
'renderTriggered',
5459
'errorCaptured'
5560
]
56-
57-
/**
58-
* Check whether the given token is a left brace.
59-
* @param {Token} token The token to check.
60-
* @returns {boolean} `true` if the token is a left brace.
61-
*/
62-
function isLeftBrace(token) {
63-
return token != null && token.type === 'Punctuator' && token.value === '{'
64-
}
65-
66-
/**
67-
* Check whether the given token is a right brace.
68-
* @param {Token} token The token to check.
69-
* @returns {boolean} `true` if the token is a right brace.
70-
*/
71-
function isRightBrace(token) {
72-
return token != null && token.type === 'Punctuator' && token.value === '}'
73-
}
74-
75-
/**
76-
* Check whether the given token is a left bracket.
77-
* @param {Token} token The token to check.
78-
* @returns {boolean} `true` if the token is a left bracket.
79-
*/
80-
function isLeftBracket(token) {
81-
return token != null && token.type === 'Punctuator' && token.value === '['
82-
}
83-
8461
// ------------------------------------------------------------------------------
8562
// Rule Definition
8663
// ------------------------------------------------------------------------------
@@ -486,7 +463,7 @@ function buildSuggest(define, emits, nameNode, context) {
486463
const emitsOptionValue = emitsOption.value
487464
if (emitsOptionValue.type === 'ArrayExpression') {
488465
const leftBracket = /** @type {Token} */ (
489-
sourceCode.getFirstToken(emitsOptionValue, isLeftBracket)
466+
sourceCode.getFirstToken(emitsOptionValue, isOpeningBracketToken)
490467
)
491468
return [
492469
{
@@ -504,7 +481,7 @@ function buildSuggest(define, emits, nameNode, context) {
504481
]
505482
} else if (emitsOptionValue.type === 'ObjectExpression') {
506483
const leftBrace = /** @type {Token} */ (
507-
sourceCode.getFirstToken(emitsOptionValue, isLeftBrace)
484+
sourceCode.getFirstToken(emitsOptionValue, isOpeningBraceToken)
508485
)
509486
return [
510487
{
@@ -548,10 +525,10 @@ function buildSuggest(define, emits, nameNode, context) {
548525
)
549526
} else {
550527
const objectLeftBrace = /** @type {Token} */ (
551-
sourceCode.getFirstToken(object, isLeftBrace)
528+
sourceCode.getFirstToken(object, isOpeningBraceToken)
552529
)
553530
const objectRightBrace = /** @type {Token} */ (
554-
sourceCode.getLastToken(object, isRightBrace)
531+
sourceCode.getLastToken(object, isClosingBraceToken)
555532
)
556533
return fixer.insertTextAfter(
557534
objectLeftBrace,
@@ -583,10 +560,10 @@ function buildSuggest(define, emits, nameNode, context) {
583560
)
584561
} else {
585562
const objectLeftBrace = /** @type {Token} */ (
586-
sourceCode.getFirstToken(object, isLeftBrace)
563+
sourceCode.getFirstToken(object, isOpeningBraceToken)
587564
)
588565
const objectRightBrace = /** @type {Token} */ (
589-
sourceCode.getLastToken(object, isRightBrace)
566+
sourceCode.getLastToken(object, isClosingBraceToken)
590567
)
591568
return fixer.insertTextAfter(
592569
objectLeftBrace,

0 commit comments

Comments
 (0)