index.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. 'use strict';
  2. const isStandardSyntaxComment = require('../../utils/isStandardSyntaxComment');
  3. const isWhitespace = require('../../utils/isWhitespace');
  4. const report = require('../../utils/report');
  5. const ruleMessages = require('../../utils/ruleMessages');
  6. const validateOptions = require('../../utils/validateOptions');
  7. const ruleName = 'comment-whitespace-inside';
  8. const messages = ruleMessages(ruleName, {
  9. expectedOpening: 'Expected whitespace after "/*"',
  10. rejectedOpening: 'Unexpected whitespace after "/*"',
  11. expectedClosing: 'Expected whitespace before "*/"',
  12. rejectedClosing: 'Unexpected whitespace before "*/"',
  13. });
  14. const meta = {
  15. url: 'https://stylelint.io/user-guide/rules/list/comment-whitespace-inside',
  16. };
  17. /**
  18. * @param {import('postcss').Comment} comment
  19. */
  20. function addWhitespaceBefore(comment) {
  21. if (comment.text.startsWith('*')) {
  22. comment.text = comment.text.replace(/^(\*+)/, `$1 `);
  23. } else {
  24. comment.raws.left = ' ';
  25. }
  26. }
  27. /**
  28. * @param {import('postcss').Comment} comment
  29. */
  30. function addWhitespaceAfter(comment) {
  31. if (comment.text[comment.text.length - 1] === '*') {
  32. comment.text = comment.text.replace(/(\*+)$/, ` $1`);
  33. } else {
  34. comment.raws.right = ' ';
  35. }
  36. }
  37. /** @type {import('stylelint').Rule} */
  38. const rule = (primary, _secondaryOptions, context) => {
  39. return (root, result) => {
  40. const validOptions = validateOptions(result, ruleName, {
  41. actual: primary,
  42. possible: ['always', 'never'],
  43. });
  44. if (!validOptions) {
  45. return;
  46. }
  47. root.walkComments((comment) => {
  48. if (!isStandardSyntaxComment(comment)) {
  49. return;
  50. }
  51. const rawComment = comment.toString();
  52. const firstFourChars = rawComment.slice(0, 4);
  53. // Return early if sourcemap or copyright comment
  54. if (/^\/\*[#!]\s/.test(firstFourChars)) {
  55. return;
  56. }
  57. const leftMatches = rawComment.match(/(^\/\*+)(\s)?/);
  58. if (leftMatches == null) throw new Error(`Invalid comment: "${rawComment}"`);
  59. const rightMatches = rawComment.match(/(\s)?(\*+\/)$/);
  60. if (rightMatches == null) throw new Error(`Invalid comment: "${rawComment}"`);
  61. const opener = leftMatches[1];
  62. const leftSpace = leftMatches[2] || '';
  63. const rightSpace = rightMatches[1] || '';
  64. const closer = rightMatches[2];
  65. if (primary === 'never' && leftSpace !== '') {
  66. complain(messages.rejectedOpening, opener.length);
  67. }
  68. if (primary === 'always' && !isWhitespace(leftSpace)) {
  69. complain(messages.expectedOpening, opener.length);
  70. }
  71. if (primary === 'never' && rightSpace !== '') {
  72. complain(messages.rejectedClosing, comment.toString().length - closer.length - 1);
  73. }
  74. if (primary === 'always' && !isWhitespace(rightSpace)) {
  75. complain(messages.expectedClosing, comment.toString().length - closer.length - 1);
  76. }
  77. /**
  78. * @param {string} message
  79. * @param {number} index
  80. */
  81. function complain(message, index) {
  82. if (context.fix) {
  83. if (primary === 'never') {
  84. comment.raws.left = '';
  85. comment.raws.right = '';
  86. comment.text = comment.text.replace(/^(\*+)(\s+)?/, '$1').replace(/(\s+)?(\*+)$/, '$2');
  87. } else {
  88. if (!leftSpace) {
  89. addWhitespaceBefore(comment);
  90. }
  91. if (!rightSpace) {
  92. addWhitespaceAfter(comment);
  93. }
  94. }
  95. return;
  96. }
  97. report({
  98. message,
  99. index,
  100. result,
  101. ruleName,
  102. node: comment,
  103. });
  104. }
  105. });
  106. };
  107. };
  108. rule.ruleName = ruleName;
  109. rule.messages = messages;
  110. rule.meta = meta;
  111. module.exports = rule;