@salesforce-ux/eslint-plugin-slds 1.0.5 → 1.0.6-internal

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +62 -0
  2. package/build/index.js +280 -141
  3. package/build/index.js.map +4 -4
  4. package/build/rules/enforce-bem-usage.js +5 -5
  5. package/build/rules/enforce-bem-usage.js.map +3 -3
  6. package/build/rules/no-deprecated-classes-slds2.js +2 -2
  7. package/build/rules/no-deprecated-classes-slds2.js.map +2 -2
  8. package/build/rules/v9/enforce-bem-usage.js +2 -2
  9. package/build/rules/v9/enforce-bem-usage.js.map +2 -2
  10. package/build/rules/v9/enforce-component-hook-naming-convention.js +2 -2
  11. package/build/rules/v9/enforce-component-hook-naming-convention.js.map +2 -2
  12. package/build/rules/v9/enforce-sds-to-slds-hooks.js +2 -2
  13. package/build/rules/v9/enforce-sds-to-slds-hooks.js.map +2 -2
  14. package/build/rules/v9/lwc-token-to-slds-hook.js +2 -2
  15. package/build/rules/v9/lwc-token-to-slds-hook.js.map +2 -2
  16. package/build/rules/v9/no-deprecated-slds-classes.js +2 -2
  17. package/build/rules/v9/no-deprecated-slds-classes.js.map +2 -2
  18. package/build/rules/v9/no-deprecated-tokens-slds1.js +2 -2
  19. package/build/rules/v9/no-deprecated-tokens-slds1.js.map +2 -2
  20. package/build/rules/v9/no-hardcoded-values/handlers/boxShadowHandler.js +86 -30
  21. package/build/rules/v9/no-hardcoded-values/handlers/boxShadowHandler.js.map +3 -3
  22. package/build/rules/v9/no-hardcoded-values/handlers/colorHandler.js +116 -104
  23. package/build/rules/v9/no-hardcoded-values/handlers/colorHandler.js.map +3 -3
  24. package/build/rules/v9/no-hardcoded-values/handlers/densityHandler.js +83 -30
  25. package/build/rules/v9/no-hardcoded-values/handlers/densityHandler.js.map +3 -3
  26. package/build/rules/v9/no-hardcoded-values/handlers/fontHandler.js +78 -74
  27. package/build/rules/v9/no-hardcoded-values/handlers/fontHandler.js.map +3 -3
  28. package/build/rules/v9/no-hardcoded-values/handlers/index.js +175 -99
  29. package/build/rules/v9/no-hardcoded-values/handlers/index.js.map +3 -3
  30. package/build/rules/v9/no-hardcoded-values/no-hardcoded-values-slds1.js +223 -103
  31. package/build/rules/v9/no-hardcoded-values/no-hardcoded-values-slds1.js.map +3 -3
  32. package/build/rules/v9/no-hardcoded-values/no-hardcoded-values-slds2.js +223 -103
  33. package/build/rules/v9/no-hardcoded-values/no-hardcoded-values-slds2.js.map +3 -3
  34. package/build/rules/v9/no-hardcoded-values/noHardcodedValueRule.js +221 -101
  35. package/build/rules/v9/no-hardcoded-values/noHardcodedValueRule.js.map +3 -3
  36. package/build/rules/v9/no-hardcoded-values/ruleOptionsSchema.js +63 -0
  37. package/build/rules/v9/no-hardcoded-values/ruleOptionsSchema.js.map +7 -0
  38. package/build/rules/v9/no-slds-class-overrides.js +2 -2
  39. package/build/rules/v9/no-slds-class-overrides.js.map +2 -2
  40. package/build/rules/v9/no-slds-namespace-for-custom-hooks.js +2 -2
  41. package/build/rules/v9/no-slds-namespace-for-custom-hooks.js.map +2 -2
  42. package/build/rules/v9/no-slds-var-without-fallback.js +2 -2
  43. package/build/rules/v9/no-slds-var-without-fallback.js.map +2 -2
  44. package/build/rules/v9/no-sldshook-fallback-for-lwctoken.js +2 -2
  45. package/build/rules/v9/no-sldshook-fallback-for-lwctoken.js.map +2 -2
  46. package/build/rules/v9/no-unsupported-hooks-slds2.js +2 -2
  47. package/build/rules/v9/no-unsupported-hooks-slds2.js.map +2 -2
  48. package/build/src/rules/v9/no-hardcoded-values/ruleOptionsSchema.d.ts +40 -0
  49. package/build/src/types/index.d.ts +31 -0
  50. package/build/src/utils/color-lib-utils.d.ts +16 -9
  51. package/build/src/utils/custom-mapping-utils.d.ts +9 -0
  52. package/build/src/utils/hardcoded-shared-utils.d.ts +1 -0
  53. package/build/src/utils/property-matcher.d.ts +3 -1
  54. package/build/types/index.js.map +1 -1
  55. package/build/utils/boxShadowValueParser.js.map +2 -2
  56. package/build/utils/color-lib-utils.js +26 -50
  57. package/build/utils/color-lib-utils.js.map +2 -2
  58. package/build/utils/css-utils.js.map +2 -2
  59. package/build/utils/custom-mapping-utils.js +62 -0
  60. package/build/utils/custom-mapping-utils.js.map +7 -0
  61. package/build/utils/hardcoded-shared-utils.js +29 -16
  62. package/build/utils/hardcoded-shared-utils.js.map +2 -2
  63. package/build/utils/property-matcher.js +20 -6
  64. package/build/utils/property-matcher.js.map +2 -2
  65. package/build/utils/styling-hook-utils.js.map +1 -1
  66. package/eslint.config.mjs +0 -4
  67. package/package.json +2 -2
@@ -67,11 +67,7 @@ function isCssColorFunction(value) {
67
67
  }
68
68
 
69
69
  // src/utils/color-lib-utils.ts
