disabled-area.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. /**
  2. * @author Toru Nagashima <https://github.com/mysticatea>
  3. * See LICENSE file in root directory for full license.
  4. */
  5. "use strict"
  6. const utils = require("./utils")
  7. const DELIMITER = /[\s,]+/gu
  8. const pool = new WeakMap()
  9. module.exports = class DisabledArea {
  10. /**
  11. * Get singleton instance for the given source code.
  12. *
  13. * @param {eslint.SourceCode} sourceCode - The source code to get.
  14. * @returns {DisabledArea} The singleton object for the source code.
  15. */
  16. static get(sourceCode) {
  17. let retv = pool.get(sourceCode.ast)
  18. if (retv == null) {
  19. retv = new DisabledArea()
  20. retv._scan(sourceCode)
  21. pool.set(sourceCode.ast, retv)
  22. }
  23. return retv
  24. }
  25. /**
  26. * Constructor.
  27. */
  28. constructor() {
  29. this.areas = []
  30. this.duplicateDisableDirectives = []
  31. this.unusedEnableDirectives = []
  32. this.numberOfRelatedDisableDirectives = new Map()
  33. }
  34. /**
  35. * Make disabled area.
  36. *
  37. * @param {Token} comment - The comment token to disable.
  38. * @param {object} location - The start location to disable.
  39. * @param {string[]|null} ruleIds - The ruleId names to disable.
  40. * @param {string} kind - The kind of disable-comments.
  41. * @returns {void}
  42. * @private
  43. */
  44. _disable(comment, location, ruleIds, kind) {
  45. if (ruleIds) {
  46. for (const ruleId of ruleIds) {
  47. if (this._getArea(ruleId, location) != null) {
  48. this.duplicateDisableDirectives.push({ comment, ruleId })
  49. }
  50. this.areas.push({
  51. comment,
  52. ruleId,
  53. kind,
  54. start: location,
  55. end: null,
  56. })
  57. }
  58. } else {
  59. if (this._getArea(null, location) != null) {
  60. this.duplicateDisableDirectives.push({ comment, ruleId: null })
  61. }
  62. this.areas.push({
  63. comment,
  64. ruleId: null,
  65. kind,
  66. start: location,
  67. end: null,
  68. })
  69. }
  70. }
  71. /**
  72. * Close disabled area.
  73. *
  74. * @param {Token} comment - The comment token to enable.
  75. * @param {object} location - The start location to enable.
  76. * @param {string[]|null} ruleIds - The ruleId names to enable.
  77. * @param {string} kind - The kind of disable-comments.
  78. * @returns {void}
  79. * @private
  80. */
  81. _enable(comment, location, ruleIds, kind) {
  82. const relatedDisableDirectives = new Set()
  83. if (ruleIds) {
  84. for (const ruleId of ruleIds) {
  85. let used = false
  86. for (let i = this.areas.length - 1; i >= 0; --i) {
  87. const area = this.areas[i]
  88. if (
  89. area.end === null &&
  90. area.kind === kind &&
  91. area.ruleId === ruleId
  92. ) {
  93. relatedDisableDirectives.add(area.comment)
  94. area.end = location
  95. used = true
  96. }
  97. }
  98. if (!used) {
  99. this.unusedEnableDirectives.push({ comment, ruleId })
  100. }
  101. }
  102. } else {
  103. let used = false
  104. for (let i = this.areas.length - 1; i >= 0; --i) {
  105. const area = this.areas[i]
  106. if (area.end === null && area.kind === kind) {
  107. relatedDisableDirectives.add(area.comment)
  108. area.end = location
  109. used = true
  110. }
  111. }
  112. if (!used) {
  113. this.unusedEnableDirectives.push({ comment, ruleId: null })
  114. }
  115. }
  116. this.numberOfRelatedDisableDirectives.set(
  117. comment,
  118. relatedDisableDirectives.size
  119. )
  120. }
  121. /**
  122. * Gets the area of the given ruleId and location.
  123. *
  124. * @param {string|null} ruleId - The ruleId name to get.
  125. * @param {object} location - The location to get.
  126. * @returns {object|null} The area of the given ruleId and location.
  127. * @private
  128. */
  129. _getArea(ruleId, location) {
  130. for (let i = this.areas.length - 1; i >= 0; --i) {
  131. const area = this.areas[i]
  132. if (
  133. (area.ruleId === null || area.ruleId === ruleId) &&
  134. utils.lte(area.start, location) &&
  135. (area.end === null || utils.lte(location, area.end))
  136. ) {
  137. return area
  138. }
  139. }
  140. return null
  141. }
  142. /**
  143. * Scan the source code and setup disabled area list.
  144. *
  145. * @param {eslint.SourceCode} sourceCode - The source code to scan.
  146. * @returns {void}
  147. * @private
  148. */
  149. _scan(sourceCode) {
  150. for (const comment of sourceCode.getAllComments()) {
  151. const directiveComment = utils.parseDirectiveComment(comment)
  152. if (directiveComment == null) {
  153. continue
  154. }
  155. const kind = directiveComment.kind
  156. if (
  157. kind !== "eslint-disable" &&
  158. kind !== "eslint-enable" &&
  159. kind !== "eslint-disable-line" &&
  160. kind !== "eslint-disable-next-line"
  161. ) {
  162. continue
  163. }
  164. const ruleIds = directiveComment.value
  165. ? directiveComment.value.split(DELIMITER)
  166. : null
  167. if (kind === "eslint-disable") {
  168. this._disable(comment, comment.loc.start, ruleIds, "block")
  169. } else if (kind === "eslint-enable") {
  170. this._enable(comment, comment.loc.start, ruleIds, "block")
  171. } else if (kind === "eslint-disable-line") {
  172. const line = comment.loc.start.line
  173. const start = { line, column: 0 }
  174. const end = { line: line + 1, column: -1 }
  175. this._disable(comment, start, ruleIds, "line")
  176. this._enable(comment, end, ruleIds, "line")
  177. } else if (kind === "eslint-disable-next-line") {
  178. const line = comment.loc.start.line
  179. const start = { line: line + 1, column: 0 }
  180. const end = { line: line + 2, column: -1 }
  181. this._disable(comment, start, ruleIds, "line")
  182. this._enable(comment, end, ruleIds, "line")
  183. }
  184. }
  185. }
  186. }