'use strict'; const { EOL } = require('os'); const assignDisabledRanges = require('./assignDisabledRanges'); const reportUnknownRuleNames = require('./reportUnknownRuleNames'); const rules = require('./rules'); const getStylelintRule = require('./utils/getStylelintRule'); const { DEFAULT_CONFIGURATION_COMMENT } = require('./utils/configurationComment'); /** @typedef {import('stylelint').LinterOptions} LinterOptions */ /** @typedef {import('stylelint').PostcssResult} PostcssResult */ /** @typedef {import('stylelint').Config} StylelintConfig */ /** * @param {LinterOptions} stylelintOptions * @param {PostcssResult} postcssResult * @param {StylelintConfig} config * @returns {Promise} */ module.exports = function lintPostcssResult(stylelintOptions, postcssResult, config) { postcssResult.stylelint.ruleSeverities = {}; postcssResult.stylelint.customMessages = {}; postcssResult.stylelint.ruleMetadata = {}; postcssResult.stylelint.stylelintError = false; postcssResult.stylelint.stylelintWarning = false; postcssResult.stylelint.quiet = config.quiet; postcssResult.stylelint.config = config; /** @type {string | undefined} */ let newline; const postcssDoc = postcssResult.root; if (postcssDoc) { if (!('type' in postcssDoc)) { throw new Error('Unexpected Postcss root object!'); } const newlineMatch = postcssDoc.source && postcssDoc.source.input.css.match(/\r?\n/); newline = newlineMatch ? newlineMatch[0] : EOL; assignDisabledRanges(postcssDoc, postcssResult); } const isFileFixCompatible = isFixCompatible(postcssResult); if (!isFileFixCompatible) { postcssResult.stylelint.disableWritingFix = true; } const postcssRoots = /** @type {import('postcss').Root[]} */ ( postcssDoc && postcssDoc.constructor.name === 'Document' ? postcssDoc.nodes : [postcssDoc] ); // Promises for the rules. Although the rule code runs synchronously now, // the use of Promises makes it compatible with the possibility of async // rules down the line. /** @type {Array>} */ const performRules = []; const rulesOrder = Object.keys(rules); const ruleNames = config.rules ? Object.keys(config.rules).sort((a, b) => rulesOrder.indexOf(a) - rulesOrder.indexOf(b)) : []; for (const ruleName of ruleNames) { const ruleFunction = getStylelintRule(ruleName, config); if (ruleFunction === undefined) { performRules.push( Promise.all( postcssRoots.map((postcssRoot) => reportUnknownRuleNames(ruleName, postcssRoot, postcssResult), ), ), ); continue; } const ruleSettings = config.rules && config.rules[ruleName]; if (ruleSettings === null || ruleSettings[0] === null) { continue; } if ( ruleFunction.meta && ruleFunction.meta.deprecated && !stylelintOptions.quietDeprecationWarnings ) { warnDeprecatedRule(postcssResult, ruleName); } const primaryOption = ruleSettings[0]; const secondaryOptions = ruleSettings[1]; // Log the rule's severity in the PostCSS result const defaultSeverity = config.defaultSeverity || 'error'; // disableFix in secondary option const disableFix = (secondaryOptions && secondaryOptions.disableFix === true) || false; postcssResult.stylelint.ruleSeverities[ruleName] = (secondaryOptions && secondaryOptions.severity) || defaultSeverity; postcssResult.stylelint.customMessages[ruleName] = secondaryOptions && secondaryOptions.message; postcssResult.stylelint.ruleMetadata[ruleName] = ruleFunction.meta || {}; performRules.push( Promise.all( postcssRoots.map((postcssRoot) => ruleFunction(primaryOption, secondaryOptions, { configurationComment: config.configurationComment || DEFAULT_CONFIGURATION_COMMENT, fix: !disableFix && config.fix && // Next two conditionals are temporary measures until #2643 is resolved isFileFixCompatible && !postcssResult.stylelint.disabledRanges[ruleName], newline, })(postcssRoot, postcssResult), ), ), ); } return Promise.all(performRules); }; /** * There are currently some bugs in the autofixer of Stylelint. * The autofixer does not yet adhere to stylelint-disable comments, so if there are disabled * ranges we can not autofix this document. More info in issue #2643. * * @param {PostcssResult} postcssResult * @returns {boolean} */ function isFixCompatible({ stylelint }) { // Check for issue #2643 if (stylelint.disabledRanges.all && stylelint.disabledRanges.all.length) return false; return true; } /** * @param {PostcssResult} result * @param {string} ruleName * @returns {void} */ function warnDeprecatedRule(result, ruleName) { result.warn(`The "${ruleName}" rule is deprecated.`, { stylelintType: 'deprecation' }); }