index.cjs 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  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 nodeFieldIndices = require('../../utils/nodeFieldIndices.cjs');
  6. const functionArgumentsSearch = require('../../utils/functionArgumentsSearch.cjs');
  7. const isStandardSyntaxValue = require('../../utils/isStandardSyntaxValue.cjs');
  8. const report = require('../../utils/report.cjs');
  9. const ruleMessages = require('../../utils/ruleMessages.cjs');
  10. const validateOptions = require('../../utils/validateOptions.cjs');
  11. const vendor = require('../../utils/vendor.cjs');
  12. const ruleName = 'function-linear-gradient-no-nonstandard-direction';
  13. const messages = ruleMessages(ruleName, {
  14. rejected: 'Unexpected nonstandard direction',
  15. });
  16. const meta = {
  17. url: 'https://stylelint.io/user-guide/rules/function-linear-gradient-no-nonstandard-direction',
  18. };
  19. const LINEAR_GRADIENT_FUNCTION = '(?:-webkit-|-moz-|-o-|-ms-)?linear-gradient';
  20. const LINEAR_GRADIENT_FUNCTION_CALL = new RegExp(`${LINEAR_GRADIENT_FUNCTION}\\(`, 'i');
  21. const LINEAR_GRADIENT_FUNCTION_NAME = new RegExp(`^${LINEAR_GRADIENT_FUNCTION}$`, 'i');
  22. const DIRECTION = /top|left|bottom|right/i;
  23. const DIRECTION_WITH_TO = new RegExp(`^to (${DIRECTION.source})(?: (${DIRECTION.source}))?$`, 'i');
  24. const DIRECTION_WITHOUT_TO = new RegExp(`^(${DIRECTION.source})(?: (${DIRECTION.source}))?$`, 'i');
  25. const DIGIT = /[\d.]/;
  26. const ANGLE = /^[\d.]+(?:deg|grad|rad|turn)$/;
  27. const IN_KEYWORD = /\bin\b/i;
  28. /**
  29. * @param {string} source
  30. * @param {boolean} withToPrefix
  31. */
  32. function isStandardDirection(source, withToPrefix) {
  33. const regexp = withToPrefix ? DIRECTION_WITH_TO : DIRECTION_WITHOUT_TO;
  34. const matches = source.match(regexp);
  35. if (!matches) {
  36. return false;
  37. }
  38. if (matches.length === 2) {
  39. return true;
  40. }
  41. // Cannot repeat side-or-corner, e.g. "to top top"
  42. if (matches.length === 3 && matches[1] !== matches[2]) {
  43. return true;
  44. }
  45. return false;
  46. }
  47. /** @type {import('stylelint').CoreRules[ruleName]} */
  48. const rule = (primary) => {
  49. return (root, result) => {
  50. const validOptions = validateOptions(result, ruleName, { actual: primary });
  51. if (!validOptions) {
  52. return;
  53. }
  54. root.walkDecls((decl) => {
  55. if (!LINEAR_GRADIENT_FUNCTION_CALL.test(decl.value)) return;
  56. valueParser(decl.value).walk((valueNode) => {
  57. if (valueNode.type !== 'function') {
  58. return;
  59. }
  60. functionArgumentsSearch(
  61. valueParser.stringify(valueNode).toLowerCase(),
  62. LINEAR_GRADIENT_FUNCTION_NAME,
  63. (expression, expressionIndex) => {
  64. const args = expression.split(',');
  65. const firstArg = (args[0] || '').trim();
  66. // If the first arg is not standard, return early
  67. if (!isStandardSyntaxValue(firstArg)) {
  68. return;
  69. }
  70. // Skip direction validation for CSS variables
  71. if (firstArg.startsWith('var(')) {
  72. return;
  73. }
  74. // Ignore gradients with modern syntax that have color space interpolation arguments
  75. if (IN_KEYWORD.test(firstArg)) {
  76. return;
  77. }
  78. // If the first character is a number, we can assume the user intends an angle
  79. if (DIGIT.test(firstArg.charAt(0))) {
  80. if (ANGLE.test(firstArg)) {
  81. return;
  82. }
  83. complain();
  84. return;
  85. }
  86. // The first argument may not be a direction: it may be an angle,
  87. // or a color stop (in which case user gets default direction, "to bottom")
  88. // cf. https://drafts.csswg.org/css-images-3/#linear-gradient-syntax
  89. if (!DIRECTION.test(firstArg)) {
  90. return;
  91. }
  92. const withToPrefix = !vendor.prefix(valueNode.value);
  93. if (!isStandardDirection(firstArg, withToPrefix)) {
  94. complain();
  95. }
  96. function complain() {
  97. const index = nodeFieldIndices.declarationValueIndex(decl) + valueNode.sourceIndex + expressionIndex;
  98. const endIndex = index + (args[0] || '').trimEnd().length;
  99. report({
  100. message: messages.rejected,
  101. messageArgs: [],
  102. node: decl,
  103. index,
  104. endIndex,
  105. result,
  106. ruleName,
  107. });
  108. }
  109. },
  110. );
  111. });
  112. });
  113. };
  114. };
  115. rule.ruleName = ruleName;
  116. rule.messages = messages;
  117. rule.meta = meta;
  118. module.exports = rule;