From 443999b63e30263f8a6230363bf3455c904b3ae2 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Fri, 7 Apr 2023 22:19:25 +0800 Subject: [PATCH] `prefer-dom-node-{append,remove}`: Check optional chaining (#2061) --- rules/prefer-dom-node-append.js | 1 + rules/prefer-dom-node-remove.js | 3 ++- rules/utils/is-value-not-usable.js | 7 ++++++- test/prefer-dom-node-append.mjs | 11 +++++++++++ test/prefer-dom-node-remove.mjs | 7 +++++++ 5 files changed, 27 insertions(+), 2 deletions(-) diff --git a/rules/prefer-dom-node-append.js b/rules/prefer-dom-node-append.js index 7aeb5287c5..cbce3b8441 100644 --- a/rules/prefer-dom-node-append.js +++ b/rules/prefer-dom-node-append.js @@ -10,6 +10,7 @@ const selector = [ methodCallSelector({ method: 'appendChild', argumentsLength: 1, + includeOptionalMember: true, }), notDomNodeSelector('callee.object'), notDomNodeSelector('arguments.0'), diff --git a/rules/prefer-dom-node-remove.js b/rules/prefer-dom-node-remove.js index 1809f4016e..a199ebbe42 100644 --- a/rules/prefer-dom-node-remove.js +++ b/rules/prefer-dom-node-remove.js @@ -17,6 +17,7 @@ const selector = [ methodCallSelector({ method: 'removeChild', argumentsLength: 1, + includeOptionalMember: true, }), notDomNodeSelector('callee.object'), notDomNodeSelector('arguments.0'), @@ -52,7 +53,7 @@ const create = context => { return fixer.replaceText(node, `${childNodeText}.remove()`); }; - if (!hasSideEffect(parentNode, sourceCode) && isValueNotUsable(node)) { + if (!hasSideEffect(parentNode, sourceCode) && isValueNotUsable(node) && !node.callee.optional) { problem.fix = fix; } else { problem.suggest = [ diff --git a/rules/utils/is-value-not-usable.js b/rules/utils/is-value-not-usable.js index fe03b215cc..c8e24733dc 100644 --- a/rules/utils/is-value-not-usable.js +++ b/rules/utils/is-value-not-usable.js @@ -1,3 +1,8 @@ 'use strict'; -module.exports = ({parent}) => !parent || parent.type === 'ExpressionStatement'; +module.exports = node => + node.parent.type === 'ExpressionStatement' + || ( + node.parent.type === 'ChainExpression' + && node.parent.parent.type === 'ExpressionStatement' + ); diff --git a/test/prefer-dom-node-append.mjs b/test/prefer-dom-node-append.mjs index 2281754514..ad307f13d1 100644 --- a/test/prefer-dom-node-append.mjs +++ b/test/prefer-dom-node-append.mjs @@ -26,6 +26,8 @@ test({ 'parent.appendChild(one, two);', 'parent.appendChild();', 'parent.appendChild(...argumentsArray)', + // Optional call + 'parent.appendChild?.(child)', // `callee.object` is not a DOM Node, ...notDomNodeTypes.map(data => `(${data}).appendChild(foo)`), // First argument is not a DOM Node, @@ -117,5 +119,14 @@ test({ code: 'foo(bar = node.appendChild(child))', errors: [error], }, + { + code: 'node?.appendChild(child);', + output: 'node?.append(child);', + errors: [error], + }, + { + code: '() => node?.appendChild(child)', + errors: [error], + }, ], }); diff --git a/test/prefer-dom-node-remove.mjs b/test/prefer-dom-node-remove.mjs index 9833669ad6..81806495e5 100644 --- a/test/prefer-dom-node-remove.mjs +++ b/test/prefer-dom-node-remove.mjs @@ -59,6 +59,8 @@ test({ 'parentNode.removeChild(bar, extra);', 'parentNode.removeChild();', 'parentNode.removeChild(...argumentsArray)', + // Optional call + 'parentNode.removeChild?.(foo)', // `callee.object` is not a DOM Node, ...notDomNodeTypes.map(data => `(${data}).removeChild(foo)`), @@ -245,5 +247,10 @@ test({ code: 'foo[doSomething()].removeChild(child)', suggestionOutput: 'child.remove()', }, + // Optional parent + { + code: 'parentNode?.removeChild(foo)', + suggestionOutput: 'foo.remove()', + }, ].map(options => invalidTestCase(options)), });