index.js 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. 'use strict';
  2. const beforeBlockString = require('../../utils/beforeBlockString');
  3. const hasBlock = require('../../utils/hasBlock');
  4. const optionsMatches = require('../../utils/optionsMatches');
  5. const report = require('../../utils/report');
  6. const ruleMessages = require('../../utils/ruleMessages');
  7. const { isStylelintCommand } = require('../../utils/stylelintCommand');
  8. const { isComment } = require('../../utils/typeGuards');
  9. const validateOptions = require('../../utils/validateOptions');
  10. const { isBoolean } = require('../../utils/validateTypes');
  11. const ruleName = 'block-no-empty';
  12. const messages = ruleMessages(ruleName, {
  13. rejected: 'Unexpected empty block',
  14. });
  15. const meta = {
  16. url: 'https://stylelint.io/user-guide/rules/block-no-empty',
  17. };
  18. /** @type {import('stylelint').Rule} */
  19. const rule = (primary, secondaryOptions) => {
  20. return (root, result) => {
  21. const validOptions = validateOptions(
  22. result,
  23. ruleName,
  24. {
  25. actual: primary,
  26. possible: isBoolean,
  27. },
  28. {
  29. actual: secondaryOptions,
  30. possible: {
  31. ignore: ['comments'],
  32. },
  33. optional: true,
  34. },
  35. );
  36. if (!validOptions) {
  37. return;
  38. }
  39. const ignoreComments = optionsMatches(secondaryOptions, 'ignore', 'comments');
  40. // Check both kinds of statements: rules and at-rules
  41. root.walkRules(check);
  42. root.walkAtRules(check);
  43. /** @typedef {import('postcss').Rule | import('postcss').AtRule} Statement */
  44. /**
  45. * @param {Statement} statement
  46. */
  47. function check(statement) {
  48. if (!hasBlock(statement)) {
  49. return;
  50. }
  51. if (hasNotableChild(statement)) {
  52. return;
  53. }
  54. let index = beforeBlockString(statement, { noRawBefore: true }).length;
  55. // For empty blocks when using SugarSS parser
  56. if (statement.raws.between === undefined) {
  57. index--;
  58. }
  59. report({
  60. message: messages.rejected,
  61. node: statement,
  62. start: statement.positionBy({ index }),
  63. result,
  64. ruleName,
  65. });
  66. }
  67. /**
  68. * @param {Statement} statement
  69. * @returns {boolean}
  70. */
  71. function hasNotableChild(statement) {
  72. return statement.nodes.some((child) => {
  73. if (isComment(child)) {
  74. if (ignoreComments) return false;
  75. if (isStylelintCommand(child)) return false;
  76. }
  77. return true;
  78. });
  79. }
  80. };
  81. };
  82. rule.ruleName = ruleName;
  83. rule.messages = messages;
  84. rule.meta = meta;
  85. module.exports = rule;