Skip to content

[Update] Make vue/prop-name-casing fixable #402

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Mar 21, 2018
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ module.exports = {
All component-related rules are being applied to code that passes any of the following checks:

* `Vue.component()` expression
* `Vue.extend()` expression
* `Vue.mixin()` expression
* `export default {}` in `.vue` or `.jsx` file

If you however want to take advantage of our rules in any of your custom objects that are Vue components, you might need to use special comment `// @vue/component` that marks object in the next line as a Vue component in any file, e.g.:
Expand Down Expand Up @@ -202,6 +204,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi

| | Rule ID | Description |
|:---|:--------|:------------|
| | [vue/attributes-order](./docs/rules/attributes-order.md) | enforce order of attributes |
| :wrench: | [vue/html-closing-bracket-newline](./docs/rules/html-closing-bracket-newline.md) | require or disallow a line break before tag's closing brackets |
| :wrench: | [vue/html-closing-bracket-spacing](./docs/rules/html-closing-bracket-spacing.md) | require or disallow a space before tag's closing brackets |
| :wrench: | [vue/script-indent](./docs/rules/script-indent.md) | enforce consistent indentation in `<script>` |
Expand Down
114 changes: 114 additions & 0 deletions docs/rules/attributes-order.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# enforce order of attributes (vue/attributes-order)

## :book: Rule Details

This rule aims to enfore ordering of component attributes. The default order is specified in the [Vue styleguide](https://vuejs.org/v2/style-guide/#Element-attribute-order-recommended) and is:
- DEFINITION
ex: 'is'
- LIST_RENDERING
ex: 'v-for item in items'
- CONDITIONALS
ex: 'v-if', 'v-else-if', 'v-else', 'v-show', 'v-cloak'
- RENDER_MODIFIERS
ex: 'v-once', 'v-pre'
- GLOBAL
ex: 'id'
- UNIQUE
ex: 'ref', 'key', 'slot'
- BINDING
ex: 'v-model', 'v-bind', ':property="foo"'
- OTHER_ATTR
ex: 'customProp="foo"'
- EVENTS
ex: '@click="functionCall"', 'v-on="event"'
- CONTENT
ex: 'v-text', 'v-html'

:+1: Examples of **correct** code`:

```html
<div
is="header"
v-for="item in items"
v-if="!visible"
v-once
id="uniqueID"
ref="header"
v-model="headerData"
myProp="prop"
@click="functionCall"
v-text="textContent">
</div>
```

```html
<div
v-for="item in items"
v-if="!visible"
propOne="prop"
propTwo="prop"
propThree="prop"
@click="functionCall"
v-text="textContent">
</div>
```

```html
<div
propOne="prop"
propTwo="prop"
propThree="prop">
</div>
```

:-1: Examples of **incorrect** code`:

```html
<div
ref="header"
v-for="item in items"
v-once id="uniqueID"
v-model="headerData"
myProp="prop"
v-if="!visible"
is="header"
@click="functionCall"
v-text="textContent">
</div>
```

### `order`

Specify custom order of attribute groups

:+1: Examples of **correct** code with custom order`:

```html
<!-- 'vue/attribute-order': [2, { order: ['LIST_RENDERING', 'CONDITIONALS', 'RENDER_MODIFIERS', 'GLOBAL', 'UNIQUE', 'BINDING', 'OTHER_ATTR', 'EVENTS', 'CONTENT', 'DEFINITION'] }] -->
<div
propOne="prop"
propTwo="prop"
is="header">
</div>
```

```html
<!-- 'vue/attribute-order': [2, { order: ['LIST_RENDERING', 'CONDITIONALS', 'RENDER_MODIFIERS', 'GLOBAL', 'UNIQUE', 'BINDING', 'DEFINITION', 'OTHER_ATTR', 'EVENTS', 'CONTENT'] }] -->
<div
ref="header"
is="header"
propOne="prop"
propTwo="prop">
</div>
```

:-1: Examples of **incorrect** code with custom order`:

```html
<!-- 'vue/attribute-order': [2, { order: ['LIST_RENDERING', 'CONDITIONALS', 'RENDER_MODIFIERS', 'GLOBAL', 'UNIQUE', 'BINDING', 'DEFINITION', 'OTHER_ATTR', 'EVENTS', 'CONTENT'] }] -->
<div
ref="header"
propOne="prop"
is="header">
</div>
```
2 changes: 1 addition & 1 deletion docs/rules/no-multi-spaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

The `--fix` option on the command line can automatically fix some of the problems reported by this rule.

This rule aims to remove multiple spaces in a row between attributes witch are not used for indentation.
This rule aims to remove multiple spaces in a row between attributes which are not used for indentation.

## Rule Details

Expand Down
35 changes: 35 additions & 0 deletions docs/rules/prop-name-casing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# enforce specific casing for the Prop name in Vue components(prop-name-casing)

This rule would enforce proper casing of props in vue components(camelCase).

## :book: Rule Details

(https://vuejs.org/v2/style-guide/#Prop-name-casing-strongly-recommended).

:+1: Examples of **correct** code for `camelCase`:

```js
export default {
props: {
greetingText: String
}
}
```

:-1: Examples of **incorrect** code for `camelCase`:

```js
export default {
props: {
'greeting-text': String
}
}
```

## :wrench: Options

Default casing is set to `camelCase`.

```
"vue/prop-name-casing": ["error", "camelCase|snake_case"]
```
1 change: 1 addition & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
module.exports = {
rules: {
'attribute-hyphenation': require('./rules/attribute-hyphenation'),
'attributes-order': require('./rules/attributes-order'),
'comment-directive': require('./rules/comment-directive'),
'html-closing-bracket-newline': require('./rules/html-closing-bracket-newline'),
'html-closing-bracket-spacing': require('./rules/html-closing-bracket-spacing'),
Expand Down
103 changes: 103 additions & 0 deletions lib/rules/attributes-order.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/**
* @fileoverview enforce ordering of attributes
* @author Erin Depew
*/
'use strict'
const utils = require('../utils')

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------

function getAttributeType (name, isDirective) {
if (isDirective) {
if (name === 'for') {
return 'LIST_RENDERING'
} else if (name === 'if' || name === 'else-if' || name === 'else' || name === 'show' || name === 'cloak') {
return 'CONDITIONALS'
} else if (name === 'pre' || name === 'once') {
return 'RENDER_MODIFIERS'
} else if (name === 'model' || name === 'bind') {
return 'BINDING'
} else if (name === 'on') {
return 'EVENTS'
} else if (name === 'html' || name === 'text') {
return 'CONTENT'
}
} else {
if (name === 'is') {
return 'DEFINITION'
} else if (name === 'id') {
return 'GLOBAL'
} else if (name === 'ref' || name === 'key' || name === 'slot') {
return 'UNIQUE'
} else {
return 'OTHER_ATTR'
}
}
}
function getPosition (attribute, attributeOrder) {
const attributeType = getAttributeType(attribute.key.name, attribute.directive)
return attributeOrder.indexOf(attributeType)
}

function create (context) {
const sourceCode = context.getSourceCode()
let attributeOrder = ['DEFINITION', 'LIST_RENDERING', 'CONDITIONALS', 'RENDER_MODIFIERS', 'GLOBAL', 'UNIQUE', 'BINDING', 'OTHER_ATTR', 'EVENTS', 'CONTENT']
if (context.options[0] && context.options[0].order) {
attributeOrder = context.options[0].order
}
let currentPosition
let previousNode

function reportIssue (node, previousNode) {
const currentNode = sourceCode.getText(node.key)
const prevNode = sourceCode.getText(previousNode.key)
context.report({
node: node.key,
loc: node.loc,
message: `Attribute "${currentNode}" should go before "${prevNode}".`,
data: {
currentNode
}
})
}

return utils.defineTemplateBodyVisitor(context, {
'VStartTag' () {
currentPosition = -1
previousNode = null
},
'VAttribute' (node) {
if ((currentPosition === -1) || (currentPosition <= getPosition(node, attributeOrder))) {
currentPosition = getPosition(node, attributeOrder)
previousNode = node
} else {
reportIssue(node, previousNode)
}
}
})
}

module.exports = {
meta: {
docs: {
description: 'enforce order of attributes',
category: 'recommended'
},
fixable: null,
schema: {
type: 'array',
properties: {
order: {
items: {
type: 'string'
},
maxItems: 10,
minItems: 10
}
}
}
},
create
}
7 changes: 4 additions & 3 deletions lib/rules/max-attributes-per-line.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ module.exports = {
category: 'strongly-recommended',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v4.2.2/docs/rules/max-attributes-per-line.md'
},
fixable: null,
fixable: 'whitespace', // or "code" or "whitespace"
schema: [
{
type: 'object',
Expand Down Expand Up @@ -129,14 +129,15 @@ module.exports = {
}

function showErrors (attributes, node) {
attributes.forEach((prop) => {
attributes.forEach((prop, i) => {
context.report({
node: prop,
loc: prop.loc,
message: 'Attribute "{{propName}}" should be on a new line.',
data: {
propName: prop.key.name
}
},
fix: i === 0 ? (fixer) => fixer.insertTextBefore(prop, '\n') : undefined
})
})
}
Expand Down
Loading