get-convert-path.js 5.7 KB

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