From e8fba6a856f594832ddbf1cebc6ef549646204b6 Mon Sep 17 00:00:00 2001 From: ota Date: Tue, 5 Feb 2019 17:17:17 +0900 Subject: [PATCH 1/4] =?UTF-8?q?=E2=AD=90=EF=B8=8FNew:=20Add=20vue/no-depre?= =?UTF-8?q?cated-slot-scope-attribute=20rule?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/rules/README.md | 1 + .../no-deprecated-slot-scope-attribute.md | 44 ++++++ lib/index.js | 1 + .../no-deprecated-slot-scope-attribute.js | 28 ++++ lib/rules/syntaxes/slot-scope-attribute.js | 60 ++++++++ .../no-deprecated-slot-scope-attribute.js | 128 ++++++++++++++++++ 6 files changed, 262 insertions(+) create mode 100644 docs/rules/no-deprecated-slot-scope-attribute.md create mode 100644 lib/rules/no-deprecated-slot-scope-attribute.js create mode 100644 lib/rules/syntaxes/slot-scope-attribute.js create mode 100644 tests/lib/rules/no-deprecated-slot-scope-attribute.js diff --git a/docs/rules/README.md b/docs/rules/README.md index 97117319f..f53cc9956 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -153,6 +153,7 @@ For example: | [vue/match-component-file-name](./match-component-file-name.md) | require component name property to match its file name | | | [vue/no-boolean-default](./no-boolean-default.md) | disallow boolean defaults | :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-restricted-syntax](./no-restricted-syntax.md) | disallow specified syntax | | | [vue/object-curly-spacing](./object-curly-spacing.md) | enforce consistent spacing inside braces | :wrench: | 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 6c727f241..0533af811 100644 --- a/lib/index.js +++ b/lib/index.js @@ -37,6 +37,7 @@ module.exports = { 'no-boolean-default': require('./rules/no-boolean-default'), 'no-confusing-v-for-v-if': require('./rules/no-confusing-v-for-v-if'), '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..0182b387f --- /dev/null +++ b/lib/rules/syntaxes/slot-scope-attribute.js @@ -0,0 +1,60 @@ +/** + * @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() + /** + * Convert to `v-slot`. + * @param {object} fixer fixer + * @param {VAttribute | null} slotAttr node of `slot` + * @param {VAttribute | null} scopeAttr node of `slot-scope` + * @returns {*} fix data + */ + function fixSlotToVSlot (fixer, slotAttr, scopeAttr) { + const nameArgument = slotAttr && slotAttr.value && slotAttr && slotAttr.value.value + ? `:${slotAttr.value.value}` + : '' + const scopeValue = scopeAttr && scopeAttr.value + ? `=${sourceCode.getText(scopeAttr.value)}` + : '' + + const replaceText = `v-slot${nameArgument}${scopeValue}` + const fixers = [ + fixer.replaceText(slotAttr || scopeAttr, replaceText) + ] + if (slotAttr && scopeAttr) { + fixers.push(fixer.remove(scopeAttr)) + } + return fixers + } + /** + * 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 element = scopeAttr.parent + const slotAttr = element.attributes + .find(attr => attr.directive === false && attr.key.name === 'slot') + return fixSlotToVSlot(fixer, slotAttr, 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..f607064f5 --- /dev/null +++ b/tests/lib/rules/no-deprecated-slot-scope-attribute.js @@ -0,0 +1,128 @@ +'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: [ + ``, + ``, + ``, + ``, + ``, + `` + ], + invalid: [ + { + code: ` + `, + output: ` + `, + errors: [ + { + message: '`slot-scope` are deprecated.', + line: 4 + } + ] + }, + { + code: ` + `, + output: ` + `, + errors: [ + { + message: '`slot-scope` are deprecated.', + line: 4 + } + ] + }, + { + code: ` + `, + output: ` + `, + errors: [ + { + message: '`slot-scope` are deprecated.', + line: 4 + } + ] + }, + { + code: ` + `, + output: ` + `, + errors: [ + { + message: '`slot-scope` are deprecated.', + line: 4 + } + ] + } + ] +}) From 31bbfafa54c7d8fbfddd8d9133f0a4790049c023 Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Thu, 28 Feb 2019 18:36:06 +0900 Subject: [PATCH 2/4] Changed not to autofix, if it becomes an invalid attribute --- lib/rules/syntaxes/slot-scope-attribute.js | 20 ++++++++++++++++++- .../no-deprecated-slot-scope-attribute.js | 16 +++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/lib/rules/syntaxes/slot-scope-attribute.js b/lib/rules/syntaxes/slot-scope-attribute.js index 0182b387f..92dddea72 100644 --- a/lib/rules/syntaxes/slot-scope-attribute.js +++ b/lib/rules/syntaxes/slot-scope-attribute.js @@ -8,6 +8,21 @@ module.exports = { supported: '2.5.0', createTemplateBodyVisitor (context, { fixToUpgrade } = {}) { const sourceCode = context.getSourceCode() + + /** + * Checks whether the given node can convert to the `v-slot`. + * @param {VAttribute | null} slotAttr node of `slot` + * @returns {boolean} `true` if the given node can convert to the `v-slot` + */ + function canConvertToVSlot (slotAttr) { + if (!slotAttr || !slotAttr.value) { + return true + } + const slotName = slotAttr.value.value + // If non-Latin characters are included it can not be converted. + return !/[^a-z]/i.test(slotName) + } + /** * Convert to `v-slot`. * @param {object} fixer fixer @@ -16,7 +31,7 @@ module.exports = { * @returns {*} fix data */ function fixSlotToVSlot (fixer, slotAttr, scopeAttr) { - const nameArgument = slotAttr && slotAttr.value && slotAttr && slotAttr.value.value + const nameArgument = slotAttr && slotAttr.value && slotAttr.value.value ? `:${slotAttr.value.value}` : '' const scopeValue = scopeAttr && scopeAttr.value @@ -47,6 +62,9 @@ module.exports = { const element = scopeAttr.parent const slotAttr = element.attributes .find(attr => attr.directive === false && attr.key.name === 'slot') + if (!canConvertToVSlot(slotAttr)) { + return null + } return fixSlotToVSlot(fixer, slotAttr, scopeAttr) } : null diff --git a/tests/lib/rules/no-deprecated-slot-scope-attribute.js b/tests/lib/rules/no-deprecated-slot-scope-attribute.js index f607064f5..cbd77c35a 100644 --- a/tests/lib/rules/no-deprecated-slot-scope-attribute.js +++ b/tests/lib/rules/no-deprecated-slot-scope-attribute.js @@ -123,6 +123,22 @@ tester.run('no-deprecated-slot-scope-attribute', rule, { line: 4 } ] + }, + // cannot fix + { + code: ` + `, + output: null, + errors: [ + { + message: '`slot-scope` are deprecated.', + line: 4 + } + ] } ] }) From a0ca7dd49a42a1c35763c79574c756dcd66eff7f Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Thu, 28 Feb 2019 22:37:42 +0900 Subject: [PATCH 3/4] Changed to not autofix, if if the element have `v-bind:slot` --- lib/rules/syntaxes/slot-scope-attribute.js | 27 ++++++++++++++----- .../no-deprecated-slot-scope-attribute.js | 15 +++++++++++ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/lib/rules/syntaxes/slot-scope-attribute.js b/lib/rules/syntaxes/slot-scope-attribute.js index 92dddea72..87aa19d11 100644 --- a/lib/rules/syntaxes/slot-scope-attribute.js +++ b/lib/rules/syntaxes/slot-scope-attribute.js @@ -12,15 +12,28 @@ module.exports = { /** * Checks whether the given node can convert to the `v-slot`. * @param {VAttribute | null} slotAttr node of `slot` + * @param {VElement} slotAttr node of `slot` * @returns {boolean} `true` if the given node can convert to the `v-slot` */ - function canConvertToVSlot (slotAttr) { - if (!slotAttr || !slotAttr.value) { - return true + function canConvertToVSlot (slotAttr, element) { + if (slotAttr) { + if (!slotAttr.value) { + return true + } + const slotName = slotAttr.value.value + // If non-Latin characters are included it can not be converted. + return !/[^a-z]/i.test(slotName) } - const slotName = slotAttr.value.value - // If non-Latin characters are included it can not be converted. - return !/[^a-z]/i.test(slotName) + + const vBindSlotAttr = element.attributes + .find(attr => + attr.directive === true && + attr.key.name.name === 'bind' && + attr.key.argument && + attr.key.argument.name === 'slot') + // 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 !vBindSlotAttr } /** @@ -62,7 +75,7 @@ module.exports = { const element = scopeAttr.parent const slotAttr = element.attributes .find(attr => attr.directive === false && attr.key.name === 'slot') - if (!canConvertToVSlot(slotAttr)) { + if (!canConvertToVSlot(slotAttr, element)) { return null } return fixSlotToVSlot(fixer, slotAttr, scopeAttr) diff --git a/tests/lib/rules/no-deprecated-slot-scope-attribute.js b/tests/lib/rules/no-deprecated-slot-scope-attribute.js index cbd77c35a..ab9a610ba 100644 --- a/tests/lib/rules/no-deprecated-slot-scope-attribute.js +++ b/tests/lib/rules/no-deprecated-slot-scope-attribute.js @@ -139,6 +139,21 @@ tester.run('no-deprecated-slot-scope-attribute', rule, { line: 4 } ] + }, + { + code: ` + `, + output: null, + errors: [ + { + message: '`slot-scope` are deprecated.', + line: 4 + } + ] } ] }) From c9afae4487b75ad32c1f4400f413ae883f8ca012 Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Tue, 2 Apr 2019 10:54:21 +0900 Subject: [PATCH 4/4] Change to autofix --- lib/rules/syntaxes/slot-scope-attribute.js | 55 +++++++--------- .../no-deprecated-slot-scope-attribute.js | 62 +++++++------------ 2 files changed, 47 insertions(+), 70 deletions(-) diff --git a/lib/rules/syntaxes/slot-scope-attribute.js b/lib/rules/syntaxes/slot-scope-attribute.js index 87aa19d11..8e8464170 100644 --- a/lib/rules/syntaxes/slot-scope-attribute.js +++ b/lib/rules/syntaxes/slot-scope-attribute.js @@ -11,54 +11,49 @@ module.exports = { /** * Checks whether the given node can convert to the `v-slot`. - * @param {VAttribute | null} slotAttr node of `slot` - * @param {VElement} slotAttr node of `slot` + * @param {VStartTag} startTag node of `` * @returns {boolean} `true` if the given node can convert to the `v-slot` */ - function canConvertToVSlot (slotAttr, element) { + 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 (!slotAttr.value) { - return true - } - const slotName = slotAttr.value.value - // If non-Latin characters are included it can not be converted. - return !/[^a-z]/i.test(slotName) + // 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 = element.attributes + const vBindSlotAttr = startTag.attributes .find(attr => attr.directive === true && attr.key.name.name === 'bind' && attr.key.argument && attr.key.argument.name === 'slot') - // 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 !vBindSlotAttr + 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} slotAttr node of `slot` * @param {VAttribute | null} scopeAttr node of `slot-scope` * @returns {*} fix data */ - function fixSlotToVSlot (fixer, slotAttr, scopeAttr) { - const nameArgument = slotAttr && slotAttr.value && slotAttr.value.value - ? `:${slotAttr.value.value}` - : '' + function fixSlotScopeToVSlot (fixer, scopeAttr) { const scopeValue = scopeAttr && scopeAttr.value ? `=${sourceCode.getText(scopeAttr.value)}` : '' - const replaceText = `v-slot${nameArgument}${scopeValue}` - const fixers = [ - fixer.replaceText(slotAttr || scopeAttr, replaceText) - ] - if (slotAttr && scopeAttr) { - fixers.push(fixer.remove(scopeAttr)) - } - return fixers + const replaceText = `v-slot${scopeValue}` + return fixer.replaceText(scopeAttr, replaceText) } /** * Reports `slot-scope` node @@ -72,13 +67,11 @@ module.exports = { fix: fixToUpgrade // fix to use `v-slot` ? (fixer) => { - const element = scopeAttr.parent - const slotAttr = element.attributes - .find(attr => attr.directive === false && attr.key.name === 'slot') - if (!canConvertToVSlot(slotAttr, element)) { + const startTag = scopeAttr.parent + if (!canConvertToVSlot(startTag)) { return null } - return fixSlotToVSlot(fixer, slotAttr, scopeAttr) + return fixSlotScopeToVSlot(fixer, scopeAttr) } : null }) diff --git a/tests/lib/rules/no-deprecated-slot-scope-attribute.js b/tests/lib/rules/no-deprecated-slot-scope-attribute.js index ab9a610ba..ba5ce74f1 100644 --- a/tests/lib/rules/no-deprecated-slot-scope-attribute.js +++ b/tests/lib/rules/no-deprecated-slot-scope-attribute.js @@ -14,22 +14,27 @@ tester.run('no-deprecated-slot-scope-attribute', rule, { valid: [ ``, ``, ``, + ``, ``, ``, `` + `, + `