prefer-dom-node-remove.js 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. 'use strict';
  2. const {isParenthesized, hasSideEffect} = require('@eslint-community/eslint-utils');
  3. const {methodCallSelector, notDomNodeSelector} = require('./selectors/index.js');
  4. const needsSemicolon = require('./utils/needs-semicolon.js');
  5. const isValueNotUsable = require('./utils/is-value-not-usable.js');
  6. const {getParenthesizedText} = require('./utils/parentheses.js');
  7. const shouldAddParenthesesToMemberExpressionObject = require('./utils/should-add-parentheses-to-member-expression-object.js');
  8. const ERROR_MESSAGE_ID = 'error';
  9. const SUGGESTION_MESSAGE_ID = 'suggestion';
  10. const messages = {
  11. [ERROR_MESSAGE_ID]: 'Prefer `childNode.remove()` over `parentNode.removeChild(childNode)`.',
  12. [SUGGESTION_MESSAGE_ID]: 'Replace `parentNode.removeChild(childNode)` with `childNode.remove()`.',
  13. };
  14. const selector = [
  15. methodCallSelector({
  16. method: 'removeChild',
  17. argumentsLength: 1,
  18. }),
  19. notDomNodeSelector('callee.object'),
  20. notDomNodeSelector('arguments.0'),
  21. ].join('');
  22. /** @param {import('eslint').Rule.RuleContext} context */
  23. const create = context => {
  24. const sourceCode = context.getSourceCode();
  25. return {
  26. [selector](node) {
  27. const parentNode = node.callee.object;
  28. const childNode = node.arguments[0];
  29. const problem = {
  30. node,
  31. messageId: ERROR_MESSAGE_ID,
  32. };
  33. const fix = fixer => {
  34. let childNodeText = getParenthesizedText(childNode, sourceCode);
  35. if (
  36. !isParenthesized(childNode, sourceCode)
  37. && shouldAddParenthesesToMemberExpressionObject(childNode, sourceCode)
  38. ) {
  39. childNodeText = `(${childNodeText})`;
  40. }
  41. if (needsSemicolon(sourceCode.getTokenBefore(node), sourceCode, childNodeText)) {
  42. childNodeText = `;${childNodeText}`;
  43. }
  44. return fixer.replaceText(node, `${childNodeText}.remove()`);
  45. };
  46. if (!hasSideEffect(parentNode, sourceCode) && isValueNotUsable(node)) {
  47. problem.fix = fix;
  48. } else {
  49. problem.suggest = [
  50. {
  51. messageId: SUGGESTION_MESSAGE_ID,
  52. fix,
  53. },
  54. ];
  55. }
  56. return problem;
  57. },
  58. };
  59. };
  60. /** @type {import('eslint').Rule.RuleModule} */
  61. module.exports = {
  62. create,
  63. meta: {
  64. type: 'suggestion',
  65. docs: {
  66. description: 'Prefer `childNode.remove()` over `parentNode.removeChild(childNode)`.',
  67. },
  68. fixable: 'code',
  69. hasSuggestions: true,
  70. messages,
  71. },
  72. };