index.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. 'use strict';
  2. const report = require('../../utils/report');
  3. const ruleMessages = require('../../utils/ruleMessages');
  4. const validateOptions = require('../../utils/validateOptions');
  5. const ruleName = 'no-irregular-whitespace';
  6. const messages = ruleMessages(ruleName, {
  7. unexpected: 'Unexpected irregular whitespace',
  8. });
  9. const meta = {
  10. url: 'https://stylelint.io/user-guide/rules/no-irregular-whitespace',
  11. };
  12. const IRREGULAR_WHITESPACES = [
  13. '\u000B', // Line Tabulation (\v) - <VT>
  14. '\u000C', // Form Feed (\f) - <FF>
  15. '\u00A0', // No-Break Space - <NBSP>
  16. '\u0085', // Next Line
  17. '\u1680', // Ogham Space Mark
  18. '\u180E', // Mongolian Vowel Separator - <MVS>
  19. '\uFEFF', // Zero Width No-Break Space - <BOM>
  20. '\u2000', // En Quad
  21. '\u2001', // Em Quad
  22. '\u2002', // En Space - <ENSP>
  23. '\u2003', // Em Space - <EMSP>
  24. '\u2004', // Tree-Per-Em
  25. '\u2005', // Four-Per-Em
  26. '\u2006', // Six-Per-Em
  27. '\u2007', // Figure Space
  28. '\u2008', // Punctuation Space - <PUNCSP>
  29. '\u2009', // Thin Space
  30. '\u200A', // Hair Space
  31. '\u200B', // Zero Width Space - <ZWSP>
  32. '\u2028', // Line Separator
  33. '\u2029', // Paragraph Separator
  34. '\u202F', // Narrow No-Break Space
  35. '\u205F', // Medium Mathematical Space
  36. '\u3000', // Ideographic Space
  37. ];
  38. const IRREGULAR_WHITESPACES_PATTERN = new RegExp(`([${IRREGULAR_WHITESPACES.join('')}])`);
  39. /**
  40. * @param {string} str
  41. * @returns {string | null}
  42. */
  43. const findIrregularWhitespace = (str) => {
  44. const result = IRREGULAR_WHITESPACES_PATTERN.exec(str);
  45. return (result && result[1]) || null;
  46. };
  47. /** @type {import('stylelint').Rule} */
  48. const rule = (primary) => {
  49. return (root, result) => {
  50. const validOptions = validateOptions(result, ruleName, { actual: primary });
  51. if (!validOptions) {
  52. return;
  53. }
  54. /**
  55. * @param {import('postcss').Node} node
  56. * @param {string | undefined} value
  57. */
  58. const validate = (node, value) => {
  59. const issue = value && findIrregularWhitespace(value);
  60. if (issue) {
  61. report({
  62. ruleName,
  63. result,
  64. message: messages.unexpected,
  65. node,
  66. word: issue,
  67. });
  68. }
  69. };
  70. root.walkAtRules((atRule) => {
  71. validate(atRule, atRule.name);
  72. validate(atRule, atRule.params);
  73. validate(atRule, atRule.raws.before);
  74. validate(atRule, atRule.raws.after);
  75. validate(atRule, atRule.raws.afterName);
  76. validate(atRule, atRule.raws.between);
  77. });
  78. root.walkRules((ruleNode) => {
  79. validate(ruleNode, ruleNode.selector);
  80. validate(ruleNode, ruleNode.raws.before);
  81. validate(ruleNode, ruleNode.raws.after);
  82. validate(ruleNode, ruleNode.raws.between);
  83. });
  84. root.walkDecls((decl) => {
  85. validate(decl, decl.prop);
  86. validate(decl, decl.value);
  87. validate(decl, decl.raws.before);
  88. validate(decl, decl.raws.between);
  89. });
  90. };
  91. };
  92. rule.ruleName = ruleName;
  93. rule.messages = messages;
  94. rule.meta = meta;
  95. module.exports = rule;