Skip to content

New: some core rules for <template> #680

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 13 commits into from
Jan 5, 2019
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,13 @@ Enforce all the rules in this category, as well as all higher priority rules, wi

| | Rule ID | Description |
|:---|:--------|:------------|
| :wrench: | [vue/array-bracket-spacing](./docs/rules/array-bracket-spacing.md) | enforce consistent spacing inside array brackets |
| :wrench: | [vue/eqeqeq](./docs/rules/eqeqeq.md) | require the use of `===` and `!==` |
| :wrench: | [vue/key-spacing](./docs/rules/key-spacing.md) | enforce consistent spacing between keys and values in object literal properties |
| :wrench: | [vue/object-curly-spacing](./docs/rules/object-curly-spacing.md) | enforce consistent spacing inside braces |
| :wrench: | [vue/script-indent](./docs/rules/script-indent.md) | enforce consistent indentation in `<script>` |
| :wrench: | [vue/space-infix-ops](./docs/rules/space-infix-ops.md) | require spacing around infix operators |
| :wrench: | [vue/space-unary-ops](./docs/rules/space-unary-ops.md) | enforce consistent spacing before or after unary operators |

### Deprecated

Expand Down
11 changes: 11 additions & 0 deletions docs/rules/array-bracket-spacing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# enforce consistent spacing inside array brackets (vue/array-bracket-spacing)

- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.

This rule is the same rule as core [array-bracket-spacing] rule but it applies to the expressions in `<template>`.

## :books: Further reading

- [array-bracket-spacing]

[array-bracket-spacing]: https://eslint.org/docs/rules/array-bracket-spacing
11 changes: 11 additions & 0 deletions docs/rules/eqeqeq.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# require the use of `===` and `!==` (vue/eqeqeq)

- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.

This rule is the same rule as core [eqeqeq] rule but it applies to the expressions in `<template>`.

## :books: Further reading

- [eqeqeq]

[eqeqeq]: https://eslint.org/docs/rules/eqeqeq
11 changes: 11 additions & 0 deletions docs/rules/key-spacing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# enforce consistent spacing between keys and values in object literal properties (vue/key-spacing)

- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.

This rule is the same rule as core [key-spacing] rule but it applies to the expressions in `<template>`.

## :books: Further reading

- [key-spacing]

[key-spacing]: https://eslint.org/docs/rules/key-spacing
11 changes: 11 additions & 0 deletions docs/rules/object-curly-spacing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# enforce consistent spacing inside braces (vue/object-curly-spacing)

- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.

This rule is the same rule as core [object-curly-spacing] rule but it applies to the expressions in `<template>`.

## :books: Further reading

- [object-curly-spacing]

[object-curly-spacing]: https://eslint.org/docs/rules/object-curly-spacing
11 changes: 11 additions & 0 deletions docs/rules/space-infix-ops.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# require spacing around infix operators (vue/space-infix-ops)

- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.

This rule is the same rule as core [space-infix-ops] rule but it applies to the expressions in `<template>`.

## :books: Further reading

- [space-infix-ops]

[space-infix-ops]: https://eslint.org/docs/rules/space-infix-ops
11 changes: 11 additions & 0 deletions docs/rules/space-unary-ops.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# enforce consistent spacing before or after unary operators (vue/space-unary-ops)

- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.

This rule is the same rule as core [space-unary-ops] rule but it applies to the expressions in `<template>`.

## :books: Further reading

- [space-unary-ops]

