diff --git a/docs/rules/README.md b/docs/rules/README.md index 8ea5ef8ba..addfcb28c 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -155,6 +155,7 @@ For example: | [vue/no-boolean-default](./no-boolean-default.md) | disallow boolean defaults | :wrench: | | [vue/no-deprecated-slot-attribute](./no-deprecated-slot-attribute.md) | disallow deprecated `slot` attribute (in Vue.js 2.6.0+) | :wrench: | | [vue/no-deprecated-scope-attribute](./no-deprecated-scope-attribute.md) | disallow deprecated `scope` attribute (in Vue.js 2.5.0+) | :wrench: | +| [vue/no-deprecated-slot-scope-attribute](./no-deprecated-slot-scope-attribute.md) | disallow deprecated `slot-scope` attribute (in Vue.js 2.6.0+) | :wrench: | | [vue/no-empty-pattern](./no-empty-pattern.md) | disallow empty destructuring patterns | | | [vue/no-reserved-component-names](./no-reserved-component-names.md) | disallow the use of reserved names in component definitions | | | [vue/no-restricted-syntax](./no-restricted-syntax.md) | disallow specified syntax | | diff --git a/docs/rules/no-deprecated-slot-scope-attribute.md b/docs/rules/no-deprecated-slot-scope-attribute.md new file mode 100644 index 000000000..2282a361c --- /dev/null +++ b/docs/rules/no-deprecated-slot-scope-attribute.md @@ -0,0 +1,44 @@ +--- +pageClass: rule-details +sidebarDepth: 0 +title: vue/no-deprecated-slot-scope-attribute +description: disallow deprecated `slot-scope` attribute (in Vue.js 2.6.0+) +--- +# vue/no-deprecated-slot-scope-attribute +> disallow deprecated `slot-scope` attribute (in Vue.js 2.6.0+) + +- :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. + +## :book: Rule Details + +This rule reports deprecated `slot-scope` attribute in Vue.js v2.6.0+. + + + +```vue + +``` + + + +## :books: Further reading + +- [API - slot-scope](https://vuejs.org/v2/api/#slot-scope-deprecated) + +## :mag: Implementation + +- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-deprecated-slot-scope-attribute.js) +- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-deprecated-slot-scope-attribute.js) diff --git a/lib/index.js b/lib/index.js index 32ea7296e..3fc0f2c92 100644 --- a/lib/index.js +++ b/lib/index.js @@ -39,6 +39,7 @@ module.exports = { 'no-confusing-v-for-v-if': require('./rules/no-confusing-v-for-v-if'), 'no-deprecated-slot-attribute': require('./rules/no-deprecated-slot-attribute'), 'no-deprecated-scope-attribute': require('./rules/no-deprecated-scope-attribute'), + 'no-deprecated-slot-scope-attribute': require('./rules/no-deprecated-slot-scope-attribute'), 'no-dupe-keys': require('./rules/no-dupe-keys'), 'no-duplicate-attributes': require('./rules/no-duplicate-attributes'), 'no-empty-pattern': require('./rules/no-empty-pattern'), diff --git a/lib/rules/no-deprecated-slot-scope-attribute.js b/lib/rules/no-deprecated-slot-scope-attribute.js new file mode 100644 index 000000000..97d9963bd --- /dev/null +++ b/lib/rules/no-deprecated-slot-scope-attribute.js @@ -0,0 +1,28 @@ +/** + * @author Yosuke Ota + * See LICENSE file in root directory for full license. + */ +'use strict' + +const utils = require('../utils') +const slotScopeAttribute = require('./syntaxes/slot-scope-attribute') + +module.exports = { + meta: { + type: 'suggestion', + docs: { + description: 'disallow deprecated `slot-scope` attribute (in Vue.js 2.6.0+)', + category: undefined, + url: 'https://eslint.vuejs.org/rules/no-deprecated-slot-scope-attribute.html' + }, + fixable: 'code', + schema: [], + messages: { + forbiddenSlotScopeAttribute: '`slot-scope` are deprecated.' + } + }, + create (context) { + const templateBodyVisitor = slotScopeAttribute.createTemplateBodyVisitor(context, { fixToUpgrade: true }) + return utils.defineTemplateBodyVisitor(context, templateBodyVisitor) + } +} diff --git a/lib/rules/syntaxes/slot-scope-attribute.js b/lib/rules/syntaxes/slot-scope-attribute.js new file mode 100644 index 000000000..8e8464170 --- /dev/null +++ b/lib/rules/syntaxes/slot-scope-attribute.js @@ -0,0 +1,84 @@ +/** + * @author Yosuke Ota + * See LICENSE file in root directory for full license. + */ +'use strict' +module.exports = { + deprecated: '2.6.0', + supported: '2.5.0', + createTemplateBodyVisitor (context, { fixToUpgrade } = {}) { + const sourceCode = context.getSourceCode() + + /** + * Checks whether the given node can convert to the `v-slot`. + * @param {VStartTag} startTag node of `` + * @returns {boolean} `true` if the given node can convert to the `v-slot` + */ + function canConvertToVSlot (startTag) { + if (startTag.parent.name !== 'template') { + return false + } + + const slotAttr = startTag.attributes + .find(attr => attr.directive === false && attr.key.name === 'slot') + if (slotAttr) { + // if the element have `slot` it can not be converted. + // Conversion of `slot` is done with `vue/no-deprecated-slot-attribute`. + return false + } + + const vBindSlotAttr = startTag.attributes + .find(attr => + attr.directive === true && + attr.key.name.name === 'bind' && + attr.key.argument && + attr.key.argument.name === 'slot') + if (vBindSlotAttr) { + // if the element have `v-bind:slot` it can not be converted. + // Conversion of `v-bind:slot` is done with `vue/no-deprecated-slot-attribute`. + return false + } + return true + } + + /** + * Convert to `v-slot`. + * @param {object} fixer fixer + * @param {VAttribute | null} scopeAttr node of `slot-scope` + * @returns {*} fix data + */ + function fixSlotScopeToVSlot (fixer, scopeAttr) { + const scopeValue = scopeAttr && scopeAttr.value + ? `=${sourceCode.getText(scopeAttr.value)}` + : '' + + const replaceText = `v-slot${scopeValue}` + return fixer.replaceText(scopeAttr, replaceText) + } + /** + * Reports `slot-scope` node + * @param {VAttribute} scopeAttr node of `slot-scope` + * @returns {void} + */ + function reportSlotScope (scopeAttr) { + context.report({ + node: scopeAttr.key, + messageId: 'forbiddenSlotScopeAttribute', + fix: fixToUpgrade + // fix to use `v-slot` + ? (fixer) => { + const startTag = scopeAttr.parent + if (!canConvertToVSlot(startTag)) { + return null + } + return fixSlotScopeToVSlot(fixer, scopeAttr) + } + : null + }) + } + + return { + "VAttribute[directive=true][key.name.name='slot-scope']": reportSlotScope + } + } +} diff --git a/tests/lib/rules/no-deprecated-slot-scope-attribute.js b/tests/lib/rules/no-deprecated-slot-scope-attribute.js new file mode 100644 index 000000000..ba5ce74f1 --- /dev/null +++ b/tests/lib/rules/no-deprecated-slot-scope-attribute.js @@ -0,0 +1,143 @@ +'use strict' + +const RuleTester = require('eslint').RuleTester +const rule = require('../../../lib/rules/no-deprecated-slot-scope-attribute') + +const tester = new RuleTester({ + parser: 'vue-eslint-parser', + parserOptions: { + ecmaVersion: 2015 + } +}) + +tester.run('no-deprecated-slot-scope-attribute', rule, { + valid: [ + ``, + ``, + ``, + ``, + ``, + ``, + ``, + `