@salesforce-ux/eslint-plugin-slds 1.0.7 → 1.0.9-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.
- package/README.md +62 -0
- package/build/config/rule-messages.d.ts +2 -0
- package/build/config/rule-messages.js +197 -0
- package/build/config/rule-messages.js.map +7 -0
- package/build/index.js +40 -2332
- package/build/index.js.map +4 -4
- package/build/rules/enforce-bem-usage.js +18 -234
- package/build/rules/enforce-bem-usage.js.map +4 -4
- package/build/rules/modal-close-button-issue.js +11 -183
- package/build/rules/modal-close-button-issue.js.map +4 -4
- package/build/rules/no-deprecated-classes-slds2.js +6 -178
- package/build/rules/no-deprecated-classes-slds2.js.map +4 -4
- package/build/rules/v9/enforce-bem-usage.js +3 -163
- package/build/rules/v9/enforce-bem-usage.js.map +4 -4
- package/build/rules/v9/enforce-component-hook-naming-convention.js +3 -163
- package/build/rules/v9/enforce-component-hook-naming-convention.js.map +4 -4
- package/build/rules/v9/enforce-sds-to-slds-hooks.js +3 -163
- package/build/rules/v9/enforce-sds-to-slds-hooks.js.map +4 -4
- package/build/rules/v9/lwc-token-to-slds-hook.js +6 -264
- package/build/rules/v9/lwc-token-to-slds-hook.js.map +4 -4
- package/build/rules/v9/no-deprecated-slds-classes.js +3 -163
- package/build/rules/v9/no-deprecated-slds-classes.js.map +4 -4
- package/build/rules/v9/no-deprecated-tokens-slds1.js +30 -169
- package/build/rules/v9/no-deprecated-tokens-slds1.js.map +4 -4
- package/build/rules/v9/no-hardcoded-values/handlers/boxShadowHandler.js +29 -253
- package/build/rules/v9/no-hardcoded-values/handlers/boxShadowHandler.js.map +4 -4
- package/build/rules/v9/no-hardcoded-values/handlers/colorHandler.js +31 -289
- package/build/rules/v9/no-hardcoded-values/handlers/colorHandler.js.map +4 -4
- package/build/rules/v9/no-hardcoded-values/handlers/densityHandler.js +23 -286
- package/build/rules/v9/no-hardcoded-values/handlers/densityHandler.js.map +4 -4
- package/build/rules/v9/no-hardcoded-values/handlers/fontHandler.js +24 -316
- package/build/rules/v9/no-hardcoded-values/handlers/fontHandler.js.map +4 -4
- package/build/rules/v9/no-hardcoded-values/handlers/index.js +8 -799
- package/build/rules/v9/no-hardcoded-values/handlers/index.js.map +4 -4
- package/build/rules/v9/no-hardcoded-values/no-hardcoded-values-slds1.js +5 -1090
- package/build/rules/v9/no-hardcoded-values/no-hardcoded-values-slds1.js.map +4 -4
- package/build/rules/v9/no-hardcoded-values/no-hardcoded-values-slds2.js +5 -1090
- package/build/rules/v9/no-hardcoded-values/no-hardcoded-values-slds2.js.map +4 -4
- package/build/rules/v9/no-hardcoded-values/noHardcodedValueRule.js +31 -882
- package/build/rules/v9/no-hardcoded-values/noHardcodedValueRule.js.map +4 -4
- package/build/rules/v9/no-hardcoded-values/ruleOptionsSchema.d.ts +40 -0
- package/build/rules/v9/no-hardcoded-values/ruleOptionsSchema.js +63 -0
- package/build/rules/v9/no-hardcoded-values/ruleOptionsSchema.js.map +7 -0
- package/build/rules/v9/no-slds-class-overrides.js +3 -163
- package/build/rules/v9/no-slds-class-overrides.js.map +4 -4
- package/build/rules/v9/no-slds-namespace-for-custom-hooks.js +5 -254
- package/build/rules/v9/no-slds-namespace-for-custom-hooks.js.map +4 -4
- package/build/rules/v9/no-slds-private-var.js +1 -161
- package/build/rules/v9/no-slds-private-var.js.map +4 -4
- package/build/rules/v9/no-slds-var-without-fallback.js +5 -257
- package/build/rules/v9/no-slds-var-without-fallback.js.map +4 -4
- package/build/rules/v9/no-sldshook-fallback-for-lwctoken.js +3 -163
- package/build/rules/v9/no-sldshook-fallback-for-lwctoken.js.map +4 -4
- package/build/rules/v9/no-unsupported-hooks-slds2.js +3 -163
- package/build/rules/v9/no-unsupported-hooks-slds2.js.map +4 -4
- package/build/rules/v9/reduce-annotations.js +1 -161
- package/build/rules/v9/reduce-annotations.js.map +4 -4
- package/build/types/index.d.ts +57 -0
- package/build/types/index.js.map +1 -1
- package/build/{src/utils → utils}/boxShadowValueParser.d.ts +1 -1
- package/build/utils/boxShadowValueParser.js +63 -93
- package/build/utils/boxShadowValueParser.js.map +4 -4
- package/build/utils/color-lib-utils.d.ts +25 -0
- package/build/utils/color-lib-utils.js +28 -79
- package/build/utils/color-lib-utils.js.map +3 -3
- package/build/{src/utils → utils}/css-utils.d.ts +7 -0
- package/build/utils/css-utils.js +10 -71
- package/build/utils/css-utils.js.map +4 -4
- package/build/utils/custom-mapping-utils.d.ts +9 -0
- package/build/utils/custom-mapping-utils.js +62 -0
- package/build/utils/custom-mapping-utils.js.map +7 -0
- package/build/{src/utils → utils}/hardcoded-shared-utils.d.ts +1 -0
- package/build/utils/hardcoded-shared-utils.js +46 -98
- package/build/utils/hardcoded-shared-utils.js.map +4 -4
- package/build/{src/utils → utils}/property-matcher.d.ts +3 -1
- package/build/utils/property-matcher.js +22 -8
- package/build/utils/property-matcher.js.map +2 -2
- package/build/utils/styling-hook-utils.js +16 -39
- package/build/utils/styling-hook-utils.js.map +3 -3
- package/build/{src/utils → utils}/value-utils.d.ts +2 -2
- package/build/utils/value-utils.js +8 -8
- package/build/utils/value-utils.js.map +2 -2
- package/package.json +6 -11
- package/build/src/types/index.d.ts +0 -26
- package/build/src/utils/color-lib-utils.d.ts +0 -18
- package/src/config/rule-messages.yml +0 -143
- /package/build/{src/index.d.ts → index.d.ts} +0 -0
- /package/build/{src/rules → rules}/enforce-bem-usage.d.ts +0 -0
- /package/build/{src/rules → rules}/modal-close-button-issue.d.ts +0 -0
- /package/build/{src/rules → rules}/no-deprecated-classes-slds2.d.ts +0 -0
- /package/build/{src/rules → rules}/v9/enforce-bem-usage.d.ts +0 -0
- /package/build/{src/rules → rules}/v9/enforce-component-hook-naming-convention.d.ts +0 -0
- /package/build/{src/rules → rules}/v9/enforce-sds-to-slds-hooks.d.ts +0 -0
- /package/build/{src/rules → rules}/v9/lwc-token-to-slds-hook.d.ts +0 -0
- /package/build/{src/rules → rules}/v9/no-deprecated-slds-classes.d.ts +0 -0
- /package/build/{src/rules → rules}/v9/no-deprecated-tokens-slds1.d.ts +0 -0
- /package/build/{src/rules → rules}/v9/no-hardcoded-values/handlers/boxShadowHandler.d.ts +0 -0
- /package/build/{src/rules → rules}/v9/no-hardcoded-values/handlers/colorHandler.d.ts +0 -0
- /package/build/{src/rules → rules}/v9/no-hardcoded-values/handlers/densityHandler.d.ts +0 -0
- /package/build/{src/rules → rules}/v9/no-hardcoded-values/handlers/fontHandler.d.ts +0 -0
- /package/build/{src/rules → rules}/v9/no-hardcoded-values/handlers/index.d.ts +0 -0
- /package/build/{src/rules → rules}/v9/no-hardcoded-values/no-hardcoded-values-slds1.d.ts +0 -0
- /package/build/{src/rules → rules}/v9/no-hardcoded-values/no-hardcoded-values-slds2.d.ts +0 -0
- /package/build/{src/rules → rules}/v9/no-hardcoded-values/noHardcodedValueRule.d.ts +0 -0
- /package/build/{src/rules → rules}/v9/no-slds-class-overrides.d.ts +0 -0
- /package/build/{src/rules → rules}/v9/no-slds-namespace-for-custom-hooks.d.ts +0 -0
- /package/build/{src/rules → rules}/v9/no-slds-private-var.d.ts +0 -0
- /package/build/{src/rules → rules}/v9/no-slds-var-without-fallback.d.ts +0 -0
- /package/build/{src/rules → rules}/v9/no-sldshook-fallback-for-lwctoken.d.ts +0 -0
- /package/build/{src/rules → rules}/v9/no-unsupported-hooks-slds2.d.ts +0 -0
- /package/build/{src/rules → rules}/v9/reduce-annotations.d.ts +0 -0
- /package/build/{src/utils → utils}/css-functions.d.ts +0 -0
- /package/build/{src/utils → utils}/node.d.ts +0 -0
- /package/build/{src/utils → utils}/rule-utils.d.ts +0 -0
- /package/build/{src/utils → utils}/styling-hook-utils.d.ts +0 -0
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
var __create = Object.create;
|
|
2
1
|
var __defProp = Object.defineProperty;
|
|
3
2
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
6
4
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
5
|
var __export = (target, all) => {
|
|
8
6
|
for (var name in all)
|
|
@@ -16,14 +14,6 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
14
|
}
|
|
17
15
|
return to;
|
|
18
16
|
};
|
|
19
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
-
mod
|
|
26
|
-
));
|
|
27
17
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
18
|
|
|
29
19
|
// src/utils/hardcoded-shared-utils.ts
|
|
@@ -37,65 +27,10 @@ __export(hardcoded_shared_utils_exports, {
|
|
|
37
27
|
isKnownFontWeight: () => isKnownFontWeight
|
|
38
28
|
});
|
|
39
29
|
module.exports = __toCommonJS(hardcoded_shared_utils_exports);
|
|
40
|
-
var import_css_tree2 = require("@eslint/css-tree");
|
|
41
|
-
|
|
42
|
-
// src/utils/value-utils.ts
|
|
43
|
-
var ALLOWED_UNITS = ["px", "em", "rem", "%", "ch"];
|
|
44
|
-
|
|
45
|
-
// src/utils/color-lib-utils.ts
|
|
46
|
-
var import_chroma_js = __toESM(require("chroma-js"));
|
|
47
30
|
var import_css_tree = require("@eslint/css-tree");
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
var
|
|
51
|
-
"attr",
|
|
52
|
-
"calc",
|
|
53
|
-
"color-mix",
|
|
54
|
-
"conic-gradient",
|
|
55
|
-
"counter",
|
|
56
|
-
"cubic-bezier",
|
|
57
|
-
"linear-gradient",
|
|
58
|
-
"max",
|
|
59
|
-
"min",
|
|
60
|
-
"radial-gradient",
|
|
61
|
-
"repeating-conic-gradient",
|
|
62
|
-
"repeating-linear-gradient",
|
|
63
|
-
"repeating-radial-gradient",
|
|
64
|
-
"var"
|
|
65
|
-
];
|
|
66
|
-
var CSS_MATH_FUNCTIONS = ["calc", "min", "max"];
|
|
67
|
-
var RGB_COLOR_FUNCTIONS = ["rgb", "rgba", "hsl", "hsla"];
|
|
68
|
-
var cssFunctionsRegex = new RegExp(`(?:${CSS_FUNCTIONS.join("|")})`);
|
|
69
|
-
var cssFunctionsExactRegex = new RegExp(`^(?:${CSS_FUNCTIONS.join("|")})$`);
|
|
70
|
-
var cssMathFunctionsRegex = new RegExp(`^(?:${CSS_MATH_FUNCTIONS.join("|")})$`);
|
|
71
|
-
function isCssFunction(value) {
|
|
72
|
-
return cssFunctionsExactRegex.test(value);
|
|
73
|
-
}
|
|
74
|
-
function isCssColorFunction(value) {
|
|
75
|
-
return RGB_COLOR_FUNCTIONS.includes(value);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// src/utils/color-lib-utils.ts
|
|
79
|
-
var isValidColor = (val) => import_chroma_js.default.valid(val);
|
|
80
|
-
var extractColorValue = (node) => {
|
|
81
|
-
let colorValue = null;
|
|
82
|
-
switch (node.type) {
|
|
83
|
-
case "Hash":
|
|
84
|
-
colorValue = `#${node.value}`;
|
|
85
|
-
break;
|
|
86
|
-
case "Identifier":
|
|
87
|
-
colorValue = node.name;
|
|
88
|
-
break;
|
|
89
|
-
case "Function":
|
|
90
|
-
if (isCssColorFunction(node.name)) {
|
|
91
|
-
colorValue = (0, import_css_tree.generate)(node);
|
|
92
|
-
}
|
|
93
|
-
break;
|
|
94
|
-
}
|
|
95
|
-
return colorValue && isValidColor(colorValue) ? colorValue : null;
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
// src/utils/hardcoded-shared-utils.ts
|
|
31
|
+
var import_value_utils = require("./value-utils");
|
|
32
|
+
var import_color_lib_utils = require("./color-lib-utils");
|
|
33
|
+
var import_css_functions = require("./css-functions");
|
|
99
34
|
var FONT_WEIGHTS = [
|
|
100
35
|
"normal",
|
|
101
36
|
"bold",
|
|
@@ -116,13 +51,33 @@ function isKnownFontWeight(value) {
|
|
|
116
51
|
return FONT_WEIGHTS.includes(stringValue.toLowerCase());
|
|
117
52
|
}
|
|
118
53
|
function handleShorthandAutoFix(declarationNode, context, valueText, replacements) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
sortedReplacements.
|
|
54
|
+
if (!replacements || replacements.length === 0) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const sortedReplacements = replacements.sort((a, b) => b.start - a.start);
|
|
58
|
+
const reportNumericValue = context.options?.reportNumericValue || "always";
|
|
59
|
+
const fixCallback = (start, originalValue, replacement) => {
|
|
60
|
+
let newValue = valueText;
|
|
61
|
+
newValue = newValue.substring(0, start) + replacement + newValue.substring(start + originalValue.length);
|
|
62
|
+
if (newValue !== valueText) {
|
|
63
|
+
return (fixer) => {
|
|
64
|
+
return fixer.replaceText(declarationNode.value, newValue);
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
sortedReplacements.forEach(({ start, end, replacement, displayValue, hasHook, isNumeric }) => {
|
|
123
69
|
const originalValue = valueText.substring(start, end);
|
|
124
|
-
|
|
125
|
-
|
|
70
|
+
if (isNumeric) {
|
|
71
|
+
if (reportNumericValue === "never") {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (reportNumericValue === "hasReplacement" && !hasHook) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const valueColumnStart = declarationNode.value.loc.start.column + start;
|
|
79
|
+
const valueColumnEnd = valueColumnStart + originalValue.length;
|
|
80
|
+
const canAutoFix = originalValue !== replacement;
|
|
126
81
|
const { loc: { start: locStart, end: locEnd } } = declarationNode.value;
|
|
127
82
|
const reportNode = {
|
|
128
83
|
...declarationNode.value,
|
|
@@ -130,23 +85,16 @@ function handleShorthandAutoFix(declarationNode, context, valueText, replacement
|
|
|
130
85
|
...declarationNode.value.loc,
|
|
131
86
|
start: {
|
|
132
87
|
...locStart,
|
|
133
|
-
column:
|
|
88
|
+
column: valueColumnStart
|
|
134
89
|
},
|
|
135
90
|
end: {
|
|
136
91
|
...locEnd,
|
|
137
|
-
column:
|
|
92
|
+
column: valueColumnEnd
|
|
138
93
|
}
|
|
139
94
|
}
|
|
140
95
|
};
|
|
141
96
|
if (hasHook) {
|
|
142
|
-
const fix = canAutoFix ? (
|
|
143
|
-
let newValue = valueText;
|
|
144
|
-
for (let i = sortedReplacements.length - 1; i >= 0; i--) {
|
|
145
|
-
const { start: rStart, end: rEnd, replacement: rReplacement } = sortedReplacements[i];
|
|
146
|
-
newValue = newValue.substring(0, rStart) + rReplacement + newValue.substring(rEnd);
|
|
147
|
-
}
|
|
148
|
-
return fixer.replaceText(declarationNode.value, newValue);
|
|
149
|
-
} : void 0;
|
|
97
|
+
const fix = canAutoFix ? fixCallback(start, originalValue, replacement) : void 0;
|
|
150
98
|
context.context.report({
|
|
151
99
|
node: reportNode,
|
|
152
100
|
messageId: "hardcodedValue",
|
|
@@ -172,8 +120,8 @@ function forEachValue(valueText, extractValue, shouldSkipNode, callback) {
|
|
|
172
120
|
return;
|
|
173
121
|
}
|
|
174
122
|
try {
|
|
175
|
-
const ast = (0,
|
|
176
|
-
(0,
|
|
123
|
+
const ast = (0, import_css_tree.parse)(valueText, { context: "value", positions: true });
|
|
124
|
+
(0, import_css_tree.walk)(ast, {
|
|
177
125
|
enter(node) {
|
|
178
126
|
if (shouldSkipNode(node)) {
|
|
179
127
|
return this.skip;
|
|
@@ -193,7 +141,7 @@ function forEachValue(valueText, extractValue, shouldSkipNode, callback) {
|
|
|
193
141
|
}
|
|
194
142
|
}
|
|
195
143
|
function shouldSkipColorNode(node) {
|
|
196
|
-
return node.type === "Function" && isCssFunction(node.name);
|
|
144
|
+
return node.type === "Function" && (0, import_css_functions.isCssFunction)(node.name);
|
|
197
145
|
}
|
|
198
146
|
function shouldSkipDimensionNode(node) {
|
|
199
147
|
return node.type === "Function";
|
|
@@ -205,23 +153,23 @@ function extractDimensionValue(valueNode, cssProperty) {
|
|
|
205
153
|
const numValue = Number(valueNode.value);
|
|
206
154
|
if (numValue === 0) return null;
|
|
207
155
|
const unit = valueNode.unit.toLowerCase();
|
|
208
|
-
if (!ALLOWED_UNITS.includes(unit)) return null;
|
|
156
|
+
if (!import_value_utils.ALLOWED_UNITS.includes(unit)) return null;
|
|
209
157
|
return {
|
|
210
|
-
|
|
158
|
+
value: numValue,
|
|
211
159
|
unit
|
|
212
160
|
};
|
|
213
161
|
case "Number":
|
|
214
162
|
const numberValue = Number(valueNode.value);
|
|
215
163
|
if (numberValue === 0) return null;
|
|
216
164
|
return {
|
|
217
|
-
|
|
165
|
+
value: numberValue,
|
|
218
166
|
unit: null
|
|
219
167
|
};
|
|
220
168
|
case "Percentage":
|
|
221
169
|
const percentValue = Number(valueNode.value);
|
|
222
170
|
if (percentValue === 0) return null;
|
|
223
171
|
return {
|
|
224
|
-
|
|
172
|
+
value: percentValue,
|
|
225
173
|
unit: "%"
|
|
226
174
|
};
|
|
227
175
|
case "Value":
|
|
@@ -230,7 +178,7 @@ function extractDimensionValue(valueNode, cssProperty) {
|
|
|
230
178
|
return null;
|
|
231
179
|
}
|
|
232
180
|
function forEachColorValue(valueText, callback) {
|
|
233
|
-
forEachValue(valueText, extractColorValue, shouldSkipColorNode, callback);
|
|
181
|
+
forEachValue(valueText, import_color_lib_utils.extractColorValue, shouldSkipColorNode, callback);
|
|
234
182
|
}
|
|
235
183
|
function forEachDensityValue(valueText, cssProperty, callback) {
|
|
236
184
|
forEachValue(
|
|
@@ -247,9 +195,9 @@ function extractFontValue(node) {
|
|
|
247
195
|
const numValue = Number(node.value);
|
|
248
196
|
if (numValue <= 0) return null;
|
|
249
197
|
const unit = node.unit.toLowerCase();
|
|
250
|
-
if (!ALLOWED_UNITS.includes(unit)) return null;
|
|
198
|
+
if (!import_value_utils.ALLOWED_UNITS.includes(unit)) return null;
|
|
251
199
|
return {
|
|
252
|
-
|
|
200
|
+
value: numValue,
|
|
253
201
|
unit
|
|
254
202
|
};
|
|
255
203
|
case "Number":
|
|
@@ -261,7 +209,7 @@ function extractFontValue(node) {
|
|
|
261
209
|
return null;
|
|
262
210
|
}
|
|
263
211
|
return {
|
|
264
|
-
|
|
212
|
+
value: numberValue,
|
|
265
213
|
unit: null
|
|
266
214
|
};
|
|
267
215
|
case "Identifier":
|
|
@@ -270,14 +218,14 @@ function extractFontValue(node) {
|
|
|
270
218
|
return null;
|
|
271
219
|
}
|
|
272
220
|
if (namedValue === "normal") {
|
|
273
|
-
return {
|
|
221
|
+
return { value: 400, unit: null };
|
|
274
222
|
}
|
|
275
|
-
return {
|
|
223
|
+
return { value: namedValue, unit: null };
|
|
276
224
|
case "Percentage":
|
|
277
225
|
const percentValue = Number(node.value);
|
|
278
226
|
if (percentValue === 0) return null;
|
|
279
227
|
return {
|
|
280
|
-
|
|
228
|
+
value: percentValue,
|
|
281
229
|
unit: "%"
|
|
282
230
|
};
|
|
283
231
|
case "Value":
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../src/utils/hardcoded-shared-utils.ts"
|
|
4
|
-
"sourcesContent": ["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", "// Simplified value parsing\n\n/**\n * Checks if a value is a CSS global value.\n *\n * CSS global values are special keywords that can be used for any CSS property and have a universal meaning:\n * - initial: Resets the property to its initial value as defined by the CSS specification.\n * - inherit: Inherits the value from the parent element.\n * - unset: Acts as inherit if the property is inheritable, otherwise acts as initial.\n * - revert: Rolls back the property to the value established by the user-agent or user styles.\n * - revert-layer: Rolls back the property to the value established by the previous cascade layer.\n *\n * All CSS properties accept these global values, including but not limited to:\n * - color\n * - background\n * - font-size\n * - margin\n * - padding\n * - border\n * - display\n * - position\n * - z-index\n * - and many more\n *\n * These values are part of the CSS standard and are not considered violations, even if a rule would otherwise flag a value as invalid or non-design-token. They are always allowed for any property.\n *\n * @param value The CSS value to check.\n * @returns True if the value is a CSS global value, false otherwise.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/initial\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/inherit\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/unset\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/revert\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/revert-layer\n */\nexport function isGlobalValue(value: string): boolean {\n return value === 'initial' || value === 'inherit' || value === 'unset' || value === 'revert' || value === 'revert-layer';\n }\n\n// Configurable list of allowed CSS units\nexport const ALLOWED_UNITS = ['px', 'em', 'rem', '%', 'ch'];\n\nexport type ParsedUnitValue = {\n unit: 'px' | 'rem' | '%' | 'em' | 'ch' | null;\n number: number;\n} | null;\n\nexport function parseUnitValue(value: string): ParsedUnitValue {\n if (!value) return null;\n \n // Create regex pattern from allowed units\n const unitsPattern = ALLOWED_UNITS.join('|');\n const regex = new RegExp(`^(-?\\\\d*\\\\.?\\\\d+)(${unitsPattern})?$`);\n const match = value.match(regex);\n if (!match) return null;\n \n const number = parseFloat(match[1]);\n const unit = match[2] ? (match[2] as 'px' | 'rem' | '%' | 'em' | 'ch') : null; // Keep unitless values as null\n \n if (isNaN(number)) return null;\n \n return { number, unit };\n}\n\nexport function toAlternateUnitValue(numberVal: number, unitType: 'px' | 'rem' | '%' | 'em' | 'ch' | null): ParsedUnitValue {\n if (unitType === 'px') {\n let floatValue = parseFloat(`${numberVal / 16}`);\n if (!isNaN(floatValue)) {\n return {\n unit: 'rem',\n number: parseFloat(floatValue.toFixed(4))\n }\n }\n } else if (unitType === 'rem') {\n const intValue = parseInt(`${numberVal * 16}`);\n if (!isNaN(intValue)) {\n return {\n unit: 'px',\n number: intValue\n }\n }\n }\n // For other units (%, em, ch) and unitless values, no alternate unit conversion available\n // These units are context-dependent and don't have standard conversion ratios\n return null;\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 }"],
|
|
5
|
-
"mappings": "
|
|
6
|
-
"names": [
|
|
3
|
+
"sources": ["../../src/utils/hardcoded-shared-utils.ts"],
|
|
4
|
+
"sourcesContent": ["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 value: 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 value: 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 value: 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 value: 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 value: 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 { value: 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 { value: 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 value: 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"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
7
|
}
|
|
@@ -20,4 +20,6 @@ export declare const densificationProperties: string[];
|
|
|
20
20
|
* Handles wildcards (*) and creates proper ESLint CSS selector syntax
|
|
21
21
|
*/
|
|
22
22
|
export declare function toSelector(properties: string[]): string;
|
|
23
|
-
export declare function
|
|
23
|
+
export declare function resolveDensityPropertyToMatch(cssProperty: string): string;
|
|
24
|
+
export declare function resolveColorPropertyToMatch(cssProperty: string): string;
|
|
25
|
+
export declare function isOutlineWidthProperty(propertyToMatch: string): propertyToMatch is "outline" | "outline-width";
|
|
@@ -28,9 +28,11 @@ __export(property_matcher_exports, {
|
|
|
28
28
|
isDimensionProperty: () => isDimensionProperty,
|
|
29
29
|
isInsetProperty: () => isInsetProperty,
|
|
30
30
|
isMarginProperty: () => isMarginProperty,
|
|
31
|
+
isOutlineWidthProperty: () => isOutlineWidthProperty,
|
|
31
32
|
isPaddingProperty: () => isPaddingProperty,
|
|
32
33
|
matchesCssProperty: () => matchesCssProperty,
|
|
33
|
-
|
|
34
|
+
resolveColorPropertyToMatch: () => resolveColorPropertyToMatch,
|
|
35
|
+
resolveDensityPropertyToMatch: () => resolveDensityPropertyToMatch,
|
|
34
36
|
toSelector: () => toSelector
|
|
35
37
|
});
|
|
36
38
|
module.exports = __toCommonJS(property_matcher_exports);
|
|
@@ -52,10 +54,10 @@ var PADDING_REGEX = new RegExp(`^padding(?:-${DIRECTION_VALUES})?$`);
|
|
|
52
54
|
var BORDER_RADIUS_REGEX = new RegExp(`^border(?:-${CORNER_VALUES})?-radius$`);
|
|
53
55
|
var INSET_REGEX = new RegExp(`^inset(?:-${INSET_VALUES})?$`);
|
|
54
56
|
function isBorderColorProperty(cssProperty) {
|
|
55
|
-
return BORDER_COLOR_REGEX.test(cssProperty);
|
|
57
|
+
return cssProperty === "border" || BORDER_COLOR_REGEX.test(cssProperty);
|
|
56
58
|
}
|
|
57
59
|
function isBorderWidthProperty(cssProperty) {
|
|
58
|
-
return BORDER_WIDTH_REGEX.test(cssProperty);
|
|
60
|
+
return cssProperty === "border" || BORDER_WIDTH_REGEX.test(cssProperty);
|
|
59
61
|
}
|
|
60
62
|
function isMarginProperty(cssProperty) {
|
|
61
63
|
return MARGIN_REGEX.test(cssProperty);
|
|
@@ -119,9 +121,9 @@ function toSelector(properties) {
|
|
|
119
121
|
});
|
|
120
122
|
return selectorParts.join(", ");
|
|
121
123
|
}
|
|
122
|
-
function
|
|
124
|
+
function resolveDensityPropertyToMatch(cssProperty) {
|
|
123
125
|
const propertyToMatch = cssProperty.toLowerCase();
|
|
124
|
-
if (propertyToMatch
|
|
126
|
+
if (isOutlineWidthProperty(propertyToMatch) || isBorderWidthProperty(propertyToMatch)) {
|
|
125
127
|
return "border-width";
|
|
126
128
|
} else if (isMarginProperty(propertyToMatch)) {
|
|
127
129
|
return "margin";
|
|
@@ -133,13 +135,23 @@ function resolvePropertyToMatch(cssProperty) {
|
|
|
133
135
|
return "width";
|
|
134
136
|
} else if (isInsetProperty(propertyToMatch)) {
|
|
135
137
|
return "top";
|
|
136
|
-
}
|
|
138
|
+
}
|
|
139
|
+
return propertyToMatch;
|
|
140
|
+
}
|
|
141
|
+
function resolveColorPropertyToMatch(cssProperty) {
|
|
142
|
+
const propertyToMatch = cssProperty.toLowerCase();
|
|
143
|
+
if (propertyToMatch === "outline" || propertyToMatch === "outline-color") {
|
|
144
|
+
return "border-color";
|
|
145
|
+
} else if (propertyToMatch === "background" || propertyToMatch === "background-color") {
|
|
137
146
|
return "background-color";
|
|
138
|
-
} else if (
|
|
147
|
+
} else if (isBorderColorProperty(propertyToMatch)) {
|
|
139
148
|
return "border-color";
|
|
140
149
|
}
|
|
141
150
|
return propertyToMatch;
|
|
142
151
|
}
|
|
152
|
+
function isOutlineWidthProperty(propertyToMatch) {
|
|
153
|
+
return propertyToMatch === "outline" || propertyToMatch === "outline-width";
|
|
154
|
+
}
|
|
143
155
|
// Annotate the CommonJS export names for ESM import in node:
|
|
144
156
|
0 && (module.exports = {
|
|
145
157
|
colorProperties,
|
|
@@ -151,9 +163,11 @@ function resolvePropertyToMatch(cssProperty) {
|
|
|
151
163
|
isDimensionProperty,
|
|
152
164
|
isInsetProperty,
|
|
153
165
|
isMarginProperty,
|
|
166
|
+
isOutlineWidthProperty,
|
|
154
167
|
isPaddingProperty,
|
|
155
168
|
matchesCssProperty,
|
|
156
|
-
|
|
169
|
+
resolveColorPropertyToMatch,
|
|
170
|
+
resolveDensityPropertyToMatch,
|
|
157
171
|
toSelector
|
|
158
172
|
});
|
|
159
173
|
//# sourceMappingURL=property-matcher.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/utils/property-matcher.ts"],
|
|
4
|
-
"sourcesContent": ["///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
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOO,SAAS,mBACd,gBACA,aACS;AACT,SAAO,eAAe,KAAK,CAAC,oBAA4B;AACtD,UAAM,eAAe,IAAI;AAAA,MACvB,MAAM,gBAAgB,QAAQ,OAAO,IAAI,IAAI;AAAA,IAC/C;AACA,WAAO,aAAa,KAAK,WAAW;AAAA,EACtC,CAAC;AACH;AAGA,IAAM,mBAAmB;AACzB,IAAM,gBAAgB;AACtB,IAAM,eAAe;AAIrB,IAAM,qBAAqB,IAAI,OAAO,cAAc,gBAAgB,WAAW;AAC/E,IAAM,qBAAqB,IAAI,OAAO,cAAc,gBAAgB,WAAW;AAC/E,IAAM,eAAe,IAAI,OAAO,cAAc,gBAAgB,KAAK;AACnE,IAAM,gBAAgB,IAAI,OAAO,eAAe,gBAAgB,KAAK;AACrE,IAAM,sBAAsB,IAAI,OAAO,cAAc,aAAa,YAAY;AAC9E,IAAM,cAAc,IAAI,OAAO,aAAa,YAAY,KAAK;AAEtD,SAAS,sBAAsB,aAA8B;AAClE,SAAO,mBAAmB,KAAK,WAAW;
|
|
4
|
+
"sourcesContent": ["///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 cssProperty === 'border' || BORDER_COLOR_REGEX.test(cssProperty);\n}\n\nexport function isBorderWidthProperty(cssProperty: string): boolean {\n return cssProperty === 'border' || 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}"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -22,54 +22,31 @@ __export(styling_hook_utils_exports, {
|
|
|
22
22
|
getStylingHooksForDensityValue: () => getStylingHooksForDensityValue
|
|
23
23
|
});
|
|
24
24
|
module.exports = __toCommonJS(styling_hook_utils_exports);
|
|
25
|
-
|
|
26
|
-
// src/utils/value-utils.ts
|
|
27
|
-
var ALLOWED_UNITS = ["px", "em", "rem", "%", "ch"];
|
|
28
|
-
function parseUnitValue(value) {
|
|
29
|
-
if (!value) return null;
|
|
30
|
-
const unitsPattern = ALLOWED_UNITS.join("|");
|
|
31
|
-
const regex = new RegExp(`^(-?\\d*\\.?\\d+)(${unitsPattern})?$`);
|
|
32
|
-
const match = value.match(regex);
|
|
33
|
-
if (!match) return null;
|
|
34
|
-
const number = parseFloat(match[1]);
|
|
35
|
-
const unit = match[2] ? match[2] : null;
|
|
36
|
-
if (isNaN(number)) return null;
|
|
37
|
-
return { number, unit };
|
|
38
|
-
}
|
|
39
|
-
function toAlternateUnitValue(numberVal, unitType) {
|
|
40
|
-
if (unitType === "px") {
|
|
41
|
-
let floatValue = parseFloat(`${numberVal / 16}`);
|
|
42
|
-
if (!isNaN(floatValue)) {
|
|
43
|
-
return {
|
|
44
|
-
unit: "rem",
|
|
45
|
-
number: parseFloat(floatValue.toFixed(4))
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
} else if (unitType === "rem") {
|
|
49
|
-
const intValue = parseInt(`${numberVal * 16}`);
|
|
50
|
-
if (!isNaN(intValue)) {
|
|
51
|
-
return {
|
|
52
|
-
unit: "px",
|
|
53
|
-
number: intValue
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// src/utils/styling-hook-utils.ts
|
|
25
|
+
var import_value_utils = require("./value-utils");
|
|
61
26
|
function isValueMatch(valueToMatch, sldsValue) {
|
|
62
27
|
if (!valueToMatch || !sldsValue) {
|
|
63
28
|
return false;
|
|
64
29
|
}
|
|
65
|
-
return valueToMatch.unit == sldsValue.unit && valueToMatch.
|
|
30
|
+
return valueToMatch.unit == sldsValue.unit && valueToMatch.value === sldsValue.value;
|
|
31
|
+
}
|
|
32
|
+
function isKeywordValue(parsedValue) {
|
|
33
|
+
return parsedValue !== null && typeof parsedValue.value === "string";
|
|
66
34
|
}
|
|
67
35
|
function getStylingHooksForDensityValue(parsedValue, supportedStylinghooks, cssProperty) {
|
|
68
36
|
if (!parsedValue) return [];
|
|
69
|
-
const alternateValue = toAlternateUnitValue(parsedValue.number, parsedValue.unit);
|
|
70
37
|
const matchedHooks = [];
|
|
38
|
+
if (isKeywordValue(parsedValue)) {
|
|
39
|
+
const keywordLower = parsedValue.value.toLowerCase();
|
|
40
|
+
for (const [sldsValue, hooks] of Object.entries(supportedStylinghooks)) {
|
|
41
|
+
if (sldsValue.toLowerCase() === keywordLower) {
|
|
42
|
+
hooks.filter((hook) => hook.properties.includes(cssProperty)).forEach((hook) => matchedHooks.push(hook.name));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return matchedHooks;
|
|
46
|
+
}
|
|
47
|
+
const alternateValue = typeof parsedValue.value === "number" ? (0, import_value_utils.toAlternateUnitValue)(parsedValue.value, parsedValue.unit) : null;
|
|
71
48
|
for (const [sldsValue, hooks] of Object.entries(supportedStylinghooks)) {
|
|
72
|
-
const parsedSldsValue = parseUnitValue(sldsValue);
|
|
49
|
+
const parsedSldsValue = (0, import_value_utils.parseUnitValue)(sldsValue);
|
|
73
50
|
if (isValueMatch(parsedValue, parsedSldsValue) || alternateValue && isValueMatch(alternateValue, parsedSldsValue)) {
|
|
74
51
|
hooks.filter((hook) => hook.properties.includes(cssProperty)).forEach((hook) => matchedHooks.push(hook.name));
|
|
75
52
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../src/utils/styling-hook-utils.ts"
|
|
4
|
-
"sourcesContent": ["import type { ValueToStylingHookEntry, ValueToStylingHooksMapping } from '@salesforce-ux/sds-metadata';\nimport { ParsedUnitValue, parseUnitValue, toAlternateUnitValue } from './value-utils';\n\nfunction isValueMatch(valueToMatch: ParsedUnitValue, sldsValue: ParsedUnitValue): boolean {\n if (!valueToMatch || !sldsValue) {\n return false;\n }\n return valueToMatch.unit == sldsValue.unit && valueToMatch.
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA
|
|
3
|
+
"sources": ["../../src/utils/styling-hook-utils.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ValueToStylingHookEntry, ValueToStylingHooksMapping } from '@salesforce-ux/sds-metadata/next';\nimport { ParsedUnitValue, parseUnitValue, toAlternateUnitValue } from './value-utils';\n\nfunction isValueMatch(valueToMatch: ParsedUnitValue, sldsValue: ParsedUnitValue): boolean {\n if (!valueToMatch || !sldsValue) {\n return false;\n }\n return valueToMatch.unit == sldsValue.unit && valueToMatch.value === sldsValue.value;\n}\n\n/**\n * Check if a parsed value is a keyword (string) rather than a numeric value\n */\nfunction isKeywordValue(parsedValue: ParsedUnitValue): parsedValue is { value: string; unit: null } {\n return parsedValue !== null && typeof parsedValue.value === 'string';\n}\n\n/**\n * Get styling hooks for a density value using structured data from CSS AST\n * Eliminates regex parsing by accepting pre-parsed dimension data\n */\nexport function getStylingHooksForDensityValue(\n parsedValue: ParsedUnitValue,\n supportedStylinghooks: ValueToStylingHooksMapping,\n cssProperty: string\n): string[] {\n if (!parsedValue) return [];\n \n const matchedHooks: string[] = [];\n\n // Handle keyword values (e.g., 'bold', 'normal') with direct string match\n if (isKeywordValue(parsedValue)) {\n const keywordLower = parsedValue.value.toLowerCase();\n for (const [sldsValue, hooks] of Object.entries(supportedStylinghooks)) {\n if (sldsValue.toLowerCase() === keywordLower) {\n hooks\n .filter((hook: ValueToStylingHookEntry) => hook.properties.includes(cssProperty))\n .forEach((hook) => matchedHooks.push(hook.name));\n }\n }\n return matchedHooks;\n }\n\n // Handle numeric values with unit conversion\n const alternateValue = typeof parsedValue.value === 'number' \n ? toAlternateUnitValue(parsedValue.value, parsedValue.unit)\n : null;\n\n for (const [sldsValue, hooks] of Object.entries(supportedStylinghooks)) {\n const parsedSldsValue = parseUnitValue(sldsValue);\n if (isValueMatch(parsedValue, parsedSldsValue) || (alternateValue && isValueMatch(alternateValue, parsedSldsValue))) {\n hooks\n .filter((hook: ValueToStylingHookEntry) => hook.properties.includes(cssProperty))\n .forEach((hook) => matchedHooks.push(hook.name));\n }\n }\n return matchedHooks;\n}"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,yBAAsE;AAEtE,SAAS,aAAa,cAA+B,WAAqC;AACxF,MAAI,CAAC,gBAAgB,CAAC,WAAW;AAC/B,WAAO;AAAA,EACT;AACA,SAAO,aAAa,QAAQ,UAAU,QAAQ,aAAa,UAAU,UAAU;AACjF;AAKA,SAAS,eAAe,aAA4E;AAClG,SAAO,gBAAgB,QAAQ,OAAO,YAAY,UAAU;AAC9D;AAMO,SAAS,+BACd,aACA,uBACA,aACU;AACV,MAAI,CAAC,YAAa,QAAO,CAAC;AAE1B,QAAM,eAAyB,CAAC;AAGhC,MAAI,eAAe,WAAW,GAAG;AAC/B,UAAM,eAAe,YAAY,MAAM,YAAY;AACnD,eAAW,CAAC,WAAW,KAAK,KAAK,OAAO,QAAQ,qBAAqB,GAAG;AACtE,UAAI,UAAU,YAAY,MAAM,cAAc;AAC5C,cACG,OAAO,CAAC,SAAkC,KAAK,WAAW,SAAS,WAAW,CAAC,EAC/E,QAAQ,CAAC,SAAS,aAAa,KAAK,KAAK,IAAI,CAAC;AAAA,MACnD;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAGA,QAAM,iBAAiB,OAAO,YAAY,UAAU,eAChD,yCAAqB,YAAY,OAAO,YAAY,IAAI,IACxD;AAEJ,aAAW,CAAC,WAAW,KAAK,KAAK,OAAO,QAAQ,qBAAqB,GAAG;AACtE,UAAM,sBAAkB,mCAAe,SAAS;AAChD,QAAI,aAAa,aAAa,eAAe,KAAM,kBAAkB,aAAa,gBAAgB,eAAe,GAAI;AACnH,YACG,OAAO,CAAC,SAAkC,KAAK,WAAW,SAAS,WAAW,CAAC,EAC/E,QAAQ,CAAC,SAAS,aAAa,KAAK,KAAK,IAAI,CAAC;AAAA,IACnD;AAAA,EACF;AACA,SAAO;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -35,7 +35,7 @@ export declare function isGlobalValue(value: string): boolean;
|
|
|
35
35
|
export declare const ALLOWED_UNITS: string[];
|
|
36
36
|
export type ParsedUnitValue = {
|
|
37
37
|
unit: 'px' | 'rem' | '%' | 'em' | 'ch' | null;
|
|
38
|
-
|
|
38
|
+
value: number | string;
|
|
39
39
|
} | null;
|
|
40
|
-
export declare function parseUnitValue(
|
|
40
|
+
export declare function parseUnitValue(valueStr: string): ParsedUnitValue;
|
|
41
41
|
export declare function toAlternateUnitValue(numberVal: number, unitType: 'px' | 'rem' | '%' | 'em' | 'ch' | null): ParsedUnitValue;
|