@salesforce-ux/eslint-plugin-slds 1.0.0 → 1.0.2

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 +18 -1
  2. package/build/index.js +686 -693
  3. package/build/index.js.map +4 -4
  4. package/build/rules/enforce-bem-usage.js +1 -1
  5. package/build/rules/enforce-bem-usage.js.map +1 -1
  6. package/build/rules/modal-close-button-issue.js +1 -1
  7. package/build/rules/modal-close-button-issue.js.map +1 -1
  8. package/build/rules/no-deprecated-classes-slds2.js +1 -1
  9. package/build/rules/no-deprecated-classes-slds2.js.map +1 -1
  10. package/build/rules/v9/enforce-bem-usage.js +1 -1
  11. package/build/rules/v9/enforce-bem-usage.js.map +1 -1
  12. package/build/rules/v9/enforce-component-hook-naming-convention.js +1 -1
  13. package/build/rules/v9/enforce-component-hook-naming-convention.js.map +1 -1
  14. package/build/rules/v9/enforce-sds-to-slds-hooks.js +1 -1
  15. package/build/rules/v9/enforce-sds-to-slds-hooks.js.map +1 -1
  16. package/build/rules/v9/lwc-token-to-slds-hook.js +41 -2
  17. package/build/rules/v9/lwc-token-to-slds-hook.js.map +4 -4
  18. package/build/rules/v9/no-deprecated-slds-classes.js +1 -1
  19. package/build/rules/v9/no-deprecated-slds-classes.js.map +1 -1
  20. package/build/rules/v9/no-deprecated-tokens-slds1.js +1 -1
  21. package/build/rules/v9/no-deprecated-tokens-slds1.js.map +1 -1
  22. package/build/rules/v9/no-hardcoded-values/handlers/boxShadowHandler.js +49 -54
  23. package/build/rules/v9/no-hardcoded-values/handlers/boxShadowHandler.js.map +3 -3
  24. package/build/rules/v9/no-hardcoded-values/handlers/colorHandler.js +9 -2
  25. package/build/rules/v9/no-hardcoded-values/handlers/colorHandler.js.map +3 -3
  26. package/build/rules/v9/no-hardcoded-values/handlers/densityHandler.js +14 -3
  27. package/build/rules/v9/no-hardcoded-values/handlers/densityHandler.js.map +3 -3
  28. package/build/rules/v9/no-hardcoded-values/handlers/fontHandler.js +14 -3
  29. package/build/rules/v9/no-hardcoded-values/handlers/fontHandler.js.map +3 -3
  30. package/build/rules/v9/no-hardcoded-values/handlers/index.js +86 -90
  31. package/build/rules/v9/no-hardcoded-values/handlers/index.js.map +3 -3
  32. package/build/rules/v9/no-hardcoded-values/no-hardcoded-values-slds1.js +88 -92
  33. package/build/rules/v9/no-hardcoded-values/no-hardcoded-values-slds1.js.map +3 -3
  34. package/build/rules/v9/no-hardcoded-values/no-hardcoded-values-slds2.js +88 -92
  35. package/build/rules/v9/no-hardcoded-values/no-hardcoded-values-slds2.js.map +3 -3
  36. package/build/rules/v9/no-hardcoded-values/noHardcodedValueRule.js +87 -91
  37. package/build/rules/v9/no-hardcoded-values/noHardcodedValueRule.js.map +3 -3
  38. package/build/rules/v9/no-slds-class-overrides.js +1 -1
  39. package/build/rules/v9/no-slds-class-overrides.js.map +1 -1
  40. package/build/rules/v9/no-slds-namespace-for-custom-hooks.js +1 -1
  41. package/build/rules/v9/no-slds-namespace-for-custom-hooks.js.map +1 -1
  42. package/build/rules/v9/no-slds-private-var.js +1 -1
  43. package/build/rules/v9/no-slds-private-var.js.map +1 -1
  44. package/build/rules/v9/no-slds-var-without-fallback.js +1 -1
  45. package/build/rules/v9/no-slds-var-without-fallback.js.map +2 -2
  46. package/build/rules/v9/no-sldshook-fallback-for-lwctoken.js +1 -1
  47. package/build/rules/v9/no-sldshook-fallback-for-lwctoken.js.map +1 -1
  48. package/build/rules/v9/no-unsupported-hooks-slds2.js +1 -1
  49. package/build/rules/v9/no-unsupported-hooks-slds2.js.map +1 -1
  50. package/build/rules/v9/reduce-annotations.js +1 -1
  51. package/build/rules/v9/reduce-annotations.js.map +1 -1
  52. package/build/src/utils/css-utils.d.ts +6 -0
  53. package/build/src/utils/value-utils.d.ts +3 -2
  54. package/build/utils/boxShadowValueParser.js +4 -1
  55. package/build/utils/boxShadowValueParser.js.map +2 -2
  56. package/build/utils/css-utils.js +8 -0
  57. package/build/utils/css-utils.js.map +2 -2
  58. package/build/utils/hardcoded-shared-utils.js +5 -2
  59. package/build/utils/hardcoded-shared-utils.js.map +3 -3
  60. package/build/utils/property-matcher.js +1 -1
  61. package/build/utils/property-matcher.js.map +2 -2
  62. package/build/utils/styling-hook-utils.js +4 -1
  63. package/build/utils/styling-hook-utils.js.map +2 -2
  64. package/build/utils/value-utils.js +6 -1
  65. package/build/utils/value-utils.js.map +2 -2
  66. package/package.json +2 -2
  67. package/src/config/rule-messages.yml +1 -1
