From 4c389839790a192d9ad424c9791cf92171ac2082 Mon Sep 17 00:00:00 2001 From: Yosuke Ota Date: Wed, 18 Sep 2024 10:44:36 +0900 Subject: [PATCH] Add support for props destructure to `vue/no-restricted-props` rule (#2562) --- lib/rules/no-restricted-props.js | 55 +++++++++++++++++++------- tests/lib/rules/no-restricted-props.js | 52 +++++++++++++++++++++++- 2 files changed, 92 insertions(+), 15 deletions(-) diff --git a/lib/rules/no-restricted-props.js b/lib/rules/no-restricted-props.js index 21f53cc7d..2d2f74bb0 100644 --- a/lib/rules/no-restricted-props.js +++ b/lib/rules/no-restricted-props.js @@ -95,9 +95,9 @@ module.exports = { /** * @param {ComponentProp[]} props - * @param { { [key: string]: Property | undefined } } [withDefaultsProps] + * @param {(fixer: RuleFixer, propName: string, replaceKeyText: string) => Iterable} [fixPropInOtherPlaces] */ - function processProps(props, withDefaultsProps) { + function processProps(props, fixPropInOtherPlaces) { for (const prop of props) { if (!prop.propName) { continue @@ -118,7 +118,14 @@ module.exports = { : createSuggest( prop.key, option, - withDefaultsProps && withDefaultsProps[prop.propName] + fixPropInOtherPlaces + ? (fixer, replaceKeyText) => + fixPropInOtherPlaces( + fixer, + prop.propName, + replaceKeyText + ) + : undefined ) }) break @@ -129,7 +136,33 @@ module.exports = { return utils.compositingVisitors( utils.defineScriptSetupVisitor(context, { onDefinePropsEnter(node, props) { - processProps(props, utils.getWithDefaultsProps(node)) + processProps(props, fixPropInOtherPlaces) + + /** + * @param {RuleFixer} fixer + * @param {string} propName + * @param {string} replaceKeyText + */ + function fixPropInOtherPlaces(fixer, propName, replaceKeyText) { + /** @type {(Property|AssignmentProperty)[]} */ + const propertyNodes = [] + const withDefault = utils.getWithDefaultsProps(node)[propName] + if (withDefault) { + propertyNodes.push(withDefault) + } + const propDestructure = utils.getPropsDestructure(node)[propName] + if (propDestructure) { + propertyNodes.push(propDestructure) + } + return propertyNodes.map((propertyNode) => + propertyNode.shorthand + ? fixer.insertTextBefore( + propertyNode.value, + `${replaceKeyText}:` + ) + : fixer.replaceText(propertyNode.key, replaceKeyText) + ) + } } }), utils.defineVueVisitor(context, { @@ -144,10 +177,10 @@ module.exports = { /** * @param {Expression} node * @param {ParsedOption} option - * @param {Property} [withDefault] + * @param {(fixer: RuleFixer, replaceKeyText: string) => Iterable} [fixPropInOtherPlaces] * @returns {Rule.SuggestionReportDescriptor[]} */ -function createSuggest(node, option, withDefault) { +function createSuggest(node, option, fixPropInOtherPlaces) { if (!option.suggest) { return [] } @@ -168,14 +201,8 @@ function createSuggest(node, option, withDefault) { { fix(fixer) { const fixes = [fixer.replaceText(node, replaceText)] - if (withDefault) { - if (withDefault.shorthand) { - fixes.push( - fixer.insertTextBefore(withDefault.value, `${replaceText}:`) - ) - } else { - fixes.push(fixer.replaceText(withDefault.key, replaceText)) - } + if (fixPropInOtherPlaces) { + fixes.push(...fixPropInOtherPlaces(fixer, replaceText)) } return fixes.sort((a, b) => a.range[0] - b.range[0]) }, diff --git a/tests/lib/rules/no-restricted-props.js b/tests/lib/rules/no-restricted-props.js index 2dc1bcf64..a91615e15 100644 --- a/tests/lib/rules/no-restricted-props.js +++ b/tests/lib/rules/no-restricted-props.js @@ -611,6 +611,56 @@ tester.run('no-restricted-props', rule, { } ] } - ]) + ]), + { + filename: 'test.vue', + code: ` + + `, + options: [{ name: 'foo', suggest: 'Foo' }], + errors: [ + { + message: 'Using `foo` props is not allowed.', + line: 3, + suggestions: [ + { + desc: 'Instead, change to `Foo`.', + output: ` + + ` + } + ] + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + options: [{ name: 'foo', suggest: 'Foo' }], + errors: [ + { + message: 'Using `foo` props is not allowed.', + line: 3, + suggestions: [ + { + desc: 'Instead, change to `Foo`.', + output: ` + + ` + } + ] + } + ] + } ] })