index.cjs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. // NOTICE: This file is generated by Rollup. To modify it,
  2. // please instead edit the ESM counterpart and rebuild with Rollup (npm run build).
  3. 'use strict';
  4. const valueParser = require('postcss-value-parser');
  5. const validateTypes = require('../../utils/validateTypes.cjs');
  6. const nodeFieldIndices = require('../../utils/nodeFieldIndices.cjs');
  7. const getDeclarationValue = require('../../utils/getDeclarationValue.cjs');
  8. const isStandardSyntaxValue = require('../../utils/isStandardSyntaxValue.cjs');
  9. const typeGuards = require('../../utils/typeGuards.cjs');
  10. const optionsMatches = require('../../utils/optionsMatches.cjs');
  11. const report = require('../../utils/report.cjs');
  12. const ruleMessages = require('../../utils/ruleMessages.cjs');
  13. const setDeclarationValue = require('../../utils/setDeclarationValue.cjs');
  14. const validateOptions = require('../../utils/validateOptions.cjs');
  15. const ruleName = 'alpha-value-notation';
  16. const messages = ruleMessages(ruleName, {
  17. expected: (unfixed, fixed) => `Expected "${unfixed}" to be "${fixed}"`,
  18. });
  19. const meta = {
  20. url: 'https://stylelint.io/user-guide/rules/alpha-value-notation',
  21. fixable: true,
  22. };
  23. const ALPHA_PROPS =
  24. /^(?:opacity|shape-image-threshold|fill-opacity|flood-opacity|stop-opacity|stroke-opacity)$/i;
  25. const ALPHA_FUNCTION = /(?:color|hsla?|rgba?|hwb|lab|lch|oklab|oklch)\(/i;
  26. const ALPHA_FUNCTION_NAME = /^(?:color|hsla?|rgba?|hwb|lab|lch|oklab|oklch)$/i;
  27. const DIGIT = /\d/;
  28. /** @type {import('stylelint').CoreRules[ruleName]} */
  29. const rule = (primary, secondaryOptions) => {
  30. return (root, result) => {
  31. const validOptions = validateOptions(
  32. result,
  33. ruleName,
  34. {
  35. actual: primary,
  36. possible: ['number', 'percentage'],
  37. },
  38. {
  39. actual: secondaryOptions,
  40. possible: {
  41. exceptProperties: [validateTypes.isString, validateTypes.isRegExp],
  42. },
  43. optional: true,
  44. },
  45. );
  46. if (!validOptions) return;
  47. const optionFuncs = Object.freeze({
  48. number: {
  49. expFunc: isNumber,
  50. fixFunc: asNumber,
  51. },
  52. percentage: {
  53. expFunc: isPercentage,
  54. fixFunc: asPercentage,
  55. },
  56. });
  57. root.walkDecls((decl) => {
  58. const declarationValue = getDeclarationValue(decl);
  59. const isAlphaProp = ALPHA_PROPS.test(decl.prop);
  60. const isAlpha = isAlphaProp || ALPHA_FUNCTION.test(declarationValue);
  61. if (!isAlpha || !DIGIT.test(declarationValue)) return;
  62. const parsedValue = valueParser(declarationValue);
  63. parsedValue.walk((node) => {
  64. const inVal = isAlphaProp && DIGIT.test(node.value);
  65. const inFn = node.type === 'function' && ALPHA_FUNCTION_NAME.test(node.value);
  66. const alpha = inVal ? findAlphaInValue(node) : inFn ? findAlphaInFunction(node) : undefined;
  67. if (!alpha) return;
  68. const { value } = alpha;
  69. if (!isStandardSyntaxValue(value)) return;
  70. if (!isNumber(value) && !isPercentage(value)) return;
  71. /** @type {'number' | 'percentage'} */
  72. let expectation = primary;
  73. if (optionsMatches(secondaryOptions, 'exceptProperties', decl.prop)) {
  74. if (expectation === 'number') {
  75. expectation = 'percentage';
  76. } else if (expectation === 'percentage') {
  77. expectation = 'number';
  78. }
  79. }
  80. if (optionFuncs[expectation].expFunc(value)) return;
  81. const fixed = optionFuncs[expectation].fixFunc(value);
  82. const unfixed = value;
  83. const index = nodeFieldIndices.declarationValueIndex(decl) + alpha.sourceIndex;
  84. const endIndex = index + alpha.value.length;
  85. const fix = () => {
  86. alpha.value = fixed;
  87. setDeclarationValue(decl, parsedValue.toString());
  88. };
  89. report({
  90. message: messages.expected,
  91. messageArgs: [unfixed, fixed],
  92. node: decl,
  93. index,
  94. endIndex,
  95. result,
  96. ruleName,
  97. fix: {
  98. apply: fix,
  99. node: decl,
  100. },
  101. });
  102. });
  103. });
  104. };
  105. };
  106. /**
  107. * @param {string} value
  108. * @returns {string}
  109. */
  110. function asPercentage(value) {
  111. const number = Number(value);
  112. return `${Number((number * 100).toPrecision(3))}%`;
  113. }
  114. /**
  115. * @param {string} value
  116. * @returns {string}
  117. */
  118. function asNumber(value) {
  119. const dimension = valueParser.unit(value);
  120. validateTypes.assert(dimension);
  121. const number = Number(dimension.number);
  122. return Number((number / 100).toPrecision(3)).toString();
  123. }
  124. /**
  125. * @template {import('postcss-value-parser').Node} T
  126. * @param {T} node
  127. * @returns {T | undefined}
  128. */
  129. function findAlphaInValue(node) {
  130. return node.type === 'word' || node.type === 'function' ? node : undefined;
  131. }
  132. /**
  133. * @param {import('postcss-value-parser').FunctionNode} node
  134. * @returns {import('postcss-value-parser').Node | undefined}
  135. */
  136. function findAlphaInFunction(node) {
  137. const legacySyntax = node.nodes.some(({ type, value }) => type === 'div' && value === ',');
  138. if (legacySyntax) {
  139. const args = node.nodes.filter(({ type }) => type === 'word' || type === 'function');
  140. if (args.length === 4) return args[3];
  141. return undefined;
  142. }
  143. const slashNodeIndex = node.nodes.findIndex(({ type, value }) => type === 'div' && value === '/');
  144. if (slashNodeIndex !== -1) {
  145. const nodesAfterSlash = node.nodes.slice(slashNodeIndex + 1, node.nodes.length);
  146. return nodesAfterSlash.find(typeGuards.isValueWord);
  147. }
  148. return undefined;
  149. }
  150. /**
  151. * @param {string} value
  152. * @returns {boolean}
  153. */
  154. function isPercentage(value) {
  155. const dimension = valueParser.unit(value);
  156. return dimension && dimension.unit === '%';
  157. }
  158. /**
  159. * @param {string} value
  160. * @returns {boolean}
  161. */
  162. function isNumber(value) {
  163. const dimension = valueParser.unit(value);
  164. return dimension && dimension.unit === '';
  165. }
  166. rule.ruleName = ruleName;
  167. rule.messages = messages;
  168. rule.meta = meta;
  169. module.exports = rule;