matchesStringOrRegExp.js 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. 'use strict';
  2. /**
  3. * Compares a string to a second value that, if it fits a certain convention,
  4. * is converted to a regular expression before the comparison.
  5. * If it doesn't fit the convention, then two strings are compared.
  6. *
  7. * Any strings starting and ending with `/` are interpreted
  8. * as regular expressions.
  9. *
  10. * @param {string | Array<string>} input
  11. * @param {string | RegExp | Array<string | RegExp>} comparison
  12. *
  13. * @returns {false | {match: string, pattern: (string | RegExp), substring: string}}
  14. */
  15. module.exports = function matchesStringOrRegExp(input, comparison) {
  16. if (!Array.isArray(input)) {
  17. return testAgainstStringOrRegExpOrArray(input, comparison);
  18. }
  19. for (const inputItem of input) {
  20. const testResult = testAgainstStringOrRegExpOrArray(inputItem, comparison);
  21. if (testResult) {
  22. return testResult;
  23. }
  24. }
  25. return false;
  26. };
  27. /**
  28. * @param {string} value
  29. * @param {string | RegExp | Array<string | RegExp>} comparison
  30. */
  31. function testAgainstStringOrRegExpOrArray(value, comparison) {
  32. if (!Array.isArray(comparison)) {
  33. return testAgainstStringOrRegExp(value, comparison);
  34. }
  35. for (const comparisonItem of comparison) {
  36. const testResult = testAgainstStringOrRegExp(value, comparisonItem);
  37. if (testResult) {
  38. return testResult;
  39. }
  40. }
  41. return false;
  42. }
  43. /**
  44. * @param {string} value
  45. * @param {string | RegExp} comparison
  46. */
  47. function testAgainstStringOrRegExp(value, comparison) {
  48. // If it's a RegExp, test directly
  49. if (comparison instanceof RegExp) {
  50. const match = value.match(comparison);
  51. return match ? { match: value, pattern: comparison, substring: match[0] || '' } : false;
  52. }
  53. // Check if it's RegExp in a string
  54. const firstComparisonChar = comparison[0];
  55. const lastComparisonChar = comparison[comparison.length - 1];
  56. const secondToLastComparisonChar = comparison[comparison.length - 2];
  57. const comparisonIsRegex =
  58. firstComparisonChar === '/' &&
  59. (lastComparisonChar === '/' ||
  60. (secondToLastComparisonChar === '/' && lastComparisonChar === 'i'));
  61. const hasCaseInsensitiveFlag = comparisonIsRegex && lastComparisonChar === 'i';
  62. // If so, create a new RegExp from it
  63. if (comparisonIsRegex) {
  64. const valueMatch = hasCaseInsensitiveFlag
  65. ? value.match(new RegExp(comparison.slice(1, -2), 'i'))
  66. : value.match(new RegExp(comparison.slice(1, -1)));
  67. return valueMatch
  68. ? { match: value, pattern: comparison, substring: valueMatch[0] || '' }
  69. : false;
  70. }
  71. // Otherwise, it's a string. Do a strict comparison
  72. return value === comparison ? { match: value, pattern: comparison, substring: value } : false;
  73. }