package/build/index.js CHANGED
@@ -183,7 +183,7 @@ var require_rule_messages = __commonJS({
183
183
  },
184
184
  "no-hardcoded-values-slds2": {
185
185
  "description": "Replace static values with SLDS 2 styling hooks. For more information, look up design tokens on lightningdesignsystem.com.",
186
- "url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#no-hardcoded-value",
186
+ "url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#no-hardcoded-values-slds2",
187
187
  "type": "suggestion",
188
188
  "messages": {
189
189
  "hardcodedValue": "Consider replacing the {{oldValue}} static value with an SLDS 2 styling hook that has a similar value: {{newValue}}.",
@@ -708,169 +708,456 @@ var no_deprecated_tokens_slds1_default = {
708
708
  // src/rules/v9/lwc-token-to-slds-hook.ts
709
709
  var import_sds_metadata5 = __toESM(require("@salesforce-ux/sds-metadata"));
710
710
  var import_rule_messages5 = __toESM(require_rule_messages());
711
- var ruleConfig3 = import_rule_messages5.default["lwc-token-to-slds-hook"];
712
- var { type: type4, description: description4, url: url4, messages: messages4 } = ruleConfig3;
713
- var lwcToSlds = import_sds_metadata5.default.lwcToSlds;
714
- function shouldIgnoreDetection(lwcToken) {
715
- return !lwcToken.startsWith("--lwc-") || !(lwcToken in lwcToSlds) || lwcToSlds[lwcToken].continueToUse;
711
+
712
+ // src/utils/hardcoded-shared-utils.ts
713
+ var import_css_tree2 = require("@eslint/css-tree");
714
+
715
+ // src/utils/value-utils.ts
716
+ var ALLOWED_UNITS = ["px", "em", "rem", "%", "ch"];
717
+ function parseUnitValue(value) {
718
+ if (!value) return null;
719
+ const unitsPattern = ALLOWED_UNITS.join("|");
720
+ const regex = new RegExp(`^(-?\\d*\\.?\\d+)(${unitsPattern})?$`);
721
+ const match = value.match(regex);
722
+ if (!match) return null;
723
+ const number = parseFloat(match[1]);
724
+ const unit = match[2] ? match[2] : null;
725
+ if (isNaN(number)) return null;
726
+ return { number, unit };
716
727
  }
717
- function categorizeReplacement(recommendation) {
718
- if (!recommendation || recommendation === "--") {
719
- return "empty" /* EMPTY */;
720
- }
721
- if (Array.isArray(recommendation)) {
722
- return "array" /* ARRAY */;
723
- }
724
- if (typeof recommendation === "string" && recommendation.startsWith("--slds-")) {
725
- return "slds_token" /* SLDS_TOKEN */;
728
+ function toAlternateUnitValue(numberVal, unitType) {
729
+ if (unitType === "px") {
730
+ let floatValue = parseFloat(`${numberVal / 16}`);
731
+ if (!isNaN(floatValue)) {
732
+ return {
733
+ unit: "rem",
734
+ number: parseFloat(floatValue.toFixed(4))
735
+ };
736
+ }
737
+ } else if (unitType === "rem") {
738
+ const intValue = parseInt(`${numberVal * 16}`);
739
+ if (!isNaN(intValue)) {
740
+ return {
741
+ unit: "px",
742
+ number: intValue
743
+ };
744
+ }
726
745
  }
727
- return "raw_value" /* RAW_VALUE */;
746
+ return null;
728
747
  }
729
- function getRecommendation(lwcToken) {
730
- const oldValue = lwcToken;
731
- const recommendation = lwcToSlds[oldValue]?.replacement || "";
732
- const replacementCategory = categorizeReplacement(recommendation);
733
- const hasRecommendation = oldValue in lwcToSlds && replacementCategory !== "empty" /* EMPTY */;
734
- return { hasRecommendation, recommendation, replacementCategory };
748
+
749
+ // src/utils/color-lib-utils.ts
750
+ var import_chroma_js = __toESM(require("chroma-js"));
751
+ var import_css_tree = require("@eslint/css-tree");
752
+
753
+ // src/utils/css-functions.ts
754
+ var CSS_FUNCTIONS = [
755
+ "attr",
756
+ "calc",
757
+ "color-mix",
758
+ "conic-gradient",
759
+ "counter",
760
+ "cubic-bezier",
761
+ "linear-gradient",
762
+ "max",
763
+ "min",
764
+ "radial-gradient",
765
+ "repeating-conic-gradient",
766
+ "repeating-linear-gradient",
767
+ "repeating-radial-gradient",
768
+ "var"
769
+ ];
770
+ var CSS_MATH_FUNCTIONS = ["calc", "min", "max"];
771
+ var RGB_COLOR_FUNCTIONS = ["rgb", "rgba", "hsl", "hsla"];
772
+ var cssFunctionsRegex = new RegExp(`(?:${CSS_FUNCTIONS.join("|")})`);
773
+ var cssFunctionsExactRegex = new RegExp(`^(?:${CSS_FUNCTIONS.join("|")})$`);
774
+ var cssMathFunctionsRegex = new RegExp(`^(?:${CSS_MATH_FUNCTIONS.join("|")})$`);
775
+ function isCssFunction(value) {
776
+ return cssFunctionsExactRegex.test(value);
735
777
  }
736
- function getReportMessage(cssVar, replacementCategory, recommendation) {
737
- if (!recommendation) {
738
- return {
739
- messageId: "errorWithNoRecommendation",
740
- data: { oldValue: cssVar }
741
- };
742
- } else if (replacementCategory === "array" /* ARRAY */) {
743
- return {
744
- messageId: "errorWithStyleHooks",
745
- data: { oldValue: cssVar, newValue: recommendation.join(" or ") }
746
- };
747
- } else if (replacementCategory === "slds_token" /* SLDS_TOKEN */) {
748
- return {
749
- messageId: "errorWithStyleHooks",
750
- data: { oldValue: cssVar, newValue: recommendation }
751
- };
752
- } else {
753
- return {
754
- messageId: "errorWithReplacement",
755
- data: { oldValue: cssVar, newValue: recommendation }
756
- };
757
- }
778
+ function isCssColorFunction(value) {
779
+ return RGB_COLOR_FUNCTIONS.includes(value);
758
780
  }
759
- var lwc_token_to_slds_hook_default = {
760
- meta: {
761
- type: type4,
762
- docs: {
763
- description: description4,
764
- recommended: true,
765
- url: url4
766
- },
767
- fixable: "code",
768
- messages: messages4
769
- },
770
- create(context) {
771
- function reportAndFix(node, oldValue, suggestedMatch, messageId, data) {
772
- let fixFunction = null;
773
- if (suggestedMatch) {
774
- fixFunction = (fixer) => {
775
- if (node.type === "Declaration") {
776
- const sourceCode = context.sourceCode;
777
- const fullText = sourceCode.getText();
778
- const nodeOffset = node.loc.start.offset;
779
- const propertyStart = nodeOffset;
780
- const propertyEnd = propertyStart + oldValue.length;
781
- const textAtPosition = fullText.substring(propertyStart, propertyEnd);
782
- if (textAtPosition === oldValue) {
783
- return fixer.replaceTextRange([propertyStart, propertyEnd], suggestedMatch);
784
- }
785
- } else {
786
- const sourceCode = context.sourceCode;
787
- const fullText = sourceCode.getText();
788
- const varFunctionCall = `var(${oldValue})`;
789
- const nodeOffset = node.loc.start.offset;
790
- const searchStart = Math.max(0, nodeOffset - 4);
791
- const searchEnd = nodeOffset + oldValue.length + 1;
792
- const searchArea = fullText.substring(searchStart, searchEnd);
793
- const functionCallIndex = searchArea.indexOf(varFunctionCall);
794
- if (functionCallIndex !== -1) {
795
- const actualStart = searchStart + functionCallIndex;
796
- const actualEnd = actualStart + varFunctionCall.length;
797
- return fixer.replaceTextRange([actualStart, actualEnd], suggestedMatch);
798
- }
781
+
782
+ // src/utils/color-lib-utils.ts
783
+ var LAB_THRESHOLD = 25;
784
+ var isHexCode = (color) => {
785
+ const hexPattern = /^#(?:[0-9a-fA-F]{3}){1,2}$/;
786
+ return hexPattern.test(color);
787
+ };
788
+ var convertToHex = (color) => {
789
+ try {
790
+ return (0, import_chroma_js.default)(color).hex();
791
+ } catch (e) {
792
+ return null;
793
+ }
794
+ };
795
+ var findClosestColorHook = (color, supportedColors, cssProperty) => {
796
+ const returnStylingHooks = [];
797
+ const closestHooksWithSameProperty = [];
798
+ const closestHooksWithoutSameProperty = [];
799
+ const closestHooksWithAllProperty = [];
800
+ const labColor = (0, import_chroma_js.default)(color).lab();
801
+ Object.entries(supportedColors).forEach(([sldsValue, data]) => {
802
+ if (sldsValue && isHexCode(sldsValue)) {
803
+ const hooks = data;
804
+ hooks.forEach((hook) => {
805
+ const labSupportedColor = (0, import_chroma_js.default)(sldsValue).lab();
806
+ 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");
807
+ if (hook.properties.includes(cssProperty)) {
808
+ if (distance <= LAB_THRESHOLD) {
809
+ closestHooksWithSameProperty.push({ name: hook.name, distance });
799
810
  }
800
- return null;
801
- };
802
- }
803
- context.report({
804
- node,
805
- messageId,
806
- data,
807
- fix: fixFunction
808
- });
809
- }
810
- return {
811
- // CSS custom property declarations: --lwc-* properties
812
- "Declaration[property=/^--lwc-/]"(node) {
813
- const property = node.property;
814
- if (shouldIgnoreDetection(property)) {
815
- return;
816
- }
817
- const { hasRecommendation, recommendation, replacementCategory } = getRecommendation(property);
818
- const { messageId, data } = getReportMessage(property, replacementCategory, recommendation);
819
- const suggestedMatch = hasRecommendation && replacementCategory === "slds_token" /* SLDS_TOKEN */ ? recommendation : null;
820
- reportAndFix(node, property, suggestedMatch, messageId, data);
821
- },
822
- // LWC tokens inside var() functions: var(--lwc-*)
823
- "Function[name='var'] Identifier[name=/^--lwc-/]"(node) {
824
- const tokenName = node.name;
825
- if (shouldIgnoreDetection(tokenName)) {
826
- return;
827
- }
828
- const { hasRecommendation, recommendation, replacementCategory } = getRecommendation(tokenName);
829
- const { messageId, data } = getReportMessage(tokenName, replacementCategory, recommendation);
830
- let suggestedMatch = null;
831
- if (hasRecommendation) {
832
- if (replacementCategory === "slds_token" /* SLDS_TOKEN */) {
833
- const originalVarCall = `var(${tokenName})`;
834
- suggestedMatch = `var(${recommendation}, ${originalVarCall})`;
835
- } else if (replacementCategory === "raw_value" /* RAW_VALUE */) {
836
- suggestedMatch = recommendation;
811
+ } else if (hook.properties.includes("*")) {
812
+ if (distance <= LAB_THRESHOLD) {
813
+ closestHooksWithAllProperty.push({ name: hook.name, distance });
814
+ }
815
+ } else {
816
+ if (distance <= LAB_THRESHOLD) {
817
+ closestHooksWithoutSameProperty.push({ name: hook.name, distance });
837
818
  }
838
819
  }
839
- reportAndFix(node, tokenName, suggestedMatch, messageId, data);
820
+ });
821
+ }
822
+ });
823
+ const closesthookGroups = [
824
+ { hooks: closestHooksWithSameProperty, distance: 0 },
825
+ { hooks: closestHooksWithAllProperty, distance: 0 },
826
+ { hooks: closestHooksWithSameProperty, distance: Infinity },
827
+ // For hooks with distance > 0
828
+ { hooks: closestHooksWithAllProperty, distance: Infinity },
829
+ { hooks: closestHooksWithoutSameProperty, distance: Infinity }
830
+ ];
831
+ for (const group of closesthookGroups) {
832
+ const filteredHooks = group.hooks.filter(
833
+ (h) => group.distance === 0 ? h.distance === 0 : h.distance > 0
834
+ );
835
+ if (returnStylingHooks.length < 1 && filteredHooks.length > 0) {
836
+ filteredHooks.sort((a, b) => a.distance - b.distance);
837
+ returnStylingHooks.push(...filteredHooks.slice(0, 5).map((h) => h.name));
838
+ }
839
+ }
840
+ return Array.from(new Set(returnStylingHooks));
841
+ };
842
+ var isValidColor = (val) => import_chroma_js.default.valid(val);
843
+ var extractColorValue = (node) => {
844
+ let colorValue = null;
845
+ switch (node.type) {
846
+ case "Hash":
847
+ colorValue = `#${node.value}`;
848
+ break;
849
+ case "Identifier":
850
+ colorValue = node.name;
851
+ break;
852
+ case "Function":
853
+ if (isCssColorFunction(node.name)) {
854
+ colorValue = (0, import_css_tree.generate)(node);
840
855
  }
841
- };
856
+ break;
842
857
  }
858
+ return colorValue && isValidColor(colorValue) ? colorValue : null;
843
859
  };
844
860
 
845
- // src/rules/v9/enforce-sds-to-slds-hooks.ts
846
- var import_sds_metadata6 = __toESM(require("@salesforce-ux/sds-metadata"));
847
- var import_rule_messages6 = __toESM(require_rule_messages());
848
- var ruleConfig4 = import_rule_messages6.default["enforce-sds-to-slds-hooks"];
849
- var { type: type5, description: description5, url: url5, messages: messages5 } = ruleConfig4;
850
- var sldsPlusStylingHooks = import_sds_metadata6.default.sldsPlusStylingHooks;
851
- var allSldsHooks = [...sldsPlusStylingHooks.global, ...sldsPlusStylingHooks.component];
852
- var toSldsToken = (sdsToken) => sdsToken.replace("--sds-", "--slds-");
853
- function shouldIgnoreDetection2(sdsToken) {
854
- return !sdsToken.startsWith("--sds-") || !allSldsHooks.includes(toSldsToken(sdsToken));
861
+ // src/utils/hardcoded-shared-utils.ts
862
+ var FONT_WEIGHTS = [
863
+ "normal",
864
+ "bold",
865
+ "bolder",
866
+ "lighter",
867
+ "100",
868
+ "200",
869
+ "300",
870
+ "400",
871
+ "500",
872
+ "600",
873
+ "700",
874
+ "800",
875
+ "900"
876
+ ];
877
+ function isKnownFontWeight(value) {
878
+ const stringValue = value.toString();
879
+ return FONT_WEIGHTS.includes(stringValue.toLowerCase());
880
+ }
881
+ function handleShorthandAutoFix(declarationNode, context, valueText, replacements) {
882
+ const sortedReplacements = replacements.sort((a, b) => a.start - b.start);
883
+ const hasAnyHooks = sortedReplacements.some((r) => r.hasHook);
884
+ const canAutoFix = hasAnyHooks;
885
+ sortedReplacements.forEach(({ start, end, replacement, displayValue, hasHook }) => {
886
+ const originalValue = valueText.substring(start, end);
887
+ const valueStartColumn = declarationNode.value.loc.start.column;
888
+ const valueColumn = valueStartColumn + start;
889
+ const { loc: { start: locStart, end: locEnd } } = declarationNode.value;
890
+ const reportNode = {
891
+ ...declarationNode.value,
892
+ loc: {
893
+ ...declarationNode.value.loc,
894
+ start: {
895
+ ...locStart,
896
+ column: valueColumn
897
+ },
898
+ end: {
899
+ ...locEnd,
900
+ column: valueColumn + originalValue.length
901
+ }
902
+ }
903
+ };
904
+ if (hasHook) {
905
+ const fix = canAutoFix ? (fixer) => {
906
+ let newValue = valueText;
907
+ for (let i = sortedReplacements.length - 1; i >= 0; i--) {
908
+ const { start: rStart, end: rEnd, replacement: rReplacement } = sortedReplacements[i];
909
+ newValue = newValue.substring(0, rStart) + rReplacement + newValue.substring(rEnd);
910
+ }
911
+ return fixer.replaceText(declarationNode.value, newValue);
912
+ } : void 0;
913
+ context.context.report({
914
+ node: reportNode,
915
+ messageId: "hardcodedValue",
916
+ data: {
917
+ oldValue: originalValue,
918
+ newValue: displayValue
919
+ },
920
+ fix
921
+ });
922
+ } else {
923
+ context.context.report({
924
+ node: reportNode,
925
+ messageId: "noReplacement",
926
+ data: {
927
+ oldValue: originalValue
928
+ }
929
+ });
930
+ }
931
+ });
932
+ }
933
+ function forEachValue(valueText, extractValue, shouldSkipNode, callback) {
934
+ if (!valueText || typeof valueText !== "string") {
935
+ return;
936
+ }
937
+ try {
938
+ const ast = (0, import_css_tree2.parse)(valueText, { context: "value", positions: true });
939
+ (0, import_css_tree2.walk)(ast, {
940
+ enter(node) {
941
+ if (shouldSkipNode(node)) {
942
+ return this.skip;
943
+ }
944
+ const value = extractValue(node);
945
+ if (value !== null) {
946
+ const positionInfo = {
947
+ start: node.loc?.start,
948
+ end: node.loc?.end
949
+ };
950
+ callback(value, positionInfo);
951
+ }
952
+ }
953
+ });
954
+ } catch (error) {
955
+ return;
956
+ }
957
+ }
958
+ function shouldSkipColorNode(node) {
959
+ return node.type === "Function" && isCssFunction(node.name);
960
+ }
961
+ function shouldSkipDimensionNode(node) {
962
+ return node.type === "Function";
963
+ }
964
+ function extractDimensionValue(valueNode, cssProperty) {
965
+ if (!valueNode) return null;
966
+ switch (valueNode.type) {
967
+ case "Dimension":
968
+ const numValue = Number(valueNode.value);
969
+ if (numValue === 0) return null;
970
+ const unit = valueNode.unit.toLowerCase();
971
+ if (!ALLOWED_UNITS.includes(unit)) return null;
972
+ return {
973
+ number: numValue,
974
+ unit
975
+ };
976
+ case "Number":
977
+ const numberValue = Number(valueNode.value);
978
+ if (numberValue === 0) return null;
979
+ return {
980
+ number: numberValue,
981
+ unit: null
982
+ };
983
+ case "Percentage":
984
+ const percentValue = Number(valueNode.value);
985
+ if (percentValue === 0) return null;
986
+ return {
987
+ number: percentValue,
988
+ unit: "%"
989
+ };
990
+ case "Value":
991
+ return valueNode.children?.[0] ? extractDimensionValue(valueNode.children[0], cssProperty) : null;
992
+ }
993
+ return null;
994
+ }
995
+ function forEachColorValue(valueText, callback) {
996
+ forEachValue(valueText, extractColorValue, shouldSkipColorNode, callback);
997
+ }
998
+ function forEachDensityValue(valueText, cssProperty, callback) {
999
+ forEachValue(
1000
+ valueText,
1001
+ (node) => extractDimensionValue(node, cssProperty),
1002
+ shouldSkipDimensionNode,
1003
+ callback
1004
+ );
1005
+ }
1006
+ function extractFontValue(node) {
1007
+ if (!node) return null;
1008
+ switch (node.type) {
1009
+ case "Dimension":
1010
+ const numValue = Number(node.value);
1011
+ if (numValue <= 0) return null;
1012
+ const unit = node.unit.toLowerCase();
1013
+ if (!ALLOWED_UNITS.includes(unit)) return null;
1014
+ return {
1015
+ number: numValue,
1016
+ unit
1017
+ };
1018
+ case "Number":
1019
+ const numberValue = Number(node.value);
1020
+ if (numberValue <= 0) {
1021
+ return null;
1022
+ }
1023
+ if (!isKnownFontWeight(numberValue)) {
1024
+ return null;
1025
+ }
1026
+ return {
1027
+ number: numberValue,
1028
+ unit: null
1029
+ };
1030
+ case "Identifier":
1031
+ const namedValue = node.name.toLowerCase();
1032
+ if (!isKnownFontWeight(namedValue)) {
1033
+ return null;
1034
+ }
1035
+ if (namedValue === "normal") {
1036
+ return { number: 400, unit: null };
1037
+ }
1038
+ return { number: namedValue, unit: null };
1039
+ case "Percentage":
1040
+ const percentValue = Number(node.value);
1041
+ if (percentValue === 0) return null;
1042
+ return {
1043
+ number: percentValue,
1044
+ unit: "%"
1045
+ };
1046
+ case "Value":
1047
+ return node.children?.[0] ? extractFontValue(node.children[0]) : null;
1048
+ }
1049
+ return null;
1050
+ }
1051
+ function shouldSkipFontNode(node) {
1052
+ return node.type === "Function";
1053
+ }
1054
+ function forEachFontValue(valueText, callback) {
1055
+ forEachValue(valueText, extractFontValue, shouldSkipFontNode, callback);
1056
+ }
1057
+
1058
+ // src/utils/css-utils.ts
1059
+ function extractSldsVariable(node) {
1060
+ if (!node || node.type !== "Function" || node.name !== "var") {
1061
+ return null;
1062
+ }
1063
+ if (!node.children) {
1064
+ return null;
1065
+ }
1066
+ const childrenArray = Array.from(node.children);
1067
+ if (childrenArray.length === 0) {
1068
+ return null;
1069
+ }
1070
+ const firstChild = childrenArray[0];
1071
+ if (!firstChild || firstChild.type !== "Identifier") {
1072
+ return null;
1073
+ }
1074
+ const variableName = firstChild.name;
1075
+ if (!variableName || !variableName.startsWith("--slds-")) {
1076
+ return null;
1077
+ }
1078
+ const hasFallback = childrenArray.some(
1079
+ (child) => child.type === "Operator" && child.value === ","
1080
+ );
1081
+ return {
1082
+ name: variableName,
1083
+ hasFallback
1084
+ };
1085
+ }
1086
+ function forEachSldsVariable(valueText, callback) {
1087
+ forEachValue(valueText, extractSldsVariable, () => false, callback);
1088
+ }
1089
+ function formatSuggestionHooks(hooks) {
1090
+ if (hooks.length === 1) {
1091
+ return `${hooks[0]}`;
1092
+ }
1093
+ return "\n" + hooks.map((hook, index) => `${index + 1}. ${hook}`).join("\n");
1094
+ }
1095
+
1096
+ // src/rules/v9/lwc-token-to-slds-hook.ts
1097
+ var ruleConfig3 = import_rule_messages5.default["lwc-token-to-slds-hook"];
1098
+ var { type: type4, description: description4, url: url4, messages: messages4 } = ruleConfig3;
1099
+ var lwcToSlds = import_sds_metadata5.default.lwcToSlds;
1100
+ function shouldIgnoreDetection(lwcToken) {
1101
+ return !lwcToken.startsWith("--lwc-") || !(lwcToken in lwcToSlds) || lwcToSlds[lwcToken].continueToUse;
1102
+ }
1103
+ function categorizeReplacement(recommendation) {
1104
+ if (!recommendation || recommendation === "--") {
1105
+ return "empty" /* EMPTY */;
1106
+ }
1107
+ if (Array.isArray(recommendation)) {
1108
+ return "array" /* ARRAY */;
1109
+ }
1110
+ if (typeof recommendation === "string" && recommendation.startsWith("--slds-")) {
1111
+ return "slds_token" /* SLDS_TOKEN */;
1112
+ }
1113
+ return "raw_value" /* RAW_VALUE */;
1114
+ }
1115
+ function getRecommendation(lwcToken) {
1116
+ const oldValue = lwcToken;
1117
+ const recommendation = lwcToSlds[oldValue]?.replacement || "";
1118
+ const replacementCategory = categorizeReplacement(recommendation);
1119
+ const hasRecommendation = oldValue in lwcToSlds && replacementCategory !== "empty" /* EMPTY */;
1120
+ return { hasRecommendation, recommendation, replacementCategory };
1121
+ }
1122
+ function getReportMessage(cssVar, replacementCategory, recommendation) {
1123
+ if (!recommendation) {
1124
+ return {
1125
+ messageId: "errorWithNoRecommendation",
1126
+ data: { oldValue: cssVar }
1127
+ };
1128
+ } else if (replacementCategory === "array" /* ARRAY */) {
1129
+ return {
1130
+ messageId: "errorWithStyleHooks",
1131
+ data: { oldValue: cssVar, newValue: formatSuggestionHooks(recommendation) }
1132
+ };
1133
+ } else if (replacementCategory === "slds_token" /* SLDS_TOKEN */) {
1134
+ return {
1135
+ messageId: "errorWithStyleHooks",
1136
+ data: { oldValue: cssVar, newValue: recommendation }
1137
+ };
1138
+ } else {
1139
+ return {
1140
+ messageId: "errorWithReplacement",
1141
+ data: { oldValue: cssVar, newValue: recommendation }
1142
+ };
1143
+ }
855
1144
  }
856
- var enforce_sds_to_slds_hooks_default = {
1145
+ var lwc_token_to_slds_hook_default = {
857
1146
  meta: {
858
- type: type5,
1147
+ type: type4,
859
1148
  docs: {
860
- description: description5,
1149
+ description: description4,
861
1150
  recommended: true,
862
- url: url5
1151
+ url: url4
863
1152
  },
864
1153
  fixable: "code",
865
- messages: messages5
1154
+ messages: messages4
866
1155
  },
867
1156
  create(context) {
868
- function reportAndFix(node, oldValue, suggestedMatch) {
869
- context.report({
870
- node,
871
- messageId: "replaceSdsWithSlds",
872
- data: { oldValue, suggestedMatch },
873
- fix(fixer) {
1157
+ function reportAndFix(node, oldValue, suggestedMatch, messageId, data) {
1158
+ let fixFunction = null;
1159
+ if (suggestedMatch) {
1160
+ fixFunction = (fixer) => {
874
1161
  if (node.type === "Declaration") {
875
1162
  const sourceCode = context.sourceCode;
876
1163
  const fullText = sourceCode.getText();
@@ -881,493 +1168,246 @@ var enforce_sds_to_slds_hooks_default = {
881
1168
  if (textAtPosition === oldValue) {
882
1169
  return fixer.replaceTextRange([propertyStart, propertyEnd], suggestedMatch);
883
1170
  }
1171
+ } else {
1172
+ const sourceCode = context.sourceCode;
1173
+ const fullText = sourceCode.getText();
1174
+ const varFunctionCall = `var(${oldValue})`;
1175
+ const nodeOffset = node.loc.start.offset;
1176
+ const searchStart = Math.max(0, nodeOffset - 4);
1177
+ const searchEnd = nodeOffset + oldValue.length + 1;
1178
+ const searchArea = fullText.substring(searchStart, searchEnd);
1179
+ const functionCallIndex = searchArea.indexOf(varFunctionCall);
1180
+ if (functionCallIndex !== -1) {
1181
+ const actualStart = searchStart + functionCallIndex;
1182
+ const actualEnd = actualStart + varFunctionCall.length;
1183
+ return fixer.replaceTextRange([actualStart, actualEnd], suggestedMatch);
1184
+ }
884
1185
  }
885
- return fixer.replaceText(node, suggestedMatch);
886
- }
887
- });
888
- }
889
- return {
890
- // CSS custom property declarations: --sds-* properties
891
- "Declaration[property=/^--sds-/]"(node) {
892
- const property = node.property;
893
- if (shouldIgnoreDetection2(property)) {
894
- return;
895
- }
896
- const suggestedMatch = toSldsToken(property);
897
- reportAndFix(node, property, suggestedMatch);
898
- },
899
- // SDS tokens inside var() functions: var(--sds-*)
900
- "Function[name='var'] Identifier[name=/^--sds-/]"(node) {
901
- const tokenName = node.name;
902
- if (shouldIgnoreDetection2(tokenName)) {
903
- return;
904
- }
905
- const suggestedMatch = toSldsToken(tokenName);
906
- reportAndFix(node, tokenName, suggestedMatch);
907
- }
908
- };
909
- }
910
- };
911
-
912
- // src/rules/v9/no-sldshook-fallback-for-lwctoken.ts
913
- var import_sds_metadata7 = __toESM(require("@salesforce-ux/sds-metadata"));
914
- var import_rule_messages7 = __toESM(require_rule_messages());
915
- var ruleConfig5 = import_rule_messages7.default["no-sldshook-fallback-for-lwctoken"];
916
- var { type: type6, description: description6, url: url6, messages: messages6 } = ruleConfig5;
917
- var sldsPlusStylingHooks2 = import_sds_metadata7.default.sldsPlusStylingHooks;
918
- var allSldsHooks2 = [...sldsPlusStylingHooks2.global, ...sldsPlusStylingHooks2.component];
919
- var allSldsHooksSet = new Set(allSldsHooks2);
920
- function hasUnsupportedFallback(lwcToken, sldsToken) {
921
- const normalizedSldsToken = sldsToken.replace("--sds-", "--slds-");
922
- return lwcToken.startsWith("--lwc-") && normalizedSldsToken.startsWith("--slds-") && allSldsHooksSet.has(normalizedSldsToken);
923
- }
924
- var no_sldshook_fallback_for_lwctoken_default = {
925
- meta: {
926
- type: type6,
927
- docs: {
928
- description: description6,
929
- recommended: true,
930
- url: url6
931
- },
932
- messages: messages6
933
- },
934
- create(context) {
935
- return {
936
- // Handle LWC tokens inside var() functions: var(--lwc-*, ...)
937
- "Function[name='var'] Identifier[name=/^--lwc-/]"(node) {
938
- const lwcToken = node.name;
939
- const varFunctionNode = context.sourceCode.getAncestors(node).at(-1);
940
- if (!varFunctionNode) return;
941
- const varFunctionChildren = varFunctionNode.children;
942
- if (!varFunctionChildren) return;
943
- let foundComma = false;
944
- let fallbackRawNode = null;
945
- for (const child of varFunctionChildren) {
946
- if (child.type === "Operator" && child.value === ",") {
947
- foundComma = true;
948
- continue;
949
- }
950
- if (foundComma && child.type === "Raw") {
951
- fallbackRawNode = child;
952
- break;
953
- }
954
- }
955
- if (!fallbackRawNode) return;
956
- const fallbackValue = fallbackRawNode.value.trim();
957
- const varMatch = fallbackValue.match(/var\(([^,)]+)/);
958
- if (!varMatch) return;
959
- const sldsToken = varMatch[1];
960
- if (hasUnsupportedFallback(lwcToken, sldsToken)) {
961
- context.report({
962
- node,
963
- messageId: "unsupportedFallback",
964
- data: { lwcToken, sldsToken }
965
- });
966
- }
1186
+ return null;
1187
+ };
967
1188
  }
968
- };
969
- }
970
- };
971
-
972
- // src/rules/v9/no-unsupported-hooks-slds2.ts
973
- var import_sds_metadata8 = __toESM(require("@salesforce-ux/sds-metadata"));
974
- var import_rule_messages8 = __toESM(require_rule_messages());
975
- var ruleConfig6 = import_rule_messages8.default["no-unsupported-hooks-slds2"];
976
- var { type: type7, description: description7, url: url7, messages: messages7 } = ruleConfig6;
977
- var deprecatedHooks = new Set(import_sds_metadata8.default.deprecatedStylingHooks);
978
- function shouldIgnoreDetection3(sldsHook) {
979
- return !deprecatedHooks.has(sldsHook);
980
- }
981
- var no_unsupported_hooks_slds2_default = {
982
- meta: {
983
- type: type7,
984
- docs: {
985
- description: description7,
986
- recommended: true,
987
- url: url7
988
- },
989
- messages: messages7
990
- },
991
- create(context) {
992
- function reportDeprecatedHook(node, token) {
993
1189
  context.report({
994
1190
  node,
995
- messageId: "deprecated",
996
- data: { token }
1191
+ messageId,
1192
+ data,
1193
+ fix: fixFunction
997
1194
  });
998
1195
  }
999
1196
  return {
1000
- // Handle CSS custom property declarations (left-side usage): --slds-* properties
1001
- // Example: .THIS { --slds-g-link-color: #f73650; }
1002
- "Declaration[property=/^--s(lds|ds)-/]"(node) {
1197
+ // CSS custom property declarations: --lwc-* properties
1198
+ "Declaration[property=/^--lwc-/]"(node) {
1003
1199
  const property = node.property;
1004
- if (shouldIgnoreDetection3(property)) {
1005
- return;
1006
- }
1007
- reportDeprecatedHook(node, property);
1008
- },
1009
- // Handle SLDS/SDS hooks inside var() functions (right-side usage): var(--slds-*)
1010
- // Example: .THIS .demo { border-top: 1px solid var(--slds-g-color-border-brand-1); }
1011
- "Function[name='var'] Identifier[name=/^--s(lds|ds)-/]"(node) {
1012
- const tokenName = node.name;
1013
- if (shouldIgnoreDetection3(tokenName)) {
1014
- return;
1015
- }
1016
- reportDeprecatedHook(node, tokenName);
1017
- }
1018
- };
1019
- }
1020
- };
1021
-
1022
- // src/rules/v9/no-slds-var-without-fallback.ts
1023
- var import_sds_metadata9 = __toESM(require("@salesforce-ux/sds-metadata"));
1024
- var import_rule_messages9 = __toESM(require_rule_messages());
1025
-
1026
- // src/utils/hardcoded-shared-utils.ts
1027
- var import_css_tree2 = require("@eslint/css-tree");
1028
-
1029
- // src/utils/color-lib-utils.ts
1030
- var import_chroma_js = __toESM(require("chroma-js"));
1031
- var import_css_tree = require("@eslint/css-tree");
1032
-
1033
- // src/utils/css-functions.ts
1034
- var CSS_FUNCTIONS = [
1035
- "attr",
1036
- "calc",
1037
- "color-mix",
1038
- "conic-gradient",
1039
- "counter",
1040
- "cubic-bezier",
1041
- "linear-gradient",
1042
- "max",
1043
- "min",
1044
- "radial-gradient",
1045
- "repeating-conic-gradient",
1046
- "repeating-linear-gradient",
1047
- "repeating-radial-gradient",
1048
- "var"
1049
- ];
1050
- var CSS_MATH_FUNCTIONS = ["calc", "min", "max"];
1051
- var RGB_COLOR_FUNCTIONS = ["rgb", "rgba", "hsl", "hsla"];
1052
- var cssFunctionsRegex = new RegExp(`(?:${CSS_FUNCTIONS.join("|")})`);
1053
- var cssFunctionsExactRegex = new RegExp(`^(?:${CSS_FUNCTIONS.join("|")})$`);
1054
- var cssMathFunctionsRegex = new RegExp(`^(?:${CSS_MATH_FUNCTIONS.join("|")})$`);
1055
- function isCssFunction(value) {
1056
- return cssFunctionsExactRegex.test(value);
1057
- }
1058
- function isCssColorFunction(value) {
1059
- return RGB_COLOR_FUNCTIONS.includes(value);
1060
- }
1061
-
1062
- // src/utils/color-lib-utils.ts
1063
- var LAB_THRESHOLD = 25;
1064
- var isHexCode = (color) => {
1065
- const hexPattern = /^#(?:[0-9a-fA-F]{3}){1,2}$/;
1066
- return hexPattern.test(color);
1067
- };
1068
- var convertToHex = (color) => {
1069
- try {
1070
- return (0, import_chroma_js.default)(color).hex();
1071
- } catch (e) {
1072
- return null;
1073
- }
1074
- };
1075
- var findClosestColorHook = (color, supportedColors, cssProperty) => {
1076
- const returnStylingHooks = [];
1077
- const closestHooksWithSameProperty = [];
1078
- const closestHooksWithoutSameProperty = [];
1079
- const closestHooksWithAllProperty = [];
1080
- const labColor = (0, import_chroma_js.default)(color).lab();
1081
- Object.entries(supportedColors).forEach(([sldsValue, data]) => {
1082
- if (sldsValue && isHexCode(sldsValue)) {
1083
- const hooks = data;
1084
- hooks.forEach((hook) => {
1085
- const labSupportedColor = (0, import_chroma_js.default)(sldsValue).lab();
1086
- 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");
1087
- if (hook.properties.includes(cssProperty)) {
1088
- if (distance <= LAB_THRESHOLD) {
1089
- closestHooksWithSameProperty.push({ name: hook.name, distance });
1090
- }
1091
- } else if (hook.properties.includes("*")) {
1092
- if (distance <= LAB_THRESHOLD) {
1093
- closestHooksWithAllProperty.push({ name: hook.name, distance });
1094
- }
1095
- } else {
1096
- if (distance <= LAB_THRESHOLD) {
1097
- closestHooksWithoutSameProperty.push({ name: hook.name, distance });
1200
+ if (shouldIgnoreDetection(property)) {
1201
+ return;
1202
+ }
1203
+ const { hasRecommendation, recommendation, replacementCategory } = getRecommendation(property);
1204
+ const { messageId, data } = getReportMessage(property, replacementCategory, recommendation);
1205
+ const suggestedMatch = hasRecommendation && replacementCategory === "slds_token" /* SLDS_TOKEN */ ? recommendation : null;
1206
+ reportAndFix(node, property, suggestedMatch, messageId, data);
1207
+ },
1208
+ // LWC tokens inside var() functions: var(--lwc-*)
1209
+ "Function[name='var'] Identifier[name=/^--lwc-/]"(node) {
1210
+ const tokenName = node.name;
1211
+ if (shouldIgnoreDetection(tokenName)) {
1212
+ return;
1213
+ }
1214
+ const { hasRecommendation, recommendation, replacementCategory } = getRecommendation(tokenName);
1215
+ const { messageId, data } = getReportMessage(tokenName, replacementCategory, recommendation);
1216
+ let suggestedMatch = null;
1217
+ if (hasRecommendation) {
1218
+ if (replacementCategory === "slds_token" /* SLDS_TOKEN */) {
1219
+ const originalVarCall = `var(${tokenName})`;
1220
+ suggestedMatch = `var(${recommendation}, ${originalVarCall})`;
1221
+ } else if (replacementCategory === "raw_value" /* RAW_VALUE */) {
1222
+ suggestedMatch = recommendation;
1098
1223
  }
1099
1224
  }
1100
- });
1101
- }
1102
- });
1103
- const closesthookGroups = [
1104
- { hooks: closestHooksWithSameProperty, distance: 0 },
1105
- { hooks: closestHooksWithAllProperty, distance: 0 },
1106
- { hooks: closestHooksWithSameProperty, distance: Infinity },
1107
- // For hooks with distance > 0
1108
- { hooks: closestHooksWithAllProperty, distance: Infinity },
1109
- { hooks: closestHooksWithoutSameProperty, distance: Infinity }
1110
- ];
1111
- for (const group of closesthookGroups) {
1112
- const filteredHooks = group.hooks.filter(
1113
- (h) => group.distance === 0 ? h.distance === 0 : h.distance > 0
1114
- );
1115
- if (returnStylingHooks.length < 1 && filteredHooks.length > 0) {
1116
- filteredHooks.sort((a, b) => a.distance - b.distance);
1117
- returnStylingHooks.push(...filteredHooks.slice(0, 5).map((h) => h.name));
1118
- }
1119
- }
1120
- return Array.from(new Set(returnStylingHooks));
1121
- };
1122
- var isValidColor = (val) => import_chroma_js.default.valid(val);
1123
- var extractColorValue = (node) => {
1124
- let colorValue = null;
1125
- switch (node.type) {
1126
- case "Hash":
1127
- colorValue = `#${node.value}`;
1128
- break;
1129
- case "Identifier":
1130
- colorValue = node.name;
1131
- break;
1132
- case "Function":
1133
- if (isCssColorFunction(node.name)) {
1134
- colorValue = (0, import_css_tree.generate)(node);
1225
+ reportAndFix(node, tokenName, suggestedMatch, messageId, data);
1135
1226
  }
1136
- break;
1227
+ };
1137
1228
  }
1138
- return colorValue && isValidColor(colorValue) ? colorValue : null;
1139
1229
  };
1140
1230
 
1141
- // src/utils/hardcoded-shared-utils.ts
1142
- var FONT_WEIGHTS = [
1143
- "normal",
1144
- "bold",
1145
- "bolder",
1146
- "lighter",
1147
- "100",
1148
- "200",
1149
- "300",
1150
- "400",
1151
- "500",
1152
- "600",
1153
- "700",
1154
- "800",
1155
- "900"
1156
- ];
1157
- function isKnownFontWeight(value) {
1158
- const stringValue = value.toString();
1159
- return FONT_WEIGHTS.includes(stringValue.toLowerCase());
1231
+ // src/rules/v9/enforce-sds-to-slds-hooks.ts
1232
+ var import_sds_metadata6 = __toESM(require("@salesforce-ux/sds-metadata"));
1233
+ var import_rule_messages6 = __toESM(require_rule_messages());
1234
+ var ruleConfig4 = import_rule_messages6.default["enforce-sds-to-slds-hooks"];
1235
+ var { type: type5, description: description5, url: url5, messages: messages5 } = ruleConfig4;
1236
+ var sldsPlusStylingHooks = import_sds_metadata6.default.sldsPlusStylingHooks;
1237
+ var allSldsHooks = [...sldsPlusStylingHooks.global, ...sldsPlusStylingHooks.component];
1238
+ var toSldsToken = (sdsToken) => sdsToken.replace("--sds-", "--slds-");
1239
+ function shouldIgnoreDetection2(sdsToken) {
1240
+ return !sdsToken.startsWith("--sds-") || !allSldsHooks.includes(toSldsToken(sdsToken));
1160
1241
  }
1161
- function handleShorthandAutoFix(declarationNode, context, valueText, replacements) {
1162
- const sortedReplacements = replacements.sort((a, b) => a.start - b.start);
1163
- const hasAnyHooks = sortedReplacements.some((r) => r.hasHook);
1164
- const canAutoFix = hasAnyHooks;
1165
- sortedReplacements.forEach(({ start, end, replacement, displayValue, hasHook }) => {
1166
- const originalValue = valueText.substring(start, end);
1167
- const valueStartColumn = declarationNode.value.loc.start.column;
1168
- const valueColumn = valueStartColumn + start;
1169
- const { loc: { start: locStart, end: locEnd } } = declarationNode.value;
1170
- const reportNode = {
1171
- ...declarationNode.value,
1172
- loc: {
1173
- ...declarationNode.value.loc,
1174
- start: {
1175
- ...locStart,
1176
- column: valueColumn
1177
- },
1178
- end: {
1179
- ...locEnd,
1180
- column: valueColumn + originalValue.length
1181
- }
1182
- }
1183
- };
1184
- if (hasHook) {
1185
- const fix = canAutoFix ? (fixer) => {
1186
- let newValue = valueText;
1187
- for (let i = sortedReplacements.length - 1; i >= 0; i--) {
1188
- const { start: rStart, end: rEnd, replacement: rReplacement } = sortedReplacements[i];
1189
- newValue = newValue.substring(0, rStart) + rReplacement + newValue.substring(rEnd);
1190
- }
1191
- return fixer.replaceText(declarationNode.value, newValue);
1192
- } : void 0;
1193
- context.context.report({
1194
- node: reportNode,
1195
- messageId: "hardcodedValue",
1196
- data: {
1197
- oldValue: originalValue,
1198
- newValue: displayValue
1199
- },
1200
- fix
1201
- });
1202
- } else {
1203
- context.context.report({
1204
- node: reportNode,
1205
- messageId: "noReplacement",
1206
- data: {
1207
- oldValue: originalValue
1242
+ var enforce_sds_to_slds_hooks_default = {
1243
+ meta: {
1244
+ type: type5,
1245
+ docs: {
1246
+ description: description5,
1247
+ recommended: true,
1248
+ url: url5
1249
+ },
1250
+ fixable: "code",
1251
+ messages: messages5
1252
+ },
1253
+ create(context) {
1254
+ function reportAndFix(node, oldValue, suggestedMatch) {
1255
+ context.report({
1256
+ node,
1257
+ messageId: "replaceSdsWithSlds",
1258
+ data: { oldValue, suggestedMatch },
1259
+ fix(fixer) {
1260
+ if (node.type === "Declaration") {
1261
+ const sourceCode = context.sourceCode;
1262
+ const fullText = sourceCode.getText();
1263
+ const nodeOffset = node.loc.start.offset;
1264
+ const propertyStart = nodeOffset;
1265
+ const propertyEnd = propertyStart + oldValue.length;
1266
+ const textAtPosition = fullText.substring(propertyStart, propertyEnd);
1267
+ if (textAtPosition === oldValue) {
1268
+ return fixer.replaceTextRange([propertyStart, propertyEnd], suggestedMatch);
1269
+ }
1270
+ }
1271
+ return fixer.replaceText(node, suggestedMatch);
1208
1272
  }
1209
1273
  });
1210
1274
  }
1211
- });
1212
- }
1213
- function forEachValue(valueText, extractValue, shouldSkipNode, callback) {
1214
- if (!valueText || typeof valueText !== "string") {
1215
- return;
1275
+ return {
1276
+ // CSS custom property declarations: --sds-* properties
1277
+ "Declaration[property=/^--sds-/]"(node) {
1278
+ const property = node.property;
1279
+ if (shouldIgnoreDetection2(property)) {
1280
+ return;
1281
+ }
1282
+ const suggestedMatch = toSldsToken(property);
1283
+ reportAndFix(node, property, suggestedMatch);
1284
+ },
1285
+ // SDS tokens inside var() functions: var(--sds-*)
1286
+ "Function[name='var'] Identifier[name=/^--sds-/]"(node) {
1287
+ const tokenName = node.name;
1288
+ if (shouldIgnoreDetection2(tokenName)) {
1289
+ return;
1290
+ }
1291
+ const suggestedMatch = toSldsToken(tokenName);
1292
+ reportAndFix(node, tokenName, suggestedMatch);
1293
+ }
1294
+ };
1216
1295
  }
1217
- try {
1218
- const ast = (0, import_css_tree2.parse)(valueText, { context: "value", positions: true });
1219
- (0, import_css_tree2.walk)(ast, {
1220
- enter(node) {
1221
- if (shouldSkipNode(node)) {
1222
- return this.skip;
1296
+ };
1297
+
1298
+ // src/rules/v9/no-sldshook-fallback-for-lwctoken.ts
1299
+ var import_sds_metadata7 = __toESM(require("@salesforce-ux/sds-metadata"));
1300
+ var import_rule_messages7 = __toESM(require_rule_messages());
1301
+ var ruleConfig5 = import_rule_messages7.default["no-sldshook-fallback-for-lwctoken"];
1302
+ var { type: type6, description: description6, url: url6, messages: messages6 } = ruleConfig5;
1303
+ var sldsPlusStylingHooks2 = import_sds_metadata7.default.sldsPlusStylingHooks;
1304
+ var allSldsHooks2 = [...sldsPlusStylingHooks2.global, ...sldsPlusStylingHooks2.component];
1305
+ var allSldsHooksSet = new Set(allSldsHooks2);
1306
+ function hasUnsupportedFallback(lwcToken, sldsToken) {
1307
+ const normalizedSldsToken = sldsToken.replace("--sds-", "--slds-");
1308
+ return lwcToken.startsWith("--lwc-") && normalizedSldsToken.startsWith("--slds-") && allSldsHooksSet.has(normalizedSldsToken);
1309
+ }
1310
+ var no_sldshook_fallback_for_lwctoken_default = {
1311
+ meta: {
1312
+ type: type6,
1313
+ docs: {
1314
+ description: description6,
1315
+ recommended: true,
1316
+ url: url6
1317
+ },
1318
+ messages: messages6
1319
+ },
1320
+ create(context) {
1321
+ return {
1322
+ // Handle LWC tokens inside var() functions: var(--lwc-*, ...)
1323
+ "Function[name='var'] Identifier[name=/^--lwc-/]"(node) {
1324
+ const lwcToken = node.name;
1325
+ const varFunctionNode = context.sourceCode.getAncestors(node).at(-1);
1326
+ if (!varFunctionNode) return;
1327
+ const varFunctionChildren = varFunctionNode.children;
1328
+ if (!varFunctionChildren) return;
1329
+ let foundComma = false;
1330
+ let fallbackRawNode = null;
1331
+ for (const child of varFunctionChildren) {
1332
+ if (child.type === "Operator" && child.value === ",") {
1333
+ foundComma = true;
1334
+ continue;
1335
+ }
1336
+ if (foundComma && child.type === "Raw") {
1337
+ fallbackRawNode = child;
1338
+ break;
1339
+ }
1223
1340
  }
1224
- const value = extractValue(node);
1225
- if (value !== null) {
1226
- const positionInfo = {
1227
- start: node.loc?.start,
1228
- end: node.loc?.end
1229
- };
1230
- callback(value, positionInfo);
1341
+ if (!fallbackRawNode) return;
1342
+ const fallbackValue = fallbackRawNode.value.trim();
1343
+ const varMatch = fallbackValue.match(/var\(([^,)]+)/);
1344
+ if (!varMatch) return;
1345
+ const sldsToken = varMatch[1];
1346
+ if (hasUnsupportedFallback(lwcToken, sldsToken)) {
1347
+ context.report({
1348
+ node,
1349
+ messageId: "unsupportedFallback",
1350
+ data: { lwcToken, sldsToken }
1351
+ });
1231
1352
  }
1232
1353
  }
1233
- });
1234
- } catch (error) {
1235
- return;
1236
- }
1237
- }
1238
- function shouldSkipColorNode(node) {
1239
- return node.type === "Function" && isCssFunction(node.name);
1240
- }
1241
- function shouldSkipDimensionNode(node) {
1242
- return node.type === "Function";
1243
- }
1244
- function extractDimensionValue(valueNode, cssProperty) {
1245
- if (!valueNode) return null;
1246
- switch (valueNode.type) {
1247
- case "Dimension":
1248
- const numValue = Number(valueNode.value);
1249
- if (numValue === 0) return null;
1250
- const unit = valueNode.unit.toLowerCase();
1251
- if (unit !== "px" && unit !== "rem" && unit !== "%") return null;
1252
- return {
1253
- number: numValue,
1254
- unit
1255
- };
1256
- case "Number":
1257
- const numberValue = Number(valueNode.value);
1258
- if (numberValue === 0) return null;
1259
- return {
1260
- number: numberValue,
1261
- unit: null
1262
- };
1263
- case "Percentage":
1264
- const percentValue = Number(valueNode.value);
1265
- if (percentValue === 0) return null;
1266
- return {
1267
- number: percentValue,
1268
- unit: "%"
1269
- };
1270
- case "Value":
1271
- return valueNode.children?.[0] ? extractDimensionValue(valueNode.children[0], cssProperty) : null;
1354
+ };
1272
1355
  }
1273
- return null;
1274
- }
1275
- function forEachColorValue(valueText, callback) {
1276
- forEachValue(valueText, extractColorValue, shouldSkipColorNode, callback);
1277
- }
1278
- function forEachDensityValue(valueText, cssProperty, callback) {
1279
- forEachValue(
1280
- valueText,
1281
- (node) => extractDimensionValue(node, cssProperty),
1282
- shouldSkipDimensionNode,
1283
- callback
1284
- );
1356
+ };
1357
+
1358
+ // src/rules/v9/no-unsupported-hooks-slds2.ts
1359
+ var import_sds_metadata8 = __toESM(require("@salesforce-ux/sds-metadata"));
1360
+ var import_rule_messages8 = __toESM(require_rule_messages());
1361
+ var ruleConfig6 = import_rule_messages8.default["no-unsupported-hooks-slds2"];
1362
+ var { type: type7, description: description7, url: url7, messages: messages7 } = ruleConfig6;
1363
+ var deprecatedHooks = new Set(import_sds_metadata8.default.deprecatedStylingHooks);
1364
+ function shouldIgnoreDetection3(sldsHook) {
1365
+ return !deprecatedHooks.has(sldsHook);
1285
1366
  }
1286
- function extractFontValue(node) {
1287
- if (!node) return null;
1288
- switch (node.type) {
1289
- case "Dimension":
1290
- const numValue = Number(node.value);
1291
- if (numValue <= 0) return null;
1292
- const unit = node.unit.toLowerCase();
1293
- if (unit !== "px" && unit !== "rem" && unit !== "%") return null;
1294
- return {
1295
- number: numValue,
1296
- unit
1297
- };
1298
- case "Number":
1299
- const numberValue = Number(node.value);
1300
- if (numberValue <= 0) {
1301
- return null;
1302
- }
1303
- if (!isKnownFontWeight(numberValue)) {
1304
- return null;
1305
- }
1306
- return {
1307
- number: numberValue,
1308
- unit: null
1309
- };
1310
- case "Identifier":
1311
- const namedValue = node.name.toLowerCase();
1312
- if (!isKnownFontWeight(namedValue)) {
1313
- return null;
1314
- }
1315
- if (namedValue === "normal") {
1316
- return { number: 400, unit: null };
1367
+ var no_unsupported_hooks_slds2_default = {
1368
+ meta: {
1369
+ type: type7,
1370
+ docs: {
1371
+ description: description7,
1372
+ recommended: true,
1373
+ url: url7
1374
+ },
1375
+ messages: messages7
1376
+ },
1377
+ create(context) {
1378
+ function reportDeprecatedHook(node, token) {
1379
+ context.report({
1380
+ node,
1381
+ messageId: "deprecated",
1382
+ data: { token }
1383
+ });
1384
+ }
1385
+ return {
1386
+ // Handle CSS custom property declarations (left-side usage): --slds-* properties
1387
+ // Example: .THIS { --slds-g-link-color: #f73650; }
1388
+ "Declaration[property=/^--s(lds|ds)-/]"(node) {
1389
+ const property = node.property;
1390
+ if (shouldIgnoreDetection3(property)) {
1391
+ return;
1392
+ }
1393
+ reportDeprecatedHook(node, property);
1394
+ },
1395
+ // Handle SLDS/SDS hooks inside var() functions (right-side usage): var(--slds-*)
1396
+ // Example: .THIS .demo { border-top: 1px solid var(--slds-g-color-border-brand-1); }
1397
+ "Function[name='var'] Identifier[name=/^--s(lds|ds)-/]"(node) {
1398
+ const tokenName = node.name;
1399
+ if (shouldIgnoreDetection3(tokenName)) {
1400
+ return;
1401
+ }
1402
+ reportDeprecatedHook(node, tokenName);
1317
1403
  }
1318
- return { number: namedValue, unit: null };
1319
- case "Percentage":
1320
- const percentValue = Number(node.value);
1321
- if (percentValue === 0) return null;
1322
- return {
1323
- number: percentValue,
1324
- unit: "%"
1325
- };
1326
- case "Value":
1327
- return node.children?.[0] ? extractFontValue(node.children[0]) : null;
1328
- }
1329
- return null;
1330
- }
1331
- function shouldSkipFontNode(node) {
1332
- return node.type === "Function";
1333
- }
1334
- function forEachFontValue(valueText, callback) {
1335
- forEachValue(valueText, extractFontValue, shouldSkipFontNode, callback);
1336
- }
1337
-
1338
- // src/utils/css-utils.ts
1339
- function extractSldsVariable(node) {
1340
- if (!node || node.type !== "Function" || node.name !== "var") {
1341
- return null;
1342
- }
1343
- if (!node.children) {
1344
- return null;
1345
- }
1346
- const childrenArray = Array.from(node.children);
1347
- if (childrenArray.length === 0) {
1348
- return null;
1349
- }
1350
- const firstChild = childrenArray[0];
1351
- if (!firstChild || firstChild.type !== "Identifier") {
1352
- return null;
1353
- }
1354
- const variableName = firstChild.name;
1355
- if (!variableName || !variableName.startsWith("--slds-")) {
1356
- return null;
1404
+ };
1357
1405
  }
1358
- const hasFallback = childrenArray.some(
1359
- (child) => child.type === "Operator" && child.value === ","
1360
- );
1361
- return {
1362
- name: variableName,
1363
- hasFallback
1364
- };
1365
- }
1366
- function forEachSldsVariable(valueText, callback) {
1367
- forEachValue(valueText, extractSldsVariable, () => false, callback);
1368
- }
1406
+ };
1369
1407
 
1370
1408
  // src/rules/v9/no-slds-var-without-fallback.ts
1409
+ var import_sds_metadata9 = __toESM(require("@salesforce-ux/sds-metadata"));
1410
+ var import_rule_messages9 = __toESM(require_rule_messages());
1371
1411
  var ruleConfig7 = import_rule_messages9.default["no-slds-var-without-fallback"];
1372
1412
  var { type: type8, description: description8, url: url8, messages: messages8 } = ruleConfig7;
1373
1413
  var sldsVariables = import_sds_metadata9.default.slds1ExcludedVars || {};
@@ -1700,7 +1740,7 @@ function toSelector(properties) {
1700
1740
  const selectorParts = properties.map((prop) => {
1701
1741
  if (prop.includes("*")) {
1702
1742
  const regexPattern = prop.replace(/\*/g, ".*");
1703
- return `Declaration[property=/${regexPattern}$/]`;
1743
+ return `Declaration[property=/^${regexPattern}$/]`;
1704
1744
  } else {
1705
1745
  return `Declaration[property='${prop}']`;
1706
1746
  }
@@ -1771,9 +1811,8 @@ function createColorReplacement(colorValue, cssProperty, context, positionInfo,
1771
1811
  end,
1772
1812
  replacement: originalValue,
1773
1813
  // Use original value to preserve spacing
1774
- displayValue: closestHooks.join(", "),
1814
+ displayValue: formatSuggestionHooks(closestHooks),
1775
1815
  hasHook: true
1776
- // ← THE FIX: Multiple hooks still means "has hooks"
1777
1816
  };
1778
1817
  } else {
1779
1818
  return {
@@ -1787,37 +1826,6 @@ function createColorReplacement(colorValue, cssProperty, context, positionInfo,
1787
1826
  }
1788
1827
  }
1789
1828
 
1790
- // src/utils/value-utils.ts
1791
- function parseUnitValue(value) {
1792
- if (!value) return null;
1793
- const match = value.match(/^(-?\d*\.?\d+)(px|rem|%)?$/);
1794
- if (!match) return null;
1795
- const number = parseFloat(match[1]);
1796
- const unit = match[2] ? match[2] : null;
1797
- if (isNaN(number)) return null;
1798
- return { number, unit };
1799
- }
1800
- function toAlternateUnitValue(numberVal, unitType) {
1801
- if (unitType === "px") {
1802
- let floatValue = parseFloat(`${numberVal / 16}`);
1803
- if (!isNaN(floatValue)) {
1804
- return {
1805
- unit: "rem",
1806
- number: parseFloat(floatValue.toFixed(4))
1807
- };
1808
- }
1809
- } else if (unitType === "rem") {
1810
- const intValue = parseInt(`${numberVal * 16}`);
1811
- if (!isNaN(intValue)) {
1812
- return {
1813
- unit: "px",
1814
- number: intValue
1815
- };
1816
- }
1817
- }
1818
- return null;
1819
- }
1820
-
1821
1829
  // src/utils/styling-hook-utils.ts
1822
1830
  function isValueMatch(valueToMatch, sldsValue) {
1823
1831
  if (!valueToMatch || !sldsValue) {
@@ -1875,7 +1883,7 @@ function createDimensionReplacement(parsedDimension, cssProperty, context, posit
1875
1883
  start,
1876
1884
  end,
1877
1885
  replacement: rawValue,
1878
- displayValue: closestHooks.join(", "),
1886
+ displayValue: formatSuggestionHooks(closestHooks),
1879
1887
  hasHook: true
1880
1888
  };
1881
1889
  } else {
@@ -1942,7 +1950,7 @@ function createFontReplacement(fontValue, cssProperty, context, positionInfo) {
1942
1950
  start,
1943
1951
  end,
1944
1952
  replacement: rawValue,
1945
- displayValue: closestHooks.join(", "),
1953
+ displayValue: formatSuggestionHooks(closestHooks),
1946
1954
  hasHook: true
1947
1955
  };
1948
1956
  } else {
@@ -2069,59 +2077,51 @@ function isBoxShadowMatch(parsedCssValue, parsedValueHook) {
2069
2077
  }
2070
2078
 
2071
2079
  // src/rules/v9/no-hardcoded-values/handlers/boxShadowHandler.ts
2072
- function containsCssVariable(valueText) {
2073
- return valueText.includes("var(");
2074
- }
2075
- var handleBoxShadowDeclaration = (node, context) => {
2076
- const cssProperty = node.property.toLowerCase();
2077
- const valueText = context.sourceCode.getText(node.value);
2078
- if (containsCssVariable(valueText)) {
2079
- return;
2080
- }
2081
- const replacements = [];
2082
- const parsedCssValue = parseAndValidateBoxShadow(valueText);
2083
- if (parsedCssValue) {
2084
- const shadowHooks = getBoxShadowHooks(context.valueToStylinghook);
2085
- const closestHooks = findMatchingBoxShadowHooks(parsedCssValue, shadowHooks);
2086
- const positionInfo = {
2087
- start: { offset: 0, line: 1, column: 1 },
2088
- end: { offset: valueText.length, line: 1, column: valueText.length + 1 }
2089
- };
2090
- const replacement = createBoxShadowReplacement(
2091
- valueText,
2092
- closestHooks,
2093
- context,
2094
- positionInfo
2095
- );
2096
- if (replacement) {
2097
- replacements.push(replacement);
2098
- }
2080
+ function toBoxShadowValue(cssValue) {
2081
+ const parsedCssValue = parseBoxShadowValue(cssValue).filter((shadow) => Object.keys(shadow).length > 0);
2082
+ if (parsedCssValue.length === 0) {
2083
+ return null;
2099
2084
  }
2100
- handleShorthandAutoFix(node, context, valueText, replacements);
2101
- };
2102
- function getBoxShadowHooks(supportedStylinghooks) {
2085
+ return parsedCssValue;
2086
+ }
2087
+ function shadowValueToHookEntries(supportedStylinghooks) {
2103
2088
  return Object.entries(supportedStylinghooks).filter(([key, value]) => {
2104
2089
  return value.some((hook) => hook.properties.includes("box-shadow"));
2105
2090
  }).map(([key, value]) => {
2106
2091
  return [key, value.map((hook) => hook.name)];
2107
2092
  });
2108
2093
  }
2109
- function parseAndValidateBoxShadow(cssValue) {
2110
- const parsedCssValue = parseBoxShadowValue(cssValue).filter((shadow) => Object.keys(shadow).length > 0);
2111
- if (parsedCssValue.length === 0) {
2112
- return null;
2094
+ var handleBoxShadowDeclaration = (node, context) => {
2095
+ const cssProperty = node.property.toLowerCase();
2096
+ const valueText = context.sourceCode.getText(node.value);
2097
+ const shadowHooks = shadowValueToHookEntries(context.valueToStylinghook);
2098
+ const parsedCssValue = toBoxShadowValue(valueText);
2099
+ if (!parsedCssValue) {
2100
+ return;
2113
2101
  }
2114
- return parsedCssValue;
2115
- }
2116
- function findMatchingBoxShadowHooks(parsedCssValue, shadowHooks) {
2117
- for (const [shadowHookValue, closestHooks] of shadowHooks) {
2118
- const parsedHookValue = parseAndValidateBoxShadow(shadowHookValue);
2119
- if (parsedHookValue && isBoxShadowMatch(parsedCssValue, parsedHookValue)) {
2120
- return closestHooks;
2102
+ for (const [shadow, closestHooks] of shadowHooks) {
2103
+ const parsedValueHook = toBoxShadowValue(shadow);
2104
+ if (parsedValueHook && isBoxShadowMatch(parsedCssValue, parsedValueHook)) {
2105
+ if (closestHooks.length > 0) {
2106
+ const positionInfo = {
2107
+ start: { offset: 0, line: 1, column: 1 },
2108
+ end: { offset: valueText.length, line: 1, column: valueText.length + 1 }
2109
+ };
2110
+ const replacement = createBoxShadowReplacement(
2111
+ valueText,
2112
+ closestHooks,
2113
+ context,
2114
+ positionInfo
2115
+ );
2116
+ if (replacement) {
2117
+ const replacements = [replacement];
2118
+ handleShorthandAutoFix(node, context, valueText, replacements);
2119
+ }
2120
+ }
2121
+ return;
2121
2122
  }
2122
2123
  }
2123
- return [];
2124
- }
2124
+ };
2125
2125
  function createBoxShadowReplacement(originalValue, hooks, context, positionInfo) {
2126
2126
  if (!positionInfo?.start) {
2127
2127
  return null;
@@ -2136,21 +2136,13 @@ function createBoxShadowReplacement(originalValue, hooks, context, positionInfo)
2136
2136
  displayValue: hooks[0],
2137
2137
  hasHook: true
2138
2138
  };
2139
- } else if (hooks.length > 1) {
2140
- return {
2141
- start,
2142
- end,
2143
- replacement: originalValue,
2144
- displayValue: hooks.join(", "),
2145
- hasHook: true
2146
- };
2147
2139
  } else {
2148
2140
  return {
2149
2141
  start,
2150
2142
  end,
2151
2143
  replacement: originalValue,
2152
- displayValue: originalValue,
2153
- hasHook: false
2144
+ displayValue: formatSuggestionHooks(hooks),
2145
+ hasHook: true
2154
2146
  };
2155
2147
  }
2156
2148
  }
@@ -2280,6 +2272,7 @@ var eslint_rules_default = {
2280
2272
  css: {
2281
2273
  "@salesforce-ux/slds/no-slds-class-overrides": "warn",
2282
2274
  "@salesforce-ux/slds/no-deprecated-slds-classes": "warn",
2275
+ "@salesforce-ux/slds/lwc-token-to-slds-hook": "error",
2283
2276
  "@salesforce-ux/slds/enforce-sds-to-slds-hooks": "warn",
2284
2277
  "@salesforce-ux/slds/no-sldshook-fallback-for-lwctoken": "warn",
2285
2278
  "@salesforce-ux/slds/no-unsupported-hooks-slds2": "warn",
@@ -2320,7 +2313,7 @@ var rules = {
2320
2313
  var plugin = {
2321
2314
  meta: {
2322
2315
  name: "@salesforce-ux/eslint-plugin-slds",
2323
- version: "1.0.0"
2316
+ version: "1.0.2"
2324
2317
  },
2325
2318
  rules,
2326
2319
  configs: {}