diff --git a/docs/rules/mustache-interpolation-spacing.md b/docs/rules/mustache-interpolation-spacing.md
new file mode 100644
index 000000000..4844d15ab
--- /dev/null
+++ b/docs/rules/mustache-interpolation-spacing.md
@@ -0,0 +1,67 @@
+# enforce unified spacing in mustache interpolations. (mustache-interpolation-spacing)
+
+- :wrench: The `--fix` option on the [command line](http://eslint.org/docs/user-guide/command-line-interface#fix) can automatically fix some of the problems reported by this rule.
+
+## :book: Rule Details
+
+This rule aims to enforce unified spacing in mustache interpolations.
+
+:-1: Examples of **incorrect** code for this rule:
+
+```html
+
+ {{ text }}
+
+```
+
+:+1: Examples of **correct** code for this rule:
+
+```html
+
+ {{ text }}
+
+```
+
+## :wrench: Options
+
+Default spacing is set to `always`
+
+```
+'vue/mustache-interpolation-spacing': [2, 'always'|'never']
+```
+
+### `"always"` - Expect one space between expression and curly brackets.
+
+:+1: Examples of **correct** code`:
+
+```html
+
+ {{ text }}
+
+```
+
+:-1: Examples of **incorrect** code`:
+
+```html
+
+ {{text}}
+
+```
+
+### `"never"` - Expect no spaces between expression and curly brackets.
+
+:+1: Examples of **correct** code`:
+
+```html
+
+ {{text}}
+
+```
+
+:-1: Examples of **incorrect** code`:
+
+```html
+
+ {{ text }}
+
+```
diff --git a/lib/rules/mustache-interpolation-spacing.js b/lib/rules/mustache-interpolation-spacing.js
new file mode 100644
index 000000000..4ea93e96a
--- /dev/null
+++ b/lib/rules/mustache-interpolation-spacing.js
@@ -0,0 +1,91 @@
+/**
+ * @fileoverview enforce unified spacing in mustache interpolations.
+ * @author Armano
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: 'enforce unified spacing in mustache interpolations.',
+ category: 'Stylistic Issues',
+ recommended: false
+ },
+ fixable: 'whitespace',
+ schema: [
+ {
+ enum: ['always', 'never']
+ }
+ ]
+ },
+
+ create (context) {
+ const options = context.options[0]
+ const optSpaces = options !== 'never'
+ const template = context.parserServices.getTemplateBodyTokenStore && context.parserServices.getTemplateBodyTokenStore()
+
+ // ----------------------------------------------------------------------
+ // Helpers
+ // ----------------------------------------------------------------------
+
+ function checkTokens (leftToken, rightToken) {
+ if (leftToken.loc.end.line === rightToken.loc.start.line) {
+ const spaces = rightToken.loc.start.column - leftToken.loc.end.column
+ const noSpacesFound = spaces === 0
+
+ if (optSpaces === noSpacesFound) {
+ context.report({
+ node: rightToken,
+ loc: {
+ start: leftToken.loc.end,
+ end: rightToken.loc.start
+ },
+ message: 'Found {{spaces}} whitespaces, {{type}} expected.',
+ data: {
+ spaces: spaces === 0 ? 'none' : spaces,
+ type: optSpaces ? '1' : 'none'
+ },
+ fix: (fixer) => fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], optSpaces ? ' ' : '')
+ })
+ }
+ }
+ }
+
+ // ----------------------------------------------------------------------
+ // Public
+ // ----------------------------------------------------------------------
+
+ utils.registerTemplateBodyVisitor(context, {
+ 'VExpressionContainer[expression!=null]' (node) {
+ const tokens = template.getTokens(node, {
+ includeComments: true,
+ filter: token => token.type !== 'HTMLWhitespace' // When there is only whitespace between ignore it
+ })
+
+ const startToken = tokens.shift()
+ if (!startToken || startToken.type !== 'VExpressionStart') return
+ const endToken = tokens.pop()
+ if (!endToken || endToken.type !== 'VExpressionEnd') return
+
+ if (tokens.length > 0) {
+ checkTokens(startToken, tokens[0])
+ checkTokens(tokens[tokens.length - 1], endToken)
+ } else {
+ checkTokens(startToken, endToken)
+ }
+ }
+ })
+
+ return { }
+ }
+}
diff --git a/tests/lib/rules/mustache-interpolation-spacing.js b/tests/lib/rules/mustache-interpolation-spacing.js
new file mode 100644
index 000000000..84c8ef971
--- /dev/null
+++ b/tests/lib/rules/mustache-interpolation-spacing.js
@@ -0,0 +1,168 @@
+/**
+ * @fileoverview enforce unified spacing in mustache interpolations.
+ * @author Armano
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/mustache-interpolation-spacing')
+const RuleTester = require('eslint').RuleTester
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({
+ parser: 'vue-eslint-parser',
+ parserOptions: { ecmaVersion: 2015 }
+})
+
+ruleTester.run('mustache-interpolation-spacing', rule, {
+
+ valid: [
+ {
+ filename: 'test.vue',
+ code: ''
+ },
+ {
+ filename: 'test.vue',
+ code: ''
+ },
+ {
+ filename: 'test.vue',
+ code: ' '
+ },
+ {
+ filename: 'test.vue',
+ code: ' '
+ },
+ {
+ filename: 'test.vue',
+ code: '{{ text }}
'
+ },
+ {
+ filename: 'test.vue',
+ code: '{{ }}
'
+ },
+ {
+ filename: 'test.vue',
+ code: '{{ }}
',
+ options: ['always']
+ },
+ {
+ filename: 'test.vue',
+ code: '{{}}
',
+ options: ['never']
+ },
+ {
+ filename: 'test.vue',
+ code: '{{text}}
',
+ options: ['never']
+ },
+ {
+ filename: 'test.vue',
+ code: '{{ text }}
',
+ options: ['always']
+ },
+ {
+ filename: 'test.vue',
+ code: '{{ }}
',
+ options: ['always']
+ },
+ {
+ filename: 'test.vue',
+ code: '{{ }}
',
+ options: ['never']
+ },
+ {
+ filename: 'test.vue',
+ code: '{{ text }}
',
+ options: ['always']
+ }
+ ],
+
+ invalid: [
+ {
+ filename: 'test.vue',
+ code: '{{ text}}
',
+ output: '{{ text }}
',
+ options: ['always'],
+ errors: [{
+ message: 'Found none whitespaces, 1 expected.',
+ type: 'VExpressionEnd'
+ }]
+ },
+ {
+ filename: 'test.vue',
+ code: '{{text }}
',
+ output: '{{ text }}
',
+ options: ['always'],
+ errors: [{
+ message: 'Found none whitespaces, 1 expected.',
+ type: 'Identifier'
+ }]
+ },
+ {
+ filename: 'test.vue',
+ code: '{{ text}}
',
+ output: '{{text}}
',
+ options: ['never'],
+ errors: [{
+ message: 'Found 1 whitespaces, none expected.',
+ type: 'Identifier'
+ }]
+ },
+ {
+ filename: 'test.vue',
+ code: '{{text }}
',
+ output: '{{text}}
',
+ options: ['never'],
+ errors: [{
+ message: 'Found 1 whitespaces, none expected.',
+ type: 'VExpressionEnd'
+ }]
+ },
+ {
+ filename: 'test.vue',
+ code: '{{text}}
',
+ output: '{{ text }}
',
+ options: ['always'],
+ errors: [{
+ message: 'Found none whitespaces, 1 expected.',
+ type: 'Identifier'
+ }, {
+ message: 'Found none whitespaces, 1 expected.',
+ type: 'VExpressionEnd'
+ }]
+ },
+ {
+ filename: 'test.vue',
+ code: '{{ text }}
',
+ output: '{{text}}
',
+ options: ['never'],
+ errors: [{
+ message: 'Found 1 whitespaces, none expected.',
+ type: 'Identifier'
+ }, {
+ message: 'Found 1 whitespaces, none expected.',
+ type: 'VExpressionEnd'
+ }]
+ },
+ {
+ filename: 'test.vue',
+ code: '{{ text }}
',
+ output: '{{text}}
',
+ options: ['never'],
+ errors: [{
+ message: 'Found 3 whitespaces, none expected.',
+ type: 'Identifier'
+ }, {
+ message: 'Found 3 whitespaces, none expected.',
+ type: 'VExpressionEnd'
+ }]
+ }
+ ]
+})