70
- var LAB_THRESHOLD = 25;
71
- var isHexCode = (color) => {
72
- const hexPattern = /^#(?:[0-9a-fA-F]{3}){1,2}$/;
73
- return hexPattern.test(color);
74
- };
70
+ var DELTAE_THRESHOLD = 10;
75
71
  var convertToHex = (color) => {
76
72
  try {
77
73
  return (0, import_chroma_js.default)(color).hex();
@@ -79,52 +75,40 @@ var convertToHex = (color) => {
79
75
  return null;
80
76
  }
81
77
  };
78
+ var isHookPropertyMatch = (hook, cssProperty) => {
79
+ return hook.properties.includes(cssProperty) || hook.properties.includes("*");
80
+ };
81
+ function getOrderByCssProp(cssProperty) {
82
+ if (cssProperty === "color" || cssProperty === "fill") {
83
+ return ["surface", "theme", "feedback", "reference"];
84
+ } else if (cssProperty.match(/background/)) {
85
+ return ["surface", "surface-inverse", "theme", "feedback", "reference"];
86
+ } else if (cssProperty.match(/border/) || cssProperty.match(/outline/) || cssProperty.match(/stroke/)) {
87
+ return ["borders", "borders-inverse", "feedback", "theme", "reference"];
88
+ }
89
+ return ["surface", "surface-inverse", "borders", "borders-inverse", "theme", "feedback", "reference"];
90
+ }
82
91
  var findClosestColorHook = (color, supportedColors, cssProperty) => {
83
- const returnStylingHooks = [];
84
- const closestHooksWithSameProperty = [];
85
- const closestHooksWithoutSameProperty = [];
86
- const closestHooksWithAllProperty = [];
87
- const labColor = (0, import_chroma_js.default)(color).lab();
92
+ const closestHooks = [];
88
93
  Object.entries(supportedColors).forEach(([sldsValue, data]) => {
89
- if (sldsValue && isHexCode(sldsValue)) {
94
+ if (sldsValue && isValidColor(sldsValue)) {
90
95
  const hooks = data;
91
96
  hooks.forEach((hook) => {
92
- const labSupportedColor = (0, import_chroma_js.default)(sldsValue).lab();
93
- const distance = JSON.stringify(labColor) === JSON.stringify(labSupportedColor) ? 0 : import_chroma_js.default.distance(import_chroma_js.default.lab(...labColor), import_chroma_js.default.lab(...labSupportedColor), "lab");
94
- if (hook.properties.includes(cssProperty)) {
95
- if (distance <= LAB_THRESHOLD) {
96
- closestHooksWithSameProperty.push({ name: hook.name, distance });
97
- }
98
- } else if (hook.properties.includes("*")) {
99
- if (distance <= LAB_THRESHOLD) {
100
- closestHooksWithAllProperty.push({ name: hook.name, distance });
101
- }
102
- } else {
103
- if (distance <= LAB_THRESHOLD) {
104
- closestHooksWithoutSameProperty.push({ name: hook.name, distance });
105
- }
97
+ const distance = sldsValue.toLowerCase() === color.toLowerCase() ? 0 : import_chroma_js.default.deltaE(sldsValue, color);
98
+ if (isHookPropertyMatch(hook, cssProperty) && distance <= DELTAE_THRESHOLD) {
99
+ closestHooks.push({ distance, group: hook.group, name: hook.name });
106
100
  }
107
101
  });
108
102
  }
109
103
  });
110
- const closesthookGroups = [
111
- { hooks: closestHooksWithSameProperty, distance: 0 },
112
- { hooks: closestHooksWithAllProperty, distance: 0 },
113
- { hooks: closestHooksWithSameProperty, distance: Infinity },
114
- // For hooks with distance > 0
115
- { hooks: closestHooksWithAllProperty, distance: Infinity },
116
- { hooks: closestHooksWithoutSameProperty, distance: Infinity }
117
- ];
118
- for (const group of closesthookGroups) {
119
- const filteredHooks = group.hooks.filter(
120
- (h) => group.distance === 0 ? h.distance === 0 : h.distance > 0
121
- );
122
- if (returnStylingHooks.length < 1 && filteredHooks.length > 0) {
123
- filteredHooks.sort((a, b) => a.distance - b.distance);
124
- returnStylingHooks.push(...filteredHooks.slice(0, 5).map((h) => h.name));
104
+ const hooksByGroupMap = closestHooks.sort((a, b) => a.distance - b.distance).reduce((acc, hook) => {
105
+ if (!acc[hook.group]) {
106
+ acc[hook.group] = [];
125
107
  }
126
- }
127
- return Array.from(new Set(returnStylingHooks));
108
+ acc[hook.group].push(hook.name);
109
+ return acc;
110
+ }, {});
111
+ return getOrderByCssProp(cssProperty).map((group) => hooksByGroupMap[group] || []).flat().slice(0, 5);
128
112
  };
129
113
  var isValidColor = (val) => import_chroma_js.default.valid(val);
130
114
  var extractColorValue = (node) => {
@@ -158,41 +142,13 @@ var INSET_REGEX = new RegExp(`^inset(?:-${INSET_VALUES})?$`);
158
142
  function isBorderColorProperty(cssProperty) {
159
143
  return BORDER_COLOR_REGEX.test(cssProperty);
160
144
  }
161
- function isBorderWidthProperty(cssProperty) {
162
- return BORDER_WIDTH_REGEX.test(cssProperty);
163
- }
164
- function isMarginProperty(cssProperty) {
165
- return MARGIN_REGEX.test(cssProperty);
166
- }
167
- function isPaddingProperty(cssProperty) {
168
- return PADDING_REGEX.test(cssProperty);
169
- }
170
- function isBorderRadius(cssProperty) {
171
- return BORDER_RADIUS_REGEX.test(cssProperty);
172
- }
173
- function isDimensionProperty(cssProperty) {
174
- return ["width", "height", "min-width", "max-width", "min-height", "max-height"].includes(cssProperty);
175
- }
176
- function isInsetProperty(cssProperty) {
177
- return INSET_REGEX.test(cssProperty);
178
- }
179
- function resolvePropertyToMatch(cssProperty) {
145
+ function resolveColorPropertyToMatch(cssProperty) {
180
146
  const propertyToMatch = cssProperty.toLowerCase();
181
- if (propertyToMatch === "outline" || propertyToMatch === "outline-width" || isBorderWidthProperty(propertyToMatch)) {
182
- return "border-width";
183
- } else if (isMarginProperty(propertyToMatch)) {
184
- return "margin";
185
- } else if (isPaddingProperty(propertyToMatch)) {
186
- return "padding";
187
- } else if (isBorderRadius(propertyToMatch)) {
188
- return "border-radius";
189
- } else if (isDimensionProperty(propertyToMatch)) {
190
- return "width";
191
- } else if (isInsetProperty(propertyToMatch)) {
192
- return "top";
193
- } else if (cssProperty === "background" || cssProperty === "background-color") {
147
+ if (propertyToMatch === "outline" || propertyToMatch === "outline-color") {
148
+ return "border-color";
149
+ } else if (propertyToMatch === "background" || propertyToMatch === "background-color") {
194
150
  return "background-color";
195
- } else if (cssProperty === "outline" || cssProperty === "outline-color" || isBorderColorProperty(cssProperty)) {
151
+ } else if (isBorderColorProperty(propertyToMatch)) {
196
152
  return "border-color";
197
153
  }
198
154
  return propertyToMatch;
@@ -201,13 +157,33 @@ function resolvePropertyToMatch(cssProperty) {
201
157
  // src/utils/hardcoded-shared-utils.ts
202
158
  var import_css_tree2 = require("@eslint/css-tree");
203
159
  function handleShorthandAutoFix(declarationNode, context, valueText, replacements) {
204
- const sortedReplacements = replacements.sort((a, b) => a.start - b.start);
205
- const hasAnyHooks = sortedReplacements.some((r) => r.hasHook);
206
- const canAutoFix = hasAnyHooks;
207
- sortedReplacements.forEach(({ start, end, replacement, displayValue, hasHook }) => {
160
+ if (!replacements || replacements.length === 0) {
161
+ return;
162
+ }
163
+ const sortedReplacements = replacements.sort((a, b) => b.start - a.start);
164
+ const reportNumericValue = context.options?.reportNumericValue || "always";
165
+ const fixCallback = (start, originalValue, replacement) => {
166
+ let newValue = valueText;
167
+ newValue = newValue.substring(0, start) + replacement + newValue.substring(start + originalValue.length);
168
+ if (newValue !== valueText) {
169
+ return (fixer) => {
170
+ return fixer.replaceText(declarationNode.value, newValue);
171
+ };
172
+ }
173
+ };
174
+ sortedReplacements.forEach(({ start, end, replacement, displayValue, hasHook, isNumeric }) => {
208
175
  const originalValue = valueText.substring(start, end);
209
- const valueStartColumn = declarationNode.value.loc.start.column;
210
- const valueColumn = valueStartColumn + start;
176
+ if (isNumeric) {
177
+ if (reportNumericValue === "never") {
178
+ return;
179
+ }
180
+ if (reportNumericValue === "hasReplacement" && !hasHook) {
181
+ return;
182
+ }
183
+ }
184
+ const valueColumnStart = declarationNode.value.loc.start.column + start;
185
+ const valueColumnEnd = valueColumnStart + originalValue.length;
186
+ const canAutoFix = originalValue !== replacement;
211
187
  const { loc: { start: locStart, end: locEnd } } = declarationNode.value;
212
188
  const reportNode = {
213
189
  ...declarationNode.value,
@@ -215,23 +191,16 @@ function handleShorthandAutoFix(declarationNode, context, valueText, replacement
215
191
  ...declarationNode.value.loc,
216
192
  start: {
217
193
  ...locStart,
218
- column: valueColumn
194
+ column: valueColumnStart
219
195
  },
220
196
  end: {
221
197
  ...locEnd,
222
- column: valueColumn + originalValue.length
198
+ column: valueColumnEnd
223
199
  }
224
200
  }
225
201
  };
226
202
  if (hasHook) {
227
- const fix = canAutoFix ? (fixer) => {
228
- let newValue = valueText;
229
- for (let i = sortedReplacements.length - 1; i >= 0; i--) {
230
- const { start: rStart, end: rEnd, replacement: rReplacement } = sortedReplacements[i];
231
- newValue = newValue.substring(0, rStart) + rReplacement + newValue.substring(rEnd);
232
- }
233
- return fixer.replaceText(declarationNode.value, newValue);
234
- } : void 0;
203
+ const fix = canAutoFix ? fixCallback(start, originalValue, replacement) : void 0;
235
204
  context.context.report({
236
205
  node: reportNode,
237
206
  messageId: "hardcodedValue",
@@ -292,6 +261,41 @@ function formatSuggestionHooks(hooks) {
292
261
  return "\n" + hooks.map((hook, index) => `${index + 1}. ${hook}`).join("\n");
293
262
  }
294
263
 
264
+ // src/utils/custom-mapping-utils.ts
265
+ function matchesPropertyPattern(cssProperty, pattern) {
266
+ const normalizedProperty = cssProperty.toLowerCase();
267
+ const normalizedPattern = pattern.toLowerCase();
268
+ if (normalizedProperty === normalizedPattern) {
269
+ return true;
270
+ }
271
+ if (normalizedPattern.endsWith("*")) {
272
+ const prefix = normalizedPattern.slice(0, -1);
273
+ return normalizedProperty.startsWith(prefix);
274
+ }
275
+ return false;
276
+ }
277
+ function getCustomMapping(cssProperty, value, customMapping) {
278
+ if (!customMapping) {
279
+ return null;
280
+ }
281
+ const normalizedValue = value.toLowerCase().trim();
282
+ for (const [hookName, config] of Object.entries(customMapping)) {
283
+ const propertyMatches = config.properties.some(
284
+ (pattern) => matchesPropertyPattern(cssProperty, pattern)
285
+ );
286
+ if (!propertyMatches) {
287
+ continue;
288
+ }
289
+ const valueMatches = config.values.some(
290
+ (configValue) => configValue.toLowerCase().trim() === normalizedValue
291
+ );
292
+ if (valueMatches) {
293
+ return hookName;
294
+ }
295
+ }
296
+ return null;
297
+ }
298
+
295
299
  // src/rules/v9/no-hardcoded-values/handlers/colorHandler.ts
296
300
  var handleColorDeclaration = (node, context) => {
297
301
  const cssProperty = node.property.toLowerCase();
@@ -315,24 +319,32 @@ function createColorReplacement(colorValue, cssProperty, context, positionInfo,
315
319
  if (!hexValue) {
316
320
  return null;
317
321
  }
318
- const propToMatch = resolvePropertyToMatch(cssProperty);
319
- const closestHooks = findClosestColorHook(hexValue, context.valueToStylinghook, propToMatch);
320
322
  const start = positionInfo.start.offset;
321
323
  const end = positionInfo.end.offset;
322
324
  const originalValue = originalValueText ? originalValueText.substring(start, end) : colorValue;
323
- if (closestHooks.length === 1) {
324
- return {
325
- start,
326
- end,
327
- replacement: `var(${closestHooks[0]}, ${colorValue})`,
328
- displayValue: closestHooks[0],
329
- hasHook: true
330
- };
331
- } else if (closestHooks.length > 1) {
325
+ const customHook = getCustomMapping(cssProperty, colorValue, context.options?.customMapping);
326
+ let closestHooks = [];
327
+ if (customHook) {
328
+ closestHooks = [customHook];
329
+ } else {
330
+ const propToMatch = resolveColorPropertyToMatch(cssProperty);
331
+ closestHooks = findClosestColorHook(hexValue, context.valueToStylinghook, propToMatch);
332
+ }
333
+ let replacement = originalValue;
334
+ let paletteHook = null;
335
+ if (context.options?.preferPaletteHook && closestHooks.length > 1) {
336
+ paletteHook = closestHooks.filter((hook) => hook.includes("-palette-"))[0];
337
+ }
338
+ if (paletteHook) {
339
+ replacement = `var(${paletteHook}, ${colorValue})`;
340
+ } else if (closestHooks.length === 1) {
341
+ replacement = `var(${closestHooks[0]}, ${colorValue})`;
342
+ }
343
+ if (closestHooks.length > 0) {
332
344
  return {
333
345
  start,
334
346
  end,
335
- replacement: originalValue,
347
+ replacement,
336
348
  // Use original value to preserve spacing
337
349
  displayValue: formatSuggestionHooks(closestHooks),
338
350
  hasHook: true
@@ -341,7 +353,7 @@ function createColorReplacement(colorValue, cssProperty, context, positionInfo,
341
353
  return {
342
354
  start,
343
355
  end,
344
- replacement: originalValue,
356
+ replacement,
345
357
  // Use original value to preserve spacing
346
358
  displayValue: originalValue,
347
359
  hasHook: false
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../../../../src/rules/v9/no-hardcoded-values/handlers/colorHandler.ts", "../../../../../src/utils/color-lib-utils.ts", "../../../../../src/utils/css-functions.ts", "../../../../../src/utils/property-matcher.ts", "../../../../../src/utils/hardcoded-shared-utils.ts", "../../../../../src/utils/css-utils.ts"],
4
- "sourcesContent": ["import { findClosestColorHook, convertToHex, isValidColor } from '../../../../utils/color-lib-utils';\nimport { resolvePropertyToMatch } from '../../../../utils/property-matcher';\nimport { formatSuggestionHooks } from '../../../../utils/css-utils';\nimport type { HandlerContext, DeclarationHandler } from '../../../../types';\n\n// Import shared utilities for common logic\nimport { \n handleShorthandAutoFix, \n forEachColorValue,\n type ReplacementInfo,\n type PositionInfo\n} from '../../../../utils/hardcoded-shared-utils';\n\n/**\n * Handle color declarations using CSS tree parsing\n * Supports shorthand properties like background, border, etc. \n * Uses css-tree for reliable AST-based parsing + chroma-js validation\n */\nexport const handleColorDeclaration: DeclarationHandler = (node: any, context: HandlerContext) => {\n const cssProperty = node.property.toLowerCase();\n const valueText = context.sourceCode.getText(node.value);\n const replacements: ReplacementInfo[] = [];\n \n forEachColorValue(valueText, (colorValue, positionInfo) => {\n if (colorValue !== 'transparent' && isValidColor(colorValue)) {\n const replacement = createColorReplacement(colorValue, cssProperty, context, positionInfo, valueText);\n if (replacement) {\n replacements.push(replacement);\n }\n }\n });\n \n // Apply shorthand auto-fix once all values are processed\n handleShorthandAutoFix(node, context, valueText, replacements);\n};\n\n\n\n\n\n/**\n * Create color replacement info for shorthand auto-fix\n * Returns replacement data or null if no valid replacement\n */\nfunction createColorReplacement(\n colorValue: string,\n cssProperty: string,\n context: HandlerContext,\n positionInfo: PositionInfo,\n originalValueText?: string\n): ReplacementInfo | null {\n if (!positionInfo?.start) {\n return null;\n }\n\n const hexValue = convertToHex(colorValue);\n if (!hexValue) {\n return null;\n }\n\n const propToMatch = resolvePropertyToMatch(cssProperty);\n const closestHooks = findClosestColorHook(hexValue, context.valueToStylinghook, propToMatch);\n\n // Use position information directly from CSS tree (already 0-based offsets)\n const start = positionInfo.start.offset;\n const end = positionInfo.end.offset;\n \n // Extract the original value from the CSS text to preserve spacing\n const originalValue = originalValueText ? originalValueText.substring(start, end) : colorValue;\n\n if (closestHooks.length === 1) {\n // Has a single hook replacement - should provide autofix\n return {\n start,\n end,\n replacement: `var(${closestHooks[0]}, ${colorValue})`,\n displayValue: closestHooks[0],\n hasHook: true\n };\n } else if (closestHooks.length > 1) {\n // Multiple hooks - format them for better readability\n return {\n start,\n end,\n replacement: originalValue, // Use original value to preserve spacing\n displayValue: formatSuggestionHooks(closestHooks),\n hasHook: true\n };\n } else {\n // No hooks - keep original value\n return {\n start,\n end,\n replacement: originalValue, // Use original value to preserve spacing\n displayValue: originalValue,\n hasHook: false\n };\n }\n}\n\n\n\n\n\n", "//stylelint-sds/packages/stylelint-plugin-slds/src/utils/color-lib-utils.ts\nimport { ValueToStylingHooksMapping, ValueToStylingHookEntry } from '@salesforce-ux/sds-metadata';\nimport chroma from 'chroma-js';\nimport { generate } from '@eslint/css-tree';\nimport { isCssColorFunction } from './css-functions';\n\nconst LAB_THRESHOLD = 25; // Adjust this to set how strict the matching should be\n\nconst isHardCodedColor = (color: string): boolean => {\n const colorRegex =\n /\\b(rgb|rgba)\\((\\s*\\d{1,3}\\s*,\\s*){2,3}\\s*(0|1|0?\\.\\d+)\\s*\\)|#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})\\b|[a-zA-Z]+/g;\n return colorRegex.test(color);\n};\n\nconst isHexCode = (color: string): boolean => {\n const hexPattern = /^#(?:[0-9a-fA-F]{3}){1,2}$/; // Pattern for #RGB or #RRGGBB\n return hexPattern.test(color);\n};\n\n// Convert a named color or hex code into a hex format using chroma-js\nconst convertToHex = (color: string): string | null => {\n try {\n // Try converting the color using chroma-js, which handles both named and hex colors\n return chroma(color).hex();\n } catch (e) {\n // If chroma can't process the color, it's likely invalid\n return null;\n }\n};\n\n// Find the closest color hook using LAB distance\nconst findClosestColorHook = (\n color: string,\n supportedColors:ValueToStylingHooksMapping,\n cssProperty: string\n): string[] => {\n const returnStylingHooks: string[] = [];\n const closestHooksWithSameProperty: { name: string; distance: number }[] = [];\n const closestHooksWithoutSameProperty: { name: string; distance: number }[] =\n [];\n const closestHooksWithAllProperty: { name: string; distance: number }[] =\n [];\n const labColor = chroma(color).lab();\n\n Object.entries(supportedColors).forEach(([sldsValue, data]) => {\n if (sldsValue && isHexCode(sldsValue)) {\n const hooks = data as ValueToStylingHookEntry[]; // Get the hooks for the sldsValue\n\n hooks.forEach((hook) => {\n const labSupportedColor = chroma(sldsValue).lab();\n const distance = (JSON.stringify(labColor) === JSON.stringify(labSupportedColor)) ? 0\n : chroma.distance(chroma.lab(...labColor), chroma.lab(...labSupportedColor), \"lab\");\n // Check if the hook has the same property\n if (hook.properties.includes(cssProperty)) {\n // Add to same property hooks if within threshold\n if (distance <= LAB_THRESHOLD) {\n closestHooksWithSameProperty.push({ name: hook.name, distance });\n }\n } \n // Check for the universal selector\n else if ( hook.properties.includes(\"*\") ){\n // Add to same property hooks if within threshold\n if (distance <= LAB_THRESHOLD) {\n closestHooksWithAllProperty.push({ name: hook.name, distance });\n }\n }\n else {\n // Add to different property hooks if within threshold\n if (distance <= LAB_THRESHOLD) {\n closestHooksWithoutSameProperty.push({ name: hook.name, distance });\n }\n }\n });\n }\n });\n\n// Group hooks by their priority\nconst closesthookGroups = [\n { hooks: closestHooksWithSameProperty, distance: 0 },\n { hooks: closestHooksWithAllProperty, distance: 0 },\n { hooks: closestHooksWithSameProperty, distance: Infinity }, // For hooks with distance > 0\n { hooks: closestHooksWithAllProperty, distance: Infinity },\n { hooks: closestHooksWithoutSameProperty, distance: Infinity },\n];\n\nfor (const group of closesthookGroups) {\n // Filter hooks based on the distance condition\n const filteredHooks = group.hooks.filter(h => \n group.distance === 0 ? h.distance === 0 : h.distance > 0\n );\n\n if (returnStylingHooks.length < 1 && filteredHooks.length > 0) {\n filteredHooks.sort((a, b) => a.distance - b.distance);\n returnStylingHooks.push(...filteredHooks.slice(0, 5).map((h) => h.name));\n }\n}\n\n\n return Array.from(new Set(returnStylingHooks));\n};\n\n/**\n * This method is usefull to identify all possible css color values.\n * - names colors\n * - 6,8 digit hex\n * - rgb and rgba\n * - hsl and hsla\n */\nconst isValidColor = (val:string):boolean => chroma.valid(val);\n\n/**\n * Extract color value from CSS AST node\n */\nconst extractColorValue = (node: any): string | null => {\n let colorValue: string | null = null;\n \n switch (node.type) {\n case 'Hash':\n colorValue = `#${node.value}`;\n break;\n case 'Identifier':\n colorValue = node.name;\n break;\n case 'Function':\n // Only extract color functions\n if (isCssColorFunction(node.name)) {\n colorValue = generate(node);\n }\n break;\n }\n \n return colorValue && isValidColor(colorValue) ? colorValue : null;\n};\n\nexport { findClosestColorHook, convertToHex, isHexCode, isHardCodedColor, isValidColor, extractColorValue };\n", "//stylelint-sds/packages/stylelint-plugin-slds/src/utils/css-functions.ts\n/**\n * Complete list of CSS functions that should be preserved/recognized\n */\nconst CSS_FUNCTIONS = [\n 'attr',\n 'calc',\n 'color-mix',\n 'conic-gradient',\n 'counter',\n 'cubic-bezier',\n 'linear-gradient',\n 'max',\n 'min',\n 'radial-gradient',\n 'repeating-conic-gradient',\n 'repeating-linear-gradient',\n 'repeating-radial-gradient',\n 'var'\n ];\n \n \n const CSS_MATH_FUNCTIONS = ['calc', 'min', 'max'];\n \n \n const RGB_COLOR_FUNCTIONS = ['rgb', 'rgba', 'hsl', 'hsla'];\n \n /**\n * Regex for matching any CSS function (for general detection)\n * Matches function names within other text\n */\n const cssFunctionsRegex = new RegExp(`(?:${CSS_FUNCTIONS.join('|')})`);\n \n \n const cssFunctionsExactRegex = new RegExp(`^(?:${CSS_FUNCTIONS.join('|')})$`);\n \n \n const cssMathFunctionsRegex = new RegExp(`^(?:${CSS_MATH_FUNCTIONS.join('|')})$`);\n \n export function containsCssFunction(value: string): boolean {\n return cssFunctionsRegex.test(value);\n }\n \n /**\n * Check if a value is exactly a CSS function name\n */\n export function isCssFunction(value: string): boolean {\n return cssFunctionsExactRegex.test(value);\n }\n \n export function isCssMathFunction(value: string): boolean {\n return cssMathFunctionsRegex.test(value);\n }\n \n export function isCssColorFunction(value: string): boolean {\n return RGB_COLOR_FUNCTIONS.includes(value);\n }", "///stylelint-sds/packages/stylelint-plugin-slds/src/utils/property-matcher.ts\n/**\n * Check if any of the hook properties match the provided cssProperty using wildcard matching.\n * @param hookProperties - Array of property patterns (can contain wildcards like `*`)\n * @param cssProperty - The CSS property to be checked\n * @returns true if a match is found, otherwise false\n */\nexport function matchesCssProperty(\n hookProperties: string[],\n cssProperty: string\n): boolean {\n return hookProperties.some((propertyPattern: string) => {\n const regexPattern = new RegExp(\n '^' + propertyPattern.replace(/\\*/g, '.*') + '$'\n );\n return regexPattern.test(cssProperty);\n });\n}\n\n// Directions & Corners\nconst DIRECTION_VALUES = '(?:top|right|bottom|left|inline|block|inline-start|inline-end|start|end|block-start|block-end)';\nconst CORNER_VALUES = '(?:top-left|top-right|bottom-right|bottom-left|start-start|start-end|end-start|end-end)';\nconst INSET_VALUES = '(?:inline|block|inline-start|inline-end|block-start|block-end)';\n\n\n// Pre-compiled regex patterns for better performance\nconst BORDER_COLOR_REGEX = new RegExp(`^border(?:-${DIRECTION_VALUES})?-color$`);\nconst BORDER_WIDTH_REGEX = new RegExp(`^border(?:-${DIRECTION_VALUES})?-width$`);\nconst MARGIN_REGEX = new RegExp(`^margin(?:-${DIRECTION_VALUES})?$`);\nconst PADDING_REGEX = new RegExp(`^padding(?:-${DIRECTION_VALUES})?$`);\nconst BORDER_RADIUS_REGEX = new RegExp(`^border(?:-${CORNER_VALUES})?-radius$`);\nconst INSET_REGEX = new RegExp(`^inset(?:-${INSET_VALUES})?$`);\n\nexport function isBorderColorProperty(cssProperty: string): boolean {\n return BORDER_COLOR_REGEX.test(cssProperty);\n}\n\nexport function isBorderWidthProperty(cssProperty: string): boolean {\n return BORDER_WIDTH_REGEX.test(cssProperty);\n}\n\nexport function isMarginProperty(cssProperty: string): boolean {\n return MARGIN_REGEX.test(cssProperty);\n}\n\nexport function isPaddingProperty(cssProperty: string): boolean {\n return PADDING_REGEX.test(cssProperty);\n}\n\nexport function isBorderRadius(cssProperty: string): boolean {\n return BORDER_RADIUS_REGEX.test(cssProperty);\n}\n\nexport function isDimensionProperty(cssProperty: string): boolean {\n return ['width', 'height', 'min-width', 'max-width', 'min-height', 'max-height'].includes(cssProperty);\n}\n\nexport function isInsetProperty(cssProperty: string): boolean {\n return INSET_REGEX.test(cssProperty);\n}\n\nexport const fontProperties = [\n 'font',\n 'font-size', \n 'font-weight'\n];\n\nexport const colorProperties = [\n 'color',\n 'fill',\n 'background',\n 'background-color',\n 'stroke',\n 'border',\n 'border*',\n 'border*-color',\n 'outline',\n 'outline-color',\n];\n\nexport const densificationProperties = [\n 'border*',\n 'margin*',\n 'padding*',\n 'width',\n 'height',\n 'min-width',\n 'max-width',\n 'min-height',\n 'max-height',\n 'inset',\n 'top',\n 'right',\n 'left',\n 'bottom',\n 'outline',\n 'outline-width',\n 'line-height'\n]; \n\n/**\n * Convert property patterns to CSS AST selector patterns\n * Handles wildcards (*) and creates proper ESLint CSS selector syntax\n */\nexport function toSelector(properties: string[]): string {\n const selectorParts = properties.map(prop => {\n if (prop.includes('*')) {\n // Convert wildcards to regex patterns for CSS AST selectors\n // Anchor to start of string to prevent matching CSS custom properties\n const regexPattern = prop.replace(/\\*/g, '.*');\n return `Declaration[property=/^${regexPattern}$/]`;\n } else {\n // Exact property match\n return `Declaration[property='${prop}']`;\n }\n });\n \n return selectorParts.join(', ');\n}\n\nexport function resolvePropertyToMatch(cssProperty:string){\n const propertyToMatch = cssProperty.toLowerCase();\n if(propertyToMatch === 'outline' || propertyToMatch === 'outline-width' || isBorderWidthProperty(propertyToMatch)){\n return 'border-width';\n } else if(isMarginProperty(propertyToMatch)){\n return 'margin';\n } else if(isPaddingProperty(propertyToMatch)){\n return 'padding';\n } else if(isBorderRadius(propertyToMatch)){\n return 'border-radius';\n } else if(isDimensionProperty(propertyToMatch)){\n // Stylinghooks includes only width as property to match, for all other dimensions we need to match width\n return 'width';\n } else if(isInsetProperty(propertyToMatch)){\n // Stylinghooks includes only top/left/right/bottom as property to match, for all other insets we need to match top\n return 'top';\n } else if(cssProperty === 'background' || cssProperty === 'background-color'){\n return 'background-color';\n } else if(cssProperty === 'outline' || cssProperty === 'outline-color' || isBorderColorProperty(cssProperty)){\n return 'border-color';\n }\n return propertyToMatch;\n}", "import { parse, walk } from '@eslint/css-tree';\nimport type { HandlerContext } from '../types';\nimport type { ParsedUnitValue } from './value-utils';\nimport { ALLOWED_UNITS } from './value-utils';\nimport { extractColorValue } from './color-lib-utils';\nimport { isCssFunction } from './css-functions';\n\n/**\n * Common replacement data structure used by both color and density handlers\n */\nexport interface ReplacementInfo {\n start: number;\n end: number;\n replacement: string; // Full CSS var: var(--hook, fallback)\n displayValue: string; // Just the hook: --hook\n hasHook: boolean;\n}\n\n/**\n * Position information from CSS tree parsing\n */\nexport interface PositionInfo {\n start?: { offset: number; line: number; column: number };\n end?: { offset: number; line: number; column: number };\n}\n\n/**\n * Generic callback for processing values with position information\n */\nexport type ValueCallback<T> = (value: T, positionInfo?: PositionInfo) => void;\n\n/**\n * Known valid font-weight values\n */\nconst FONT_WEIGHTS = [\n 'normal',\n 'bold', \n 'bolder',\n 'lighter',\n '100',\n '200', \n '300',\n '400',\n '500',\n '600',\n '700',\n '800',\n '900'\n];\n\n/**\n * Check if a value is a known font-weight\n */\nexport function isKnownFontWeight(value: string | number): boolean {\n const stringValue = value.toString();\n return FONT_WEIGHTS.includes(stringValue.toLowerCase());\n}\n\n/**\n * Generic shorthand auto-fix handler\n * Handles the common logic for reconstructing shorthand values with replacements\n */\nexport function handleShorthandAutoFix(\n declarationNode: any,\n context: HandlerContext,\n valueText: string,\n replacements: ReplacementInfo[]\n) {\n // Sort replacements by position for proper reconstruction\n const sortedReplacements = replacements.sort((a, b) => a.start - b.start);\n \n // Check if we can apply auto-fix (at least one value has a hook)\n const hasAnyHooks = sortedReplacements.some(r => r.hasHook);\n const canAutoFix = hasAnyHooks;\n\n // Report each individual value\n sortedReplacements.forEach(({ start, end, replacement, displayValue, hasHook }) => {\n const originalValue = valueText.substring(start, end);\n const valueStartColumn = declarationNode.value.loc.start.column;\n const valueColumn = valueStartColumn + start;\n \n // Create precise error location for this value\n const { loc: { start: locStart, end: locEnd } } = declarationNode.value;\n const reportNode = {\n ...declarationNode.value,\n loc: {\n ...declarationNode.value.loc,\n start: {\n ...locStart,\n column: valueColumn\n },\n end: {\n ...locEnd,\n column: valueColumn + originalValue.length\n }\n }\n };\n\n if (hasHook) {\n // Create auto-fix for the entire shorthand value\n const fix = canAutoFix ? (fixer: any) => {\n // Reconstruct the entire value with all replacements\n let newValue = valueText;\n \n // Apply replacements from right to left to maintain string positions\n for (let i = sortedReplacements.length - 1; i >= 0; i--) {\n const { start: rStart, end: rEnd, replacement: rReplacement } = sortedReplacements[i];\n newValue = newValue.substring(0, rStart) + rReplacement + newValue.substring(rEnd);\n }\n \n return fixer.replaceText(declarationNode.value, newValue);\n } : undefined;\n\n context.context.report({\n node: reportNode,\n messageId: 'hardcodedValue',\n data: {\n oldValue: originalValue,\n newValue: displayValue\n },\n fix\n });\n } else {\n // No hook available\n context.context.report({\n node: reportNode,\n messageId: 'noReplacement',\n data: {\n oldValue: originalValue\n }\n });\n }\n });\n}\n\n/**\n * Generic CSS tree traversal with position tracking\n * Always provides position information since both handlers need it\n */\nexport function forEachValue<T>(\n valueText: string,\n extractValue: (node: any) => T | null,\n shouldSkipNode: (node: any) => boolean,\n callback: (value: T, positionInfo: PositionInfo) => void\n): void {\n if (!valueText || typeof valueText !== 'string') {\n return;\n }\n\n try {\n const ast = parse(valueText, { context: 'value' as const, positions: true });\n \n walk(ast, {\n enter(node: any) {\n // Skip nodes efficiently using this.skip\n if (shouldSkipNode(node)) {\n return this.skip;\n }\n \n const value = extractValue(node);\n if (value !== null) {\n const positionInfo: PositionInfo = {\n start: node.loc?.start,\n end: node.loc?.end\n };\n callback(value, positionInfo);\n }\n }\n });\n } catch (error) {\n // Silently handle parse errors\n return;\n }\n}\n\n/**\n * Check if color node should be skipped during traversal\n */\nfunction shouldSkipColorNode(node: any): boolean {\n return node.type === 'Function' && isCssFunction(node.name);\n}\n\n/**\n * Check if dimension node should be skipped during traversal\n * Skip all function nodes by default\n */\nfunction shouldSkipDimensionNode(node: any): boolean {\n return node.type === 'Function';\n}\n\n/**\n * Extract dimension value from CSS AST node\n * Returns structured data with number and unit to eliminate regex parsing\n */\nfunction extractDimensionValue(valueNode: any, cssProperty?: string): ParsedUnitValue | null {\n if (!valueNode) return null;\n \n switch (valueNode.type) {\n case 'Dimension':\n // Dimensions: 16px, 1rem -> extract value and unit directly from AST\n const numValue = Number(valueNode.value);\n if (numValue === 0) return null; // Skip zero values\n \n const unit = valueNode.unit.toLowerCase();\n if (!ALLOWED_UNITS.includes(unit)) return null; // Support only allowed units\n \n return {\n number: numValue,\n unit: unit as 'px' | 'rem' | '%' | 'em' | 'ch'\n };\n \n case 'Number':\n // Numbers: 400, 1.5 -> treat as unitless (font-weight, line-height, etc.)\n const numberValue = Number(valueNode.value);\n if (numberValue === 0) return null; // Skip zero values\n \n return {\n number: numberValue,\n unit: null\n };\n \n case 'Percentage':\n // Percentage values: 100%, 50% -> extract value and add % unit\n const percentValue = Number(valueNode.value);\n if (percentValue === 0) return null; // Skip zero values\n \n return {\n number: percentValue,\n unit: '%'\n };\n \n case 'Value':\n // Value wrapper - extract from first child\n return valueNode.children?.[0] ? extractDimensionValue(valueNode.children[0], cssProperty) : null;\n }\n \n return null;\n}\n\n/**\n * Specialized color value traversal\n * Handles color-specific extraction and skipping logic\n */\nexport function forEachColorValue(\n valueText: string,\n callback: (colorValue: string, positionInfo: PositionInfo) => void\n): void {\n forEachValue(valueText, extractColorValue, shouldSkipColorNode, callback);\n}\n\n/**\n * Specialized density value traversal\n * Handles dimension-specific extraction and skipping logic\n */\nexport function forEachDensityValue(\n valueText: string,\n cssProperty: string,\n callback: (parsedDimension: ParsedUnitValue, positionInfo: PositionInfo) => void\n): void {\n forEachValue(\n valueText, \n (node) => extractDimensionValue(node, cssProperty), \n shouldSkipDimensionNode, \n callback\n );\n}\n\n/**\n * Extract font-related values from CSS AST node\n * Handles font-size and font-weight values\n */\nfunction extractFontValue(node: any): ParsedUnitValue | null {\n if (!node) return null;\n \n switch (node.type) {\n case 'Dimension':\n // Font-size: 16px, 1rem, etc.\n const numValue = Number(node.value);\n if (numValue <= 0) return null; // Skip zero/negative values\n \n const unit = node.unit.toLowerCase();\n if (!ALLOWED_UNITS.includes(unit)) return null;\n \n return {\n number: numValue,\n unit: unit as 'px' | 'rem' | '%' | 'em' | 'ch'\n };\n \n case 'Number':\n // Font-weight: 400, 700, etc.\n const numberValue = Number(node.value);\n if (numberValue <= 0) {\n return null; // Skip zero/negative values\n }\n \n // Only accept known font-weight values for unitless numbers\n if (!isKnownFontWeight(numberValue)) {\n return null; // Skip values that aren't valid font-weights\n }\n \n return {\n number: numberValue,\n unit: null\n };\n \n case 'Identifier':\n // Font-weight keywords: normal, bold, etc.\n const namedValue = node.name.toLowerCase();\n \n // Only accept known font-weight keywords\n if (!isKnownFontWeight(namedValue)) {\n return null;\n }\n \n // Convert known keywords to numeric values\n if (namedValue === 'normal') {\n return { number: 400, unit: null };\n }\n \n // For other keywords (bolder, lighter), we can't determine exact numeric value\n // but we know they're valid font-weight values\n return { number: namedValue, unit: null };\n \n case 'Percentage':\n // Percentage values for font-size\n const percentValue = Number(node.value);\n if (percentValue === 0) return null; // Skip zero values\n \n return {\n number: percentValue,\n unit: '%'\n };\n \n case 'Value':\n // Value wrapper - extract from first child\n return node.children?.[0] ? extractFontValue(node.children[0]) : null;\n }\n \n return null;\n}\n\n/**\n * Check if font node should be skipped during traversal\n * Skip all function nodes by default\n */\nfunction shouldSkipFontNode(node: any): boolean {\n return node.type === 'Function';\n}\n\n/**\n * Specialized font value traversal\n * Handles font-specific extraction and skipping logic\n */\nexport function forEachFontValue(\n valueText: string,\n callback: (fontValue: ParsedUnitValue, positionInfo: PositionInfo) => void\n): void {\n forEachValue(valueText, extractFontValue, shouldSkipFontNode, callback);\n}\n", "import { \n forEachValue, \n type PositionInfo \n} from './hardcoded-shared-utils';\n\n/**\n * Check if a CSS property should be targeted for linting based on prefixes or explicit targets\n * @param property - The CSS property name to check\n * @param propertyTargets - Array of specific properties to target (empty means target all)\n * @returns true if the property should be targeted\n */\nexport function isTargetProperty(property: string, propertyTargets: string[] = []): boolean {\n if (typeof property !== 'string') return false;\n return property.startsWith('--sds-')\n || property.startsWith('--slds-')\n || property.startsWith('--lwc-')\n || propertyTargets.length === 0\n || propertyTargets.includes(property);\n}\n\n/**\n * CSS Variable information for SLDS variable detection\n */\nexport interface CssVariableInfo {\n name: string; // Variable name: --slds-g-color-surface-1\n hasFallback: boolean; // Whether var() already has a fallback\n}\n\n/**\n * Generic CSS variable extractor that can be customized for different use cases\n * @param node - AST node to extract from\n * @param filter - Function to validate and extract variable information\n * @returns Extracted variable info or null\n */\nfunction extractCssVariable<T>(\n node: any,\n filter: (variableName: string, childrenArray: any[]) => T | null\n): T | null {\n if (!node || node.type !== 'Function' || node.name !== 'var') {\n return null;\n }\n\n if (!node.children) {\n return null;\n }\n\n // Convert children to array and get the first child (variable name)\n const childrenArray = Array.from(node.children);\n if (childrenArray.length === 0) {\n return null;\n }\n \n const firstChild = childrenArray[0] as any;\n if (!firstChild || firstChild.type !== 'Identifier') {\n return null;\n }\n\n const variableName = firstChild.name;\n if (!variableName) {\n return null;\n }\n\n return filter(variableName, childrenArray);\n}\n\n/**\n * Specialized CSS variable traversal for SLDS variables\n * Finds var(--slds-*) functions and reports their fallback status\n */\nexport function forEachSldsVariable(\n valueText: string,\n callback: (variableInfo: CssVariableInfo, positionInfo: PositionInfo) => void\n): void {\n const extractor = (node: any) => extractCssVariable(node, (variableName, childrenArray) => {\n if (!variableName.startsWith('--slds-')) {\n return null;\n }\n\n // Check if there's a fallback (comma separator)\n const hasFallback = childrenArray.some((child: any) => \n child.type === 'Operator' && child.value === ','\n );\n\n return { name: variableName, hasFallback };\n });\n\n forEachValue(valueText, extractor, () => false, callback);\n}\n\n/**\n * Specialized CSS variable traversal for SLDS/SDS namespace detection\n * Finds var(--slds-*) or var(--sds-*) functions in CSS values\n * Note: hasFallback is set to false as it's unused for namespace validation\n */\nexport function forEachNamespacedVariable(\n valueText: string,\n callback: (variableInfo: CssVariableInfo, positionInfo: PositionInfo) => void\n): void {\n const extractor = (node: any) => extractCssVariable(node, (variableName) => {\n // Check for SLDS or SDS namespace\n if (variableName.startsWith('--slds-') || variableName.startsWith('--sds-')) {\n return { name: variableName, hasFallback: false }; // hasFallback unused, but required by interface\n }\n return null;\n });\n\n forEachValue(valueText, extractor, () => false, callback);\n}\n\n/**\n * Specialized CSS variable traversal for LWC variables\n * Finds var(--lwc-*) functions in CSS values and reports their fallback status\n */\nexport function forEachLwcVariable(\n valueText: string,\n callback: (variableInfo: CssVariableInfo, positionInfo: PositionInfo) => void\n): void {\n const extractor = (node: any) => extractCssVariable(node, (variableName, childrenArray) => {\n if (!variableName.startsWith('--lwc-')) {\n return null;\n }\n\n // Check if there's a fallback (comma separator)\n const hasFallback = childrenArray.some((child: any) => \n child.type === 'Operator' && child.value === ','\n );\n\n return { name: variableName, hasFallback };\n });\n\n forEachValue(valueText, extractor, () => false, callback);\n}\n\n/**\n * Format multiple hook suggestions for better readability\n * @param hooks - Array of hook names to format\n * @returns Formatted string with hooks\n */\nexport function formatSuggestionHooks(hooks: string[]): string {\n if (hooks.length === 1) {\n return `${hooks[0]}`;\n }\n\n // Loop through hooks and append each as a numbered list item with line breaks\n return '\\n' + hooks.map((hook, index) => `${index + 1}. ${hook}`).join('\\n');\n}"],
5
- "mappings": "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",
3
+ "sources": ["../../../../../src/rules/v9/no-hardcoded-values/handlers/colorHandler.ts", "../../../../../src/utils/color-lib-utils.ts", "../../../../../src/utils/css-functions.ts", "../../../../../src/utils/property-matcher.ts", "../../../../../src/utils/hardcoded-shared-utils.ts", "../../../../../src/utils/css-utils.ts", "../../../../../src/utils/custom-mapping-utils.ts"],
4
+ "sourcesContent": ["import { findClosestColorHook, convertToHex, isValidColor } from '../../../../utils/color-lib-utils';\nimport { resolveColorPropertyToMatch } from '../../../../utils/property-matcher';\nimport { formatSuggestionHooks } from '../../../../utils/css-utils';\nimport { getCustomMapping } from '../../../../utils/custom-mapping-utils';\nimport type { HandlerContext, DeclarationHandler } from '../../../../types';\n\n// Import shared utilities for common logic\nimport { \n handleShorthandAutoFix, \n forEachColorValue,\n type ReplacementInfo,\n type PositionInfo\n} from '../../../../utils/hardcoded-shared-utils';\n\n\n/**\n * Handle color declarations using CSS tree parsing\n * Supports shorthand properties like background, border, etc. \n * Uses css-tree for reliable AST-based parsing + chroma-js validation\n */\nexport const handleColorDeclaration: DeclarationHandler = (node: any, context: HandlerContext) => {\n const cssProperty = node.property.toLowerCase();\n const valueText = context.sourceCode.getText(node.value);\n const replacements: ReplacementInfo[] = [];\n \n forEachColorValue(valueText, (colorValue, positionInfo) => {\n if (colorValue !== 'transparent' && isValidColor(colorValue)) {\n const replacement = createColorReplacement(colorValue, cssProperty, context, positionInfo, valueText);\n if (replacement) {\n replacements.push(replacement);\n }\n }\n });\n \n // Apply shorthand auto-fix once all values are processed\n handleShorthandAutoFix(node, context, valueText, replacements);\n};\n\n\n/**\n * Create color replacement info for shorthand auto-fix\n * Returns replacement data or null if no valid replacement\n */\nfunction createColorReplacement(\n colorValue: string,\n cssProperty: string,\n context: HandlerContext,\n positionInfo: PositionInfo,\n originalValueText?: string\n): ReplacementInfo | null {\n if (!positionInfo?.start) {\n return null;\n }\n\n const hexValue = convertToHex(colorValue);\n if (!hexValue) {\n return null;\n }\n\n // Use position information directly from CSS tree (already 0-based offsets)\n const start = positionInfo.start.offset;\n const end = positionInfo.end.offset;\n \n // Extract the original value from the CSS text to preserve spacing\n const originalValue = originalValueText ? originalValueText.substring(start, end) : colorValue;\n\n // Check custom mapping first\n const customHook = getCustomMapping(cssProperty, colorValue, context.options?.customMapping);\n let closestHooks: string[] = [];\n \n if (customHook) {\n // Use custom mapping if available\n closestHooks = [customHook];\n } else {\n // Otherwise, find closest hooks from metadata\n const propToMatch = resolveColorPropertyToMatch(cssProperty);\n closestHooks = findClosestColorHook(hexValue, context.valueToStylinghook, propToMatch);\n }\n\n let replacement = originalValue;\n let paletteHook = null;\n // Apply preferPaletteHook filter if enabled\n if (context.options?.preferPaletteHook && closestHooks.length > 1) {\n paletteHook = closestHooks.filter(hook => hook.includes('-palette-'))[0];\n }\n if(paletteHook){\n replacement = `var(${paletteHook}, ${colorValue})`;\n } else if(closestHooks.length === 1){\n replacement = `var(${closestHooks[0]}, ${colorValue})`;\n }\n\n if (closestHooks.length > 0) {\n // Multiple hooks - format them for better readability\n return {\n start,\n end,\n replacement, // Use original value to preserve spacing\n displayValue: formatSuggestionHooks(closestHooks),\n hasHook: true \n };\n } else {\n // No hooks - keep original value\n return {\n start,\n end,\n replacement, // Use original value to preserve spacing\n displayValue: originalValue,\n hasHook: false\n };\n }\n}\n\n\n\n\n\n", "import { ValueToStylingHooksMapping, ValueToStylingHookEntry } from '@salesforce-ux/sds-metadata/next';\nimport chroma from 'chroma-js';\nimport { generate } from '@eslint/css-tree';\nimport { isCssColorFunction } from './css-functions';\n\n/**\n * Perceptual color difference threshold (Delta E, CIEDE2000 via chroma.deltaE).\n * Lower values are stricter matches. Used to decide which hooks are \"close enough\".\n */\nconst DELTAE_THRESHOLD = 10;\n\n/**\n * Convert any valid CSS color (named, hex, rgb(a), hsl(a), etc.) to hex.\n * Returns null if the value is not a valid color.\n */\nconst convertToHex = (color: string): string | null => {\n try {\n // Try converting the color using chroma-js, which handles both named and hex colors\n return chroma(color).hex();\n } catch (e) {\n // If chroma can't process the color, it's likely invalid\n return null;\n }\n};\n\nconst isHookPropertyMatch = (hook: ValueToStylingHookEntry, cssProperty: string): boolean => {\n return hook.properties.includes(cssProperty) || hook.properties.includes(\"*\");\n}\n\nfunction getOrderByCssProp(cssProperty: string): string[] {\n if(cssProperty === 'color' || cssProperty === 'fill') {\n return [\"surface\", \"theme\", \"feedback\", \"reference\"];\n } else if(cssProperty.match(/background/)){\n return [\"surface\", \"surface-inverse\", \"theme\", \"feedback\", \"reference\"];\n } else if(cssProperty.match(/border/) || cssProperty.match(/outline/) || cssProperty.match(/stroke/)) {\n return [\"borders\", \"borders-inverse\", \"feedback\", \"theme\", \"reference\"];\n }\n return [\"surface\", \"surface-inverse\", \"borders\", \"borders-inverse\", \"theme\", \"feedback\", \"reference\"];\n}\n\n\n/**\n * Given an input color and the metadata mapping of supported colors to hooks,\n * suggest up to 5 styling hook names ordered by:\n * 1) Category priority: semantic -> system -> palette\n * 2) Perceptual distance (Delta E)\n * Also prioritizes exact color matches (distance 0).\n */\nconst findClosestColorHook = (\n color: string,\n supportedColors:ValueToStylingHooksMapping,\n cssProperty: string\n): string[] => {\n const closestHooks: Array<{distance: number, group: string, name: string}> = [];\n Object.entries(supportedColors).forEach(([sldsValue, data]) => {\n if (sldsValue && isValidColor(sldsValue)) {\n const hooks = data as ValueToStylingHookEntry[]; // Get the hooks for the sldsValue\n\n hooks.forEach((hook) => {\n // Exact match shortcut to avoid floating rounding noise\n const distance = (sldsValue.toLowerCase() === color.toLowerCase())\n ? 0\n : chroma.deltaE(sldsValue, color);\n \n // Check if the hook has the same property or universal selector\n if (isHookPropertyMatch(hook, cssProperty) && distance <= DELTAE_THRESHOLD) {\n // Add to same property hooks if within threshold\n closestHooks.push({ distance, group: hook.group, name: hook.name });\n }\n });\n }\n });\n\n const hooksByGroupMap:Record<string, string[]> = closestHooks.sort((a, b) => a.distance - b.distance).reduce((acc, hook) => {\n if (!acc[hook.group]) {\n acc[hook.group] = [];\n }\n acc[hook.group].push(hook.name);\n return acc;\n }, {});\n\n return getOrderByCssProp(cssProperty)\n .map(group => hooksByGroupMap[group]||[])\n .flat().slice(0, 5);\n};\n\n/**\n * Check if a value is any valid CSS color string (delegates to chroma-js).\n */\nconst isValidColor = (val:string):boolean => chroma.valid(val);\n\n/**\n * Extract a color string from a CSS AST node produced by @eslint/css-tree.\n * Supports Hash (#rrggbb), Identifier (named colors), and color Function nodes.\n * Returns null if the extracted value is not a valid color.\n */\nconst extractColorValue = (node: any): string | null => {\n let colorValue: string | null = null;\n \n switch (node.type) {\n case 'Hash':\n colorValue = `#${node.value}`;\n break;\n case 'Identifier':\n colorValue = node.name;\n break;\n case 'Function':\n // Only extract color functions\n if (isCssColorFunction(node.name)) {\n colorValue = generate(node);\n }\n break;\n }\n \n return colorValue && isValidColor(colorValue) ? colorValue : null;\n};\n\nexport { findClosestColorHook, convertToHex, isValidColor, extractColorValue };\n", "//stylelint-sds/packages/stylelint-plugin-slds/src/utils/css-functions.ts\n/**\n * Complete list of CSS functions that should be preserved/recognized\n */\nconst CSS_FUNCTIONS = [\n 'attr',\n 'calc',\n 'color-mix',\n 'conic-gradient',\n 'counter',\n 'cubic-bezier',\n 'linear-gradient',\n 'max',\n 'min',\n 'radial-gradient',\n 'repeating-conic-gradient',\n 'repeating-linear-gradient',\n 'repeating-radial-gradient',\n 'var'\n ];\n \n \n const CSS_MATH_FUNCTIONS = ['calc', 'min', 'max'];\n \n \n const RGB_COLOR_FUNCTIONS = ['rgb', 'rgba', 'hsl', 'hsla'];\n \n /**\n * Regex for matching any CSS function (for general detection)\n * Matches function names within other text\n */\n const cssFunctionsRegex = new RegExp(`(?:${CSS_FUNCTIONS.join('|')})`);\n \n \n const cssFunctionsExactRegex = new RegExp(`^(?:${CSS_FUNCTIONS.join('|')})$`);\n \n \n const cssMathFunctionsRegex = new RegExp(`^(?:${CSS_MATH_FUNCTIONS.join('|')})$`);\n \n export function containsCssFunction(value: string): boolean {\n return cssFunctionsRegex.test(value);\n }\n \n /**\n * Check if a value is exactly a CSS function name\n */\n export function isCssFunction(value: string): boolean {\n return cssFunctionsExactRegex.test(value);\n }\n \n export function isCssMathFunction(value: string): boolean {\n return cssMathFunctionsRegex.test(value);\n }\n \n export function isCssColorFunction(value: string): boolean {\n return RGB_COLOR_FUNCTIONS.includes(value);\n }", "///stylelint-sds/packages/stylelint-plugin-slds/src/utils/property-matcher.ts\n/**\n * Check if any of the hook properties match the provided cssProperty using wildcard matching.\n * @param hookProperties - Array of property patterns (can contain wildcards like `*`)\n * @param cssProperty - The CSS property to be checked\n * @returns true if a match is found, otherwise false\n */\nexport function matchesCssProperty(\n hookProperties: string[],\n cssProperty: string\n): boolean {\n return hookProperties.some((propertyPattern: string) => {\n const regexPattern = new RegExp(\n '^' + propertyPattern.replace(/\\*/g, '.*') + '$'\n );\n return regexPattern.test(cssProperty);\n });\n}\n\n// Directions & Corners\nconst DIRECTION_VALUES = '(?:top|right|bottom|left|inline|block|inline-start|inline-end|start|end|block-start|block-end)';\nconst CORNER_VALUES = '(?:top-left|top-right|bottom-right|bottom-left|start-start|start-end|end-start|end-end)';\nconst INSET_VALUES = '(?:inline|block|inline-start|inline-end|block-start|block-end)';\n\n\n// Pre-compiled regex patterns for better performance\nconst BORDER_COLOR_REGEX = new RegExp(`^border(?:-${DIRECTION_VALUES})?-color$`);\nconst BORDER_WIDTH_REGEX = new RegExp(`^border(?:-${DIRECTION_VALUES})?-width$`);\nconst MARGIN_REGEX = new RegExp(`^margin(?:-${DIRECTION_VALUES})?$`);\nconst PADDING_REGEX = new RegExp(`^padding(?:-${DIRECTION_VALUES})?$`);\nconst BORDER_RADIUS_REGEX = new RegExp(`^border(?:-${CORNER_VALUES})?-radius$`);\nconst INSET_REGEX = new RegExp(`^inset(?:-${INSET_VALUES})?$`);\n\nexport function isBorderColorProperty(cssProperty: string): boolean {\n return BORDER_COLOR_REGEX.test(cssProperty);\n}\n\nexport function isBorderWidthProperty(cssProperty: string): boolean {\n return BORDER_WIDTH_REGEX.test(cssProperty);\n}\n\nexport function isMarginProperty(cssProperty: string): boolean {\n return MARGIN_REGEX.test(cssProperty);\n}\n\nexport function isPaddingProperty(cssProperty: string): boolean {\n return PADDING_REGEX.test(cssProperty);\n}\n\nexport function isBorderRadius(cssProperty: string): boolean {\n return BORDER_RADIUS_REGEX.test(cssProperty);\n}\n\nexport function isDimensionProperty(cssProperty: string): boolean {\n return ['width', 'height', 'min-width', 'max-width', 'min-height', 'max-height'].includes(cssProperty);\n}\n\nexport function isInsetProperty(cssProperty: string): boolean {\n return INSET_REGEX.test(cssProperty);\n}\n\nexport const fontProperties = [\n 'font',\n 'font-size', \n 'font-weight'\n];\n\nexport const colorProperties = [\n 'color',\n 'fill',\n 'background',\n 'background-color',\n 'stroke',\n 'border',\n 'border*',\n 'border*-color',\n 'outline',\n 'outline-color',\n];\n\nexport const densificationProperties = [\n 'border*',\n 'margin*',\n 'padding*',\n 'width',\n 'height',\n 'min-width',\n 'max-width',\n 'min-height',\n 'max-height',\n 'inset',\n 'top',\n 'right',\n 'left',\n 'bottom',\n 'outline',\n 'outline-width',\n 'line-height'\n]; \n\n/**\n * Convert property patterns to CSS AST selector patterns\n * Handles wildcards (*) and creates proper ESLint CSS selector syntax\n */\nexport function toSelector(properties: string[]): string {\n const selectorParts = properties.map(prop => {\n if (prop.includes('*')) {\n // Convert wildcards to regex patterns for CSS AST selectors\n // Anchor to start of string to prevent matching CSS custom properties\n const regexPattern = prop.replace(/\\*/g, '.*');\n return `Declaration[property=/^${regexPattern}$/]`;\n } else {\n // Exact property match\n return `Declaration[property='${prop}']`;\n }\n });\n \n return selectorParts.join(', ');\n}\n\nexport function resolveDensityPropertyToMatch(cssProperty:string){\n const propertyToMatch = cssProperty.toLowerCase();\n if(isOutlineWidthProperty(propertyToMatch) || isBorderWidthProperty(propertyToMatch)){\n return 'border-width';\n } else if(isMarginProperty(propertyToMatch)){\n return 'margin';\n } else if(isPaddingProperty(propertyToMatch)){\n return 'padding';\n } else if(isBorderRadius(propertyToMatch)){\n return 'border-radius';\n } else if(isDimensionProperty(propertyToMatch)){\n // Stylinghooks includes only width as property to match, for all other dimensions we need to match width\n return 'width';\n } else if(isInsetProperty(propertyToMatch)){\n // Stylinghooks includes only top/left/right/bottom as property to match, for all other insets we need to match top\n return 'top';\n }\n return propertyToMatch;\n}\n\nexport function resolveColorPropertyToMatch(cssProperty:string){\n const propertyToMatch = cssProperty.toLowerCase();\n if(propertyToMatch === 'outline' || propertyToMatch === 'outline-color'){\n return 'border-color';\n } else if(propertyToMatch === 'background' || propertyToMatch === 'background-color'){\n return 'background-color';\n } else if(isBorderColorProperty(propertyToMatch)){\n return 'border-color';\n }\n return propertyToMatch;\n}\n\nexport function isOutlineWidthProperty(propertyToMatch:string){\n return propertyToMatch === 'outline' || propertyToMatch === 'outline-width';\n}", "import { parse, walk } from '@eslint/css-tree';\nimport type { HandlerContext } from '../types';\nimport type { ParsedUnitValue } from './value-utils';\nimport { ALLOWED_UNITS } from './value-utils';\nimport { extractColorValue } from './color-lib-utils';\nimport { isCssFunction } from './css-functions';\n\n/**\n * Common replacement data structure used by both color and density handlers\n */\nexport interface ReplacementInfo {\n start: number;\n end: number;\n replacement: string; // Full CSS var: var(--hook, fallback)\n displayValue: string; // Just the hook: --hook\n hasHook: boolean;\n isNumeric?: boolean; // Whether this is a numeric (dimension) value\n}\n\n/**\n * Position information from CSS tree parsing\n */\nexport interface PositionInfo {\n start?: { offset: number; line: number; column: number };\n end?: { offset: number; line: number; column: number };\n}\n\n/**\n * Generic callback for processing values with position information\n */\nexport type ValueCallback<T> = (value: T, positionInfo?: PositionInfo) => void;\n\n/**\n * Known valid font-weight values\n */\nconst FONT_WEIGHTS = [\n 'normal',\n 'bold', \n 'bolder',\n 'lighter',\n '100',\n '200', \n '300',\n '400',\n '500',\n '600',\n '700',\n '800',\n '900'\n];\n\n/**\n * Check if a value is a known font-weight\n */\nexport function isKnownFontWeight(value: string | number): boolean {\n const stringValue = value.toString();\n return FONT_WEIGHTS.includes(stringValue.toLowerCase());\n}\n\n/**\n * Generic shorthand auto-fix handler\n * Handles the common logic for reconstructing shorthand values with replacements\n */\nexport function handleShorthandAutoFix(\n declarationNode: any,\n context: HandlerContext,\n valueText: string,\n replacements: ReplacementInfo[]\n) {\n if(!replacements || replacements.length === 0){\n return;\n }\n // Sort replacements by position for proper reconstruction\n const sortedReplacements = replacements.sort((a,b)=> b.start-a.start);\n\n // Get rule options\n const reportNumericValue = context.options?.reportNumericValue || 'always';\n\n const fixCallback = (start:number, originalValue:string, replacement:string) => {\n // Reconstruct the entire value with all replacements\n let newValue = valueText;\n\n newValue = newValue.substring(0, start) + replacement + newValue.substring(start+originalValue.length);\n\n if(newValue !== valueText){\n return (fixer:any)=>{\n return fixer.replaceText(declarationNode.value, newValue);\n }\n }\n }\n\n // Report each individual value\n sortedReplacements.forEach(({ start, end, replacement, displayValue, hasHook, isNumeric }) => {\n const originalValue = valueText.substring(start, end);\n \n // Check if we should skip reporting based on reportNumericValue option\n if (isNumeric) {\n if (reportNumericValue === 'never') {\n return; // Skip reporting numeric values\n }\n if (reportNumericValue === 'hasReplacement' && !hasHook) {\n return; // Skip reporting numeric values without replacements\n }\n }\n \n \n const valueColumnStart = declarationNode.value.loc.start.column + start;\n const valueColumnEnd = valueColumnStart + originalValue.length;\n const canAutoFix = originalValue !== replacement;\n \n // Create precise error location for this value\n const { loc: { start: locStart, end: locEnd } } = declarationNode.value;\n const reportNode = {\n ...declarationNode.value,\n loc: {\n ...declarationNode.value.loc,\n start: {\n ...locStart,\n column: valueColumnStart\n },\n end: {\n ...locEnd,\n column: valueColumnEnd\n }\n }\n };\n\n if (hasHook) {\n // Create auto-fix for the entire shorthand value\n const fix = canAutoFix ? fixCallback(start, originalValue, replacement) : undefined;\n\n context.context.report({\n node: reportNode,\n messageId: 'hardcodedValue',\n data: {\n oldValue: originalValue,\n newValue: displayValue\n },\n fix\n });\n } else {\n // No hook available\n context.context.report({\n node: reportNode,\n messageId: 'noReplacement',\n data: {\n oldValue: originalValue\n }\n });\n }\n });\n}\n\n/**\n * Generic CSS tree traversal with position tracking\n * Always provides position information since both handlers need it\n */\nexport function forEachValue<T>(\n valueText: string,\n extractValue: (node: any) => T | null,\n shouldSkipNode: (node: any) => boolean,\n callback: (value: T, positionInfo: PositionInfo) => void\n): void {\n if (!valueText || typeof valueText !== 'string') {\n return;\n }\n\n try {\n const ast = parse(valueText, { context: 'value' as const, positions: true });\n \n walk(ast, {\n enter(node: any) {\n // Skip nodes efficiently using this.skip\n if (shouldSkipNode(node)) {\n return this.skip;\n }\n \n const value = extractValue(node);\n if (value !== null) {\n const positionInfo: PositionInfo = {\n start: node.loc?.start,\n end: node.loc?.end\n };\n callback(value, positionInfo);\n }\n }\n });\n } catch (error) {\n // Silently handle parse errors\n return;\n }\n}\n\n/**\n * Check if color node should be skipped during traversal\n */\nfunction shouldSkipColorNode(node: any): boolean {\n return node.type === 'Function' && isCssFunction(node.name);\n}\n\n/**\n * Check if dimension node should be skipped during traversal\n * Skip all function nodes by default\n */\nfunction shouldSkipDimensionNode(node: any): boolean {\n return node.type === 'Function';\n}\n\n/**\n * Extract dimension value from CSS AST node\n * Returns structured data with number and unit to eliminate regex parsing\n */\nfunction extractDimensionValue(valueNode: any, cssProperty?: string): ParsedUnitValue | null {\n if (!valueNode) return null;\n \n switch (valueNode.type) {\n case 'Dimension':\n // Dimensions: 16px, 1rem -> extract value and unit directly from AST\n const numValue = Number(valueNode.value);\n if (numValue === 0) return null; // Skip zero values\n \n const unit = valueNode.unit.toLowerCase();\n if (!ALLOWED_UNITS.includes(unit)) return null; // Support only allowed units\n \n return {\n number: numValue,\n unit: unit as 'px' | 'rem' | '%' | 'em' | 'ch'\n };\n \n case 'Number':\n // Numbers: 400, 1.5 -> treat as unitless (font-weight, line-height, etc.)\n const numberValue = Number(valueNode.value);\n if (numberValue === 0) return null; // Skip zero values\n \n return {\n number: numberValue,\n unit: null\n };\n \n case 'Percentage':\n // Percentage values: 100%, 50% -> extract value and add % unit\n const percentValue = Number(valueNode.value);\n if (percentValue === 0) return null; // Skip zero values\n \n return {\n number: percentValue,\n unit: '%'\n };\n \n case 'Value':\n // Value wrapper - extract from first child\n return valueNode.children?.[0] ? extractDimensionValue(valueNode.children[0], cssProperty) : null;\n }\n \n return null;\n}\n\n/**\n * Specialized color value traversal\n * Handles color-specific extraction and skipping logic\n */\nexport function forEachColorValue(\n valueText: string,\n callback: (colorValue: string, positionInfo: PositionInfo) => void\n): void {\n forEachValue(valueText, extractColorValue, shouldSkipColorNode, callback);\n}\n\n/**\n * Specialized density value traversal\n * Handles dimension-specific extraction and skipping logic\n */\nexport function forEachDensityValue(\n valueText: string,\n cssProperty: string,\n callback: (parsedDimension: ParsedUnitValue, positionInfo: PositionInfo) => void\n): void {\n forEachValue(\n valueText, \n (node) => extractDimensionValue(node, cssProperty), \n shouldSkipDimensionNode, \n callback\n );\n}\n\n/**\n * Extract font-related values from CSS AST node\n * Handles font-size and font-weight values\n */\nfunction extractFontValue(node: any): ParsedUnitValue | null {\n if (!node) return null;\n \n switch (node.type) {\n case 'Dimension':\n // Font-size: 16px, 1rem, etc.\n const numValue = Number(node.value);\n if (numValue <= 0) return null; // Skip zero/negative values\n \n const unit = node.unit.toLowerCase();\n if (!ALLOWED_UNITS.includes(unit)) return null;\n \n return {\n number: numValue,\n unit: unit as 'px' | 'rem' | '%' | 'em' | 'ch'\n };\n \n case 'Number':\n // Font-weight: 400, 700, etc.\n const numberValue = Number(node.value);\n if (numberValue <= 0) {\n return null; // Skip zero/negative values\n }\n \n // Only accept known font-weight values for unitless numbers\n if (!isKnownFontWeight(numberValue)) {\n return null; // Skip values that aren't valid font-weights\n }\n \n return {\n number: numberValue,\n unit: null\n };\n \n case 'Identifier':\n // Font-weight keywords: normal, bold, etc.\n const namedValue = node.name.toLowerCase();\n \n // Only accept known font-weight keywords\n if (!isKnownFontWeight(namedValue)) {\n return null;\n }\n \n // Convert known keywords to numeric values\n if (namedValue === 'normal') {\n return { number: 400, unit: null };\n }\n \n // For other keywords (bolder, lighter), we can't determine exact numeric value\n // but we know they're valid font-weight values\n return { number: namedValue, unit: null };\n \n case 'Percentage':\n // Percentage values for font-size\n const percentValue = Number(node.value);\n if (percentValue === 0) return null; // Skip zero values\n \n return {\n number: percentValue,\n unit: '%'\n };\n \n case 'Value':\n // Value wrapper - extract from first child\n return node.children?.[0] ? extractFontValue(node.children[0]) : null;\n }\n \n return null;\n}\n\n/**\n * Check if font node should be skipped during traversal\n * Skip all function nodes by default\n */\nfunction shouldSkipFontNode(node: any): boolean {\n return node.type === 'Function';\n}\n\n/**\n * Specialized font value traversal\n * Handles font-specific extraction and skipping logic\n */\nexport function forEachFontValue(\n valueText: string,\n callback: (fontValue: ParsedUnitValue, positionInfo: PositionInfo) => void\n): void {\n forEachValue(valueText, extractFontValue, shouldSkipFontNode, callback);\n}\n", "import { \n forEachValue, \n type PositionInfo \n} from './hardcoded-shared-utils';\n\n/**\n * Check if a CSS property should be targeted for linting based on prefixes or explicit targets\n * @param property - The CSS property name to check\n * @param propertyTargets - Array of specific properties to target (empty means target all)\n * @returns true if the property should be targeted\n */\nexport function isTargetProperty(property: string, propertyTargets: string[] = []): boolean {\n if (typeof property !== 'string') return false;\n return property.startsWith('--sds-')\n || property.startsWith('--slds-')\n || property.startsWith('--lwc-')\n || propertyTargets.length === 0\n || propertyTargets.includes(property);\n}\n\n/**\n * CSS Variable information for SLDS variable detection\n */\nexport interface CssVariableInfo {\n name: string; // Variable name: --slds-g-color-surface-1\n hasFallback: boolean; // Whether var() already has a fallback\n}\n\n/**\n * Generic CSS variable extractor that can be customized for different use cases\n * @param node - AST node to extract from\n * @param filter - Function to validate and extract variable information\n * @returns Extracted variable info or null\n */\nfunction extractCssVariable<T>(\n node: any,\n filter: (variableName: string, childrenArray: any[]) => T | null\n): T | null {\n if (!node || node.type !== 'Function' || node.name !== 'var') {\n return null;\n }\n\n if (!node.children) {\n return null;\n }\n\n // Convert children to array and get the first child (variable name)\n const childrenArray = Array.from(node.children);\n if (childrenArray.length === 0) {\n return null;\n }\n \n const firstChild = childrenArray[0] as any;\n if (!firstChild || firstChild.type !== 'Identifier') {\n return null;\n }\n\n const variableName = firstChild.name;\n if (!variableName) {\n return null;\n }\n\n return filter(variableName, childrenArray);\n}\n\n/**\n * Specialized CSS variable traversal for SLDS variables\n * Finds var(--slds-*) functions and reports their fallback status\n */\nexport function forEachSldsVariable(\n valueText: string,\n callback: (variableInfo: CssVariableInfo, positionInfo: PositionInfo) => void\n): void {\n const extractor = (node: any) => extractCssVariable(node, (variableName, childrenArray) => {\n if (!variableName.startsWith('--slds-')) {\n return null;\n }\n\n // Check if there's a fallback (comma separator)\n const hasFallback = childrenArray.some((child: any) => \n child.type === 'Operator' && child.value === ','\n );\n\n return { name: variableName, hasFallback };\n });\n\n forEachValue(valueText, extractor, () => false, callback);\n}\n\n/**\n * Specialized CSS variable traversal for SLDS/SDS namespace detection\n * Finds var(--slds-*) or var(--sds-*) functions in CSS values\n * Note: hasFallback is set to false as it's unused for namespace validation\n */\nexport function forEachNamespacedVariable(\n valueText: string,\n callback: (variableInfo: CssVariableInfo, positionInfo: PositionInfo) => void\n): void {\n const extractor = (node: any) => extractCssVariable(node, (variableName) => {\n // Check for SLDS or SDS namespace\n if (variableName.startsWith('--slds-') || variableName.startsWith('--sds-')) {\n return { name: variableName, hasFallback: false }; // hasFallback unused, but required by interface\n }\n return null;\n });\n\n forEachValue(valueText, extractor, () => false, callback);\n}\n\n/**\n * Specialized CSS variable traversal for LWC variables\n * Finds var(--lwc-*) functions in CSS values and reports their fallback status\n */\nexport function forEachLwcVariable(\n valueText: string,\n callback: (variableInfo: CssVariableInfo, positionInfo: PositionInfo) => void\n): void {\n const extractor = (node: any) => extractCssVariable(node, (variableName, childrenArray) => {\n if (!variableName.startsWith('--lwc-')) {\n return null;\n }\n\n // Check if there's a fallback (comma separator)\n const hasFallback = childrenArray.some((child: any) => \n child.type === 'Operator' && child.value === ','\n );\n\n return { name: variableName, hasFallback };\n });\n\n forEachValue(valueText, extractor, () => false, callback);\n}\n\n/**\n * Format multiple hook suggestions for better readability\n * @param hooks - Array of hook names to format\n * @returns Formatted string with hooks\n */\nexport function formatSuggestionHooks(hooks: string[]): string {\n if (hooks.length === 1) {\n return `${hooks[0]}`;\n }\n\n // Loop through hooks and append each as a numbered list item with line breaks\n return '\\n' + hooks.map((hook, index) => `${index + 1}. ${hook}`).join('\\n');\n}", "import type { CustomHookMapping } from '../types';\n\n/**\n * Check if a CSS property matches a pattern (supports wildcards)\n * @param cssProperty - The CSS property to check (e.g., \"background-color\")\n * @param pattern - The pattern to match against (e.g., \"background*\" or \"color\")\n * @returns true if the property matches the pattern\n */\nfunction matchesPropertyPattern(cssProperty: string, pattern: string): boolean {\n const normalizedProperty = cssProperty.toLowerCase();\n const normalizedPattern = pattern.toLowerCase();\n\n // Exact match\n if (normalizedProperty === normalizedPattern) {\n return true;\n }\n\n // Wildcard match (e.g., \"background*\" matches \"background-color\", \"background-image\")\n if (normalizedPattern.endsWith('*')) {\n const prefix = normalizedPattern.slice(0, -1);\n return normalizedProperty.startsWith(prefix);\n }\n\n return false;\n}\n\n/**\n * Get custom hook mapping for a given CSS property and value\n * @param cssProperty - The CSS property (e.g., \"background-color\", \"color\")\n * @param value - The hardcoded value (e.g., \"#fff\", \"16px\")\n * @param customMapping - The custom mapping configuration from rule options\n * @returns The hook name if a mapping is found, null otherwise\n */\nexport function getCustomMapping(\n cssProperty: string,\n value: string,\n customMapping?: CustomHookMapping\n): string | null {\n if (!customMapping) {\n return null;\n }\n\n const normalizedValue = value.toLowerCase().trim();\n\n // Iterate through all hook mappings\n for (const [hookName, config] of Object.entries(customMapping)) {\n // Check if any property pattern matches\n const propertyMatches = config.properties.some((pattern) =>\n matchesPropertyPattern(cssProperty, pattern)\n );\n\n if (!propertyMatches) {\n continue;\n }\n\n // Check if the value matches any configured value\n const valueMatches = config.values.some(\n (configValue) => configValue.toLowerCase().trim() === normalizedValue\n );\n\n if (valueMatches) {\n return hookName;\n }\n }\n\n return null;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["chroma", "import_css_tree"]
7
7
  }