index.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. // @ts-nocheck
  2. 'use strict';
  3. const _ = require('lodash');
  4. const report = require('../../utils/report');
  5. const ruleMessages = require('../../utils/ruleMessages');
  6. const validateOptions = require('../../utils/validateOptions');
  7. const valueParser = require('postcss-value-parser');
  8. const ruleName = 'function-max-empty-lines';
  9. const messages = ruleMessages(ruleName, {
  10. expected: (max) => `Expected no more than ${max} empty ${max === 1 ? 'line' : 'lines'}`,
  11. });
  12. function placeIndexOnValueStart(decl) {
  13. return decl.prop.length + decl.raws.between.length - 1;
  14. }
  15. function rule(max, options, context) {
  16. const maxAdjacentNewlines = max + 1;
  17. return (root, result) => {
  18. const validOptions = validateOptions(result, ruleName, {
  19. actual: max,
  20. possible: _.isNumber,
  21. });
  22. if (!validOptions) {
  23. return;
  24. }
  25. const violatedCRLFNewLinesRegex = new RegExp(`(?:\r\n){${maxAdjacentNewlines + 1},}`);
  26. const violatedLFNewLinesRegex = new RegExp(`\n{${maxAdjacentNewlines + 1},}`);
  27. const allowedLFNewLinesString = context.fix ? '\n'.repeat(maxAdjacentNewlines) : '';
  28. const allowedCRLFNewLinesString = context.fix ? '\r\n'.repeat(maxAdjacentNewlines) : '';
  29. root.walkDecls((decl) => {
  30. if (!decl.value.includes('(')) {
  31. return;
  32. }
  33. const stringValue = decl.raws.value ? decl.raws.value.raw : decl.value;
  34. const splittedValue = [];
  35. let sourceIndexStart = 0;
  36. valueParser(stringValue).walk((node) => {
  37. if (
  38. node.type !== 'function' /* ignore non functions */ ||
  39. node.value.length === 0 /* ignore sass lists */
  40. ) {
  41. return;
  42. }
  43. const stringifiedNode = valueParser.stringify(node);
  44. if (
  45. !violatedLFNewLinesRegex.test(stringifiedNode) &&
  46. !violatedCRLFNewLinesRegex.test(stringifiedNode)
  47. ) {
  48. return;
  49. }
  50. if (context.fix) {
  51. const newNodeString = stringifiedNode
  52. .replace(new RegExp(violatedLFNewLinesRegex, 'gm'), allowedLFNewLinesString)
  53. .replace(new RegExp(violatedCRLFNewLinesRegex, 'gm'), allowedCRLFNewLinesString);
  54. splittedValue.push([
  55. stringValue.slice(sourceIndexStart, node.sourceIndex),
  56. newNodeString,
  57. ]);
  58. sourceIndexStart = node.sourceIndex + stringifiedNode.length;
  59. } else {
  60. report({
  61. message: messages.expected(max),
  62. node: decl,
  63. index: placeIndexOnValueStart(decl) + node.sourceIndex,
  64. result,
  65. ruleName,
  66. });
  67. }
  68. });
  69. if (context.fix && splittedValue.length > 0) {
  70. const updatedValue =
  71. splittedValue.reduce((acc, curr) => acc + curr[0] + curr[1], '') +
  72. stringValue.slice(sourceIndexStart);
  73. if (decl.raws.value) {
  74. decl.raws.value.raw = updatedValue;
  75. } else {
  76. decl.value = updatedValue;
  77. }
  78. }
  79. });
  80. };
  81. }
  82. rule.ruleName = ruleName;
  83. rule.messages = messages;
  84. module.exports = rule;