notes/node_modules/stylelint/lib/assignDisabledRanges.js

182 lines
5.7 KiB
JavaScript

/* @flow */
"use strict"
const _ = require("lodash")
const COMMAND_PREFIX = "stylelint-"
const disableCommand = COMMAND_PREFIX + "disable"
const enableCommand = COMMAND_PREFIX + "enable"
const disableLineCommand = COMMAND_PREFIX + "disable-line"
const disableNextLineCommand = COMMAND_PREFIX + "disable-next-line"
const ALL_RULES = "all"
/*:: type disabledRangeObject = {
[ruleName: string]: Array<{
start: number,
end?: number,
}>
}*/
// Run it like a plugin ...
module.exports = function (
root/*: Object*/,
result/*: Object*/
) {
result.stylelint = result.stylelint || {}
// Most of the functions below work via side effects mutating
// this object
const disabledRanges/*: disabledRangeObject*/ = {
all: [],
}
result.stylelint.disabledRanges = disabledRanges
root.walkComments(checkComment)
return result
function processDisableLineCommand(comment/*: postcss$comment*/) {
getCommandRules(disableLineCommand, comment.text).forEach(ruleName => {
disableLine(comment.source.start.line, ruleName, comment)
})
}
function processDisableNextLineCommand(comment/*: postcss$comment*/) {
getCommandRules(disableNextLineCommand, comment.text).forEach(ruleName => {
disableLine(comment.source.start.line + 1, ruleName, comment)
})
}
function disableLine(
line/*: number*/,
ruleName/*: string*/,
comment/*: postcss$comment*/
) {
if (ruleIsDisabled(ALL_RULES)) {
throw comment.error("All rules have already been disabled", { plugin: "stylelint" })
}
if (ruleIsDisabled(ruleName)) {
throw comment.error(`"${ruleName}" has already been disabled`, { plugin: "stylelint" })
}
if (ruleName === ALL_RULES) {
Object.keys(disabledRanges).forEach(disabledRuleName => {
startDisabledRange(line, disabledRuleName)
endDisabledRange(line, disabledRuleName)
})
} else {
startDisabledRange(line, ruleName)
endDisabledRange(line, ruleName)
}
}
function processDisableCommand(comment/*: postcss$comment*/) {
getCommandRules(disableCommand, comment.text).forEach(ruleToDisable => {
if (ruleToDisable === ALL_RULES) {
if (ruleIsDisabled(ALL_RULES)) {
throw comment.error("All rules have already been disabled", { plugin: "stylelint" })
}
Object.keys(disabledRanges).forEach(ruleName => {
startDisabledRange(comment.source.start.line, ruleName)
})
return
}
if (ruleIsDisabled(ruleToDisable)) {
throw comment.error(`"${ruleToDisable}" has already been disabled`, { plugin: "stylelint" })
}
startDisabledRange(comment.source.start.line, ruleToDisable)
})
}
function processEnableCommand(comment/*: postcss$comment*/) {
getCommandRules(enableCommand, comment.text).forEach(ruleToEnable => {
if (ruleToEnable === ALL_RULES) {
if (_.values(disabledRanges).every(ranges => _.isEmpty(ranges) || !!_.last(ranges.end))) {
throw comment.error("No rules have been disabled", { plugin: "stylelint" })
}
Object.keys(disabledRanges).forEach(ruleName => {
if (!_.get(_.last(disabledRanges[ruleName]), "end")) {
endDisabledRange(comment.source.end.line, ruleName)
}
})
return
}
if (ruleIsDisabled(ALL_RULES) && disabledRanges[ruleToEnable] === undefined) {
// Get a starting point from the where all rules were disabled
if (!disabledRanges[ruleToEnable]) {
disabledRanges[ruleToEnable] = _.cloneDeep(disabledRanges.all)
} else {
disabledRanges[ruleToEnable].push(_.clone(_.last(disabledRanges[ALL_RULES])))
}
endDisabledRange(comment.source.end.line, ruleToEnable)
return
}
if (ruleIsDisabled(ruleToEnable)) {
endDisabledRange(comment.source.end.line, ruleToEnable)
return
}
throw comment.error(`"${ruleToEnable}" has not been disabled`, { plugin: "stylelint" })
})
}
function checkComment(comment/*: postcss$comment*/) {
const text = comment.text
// Ignore comments that are not relevant commands
if (text.indexOf(COMMAND_PREFIX) !== 0) {
return result
}
if (text.indexOf(disableLineCommand) === 0) {
processDisableLineCommand(comment)
} else if (text.indexOf(disableNextLineCommand) === 0) {
processDisableNextLineCommand(comment)
} else if (text.indexOf(disableCommand) === 0) {
processDisableCommand(comment)
} else if (text.indexOf(enableCommand) === 0) {
processEnableCommand(comment)
}
}
function getCommandRules(
command/*: string*/,
fullText/*: string*/
)/*: Array<string>*/ {
const rules = _.compact(fullText.slice(command.length).split(",")).map(r => r.trim())
if (_.isEmpty(rules)) {
return [ALL_RULES]
}
return rules
}
function startDisabledRange(line/*: number*/, ruleName/*: string*/) {
const rangeObj = { start: line }
ensureRuleRanges(ruleName)
disabledRanges[ruleName].push(rangeObj)
}
function endDisabledRange(line/*: number*/, ruleName/*: string*/) {
const lastRangeForRule = _.last(disabledRanges[ruleName])
if (!lastRangeForRule) {
return
}
// Add an `end` prop to the last range of that rule
lastRangeForRule.end = line
}
function ensureRuleRanges(ruleName/*: string*/) {
if (!disabledRanges[ruleName]) {
disabledRanges[ruleName] = _.cloneDeep(disabledRanges.all)
}
}
function ruleIsDisabled(ruleName/*: string*/)/*: boolean*/ {
if (disabledRanges[ruleName] === undefined) return false
if (_.last(disabledRanges[ruleName]) === undefined) return false
if (_.get(_.last(disabledRanges[ruleName]), "end") === undefined) return true
return false
}
}