| // NOTICE: This file is generated by Rollup. To modify it, |
| // please instead edit the ESM counterpart and rebuild with Rollup (npm run build). |
| 'use strict'; |
| |
| const constants = require('./constants.cjs'); |
| const configurationComment = require('./utils/configurationComment.cjs'); |
| const validateTypes = require('./utils/validateTypes.cjs'); |
| const typeGuards = require('./utils/typeGuards.cjs'); |
| const isStandardSyntaxComment = require('./utils/isStandardSyntaxComment.cjs'); |
| const cssTokenizer = require('@csstools/css-tokenizer'); |
| |
| /** @import {Node as PostcssNode, Comment as PostcssComment, Document as PostcssDocument, Root as PostcssRoot, Source as PostcssSource} from 'postcss' */ |
| /** @import {DisabledRange, DisabledRangeObject, PostcssResult} from 'stylelint' */ |
| /** @typedef {Pick<PostcssSource, 'start' | 'end'>} Source */ |
| |
| /** |
| * @param {PostcssNode} node |
| * @param {number} start |
| * @param {boolean} strictStart |
| * @param {string|undefined} description |
| * @param {number} [end] |
| * @param {boolean} [strictEnd] |
| * @returns {DisabledRange} |
| */ |
| function createDisableRange(node, start, strictStart, description, end, strictEnd) { |
| return { |
| node, |
| start, |
| end: end || undefined, |
| strictStart, |
| strictEnd: typeof strictEnd === 'boolean' ? strictEnd : undefined, |
| description, |
| }; |
| } |
| |
| /** |
| * Run it like a PostCSS plugin |
| * @param {PostcssRoot | PostcssDocument} root |
| * @param {PostcssResult} result |
| * @returns {PostcssResult} |
| */ |
| function assignDisabledRanges(root, result) { |
| /** |
| * Most of the functions below work via side effects mutating this object |
| * @type {DisabledRangeObject} |
| */ |
| const disabledRanges = result.stylelint.disabledRanges; |
| |
| /** @type {DisabledRange[]} */ |
| const disabledRangesAll = []; |
| |
| disabledRanges[constants.RULE_NAME_ALL] = disabledRangesAll; |
| |
| // Work around postcss/postcss-scss#109 by merging adjacent `//` comments |
| // into a single node before passing to `checkComment`. |
| |
| /** @type {PostcssComment?} */ |
| let inlineEnd; |
| |
| const configurationComment$1 = result.stylelint.config?.configurationComment; |
| |
| root.walk((node) => { |
| if (typeGuards.isComment(node)) { |
| if (inlineEnd) { |
| // Ignore comments already processed by grouping with a previous one. |
| if (inlineEnd === node) inlineEnd = null; |
| |
| return; |
| } |
| |
| const nextComment = node.next(); |
| |
| // If any of these conditions are not met, do not merge comments. |
| if ( |
| !( |
| !isStandardSyntaxComment(node) && |
| configurationComment.isConfigurationComment(node.text, configurationComment$1) && |
| nextComment && |
| typeGuards.isComment(nextComment) && |
| (node.text.includes('--') || nextComment.text.startsWith('--')) |
| ) |
| ) { |
| checkComment(node, node.source, node.text); |
| |
| return; |
| } |
| |
| let lastLine = node.source?.end?.line ?? 0; |
| const fullComment = node.clone(); |
| |
| let current = nextComment; |
| |
| while ( |
| !isStandardSyntaxComment(current) && |
| !configurationComment.isConfigurationComment(current.text, configurationComment$1) |
| ) { |
| const currentLine = current.source?.end?.line ?? 0; |
| |
| if (lastLine + 1 !== currentLine) break; |
| |
| fullComment.text += `\n${current.text}`; |
| |
| if (fullComment.source && current.source) { |
| fullComment.source.end = current.source.end; |
| } |
| |
| inlineEnd = current; |
| const next = current.next(); |
| |
| if (!next || !typeGuards.isComment(next)) break; |
| |
| current = next; |
| lastLine = currentLine; |
| } |
| |
| checkComment(fullComment, fullComment.source, fullComment.text); |
| } |
| |
| if (typeGuards.isRule(node)) { |
| let offset = 0; |
| const selector = node.raws?.selector?.raw; |
| |
| checkCommentsInNode(node, selector, offset); |
| |
| offset += selector?.length ?? node.selector.length; |
| const between = node.raws?.between; |
| |
| checkCommentsInNode(node, between, offset); |
| } |
| |
| if (typeGuards.isAtRule(node)) { |
| let offset = node.name.length + 1; // `@` + name |
| const afterName = node.raws?.afterName; |
| |
| checkCommentsInNode(node, afterName, offset); |
| |
| offset += afterName?.length ?? 0; |
| const params = node.raws?.params?.raw; |
| |
| checkCommentsInNode(node, params, offset); |
| |
| offset += params?.length ?? node.params.length; |
| const between = node.raws?.between; |
| |
| checkCommentsInNode(node, between, offset); |
| } |
| |
| if (typeGuards.isDeclaration(node)) { |
| let offset = node.prop.length; |
| const between = node.raws?.between; |
| |
| checkCommentsInNode(node, between, offset); |
| |
| offset += between?.length ?? 0; |
| const value = node.raws?.value?.raw; |
| |
| checkCommentsInNode(node, value, offset); |
| } |
| }); |
| |
| return result; |
| |
| /** |
| * @param {PostcssNode} node |
| * @param {Source} source |
| * @param {string} text |
| */ |
| function processDisableLineCommand(node, source, text) { |
| if (source.start) { |
| const line = source.start.line; |
| const description = getDescription(text); |
| |
| for (const ruleName of getCommandRules(configurationComment.DISABLE_LINE_COMMAND, text)) { |
| disableLine(node, line, ruleName, description); |
| } |
| } |
| } |
| |
| /** |
| * @param {PostcssNode} node |
| * @param {Source} source |
| * @param {string} text |
| */ |
| function processDisableNextLineCommand(node, source, text) { |
| if (source.end) { |
| const line = source.end.line; |
| const description = getDescription(text); |
| |
| for (const ruleName of getCommandRules(configurationComment.DISABLE_NEXT_LINE_COMMAND, text)) { |
| disableLine(node, line + 1, ruleName, description); |
| } |
| } |
| } |
| |
| /** |
| * @param {PostcssNode} node |
| * @param {number} line |
| * @param {string} ruleName |
| * @param {string|undefined} description |
| */ |
| function disableLine(node, line, ruleName, description) { |
| if (ruleIsDisabled(constants.RULE_NAME_ALL)) { |
| throw node.error('All rules have already been disabled', { |
| plugin: 'stylelint', |
| }); |
| } |
| |
| if (ruleName === constants.RULE_NAME_ALL) { |
| for (const disabledRuleName of Object.keys(disabledRanges)) { |
| if (ruleIsDisabled(disabledRuleName)) continue; |
| |
| const strict = disabledRuleName === constants.RULE_NAME_ALL; |
| |
| startDisabledRange(node, line, disabledRuleName, strict, description); |
| endDisabledRange(line, disabledRuleName, strict); |
| } |
| } else { |
| if (ruleIsDisabled(ruleName)) { |
| throw node.error(`"${ruleName}" has already been disabled`, { |
| plugin: 'stylelint', |
| }); |
| } |
| |
| startDisabledRange(node, line, ruleName, true, description); |
| endDisabledRange(line, ruleName, true); |
| } |
| } |
| |
| /** |
| * @param {PostcssNode} node |
| * @param {Source} source |
| * @param {string} text |
| */ |
| function processDisableCommand(node, source, text) { |
| const description = getDescription(text); |
| |
| for (const ruleToDisable of getCommandRules(configurationComment.DISABLE_COMMAND, text)) { |
| const isAllRules = ruleToDisable === constants.RULE_NAME_ALL; |
| |
| if (ruleIsDisabled(ruleToDisable)) { |
| throw node.error( |
| isAllRules |
| ? 'All rules have already been disabled' |
| : `"${ruleToDisable}" has already been disabled`, |
| { |
| plugin: 'stylelint', |
| }, |
| ); |
| } |
| |
| if (source.start) { |
| const line = source.start.line; |
| |
| if (isAllRules) { |
| for (const ruleName of Object.keys(disabledRanges)) { |
| startDisabledRange(node, line, ruleName, ruleName === constants.RULE_NAME_ALL, description); |
| } |
| } else { |
| startDisabledRange(node, line, ruleToDisable, true, description); |
| } |
| } |
| } |
| } |
| |
| /** |
| * @param {PostcssNode} node |
| * @param {Source} source |
| * @param {string} text |
| */ |
| function processEnableCommand(node, source, text) { |
| for (const ruleToEnable of getCommandRules(configurationComment.ENABLE_COMMAND, text)) { |
| // need fallback if endLine will be undefined |
| const endLine = source.end?.line; |
| |
| validateTypes.assertNumber(endLine); |
| |
| if (ruleToEnable === constants.RULE_NAME_ALL) { |
| if ( |
| Object.values(disabledRanges).every((ranges) => { |
| if (ranges.length === 0) return true; |
| |
| const lastRange = ranges[ranges.length - 1]; |
| |
| return lastRange && typeof lastRange.end === 'number'; |
| }) |
| ) { |
| throw node.error('No rules have been disabled', { |
| plugin: 'stylelint', |
| }); |
| } |
| |
| for (const [ruleName, ranges] of Object.entries(disabledRanges)) { |
| const lastRange = ranges[ranges.length - 1]; |
| |
| if (!lastRange || !lastRange.end) { |
| endDisabledRange(endLine, ruleName, ruleName === constants.RULE_NAME_ALL); |
| } |
| } |
| |
| continue; |
| } |
| |
| if (ruleIsDisabled(constants.RULE_NAME_ALL) && disabledRanges[ruleToEnable] === undefined) { |
| // Get a starting point from the where all rules were disabled |
| disabledRanges[ruleToEnable] = disabledRangesAll.map(({ start, end, description }) => |
| createDisableRange(node, start, false, description, end, false), |
| ); |
| |
| endDisabledRange(endLine, ruleToEnable, true); |
| |
| continue; |
| } |
| |
| if (ruleIsDisabled(ruleToEnable)) { |
| endDisabledRange(endLine, ruleToEnable, true); |
| |
| continue; |
| } |
| |
| throw node.error(`"${ruleToEnable}" has not been disabled`, { |
| plugin: 'stylelint', |
| }); |
| } |
| } |
| |
| /** |
| * @param {PostcssNode} node |
| * @param {string | undefined} part |
| * @param {number} offset |
| */ |
| function checkCommentsInNode(node, part, offset) { |
| if (!(part && part.includes('/*') && part.includes('*/'))) return; |
| |
| cssTokenizer.tokenize({ css: part }).forEach((token) => { |
| if (!cssTokenizer.isTokenComment(token)) return; |
| |
| const [, text, start, end] = token; |
| |
| const source = node.rangeBy({ |
| index: start + offset, |
| endIndex: end + 1 + offset, |
| }); |
| |
| checkComment(node, source, text.slice(2, -2).trim()); |
| }); |
| } |
| |
| /** |
| * @param {PostcssNode} node |
| * @param {Source | undefined} source |
| * @param {string} text |
| */ |
| function checkComment(node, source, text) { |
| if (!source) return; |
| |
| // Ignore comments that are not relevant commands |
| if (!configurationComment.isConfigurationComment(text, configurationComment$1)) { |
| return; |
| } |
| |
| switch (configurationComment.extractConfigurationComment(text, configurationComment$1)) { |
| case configurationComment.DISABLE_LINE_COMMAND: |
| processDisableLineCommand(node, source, text); |
| break; |
| case configurationComment.DISABLE_NEXT_LINE_COMMAND: |
| processDisableNextLineCommand(node, source, text); |
| break; |
| case configurationComment.DISABLE_COMMAND: |
| processDisableCommand(node, source, text); |
| break; |
| case configurationComment.ENABLE_COMMAND: |
| processEnableCommand(node, source, text); |
| break; |
| } |
| } |
| |
| /** |
| * @param {string} command |
| * @param {string} fullText |
| * @returns {string[]} |
| */ |
| function getCommandRules(command, fullText) { |
| // Allow for description (f.e. /* stylelint-disable a, b -- Description */). |
| const fullCommand = configurationComment.getConfigurationComment(command, configurationComment$1); |
| const rulesText = fullText.slice(fullCommand.length).split(/\s-{2,}\s/u)[0]; |
| |
| validateTypes.assertString(rulesText); |
| const rules = rulesText |
| .trim() |
| .split(',') |
| .filter(Boolean) |
| .map((r) => r.trim()); |
| |
| if (rules.length === 0) { |
| return [constants.RULE_NAME_ALL]; |
| } |
| |
| return rules; |
| } |
| |
| /** |
| * @param {string} fullText |
| * @returns {string|undefined} |
| */ |
| function getDescription(fullText) { |
| const descriptionStart = fullText.indexOf('--'); |
| |
| if (descriptionStart === -1) return; |
| |
| return fullText.slice(descriptionStart + 2).trim(); |
| } |
| |
| /** |
| * @param {PostcssNode} node |
| * @param {number} line |
| * @param {string} ruleName |
| * @param {boolean} strict |
| * @param {string|undefined} description |
| */ |
| function startDisabledRange(node, line, ruleName, strict, description) { |
| const rangeObj = createDisableRange(node, line, strict, description); |
| |
| ensureRuleRanges(ruleName); |
| |
| const range = disabledRanges[ruleName]; |
| |
| validateTypes.assert(range); |
| range.push(rangeObj); |
| } |
| |
| /** |
| * @param {number} line |
| * @param {string} ruleName |
| * @param {boolean} strict |
| */ |
| function endDisabledRange(line, ruleName, strict) { |
| const ranges = disabledRanges[ruleName]; |
| const lastRangeForRule = ranges ? ranges[ranges.length - 1] : null; |
| |
| if (!lastRangeForRule) { |
| return; |
| } |
| |
| // Add an `end` prop to the last range of that rule |
| lastRangeForRule.end = line; |
| lastRangeForRule.strictEnd = strict; |
| } |
| |
| /** |
| * @param {string} ruleName |
| */ |
| function ensureRuleRanges(ruleName) { |
| if (!disabledRanges[ruleName]) { |
| disabledRanges[ruleName] = disabledRangesAll.map(({ node, start, end, description }) => |
| createDisableRange(node, start, false, description, end, false), |
| ); |
| } |
| } |
| |
| /** |
| * @param {string} ruleName |
| * @returns {boolean} |
| */ |
| function ruleIsDisabled(ruleName) { |
| const ranges = disabledRanges[ruleName]; |
| |
| if (!ranges) return false; |
| |
| const lastRange = ranges[ranges.length - 1]; |
| |
| if (!lastRange) return false; |
| |
| if (!lastRange.end) return true; |
| |
| return false; |
| } |
| } |
| |
| module.exports = assignDisabledRanges; |