prefer-string-replace-all.js 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. 'use strict';
  2. const quoteString = require('./utils/quote-string.js');
  3. const {methodCallSelector} = require('./selectors/index.js');
  4. const MESSAGE_ID = 'prefer-string-replace-all';
  5. const messages = {
  6. [MESSAGE_ID]: 'Prefer `String#replaceAll()` over `String#replace()`.',
  7. };
  8. const selector = methodCallSelector({
  9. method: 'replace',
  10. argumentsLength: 2,
  11. });
  12. function isRegexWithGlobalFlag(node) {
  13. const {type, regex} = node;
  14. if (type !== 'Literal' || !regex) {
  15. return false;
  16. }
  17. const {flags} = regex;
  18. return flags.replace('u', '') === 'g';
  19. }
  20. function isLiteralCharactersOnly(node) {
  21. const searchPattern = node.regex.pattern;
  22. return !/[$()*+.?[\\\]^{}]/.test(searchPattern.replace(/\\[$()*+.?[\\\]^{}]/g, ''));
  23. }
  24. function removeEscapeCharacters(regexString) {
  25. let fixedString = regexString;
  26. let index = 0;
  27. do {
  28. index = fixedString.indexOf('\\', index);
  29. if (index >= 0) {
  30. fixedString = fixedString.slice(0, index) + fixedString.slice(index + 1);
  31. index++;
  32. }
  33. } while (index >= 0);
  34. return fixedString;
  35. }
  36. /** @param {import('eslint').Rule.RuleContext} context */
  37. const create = () => ({
  38. [selector]: node => {
  39. const {arguments: arguments_, callee} = node;
  40. const [search] = arguments_;
  41. if (!isRegexWithGlobalFlag(search) || !isLiteralCharactersOnly(search)) {
  42. return;
  43. }
  44. return {
  45. node,
  46. messageId: MESSAGE_ID,
  47. fix: fixer => [
  48. fixer.insertTextAfter(callee, 'All'),
  49. fixer.replaceText(search, quoteString(removeEscapeCharacters(search.regex.pattern))),
  50. ],
  51. };
  52. },
  53. });
  54. /** @type {import('eslint').Rule.RuleModule} */
  55. module.exports = {
  56. create,
  57. meta: {
  58. type: 'suggestion',
  59. docs: {
  60. description: 'Prefer `String#replaceAll()` over regex searches with the global flag.',
  61. },
  62. fixable: 'code',
  63. messages,
  64. },
  65. };