get-convert-path.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. /**
  2. * @author Toru Nagashima
  3. * See LICENSE file in root directory for full license.
  4. */
  5. "use strict"
  6. const Minimatch = require("minimatch").Minimatch
  7. /**
  8. * @param {any} x - An any value.
  9. * @returns {any} Always `x`.
  10. */
  11. function identity(x) {
  12. return x
  13. }
  14. /**
  15. * Converts old-style value to new-style value.
  16. *
  17. * @param {any} x - The value to convert.
  18. * @returns {({include: string[], exclude: string[], replace: string[]})[]} Normalized value.
  19. */
  20. function normalizeValue(x) {
  21. if (Array.isArray(x)) {
  22. return x
  23. }
  24. return Object.keys(x).map(pattern => ({
  25. include: [pattern],
  26. exclude: [],
  27. replace: x[pattern],
  28. }))
  29. }
  30. /**
  31. * Ensures the given value is a string array.
  32. *
  33. * @param {any} x - The value to ensure.
  34. * @returns {string[]} The string array.
  35. */
  36. function toStringArray(x) {
  37. if (Array.isArray(x)) {
  38. return x.map(String)
  39. }
  40. return []
  41. }
  42. /**
  43. * Creates the function which checks whether a file path is matched with the given pattern or not.
  44. *
  45. * @param {string[]} includePatterns - The glob patterns to include files.
  46. * @param {string[]} excludePatterns - The glob patterns to exclude files.
  47. * @returns {function} Created predicate function.
  48. */
  49. function createMatch(includePatterns, excludePatterns) {
  50. const include = includePatterns.map(pattern => new Minimatch(pattern))
  51. const exclude = excludePatterns.map(pattern => new Minimatch(pattern))
  52. return filePath =>
  53. include.some(m => m.match(filePath)) &&
  54. !exclude.some(m => m.match(filePath))
  55. }
  56. /**
  57. * Creates a function which replaces a given path.
  58. *
  59. * @param {RegExp} fromRegexp - A `RegExp` object to replace.
  60. * @param {string} toStr - A new string to replace.
  61. * @returns {function} A function which replaces a given path.
  62. */
  63. function defineConvert(fromRegexp, toStr) {
  64. return filePath => filePath.replace(fromRegexp, toStr)
  65. }
  66. /**
  67. * Combines given converters.
  68. * The result function converts a given path with the first matched converter.
  69. *
  70. * @param {{match: function, convert: function}} converters - A list of converters to combine.
  71. * @returns {function} A function which replaces a given path.
  72. */
  73. function combine(converters) {
  74. return filePath => {
  75. for (const converter of converters) {
  76. if (converter.match(filePath)) {
  77. return converter.convert(filePath)
  78. }
  79. }
  80. return filePath
  81. }
  82. }
  83. /**
  84. * Parses `convertPath` property from a given option object.
  85. *
  86. * @param {object|undefined} option - An option object to get.
  87. * @returns {function|null} A function which converts a path., or `null`.
  88. */
  89. function parse(option) {
  90. if (
  91. !option ||
  92. !option.convertPath ||
  93. typeof option.convertPath !== "object"
  94. ) {
  95. return null
  96. }
  97. const converters = []
  98. for (const pattern of normalizeValue(option.convertPath)) {
  99. const include = toStringArray(pattern.include)
  100. const exclude = toStringArray(pattern.exclude)
  101. const fromRegexp = new RegExp(String(pattern.replace[0])) //eslint-disable-line require-unicode-regexp
  102. const toStr = String(pattern.replace[1])
  103. converters.push({
  104. match: createMatch(include, exclude),
  105. convert: defineConvert(fromRegexp, toStr),
  106. })
  107. }
  108. return combine(converters)
  109. }
  110. /**
  111. * Gets "convertPath" setting.
  112. *
  113. * 1. This checks `options` property, then returns it if exists.
  114. * 2. This checks `settings.node` property, then returns it if exists.
  115. * 3. This returns a function of identity.
  116. *
  117. * @param {RuleContext} context - The rule context.
  118. * @returns {function} A function which converts a path.
  119. */
  120. module.exports = function getConvertPath(context) {
  121. return (
  122. parse(context.options && context.options[0]) ||
  123. parse(context.settings && context.settings.node) ||
  124. identity
  125. )
  126. }
  127. /**
  128. * JSON Schema for `convertPath` option.
  129. */
  130. module.exports.schema = {
  131. anyOf: [
  132. {
  133. type: "object",
  134. properties: {},
  135. patternProperties: {
  136. "^.+$": {
  137. type: "array",
  138. items: { type: "string" },
  139. minItems: 2,
  140. maxItems: 2,
  141. },
  142. },
  143. additionalProperties: false,
  144. },
  145. {
  146. type: "array",
  147. items: {
  148. type: "object",
  149. properties: {
  150. include: {
  151. type: "array",
  152. items: { type: "string" },
  153. minItems: 1,
  154. uniqueItems: true,
  155. },
  156. exclude: {
  157. type: "array",
  158. items: { type: "string" },
  159. uniqueItems: true,
  160. },
  161. replace: {
  162. type: "array",
  163. items: { type: "string" },
  164. minItems: 2,
  165. maxItems: 2,
  166. },
  167. },
  168. additionalProperties: false,
  169. required: ["include", "replace"],
  170. },
  171. minItems: 1,
  172. },
  173. ],
  174. }