index.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. 'use strict';
  2. const declarationValueIndex = require('../../utils/declarationValueIndex');
  3. const getDeclarationValue = require('../../utils/getDeclarationValue');
  4. const isStandardSyntaxFunction = require('../../utils/isStandardSyntaxFunction');
  5. const { camelCaseFunctions } = require('../../reference/functions');
  6. const optionsMatches = require('../../utils/optionsMatches');
  7. const report = require('../../utils/report');
  8. const ruleMessages = require('../../utils/ruleMessages');
  9. const setDeclarationValue = require('../../utils/setDeclarationValue');
  10. const validateOptions = require('../../utils/validateOptions');
  11. const valueParser = require('postcss-value-parser');
  12. const { isRegExp, isString } = require('../../utils/validateTypes');
  13. const ruleName = 'function-name-case';
  14. const messages = ruleMessages(ruleName, {
  15. expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`,
  16. });
  17. const meta = {
  18. url: 'https://stylelint.io/user-guide/rules/function-name-case',
  19. fixable: true,
  20. };
  21. const mapLowercaseFunctionNamesToCamelCase = new Map();
  22. for (const func of camelCaseFunctions) {
  23. mapLowercaseFunctionNamesToCamelCase.set(func.toLowerCase(), func);
  24. }
  25. /** @type {import('stylelint').Rule} */
  26. const rule = (primary, secondaryOptions, context) => {
  27. return (root, result) => {
  28. const validOptions = validateOptions(
  29. result,
  30. ruleName,
  31. {
  32. actual: primary,
  33. possible: ['lower', 'upper'],
  34. },
  35. {
  36. actual: secondaryOptions,
  37. possible: {
  38. ignoreFunctions: [isString, isRegExp],
  39. },
  40. optional: true,
  41. },
  42. );
  43. if (!validOptions) {
  44. return;
  45. }
  46. root.walkDecls((decl) => {
  47. let needFix = false;
  48. const parsed = valueParser(getDeclarationValue(decl));
  49. parsed.walk((node) => {
  50. if (node.type !== 'function' || !isStandardSyntaxFunction(node)) {
  51. return;
  52. }
  53. const functionName = node.value;
  54. const functionNameLowerCase = functionName.toLowerCase();
  55. if (optionsMatches(secondaryOptions, 'ignoreFunctions', functionName)) {
  56. return;
  57. }
  58. let expectedFunctionName = null;
  59. if (
  60. primary === 'lower' &&
  61. mapLowercaseFunctionNamesToCamelCase.has(functionNameLowerCase)
  62. ) {
  63. expectedFunctionName = mapLowercaseFunctionNamesToCamelCase.get(functionNameLowerCase);
  64. } else if (primary === 'lower') {
  65. expectedFunctionName = functionNameLowerCase;
  66. } else {
  67. expectedFunctionName = functionName.toUpperCase();
  68. }
  69. if (functionName === expectedFunctionName) {
  70. return;
  71. }
  72. if (context.fix) {
  73. needFix = true;
  74. node.value = expectedFunctionName;
  75. return;
  76. }
  77. report({
  78. message: messages.expected(functionName, expectedFunctionName),
  79. node: decl,
  80. index: declarationValueIndex(decl) + node.sourceIndex,
  81. result,
  82. ruleName,
  83. });
  84. });
  85. if (context.fix && needFix) {
  86. setDeclarationValue(decl, parsed.toString());
  87. }
  88. });
  89. };
  90. };
  91. rule.ruleName = ruleName;
  92. rule.messages = messages;
  93. rule.meta = meta;
  94. module.exports = rule;