diff --git a/docs/rules/no-this-in-template.md b/docs/rules/no-this-in-template.md
deleted file mode 100644
index 0b877094f..000000000
--- a/docs/rules/no-this-in-template.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# Disallow usage of `this` in template. (no-this-in-template)
-
-This rule reports expresions that contain `this` keyword in expressions
-
-## :book: Rule Details
-
-:-1: Examples of **incorrect** code for this rule:
-
-```html
-
- {{this.text}}
-
-```
-
-:+1: Examples of **correct** code for this rule:
-
-```html
-
- {{text}}
-
-```
-
-## :wrench: Options
-
-Nothing.
diff --git a/docs/rules/require-render-return.md b/docs/rules/require-render-return.md
index 5a2479f93..44ea2d820 100644
--- a/docs/rules/require-render-return.md
+++ b/docs/rules/require-render-return.md
@@ -1,6 +1,6 @@
# Enforces render function to always return value (require-render-return)
-This rule aims to enforce render function to allways return value
+This rule aims to enforce render function to always return value
## :book: Rule Details
diff --git a/docs/rules/this-in-template.md b/docs/rules/this-in-template.md
new file mode 100644
index 000000000..d0646206e
--- /dev/null
+++ b/docs/rules/this-in-template.md
@@ -0,0 +1,71 @@
+# enforce usage of `this` in template. (this-in-template)
+
+## :book: Rule Details
+
+:-1: Examples of **incorrect** code for this rule:
+
+```html
+
+ {{this.text}}
+
+```
+
+:+1: Examples of **correct** code for this rule:
+
+```html
+
+ {{text}}
+
+```
+
+## :wrench: Options
+
+Default is set to `never`.
+
+```
+'vue/this-in-template': [2, 'always'|'never']
+```
+
+### `"always"` - Always use `this` while accessing properties from vue
+
+:+1: Examples of **correct** code`:
+
+```html
+
+
+ {{ this.foo }}
+
+
+```
+
+:-1: Examples of **incorrect** code`:
+
+```html
+
+
+ {{ foo }}
+
+
+```
+
+### `"never"` - Never use expresions that contain `this` keyword in expressions
+
+:+1: Examples of **correct** code`:
+
+```html
+
+
+ {{ this.foo }}
+
+
+```
+
+:-1: Examples of **incorrect** code`:
+
+```html
+
+
+ {{ foo }}
+
+
+```
diff --git a/lib/rules/no-this-in-template.js b/lib/rules/no-this-in-template.js
deleted file mode 100644
index bec97d76a..000000000
--- a/lib/rules/no-this-in-template.js
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
- * @fileoverview Disallow usage of `this` in template.
- * @author Armano
- */
-'use strict'
-
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
-const utils = require('../utils')
-
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
-
-module.exports = {
- meta: {
- docs: {
- description: 'Disallow usage of `this` in template.',
- category: 'Best Practices',
- recommended: false
- },
- fixable: null,
- schema: []
- },
-
- /**
- * Creates AST event handlers for no-this-in-template.
- *
- * @param {RuleContext} context - The rule context.
- * @returns {Object} AST event handlers.
- */
- create (context) {
- utils.registerTemplateBodyVisitor(context, {
- 'VExpressionContainer ThisExpression' (node) {
- context.report({
- node,
- loc: node.loc,
- message: "Unexpected usage of 'this'."
- })
- }
- })
-
- return {}
- }
-}
diff --git a/lib/rules/this-in-template.js b/lib/rules/this-in-template.js
new file mode 100644
index 000000000..3581aaaf5
--- /dev/null
+++ b/lib/rules/this-in-template.js
@@ -0,0 +1,103 @@
+/**
+ * @fileoverview enforce usage of `this` in template.
+ * @author Armano
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+const RESERVED_NAMES = new Set(require('../utils/js-reserved.json'))
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: 'enforce usage of `this` in template.',
+ category: 'Best Practices',
+ recommended: false
+ },
+ fixable: null,
+ schema: [
+ {
+ enum: ['always', 'never']
+ }
+ ]
+ },
+
+ /**
+ * Creates AST event handlers for this-in-template.
+ *
+ * @param {RuleContext} context - The rule context.
+ * @returns {Object} AST event handlers.
+ */
+ create (context) {
+ const options = context.options[0] !== 'always' ? 'never' : 'always'
+ let scope = {
+ parent: null,
+ nodes: []
+ }
+
+ utils.registerTemplateBodyVisitor(context, Object.assign({
+ VElement (node) {
+ scope = {
+ parent: scope,
+ nodes: scope.nodes.slice() // make copy
+ }
+ if (node.variables) {
+ for (const variable of node.variables) {
+ const varNode = variable.id
+ const name = varNode.name
+ if (!scope.nodes.some(node => node.name === name)) { // Prevent adding duplicates
+ scope.nodes.push(varNode)
+ }
+ }
+ }
+ },
+ 'VElement:exit' (node) {
+ scope = scope.parent
+ }
+ }, options === 'never'
+ ? {
+ 'VExpressionContainer MemberExpression > ThisExpression' (node) {
+ const propertyName = utils.getStaticPropertyName(node.parent.property)
+ if (!propertyName ||
+ scope.nodes.some(el => el.name === propertyName) ||
+ RESERVED_NAMES.has(propertyName) || // this.class | this['class']
+ /^[0-9].*$|[^a-zA-Z0-9_]/.test(propertyName) // this['0aaaa'] | this['foo-bar bas']
+ ) {
+ return
+ }
+
+ context.report({
+ node,
+ loc: node.loc,
+ message: "Unexpected usage of 'this'."
+ })
+ }
+ }
+ : {
+ 'VExpressionContainer' (node) {
+ if (node.references) {
+ for (const reference of node.references) {
+ if (!scope.nodes.some(el => el.name === reference.id.name)) {
+ context.report({
+ node: reference.id,
+ loc: reference.id.loc,
+ message: "Expected 'this'."
+ })
+ }
+ }
+ }
+ }
+ }
+ ))
+
+ return {}
+ }
+}
diff --git a/lib/utils/js-reserved.json b/lib/utils/js-reserved.json
new file mode 100644
index 000000000..9b3eb11cc
--- /dev/null
+++ b/lib/utils/js-reserved.json
@@ -0,0 +1,18 @@
+[
+ "abstract", "arguments", "await", "boolean",
+ "break", "byte", "case", "catch",
+ "char", "class", "const", "continue",
+ "debugger", "default", "delete", "do",
+ "double", "else", "enum", "eval",
+ "export", "extends", "false", "final",
+ "finally", "float", "for", "function",
+ "goto", "if", "implements", "import",
+ "in", "instanceof", "int", "interface",
+ "let", "long", "native", "new",
+ "null", "package", "private", "protected",
+ "public", "return", "short", "static",
+ "super", "switch", "synchronized", "this",
+ "throw", "throws", "transient", "true",
+ "try", "typeof", "var", "void",
+ "volatile", "while", "with", "yield"
+]
diff --git a/package.json b/package.json
index b12c22b53..1eb5e20a2 100644
--- a/package.json
+++ b/package.json
@@ -45,7 +45,7 @@
},
"dependencies": {
"requireindex": "^1.1.0",
- "vue-eslint-parser": "2.0.0-beta.7"
+ "vue-eslint-parser": "2.0.0-beta.10"
},
"devDependencies": {
"@types/node": "^4.2.16",
diff --git a/tests/lib/rules/no-this-in-template.js b/tests/lib/rules/no-this-in-template.js
deleted file mode 100644
index 991ef74ce..000000000
--- a/tests/lib/rules/no-this-in-template.js
+++ /dev/null
@@ -1,79 +0,0 @@
-/**
- * @fileoverview Disallow usage of `this` in template.
- * @author Armano
- */
-'use strict'
-
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
-const rule = require('../../../lib/rules/no-this-in-template')
-
-const RuleTester = require('eslint').RuleTester
-
-// ------------------------------------------------------------------------------
-// Tests
-// ------------------------------------------------------------------------------
-
-const ruleTester = new RuleTester({
- parser: 'vue-eslint-parser',
- parserOptions: { ecmaVersion: 2015 }
-})
-
-ruleTester.run('no-this-in-template', rule, {
- valid: [
- '',
- '',
- '',
- '{{ foo.bar }}
',
- '{{ foo }}
',
- '{{ foo }}
',
- '{{ foo }}
',
- '{{ foo }}
'
- ],
- invalid: [
- {
- code: '{{ this.foo }}
',
- errors: [{
- message: "Unexpected usage of 'this'.",
- type: 'ThisExpression'
- }]
- },
- {
- code: '',
- errors: [{
- message: "Unexpected usage of 'this'.",
- type: 'ThisExpression'
- }]
- },
- {
- code: '',
- errors: [{
- message: "Unexpected usage of 'this'.",
- type: 'ThisExpression'
- }]
- },
- {
- code: '',
- errors: [{
- message: "Unexpected usage of 'this'.",
- type: 'ThisExpression'
- }]
- },
- {
- code: '',
- errors: [{
- message: "Unexpected usage of 'this'.",
- type: 'ThisExpression'
- }]
- },
- {
- code: '',
- errors: [{
- message: "Unexpected usage of 'this'.",
- type: 'ThisExpression'
- }]
- }
- ]
-})
diff --git a/tests/lib/rules/this-in-template.js b/tests/lib/rules/this-in-template.js
new file mode 100644
index 000000000..a35c08f8e
--- /dev/null
+++ b/tests/lib/rules/this-in-template.js
@@ -0,0 +1,182 @@
+/**
+ * @fileoverview enforce usage of `this` in template.
+ * @author Armano
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/this-in-template')
+
+const RuleTester = require('eslint').RuleTester
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({
+ parser: 'vue-eslint-parser',
+ parserOptions: { ecmaVersion: 2015 }
+})
+
+function createValidTests (prefix, options) {
+ const comment = options.join('')
+ return [
+ {
+ code: `{{ ${prefix}foo.bar }}
`,
+ options
+ },
+ {
+ code: `{{ foo }}
`,
+ options
+ },
+ {
+ code: `{{ ${prefix}foo }}
`,
+ options
+ },
+ {
+ code: `{{ ${prefix}foo }}
`,
+ options
+ },
+ {
+ code: `{{ ${prefix}foo }}
`,
+ options
+ },
+ {
+ code: `{{ bar }}
`,
+ options
+ },
+ {
+ code: `{{ ${prefix}bar }}
`,
+ options
+ },
+ {
+ code: ``,
+ options
+ },
+ {
+ code: `{{this.x}}
`,
+ options
+ },
+ {
+ code: `{{this.x()}}
`,
+ options
+ },
+ {
+ code: `{{this.x.y()}}
`,
+ options
+ },
+ {
+ code: `{{this.x['foo']}}
`,
+ options
+ },
+ {
+ code: `{{this['x']}}
`,
+ options
+ },
+ {
+ code: `{{ this.class }}
`,
+ options
+ },
+ {
+ code: `{{ this['0'] }}
`,
+ options
+ },
+ {
+ code: `{{ this['this'] }}
`,
+ options
+ },
+ {
+ code: `{{ this['foo bar'] }}
`,
+ options
+ },
+ {
+ code: `{{ }}
`,
+ options
+ },
+ {
+ code: `
+
+ `,
+ options
+ }
+ ]
+}
+
+function createInvalidTests (prefix, options, message, type) {
+ const comment = options.join('')
+ return [
+ {
+ code: `{{ ${prefix}foo }}
`,
+ errors: [{ message, type }],
+ options
+ },
+ {
+ code: `{{ ${prefix}foo() }}
`,
+ errors: [{ message, type }],
+ options
+ },
+ {
+ code: `{{ ${prefix}foo.bar() }}
`,
+ errors: [{ message, type }],
+ options
+ },
+ {
+ code: ``,
+ errors: [{ message, type }],
+ options
+ },
+ {
+ code: ``,
+ errors: [{ message, type }],
+ options
+ },
+ {
+ code: ``,
+ errors: [{ message, type }],
+ options
+ },
+ {
+ code: ``,
+ errors: [{ message, type }],
+ options
+ },
+ {
+ code: ``,
+ errors: [{ message, type }],
+ options
+ }
+ ].concat(options[0] === 'always'
+ ? []
+ : [
+ {
+ code: `{{ this['xs'] }}
`,
+ errors: [{ message, type }],
+ options
+ },
+ {
+ code: `{{ this['xs0AZ_foo'] }}
`,
+ errors: [{ message, type }],
+ options
+ }
+ ]
+ )
+}
+
+ruleTester.run('this-in-template', rule, {
+ valid: ['', '', '']
+ .concat(createValidTests('', []))
+ .concat(createValidTests('', ['never']))
+ .concat(createValidTests('this.', ['always'])),
+ invalid: []
+ .concat(createInvalidTests('this.', [], "Unexpected usage of 'this'.", 'ThisExpression'))
+ .concat(createInvalidTests('this.', ['never'], "Unexpected usage of 'this'.", 'ThisExpression'))
+ .concat(createInvalidTests('', ['always'], "Expected 'this'.", 'Identifier'))
+})