diff --git a/lib/lintPostcssResult.js b/lib/lintPostcssResult.js new file mode 100644 index 0000000000..b3e8f8051b --- /dev/null +++ b/lib/lintPostcssResult.js @@ -0,0 +1,142 @@ +'use strict'; + +const assignDisabledRanges = require('./assignDisabledRanges'); +const get = require('lodash/get'); +const getOsEol = require('./utils/getOsEol'); +const reportUnknownRuleNames = require('./reportUnknownRuleNames'); +const rulesOrder = require('./rules'); + +/** @typedef {import('stylelint').StylelintStandaloneOptions} StylelintStandaloneOptions */ +/** @typedef {import('stylelint').PostcssResult} PostcssResult */ +/** @typedef {import('stylelint').StylelintConfig} StylelintConfig */ + +/** + * @param {StylelintStandaloneOptions} stylelintOptions + * @param {PostcssResult} postcssResult + * @param {StylelintConfig} config + * @returns {Promise} + */ +function lintPostcssResult(stylelintOptions, postcssResult, config) { + postcssResult.stylelint.ruleSeverities = {}; + postcssResult.stylelint.customMessages = {}; + postcssResult.stylelint.stylelintError = false; + postcssResult.stylelint.quiet = config.quiet; + postcssResult.stylelint.config = config; + + /** @type {string} */ + let newline; + const postcssDoc = postcssResult.root; + + if (postcssDoc) { + if (!('type' in postcssDoc)) { + throw new Error('Unexpected Postcss root object!'); + } + + // @ts-ignore TODO TYPES property css does not exists + const newlineMatch = postcssDoc.source && postcssDoc.source.input.css.match(/\r?\n/); + + newline = newlineMatch ? newlineMatch[0] : getOsEol(); + + assignDisabledRanges(postcssDoc, postcssResult); + } + + if (stylelintOptions.ignoreDisables) { + postcssResult.stylelint.ignoreDisables = true; + } + + if (stylelintOptions.reportNeedlessDisables) { + postcssResult.stylelint.reportNeedlessDisables = true; + } + + 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 rules = config.rules + ? Object.keys(config.rules).sort( + (a, b) => Object.keys(rulesOrder).indexOf(a) - Object.keys(rulesOrder).indexOf(b), + ) + : []; + + rules.forEach((ruleName) => { + const ruleFunction = rulesOrder[ruleName] || get(config, ['pluginFunctions', ruleName]); + + if (ruleFunction === undefined) { + performRules.push( + Promise.all( + postcssRoots.map((postcssRoot) => + reportUnknownRuleNames(ruleName, postcssRoot, postcssResult), + ), + ), + ); + + return; + } + + const ruleSettings = get(config, ['rules', ruleName]); + + if (ruleSettings === null || ruleSettings[0] === null) { + return; + } + + const primaryOption = ruleSettings[0]; + const secondaryOptions = ruleSettings[1]; + + // Log the rule's severity in the PostCSS result + const defaultSeverity = config.defaultSeverity || 'error'; + + postcssResult.stylelint.ruleSeverities[ruleName] = get( + secondaryOptions, + 'severity', + defaultSeverity, + ); + postcssResult.stylelint.customMessages[ruleName] = get(secondaryOptions, 'message'); + + performRules.push( + Promise.all( + postcssRoots.map((postcssRoot) => + ruleFunction(primaryOption, secondaryOptions, { + fix: + stylelintOptions.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.length) return false; + + return true; +} + +module.exports = lintPostcssResult; diff --git a/lib/lintSource.js b/lib/lintSource.js index 4c38ac96ec..f5bc8f3df4 100644 --- a/lib/lintSource.js +++ b/lib/lintSource.js @@ -1,16 +1,13 @@ 'use strict'; -const _ = require('lodash'); -const assignDisabledRanges = require('./assignDisabledRanges'); -const getOsEol = require('./utils/getOsEol'); +const lintPostcssResult = require('./lintPostcssResult'); const path = require('path'); -const reportUnknownRuleNames = require('./reportUnknownRuleNames'); -const rulesOrder = require('./rules'); /** @typedef {import('stylelint').StylelintInternalApi} StylelintInternalApi */ +/** @typedef {import('stylelint').GetLintSourceOptions} Options */ +/** @typedef {import('postcss').Result} Result */ /** @typedef {import('stylelint').PostcssResult} PostcssResult */ /** @typedef {import('stylelint').StylelintPostcssResult} StylelintPostcssResult */ -/** @typedef {import('stylelint').GetLintSourceOptions} Options */ /** * Run stylelint on a PostCSS Result, either one that is provided @@ -84,7 +81,7 @@ module.exports = function lintSource(stylelint, options = {}) { stylelint: stylelintResult, }); - return lintPostcssResult(stylelint, stylelintPostcssResult, config).then( + return lintPostcssResult(stylelint._options, stylelintPostcssResult, config).then( () => stylelintPostcssResult, ); } @@ -101,7 +98,7 @@ module.exports = function lintSource(stylelint, options = {}) { stylelint: stylelintResult, }); - return lintPostcssResult(stylelint, stylelintPostcssResult, config).then( + return lintPostcssResult(stylelint._options, stylelintPostcssResult, config).then( () => stylelintPostcssResult, ); }); @@ -109,120 +106,6 @@ module.exports = function lintSource(stylelint, options = {}) { }); }; -/** - * @param {StylelintInternalApi} stylelint - * @param {PostcssResult} postcssResult - * @param {import('stylelint').StylelintConfig} config - * @returns {Promise} - */ -function lintPostcssResult(stylelint, postcssResult, config) { - postcssResult.stylelint.ruleSeverities = {}; - postcssResult.stylelint.customMessages = {}; - postcssResult.stylelint.stylelintError = false; - postcssResult.stylelint.quiet = config.quiet; - postcssResult.stylelint.config = config; - - /** @type {string} */ - let newline; - const postcssDoc = postcssResult.root; - - if (postcssDoc) { - if (!('type' in postcssDoc)) { - throw new Error('Unexpected Postcss root object!'); - } - - // @ts-ignore TODO TYPES property css does not exists - const newlineMatch = postcssDoc.source && postcssDoc.source.input.css.match(/\r?\n/); - - newline = newlineMatch ? newlineMatch[0] : getOsEol(); - - assignDisabledRanges(postcssDoc, postcssResult); - } - - if (stylelint._options.ignoreDisables) { - postcssResult.stylelint.ignoreDisables = true; - } - - if (stylelint._options.reportNeedlessDisables) { - postcssResult.stylelint.reportNeedlessDisables = true; - } - - 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 rules = config.rules - ? Object.keys(config.rules).sort( - (a, b) => Object.keys(rulesOrder).indexOf(a) - Object.keys(rulesOrder).indexOf(b), - ) - : []; - - rules.forEach((ruleName) => { - const ruleFunction = rulesOrder[ruleName] || _.get(config, ['pluginFunctions', ruleName]); - - if (ruleFunction === undefined) { - performRules.push( - Promise.all( - postcssRoots.map((postcssRoot) => - reportUnknownRuleNames(ruleName, postcssRoot, postcssResult), - ), - ), - ); - - return; - } - - const ruleSettings = _.get(config, ['rules', ruleName]); - - if (ruleSettings === null || ruleSettings[0] === null) { - return; - } - - const primaryOption = ruleSettings[0]; - const secondaryOptions = ruleSettings[1]; - - // Log the rule's severity in the PostCSS result - const defaultSeverity = config.defaultSeverity || 'error'; - - postcssResult.stylelint.ruleSeverities[ruleName] = _.get( - secondaryOptions, - 'severity', - defaultSeverity, - ); - postcssResult.stylelint.customMessages[ruleName] = _.get(secondaryOptions, 'message'); - - performRules.push( - Promise.all( - postcssRoots.map((postcssRoot) => - ruleFunction(primaryOption, secondaryOptions, { - fix: - stylelint._options.fix && - // Next two conditionals are temporary measures until #2643 is resolved - isFileFixCompatible && - !postcssResult.stylelint.disabledRanges[ruleName], - newline, - })(postcssRoot, postcssResult), - ), - ), - ); - }); - - return Promise.all(performRules); -} - /** * @returns {StylelintPostcssResult} */ @@ -253,18 +136,3 @@ function createEmptyPostcssResult(filePath) { warn: () => {}, }; } - -/** - * 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.length) return false; - - return true; -}