@tenphi/eslint-plugin-tasty 0.3.0 → 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.
Files changed (72) hide show
  1. package/dist/config.js +100 -20
  2. package/dist/config.js.map +1 -1
  3. package/dist/configs.js +5 -4
  4. package/dist/configs.js.map +1 -1
  5. package/dist/constants.js +56 -1
  6. package/dist/constants.js.map +1 -1
  7. package/dist/context.js +37 -6
  8. package/dist/context.js.map +1 -1
  9. package/dist/index.js +3 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/parsers/state-key-parser.js +486 -0
  12. package/dist/parsers/state-key-parser.js.map +1 -0
  13. package/dist/parsers/utils.js +128 -0
  14. package/dist/parsers/utils.js.map +1 -0
  15. package/dist/parsers/value-parser.js +613 -0
  16. package/dist/parsers/value-parser.js.map +1 -0
  17. package/dist/rules/consistent-token-usage.js +20 -19
  18. package/dist/rules/consistent-token-usage.js.map +1 -1
  19. package/dist/rules/known-property.js +31 -30
  20. package/dist/rules/known-property.js.map +1 -1
  21. package/dist/rules/no-duplicate-state.js +12 -11
  22. package/dist/rules/no-duplicate-state.js.map +1 -1
  23. package/dist/rules/no-important.js +12 -11
  24. package/dist/rules/no-important.js.map +1 -1
  25. package/dist/rules/no-nested-selector.js +15 -14
  26. package/dist/rules/no-nested-selector.js.map +1 -1
  27. package/dist/rules/no-nested-state-map.js +19 -18
  28. package/dist/rules/no-nested-state-map.js.map +1 -1
  29. package/dist/rules/no-raw-color-values.js +15 -14
  30. package/dist/rules/no-raw-color-values.js.map +1 -1
  31. package/dist/rules/no-runtime-styles-mutation.js +6 -5
  32. package/dist/rules/no-runtime-styles-mutation.js.map +1 -1
  33. package/dist/rules/no-unknown-state-alias.js +12 -11
  34. package/dist/rules/no-unknown-state-alias.js.map +1 -1
  35. package/dist/rules/prefer-shorthand-property.js +19 -18
  36. package/dist/rules/prefer-shorthand-property.js.map +1 -1
  37. package/dist/rules/require-default-state.js +22 -21
  38. package/dist/rules/require-default-state.js.map +1 -1
  39. package/dist/rules/static-no-dynamic-values.js +7 -6
  40. package/dist/rules/static-no-dynamic-values.js.map +1 -1
  41. package/dist/rules/static-valid-selector.js +1 -1
  42. package/dist/rules/valid-boolean-property.js +19 -18
  43. package/dist/rules/valid-boolean-property.js.map +1 -1
  44. package/dist/rules/valid-color-token.js +33 -21
  45. package/dist/rules/valid-color-token.js.map +1 -1
  46. package/dist/rules/valid-custom-property.js +31 -19
  47. package/dist/rules/valid-custom-property.js.map +1 -1
  48. package/dist/rules/valid-custom-unit.js +12 -16
  49. package/dist/rules/valid-custom-unit.js.map +1 -1
  50. package/dist/rules/valid-directional-modifier.js +21 -20
  51. package/dist/rules/valid-directional-modifier.js.map +1 -1
  52. package/dist/rules/valid-preset.js +5 -2
  53. package/dist/rules/valid-preset.js.map +1 -1
  54. package/dist/rules/valid-radius-shape.js +19 -18
  55. package/dist/rules/valid-radius-shape.js.map +1 -1
  56. package/dist/rules/valid-recipe.js +5 -2
  57. package/dist/rules/valid-recipe.js.map +1 -1
  58. package/dist/rules/valid-state-definition.js +70 -0
  59. package/dist/rules/valid-state-definition.js.map +1 -0
  60. package/dist/rules/valid-state-key.js +39 -99
  61. package/dist/rules/valid-state-key.js.map +1 -1
  62. package/dist/rules/valid-styles-structure.js +39 -38
  63. package/dist/rules/valid-styles-structure.js.map +1 -1
  64. package/dist/rules/valid-sub-element.js +21 -20
  65. package/dist/rules/valid-sub-element.js.map +1 -1
  66. package/dist/rules/valid-transition.js +19 -18
  67. package/dist/rules/valid-transition.js.map +1 -1
  68. package/dist/rules/valid-value.js +115 -64
  69. package/dist/rules/valid-value.js.map +1 -1
  70. package/package.json +1 -8
  71. package/dist/parser.js +0 -46
  72. package/dist/parser.js.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import { createRule } from "../create-rule.js";
2
2
  import { SHORTHAND_MAPPING } from "../constants.js";
3
- import { TastyContext } from "../context.js";
4
3
  import { getKeyName } from "../utils.js";
4
+ import { TastyContext, styleObjectListeners } from "../context.js";
5
5
 
6
6
  //#region src/rules/prefer-shorthand-property.ts
