@tenphi/eslint-plugin-tasty 0.3.1 → 0.4.0
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/dist/config.js +100 -20
- package/dist/config.js.map +1 -1
- package/dist/configs.js +5 -4
- package/dist/configs.js.map +1 -1
- package/dist/constants.js +55 -1
- package/dist/constants.js.map +1 -1
- package/dist/context.js +37 -6
- package/dist/context.js.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/parsers/state-key-parser.js +486 -0
- package/dist/parsers/state-key-parser.js.map +1 -0
- package/dist/parsers/utils.js +128 -0
- package/dist/parsers/utils.js.map +1 -0
- package/dist/parsers/value-parser.js +613 -0
- package/dist/parsers/value-parser.js.map +1 -0
- package/dist/rules/consistent-token-usage.js +20 -19
- package/dist/rules/consistent-token-usage.js.map +1 -1
- package/dist/rules/known-property.js +31 -30
- package/dist/rules/known-property.js.map +1 -1
- package/dist/rules/no-duplicate-state.js +12 -11
- package/dist/rules/no-duplicate-state.js.map +1 -1
- package/dist/rules/no-important.js +12 -11
- package/dist/rules/no-important.js.map +1 -1
- package/dist/rules/no-nested-selector.js +15 -14
- package/dist/rules/no-nested-selector.js.map +1 -1
- package/dist/rules/no-nested-state-map.js +19 -18
- package/dist/rules/no-nested-state-map.js.map +1 -1
- package/dist/rules/no-raw-color-values.js +15 -14
- package/dist/rules/no-raw-color-values.js.map +1 -1
- package/dist/rules/no-runtime-styles-mutation.js +6 -5
- package/dist/rules/no-runtime-styles-mutation.js.map +1 -1
- package/dist/rules/no-unknown-state-alias.js +12 -11
- package/dist/rules/no-unknown-state-alias.js.map +1 -1
- package/dist/rules/prefer-shorthand-property.js +19 -18
- package/dist/rules/prefer-shorthand-property.js.map +1 -1
- package/dist/rules/require-default-state.js +22 -21
- package/dist/rules/require-default-state.js.map +1 -1
- package/dist/rules/static-no-dynamic-values.js +7 -6
- package/dist/rules/static-no-dynamic-values.js.map +1 -1
- package/dist/rules/static-valid-selector.js +1 -1
- package/dist/rules/valid-boolean-property.js +19 -18
- package/dist/rules/valid-boolean-property.js.map +1 -1
- package/dist/rules/valid-color-token.js +33 -21
- package/dist/rules/valid-color-token.js.map +1 -1
- package/dist/rules/valid-custom-property.js +31 -19
- package/dist/rules/valid-custom-property.js.map +1 -1
- package/dist/rules/valid-custom-unit.js +12 -16
- package/dist/rules/valid-custom-unit.js.map +1 -1
- package/dist/rules/valid-directional-modifier.js +21 -20
- package/dist/rules/valid-directional-modifier.js.map +1 -1
- package/dist/rules/valid-preset.js +5 -2
- package/dist/rules/valid-preset.js.map +1 -1
- package/dist/rules/valid-radius-shape.js +19 -18
- package/dist/rules/valid-radius-shape.js.map +1 -1
- package/dist/rules/valid-recipe.js +5 -2
- package/dist/rules/valid-recipe.js.map +1 -1
- package/dist/rules/valid-state-definition.js +70 -0
- package/dist/rules/valid-state-definition.js.map +1 -0
- package/dist/rules/valid-state-key.js +39 -102
- package/dist/rules/valid-state-key.js.map +1 -1
- package/dist/rules/valid-styles-structure.js +39 -38
- package/dist/rules/valid-styles-structure.js.map +1 -1
- package/dist/rules/valid-sub-element.js +21 -20
- package/dist/rules/valid-sub-element.js.map +1 -1
- package/dist/rules/valid-transition.js +19 -18
- package/dist/rules/valid-transition.js.map +1 -1
- package/dist/rules/valid-value.js +115 -64
- package/dist/rules/valid-value.js.map +1 -1
- package/package.json +1 -8
- package/dist/parser.js +0 -46
- package/dist/parser.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createRule } from "../create-rule.js";
|
|
2
|
-
import { TastyContext } from "../context.js";
|
|
3
2
|
import { getKeyName } from "../utils.js";
|
|
3
|
+
import { TastyContext, styleObjectListeners } from "../context.js";
|
|
4
4
|
|
|
5
5
|
//#region src/rules/valid-sub-element.ts
|
|
6
6
|
var valid_sub_element_default = createRule({
|
|
@@ -14,29 +14,30 @@ var valid_sub_element_default = createRule({
|
|
|
14
14
|
defaultOptions: [],
|
|
15
15
|
create(context) {
|
|
16
16
|
const ctx = new TastyContext(context);
|
|
17
|
+
function handleStyleObject(node) {
|
|
18
|
+
if (!ctx.isStyleObject(node)) return;
|
|
19
|
+
for (const prop of node.properties) {
|
|
20
|
+
if (prop.type !== "Property" || prop.computed) continue;
|
|
21
|
+
const key = getKeyName(prop.key);
|
|
22
|
+
if (key === null || !/^[A-Z]/.test(key)) continue;
|
|
23
|
+
if (prop.value.type !== "ObjectExpression") {
|
|
24
|
+
const valueType = prop.value.type === "Literal" ? typeof prop.value.value : prop.value.type;
|
|
25
|
+
context.report({
|
|
26
|
+
node: prop.value,
|
|
27
|
+
messageId: "subElementNotObject",
|
|
28
|
+
data: {
|
|
29
|
+
name: key,
|
|
30
|
+
type: valueType
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
17
36
|
return {
|
|
18
37
|
ImportDeclaration(node) {
|
|
19
38
|
ctx.trackImport(node);
|
|
20
39
|
},
|
|
21
|
-
|
|
22
|
-
if (!ctx.isStyleObject(node)) return;
|
|
23
|
-
for (const prop of node.properties) {
|
|
24
|
-
if (prop.type !== "Property" || prop.computed) continue;
|
|
25
|
-
const key = getKeyName(prop.key);
|
|
26
|
-
if (key === null || !/^[A-Z]/.test(key)) continue;
|
|
27
|
-
if (prop.value.type !== "ObjectExpression") {
|
|
28
|
-
const valueType = prop.value.type === "Literal" ? typeof prop.value.value : prop.value.type;
|
|
29
|
-
context.report({
|
|
30
|
-
node: prop.value,
|
|
31
|
-
messageId: "subElementNotObject",
|
|
32
|
-
data: {
|
|
33
|
-
name: key,
|
|
34
|
-
type: valueType
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
+
...styleObjectListeners(handleStyleObject)
|
|
40
41
|
};
|
|
41
42
|
}
|
|
42
43
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"valid-sub-element.js","names":[],"sources":["../../src/rules/valid-sub-element.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport { createRule } from '../create-rule.js';\nimport { TastyContext } from '../context.js';\nimport { getKeyName } from '../utils.js';\n\ntype MessageIds = 'subElementNotObject';\n\nexport default createRule<[], MessageIds>({\n name: 'valid-sub-element',\n meta: {\n type: 'problem',\n docs: {\n description: 'Validate sub-element key format in style objects',\n },\n messages: {\n subElementNotObject:\n \"Sub-element '{{name}}' value must be a style object, not a {{type}}.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n\n
|
|
1
|
+
{"version":3,"file":"valid-sub-element.js","names":[],"sources":["../../src/rules/valid-sub-element.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport { createRule } from '../create-rule.js';\nimport { TastyContext, styleObjectListeners } from '../context.js';\nimport { getKeyName } from '../utils.js';\n\ntype MessageIds = 'subElementNotObject';\n\nexport default createRule<[], MessageIds>({\n name: 'valid-sub-element',\n meta: {\n type: 'problem',\n docs: {\n description: 'Validate sub-element key format in style objects',\n },\n messages: {\n subElementNotObject:\n \"Sub-element '{{name}}' value must be a style object, not a {{type}}.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n\n function handleStyleObject(node: TSESTree.ObjectExpression) {\n if (!ctx.isStyleObject(node)) return;\n\n for (const prop of node.properties) {\n if (prop.type !== 'Property' || prop.computed) continue;\n\n const key = getKeyName(prop.key);\n if (key === null || !/^[A-Z]/.test(key)) continue;\n\n if (prop.value.type !== 'ObjectExpression') {\n const valueType =\n prop.value.type === 'Literal'\n ? typeof prop.value.value\n : prop.value.type;\n\n context.report({\n node: prop.value,\n messageId: 'subElementNotObject',\n data: { name: key, type: valueType },\n });\n }\n }\n }\n\n return {\n ImportDeclaration(node) {\n ctx.trackImport(node);\n },\n ...styleObjectListeners(handleStyleObject),\n };\n },\n});\n"],"mappings":";;;;;AAOA,gCAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aAAa,oDACd;EACD,UAAU,EACR,qBACE,wEACH;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;EAErC,SAAS,kBAAkB,MAAiC;AAC1D,OAAI,CAAC,IAAI,cAAc,KAAK,CAAE;AAE9B,QAAK,MAAM,QAAQ,KAAK,YAAY;AAClC,QAAI,KAAK,SAAS,cAAc,KAAK,SAAU;IAE/C,MAAM,MAAM,WAAW,KAAK,IAAI;AAChC,QAAI,QAAQ,QAAQ,CAAC,SAAS,KAAK,IAAI,CAAE;AAEzC,QAAI,KAAK,MAAM,SAAS,oBAAoB;KAC1C,MAAM,YACJ,KAAK,MAAM,SAAS,YAChB,OAAO,KAAK,MAAM,QAClB,KAAK,MAAM;AAEjB,aAAQ,OAAO;MACb,MAAM,KAAK;MACX,WAAW;MACX,MAAM;OAAE,MAAM;OAAK,MAAM;OAAW;MACrC,CAAC;;;;AAKR,SAAO;GACL,kBAAkB,MAAM;AACtB,QAAI,YAAY,KAAK;;GAEvB,GAAG,qBAAqB,kBAAkB;GAC3C;;CAEJ,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRule } from "../create-rule.js";
|
|
2
2
|
import { KNOWN_CSS_PROPERTIES, SEMANTIC_TRANSITIONS } from "../constants.js";
|
|
3
|
-
import { TastyContext } from "../context.js";
|
|
4
3
|
import { getKeyName, getStringValue } from "../utils.js";
|
|
4
|
+
import { TastyContext, styleObjectListeners } from "../context.js";
|
|
5
5
|
|
|
6
6
|
//#region src/rules/valid-transition.ts
|
|
7
7
|
var valid_transition_default = createRule({
|
|
@@ -32,27 +32,28 @@ var valid_transition_default = createRule({
|
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
+
function handleStyleObject(node) {
|
|
36
|
+
if (!ctx.isStyleObject(node)) return;
|
|
37
|
+
for (const prop of node.properties) {
|
|
38
|
+
if (prop.type !== "Property" || prop.computed) continue;
|
|
39
|
+
if (getKeyName(prop.key) !== "transition") continue;
|
|
40
|
+
const str = getStringValue(prop.value);
|
|
41
|
+
if (str) {
|
|
42
|
+
checkTransitionValue(str, prop.value);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (prop.value.type === "ObjectExpression") for (const stateProp of prop.value.properties) {
|
|
46
|
+
if (stateProp.type !== "Property") continue;
|
|
47
|
+
const stateStr = getStringValue(stateProp.value);
|
|
48
|
+
if (stateStr) checkTransitionValue(stateStr, stateProp.value);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
35
52
|
return {
|
|
36
53
|
ImportDeclaration(node) {
|
|
37
54
|
ctx.trackImport(node);
|
|
38
55
|
},
|
|
39
|
-
|
|
40
|
-
if (!ctx.isStyleObject(node)) return;
|
|
41
|
-
for (const prop of node.properties) {
|
|
42
|
-
if (prop.type !== "Property" || prop.computed) continue;
|
|
43
|
-
if (getKeyName(prop.key) !== "transition") continue;
|
|
44
|
-
const str = getStringValue(prop.value);
|
|
45
|
-
if (str) {
|
|
46
|
-
checkTransitionValue(str, prop.value);
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
if (prop.value.type === "ObjectExpression") for (const stateProp of prop.value.properties) {
|
|
50
|
-
if (stateProp.type !== "Property") continue;
|
|
51
|
-
const stateStr = getStringValue(stateProp.value);
|
|
52
|
-
if (stateStr) checkTransitionValue(stateStr, stateProp.value);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
+
...styleObjectListeners(handleStyleObject)
|
|
56
57
|
};
|
|
57
58
|
}
|
|
58
59
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"valid-transition.js","names":[],"sources":["../../src/rules/valid-transition.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport { createRule } from '../create-rule.js';\nimport { TastyContext } from '../context.js';\nimport { getKeyName, getStringValue } from '../utils.js';\nimport { SEMANTIC_TRANSITIONS, KNOWN_CSS_PROPERTIES } from '../constants.js';\n\ntype MessageIds = 'unknownTransition';\n\nexport default createRule<[], MessageIds>({\n name: 'valid-transition',\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Validate transition property values use valid semantic transition names',\n },\n messages: {\n unknownTransition:\n \"Unknown transition name '{{name}}'. Use a semantic name ({{known}}) or a CSS property name.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n\n function checkTransitionValue(value: string, node: TSESTree.Node): void {\n const groups = value.split(',');\n\n for (const group of groups) {\n const parts = group.trim().split(/\\s+/);\n if (parts.length === 0) continue;\n\n const name = parts[0];\n\n // $$ prefix is always valid (custom property reference)\n if (name.startsWith('$$')) continue;\n\n if (\n !SEMANTIC_TRANSITIONS.has(name) &&\n !KNOWN_CSS_PROPERTIES.has(name) &&\n name !== 'all' &&\n name !== 'none'\n ) {\n context.report({\n node,\n messageId: 'unknownTransition',\n data: {\n name,\n known: [...SEMANTIC_TRANSITIONS].join(', '),\n },\n });\n }\n }\n }\n\n
|
|
1
|
+
{"version":3,"file":"valid-transition.js","names":[],"sources":["../../src/rules/valid-transition.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport { createRule } from '../create-rule.js';\nimport { TastyContext, styleObjectListeners } from '../context.js';\nimport { getKeyName, getStringValue } from '../utils.js';\nimport { SEMANTIC_TRANSITIONS, KNOWN_CSS_PROPERTIES } from '../constants.js';\n\ntype MessageIds = 'unknownTransition';\n\nexport default createRule<[], MessageIds>({\n name: 'valid-transition',\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Validate transition property values use valid semantic transition names',\n },\n messages: {\n unknownTransition:\n \"Unknown transition name '{{name}}'. Use a semantic name ({{known}}) or a CSS property name.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n\n function checkTransitionValue(value: string, node: TSESTree.Node): void {\n const groups = value.split(',');\n\n for (const group of groups) {\n const parts = group.trim().split(/\\s+/);\n if (parts.length === 0) continue;\n\n const name = parts[0];\n\n // $$ prefix is always valid (custom property reference)\n if (name.startsWith('$$')) continue;\n\n if (\n !SEMANTIC_TRANSITIONS.has(name) &&\n !KNOWN_CSS_PROPERTIES.has(name) &&\n name !== 'all' &&\n name !== 'none'\n ) {\n context.report({\n node,\n messageId: 'unknownTransition',\n data: {\n name,\n known: [...SEMANTIC_TRANSITIONS].join(', '),\n },\n });\n }\n }\n }\n\n function handleStyleObject(node: TSESTree.ObjectExpression) {\n if (!ctx.isStyleObject(node)) return;\n\n for (const prop of node.properties) {\n if (prop.type !== 'Property' || prop.computed) continue;\n\n const key = getKeyName(prop.key);\n if (key !== 'transition') continue;\n\n const str = getStringValue(prop.value);\n if (str) {\n checkTransitionValue(str, prop.value);\n continue;\n }\n\n if (prop.value.type === 'ObjectExpression') {\n for (const stateProp of prop.value.properties) {\n if (stateProp.type !== 'Property') continue;\n const stateStr = getStringValue(stateProp.value);\n if (stateStr) {\n checkTransitionValue(stateStr, stateProp.value);\n }\n }\n }\n }\n }\n\n return {\n ImportDeclaration(node) {\n ctx.trackImport(node);\n },\n ...styleObjectListeners(handleStyleObject),\n };\n },\n});\n"],"mappings":";;;;;;AAQA,+BAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aACE,2EACH;EACD,UAAU,EACR,mBACE,+FACH;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;EAErC,SAAS,qBAAqB,OAAe,MAA2B;GACtE,MAAM,SAAS,MAAM,MAAM,IAAI;AAE/B,QAAK,MAAM,SAAS,QAAQ;IAC1B,MAAM,QAAQ,MAAM,MAAM,CAAC,MAAM,MAAM;AACvC,QAAI,MAAM,WAAW,EAAG;IAExB,MAAM,OAAO,MAAM;AAGnB,QAAI,KAAK,WAAW,KAAK,CAAE;AAE3B,QACE,CAAC,qBAAqB,IAAI,KAAK,IAC/B,CAAC,qBAAqB,IAAI,KAAK,IAC/B,SAAS,SACT,SAAS,OAET,SAAQ,OAAO;KACb;KACA,WAAW;KACX,MAAM;MACJ;MACA,OAAO,CAAC,GAAG,qBAAqB,CAAC,KAAK,KAAK;MAC5C;KACF,CAAC;;;EAKR,SAAS,kBAAkB,MAAiC;AAC1D,OAAI,CAAC,IAAI,cAAc,KAAK,CAAE;AAE9B,QAAK,MAAM,QAAQ,KAAK,YAAY;AAClC,QAAI,KAAK,SAAS,cAAc,KAAK,SAAU;AAG/C,QADY,WAAW,KAAK,IAAI,KACpB,aAAc;IAE1B,MAAM,MAAM,eAAe,KAAK,MAAM;AACtC,QAAI,KAAK;AACP,0BAAqB,KAAK,KAAK,MAAM;AACrC;;AAGF,QAAI,KAAK,MAAM,SAAS,mBACtB,MAAK,MAAM,aAAa,KAAK,MAAM,YAAY;AAC7C,SAAI,UAAU,SAAS,WAAY;KACnC,MAAM,WAAW,eAAe,UAAU,MAAM;AAChD,SAAI,SACF,sBAAqB,UAAU,UAAU,MAAM;;;;AAOzD,SAAO;GACL,kBAAkB,MAAM;AACtB,QAAI,YAAY,KAAK;;GAEvB,GAAG,qBAAqB,kBAAkB;GAC3C;;CAEJ,CAAC"}
|
|
@@ -1,95 +1,128 @@
|
|
|
1
1
|
import { createRule } from "../create-rule.js";
|
|
2
|
-
import { TastyContext } from "../context.js";
|
|
3
2
|
import { getKeyName, getStringValue } from "../utils.js";
|
|
4
|
-
import {
|
|
3
|
+
import { TastyContext, styleObjectListeners } from "../context.js";
|
|
4
|
+
import { parseValue } from "../parsers/value-parser.js";
|
|
5
5
|
import { getExpectation } from "../property-expectations.js";
|
|
6
6
|
|
|
7
7
|
//#region src/rules/valid-value.ts
|
|
8
|
-
const
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
8
|
+
const SKIP_PROPERTIES = new Set([
|
|
9
|
+
"recipe",
|
|
10
|
+
"@keyframes",
|
|
11
|
+
"@properties",
|
|
12
|
+
"content",
|
|
13
|
+
"animation",
|
|
14
|
+
"animationName",
|
|
15
|
+
"gridAreas",
|
|
16
|
+
"gridTemplate",
|
|
17
|
+
"listStyle",
|
|
18
|
+
"willChange"
|
|
14
19
|
]);
|
|
15
20
|
var valid_value_default = createRule({
|
|
16
21
|
name: "valid-value",
|
|
17
22
|
meta: {
|
|
18
23
|
type: "problem",
|
|
19
|
-
docs: { description: "Parse style values through the
|
|
24
|
+
docs: { description: "Parse style values through the value parser and validate against per-property expectations" },
|
|
20
25
|
messages: {
|
|
21
26
|
unbalancedParens: "Unbalanced parentheses in value.",
|
|
22
27
|
importantNotAllowed: "Do not use !important in tasty styles. Use state specificity instead.",
|
|
23
28
|
unexpectedMod: "Unrecognized token '{{mod}}' in '{{property}}' value. This may be a typo.",
|
|
24
29
|
unexpectedColor: "Property '{{property}}' does not accept color tokens, but found '{{color}}'.",
|
|
25
|
-
invalidMod: "Modifier '{{mod}}' is not valid for '{{property}}'. Accepted: {{accepted}}."
|
|
30
|
+
invalidMod: "Modifier '{{mod}}' is not valid for '{{property}}'. Accepted: {{accepted}}.",
|
|
31
|
+
unknownToken: "Unknown token '{{token}}' in '{{property}}' value.",
|
|
32
|
+
parseError: "{{message}}"
|
|
26
33
|
},
|
|
27
34
|
schema: []
|
|
28
35
|
},
|
|
29
36
|
defaultOptions: [],
|
|
30
37
|
create(context) {
|
|
31
38
|
const ctx = new TastyContext(context);
|
|
32
|
-
function
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
function getParserOpts() {
|
|
40
|
+
const opts = {};
|
|
41
|
+
if (ctx.config.units === false) opts.skipUnitValidation = true;
|
|
42
|
+
else if (Array.isArray(ctx.config.units)) opts.knownUnits = new Set(ctx.config.units);
|
|
43
|
+
if (ctx.config.funcs === false) opts.skipFuncValidation = true;
|
|
44
|
+
else if (Array.isArray(ctx.config.funcs)) opts.knownFuncs = new Set(ctx.config.funcs);
|
|
45
|
+
return opts;
|
|
46
|
+
}
|
|
47
|
+
function checkValue(value, property, node) {
|
|
48
|
+
if (property && SKIP_PROPERTIES.has(property)) return;
|
|
49
|
+
const result = parseValue(value, getParserOpts());
|
|
50
|
+
const erroredRaws = /* @__PURE__ */ new Set();
|
|
51
|
+
for (const error of result.errors) {
|
|
52
|
+
if (error.message.includes("parenthes")) {
|
|
38
53
|
context.report({
|
|
39
54
|
node,
|
|
40
55
|
messageId: "unbalancedParens"
|
|
41
56
|
});
|
|
42
|
-
return
|
|
57
|
+
return;
|
|
43
58
|
}
|
|
44
|
-
}
|
|
45
|
-
if (depth !== 0) {
|
|
46
59
|
context.report({
|
|
47
60
|
node,
|
|
48
|
-
messageId: "
|
|
61
|
+
messageId: "parseError",
|
|
62
|
+
data: { message: error.message }
|
|
49
63
|
});
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
return true;
|
|
53
|
-
}
|
|
54
|
-
function checkValue(value, property, node) {
|
|
55
|
-
if (!checkParenBalance(value, node)) return;
|
|
56
|
-
if (value.includes("!important")) {
|
|
57
|
-
context.report({
|
|
58
|
-
node,
|
|
59
|
-
messageId: "importantNotAllowed"
|
|
60
|
-
});
|
|
61
|
-
return;
|
|
64
|
+
if (error.raw) erroredRaws.add(error.raw);
|
|
62
65
|
}
|
|
63
66
|
if (!property) return;
|
|
64
|
-
const result = getParser(ctx.config).process(value);
|
|
65
67
|
const expectation = getExpectation(property);
|
|
66
|
-
for (const group of result.groups) {
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
68
|
+
for (const group of result.groups) for (const part of group.parts) for (const token of part.tokens) {
|
|
69
|
+
if (token.type === "important") {
|
|
70
|
+
context.report({
|
|
71
|
+
node,
|
|
72
|
+
messageId: "importantNotAllowed"
|
|
73
|
+
});
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (!expectation.acceptsColor && (token.type === "color-token" || token.type === "color-ref")) {
|
|
77
|
+
const colorName = token.type === "color-token" ? `#${token.name}` : `##${token.name}`;
|
|
78
|
+
context.report({
|
|
79
|
+
node,
|
|
80
|
+
messageId: "unexpectedColor",
|
|
81
|
+
data: {
|
|
82
|
+
property,
|
|
83
|
+
color: colorName
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (!expectation.acceptsColor && token.type === "css-function" && isColorFunction(token.name)) {
|
|
89
|
+
context.report({
|
|
87
90
|
node,
|
|
88
|
-
messageId: "
|
|
91
|
+
messageId: "unexpectedColor",
|
|
89
92
|
data: {
|
|
90
93
|
property,
|
|
91
|
-
|
|
92
|
-
|
|
94
|
+
color: `${token.name}()`
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if ((token.type === "unknown" || token.type === "css-function") && "raw" in token && token.raw && erroredRaws.has(token.raw)) continue;
|
|
100
|
+
if (token.type === "unknown") {
|
|
101
|
+
const raw = token.raw;
|
|
102
|
+
if (expectation.acceptsMods === false) context.report({
|
|
103
|
+
node,
|
|
104
|
+
messageId: "unexpectedMod",
|
|
105
|
+
data: {
|
|
106
|
+
property,
|
|
107
|
+
mod: raw
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
else if (Array.isArray(expectation.acceptsMods)) {
|
|
111
|
+
if (!expectation.acceptsMods.includes(raw)) context.report({
|
|
112
|
+
node,
|
|
113
|
+
messageId: "invalidMod",
|
|
114
|
+
data: {
|
|
115
|
+
property,
|
|
116
|
+
mod: raw,
|
|
117
|
+
accepted: expectation.acceptsMods.join(", ")
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
} else context.report({
|
|
121
|
+
node,
|
|
122
|
+
messageId: "unknownToken",
|
|
123
|
+
data: {
|
|
124
|
+
property,
|
|
125
|
+
token: raw
|
|
93
126
|
}
|
|
94
127
|
});
|
|
95
128
|
}
|
|
@@ -111,20 +144,38 @@ var valid_value_default = createRule({
|
|
|
111
144
|
if (stateStr) checkValue(stateStr, key, stateProp.value);
|
|
112
145
|
}
|
|
113
146
|
}
|
|
147
|
+
function handleStyleObject(node) {
|
|
148
|
+
if (!ctx.isStyleObject(node)) return;
|
|
149
|
+
for (const prop of node.properties) {
|
|
150
|
+
if (prop.type !== "Property") continue;
|
|
151
|
+
processProperty(prop);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
114
154
|
return {
|
|
115
155
|
ImportDeclaration(node) {
|
|
116
156
|
ctx.trackImport(node);
|
|
117
157
|
},
|
|
118
|
-
|
|
119
|
-
if (!ctx.isStyleObject(node)) return;
|
|
120
|
-
for (const prop of node.properties) {
|
|
121
|
-
if (prop.type !== "Property") continue;
|
|
122
|
-
processProperty(prop);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
158
|
+
...styleObjectListeners(handleStyleObject)
|
|
125
159
|
};
|
|
126
160
|
}
|
|
127
161
|
});
|
|
162
|
+
const COLOR_FUNC_NAMES = new Set([
|
|
163
|
+
"rgb",
|
|
164
|
+
"rgba",
|
|
165
|
+
"hsl",
|
|
166
|
+
"hsla",
|
|
167
|
+
"hwb",
|
|
168
|
+
"lab",
|
|
169
|
+
"lch",
|
|
170
|
+
"oklab",
|
|
171
|
+
"oklch",
|
|
172
|
+
"color",
|
|
173
|
+
"color-mix",
|
|
174
|
+
"color-contrast"
|
|
175
|
+
]);
|
|
176
|
+
function isColorFunction(name) {
|
|
177
|
+
return COLOR_FUNC_NAMES.has(name);
|
|
178
|
+
}
|
|
128
179
|
|
|
129
180
|
//#endregion
|
|
130
181
|
export { valid_value_default as default };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"valid-value.js","names":[],"sources":["../../src/rules/valid-value.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport { createRule } from '../create-rule.js';\nimport { TastyContext } from '../context.js';\nimport { getKeyName, getStringValue } from '../utils.js';\nimport { getParser } from '../parser.js';\nimport { getExpectation } from '../property-expectations.js';\n\nconst CSS_GLOBAL_KEYWORDS = new Set([\n 'inherit',\n 'initial',\n 'unset',\n 'revert',\n 'revert-layer',\n]);\n\ntype MessageIds =\n | 'unbalancedParens'\n | 'importantNotAllowed'\n | 'unexpectedMod'\n | 'unexpectedColor'\n | 'invalidMod';\n\nexport default createRule<[], MessageIds>({\n name: 'valid-value',\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Parse style values through the tasty parser and validate against per-property expectations',\n },\n messages: {\n unbalancedParens: 'Unbalanced parentheses in value.',\n importantNotAllowed:\n 'Do not use !important in tasty styles. Use state specificity instead.',\n unexpectedMod:\n \"Unrecognized token '{{mod}}' in '{{property}}' value. This may be a typo.\",\n unexpectedColor:\n \"Property '{{property}}' does not accept color tokens, but found '{{color}}'.\",\n invalidMod:\n \"Modifier '{{mod}}' is not valid for '{{property}}'. Accepted: {{accepted}}.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n\n function checkParenBalance(value: string, node: TSESTree.Node): boolean {\n let depth = 0;\n for (const char of value) {\n if (char === '(') depth++;\n if (char === ')') depth--;\n if (depth < 0) {\n context.report({ node, messageId: 'unbalancedParens' });\n return false;\n }\n }\n if (depth !== 0) {\n context.report({ node, messageId: 'unbalancedParens' });\n return false;\n }\n return true;\n }\n\n function checkValue(\n value: string,\n property: string | null,\n node: TSESTree.Node,\n ): void {\n if (!checkParenBalance(value, node)) return;\n\n if (value.includes('!important')) {\n context.report({ node, messageId: 'importantNotAllowed' });\n return;\n }\n\n if (!property) return;\n\n const parser = getParser(ctx.config);\n const result = parser.process(value);\n const expectation = getExpectation(property);\n\n for (const group of result.groups) {\n if (!expectation.acceptsColor && group.colors.length > 0) {\n for (const color of group.colors) {\n context.report({\n node,\n messageId: 'unexpectedColor',\n data: { property, color },\n });\n }\n }\n\n const mods = group.mods.filter((m) => !CSS_GLOBAL_KEYWORDS.has(m));\n\n if (expectation.acceptsMods === false && mods.length > 0) {\n for (const mod of mods) {\n context.report({\n node,\n messageId: 'unexpectedMod',\n data: { property, mod },\n });\n }\n } else if (Array.isArray(expectation.acceptsMods) && mods.length > 0) {\n const allowed = new Set(expectation.acceptsMods);\n for (const mod of mods) {\n if (!allowed.has(mod)) {\n context.report({\n node,\n messageId: 'invalidMod',\n data: {\n property,\n mod,\n accepted: expectation.acceptsMods.join(', '),\n },\n });\n }\n }\n }\n }\n }\n\n function processProperty(prop: TSESTree.Property): void {\n const key = !prop.computed ? getKeyName(prop.key) : null;\n\n if (key && (/^[A-Z]/.test(key) || key.startsWith('@'))) return;\n if (key && (key.startsWith('$') || key.startsWith('#'))) return;\n if (key && key.startsWith('&')) return;\n\n const str = getStringValue(prop.value);\n if (str) {\n checkValue(str, key, prop.value);\n return;\n }\n\n // State map\n if (prop.value.type === 'ObjectExpression') {\n for (const stateProp of prop.value.properties) {\n if (stateProp.type !== 'Property') continue;\n const stateStr = getStringValue(stateProp.value);\n if (stateStr) {\n checkValue(stateStr, key, stateProp.value);\n }\n }\n }\n }\n\n return {\n ImportDeclaration(node) {\n ctx.trackImport(node);\n },\n\n 'CallExpression ObjectExpression'(node: TSESTree.ObjectExpression) {\n if (!ctx.isStyleObject(node)) return;\n\n for (const prop of node.properties) {\n if (prop.type !== 'Property') continue;\n processProperty(prop);\n }\n },\n };\n },\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"valid-value.js","names":[],"sources":["../../src/rules/valid-value.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport { createRule } from '../create-rule.js';\nimport { TastyContext, styleObjectListeners } from '../context.js';\nimport { getKeyName, getStringValue } from '../utils.js';\nimport { parseValue } from '../parsers/value-parser.js';\nimport type { ValueParserOptions } from '../parsers/value-parser.js';\nimport { getExpectation } from '../property-expectations.js';\n\nconst SKIP_PROPERTIES = new Set([\n 'recipe',\n '@keyframes',\n '@properties',\n 'content',\n 'animation',\n 'animationName',\n 'gridAreas',\n 'gridTemplate',\n 'listStyle',\n 'willChange',\n]);\n\ntype MessageIds =\n | 'unbalancedParens'\n | 'importantNotAllowed'\n | 'unexpectedMod'\n | 'unexpectedColor'\n | 'invalidMod'\n | 'unknownToken'\n | 'parseError';\n\nexport default createRule<[], MessageIds>({\n name: 'valid-value',\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Parse style values through the value parser and validate against per-property expectations',\n },\n messages: {\n unbalancedParens: 'Unbalanced parentheses in value.',\n importantNotAllowed:\n 'Do not use !important in tasty styles. Use state specificity instead.',\n unexpectedMod:\n \"Unrecognized token '{{mod}}' in '{{property}}' value. This may be a typo.\",\n unexpectedColor:\n \"Property '{{property}}' does not accept color tokens, but found '{{color}}'.\",\n invalidMod:\n \"Modifier '{{mod}}' is not valid for '{{property}}'. Accepted: {{accepted}}.\",\n unknownToken:\n \"Unknown token '{{token}}' in '{{property}}' value.\",\n parseError: '{{message}}',\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n\n function getParserOpts(): ValueParserOptions {\n const opts: ValueParserOptions = {};\n if (ctx.config.units === false) {\n opts.skipUnitValidation = true;\n } else if (Array.isArray(ctx.config.units)) {\n opts.knownUnits = new Set(ctx.config.units);\n }\n if (ctx.config.funcs === false) {\n opts.skipFuncValidation = true;\n } else if (Array.isArray(ctx.config.funcs)) {\n opts.knownFuncs = new Set(ctx.config.funcs);\n }\n return opts;\n }\n\n function checkValue(\n value: string,\n property: string | null,\n node: TSESTree.Node,\n ): void {\n if (property && SKIP_PROPERTIES.has(property)) return;\n\n const result = parseValue(value, getParserOpts());\n\n const erroredRaws = new Set<string>();\n\n // Report parser-level errors (bracket balance, unknown units/functions)\n for (const error of result.errors) {\n if (error.message.includes('parenthes')) {\n context.report({ node, messageId: 'unbalancedParens' });\n return;\n }\n context.report({\n node,\n messageId: 'parseError',\n data: { message: error.message },\n });\n if (error.raw) {\n erroredRaws.add(error.raw);\n }\n }\n\n if (!property) return;\n\n const expectation = getExpectation(property);\n\n for (const group of result.groups) {\n for (const part of group.parts) {\n for (const token of part.tokens) {\n // Check !important\n if (token.type === 'important') {\n context.report({ node, messageId: 'importantNotAllowed' });\n continue;\n }\n\n // Check color tokens in non-color properties\n if (\n !expectation.acceptsColor &&\n (token.type === 'color-token' || token.type === 'color-ref')\n ) {\n const colorName =\n token.type === 'color-token'\n ? `#${token.name}`\n : `##${token.name}`;\n context.report({\n node,\n messageId: 'unexpectedColor',\n data: { property, color: colorName },\n });\n continue;\n }\n\n // Check color functions in non-color properties\n if (\n !expectation.acceptsColor &&\n token.type === 'css-function' &&\n isColorFunction(token.name)\n ) {\n context.report({\n node,\n messageId: 'unexpectedColor',\n data: { property, color: `${token.name}()` },\n });\n continue;\n }\n\n // Skip tokens already reported via parser errors\n if (\n (token.type === 'unknown' || token.type === 'css-function') &&\n 'raw' in token &&\n token.raw &&\n erroredRaws.has(token.raw)\n ) {\n continue;\n }\n\n // Check unknown tokens against property expectations\n if (token.type === 'unknown') {\n const raw = token.raw;\n\n if (expectation.acceptsMods === false) {\n context.report({\n node,\n messageId: 'unexpectedMod',\n data: { property, mod: raw },\n });\n } else if (Array.isArray(expectation.acceptsMods)) {\n if (!expectation.acceptsMods.includes(raw)) {\n context.report({\n node,\n messageId: 'invalidMod',\n data: {\n property,\n mod: raw,\n accepted: expectation.acceptsMods.join(', '),\n },\n });\n }\n } else {\n context.report({\n node,\n messageId: 'unknownToken',\n data: { property, token: raw },\n });\n }\n }\n }\n }\n }\n }\n\n function processProperty(prop: TSESTree.Property): void {\n const key = !prop.computed ? getKeyName(prop.key) : null;\n\n if (key && (/^[A-Z]/.test(key) || key.startsWith('@'))) return;\n if (key && (key.startsWith('$') || key.startsWith('#'))) return;\n if (key && key.startsWith('&')) return;\n\n const str = getStringValue(prop.value);\n if (str) {\n checkValue(str, key, prop.value);\n return;\n }\n\n // State map\n if (prop.value.type === 'ObjectExpression') {\n for (const stateProp of prop.value.properties) {\n if (stateProp.type !== 'Property') continue;\n const stateStr = getStringValue(stateProp.value);\n if (stateStr) {\n checkValue(stateStr, key, stateProp.value);\n }\n }\n }\n }\n\n function handleStyleObject(node: TSESTree.ObjectExpression) {\n if (!ctx.isStyleObject(node)) return;\n\n for (const prop of node.properties) {\n if (prop.type !== 'Property') continue;\n processProperty(prop);\n }\n }\n\n return {\n ImportDeclaration(node) {\n ctx.trackImport(node);\n },\n ...styleObjectListeners(handleStyleObject),\n };\n },\n});\n\nconst COLOR_FUNC_NAMES = new Set([\n 'rgb',\n 'rgba',\n 'hsl',\n 'hsla',\n 'hwb',\n 'lab',\n 'lch',\n 'oklab',\n 'oklch',\n 'color',\n 'color-mix',\n 'color-contrast',\n]);\n\nfunction isColorFunction(name: string): boolean {\n return COLOR_FUNC_NAMES.has(name);\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tenphi/eslint-plugin-tasty",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "ESLint plugin for validating tasty() and tastyStatic() style objects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -39,14 +39,8 @@
|
|
|
39
39
|
},
|
|
40
40
|
"homepage": "https://github.com/tenphi/eslint-plugin-tasty#readme",
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@tenphi/tasty": ">=0.1.3",
|
|
43
42
|
"eslint": ">=8.0.0"
|
|
44
43
|
},
|
|
45
|
-
"peerDependenciesMeta": {
|
|
46
|
-
"@tenphi/tasty": {
|
|
47
|
-
"optional": true
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
44
|
"dependencies": {
|
|
51
45
|
"@typescript-eslint/utils": "^8.56.0"
|
|
52
46
|
},
|
|
@@ -54,7 +48,6 @@
|
|
|
54
48
|
"@changesets/changelog-github": "^0.5.2",
|
|
55
49
|
"@changesets/cli": "^2.29.8",
|
|
56
50
|
"@eslint/js": "^10.0.1",
|
|
57
|
-
"@tenphi/tasty": "0.4.0",
|
|
58
51
|
"@types/node": "^22.0.0",
|
|
59
52
|
"@typescript-eslint/parser": "^8.56.1",
|
|
60
53
|
"@typescript-eslint/rule-tester": "^8.56.1",
|
package/dist/parser.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { BUILT_IN_UNITS } from "./constants.js";
|
|
2
|
-
import { StyleParser } from "@tenphi/tasty/core";
|
|
3
|
-
|
|
4
|
-
//#region src/parser.ts
|
|
5
|
-
const BUILT_IN_UNIT_STUBS = {
|
|
6
|
-
x: "var(--gap)",
|
|
7
|
-
r: "var(--radius)",
|
|
8
|
-
cr: "var(--card-radius)",
|
|
9
|
-
bw: "var(--border-width)",
|
|
10
|
-
ow: "var(--outline-width)",
|
|
11
|
-
fs: "var(--font-size)",
|
|
12
|
-
lh: "var(--line-height)",
|
|
13
|
-
sf: "var(--scale-factor)"
|
|
14
|
-
};
|
|
15
|
-
let cachedParser = null;
|
|
16
|
-
function configKey(config) {
|
|
17
|
-
return `${config.units === false ? "false" : JSON.stringify(config.units)}|${config.funcs === false ? "false" : JSON.stringify(config.funcs)}`;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Build a StyleParser from the ESLint plugin config.
|
|
21
|
-
* Unit handlers are stubs (they produce placeholder CSS) because we only
|
|
22
|
-
* care about bucket classification, not actual CSS output.
|
|
23
|
-
*/
|
|
24
|
-
function getParser(config) {
|
|
25
|
-
const key = configKey(config);
|
|
26
|
-
if (cachedParser && cachedParser.configKey === key) return cachedParser.parser;
|
|
27
|
-
const units = { ...BUILT_IN_UNIT_STUBS };
|
|
28
|
-
if (Array.isArray(config.units)) {
|
|
29
|
-
for (const u of config.units) if (!units[u]) units[u] = `var(--${u})`;
|
|
30
|
-
} else if (config.units !== false) for (const u of BUILT_IN_UNITS) units[u] = BUILT_IN_UNIT_STUBS[u] ?? `var(--${u})`;
|
|
31
|
-
const funcs = {};
|
|
32
|
-
if (Array.isArray(config.funcs)) for (const f of config.funcs) funcs[f] = (groups) => groups.map((g) => g.output).join(", ");
|
|
33
|
-
const parser = new StyleParser({
|
|
34
|
-
units: config.units === false ? void 0 : units,
|
|
35
|
-
funcs: Object.keys(funcs).length > 0 ? funcs : void 0
|
|
36
|
-
});
|
|
37
|
-
cachedParser = {
|
|
38
|
-
parser,
|
|
39
|
-
configKey: key
|
|
40
|
-
};
|
|
41
|
-
return parser;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
//#endregion
|
|
45
|
-
export { getParser };
|
|
46
|
-
//# sourceMappingURL=parser.js.map
|
package/dist/parser.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"parser.js","names":[],"sources":["../src/parser.ts"],"sourcesContent":["import { StyleParser } from '@tenphi/tasty/core';\nimport type { ParserOptions } from '@tenphi/tasty/core';\nimport type { ResolvedConfig } from './types.js';\nimport { BUILT_IN_UNITS } from './constants.js';\n\nconst BUILT_IN_UNIT_STUBS: Record<string, string> = {\n x: 'var(--gap)',\n r: 'var(--radius)',\n cr: 'var(--card-radius)',\n bw: 'var(--border-width)',\n ow: 'var(--outline-width)',\n fs: 'var(--font-size)',\n lh: 'var(--line-height)',\n sf: 'var(--scale-factor)',\n};\n\nlet cachedParser: { parser: StyleParser; configKey: string } | null = null;\n\nfunction configKey(config: ResolvedConfig): string {\n const units = config.units === false ? 'false' : JSON.stringify(config.units);\n const funcs = config.funcs === false ? 'false' : JSON.stringify(config.funcs);\n return `${units}|${funcs}`;\n}\n\n/**\n * Build a StyleParser from the ESLint plugin config.\n * Unit handlers are stubs (they produce placeholder CSS) because we only\n * care about bucket classification, not actual CSS output.\n */\nexport function getParser(config: ResolvedConfig): StyleParser {\n const key = configKey(config);\n if (cachedParser && cachedParser.configKey === key) {\n return cachedParser.parser;\n }\n\n const units: Record<string, string> = { ...BUILT_IN_UNIT_STUBS };\n\n if (Array.isArray(config.units)) {\n for (const u of config.units) {\n if (!units[u]) {\n units[u] = `var(--${u})`;\n }\n }\n } else if (config.units !== false) {\n for (const u of BUILT_IN_UNITS) {\n units[u] = BUILT_IN_UNIT_STUBS[u] ?? `var(--${u})`;\n }\n }\n\n const funcs: ParserOptions['funcs'] = {};\n if (Array.isArray(config.funcs)) {\n for (const f of config.funcs) {\n funcs[f] = (groups) => groups.map((g) => g.output).join(', ');\n }\n }\n\n const opts: ParserOptions = {\n units: config.units === false ? undefined : units,\n funcs: Object.keys(funcs).length > 0 ? funcs : undefined,\n };\n\n const parser = new StyleParser(opts);\n\n cachedParser = { parser, configKey: key };\n return parser;\n}\n"],"mappings":";;;;AAKA,MAAM,sBAA8C;CAClD,GAAG;CACH,GAAG;CACH,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACL;AAED,IAAI,eAAkE;AAEtE,SAAS,UAAU,QAAgC;AAGjD,QAAO,GAFO,OAAO,UAAU,QAAQ,UAAU,KAAK,UAAU,OAAO,MAAM,CAE7D,GADF,OAAO,UAAU,QAAQ,UAAU,KAAK,UAAU,OAAO,MAAM;;;;;;;AAS/E,SAAgB,UAAU,QAAqC;CAC7D,MAAM,MAAM,UAAU,OAAO;AAC7B,KAAI,gBAAgB,aAAa,cAAc,IAC7C,QAAO,aAAa;CAGtB,MAAM,QAAgC,EAAE,GAAG,qBAAqB;AAEhE,KAAI,MAAM,QAAQ,OAAO,MAAM,EAC7B;OAAK,MAAM,KAAK,OAAO,MACrB,KAAI,CAAC,MAAM,GACT,OAAM,KAAK,SAAS,EAAE;YAGjB,OAAO,UAAU,MAC1B,MAAK,MAAM,KAAK,eACd,OAAM,KAAK,oBAAoB,MAAM,SAAS,EAAE;CAIpD,MAAM,QAAgC,EAAE;AACxC,KAAI,MAAM,QAAQ,OAAO,MAAM,CAC7B,MAAK,MAAM,KAAK,OAAO,MACrB,OAAM,MAAM,WAAW,OAAO,KAAK,MAAM,EAAE,OAAO,CAAC,KAAK,KAAK;CASjE,MAAM,SAAS,IAAI,YALS;EAC1B,OAAO,OAAO,UAAU,QAAQ,SAAY;EAC5C,OAAO,OAAO,KAAK,MAAM,CAAC,SAAS,IAAI,QAAQ;EAChD,CAEmC;AAEpC,gBAAe;EAAE;EAAQ,WAAW;EAAK;AACzC,QAAO"}
|