index.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. // @ts-nocheck
  2. 'use strict';
  3. const _ = require('lodash');
  4. const valueParser = require('postcss-value-parser');
  5. const declarationValueIndex = require('../../utils/declarationValueIndex');
  6. const isStandardSyntaxDeclaration = require('../../utils/isStandardSyntaxDeclaration');
  7. const isStandardSyntaxValue = require('../../utils/isStandardSyntaxValue');
  8. const optionsMatches = require('../../utils/optionsMatches');
  9. const report = require('../../utils/report');
  10. const ruleMessages = require('../../utils/ruleMessages');
  11. const validateOptions = require('../../utils/validateOptions');
  12. const ruleName = 'alpha-value-notation';
  13. const messages = ruleMessages(ruleName, {
  14. expected: (unfixed, fixed) => `Expected "${unfixed}" to be "${fixed}"`,
  15. });
  16. const ALPHA_PROPS = ['opacity', 'shape-image-threshold'];
  17. const ALPHA_FUNCS = ['hsl', 'hsla', 'hwb', 'lab', 'lch', 'rgb', 'rgba'];
  18. function rule(primary, options, context) {
  19. return (root, result) => {
  20. const validOptions = validateOptions(
  21. result,
  22. ruleName,
  23. {
  24. actual: primary,
  25. possible: ['number', 'percentage'],
  26. },
  27. {
  28. actual: options,
  29. possible: {
  30. exceptProperties: [_.isString, _.isRegExp],
  31. },
  32. optional: true,
  33. },
  34. );
  35. if (!validOptions) return;
  36. root.walkDecls((decl) => {
  37. if (!isStandardSyntaxDeclaration(decl)) return;
  38. let needsFix = false;
  39. const parsedValue = valueParser(getValue(decl));
  40. parsedValue.walk((node) => {
  41. let alpha;
  42. if (ALPHA_PROPS.includes(decl.prop.toLowerCase())) {
  43. alpha = findAlphaInValue(node);
  44. } else {
  45. if (node.type !== 'function') return;
  46. if (!ALPHA_FUNCS.includes(node.value.toLowerCase())) return;
  47. alpha = findAlphaInFunction(node);
  48. }
  49. if (!alpha) return;
  50. const { value } = alpha;
  51. if (!isStandardSyntaxValue(value)) return;
  52. if (!isNumber(value) && !isPercentage(value)) return;
  53. const optionFuncs = {
  54. number: {
  55. expFunc: isNumber,
  56. fixFunc: asNumber,
  57. },
  58. percentage: {
  59. expFunc: isPercentage,
  60. fixFunc: asPercentage,
  61. },
  62. };
  63. let expectation = primary;
  64. if (optionsMatches(options, 'exceptProperties', decl.prop)) {
  65. expectation = Object.keys(optionFuncs).filter((key) => key !== expectation);
  66. }
  67. if (optionFuncs[expectation].expFunc(value)) return;
  68. const fixed = optionFuncs[expectation].fixFunc(value);
  69. const unfixed = value;
  70. if (context.fix) {
  71. alpha.value = fixed;
  72. needsFix = true;
  73. return;
  74. }
  75. report({
  76. message: messages.expected(unfixed, fixed),
  77. node: decl,
  78. index: declarationValueIndex(decl) + alpha.sourceIndex,
  79. result,
  80. ruleName,
  81. });
  82. });
  83. if (needsFix) {
  84. setValue(decl, parsedValue.toString());
  85. }
  86. });
  87. };
  88. }
  89. function asPercentage(value) {
  90. return `${Number((value * 100).toPrecision(3))}%`;
  91. }
  92. function asNumber(value) {
  93. const { number } = valueParser.unit(value);
  94. return Number((number / 100).toPrecision(3));
  95. }
  96. function findAlphaInValue(node) {
  97. return node.type === 'word' || node.type === 'function' ? node : false;
  98. }
  99. function findAlphaInFunction(node) {
  100. const args = node.nodes.filter(({ type }) => type === 'word' || type === 'function');
  101. if (args.length === 4) return args[3];
  102. return false;
  103. }
  104. function isPercentage(value) {
  105. const { unit } = valueParser.unit(value);
  106. return unit && unit === '%';
  107. }
  108. function isNumber(value) {
  109. const { unit } = valueParser.unit(value);
  110. return unit === '';
  111. }
  112. function getValue(decl) {
  113. return decl.raws.value ? decl.raws.value.raw : decl.value;
  114. }
  115. function setValue(decl, value) {
  116. if (decl.raws.value) decl.raws.value.raw = value;
  117. else decl.value = value;
  118. return decl;
  119. }
  120. rule.ruleName = ruleName;
  121. rule.messages = messages;
  122. module.exports = rule;