lintPostcssResult.js 4.0 KB

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