lintPostcssResult.js 4.2 KB

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