7
7
  var prefer_shorthand_property_default = createRule({
@@ -15,27 +15,28 @@ var prefer_shorthand_property_default = createRule({
15
15
  defaultOptions: [],
16
16
  create(context) {
17
17
  const ctx = new TastyContext(context);
18
+ function handleStyleObject(node) {
19
+ if (!ctx.isStyleObject(node)) return;
20
+ for (const prop of node.properties) {
21
+ if (prop.type !== "Property" || prop.computed) continue;
22
+ const key = getKeyName(prop.key);
23
+ if (key === null) continue;
24
+ const mapping = SHORTHAND_MAPPING[key];
25
+ if (mapping) context.report({
26
+ node: prop.key,
27
+ messageId: "preferShorthand",
28
+ data: {
29
+ native: key,
30
+ alternative: mapping.hint
31
+ }
32
+ });
33
+ }
34
+ }
18
35
  return {
19
36
  ImportDeclaration(node) {
20
37
  ctx.trackImport(node);
21
38
  },
22
- "CallExpression ObjectExpression"(node) {
23
- if (!ctx.isStyleObject(node)) return;
24
- for (const prop of node.properties) {
25
- if (prop.type !== "Property" || prop.computed) continue;
26
- const key = getKeyName(prop.key);
27
- if (key === null) continue;
28
- const mapping = SHORTHAND_MAPPING[key];
29
- if (mapping) context.report({
30
- node: prop.key,
31
- messageId: "preferShorthand",
32
- data: {
33
- native: key,
34
- alternative: mapping.hint
35
- }
36
- });
37
- }
38
- }
39
+ ...styleObjectListeners(handleStyleObject)
39
40
  };
40
41
  }
41
42
  });
@@ -1 +1 @@
1
- {"version":3,"file":"prefer-shorthand-property.js","names":[],"sources":["../../src/rules/prefer-shorthand-property.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';\nimport { SHORTHAND_MAPPING } from '../constants.js';\n\ntype MessageIds = 'preferShorthand';\n\nexport default createRule<[], MessageIds>({\n name: 'prefer-shorthand-property',\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Suggest tasty shorthand when a native CSS property with a tasty alternative is used',\n },\n messages: {\n preferShorthand:\n \"Prefer tasty shorthand '{{alternative}}' instead of '{{native}}'.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\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' || prop.computed) continue;\n\n const key = getKeyName(prop.key);\n if (key === null) continue;\n\n const mapping = SHORTHAND_MAPPING[key];\n if (mapping) {\n context.report({\n node: prop.key,\n messageId: 'preferShorthand',\n data: { native: key, alternative: mapping.hint },\n });\n }\n }\n },\n };\n },\n});\n"],"mappings":";;;;;;AAQA,wCAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aACE,uFACH;EACD,UAAU,EACR,iBACE,qEACH;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;AAErC,SAAO;GACL,kBAAkB,MAAM;AACtB,QAAI,YAAY,KAAK;;GAGvB,kCAAkC,MAAiC;AACjE,QAAI,CAAC,IAAI,cAAc,KAAK,CAAE;AAE9B,SAAK,MAAM,QAAQ,KAAK,YAAY;AAClC,SAAI,KAAK,SAAS,cAAc,KAAK,SAAU;KAE/C,MAAM,MAAM,WAAW,KAAK,IAAI;AAChC,SAAI,QAAQ,KAAM;KAElB,MAAM,UAAU,kBAAkB;AAClC,SAAI,QACF,SAAQ,OAAO;MACb,MAAM,KAAK;MACX,WAAW;MACX,MAAM;OAAE,QAAQ;OAAK,aAAa,QAAQ;OAAM;MACjD,CAAC;;;GAIT;;CAEJ,CAAC"}
1
+ {"version":3,"file":"prefer-shorthand-property.js","names":[],"sources":["../../src/rules/prefer-shorthand-property.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';\nimport { SHORTHAND_MAPPING } from '../constants.js';\n\ntype MessageIds = 'preferShorthand';\n\nexport default createRule<[], MessageIds>({\n name: 'prefer-shorthand-property',\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Suggest tasty shorthand when a native CSS property with a tasty alternative is used',\n },\n messages: {\n preferShorthand:\n \"Prefer tasty shorthand '{{alternative}}' instead of '{{native}}'.\",\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) continue;\n\n const mapping = SHORTHAND_MAPPING[key];\n if (mapping) {\n context.report({\n node: prop.key,\n messageId: 'preferShorthand',\n data: { native: key, alternative: mapping.hint },\n });\n }\n }\n }\n\n return {\n ImportDeclaration(node) {\n ctx.trackImport(node);\n },\n ...styleObjectListeners(handleStyleObject),\n };\n },\n});\n"],"mappings":";;;;;;AAQA,wCAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aACE,uFACH;EACD,UAAU,EACR,iBACE,qEACH;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,KAAM;IAElB,MAAM,UAAU,kBAAkB;AAClC,QAAI,QACF,SAAQ,OAAO;KACb,MAAM,KAAK;KACX,WAAW;KACX,MAAM;MAAE,QAAQ;MAAK,aAAa,QAAQ;MAAM;KACjD,CAAC;;;AAKR,SAAO;GACL,kBAAkB,MAAM;AACtB,QAAI,YAAY,KAAK;;GAEvB,GAAG,qBAAqB,kBAAkB;GAC3C;;CAEJ,CAAC"}
@@ -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/require-default-state.ts
6
6
  var require_default_state_default = createRule({
@@ -14,30 +14,31 @@ var require_default_state_default = createRule({
14
14
  defaultOptions: [],
15
15
  create(context) {
16
16
  const ctx = new TastyContext(context);
17
+ function handleStyleObject(node) {
18
+ const styleCtx = ctx.getStyleContext(node);
19
+ if (!styleCtx) return;
20
+ if (styleCtx.isExtending) return;
21
+ for (const prop of node.properties) {
22
+ if (prop.type !== "Property" || prop.computed) continue;
23
+ const key = getKeyName(prop.key);
24
+ if (key === null) continue;
25
+ if (/^[A-Z@&$#]/.test(key)) continue;
26
+ if (prop.value.type !== "ObjectExpression") continue;
27
+ if (!prop.value.properties.some((p) => {
28
+ if (p.type !== "Property") return false;
29
+ return (p.key.type === "Literal" ? p.key.value : null) === "";
30
+ })) context.report({
31
+ node: prop.value,
32
+ messageId: "missingDefaultState",
33
+ data: { property: key }
34
+ });
35
+ }
36
+ }
17
37
  return {
18
38
  ImportDeclaration(node) {
19
39
  ctx.trackImport(node);
20
40
  },
21
- "CallExpression ObjectExpression"(node) {
22
- const styleCtx = ctx.getStyleContext(node);
23
- if (!styleCtx) return;
24
- if (styleCtx.isExtending) return;
25
- for (const prop of node.properties) {
26
- if (prop.type !== "Property" || prop.computed) continue;
27
- const key = getKeyName(prop.key);
28
- if (key === null) continue;
29
- if (/^[A-Z@&$#]/.test(key)) continue;
30
- if (prop.value.type !== "ObjectExpression") continue;
31
- if (!prop.value.properties.some((p) => {
32
- if (p.type !== "Property") return false;
33
- return (p.key.type === "Literal" ? p.key.value : null) === "";
34
- })) context.report({
35
- node: prop.value,
36
- messageId: "missingDefaultState",
37
- data: { property: key }
38
- });
39
- }
40
- }
41
+ ...styleObjectListeners(handleStyleObject)
41
42
  };
42
43
  }
43
44
  });
@@ -1 +1 @@
1
- {"version":3,"file":"require-default-state.js","names":[],"sources":["../../src/rules/require-default-state.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 = 'missingDefaultState';\n\nexport default createRule<[], MessageIds>({\n name: 'require-default-state',\n meta: {\n type: 'suggestion',\n docs: {\n description:\n \"Warn when a state mapping object doesn't have a default ('') key\",\n },\n messages: {\n missingDefaultState:\n \"State mapping for '{{property}}' has no default ('') value.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n\n return {\n ImportDeclaration(node) {\n ctx.trackImport(node);\n },\n\n 'CallExpression ObjectExpression'(node: TSESTree.ObjectExpression) {\n const styleCtx = ctx.getStyleContext(node);\n if (!styleCtx) return;\n\n // Skip if extending (omitting '' is intentional)\n if (styleCtx.isExtending) 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) continue;\n\n // Skip sub-elements and special keys\n if (/^[A-Z@&$#]/.test(key)) continue;\n\n if (prop.value.type !== 'ObjectExpression') continue;\n\n // Check if this object has a '' key\n const hasDefault = prop.value.properties.some((p) => {\n if (p.type !== 'Property') return false;\n const stateKey = p.key.type === 'Literal' ? p.key.value : null;\n return stateKey === '';\n });\n\n if (!hasDefault) {\n context.report({\n node: prop.value,\n messageId: 'missingDefaultState',\n data: { property: key },\n });\n }\n }\n },\n };\n },\n});\n"],"mappings":";;;;;AAOA,oCAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aACE,oEACH;EACD,UAAU,EACR,qBACE,+DACH;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;AAErC,SAAO;GACL,kBAAkB,MAAM;AACtB,QAAI,YAAY,KAAK;;GAGvB,kCAAkC,MAAiC;IACjE,MAAM,WAAW,IAAI,gBAAgB,KAAK;AAC1C,QAAI,CAAC,SAAU;AAGf,QAAI,SAAS,YAAa;AAE1B,SAAK,MAAM,QAAQ,KAAK,YAAY;AAClC,SAAI,KAAK,SAAS,cAAc,KAAK,SAAU;KAE/C,MAAM,MAAM,WAAW,KAAK,IAAI;AAChC,SAAI,QAAQ,KAAM;AAGlB,SAAI,aAAa,KAAK,IAAI,CAAE;AAE5B,SAAI,KAAK,MAAM,SAAS,mBAAoB;AAS5C,SAAI,CANe,KAAK,MAAM,WAAW,MAAM,MAAM;AACnD,UAAI,EAAE,SAAS,WAAY,QAAO;AAElC,cADiB,EAAE,IAAI,SAAS,YAAY,EAAE,IAAI,QAAQ,UACtC;OACpB,CAGA,SAAQ,OAAO;MACb,MAAM,KAAK;MACX,WAAW;MACX,MAAM,EAAE,UAAU,KAAK;MACxB,CAAC;;;GAIT;;CAEJ,CAAC"}
1
+ {"version":3,"file":"require-default-state.js","names":[],"sources":["../../src/rules/require-default-state.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 = 'missingDefaultState';\n\nexport default createRule<[], MessageIds>({\n name: 'require-default-state',\n meta: {\n type: 'suggestion',\n docs: {\n description:\n \"Warn when a state mapping object doesn't have a default ('') key\",\n },\n messages: {\n missingDefaultState:\n \"State mapping for '{{property}}' has no default ('') value.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n\n function handleStyleObject(node: TSESTree.ObjectExpression) {\n const styleCtx = ctx.getStyleContext(node);\n if (!styleCtx) return;\n\n // Skip if extending (omitting '' is intentional)\n if (styleCtx.isExtending) 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) continue;\n\n // Skip sub-elements and special keys\n if (/^[A-Z@&$#]/.test(key)) continue;\n\n if (prop.value.type !== 'ObjectExpression') continue;\n\n // Check if this object has a '' key\n const hasDefault = prop.value.properties.some((p) => {\n if (p.type !== 'Property') return false;\n const stateKey = p.key.type === 'Literal' ? p.key.value : null;\n return stateKey === '';\n });\n\n if (!hasDefault) {\n context.report({\n node: prop.value,\n messageId: 'missingDefaultState',\n data: { property: key },\n });\n }\n }\n }\n\n return {\n ImportDeclaration(node) {\n ctx.trackImport(node);\n },\n ...styleObjectListeners(handleStyleObject),\n };\n },\n});\n"],"mappings":";;;;;AAOA,oCAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aACE,oEACH;EACD,UAAU,EACR,qBACE,+DACH;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;EAErC,SAAS,kBAAkB,MAAiC;GAC1D,MAAM,WAAW,IAAI,gBAAgB,KAAK;AAC1C,OAAI,CAAC,SAAU;AAGf,OAAI,SAAS,YAAa;AAE1B,QAAK,MAAM,QAAQ,KAAK,YAAY;AAClC,QAAI,KAAK,SAAS,cAAc,KAAK,SAAU;IAE/C,MAAM,MAAM,WAAW,KAAK,IAAI;AAChC,QAAI,QAAQ,KAAM;AAGlB,QAAI,aAAa,KAAK,IAAI,CAAE;AAE5B,QAAI,KAAK,MAAM,SAAS,mBAAoB;AAS5C,QAAI,CANe,KAAK,MAAM,WAAW,MAAM,MAAM;AACnD,SAAI,EAAE,SAAS,WAAY,QAAO;AAElC,aADiB,EAAE,IAAI,SAAS,YAAY,EAAE,IAAI,QAAQ,UACtC;MACpB,CAGA,SAAQ,OAAO;KACb,MAAM,KAAK;KACX,WAAW;KACX,MAAM,EAAE,UAAU,KAAK;KACxB,CAAC;;;AAKR,SAAO;GACL,kBAAkB,MAAM;AACtB,QAAI,YAAY,KAAK;;GAEvB,GAAG,qBAAqB,kBAAkB;GAC3C;;CAEJ,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { createRule } from "../create-rule.js";
2
- import { TastyContext } from "../context.js";
3
2
  import { isStaticValue } from "../utils.js";
3
+ import { TastyContext, styleObjectListeners } from "../context.js";
4
4
 
5
5
  //#region src/rules/static-no-dynamic-values.ts
6
6
  var static_no_dynamic_values_default = createRule({
@@ -36,15 +36,16 @@ var static_no_dynamic_values_default = createRule({
36
36
  });
37
37
  }
38
38
  }
39
+ function handleStyleObject(node) {
40
+ const styleCtx = ctx.getStyleContext(node);
41
+ if (!styleCtx || !styleCtx.isStaticCall) return;
42
+ checkProperties(node);
43
+ }
39
44
  return {
40
45
  ImportDeclaration(node) {
41
46
  ctx.trackImport(node);
42
47
  },
43
- "CallExpression ObjectExpression"(node) {
44
- const styleCtx = ctx.getStyleContext(node);
45
- if (!styleCtx || !styleCtx.isStaticCall) return;
46
- checkProperties(node);
47
- }
48
+ ...styleObjectListeners(handleStyleObject)
48
49
  };
49
50
  }
50
51
  });
@@ -1 +1 @@
1
- {"version":3,"file":"static-no-dynamic-values.js","names":[],"sources":["../../src/rules/static-no-dynamic-values.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport { createRule } from '../create-rule.js';\nimport { TastyContext } from '../context.js';\nimport { isStaticValue } from '../utils.js';\n\ntype MessageIds = 'dynamicValue';\n\nexport default createRule<[], MessageIds>({\n name: 'static-no-dynamic-values',\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Ensure all values in tastyStatic() calls are static literals',\n },\n messages: {\n dynamicValue:\n 'tastyStatic() values must be static (string, number, boolean, null, or objects/arrays of those). Dynamic expressions are not supported.',\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n\n function checkProperties(node: TSESTree.ObjectExpression): void {\n for (const prop of node.properties) {\n if (prop.type === 'SpreadElement') {\n context.report({ node: prop, messageId: 'dynamicValue' });\n continue;\n }\n\n if (prop.computed) {\n context.report({ node: prop.key, messageId: 'dynamicValue' });\n continue;\n }\n\n if (!isStaticValue(prop.value)) {\n context.report({ node: prop.value, messageId: 'dynamicValue' });\n }\n }\n }\n\n return {\n ImportDeclaration(node) {\n ctx.trackImport(node);\n },\n\n 'CallExpression ObjectExpression'(node: TSESTree.ObjectExpression) {\n const styleCtx = ctx.getStyleContext(node);\n if (!styleCtx || !styleCtx.isStaticCall) return;\n\n checkProperties(node);\n },\n };\n },\n});\n"],"mappings":";;;;;AAOA,uCAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aACE,gEACH;EACD,UAAU,EACR,cACE,2IACH;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;EAErC,SAAS,gBAAgB,MAAuC;AAC9D,QAAK,MAAM,QAAQ,KAAK,YAAY;AAClC,QAAI,KAAK,SAAS,iBAAiB;AACjC,aAAQ,OAAO;MAAE,MAAM;MAAM,WAAW;MAAgB,CAAC;AACzD;;AAGF,QAAI,KAAK,UAAU;AACjB,aAAQ,OAAO;MAAE,MAAM,KAAK;MAAK,WAAW;MAAgB,CAAC;AAC7D;;AAGF,QAAI,CAAC,cAAc,KAAK,MAAM,CAC5B,SAAQ,OAAO;KAAE,MAAM,KAAK;KAAO,WAAW;KAAgB,CAAC;;;AAKrE,SAAO;GACL,kBAAkB,MAAM;AACtB,QAAI,YAAY,KAAK;;GAGvB,kCAAkC,MAAiC;IACjE,MAAM,WAAW,IAAI,gBAAgB,KAAK;AAC1C,QAAI,CAAC,YAAY,CAAC,SAAS,aAAc;AAEzC,oBAAgB,KAAK;;GAExB;;CAEJ,CAAC"}
1
+ {"version":3,"file":"static-no-dynamic-values.js","names":[],"sources":["../../src/rules/static-no-dynamic-values.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport { createRule } from '../create-rule.js';\nimport { TastyContext, styleObjectListeners } from '../context.js';\nimport { isStaticValue } from '../utils.js';\n\ntype MessageIds = 'dynamicValue';\n\nexport default createRule<[], MessageIds>({\n name: 'static-no-dynamic-values',\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Ensure all values in tastyStatic() calls are static literals',\n },\n messages: {\n dynamicValue:\n 'tastyStatic() values must be static (string, number, boolean, null, or objects/arrays of those). Dynamic expressions are not supported.',\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n\n function checkProperties(node: TSESTree.ObjectExpression): void {\n for (const prop of node.properties) {\n if (prop.type === 'SpreadElement') {\n context.report({ node: prop, messageId: 'dynamicValue' });\n continue;\n }\n\n if (prop.computed) {\n context.report({ node: prop.key, messageId: 'dynamicValue' });\n continue;\n }\n\n if (!isStaticValue(prop.value)) {\n context.report({ node: prop.value, messageId: 'dynamicValue' });\n }\n }\n }\n\n function handleStyleObject(node: TSESTree.ObjectExpression) {\n const styleCtx = ctx.getStyleContext(node);\n if (!styleCtx || !styleCtx.isStaticCall) return;\n\n checkProperties(node);\n }\n\n return {\n ImportDeclaration(node) {\n ctx.trackImport(node);\n },\n ...styleObjectListeners(handleStyleObject),\n };\n },\n});\n"],"mappings":";;;;;AAOA,uCAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aACE,gEACH;EACD,UAAU,EACR,cACE,2IACH;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;EAErC,SAAS,gBAAgB,MAAuC;AAC9D,QAAK,MAAM,QAAQ,KAAK,YAAY;AAClC,QAAI,KAAK,SAAS,iBAAiB;AACjC,aAAQ,OAAO;MAAE,MAAM;MAAM,WAAW;MAAgB,CAAC;AACzD;;AAGF,QAAI,KAAK,UAAU;AACjB,aAAQ,OAAO;MAAE,MAAM,KAAK;MAAK,WAAW;MAAgB,CAAC;AAC7D;;AAGF,QAAI,CAAC,cAAc,KAAK,MAAM,CAC5B,SAAQ,OAAO;KAAE,MAAM,KAAK;KAAO,WAAW;KAAgB,CAAC;;;EAKrE,SAAS,kBAAkB,MAAiC;GAC1D,MAAM,WAAW,IAAI,gBAAgB,KAAK;AAC1C,OAAI,CAAC,YAAY,CAAC,SAAS,aAAc;AAEzC,mBAAgB,KAAK;;AAGvB,SAAO;GACL,kBAAkB,MAAM;AACtB,QAAI,YAAY,KAAK;;GAEvB,GAAG,qBAAqB,kBAAkB;GAC3C;;CAEJ,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { createRule } from "../create-rule.js";
2
- import { TastyContext } from "../context.js";
3
2
  import { getStringValue, isValidSelector } from "../utils.js";
3
+ import { TastyContext } from "../context.js";
4
4
 
5
5
  //#region src/rules/static-valid-selector.ts
6
6
  var static_valid_selector_default = createRule({
@@ -1,7 +1,7 @@
1
1
  import { createRule } from "../create-rule.js";
2
2
  import { BOOLEAN_TRUE_PROPERTIES } from "../constants.js";
3
- import { TastyContext } from "../context.js";
4
3
  import { getKeyName } from "../utils.js";
4
+ import { TastyContext, styleObjectListeners } from "../context.js";
5
5
 
6
6
  //#region src/rules/valid-boolean-property.ts
7
7
  var valid_boolean_property_default = createRule({
@@ -15,27 +15,28 @@ var valid_boolean_property_default = createRule({
15
15
  defaultOptions: [],
16
16
  create(context) {
17
17
  const ctx = new TastyContext(context);
18
+ function handleStyleObject(node) {
19
+ if (!ctx.isStyleObject(node)) return;
20
+ for (const prop of node.properties) {
21
+ if (prop.type !== "Property" || prop.computed) continue;
22
+ const key = getKeyName(prop.key);
23
+ if (key === null) continue;
24
+ if (/^[A-Z@&$#]/.test(key)) continue;
25
+ if (prop.value.type === "Literal" && prop.value.value === true && !BOOLEAN_TRUE_PROPERTIES.has(key)) context.report({
26
+ node: prop.value,
27
+ messageId: "invalidBooleanTrue",
28
+ data: {
29
+ name: key,
30
+ allowed: [...BOOLEAN_TRUE_PROPERTIES].join(", ")
31
+ }
32
+ });
33
+ }
34
+ }
18
35
  return {
19
36
  ImportDeclaration(node) {
20
37
  ctx.trackImport(node);
21
38
  },
22
- "CallExpression ObjectExpression"(node) {
23
- if (!ctx.isStyleObject(node)) return;
24
- for (const prop of node.properties) {
25
- if (prop.type !== "Property" || prop.computed) continue;
26
- const key = getKeyName(prop.key);
27
- if (key === null) continue;
28
- if (/^[A-Z@&$#]/.test(key)) continue;
29
- if (prop.value.type === "Literal" && prop.value.value === true && !BOOLEAN_TRUE_PROPERTIES.has(key)) context.report({
30
- node: prop.value,
31
- messageId: "invalidBooleanTrue",
32
- data: {
33
- name: key,
34
- allowed: [...BOOLEAN_TRUE_PROPERTIES].join(", ")
35
- }
36
- });
37
- }
38
- }
39
+ ...styleObjectListeners(handleStyleObject)
39
40
  };
40
41
  }
41
42
  });
@@ -1 +1 @@
1
- {"version":3,"file":"valid-boolean-property.js","names":[],"sources":["../../src/rules/valid-boolean-property.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';\nimport { BOOLEAN_TRUE_PROPERTIES } from '../constants.js';\n\ntype MessageIds = 'invalidBooleanTrue';\n\nexport default createRule<[], MessageIds>({\n name: 'valid-boolean-property',\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Validate that true/false values are only used on properties that support them',\n },\n messages: {\n invalidBooleanTrue:\n \"Property '{{name}}' does not accept boolean true. Only these properties support it: {{allowed}}.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\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' || prop.computed) continue;\n\n const key = getKeyName(prop.key);\n if (key === null) continue;\n\n // Skip sub-elements and special keys\n if (/^[A-Z@&$#]/.test(key)) continue;\n\n // Check for true literal — false is always valid (tombstone)\n if (\n prop.value.type === 'Literal' &&\n prop.value.value === true &&\n !BOOLEAN_TRUE_PROPERTIES.has(key)\n ) {\n context.report({\n node: prop.value,\n messageId: 'invalidBooleanTrue',\n data: {\n name: key,\n allowed: [...BOOLEAN_TRUE_PROPERTIES].join(', '),\n },\n });\n }\n }\n },\n };\n },\n});\n"],"mappings":";;;;;;AAQA,qCAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aACE,iFACH;EACD,UAAU,EACR,oBACE,oGACH;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;AAErC,SAAO;GACL,kBAAkB,MAAM;AACtB,QAAI,YAAY,KAAK;;GAGvB,kCAAkC,MAAiC;AACjE,QAAI,CAAC,IAAI,cAAc,KAAK,CAAE;AAE9B,SAAK,MAAM,QAAQ,KAAK,YAAY;AAClC,SAAI,KAAK,SAAS,cAAc,KAAK,SAAU;KAE/C,MAAM,MAAM,WAAW,KAAK,IAAI;AAChC,SAAI,QAAQ,KAAM;AAGlB,SAAI,aAAa,KAAK,IAAI,CAAE;AAG5B,SACE,KAAK,MAAM,SAAS,aACpB,KAAK,MAAM,UAAU,QACrB,CAAC,wBAAwB,IAAI,IAAI,CAEjC,SAAQ,OAAO;MACb,MAAM,KAAK;MACX,WAAW;MACX,MAAM;OACJ,MAAM;OACN,SAAS,CAAC,GAAG,wBAAwB,CAAC,KAAK,KAAK;OACjD;MACF,CAAC;;;GAIT;;CAEJ,CAAC"}
1
+ {"version":3,"file":"valid-boolean-property.js","names":[],"sources":["../../src/rules/valid-boolean-property.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';\nimport { BOOLEAN_TRUE_PROPERTIES } from '../constants.js';\n\ntype MessageIds = 'invalidBooleanTrue';\n\nexport default createRule<[], MessageIds>({\n name: 'valid-boolean-property',\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Validate that true/false values are only used on properties that support them',\n },\n messages: {\n invalidBooleanTrue:\n \"Property '{{name}}' does not accept boolean true. Only these properties support it: {{allowed}}.\",\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) continue;\n\n // Skip sub-elements and special keys\n if (/^[A-Z@&$#]/.test(key)) continue;\n\n // Check for true literal — false is always valid (tombstone)\n if (\n prop.value.type === 'Literal' &&\n prop.value.value === true &&\n !BOOLEAN_TRUE_PROPERTIES.has(key)\n ) {\n context.report({\n node: prop.value,\n messageId: 'invalidBooleanTrue',\n data: {\n name: key,\n allowed: [...BOOLEAN_TRUE_PROPERTIES].join(', '),\n },\n });\n }\n }\n }\n\n return {\n ImportDeclaration(node) {\n ctx.trackImport(node);\n },\n\n ...styleObjectListeners(handleStyleObject),\n };\n },\n});\n"],"mappings":";;;;;;AAQA,qCAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aACE,iFACH;EACD,UAAU,EACR,oBACE,oGACH;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,KAAM;AAGlB,QAAI,aAAa,KAAK,IAAI,CAAE;AAG5B,QACE,KAAK,MAAM,SAAS,aACpB,KAAK,MAAM,UAAU,QACrB,CAAC,wBAAwB,IAAI,IAAI,CAEjC,SAAQ,OAAO;KACb,MAAM,KAAK;KACX,WAAW;KACX,MAAM;MACJ,MAAM;MACN,SAAS,CAAC,GAAG,wBAAwB,CAAC,KAAK,KAAK;MACjD;KACF,CAAC;;;AAKR,SAAO;GACL,kBAAkB,MAAM;AACtB,QAAI,YAAY,KAAK;;GAGvB,GAAG,qBAAqB,kBAAkB;GAC3C;;CAEJ,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { createRule } from "../create-rule.js";
2
- import { TastyContext } from "../context.js";
3
2
  import { getKeyName, getStringValue, isRawHexColor, validateColorTokenSyntax } from "../utils.js";
3
+ import { TastyContext, styleObjectListeners } from "../context.js";
4
4
 
5
5
  //#region src/rules/valid-color-token.ts
6
6
  var valid_color_token_default = createRule({
@@ -18,6 +18,7 @@ var valid_color_token_default = createRule({
18
18
  create(context) {
19
19
  const ctx = new TastyContext(context);
20
20
  const fileColorTokens = /* @__PURE__ */ new Set();
21
+ const pendingChecks = [];
21
22
  function collectLocalTokens(node) {
22
23
  for (const prop of node.properties) {
23
24
  if (prop.type !== "Property" || prop.computed) continue;
@@ -46,12 +47,26 @@ var valid_color_token_default = createRule({
46
47
  if (ctx.config.tokens === false) continue;
47
48
  const baseName = token.startsWith("##") ? "#" + token.slice(2).split(".")[0] : "#" + token.slice(1).split(".")[0];
48
49
  if (baseName === "#current") continue;
49
- if (!fileColorTokens.has(baseName) && !(Array.isArray(ctx.config.tokens) && ctx.config.tokens.includes(baseName))) {
50
- if (Array.isArray(ctx.config.tokens) && ctx.config.tokens.length > 0) context.report({
51
- node,
52
- messageId: "unknownToken",
53
- data: { token }
54
- });
50
+ pendingChecks.push({
51
+ token,
52
+ baseName,
53
+ node
54
+ });
55
+ }
56
+ }
57
+ function handleStyleObject(node) {
58
+ if (!ctx.isStyleObject(node)) return;
59
+ collectLocalTokens(node);
60
+ for (const prop of node.properties) {
61
+ if (prop.type !== "Property") continue;
62
+ if (prop.value.type === "Literal") {
63
+ const str = getStringValue(prop.value);
64
+ if (str && str.includes("#")) checkColorTokensInValue(str, prop.value);
65
+ }
66
+ if (prop.value.type === "ObjectExpression") for (const stateProp of prop.value.properties) {
67
+ if (stateProp.type !== "Property") continue;
68
+ const str = getStringValue(stateProp.value);
69
+ if (str && str.includes("#")) checkColorTokensInValue(str, stateProp.value);
55
70
  }
56
71
  }
57
72
  }
@@ -59,20 +74,17 @@ var valid_color_token_default = createRule({
59
74
  ImportDeclaration(node) {
60
75
  ctx.trackImport(node);
61
76
  },
62
- "CallExpression ObjectExpression"(node) {
63
- if (!ctx.isStyleObject(node)) return;
64
- collectLocalTokens(node);
65
- for (const prop of node.properties) {
66
- if (prop.type !== "Property") continue;
67
- if (prop.value.type === "Literal") {
68
- const str = getStringValue(prop.value);
69
- if (str && str.includes("#")) checkColorTokensInValue(str, prop.value);
70
- }
71
- if (prop.value.type === "ObjectExpression") for (const stateProp of prop.value.properties) {
72
- if (stateProp.type !== "Property") continue;
73
- const str = getStringValue(stateProp.value);
74
- if (str && str.includes("#")) checkColorTokensInValue(str, stateProp.value);
75
- }
77
+ ...styleObjectListeners(handleStyleObject),
78
+ "Program:exit"() {
79
+ if (!Array.isArray(ctx.config.tokens) || ctx.config.tokens.length === 0) return;
80
+ for (const { token, baseName, node } of pendingChecks) {
81
+ if (fileColorTokens.has(baseName)) continue;
82
+ if (ctx.config.tokens.includes(baseName)) continue;
83
+ context.report({
84
+ node,
85
+ messageId: "unknownToken",
86
+ data: { token }
87
+ });
76
88
  }
77
89
  }
78
90
  };
@@ -1 +1 @@
1
- {"version":3,"file":"valid-color-token.js","names":[],"sources":["../../src/rules/valid-color-token.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport { createRule } from '../create-rule.js';\nimport { TastyContext } from '../context.js';\nimport {\n getKeyName,\n getStringValue,\n validateColorTokenSyntax,\n isRawHexColor,\n} from '../utils.js';\n\ntype MessageIds = 'invalidSyntax' | 'unknownToken';\n\nexport default createRule<[], MessageIds>({\n name: 'valid-color-token',\n meta: {\n type: 'problem',\n docs: {\n description: 'Validate color token syntax and existence',\n },\n messages: {\n invalidSyntax: \"Invalid color token '{{token}}': {{reason}}.\",\n unknownToken: \"Unknown color token '{{token}}'.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n const fileColorTokens = new Set<string>();\n\n function collectLocalTokens(node: TSESTree.ObjectExpression): void {\n for (const prop of node.properties) {\n if (prop.type !== 'Property' || prop.computed) continue;\n const key = getKeyName(prop.key);\n if (key && key.startsWith('#') && !key.startsWith('##')) {\n fileColorTokens.add(key);\n }\n }\n }\n\n function checkColorTokensInValue(value: string, node: TSESTree.Node): void {\n // Match color token references: #name or #name.N or ##name\n const tokenRegex = /##?[a-zA-Z][a-zA-Z0-9-]*(?:\\.\\$?[a-zA-Z0-9-]+)?/g;\n let match;\n\n while ((match = tokenRegex.exec(value)) !== null) {\n const token = match[0];\n\n // Skip raw hex colors\n if (isRawHexColor(token)) continue;\n\n // Check syntax\n const syntaxError = validateColorTokenSyntax(token);\n if (syntaxError) {\n context.report({\n node,\n messageId: 'invalidSyntax',\n data: { token, reason: syntaxError },\n });\n continue;\n }\n\n // Check existence (if tokens config is not false)\n if (ctx.config.tokens === false) continue;\n\n const baseName = token.startsWith('##')\n ? '#' + token.slice(2).split('.')[0]\n : '#' + token.slice(1).split('.')[0];\n\n if (baseName === '#current') continue;\n\n if (\n !fileColorTokens.has(baseName) &&\n !(\n Array.isArray(ctx.config.tokens) &&\n ctx.config.tokens.includes(baseName)\n )\n ) {\n // Only warn if config.tokens is a non-empty array\n if (\n Array.isArray(ctx.config.tokens) &&\n ctx.config.tokens.length > 0\n ) {\n context.report({\n node,\n messageId: 'unknownToken',\n data: { token },\n });\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 collectLocalTokens(node);\n\n for (const prop of node.properties) {\n if (prop.type !== 'Property') continue;\n\n // Check string values for color tokens\n if (prop.value.type === 'Literal') {\n const str = getStringValue(prop.value);\n if (str && str.includes('#')) {\n checkColorTokensInValue(str, prop.value);\n }\n }\n\n // Check state map values\n if (prop.value.type === 'ObjectExpression') {\n for (const stateProp of prop.value.properties) {\n if (stateProp.type !== 'Property') continue;\n const str = getStringValue(stateProp.value);\n if (str && str.includes('#')) {\n checkColorTokensInValue(str, stateProp.value);\n }\n }\n }\n }\n },\n };\n },\n});\n"],"mappings":";;;;;AAYA,gCAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aAAa,6CACd;EACD,UAAU;GACR,eAAe;GACf,cAAc;GACf;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;EACrC,MAAM,kCAAkB,IAAI,KAAa;EAEzC,SAAS,mBAAmB,MAAuC;AACjE,QAAK,MAAM,QAAQ,KAAK,YAAY;AAClC,QAAI,KAAK,SAAS,cAAc,KAAK,SAAU;IAC/C,MAAM,MAAM,WAAW,KAAK,IAAI;AAChC,QAAI,OAAO,IAAI,WAAW,IAAI,IAAI,CAAC,IAAI,WAAW,KAAK,CACrD,iBAAgB,IAAI,IAAI;;;EAK9B,SAAS,wBAAwB,OAAe,MAA2B;GAEzE,MAAM,aAAa;GACnB,IAAI;AAEJ,WAAQ,QAAQ,WAAW,KAAK,MAAM,MAAM,MAAM;IAChD,MAAM,QAAQ,MAAM;AAGpB,QAAI,cAAc,MAAM,CAAE;IAG1B,MAAM,cAAc,yBAAyB,MAAM;AACnD,QAAI,aAAa;AACf,aAAQ,OAAO;MACb;MACA,WAAW;MACX,MAAM;OAAE;OAAO,QAAQ;OAAa;MACrC,CAAC;AACF;;AAIF,QAAI,IAAI,OAAO,WAAW,MAAO;IAEjC,MAAM,WAAW,MAAM,WAAW,KAAK,GACnC,MAAM,MAAM,MAAM,EAAE,CAAC,MAAM,IAAI,CAAC,KAChC,MAAM,MAAM,MAAM,EAAE,CAAC,MAAM,IAAI,CAAC;AAEpC,QAAI,aAAa,WAAY;AAE7B,QACE,CAAC,gBAAgB,IAAI,SAAS,IAC9B,EACE,MAAM,QAAQ,IAAI,OAAO,OAAO,IAChC,IAAI,OAAO,OAAO,SAAS,SAAS,GAItC;SACE,MAAM,QAAQ,IAAI,OAAO,OAAO,IAChC,IAAI,OAAO,OAAO,SAAS,EAE3B,SAAQ,OAAO;MACb;MACA,WAAW;MACX,MAAM,EAAE,OAAO;MAChB,CAAC;;;;AAMV,SAAO;GACL,kBAAkB,MAAM;AACtB,QAAI,YAAY,KAAK;;GAGvB,kCAAkC,MAAiC;AACjE,QAAI,CAAC,IAAI,cAAc,KAAK,CAAE;AAC9B,uBAAmB,KAAK;AAExB,SAAK,MAAM,QAAQ,KAAK,YAAY;AAClC,SAAI,KAAK,SAAS,WAAY;AAG9B,SAAI,KAAK,MAAM,SAAS,WAAW;MACjC,MAAM,MAAM,eAAe,KAAK,MAAM;AACtC,UAAI,OAAO,IAAI,SAAS,IAAI,CAC1B,yBAAwB,KAAK,KAAK,MAAM;;AAK5C,SAAI,KAAK,MAAM,SAAS,mBACtB,MAAK,MAAM,aAAa,KAAK,MAAM,YAAY;AAC7C,UAAI,UAAU,SAAS,WAAY;MACnC,MAAM,MAAM,eAAe,UAAU,MAAM;AAC3C,UAAI,OAAO,IAAI,SAAS,IAAI,CAC1B,yBAAwB,KAAK,UAAU,MAAM;;;;GAMxD;;CAEJ,CAAC"}
1
+ {"version":3,"file":"valid-color-token.js","names":[],"sources":["../../src/rules/valid-color-token.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport { createRule } from '../create-rule.js';\nimport { TastyContext, styleObjectListeners } from '../context.js';\nimport {\n getKeyName,\n getStringValue,\n validateColorTokenSyntax,\n isRawHexColor,\n} from '../utils.js';\n\ntype MessageIds = 'invalidSyntax' | 'unknownToken';\n\ninterface PendingExistenceCheck {\n token: string;\n baseName: string;\n node: TSESTree.Node;\n}\n\nexport default createRule<[], MessageIds>({\n name: 'valid-color-token',\n meta: {\n type: 'problem',\n docs: {\n description: 'Validate color token syntax and existence',\n },\n messages: {\n invalidSyntax: \"Invalid color token '{{token}}': {{reason}}.\",\n unknownToken: \"Unknown color token '{{token}}'.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n const fileColorTokens = new Set<string>();\n const pendingChecks: PendingExistenceCheck[] = [];\n\n function collectLocalTokens(node: TSESTree.ObjectExpression): void {\n for (const prop of node.properties) {\n if (prop.type !== 'Property' || prop.computed) continue;\n const key = getKeyName(prop.key);\n if (key && key.startsWith('#') && !key.startsWith('##')) {\n fileColorTokens.add(key);\n }\n }\n }\n\n function checkColorTokensInValue(\n value: string,\n node: TSESTree.Node,\n ): void {\n const tokenRegex = /##?[a-zA-Z][a-zA-Z0-9-]*(?:\\.\\$?[a-zA-Z0-9-]+)?/g;\n let match;\n\n while ((match = tokenRegex.exec(value)) !== null) {\n const token = match[0];\n\n if (isRawHexColor(token)) continue;\n\n const syntaxError = validateColorTokenSyntax(token);\n if (syntaxError) {\n context.report({\n node,\n messageId: 'invalidSyntax',\n data: { token, reason: syntaxError },\n });\n continue;\n }\n\n if (ctx.config.tokens === false) continue;\n\n const baseName = token.startsWith('##')\n ? '#' + token.slice(2).split('.')[0]\n : '#' + token.slice(1).split('.')[0];\n\n if (baseName === '#current') continue;\n\n pendingChecks.push({ token, baseName, node });\n }\n }\n\n function handleStyleObject(node: TSESTree.ObjectExpression) {\n if (!ctx.isStyleObject(node)) return;\n collectLocalTokens(node);\n\n for (const prop of node.properties) {\n if (prop.type !== 'Property') continue;\n\n if (prop.value.type === 'Literal') {\n const str = getStringValue(prop.value);\n if (str && str.includes('#')) {\n checkColorTokensInValue(str, prop.value);\n }\n }\n\n if (prop.value.type === 'ObjectExpression') {\n for (const stateProp of prop.value.properties) {\n if (stateProp.type !== 'Property') continue;\n const str = getStringValue(stateProp.value);\n if (str && str.includes('#')) {\n checkColorTokensInValue(str, stateProp.value);\n }\n }\n }\n }\n }\n\n return {\n ImportDeclaration(node) {\n ctx.trackImport(node);\n },\n\n ...styleObjectListeners(handleStyleObject),\n\n 'Program:exit'() {\n if (\n !Array.isArray(ctx.config.tokens) ||\n ctx.config.tokens.length === 0\n ) {\n return;\n }\n\n for (const { token, baseName, node } of pendingChecks) {\n if (fileColorTokens.has(baseName)) continue;\n if (ctx.config.tokens.includes(baseName)) continue;\n\n context.report({\n node,\n messageId: 'unknownToken',\n data: { token },\n });\n }\n },\n };\n },\n});\n"],"mappings":";;;;;AAkBA,gCAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aAAa,6CACd;EACD,UAAU;GACR,eAAe;GACf,cAAc;GACf;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;EACrC,MAAM,kCAAkB,IAAI,KAAa;EACzC,MAAM,gBAAyC,EAAE;EAEjD,SAAS,mBAAmB,MAAuC;AACjE,QAAK,MAAM,QAAQ,KAAK,YAAY;AAClC,QAAI,KAAK,SAAS,cAAc,KAAK,SAAU;IAC/C,MAAM,MAAM,WAAW,KAAK,IAAI;AAChC,QAAI,OAAO,IAAI,WAAW,IAAI,IAAI,CAAC,IAAI,WAAW,KAAK,CACrD,iBAAgB,IAAI,IAAI;;;EAK9B,SAAS,wBACP,OACA,MACM;GACN,MAAM,aAAa;GACnB,IAAI;AAEJ,WAAQ,QAAQ,WAAW,KAAK,MAAM,MAAM,MAAM;IAChD,MAAM,QAAQ,MAAM;AAEpB,QAAI,cAAc,MAAM,CAAE;IAE1B,MAAM,cAAc,yBAAyB,MAAM;AACnD,QAAI,aAAa;AACf,aAAQ,OAAO;MACb;MACA,WAAW;MACX,MAAM;OAAE;OAAO,QAAQ;OAAa;MACrC,CAAC;AACF;;AAGF,QAAI,IAAI,OAAO,WAAW,MAAO;IAEjC,MAAM,WAAW,MAAM,WAAW,KAAK,GACnC,MAAM,MAAM,MAAM,EAAE,CAAC,MAAM,IAAI,CAAC,KAChC,MAAM,MAAM,MAAM,EAAE,CAAC,MAAM,IAAI,CAAC;AAEpC,QAAI,aAAa,WAAY;AAE7B,kBAAc,KAAK;KAAE;KAAO;KAAU;KAAM,CAAC;;;EAIjD,SAAS,kBAAkB,MAAiC;AAC1D,OAAI,CAAC,IAAI,cAAc,KAAK,CAAE;AAC9B,sBAAmB,KAAK;AAExB,QAAK,MAAM,QAAQ,KAAK,YAAY;AAClC,QAAI,KAAK,SAAS,WAAY;AAE9B,QAAI,KAAK,MAAM,SAAS,WAAW;KACjC,MAAM,MAAM,eAAe,KAAK,MAAM;AACtC,SAAI,OAAO,IAAI,SAAS,IAAI,CAC1B,yBAAwB,KAAK,KAAK,MAAM;;AAI5C,QAAI,KAAK,MAAM,SAAS,mBACtB,MAAK,MAAM,aAAa,KAAK,MAAM,YAAY;AAC7C,SAAI,UAAU,SAAS,WAAY;KACnC,MAAM,MAAM,eAAe,UAAU,MAAM;AAC3C,SAAI,OAAO,IAAI,SAAS,IAAI,CAC1B,yBAAwB,KAAK,UAAU,MAAM;;;;AAOvD,SAAO;GACL,kBAAkB,MAAM;AACtB,QAAI,YAAY,KAAK;;GAGvB,GAAG,qBAAqB,kBAAkB;GAE1C,iBAAiB;AACf,QACE,CAAC,MAAM,QAAQ,IAAI,OAAO,OAAO,IACjC,IAAI,OAAO,OAAO,WAAW,EAE7B;AAGF,SAAK,MAAM,EAAE,OAAO,UAAU,UAAU,eAAe;AACrD,SAAI,gBAAgB,IAAI,SAAS,CAAE;AACnC,SAAI,IAAI,OAAO,OAAO,SAAS,SAAS,CAAE;AAE1C,aAAQ,OAAO;MACb;MACA,WAAW;MACX,MAAM,EAAE,OAAO;MAChB,CAAC;;;GAGP;;CAEJ,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { createRule } from "../create-rule.js";
2
- import { TastyContext } from "../context.js";
3
2
  import { getKeyName, getStringValue } from "../utils.js";
3
+ import { TastyContext, styleObjectListeners } from "../context.js";
4
4
 
5
5
  //#region src/rules/valid-custom-property.ts
6
6
  const CUSTOM_PROP_REGEX = /\$\$?[a-zA-Z][a-zA-Z0-9-]*/g;
@@ -19,6 +19,7 @@ var valid_custom_property_default = createRule({
19
19
  create(context) {
20
20
  const ctx = new TastyContext(context);
21
21
  const fileCustomProperties = /* @__PURE__ */ new Set();
22
+ const pendingChecks = [];
22
23
  function collectLocalProperties(node) {
23
24
  for (const prop of node.properties) {
24
25
  if (prop.type !== "Property" || prop.computed) continue;
@@ -33,31 +34,42 @@ var valid_custom_property_default = createRule({
33
34
  while ((match = CUSTOM_PROP_REGEX.exec(value)) !== null) {
34
35
  const token = match[0];
35
36
  const baseName = token.startsWith("$$") ? "$" + token.slice(2) : token;
36
- if (fileCustomProperties.has(baseName)) continue;
37
- if (Array.isArray(ctx.config.tokens) && ctx.config.tokens.includes(baseName)) continue;
38
- if (Array.isArray(ctx.config.tokens) && ctx.config.tokens.length > 0) context.report({
39
- node,
40
- messageId: "unknownProperty",
41
- data: { token }
37
+ pendingChecks.push({
38
+ token,
39
+ baseName,
40
+ node
42
41
  });
43
42
  }
44
43
  }
44
+ function handleStyleObject(node) {
45
+ if (!ctx.isStyleObject(node)) return;
46
+ collectLocalProperties(node);
47
+ for (const prop of node.properties) {
48
+ if (prop.type !== "Property") continue;
49
+ const str = getStringValue(prop.value);
50
+ if (str && str.includes("$")) checkValue(str, prop.value);
51
+ if (prop.value.type === "ObjectExpression") for (const stateProp of prop.value.properties) {
52
+ if (stateProp.type !== "Property") continue;
53
+ const stateStr = getStringValue(stateProp.value);
54
+ if (stateStr && stateStr.includes("$")) checkValue(stateStr, stateProp.value);
55
+ }
56
+ }
57
+ }
45
58
  return {
46
59
  ImportDeclaration(node) {
47
60
  ctx.trackImport(node);
48
61
  },
49
- "CallExpression ObjectExpression"(node) {
50
- if (!ctx.isStyleObject(node)) return;
51
- collectLocalProperties(node);
52
- for (const prop of node.properties) {
53
- if (prop.type !== "Property") continue;
54
- const str = getStringValue(prop.value);
55
- if (str && str.includes("$")) checkValue(str, prop.value);
56
- if (prop.value.type === "ObjectExpression") for (const stateProp of prop.value.properties) {
57
- if (stateProp.type !== "Property") continue;
58
- const stateStr = getStringValue(stateProp.value);
59
- if (stateStr && stateStr.includes("$")) checkValue(stateStr, stateProp.value);
60
- }
62
+ ...styleObjectListeners(handleStyleObject),
63
+ "Program:exit"() {
64
+ if (!Array.isArray(ctx.config.tokens) || ctx.config.tokens.length === 0) return;
65
+ for (const { token, baseName, node } of pendingChecks) {
66
+ if (fileCustomProperties.has(baseName)) continue;
67
+ if (ctx.config.tokens.includes(baseName)) continue;
68
+ context.report({
69
+ node,
70
+ messageId: "unknownProperty",
71
+ data: { token }
72
+ });
61
73
  }
62
74
  }
63
75
  };
@@ -1 +1 @@
1
- {"version":3,"file":"valid-custom-property.js","names":[],"sources":["../../src/rules/valid-custom-property.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';\n\ntype MessageIds = 'invalidSyntax' | 'unknownProperty';\n\nconst CUSTOM_PROP_REGEX = /\\$\\$?[a-zA-Z][a-zA-Z0-9-]*/g;\n\nexport default createRule<[], MessageIds>({\n name: 'valid-custom-property',\n meta: {\n type: 'suggestion',\n docs: {\n description: 'Validate $name custom property references',\n },\n messages: {\n invalidSyntax: \"Invalid custom property syntax '{{token}}'.\",\n unknownProperty: \"Unknown custom property '{{token}}'.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n const fileCustomProperties = new Set<string>();\n\n function collectLocalProperties(node: TSESTree.ObjectExpression): void {\n for (const prop of node.properties) {\n if (prop.type !== 'Property' || prop.computed) continue;\n const key = getKeyName(prop.key);\n if (key && key.startsWith('$') && !key.startsWith('$$')) {\n fileCustomProperties.add(key);\n }\n }\n }\n\n function checkValue(value: string, node: TSESTree.Node): void {\n if (ctx.config.tokens === false) return;\n\n let match;\n CUSTOM_PROP_REGEX.lastIndex = 0;\n\n while ((match = CUSTOM_PROP_REGEX.exec(value)) !== null) {\n const token = match[0];\n const baseName = token.startsWith('$$') ? '$' + token.slice(2) : token;\n\n if (fileCustomProperties.has(baseName)) continue;\n\n if (\n Array.isArray(ctx.config.tokens) &&\n ctx.config.tokens.includes(baseName)\n ) {\n continue;\n }\n\n if (Array.isArray(ctx.config.tokens) && ctx.config.tokens.length > 0) {\n context.report({\n node,\n messageId: 'unknownProperty',\n data: { token },\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 collectLocalProperties(node);\n\n for (const prop of node.properties) {\n if (prop.type !== 'Property') continue;\n\n const str = getStringValue(prop.value);\n if (str && str.includes('$')) {\n checkValue(str, prop.value);\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 && stateStr.includes('$')) {\n checkValue(stateStr, stateProp.value);\n }\n }\n }\n }\n },\n };\n },\n});\n"],"mappings":";;;;;AAOA,MAAM,oBAAoB;AAE1B,oCAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aAAa,6CACd;EACD,UAAU;GACR,eAAe;GACf,iBAAiB;GAClB;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;EACrC,MAAM,uCAAuB,IAAI,KAAa;EAE9C,SAAS,uBAAuB,MAAuC;AACrE,QAAK,MAAM,QAAQ,KAAK,YAAY;AAClC,QAAI,KAAK,SAAS,cAAc,KAAK,SAAU;IAC/C,MAAM,MAAM,WAAW,KAAK,IAAI;AAChC,QAAI,OAAO,IAAI,WAAW,IAAI,IAAI,CAAC,IAAI,WAAW,KAAK,CACrD,sBAAqB,IAAI,IAAI;;;EAKnC,SAAS,WAAW,OAAe,MAA2B;AAC5D,OAAI,IAAI,OAAO,WAAW,MAAO;GAEjC,IAAI;AACJ,qBAAkB,YAAY;AAE9B,WAAQ,QAAQ,kBAAkB,KAAK,MAAM,MAAM,MAAM;IACvD,MAAM,QAAQ,MAAM;IACpB,MAAM,WAAW,MAAM,WAAW,KAAK,GAAG,MAAM,MAAM,MAAM,EAAE,GAAG;AAEjE,QAAI,qBAAqB,IAAI,SAAS,CAAE;AAExC,QACE,MAAM,QAAQ,IAAI,OAAO,OAAO,IAChC,IAAI,OAAO,OAAO,SAAS,SAAS,CAEpC;AAGF,QAAI,MAAM,QAAQ,IAAI,OAAO,OAAO,IAAI,IAAI,OAAO,OAAO,SAAS,EACjE,SAAQ,OAAO;KACb;KACA,WAAW;KACX,MAAM,EAAE,OAAO;KAChB,CAAC;;;AAKR,SAAO;GACL,kBAAkB,MAAM;AACtB,QAAI,YAAY,KAAK;;GAGvB,kCAAkC,MAAiC;AACjE,QAAI,CAAC,IAAI,cAAc,KAAK,CAAE;AAC9B,2BAAuB,KAAK;AAE5B,SAAK,MAAM,QAAQ,KAAK,YAAY;AAClC,SAAI,KAAK,SAAS,WAAY;KAE9B,MAAM,MAAM,eAAe,KAAK,MAAM;AACtC,SAAI,OAAO,IAAI,SAAS,IAAI,CAC1B,YAAW,KAAK,KAAK,MAAM;AAG7B,SAAI,KAAK,MAAM,SAAS,mBACtB,MAAK,MAAM,aAAa,KAAK,MAAM,YAAY;AAC7C,UAAI,UAAU,SAAS,WAAY;MACnC,MAAM,WAAW,eAAe,UAAU,MAAM;AAChD,UAAI,YAAY,SAAS,SAAS,IAAI,CACpC,YAAW,UAAU,UAAU,MAAM;;;;GAMhD;;CAEJ,CAAC"}
1
+ {"version":3,"file":"valid-custom-property.js","names":[],"sources":["../../src/rules/valid-custom-property.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';\n\ntype MessageIds = 'invalidSyntax' | 'unknownProperty';\n\nconst CUSTOM_PROP_REGEX = /\\$\\$?[a-zA-Z][a-zA-Z0-9-]*/g;\n\ninterface PendingExistenceCheck {\n token: string;\n baseName: string;\n node: TSESTree.Node;\n}\n\nexport default createRule<[], MessageIds>({\n name: 'valid-custom-property',\n meta: {\n type: 'suggestion',\n docs: {\n description: 'Validate $name custom property references',\n },\n messages: {\n invalidSyntax: \"Invalid custom property syntax '{{token}}'.\",\n unknownProperty: \"Unknown custom property '{{token}}'.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n const fileCustomProperties = new Set<string>();\n const pendingChecks: PendingExistenceCheck[] = [];\n\n function collectLocalProperties(node: TSESTree.ObjectExpression): void {\n for (const prop of node.properties) {\n if (prop.type !== 'Property' || prop.computed) continue;\n const key = getKeyName(prop.key);\n if (key && key.startsWith('$') && !key.startsWith('$$')) {\n fileCustomProperties.add(key);\n }\n }\n }\n\n function checkValue(value: string, node: TSESTree.Node): void {\n if (ctx.config.tokens === false) return;\n\n let match;\n CUSTOM_PROP_REGEX.lastIndex = 0;\n\n while ((match = CUSTOM_PROP_REGEX.exec(value)) !== null) {\n const token = match[0];\n const baseName = token.startsWith('$$') ? '$' + token.slice(2) : token;\n\n pendingChecks.push({ token, baseName, node });\n }\n }\n\n function handleStyleObject(node: TSESTree.ObjectExpression) {\n if (!ctx.isStyleObject(node)) return;\n collectLocalProperties(node);\n\n for (const prop of node.properties) {\n if (prop.type !== 'Property') continue;\n\n const str = getStringValue(prop.value);\n if (str && str.includes('$')) {\n checkValue(str, prop.value);\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 && stateStr.includes('$')) {\n checkValue(stateStr, stateProp.value);\n }\n }\n }\n }\n }\n\n return {\n ImportDeclaration(node) {\n ctx.trackImport(node);\n },\n\n ...styleObjectListeners(handleStyleObject),\n\n 'Program:exit'() {\n if (\n !Array.isArray(ctx.config.tokens) ||\n ctx.config.tokens.length === 0\n ) {\n return;\n }\n\n for (const { token, baseName, node } of pendingChecks) {\n if (fileCustomProperties.has(baseName)) continue;\n if (ctx.config.tokens.includes(baseName)) continue;\n\n context.report({\n node,\n messageId: 'unknownProperty',\n data: { token },\n });\n }\n },\n };\n },\n});\n"],"mappings":";;;;;AAOA,MAAM,oBAAoB;AAQ1B,oCAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aAAa,6CACd;EACD,UAAU;GACR,eAAe;GACf,iBAAiB;GAClB;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;EACrC,MAAM,uCAAuB,IAAI,KAAa;EAC9C,MAAM,gBAAyC,EAAE;EAEjD,SAAS,uBAAuB,MAAuC;AACrE,QAAK,MAAM,QAAQ,KAAK,YAAY;AAClC,QAAI,KAAK,SAAS,cAAc,KAAK,SAAU;IAC/C,MAAM,MAAM,WAAW,KAAK,IAAI;AAChC,QAAI,OAAO,IAAI,WAAW,IAAI,IAAI,CAAC,IAAI,WAAW,KAAK,CACrD,sBAAqB,IAAI,IAAI;;;EAKnC,SAAS,WAAW,OAAe,MAA2B;AAC5D,OAAI,IAAI,OAAO,WAAW,MAAO;GAEjC,IAAI;AACJ,qBAAkB,YAAY;AAE9B,WAAQ,QAAQ,kBAAkB,KAAK,MAAM,MAAM,MAAM;IACvD,MAAM,QAAQ,MAAM;IACpB,MAAM,WAAW,MAAM,WAAW,KAAK,GAAG,MAAM,MAAM,MAAM,EAAE,GAAG;AAEjE,kBAAc,KAAK;KAAE;KAAO;KAAU;KAAM,CAAC;;;EAIjD,SAAS,kBAAkB,MAAiC;AAC1D,OAAI,CAAC,IAAI,cAAc,KAAK,CAAE;AAC9B,0BAAuB,KAAK;AAE5B,QAAK,MAAM,QAAQ,KAAK,YAAY;AAClC,QAAI,KAAK,SAAS,WAAY;IAE9B,MAAM,MAAM,eAAe,KAAK,MAAM;AACtC,QAAI,OAAO,IAAI,SAAS,IAAI,CAC1B,YAAW,KAAK,KAAK,MAAM;AAG7B,QAAI,KAAK,MAAM,SAAS,mBACtB,MAAK,MAAM,aAAa,KAAK,MAAM,YAAY;AAC7C,SAAI,UAAU,SAAS,WAAY;KACnC,MAAM,WAAW,eAAe,UAAU,MAAM;AAChD,SAAI,YAAY,SAAS,SAAS,IAAI,CACpC,YAAW,UAAU,UAAU,MAAM;;;;AAO/C,SAAO;GACL,kBAAkB,MAAM;AACtB,QAAI,YAAY,KAAK;;GAGvB,GAAG,qBAAqB,kBAAkB;GAE1C,iBAAiB;AACf,QACE,CAAC,MAAM,QAAQ,IAAI,OAAO,OAAO,IACjC,IAAI,OAAO,OAAO,WAAW,EAE7B;AAGF,SAAK,MAAM,EAAE,OAAO,UAAU,UAAU,eAAe;AACrD,SAAI,qBAAqB,IAAI,SAAS,CAAE;AACxC,SAAI,IAAI,OAAO,OAAO,SAAS,SAAS,CAAE;AAE1C,aAAQ,OAAO;MACb;MACA,WAAW;MACX,MAAM,EAAE,OAAO;MAChB,CAAC;;;GAGP;;CAEJ,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { createRule } from "../create-rule.js";
2
- import { TastyContext } from "../context.js";
3
2
  import { extractCustomUnit, getStringValue, isValidUnit } from "../utils.js";
3
+ import { TastyContext, styleObjectListeners } from "../context.js";
4
4
 
5
5
  //#region src/rules/valid-custom-unit.ts
6
6
  var valid_custom_unit_default = createRule({
@@ -34,25 +34,21 @@ var valid_custom_unit_default = createRule({
34
34
  const str = getStringValue(node);
35
35
  if (str) checkUnitsInValue(str, node);
36
36
  }
37
+ function handleStyleObject(node) {
38
+ if (!ctx.isStyleObject(node)) return;
39
+ for (const prop of node.properties) {
40
+ if (prop.type !== "Property") continue;
41
+ checkNode(prop.value);
42
+ if (prop.value.type === "ObjectExpression") {
43
+ for (const stateProp of prop.value.properties) if (stateProp.type === "Property") checkNode(stateProp.value);
44
+ }
45
+ }
46
+ }
37
47
  return {
38
48
  ImportDeclaration(node) {
39
49
  ctx.trackImport(node);
40
50
  },
41
- "CallExpression ObjectExpression Property"(node) {
42
- const objExpr = node.parent;
43
- const callExpr = objExpr.parent;
44
- if (callExpr?.type !== "CallExpression" && callExpr?.type !== "Property") return;
45
- let current = objExpr;
46
- while (current) {
47
- if (current.type === "ObjectExpression" && ctx.isStyleObject(current)) break;
48
- current = current.parent;
49
- }
50
- if (!current) return;
51
- checkNode(node.value);
52
- if (node.value.type === "ObjectExpression") {
53
- for (const stateProp of node.value.properties) if (stateProp.type === "Property") checkNode(stateProp.value);
54
- }
55
- }
51
+ ...styleObjectListeners(handleStyleObject)
56
52
  };
57
53
  }
58
54
  });
@@ -1 +1 @@
1
- {"version":3,"file":"valid-custom-unit.js","names":[],"sources":["../../src/rules/valid-custom-unit.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport { createRule } from '../create-rule.js';\nimport { TastyContext } from '../context.js';\nimport { getStringValue, extractCustomUnit, isValidUnit } from '../utils.js';\n\ntype MessageIds = 'unknownUnit';\n\nexport default createRule<[], MessageIds>({\n name: 'valid-custom-unit',\n meta: {\n type: 'problem',\n docs: {\n description: 'Validate that custom units in style values are recognized',\n },\n messages: {\n unknownUnit: \"Unknown unit '{{unit}}' in value '{{value}}'.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n\n function checkUnitsInValue(value: string, node: TSESTree.Node): void {\n if (ctx.config.units === false) return;\n\n // Split by spaces and check each token\n const tokens = value.split(/\\s+/);\n for (const token of tokens) {\n // Skip function calls, colors, modifiers, special values\n if (\n token.startsWith('#') ||\n token.startsWith('$') ||\n token.startsWith('@') ||\n token.includes('(') ||\n token.includes(')') ||\n token === 'true' ||\n token === 'false' ||\n token === 'none' ||\n token === 'auto' ||\n token === 'inherit' ||\n token === 'initial' ||\n token === 'unset' ||\n token === 'revert'\n ) {\n continue;\n }\n\n const unit = extractCustomUnit(token);\n if (unit && !isValidUnit(unit, ctx.config)) {\n context.report({\n node,\n messageId: 'unknownUnit',\n data: { unit, value: token },\n });\n }\n }\n }\n\n function checkNode(node: TSESTree.Node): void {\n const str = getStringValue(node);\n if (str) {\n checkUnitsInValue(str, node);\n }\n }\n\n return {\n ImportDeclaration(node) {\n ctx.trackImport(node);\n },\n\n 'CallExpression ObjectExpression Property'(node: TSESTree.Property) {\n const objExpr = node.parent as TSESTree.ObjectExpression;\n const callExpr = objExpr.parent;\n if (\n callExpr?.type !== 'CallExpression' &&\n callExpr?.type !== 'Property'\n )\n return;\n\n // Find closest object expression that is a style object\n let current: TSESTree.Node | undefined = objExpr;\n while (current) {\n if (\n current.type === 'ObjectExpression' &&\n ctx.isStyleObject(current)\n ) {\n break;\n }\n current = current.parent;\n }\n if (!current) return;\n\n // Check value\n checkNode(node.value);\n\n // Check state map values\n if (node.value.type === 'ObjectExpression') {\n for (const stateProp of node.value.properties) {\n if (stateProp.type === 'Property') {\n checkNode(stateProp.value);\n }\n }\n }\n },\n };\n },\n});\n"],"mappings":";;;;;AAOA,gCAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aAAa,6DACd;EACD,UAAU,EACR,aAAa,iDACd;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;EAErC,SAAS,kBAAkB,OAAe,MAA2B;AACnE,OAAI,IAAI,OAAO,UAAU,MAAO;GAGhC,MAAM,SAAS,MAAM,MAAM,MAAM;AACjC,QAAK,MAAM,SAAS,QAAQ;AAE1B,QACE,MAAM,WAAW,IAAI,IACrB,MAAM,WAAW,IAAI,IACrB,MAAM,WAAW,IAAI,IACrB,MAAM,SAAS,IAAI,IACnB,MAAM,SAAS,IAAI,IACnB,UAAU,UACV,UAAU,WACV,UAAU,UACV,UAAU,UACV,UAAU,aACV,UAAU,aACV,UAAU,WACV,UAAU,SAEV;IAGF,MAAM,OAAO,kBAAkB,MAAM;AACrC,QAAI,QAAQ,CAAC,YAAY,MAAM,IAAI,OAAO,CACxC,SAAQ,OAAO;KACb;KACA,WAAW;KACX,MAAM;MAAE;MAAM,OAAO;MAAO;KAC7B,CAAC;;;EAKR,SAAS,UAAU,MAA2B;GAC5C,MAAM,MAAM,eAAe,KAAK;AAChC,OAAI,IACF,mBAAkB,KAAK,KAAK;;AAIhC,SAAO;GACL,kBAAkB,MAAM;AACtB,QAAI,YAAY,KAAK;;GAGvB,2CAA2C,MAAyB;IAClE,MAAM,UAAU,KAAK;IACrB,MAAM,WAAW,QAAQ;AACzB,QACE,UAAU,SAAS,oBACnB,UAAU,SAAS,WAEnB;IAGF,IAAI,UAAqC;AACzC,WAAO,SAAS;AACd,SACE,QAAQ,SAAS,sBACjB,IAAI,cAAc,QAAQ,CAE1B;AAEF,eAAU,QAAQ;;AAEpB,QAAI,CAAC,QAAS;AAGd,cAAU,KAAK,MAAM;AAGrB,QAAI,KAAK,MAAM,SAAS,oBACtB;UAAK,MAAM,aAAa,KAAK,MAAM,WACjC,KAAI,UAAU,SAAS,WACrB,WAAU,UAAU,MAAM;;;GAKnC;;CAEJ,CAAC"}
1
+ {"version":3,"file":"valid-custom-unit.js","names":[],"sources":["../../src/rules/valid-custom-unit.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport { createRule } from '../create-rule.js';\nimport { TastyContext, styleObjectListeners } from '../context.js';\nimport { getStringValue, extractCustomUnit, isValidUnit } from '../utils.js';\n\ntype MessageIds = 'unknownUnit';\n\nexport default createRule<[], MessageIds>({\n name: 'valid-custom-unit',\n meta: {\n type: 'problem',\n docs: {\n description: 'Validate that custom units in style values are recognized',\n },\n messages: {\n unknownUnit: \"Unknown unit '{{unit}}' in value '{{value}}'.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n\n function checkUnitsInValue(value: string, node: TSESTree.Node): void {\n if (ctx.config.units === false) return;\n\n // Split by spaces and check each token\n const tokens = value.split(/\\s+/);\n for (const token of tokens) {\n // Skip function calls, colors, modifiers, special values\n if (\n token.startsWith('#') ||\n token.startsWith('$') ||\n token.startsWith('@') ||\n token.includes('(') ||\n token.includes(')') ||\n token === 'true' ||\n token === 'false' ||\n token === 'none' ||\n token === 'auto' ||\n token === 'inherit' ||\n token === 'initial' ||\n token === 'unset' ||\n token === 'revert'\n ) {\n continue;\n }\n\n const unit = extractCustomUnit(token);\n if (unit && !isValidUnit(unit, ctx.config)) {\n context.report({\n node,\n messageId: 'unknownUnit',\n data: { unit, value: token },\n });\n }\n }\n }\n\n function checkNode(node: TSESTree.Node): void {\n const str = getStringValue(node);\n if (str) {\n checkUnitsInValue(str, node);\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\n checkNode(prop.value);\n\n if (prop.value.type === 'ObjectExpression') {\n for (const stateProp of prop.value.properties) {\n if (stateProp.type === 'Property') {\n checkNode(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":";;;;;AAOA,gCAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aAAa,6DACd;EACD,UAAU,EACR,aAAa,iDACd;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;EAErC,SAAS,kBAAkB,OAAe,MAA2B;AACnE,OAAI,IAAI,OAAO,UAAU,MAAO;GAGhC,MAAM,SAAS,MAAM,MAAM,MAAM;AACjC,QAAK,MAAM,SAAS,QAAQ;AAE1B,QACE,MAAM,WAAW,IAAI,IACrB,MAAM,WAAW,IAAI,IACrB,MAAM,WAAW,IAAI,IACrB,MAAM,SAAS,IAAI,IACnB,MAAM,SAAS,IAAI,IACnB,UAAU,UACV,UAAU,WACV,UAAU,UACV,UAAU,UACV,UAAU,aACV,UAAU,aACV,UAAU,WACV,UAAU,SAEV;IAGF,MAAM,OAAO,kBAAkB,MAAM;AACrC,QAAI,QAAQ,CAAC,YAAY,MAAM,IAAI,OAAO,CACxC,SAAQ,OAAO;KACb;KACA,WAAW;KACX,MAAM;MAAE;MAAM,OAAO;MAAO;KAC7B,CAAC;;;EAKR,SAAS,UAAU,MAA2B;GAC5C,MAAM,MAAM,eAAe,KAAK;AAChC,OAAI,IACF,mBAAkB,KAAK,KAAK;;EAIhC,SAAS,kBAAkB,MAAiC;AAC1D,OAAI,CAAC,IAAI,cAAc,KAAK,CAAE;AAE9B,QAAK,MAAM,QAAQ,KAAK,YAAY;AAClC,QAAI,KAAK,SAAS,WAAY;AAE9B,cAAU,KAAK,MAAM;AAErB,QAAI,KAAK,MAAM,SAAS,oBACtB;UAAK,MAAM,aAAa,KAAK,MAAM,WACjC,KAAI,UAAU,SAAS,WACrB,WAAU,UAAU,MAAM;;;;AAOpC,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 { DIRECTIONAL_MODIFIERS } 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-directional-modifier.ts
7
7
  const ALL_DIRECTIONS = new Set([
@@ -40,29 +40,30 @@ var valid_directional_modifier_default = createRule({
40
40
  });
41
41
  }
42
42
  }
43
+ function handleStyleObject(node) {
44
+ if (!ctx.isStyleObject(node)) return;
45
+ for (const prop of node.properties) {
46
+ if (prop.type !== "Property" || prop.computed) continue;
47
+ const key = getKeyName(prop.key);
48
+ if (key === null) continue;
49
+ if (!(key in DIRECTIONAL_MODIFIERS)) continue;
50
+ const str = getStringValue(prop.value);
51
+ if (str) {
52
+ checkValue(key, str, prop.value);
53
+ continue;
54
+ }
55
+ if (prop.value.type === "ObjectExpression") for (const stateProp of prop.value.properties) {
56
+ if (stateProp.type !== "Property") continue;
57
+ const stateStr = getStringValue(stateProp.value);
58
+ if (stateStr) checkValue(key, stateStr, stateProp.value);
59
+ }
60
+ }
61
+ }
43
62
  return {
44
63
  ImportDeclaration(node) {
45
64
  ctx.trackImport(node);
46
65
  },
47
- "CallExpression ObjectExpression"(node) {
48
- if (!ctx.isStyleObject(node)) return;
49
- for (const prop of node.properties) {
50
- if (prop.type !== "Property" || prop.computed) continue;
51
- const key = getKeyName(prop.key);
52
- if (key === null) continue;
53
- if (!(key in DIRECTIONAL_MODIFIERS)) continue;
54
- const str = getStringValue(prop.value);
55
- if (str) {
56
- checkValue(key, str, prop.value);
57
- continue;
58
- }
59
- if (prop.value.type === "ObjectExpression") for (const stateProp of prop.value.properties) {
60
- if (stateProp.type !== "Property") continue;
61
- const stateStr = getStringValue(stateProp.value);
62
- if (stateStr) checkValue(key, stateStr, stateProp.value);
63
- }
64
- }
65
- }
66
+ ...styleObjectListeners(handleStyleObject)
66
67
  };
67
68
  }
68
69
  });