Skip to content

Commit

Permalink
Add vue/require-expose rule (#1568)
Browse files Browse the repository at this point in the history
  • Loading branch information
ota-meshi committed Jul 17, 2021
1 parent 88d8d1b commit cac3beb
Show file tree
Hide file tree
Showing 8 changed files with 916 additions and 52 deletions.
1 change: 1 addition & 0 deletions docs/rules/README.md
Expand Up @@ -325,6 +325,7 @@ For example:
| [vue/padding-line-between-blocks](./padding-line-between-blocks.md) | require or disallow padding lines between blocks | :wrench: |
| [vue/require-direct-export](./require-direct-export.md) | require the component to be directly exported | |
| [vue/require-emit-validator](./require-emit-validator.md) | require type definitions in emits | |
| [vue/require-expose](./require-expose.md) | require declare public properties using `expose` | |
| [vue/require-name-property](./require-name-property.md) | require a name property in Vue components | |
| [vue/script-indent](./script-indent.md) | enforce consistent indentation in `<script>` | :wrench: |
| [vue/sort-keys](./sort-keys.md) | enforce sort-keys in a manner that is compatible with order-in-components | |
Expand Down
120 changes: 120 additions & 0 deletions docs/rules/require-expose.md
@@ -0,0 +1,120 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/require-expose
description: require declare public properties using `expose`
---
# vue/require-expose

> require declare public properties using `expose`
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>

## :book: Rule Details

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.

The `expose` API was officially introduced in Vue 3.2.

<eslint-code-block :rules="{'vue/require-expose': ['error']}">

```vue
<script>
/* ✓ GOOD */
export default {
expose: ['increment'],
data() {
return { count: 0 }
},
methods: {
increment() {
this.count++
}
}
}
</script>
```

</eslint-code-block>

<eslint-code-block :rules="{'vue/require-expose': ['error']}">

```vue
<script>
/* ✗ BAD */
export default {
data() {
return { count: 0 }
},
methods: {
increment() {
this.count++
}
}
}
</script>
```

</eslint-code-block>

<eslint-code-block :rules="{'vue/require-expose': ['error']}">

```vue
<script>
/* ✓ GOOD */
import { ref } from 'vue'
export default {
setup(props, { expose }) {
const count = ref(0)
function increment() {
count.value++
}
// public
expose({
increment
})
// private
return { count }
}
}
</script>
```

</eslint-code-block>

<eslint-code-block :rules="{'vue/require-expose': ['error']}">

```vue
<script>
/* ✗ BAD */
import { ref } from 'vue'
export default {
setup(props) {
const count = ref(0)
function increment() {
count.value++
}
return { increment, count }
}
}
</script>
```

</eslint-code-block>

## :wrench: Options

Nothing.

## :books: Further Reading

- [Vue RFCs - 0042-expose-api](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0042-expose-api.md)

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/require-expose.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/require-expose.js)
1 change: 1 addition & 0 deletions lib/index.js
Expand Up @@ -145,6 +145,7 @@ module.exports = {
'require-direct-export': require('./rules/require-direct-export'),
'require-emit-validator': require('./rules/require-emit-validator'),
'require-explicit-emits': require('./rules/require-explicit-emits'),
'require-expose': require('./rules/require-expose'),
'require-name-property': require('./rules/require-name-property'),
'require-prop-type-constructor': require('./rules/require-prop-type-constructor'),
'require-prop-types': require('./rules/require-prop-types'),
Expand Down
47 changes: 12 additions & 35 deletions lib/rules/require-explicit-emits.js
Expand Up @@ -18,7 +18,12 @@
// Requirements
// ------------------------------------------------------------------------------

const { findVariable } = require('eslint-utils')
const {
findVariable,
isOpeningBraceToken,
isClosingBraceToken,
isOpeningBracketToken
} = require('eslint-utils')
const utils = require('../utils')
const { capitalize } = require('../utils/casing')

Expand Down Expand Up @@ -53,34 +58,6 @@ const FIX_EMITS_AFTER_OPTIONS = [
'renderTriggered',
'errorCaptured'
]

/**
* Check whether the given token is a left brace.
* @param {Token} token The token to check.
* @returns {boolean} `true` if the token is a left brace.
*/
function isLeftBrace(token) {
return token != null && token.type === 'Punctuator' && token.value === '{'
}

/**
* Check whether the given token is a right brace.
* @param {Token} token The token to check.
* @returns {boolean} `true` if the token is a right brace.
*/
function isRightBrace(token) {
return token != null && token.type === 'Punctuator' && token.value === '}'
}

/**
* Check whether the given token is a left bracket.
* @param {Token} token The token to check.
* @returns {boolean} `true` if the token is a left bracket.
*/
function isLeftBracket(token) {
return token != null && token.type === 'Punctuator' && token.value === '['
}

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
Expand Down Expand Up @@ -486,7 +463,7 @@ function buildSuggest(define, emits, nameNode, context) {
const emitsOptionValue = emitsOption.value
if (emitsOptionValue.type === 'ArrayExpression') {
const leftBracket = /** @type {Token} */ (
sourceCode.getFirstToken(emitsOptionValue, isLeftBracket)
sourceCode.getFirstToken(emitsOptionValue, isOpeningBracketToken)
)
return [
{
Expand All @@ -504,7 +481,7 @@ function buildSuggest(define, emits, nameNode, context) {
]
} else if (emitsOptionValue.type === 'ObjectExpression') {
const leftBrace = /** @type {Token} */ (
sourceCode.getFirstToken(emitsOptionValue, isLeftBrace)
sourceCode.getFirstToken(emitsOptionValue, isOpeningBraceToken)
)
return [
{
Expand Down Expand Up @@ -548,10 +525,10 @@ function buildSuggest(define, emits, nameNode, context) {
)
} else {
const objectLeftBrace = /** @type {Token} */ (
sourceCode.getFirstToken(object, isLeftBrace)
sourceCode.getFirstToken(object, isOpeningBraceToken)
)
const objectRightBrace = /** @type {Token} */ (
sourceCode.getLastToken(object, isRightBrace)
sourceCode.getLastToken(object, isClosingBraceToken)
)
return fixer.insertTextAfter(
objectLeftBrace,
Expand Down Expand Up @@ -583,10 +560,10 @@ function buildSuggest(define, emits, nameNode, context) {
)
} else {
const objectLeftBrace = /** @type {Token} */ (
sourceCode.getFirstToken(object, isLeftBrace)
sourceCode.getFirstToken(object, isOpeningBraceToken)
)
const objectRightBrace = /** @type {Token} */ (
sourceCode.getLastToken(object, isRightBrace)
sourceCode.getLastToken(object, isClosingBraceToken)
)
return fixer.insertTextAfter(
objectLeftBrace,
Expand Down

0 comments on commit cac3beb

Please sign in to comment.