lintPostcssResult.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. 'use strict';
  2. const assignDisabledRanges = require('./assignDisabledRanges');
  3. const getOsEol = require('./utils/getOsEol');
  4. const reportUnknownRuleNames = require('./reportUnknownRuleNames');
  5. const rules = require('./rules');
  6. /** @typedef {import('stylelint').LinterOptions} LinterOptions */
  7. /** @typedef {import('stylelint').PostcssResult} PostcssResult */
  8. /** @typedef {import('stylelint').Config} StylelintConfig */
  9. /**
  10. * @param {LinterOptions} stylelintOptions
  11. * @param {PostcssResult} postcssResult
  12. * @param {StylelintConfig} config
  13. * @returns {Promise<any>}
  14. */
  15. function lintPostcssResult(stylelintOptions, postcssResult, config) {
  16. postcssResult.stylelint.ruleSeverities = {};
  17. postcssResult.stylelint.customMessages = {};
  18. postcssResult.stylelint.ruleMetadata = {};
  19. postcssResult.stylelint.stylelintError = false;
  20. postcssResult.stylelint.quiet = config.quiet;
  21. postcssResult.stylelint.config = config;
  22. /** @type {string | undefined} */
  23. let newline;
  24. const postcssDoc = postcssResult.root;
  25. if (postcssDoc) {
  26. if (!('type' in postcssDoc)) {
  27. throw new Error('Unexpected Postcss root object!');
  28. }
  29. const newlineMatch = postcssDoc.source && postcssDoc.source.input.css.match(/\r?\n/);
  30. newline = newlineMatch ? newlineMatch[0] : getOsEol();
  31. assignDisabledRanges(postcssDoc, postcssResult);
  32. }
  33. const isFileFixCompatible = isFixCompatible(postcssResult);
  34. if (!isFileFixCompatible) {
  35. postcssResult.stylelint.disableWritingFix = true;
  36. }
  37. const postcssRoots = /** @type {import('postcss').Root[]} */ (
  38. postcssDoc && postcssDoc.constructor.name === 'Document' ? postcssDoc.nodes : [postcssDoc]
  39. );
  40. // Promises for the rules. Although the rule code runs synchronously now,
  41. // the use of Promises makes it compatible with the possibility of async
  42. // rules down the line.
  43. /** @type {Array<Promise<any>>} */
  44. const performRules = [];
  45. const rulesOrder = Object.keys(rules);
  46. const ruleNames = config.rules
  47. ? Object.keys(config.rules).sort((a, b) => rulesOrder.indexOf(a) - rulesOrder.indexOf(b))
  48. : [];
  49. for (const ruleName of ruleNames) {
  50. const ruleFunction =
  51. rules[ruleName] || (config.pluginFunctions && config.pluginFunctions[ruleName]);
  52. if (ruleFunction === undefined) {
  53. performRules.push(
  54. Promise.all(
  55. postcssRoots.map((postcssRoot) =>
  56. reportUnknownRuleNames(ruleName, postcssRoot, postcssResult),
  57. ),
  58. ),
  59. );
  60. continue;
  61. }
  62. const ruleSettings = config.rules && config.rules[ruleName];
  63. if (ruleSettings === null || ruleSettings[0] === null) {
  64. continue;
  65. }
  66. const primaryOption = ruleSettings[0];
  67. const secondaryOptions = ruleSettings[1];
  68. // Log the rule's severity in the PostCSS result
  69. const defaultSeverity = config.defaultSeverity || 'error';
  70. // disableFix in secondary option
  71. const disableFix = (secondaryOptions && secondaryOptions.disableFix === true) || false;
  72. if (disableFix) {
  73. postcssResult.stylelint.ruleDisableFix = true;
  74. }
  75. postcssResult.stylelint.ruleSeverities[ruleName] =
  76. (secondaryOptions && secondaryOptions.severity) || defaultSeverity;
  77. postcssResult.stylelint.customMessages[ruleName] = secondaryOptions && secondaryOptions.message;
  78. postcssResult.stylelint.ruleMetadata[ruleName] = ruleFunction.meta || {};
  79. performRules.push(
  80. Promise.all(
  81. postcssRoots.map((postcssRoot) =>
  82. ruleFunction(primaryOption, secondaryOptions, {
  83. fix:
  84. !disableFix &&
  85. stylelintOptions.fix &&
  86. // Next two conditionals are temporary measures until #2643 is resolved
  87. isFileFixCompatible &&
  88. !postcssResult.stylelint.disabledRanges[ruleName],
  89. newline,
  90. })(postcssRoot, postcssResult),
  91. ),
  92. ),
  93. );
  94. }
  95. return Promise.all(performRules);
  96. }
  97. /**
  98. * There are currently some bugs in the autofixer of Stylelint.
  99. * The autofixer does not yet adhere to stylelint-disable comments, so if there are disabled
  100. * ranges we can not autofix this document. More info in issue #2643.
  101. *
  102. * @param {PostcssResult} postcssResult
  103. * @returns {boolean}
  104. */
  105. function isFixCompatible({ stylelint }) {
  106. // Check for issue #2643
  107. if (stylelint.disabledRanges.all.length) return false;
  108. return true;
  109. }
  110. module.exports = lintPostcssResult;