-
-
Notifications
You must be signed in to change notification settings - Fork 666
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New Add
vue/no-potential-property-typo
rule (#1072)
* feat(utils/index.js): add util lib * feat(tests/lib/utils/index.js): add unit test * feat: change test, complete rule * feat: add test, add preset, custom option * feat: add testcase * test: add test, 100% test cover * test: menual indentation * style: remove todo comment that have been done🚀 * fix: change rule name -> no-unknown-component-options * feat: rename `no-unknow-component-options` -> `no-potential-component-options-typo` * feat: remove unnecessary readme * feat: revert lib/utils/index.js * docs: update readme * feat: set categories to undefined * test: add test case * test: add test case * test: add vue preset as default preset, abcde and abcd test case * test: udpate test * test: all valid options * improvement: comment * test: inline test
- Loading branch information
1 parent
7f39dc7
commit d2c94a9
Showing
8 changed files
with
776 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
--- | ||
pageClass: rule-details | ||
sidebarDepth: 0 | ||
title: vue/no-potential-component-option-typo | ||
description: disallow a potential typo in your component property | ||
--- | ||
# vue/no-potential-component-option-typo | ||
> disallow a potential typo in your component property | ||
## :book: Rule Details | ||
|
||
This Rule disallow a potential typo in your component options | ||
|
||
**Here is the config** | ||
```js | ||
{'vue/no-potential-component-option-typo': ['error', {presets: ['all'], custom: ['test']}]} | ||
``` | ||
|
||
<eslint-code-block :rules="{'vue/no-potential-component-option-typo': ['error', {presets: ['all'], custom: ['test']}]}"> | ||
|
||
```vue | ||
<script> | ||
export default { | ||
/* ✓ GOOD */ | ||
props: { | ||
}, | ||
/* × BAD */ | ||
method: { | ||
}, | ||
/* ✓ GOOD */ | ||
data: { | ||
}, | ||
/* × BAD */ | ||
beforeRouteEnteR() { | ||
}, | ||
/* × BAD due to custom option 'test'*/ | ||
testt: { | ||
} | ||
} | ||
</script> | ||
``` | ||
|
||
</eslint-code-block> | ||
|
||
> we use editdistance to compare two string similarity, threshold is an option to control upper bound of editdistance to report | ||
**Here is the another example about config option `threshold`** | ||
```js | ||
{'vue/no-potential-component-option-typo': ['error', {presets: ['vue', 'nuxt'], threshold: 5}]} | ||
``` | ||
|
||
<eslint-code-block :rules="{'vue/no-potential-component-option-typo': ['error', {presets: ['vue', 'nuxt'], threshold: 5}]}"> | ||
|
||
```vue | ||
<script> | ||
export default { | ||
/* ✓ BAD, due to threshold is 5 */ | ||
props: { | ||
}, | ||
/* ✓ BAD, due to threshold is 5 */ | ||
method: { | ||
}, | ||
/* ✓ BAD, due to threshold is 5 */ | ||
data: { | ||
}, | ||
/* × GOOD, due to we don't choose vue-router preset or add a custom option */ | ||
beforeRouteEnteR() { | ||
} | ||
} | ||
</script> | ||
``` | ||
|
||
</eslint-code-block> | ||
|
||
## :wrench: Options | ||
```js | ||
{ | ||
"vue/no-unsed-vars": [{ | ||
presets: { | ||
type: 'array', | ||
items: { | ||
type: 'string', | ||
enum: ['all', 'vue', 'vue-router', 'nuxt'] | ||
}, | ||
uniqueItems: true, | ||
minItems: 0 | ||
}, | ||
custom: { | ||
type: 'array', | ||
minItems: 0, | ||
items: { type: 'string' }, | ||
uniqueItems: true | ||
}, | ||
threshold: { | ||
type: 'number', | ||
'minimum': 1 | ||
} | ||
}] | ||
} | ||
``` | ||
- `presets` ... `enum type`, contains several common vue component option set, `['all']` is the same as `['vue', 'vue-router', 'nuxt']`. **default** `[]` | ||
- `custom` ... `array type`, a list store your custom component option want to detect. **default** `[]` | ||
- `threshold` ... `number type`, a number used to control the upper limit of the reported editing distance, we recommend don't change this config option, even if it is required, not bigger than `2`. **default** `1` | ||
## :rocket: Suggestion | ||
- We provide all the possible component option that editdistance between your vue component option and configuration options is greater than 0 and lessEqual than threshold | ||
|
||
## :books: Further reading | ||
- [Edit_distance](https://en.wikipedia.org/wiki/Edit_distance) | ||
## :mag: Implementation | ||
|
||
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-potential-component-option-typo.js) | ||
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-potential-component-option-typo.js) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/** | ||
* @fileoverview detect if there is a potential typo in your component property | ||
* @author IWANABETHATGUY | ||
*/ | ||
'use strict' | ||
|
||
const utils = require('../utils') | ||
const vueComponentOptions = require('../utils/vue-component-options.json') | ||
// ------------------------------------------------------------------------------ | ||
// Rule Definition | ||
// ------------------------------------------------------------------------------ | ||
|
||
module.exports = { | ||
meta: { | ||
type: 'suggestion', | ||
docs: { | ||
description: 'disallow a potential typo in your component property', | ||
categories: undefined, | ||
recommended: false, | ||
url: 'https://eslint.vuejs.org/rules/no-potential-component-option-typo.html' | ||
}, | ||
fixable: null, | ||
schema: [ | ||
{ | ||
type: 'object', | ||
properties: { | ||
presets: { | ||
type: 'array', | ||
items: { | ||
type: 'string', | ||
enum: ['all', 'vue', 'vue-router', 'nuxt'] | ||
}, | ||
uniqueItems: true, | ||
minItems: 0 | ||
}, | ||
custom: { | ||
type: 'array', | ||
minItems: 0, | ||
items: { type: 'string' }, | ||
uniqueItems: true | ||
}, | ||
threshold: { | ||
type: 'number', | ||
'minimum': 1 | ||
} | ||
} | ||
} | ||
] | ||
}, | ||
|
||
create: function (context) { | ||
const option = context.options[0] || {} | ||
const custom = option['custom'] || [] | ||
const presets = option['presets'] || ['vue'] | ||
const threshold = option['threshold'] || 1 | ||
let candidateOptions | ||
if (presets.includes('all')) { | ||
candidateOptions = Object.keys(vueComponentOptions).reduce((pre, cur) => { | ||
return [...pre, ...vueComponentOptions[cur]] | ||
}, []) | ||
} else { | ||
candidateOptions = presets.reduce((pre, cur) => { | ||
return [...pre, ...vueComponentOptions[cur]] | ||
}, []) | ||
} | ||
const candidateOptionSet = new Set([...candidateOptions, ...custom]) | ||
const candidateOptionList = [...candidateOptionSet] | ||
if (!candidateOptionList.length) { | ||
return {} | ||
} | ||
return utils.executeOnVue(context, obj => { | ||
const componentInstanceOptions = obj.properties.filter( | ||
p => p.type === 'Property' && p.key.type === 'Identifier' | ||
) | ||
if (!componentInstanceOptions.length) { | ||
return {} | ||
} | ||
componentInstanceOptions.forEach(option => { | ||
const id = option.key | ||
const name = id.name | ||
if (candidateOptionSet.has(name)) { | ||
return | ||
} | ||
const potentialTypoList = candidateOptionList | ||
.map(o => ({ option: o, distance: utils.editDistance(o, name) })) | ||
.filter(({ distance, option }) => distance <= threshold && distance > 0) | ||
.sort((a, b) => a.distance - b.distance) | ||
if (potentialTypoList.length) { | ||
context.report({ | ||
node: id, | ||
loc: id.loc, | ||
message: `'{{name}}' may be a typo, which is similar to option [{{option}}].`, | ||
data: { | ||
name, | ||
option: potentialTypoList.map(({ option }) => option).join(',') | ||
}, | ||
suggest: potentialTypoList.map(({ option }) => ({ | ||
desc: `Replace property '${name}' to '${option}'`, | ||
fix (fixer) { | ||
return fixer.replaceText(id, option) | ||
} | ||
})) | ||
}) | ||
} | ||
}) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
{ | ||
"nuxt": ["asyncData", "fetch", "head", "key", "layout", "loading", "middleware", "scrollToTop", "transition", "validate", "watchQuery"], | ||
"vue-router": [ | ||
"beforeRouteEnter", | ||
"beforeRouteUpdate", | ||
"beforeRouteLeave" | ||
], | ||
"vue": [ | ||
"data", | ||
"props", | ||
"propsData", | ||
"computed", | ||
"methods", | ||
"watch", | ||
"el", | ||
"template", | ||
"render", | ||
"renderError", | ||
"staticRenderFns", | ||
"beforeCreate", | ||
"created", | ||
"beforeDestroy", | ||
"destroyed", | ||
"beforeMount", | ||
"mounted", | ||
"beforeUpdate", | ||
"updated", | ||
"activated", | ||
"deactivated", | ||
"errorCaptured", | ||
"serverPrefetch", | ||
"directives", | ||
"components", | ||
"transitions", | ||
"filters", | ||
"provide", | ||
"inject", | ||
"model", | ||
"parent", | ||
"mixins", | ||
"name", | ||
"extends", | ||
"delimiters", | ||
"comments", | ||
"inheritAttrs" | ||
] | ||
} |
Oops, something went wrong.