index.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. 'use strict';
  2. const blockString = require('../../utils/blockString');
  3. const hasBlock = require('../../utils/hasBlock');
  4. const hasEmptyBlock = require('../../utils/hasEmptyBlock');
  5. const report = require('../../utils/report');
  6. const ruleMessages = require('../../utils/ruleMessages');
  7. const validateOptions = require('../../utils/validateOptions');
  8. const whitespaceChecker = require('../../utils/whitespaceChecker');
  9. const ruleName = 'block-closing-brace-space-before';
  10. const messages = ruleMessages(ruleName, {
  11. expectedBefore: () => 'Expected single space before "}"',
  12. rejectedBefore: () => 'Unexpected whitespace before "}"',
  13. expectedBeforeSingleLine: () => 'Expected single space before "}" of a single-line block',
  14. rejectedBeforeSingleLine: () => 'Unexpected whitespace before "}" of a single-line block',
  15. expectedBeforeMultiLine: () => 'Expected single space before "}" of a multi-line block',
  16. rejectedBeforeMultiLine: () => 'Unexpected whitespace before "}" of a multi-line block',
  17. });
  18. const meta = {
  19. url: 'https://stylelint.io/user-guide/rules/list/block-closing-brace-space-before',
  20. fixable: true,
  21. };
  22. /** @type {import('stylelint').Rule} */
  23. const rule = (primary, _secondaryOptions, context) => {
  24. const checker = whitespaceChecker('space', primary, messages);
  25. return (root, result) => {
  26. const validOptions = validateOptions(result, ruleName, {
  27. actual: primary,
  28. possible: [
  29. 'always',
  30. 'never',
  31. 'always-single-line',
  32. 'never-single-line',
  33. 'always-multi-line',
  34. 'never-multi-line',
  35. ],
  36. });
  37. if (!validOptions) {
  38. return;
  39. }
  40. // Check both kinds of statement: rules and at-rules
  41. root.walkRules(check);
  42. root.walkAtRules(check);
  43. /**
  44. * @param {import('postcss').Rule | import('postcss').AtRule} statement
  45. */
  46. function check(statement) {
  47. // Return early if blockless or has empty block
  48. if (!hasBlock(statement) || hasEmptyBlock(statement)) {
  49. return;
  50. }
  51. const source = blockString(statement);
  52. const statementString = statement.toString();
  53. let index = statementString.length - 2;
  54. if (statementString[index - 1] === '\r') {
  55. index -= 1;
  56. }
  57. checker.before({
  58. source,
  59. index: source.length - 1,
  60. err: (msg) => {
  61. if (context.fix) {
  62. const statementRaws = statement.raws;
  63. if (typeof statementRaws.after !== 'string') return;
  64. if (primary.startsWith('always')) {
  65. statementRaws.after = statementRaws.after.replace(/\s*$/, ' ');
  66. return;
  67. }
  68. if (primary.startsWith('never')) {
  69. statementRaws.after = statementRaws.after.replace(/\s*$/, '');
  70. return;
  71. }
  72. }
  73. report({
  74. message: msg,
  75. node: statement,
  76. index,
  77. result,
  78. ruleName,
  79. });
  80. },
  81. });
  82. }
  83. };
  84. };
  85. rule.ruleName = ruleName;
  86. rule.messages = messages;
  87. rule.meta = meta;
  88. module.exports = rule;