[space-unary-ops]: https://eslint.org/docs/rules/space-unary-ops
14 changes: 8 additions & 6 deletions eslint-internal-rules/consistent-docs-description.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,15 @@ const ALLOWED_FIRST_WORDS = [
* @returns {ASTNode} The Property node or null if not found.
*/
function getPropertyFromObject (property, node) {
const properties = node.properties
if (node && node.type === 'ObjectExpression') {
const properties = node.properties

for (let i = 0; i < properties.length; i++) {
if (properties[i].key.name === property) {
return properties[i]
for (let i = 0; i < properties.length; i++) {
if (properties[i].key.name === property) {
return properties[i]
}
}
}

return null
}

Expand Down Expand Up @@ -128,7 +129,8 @@ module.exports = {
node.right &&
node.left.type === 'MemberExpression' &&
node.left.object.name === 'module' &&
node.left.property.name === 'exports') {
node.left.property.name === 'exports' &&
node.right.type === 'ObjectExpression') {
checkMetaDocsDescription(context, node.right)
}
}
Expand Down
11 changes: 6 additions & 5 deletions eslint-internal-rules/no-invalid-meta.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@
* @returns {ASTNode} The Property node or null if not found.
*/
function getPropertyFromObject (property, node) {
const properties = node.properties
if (node && node.type === 'ObjectExpression') {
const properties = node.properties

for (let i = 0; i < properties.length; i++) {
if (properties[i].key.name === property) {
return properties[i]
for (let i = 0; i < properties.length; i++) {
if (properties[i].key.name === property) {
return properties[i]
}
}
}

return null
}

Expand Down
6 changes: 6 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,20 @@

module.exports = {
rules: {
'array-bracket-spacing': require('./rules/array-bracket-spacing'),
'attribute-hyphenation': require('./rules/attribute-hyphenation'),
'attributes-order': require('./rules/attributes-order'),
'comment-directive': require('./rules/comment-directive'),
'component-name-in-template-casing': require('./rules/component-name-in-template-casing'),
'eqeqeq': require('./rules/eqeqeq'),
'html-closing-bracket-newline': require('./rules/html-closing-bracket-newline'),
'html-closing-bracket-spacing': require('./rules/html-closing-bracket-spacing'),
'html-end-tags': require('./rules/html-end-tags'),
'html-indent': require('./rules/html-indent'),
'html-quotes': require('./rules/html-quotes'),
'html-self-closing': require('./rules/html-self-closing'),
'jsx-uses-vars': require('./rules/jsx-uses-vars'),
'key-spacing': require('./rules/key-spacing'),
'max-attributes-per-line': require('./rules/max-attributes-per-line'),
'multiline-html-element-content-newline': require('./rules/multiline-html-element-content-newline'),
'mustache-interpolation-spacing': require('./rules/mustache-interpolation-spacing'),
Expand All @@ -39,6 +42,7 @@ module.exports = {
'no-unused-vars': require('./rules/no-unused-vars'),
'no-use-v-if-with-v-for': require('./rules/no-use-v-if-with-v-for'),
'no-v-html': require('./rules/no-v-html'),
'object-curly-spacing': require('./rules/object-curly-spacing'),
'order-in-components': require('./rules/order-in-components'),
'prop-name-casing': require('./rules/prop-name-casing'),
'require-component-is': require('./rules/require-component-is'),
Expand All @@ -51,6 +55,8 @@ module.exports = {
'return-in-computed-property': require('./rules/return-in-computed-property'),
'script-indent': require('./rules/script-indent'),
'singleline-html-element-content-newline': require('./rules/singleline-html-element-content-newline'),
'space-infix-ops': require('./rules/space-infix-ops'),
'space-unary-ops': require('./rules/space-unary-ops'),
'this-in-template': require('./rules/this-in-template'),
'use-v-on-exact': require('./rules/use-v-on-exact'),
'v-bind-style': require('./rules/v-bind-style'),
Expand Down
9 changes: 9 additions & 0 deletions lib/rules/array-bracket-spacing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @author Toru Nagashima
*/
'use strict'

const { wrapCoreRule } = require('../utils')

// eslint-disable-next-line
module.exports = wrapCoreRule(require('eslint/lib/rules/array-bracket-spacing'))
9 changes: 9 additions & 0 deletions lib/rules/eqeqeq.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @author Toru Nagashima
*/
'use strict'

const { wrapCoreRule } = require('../utils')

// eslint-disable-next-line
module.exports = wrapCoreRule(require('eslint/lib/rules/eqeqeq'))
9 changes: 9 additions & 0 deletions lib/rules/key-spacing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @author Toru Nagashima
*/
'use strict'

const { wrapCoreRule } = require('../utils')

// eslint-disable-next-line
module.exports = wrapCoreRule(require('eslint/lib/rules/key-spacing'))
9 changes: 9 additions & 0 deletions lib/rules/object-curly-spacing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @author Toru Nagashima
*/
'use strict'

const { wrapCoreRule } = require('../utils')

// eslint-disable-next-line
module.exports = wrapCoreRule(require('eslint/lib/rules/object-curly-spacing'))
9 changes: 9 additions & 0 deletions lib/rules/space-infix-ops.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @author Toru Nagashima
*/
'use strict'

const { wrapCoreRule } = require('../utils')

// eslint-disable-next-line
module.exports = wrapCoreRule(require('eslint/lib/rules/space-infix-ops'))
9 changes: 9 additions & 0 deletions lib/rules/space-unary-ops.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @author Toru Nagashima
*/
'use strict'

const { wrapCoreRule } = require('../utils')

// eslint-disable-next-line
module.exports = wrapCoreRule(require('eslint/lib/rules/space-unary-ops'))
64 changes: 64 additions & 0 deletions lib/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,29 @@ const HTML_ELEMENT_NAMES = new Set(require('./html-elements.json'))
const SVG_ELEMENT_NAMES = new Set(require('./svg-elements.json'))
const VOID_ELEMENT_NAMES = new Set(require('./void-elements.json'))
const assert = require('assert')
const path = require('path')
const vueEslintParser = require('vue-eslint-parser')

/**
* Wrap the rule context object to override methods which access to tokens (such as getTokenAfter).
* @param {RuleContext} context The rule context object.
* @param {TokenStore} tokenStore The token store object for template.
*/
function wrapContextToOverrideTokenMethods (context, tokenStore) {
const sourceCode = new Proxy(context.getSourceCode(), {
get (object, key) {
return key in tokenStore ? tokenStore[key] : object[key]
}
})

return {
__proto__: context,
getSourceCode () {
return sourceCode
}
}
}

// ------------------------------------------------------------------------------
// Exports
// ------------------------------------------------------------------------------
Expand All @@ -41,6 +62,49 @@ module.exports = {
return context.parserServices.defineTemplateBodyVisitor(templateBodyVisitor, scriptVisitor)
},

/**
* Wrap a given core rule to apply it to Vue.js template.
* @param {Rule} coreRule The core rule implementation to wrap.
* @param {string|undefined} category The category of this rule.
* @returns {Rule} The wrapped rule implementation.
*/
wrapCoreRule (coreRule, category) {
return {
create (context) {
const tokenStore =
context.parserServices.getTemplateBodyTokenStore &&
context.parserServices.getTemplateBodyTokenStore()

// The `context.getSourceCode()` cannot access the tokens of templates.
// So override the methods which access to tokens by the `tokenStore`.
if (tokenStore) {
context = wrapContextToOverrideTokenMethods(context, tokenStore)
}

// Move `Program` handlers to `VElement[parent.type!='VElement']`
const handlers = coreRule.create(context)
if (handlers.Program) {
handlers["VElement[parent.type!='VElement']"] = handlers.Program
delete handlers.Program
}
if (handlers['Program:exit']) {
handlers["VElement[parent.type!='VElement']:exit"] = handlers['Program:exit']
delete handlers['Program:exit']
}

// Apply the handlers to templates.
return module.exports.defineTemplateBodyVisitor(context, handlers)
},

meta: Object.assign({}, coreRule.meta, {
docs: Object.assign({}, coreRule.meta.docs, {
category,
url: `https://vuejs.github.io/eslint-plugin-vue/rules/${path.basename(coreRule.meta.docs.url || '')}.html`
})
})
}
},

/**
* Check whether the given node is the root element or not.
* @param {ASTNode} node The element node to check.
Expand Down
79 changes: 79 additions & 0 deletions tests/lib/rules/array-bracket-spacing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* @author Toru Nagashima
*/
'use strict'

const RuleTester = require('eslint').RuleTester
const rule = require('../../../lib/rules/array-bracket-spacing')

const tester = new RuleTester({
parser: 'vue-eslint-parser',
parserOptions: { ecmaVersion: 2015 }
})

tester.run('array-bracket-spacing', rule, {
valid: [
'<template><div :attr="[a]" /></template>',
{
code: '<template><div :attr="[a]" /></template>',
options: ['never']
},
{
code: '<template><div :attr="[ a ]" /></template>',
options: ['always']
}
],
invalid: [
{
code: '<template><div :attr="[ a]" /></template>',
errors: ["There should be no space after '['."]
},
{
code: '<template><div :attr="[a ]" /></template>',
errors: ["There should be no space before ']'."]
},
{
code: '<template><div :attr="[ a ]" /></template>',
errors: [
"There should be no space after '['.",
"There should be no space before ']'."
]
},
{
code: '<template><div :attr="[ a]" /></template>',
options: ['never'],
errors: ["There should be no space after '['."]
},
{
code: '<template><div :attr="[a ]" /></template>',
options: ['never'],
errors: ["There should be no space before ']'."]
},
{
code: '<template><div :attr="[ a ]" /></template>',
options: ['never'],
errors: [
"There should be no space after '['.",
"There should be no space before ']'."
]
},
{
code: '<template><div :attr="[ a]" /></template>',
options: ['always'],
errors: ["A space is required before ']'."]
},
{
code: '<template><div :attr="[a ]" /></template>',
options: ['always'],
errors: ["A space is required after '['."]
},
{
code: '<template><div :attr="[a]" /></template>',
options: ['always'],
errors: [
"A space is required after '['.",
"A space is required before ']'."
]
}
]
})
Loading