@tenphi/eslint-plugin-tasty 0.2.0 → 0.2.1
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.mjs → config.js} +3 -3
- package/dist/config.js.map +1 -0
- package/dist/{configs.d.mts → configs.d.ts} +1 -1
- package/dist/{configs.mjs → configs.js} +1 -1
- package/dist/configs.js.map +1 -0
- package/dist/{constants.mjs → constants.js} +1 -1
- package/dist/constants.js.map +1 -0
- package/dist/{context.mjs → context.js} +3 -3
- package/dist/context.js.map +1 -0
- package/dist/{create-rule.mjs → create-rule.js} +1 -1
- package/dist/create-rule.js.map +1 -0
- package/dist/{index.d.mts → index.d.ts} +3 -3
- package/dist/{index.mjs → index.js} +29 -29
- package/dist/index.js.map +1 -0
- package/dist/{parser.mjs → parser.js} +2 -2
- package/dist/parser.js.map +1 -0
- package/dist/{property-expectations.mjs → property-expectations.js} +1 -1
- package/dist/property-expectations.js.map +1 -0
- package/dist/rules/{consistent-token-usage.mjs → consistent-token-usage.js} +4 -4
- package/dist/rules/consistent-token-usage.js.map +1 -0
- package/dist/rules/{known-property.mjs → known-property.js} +5 -5
- package/dist/rules/known-property.js.map +1 -0
- package/dist/rules/{no-duplicate-state.mjs → no-duplicate-state.js} +4 -4
- package/dist/rules/no-duplicate-state.js.map +1 -0
- package/dist/rules/{no-important.mjs → no-important.js} +4 -4
- package/dist/rules/no-important.js.map +1 -0
- package/dist/rules/{no-nested-selector.mjs → no-nested-selector.js} +4 -4
- package/dist/rules/no-nested-selector.js.map +1 -0
- package/dist/rules/{no-nested-state-map.mjs → no-nested-state-map.js} +4 -4
- package/dist/rules/no-nested-state-map.js.map +1 -0
- package/dist/rules/{no-raw-color-values.mjs → no-raw-color-values.js} +4 -4
- package/dist/rules/no-raw-color-values.js.map +1 -0
- package/dist/rules/{no-runtime-styles-mutation.mjs → no-runtime-styles-mutation.js} +4 -4
- package/dist/rules/no-runtime-styles-mutation.js.map +1 -0
- package/dist/rules/{no-styles-prop.mjs → no-styles-prop.js} +2 -2
- package/dist/rules/no-styles-prop.js.map +1 -0
- package/dist/rules/{no-unknown-state-alias.mjs → no-unknown-state-alias.js} +4 -4
- package/dist/rules/no-unknown-state-alias.js.map +1 -0
- package/dist/rules/{prefer-shorthand-property.mjs → prefer-shorthand-property.js} +5 -5
- package/dist/rules/prefer-shorthand-property.js.map +1 -0
- package/dist/rules/{require-default-state.mjs → require-default-state.js} +4 -4
- package/dist/rules/require-default-state.js.map +1 -0
- package/dist/rules/{static-no-dynamic-values.mjs → static-no-dynamic-values.js} +4 -4
- package/dist/rules/static-no-dynamic-values.js.map +1 -0
- package/dist/rules/{static-valid-selector.mjs → static-valid-selector.js} +4 -4
- package/dist/rules/static-valid-selector.js.map +1 -0
- package/dist/rules/{valid-boolean-property.mjs → valid-boolean-property.js} +5 -5
- package/dist/rules/valid-boolean-property.js.map +1 -0
- package/dist/rules/{valid-color-token.mjs → valid-color-token.js} +4 -4
- package/dist/rules/valid-color-token.js.map +1 -0
- package/dist/rules/{valid-custom-property.mjs → valid-custom-property.js} +4 -4
- package/dist/rules/valid-custom-property.js.map +1 -0
- package/dist/rules/{valid-custom-unit.mjs → valid-custom-unit.js} +4 -4
- package/dist/rules/valid-custom-unit.js.map +1 -0
- package/dist/rules/{valid-directional-modifier.mjs → valid-directional-modifier.js} +5 -5
- package/dist/rules/valid-directional-modifier.js.map +1 -0
- package/dist/rules/{valid-preset.mjs → valid-preset.js} +5 -5
- package/dist/rules/valid-preset.js.map +1 -0
- package/dist/rules/{valid-radius-shape.mjs → valid-radius-shape.js} +5 -5
- package/dist/rules/valid-radius-shape.js.map +1 -0
- package/dist/rules/{valid-recipe.mjs → valid-recipe.js} +4 -4
- package/dist/rules/valid-recipe.js.map +1 -0
- package/dist/rules/{valid-state-key.mjs → valid-state-key.js} +4 -4
- package/dist/rules/valid-state-key.js.map +1 -0
- package/dist/rules/{valid-styles-structure.mjs → valid-styles-structure.js} +4 -4
- package/dist/rules/valid-styles-structure.js.map +1 -0
- package/dist/rules/{valid-sub-element.mjs → valid-sub-element.js} +4 -4
- package/dist/rules/valid-sub-element.js.map +1 -0
- package/dist/rules/{valid-transition.mjs → valid-transition.js} +5 -5
- package/dist/rules/valid-transition.js.map +1 -0
- package/dist/rules/{valid-value.mjs → valid-value.js} +6 -6
- package/dist/rules/valid-value.js.map +1 -0
- package/dist/{types.d.mts → types.d.ts} +1 -1
- package/dist/{utils.mjs → utils.js} +2 -2
- package/dist/utils.js.map +1 -0
- package/package.json +1 -1
- package/dist/config.mjs.map +0 -1
- package/dist/configs.mjs.map +0 -1
- package/dist/constants.mjs.map +0 -1
- package/dist/context.mjs.map +0 -1
- package/dist/create-rule.mjs.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/parser.mjs.map +0 -1
- package/dist/property-expectations.mjs.map +0 -1
- package/dist/rules/consistent-token-usage.mjs.map +0 -1
- package/dist/rules/known-property.mjs.map +0 -1
- package/dist/rules/no-duplicate-state.mjs.map +0 -1
- package/dist/rules/no-important.mjs.map +0 -1
- package/dist/rules/no-nested-selector.mjs.map +0 -1
- package/dist/rules/no-nested-state-map.mjs.map +0 -1
- package/dist/rules/no-raw-color-values.mjs.map +0 -1
- package/dist/rules/no-runtime-styles-mutation.mjs.map +0 -1
- package/dist/rules/no-styles-prop.mjs.map +0 -1
- package/dist/rules/no-unknown-state-alias.mjs.map +0 -1
- package/dist/rules/prefer-shorthand-property.mjs.map +0 -1
- package/dist/rules/require-default-state.mjs.map +0 -1
- package/dist/rules/static-no-dynamic-values.mjs.map +0 -1
- package/dist/rules/static-valid-selector.mjs.map +0 -1
- package/dist/rules/valid-boolean-property.mjs.map +0 -1
- package/dist/rules/valid-color-token.mjs.map +0 -1
- package/dist/rules/valid-custom-property.mjs.map +0 -1
- package/dist/rules/valid-custom-unit.mjs.map +0 -1
- package/dist/rules/valid-directional-modifier.mjs.map +0 -1
- package/dist/rules/valid-preset.mjs.map +0 -1
- package/dist/rules/valid-radius-shape.mjs.map +0 -1
- package/dist/rules/valid-recipe.mjs.map +0 -1
- package/dist/rules/valid-state-key.mjs.map +0 -1
- package/dist/rules/valid-styles-structure.mjs.map +0 -1
- package/dist/rules/valid-sub-element.mjs.map +0 -1
- package/dist/rules/valid-transition.mjs.map +0 -1
- package/dist/rules/valid-value.mjs.map +0 -1
- package/dist/utils.mjs.map +0 -1
- /package/dist/_virtual/_rolldown/{runtime.mjs → runtime.js} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { createRule } from "../create-rule.
|
|
2
|
-
import { SHORTHAND_MAPPING } from "../constants.
|
|
3
|
-
import { TastyContext } from "../context.
|
|
4
|
-
import { getKeyName } from "../utils.
|
|
1
|
+
import { createRule } from "../create-rule.js";
|
|
2
|
+
import { SHORTHAND_MAPPING } from "../constants.js";
|
|
3
|
+
import { TastyContext } from "../context.js";
|
|
4
|
+
import { getKeyName } from "../utils.js";
|
|
5
5
|
|
|
6
6
|
//#region src/rules/prefer-shorthand-property.ts
|
|
7
7
|
var prefer_shorthand_property_default = createRule({
|
|
@@ -42,4 +42,4 @@ var prefer_shorthand_property_default = createRule({
|
|
|
42
42
|
|
|
43
43
|
//#endregion
|
|
44
44
|
export { prefer_shorthand_property_default as default };
|
|
45
|
-
//# sourceMappingURL=prefer-shorthand-property.
|
|
45
|
+
//# sourceMappingURL=prefer-shorthand-property.js.map
|
|
@@ -0,0 +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,6 +1,6 @@
|
|
|
1
|
-
import { createRule } from "../create-rule.
|
|
2
|
-
import { TastyContext } from "../context.
|
|
3
|
-
import { getKeyName } from "../utils.
|
|
1
|
+
import { createRule } from "../create-rule.js";
|
|
2
|
+
import { TastyContext } from "../context.js";
|
|
3
|
+
import { getKeyName } from "../utils.js";
|
|
4
4
|
|
|
5
5
|
//#region src/rules/require-default-state.ts
|
|
6
6
|
var require_default_state_default = createRule({
|
|
@@ -44,4 +44,4 @@ var require_default_state_default = createRule({
|
|
|
44
44
|
|
|
45
45
|
//#endregion
|
|
46
46
|
export { require_default_state_default as default };
|
|
47
|
-
//# sourceMappingURL=require-default-state.
|
|
47
|
+
//# sourceMappingURL=require-default-state.js.map
|
|
@@ -0,0 +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,6 +1,6 @@
|
|
|
1
|
-
import { createRule } from "../create-rule.
|
|
2
|
-
import { TastyContext } from "../context.
|
|
3
|
-
import { isStaticValue } from "../utils.
|
|
1
|
+
import { createRule } from "../create-rule.js";
|
|
2
|
+
import { TastyContext } from "../context.js";
|
|
3
|
+
import { isStaticValue } from "../utils.js";
|
|
4
4
|
|
|
5
5
|
//#region src/rules/static-no-dynamic-values.ts
|
|
6
6
|
var static_no_dynamic_values_default = createRule({
|
|
@@ -51,4 +51,4 @@ var static_no_dynamic_values_default = createRule({
|
|
|
51
51
|
|
|
52
52
|
//#endregion
|
|
53
53
|
export { static_no_dynamic_values_default as default };
|
|
54
|
-
//# sourceMappingURL=static-no-dynamic-values.
|
|
54
|
+
//# sourceMappingURL=static-no-dynamic-values.js.map
|
|
@@ -0,0 +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,6 +1,6 @@
|
|
|
1
|
-
import { createRule } from "../create-rule.
|
|
2
|
-
import { TastyContext } from "../context.
|
|
3
|
-
import { getStringValue, isValidSelector } from "../utils.
|
|
1
|
+
import { createRule } from "../create-rule.js";
|
|
2
|
+
import { TastyContext } from "../context.js";
|
|
3
|
+
import { getStringValue, isValidSelector } from "../utils.js";
|
|
4
4
|
|
|
5
5
|
//#region src/rules/static-valid-selector.ts
|
|
6
6
|
var static_valid_selector_default = createRule({
|
|
@@ -48,4 +48,4 @@ var static_valid_selector_default = createRule({
|
|
|
48
48
|
|
|
49
49
|
//#endregion
|
|
50
50
|
export { static_valid_selector_default as default };
|
|
51
|
-
//# sourceMappingURL=static-valid-selector.
|
|
51
|
+
//# sourceMappingURL=static-valid-selector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static-valid-selector.js","names":[],"sources":["../../src/rules/static-valid-selector.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport { createRule } from '../create-rule.js';\nimport { TastyContext } from '../context.js';\nimport { getStringValue, isValidSelector } from '../utils.js';\n\ntype MessageIds = 'invalidSelector' | 'selectorNotString';\n\nexport default createRule<[], MessageIds>({\n name: 'static-valid-selector',\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Validate the selector string in tastyStatic(selector, styles) calls',\n },\n messages: {\n invalidSelector: 'Invalid CSS selector: {{reason}}',\n selectorNotString: 'tastyStatic() selector must be a string literal.',\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(node: TSESTree.CallExpression) {\n const imp = ctx.isTastyCall(node);\n if (!imp || imp.importedName !== 'tastyStatic') return;\n\n // Only check selector mode: tastyStatic(selector, styles)\n if (node.arguments.length !== 2) return;\n\n const firstArg = node.arguments[0];\n\n // Must be a string literal\n const selectorValue = getStringValue(firstArg);\n if (selectorValue === null) {\n // Could be a StaticStyle object (extending), which is fine\n if (\n firstArg.type === 'Identifier' ||\n firstArg.type === 'MemberExpression'\n ) {\n return;\n }\n\n context.report({\n node: firstArg,\n messageId: 'selectorNotString',\n });\n return;\n }\n\n const error = isValidSelector(selectorValue);\n if (error) {\n context.report({\n node: firstArg,\n messageId: 'invalidSelector',\n data: { reason: error },\n });\n }\n },\n };\n },\n});\n"],"mappings":";;;;;AAOA,oCAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aACE,uEACH;EACD,UAAU;GACR,iBAAiB;GACjB,mBAAmB;GACpB;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,eAAe,MAA+B;IAC5C,MAAM,MAAM,IAAI,YAAY,KAAK;AACjC,QAAI,CAAC,OAAO,IAAI,iBAAiB,cAAe;AAGhD,QAAI,KAAK,UAAU,WAAW,EAAG;IAEjC,MAAM,WAAW,KAAK,UAAU;IAGhC,MAAM,gBAAgB,eAAe,SAAS;AAC9C,QAAI,kBAAkB,MAAM;AAE1B,SACE,SAAS,SAAS,gBAClB,SAAS,SAAS,mBAElB;AAGF,aAAQ,OAAO;MACb,MAAM;MACN,WAAW;MACZ,CAAC;AACF;;IAGF,MAAM,QAAQ,gBAAgB,cAAc;AAC5C,QAAI,MACF,SAAQ,OAAO;KACb,MAAM;KACN,WAAW;KACX,MAAM,EAAE,QAAQ,OAAO;KACxB,CAAC;;GAGP;;CAEJ,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { createRule } from "../create-rule.
|
|
2
|
-
import { BOOLEAN_TRUE_PROPERTIES } from "../constants.
|
|
3
|
-
import { TastyContext } from "../context.
|
|
4
|
-
import { getKeyName } from "../utils.
|
|
1
|
+
import { createRule } from "../create-rule.js";
|
|
2
|
+
import { BOOLEAN_TRUE_PROPERTIES } from "../constants.js";
|
|
3
|
+
import { TastyContext } from "../context.js";
|
|
4
|
+
import { getKeyName } from "../utils.js";
|
|
5
5
|
|
|
6
6
|
//#region src/rules/valid-boolean-property.ts
|
|
7
7
|
var valid_boolean_property_default = createRule({
|
|
@@ -42,4 +42,4 @@ var valid_boolean_property_default = createRule({
|
|
|
42
42
|
|
|
43
43
|
//#endregion
|
|
44
44
|
export { valid_boolean_property_default as default };
|
|
45
|
-
//# sourceMappingURL=valid-boolean-property.
|
|
45
|
+
//# sourceMappingURL=valid-boolean-property.js.map
|
|
@@ -0,0 +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,6 +1,6 @@
|
|
|
1
|
-
import { createRule } from "../create-rule.
|
|
2
|
-
import { TastyContext } from "../context.
|
|
3
|
-
import { getKeyName, getStringValue, isRawHexColor, validateColorTokenSyntax } from "../utils.
|
|
1
|
+
import { createRule } from "../create-rule.js";
|
|
2
|
+
import { TastyContext } from "../context.js";
|
|
3
|
+
import { getKeyName, getStringValue, isRawHexColor, validateColorTokenSyntax } from "../utils.js";
|
|
4
4
|
|
|
5
5
|
//#region src/rules/valid-color-token.ts
|
|
6
6
|
var valid_color_token_default = createRule({
|
|
@@ -81,4 +81,4 @@ var valid_color_token_default = createRule({
|
|
|
81
81
|
|
|
82
82
|
//#endregion
|
|
83
83
|
export { valid_color_token_default as default };
|
|
84
|
-
//# sourceMappingURL=valid-color-token.
|
|
84
|
+
//# sourceMappingURL=valid-color-token.js.map
|
|
@@ -0,0 +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,6 +1,6 @@
|
|
|
1
|
-
import { createRule } from "../create-rule.
|
|
2
|
-
import { TastyContext } from "../context.
|
|
3
|
-
import { getKeyName, getStringValue } from "../utils.
|
|
1
|
+
import { createRule } from "../create-rule.js";
|
|
2
|
+
import { TastyContext } from "../context.js";
|
|
3
|
+
import { getKeyName, getStringValue } from "../utils.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;
|
|
@@ -66,4 +66,4 @@ var valid_custom_property_default = createRule({
|
|
|
66
66
|
|
|
67
67
|
//#endregion
|
|
68
68
|
export { valid_custom_property_default as default };
|
|
69
|
-
//# sourceMappingURL=valid-custom-property.
|
|
69
|
+
//# sourceMappingURL=valid-custom-property.js.map
|
|
@@ -0,0 +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,6 +1,6 @@
|
|
|
1
|
-
import { createRule } from "../create-rule.
|
|
2
|
-
import { TastyContext } from "../context.
|
|
3
|
-
import { extractCustomUnit, getStringValue, isValidUnit } from "../utils.
|
|
1
|
+
import { createRule } from "../create-rule.js";
|
|
2
|
+
import { TastyContext } from "../context.js";
|
|
3
|
+
import { extractCustomUnit, getStringValue, isValidUnit } from "../utils.js";
|
|
4
4
|
|
|
5
5
|
//#region src/rules/valid-custom-unit.ts
|
|
6
6
|
var valid_custom_unit_default = createRule({
|
|
@@ -59,4 +59,4 @@ var valid_custom_unit_default = createRule({
|
|
|
59
59
|
|
|
60
60
|
//#endregion
|
|
61
61
|
export { valid_custom_unit_default as default };
|
|
62
|
-
//# sourceMappingURL=valid-custom-unit.
|
|
62
|
+
//# sourceMappingURL=valid-custom-unit.js.map
|
|
@@ -0,0 +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,7 +1,7 @@
|
|
|
1
|
-
import { createRule } from "../create-rule.
|
|
2
|
-
import { DIRECTIONAL_MODIFIERS } from "../constants.
|
|
3
|
-
import { TastyContext } from "../context.
|
|
4
|
-
import { getKeyName, getStringValue } from "../utils.
|
|
1
|
+
import { createRule } from "../create-rule.js";
|
|
2
|
+
import { DIRECTIONAL_MODIFIERS } from "../constants.js";
|
|
3
|
+
import { TastyContext } from "../context.js";
|
|
4
|
+
import { getKeyName, getStringValue } from "../utils.js";
|
|
5
5
|
|
|
6
6
|
//#region src/rules/valid-directional-modifier.ts
|
|
7
7
|
const ALL_DIRECTIONS = new Set([
|
|
@@ -68,4 +68,4 @@ var valid_directional_modifier_default = createRule({
|
|
|
68
68
|
|
|
69
69
|
//#endregion
|
|
70
70
|
export { valid_directional_modifier_default as default };
|
|
71
|
-
//# sourceMappingURL=valid-directional-modifier.
|
|
71
|
+
//# sourceMappingURL=valid-directional-modifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"valid-directional-modifier.js","names":[],"sources":["../../src/rules/valid-directional-modifier.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 { DIRECTIONAL_MODIFIERS } from '../constants.js';\n\ntype MessageIds = 'invalidDirectionalModifier';\n\nconst ALL_DIRECTIONS = new Set([\n 'top',\n 'right',\n 'bottom',\n 'left',\n 'top-left',\n 'top-right',\n 'bottom-left',\n 'bottom-right',\n]);\n\nexport default createRule<[], MessageIds>({\n name: 'valid-directional-modifier',\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Validate that directional modifiers are used only on properties that support them',\n },\n messages: {\n invalidDirectionalModifier:\n \"Property '{{property}}' does not support directional modifier '{{modifier}}'.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n\n function checkValue(\n property: string,\n value: string,\n node: TSESTree.Node,\n ): void {\n const tokens = value.trim().split(/\\s+/);\n\n for (const token of tokens) {\n if (!ALL_DIRECTIONS.has(token)) continue;\n\n const allowedMods = DIRECTIONAL_MODIFIERS[property];\n if (!allowedMods || !allowedMods.has(token)) {\n context.report({\n node,\n messageId: 'invalidDirectionalModifier',\n data: { property, modifier: 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\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 // Direct value\n const str = getStringValue(prop.value);\n if (str) {\n checkValue(key, str, prop.value);\n continue;\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(key, stateStr, stateProp.value);\n }\n }\n }\n }\n },\n };\n },\n});\n"],"mappings":";;;;;;AAQA,MAAM,iBAAiB,IAAI,IAAI;CAC7B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAEF,yCAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aACE,qFACH;EACD,UAAU,EACR,4BACE,iFACH;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;EAErC,SAAS,WACP,UACA,OACA,MACM;GACN,MAAM,SAAS,MAAM,MAAM,CAAC,MAAM,MAAM;AAExC,QAAK,MAAM,SAAS,QAAQ;AAC1B,QAAI,CAAC,eAAe,IAAI,MAAM,CAAE;IAEhC,MAAM,cAAc,sBAAsB;AAC1C,QAAI,CAAC,eAAe,CAAC,YAAY,IAAI,MAAM,CACzC,SAAQ,OAAO;KACb;KACA,WAAW;KACX,MAAM;MAAE;MAAU,UAAU;MAAO;KACpC,CAAC;;;AAKR,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;KAGlB,MAAM,MAAM,eAAe,KAAK,MAAM;AACtC,SAAI,KAAK;AACP,iBAAW,KAAK,KAAK,KAAK,MAAM;AAChC;;AAIF,SAAI,KAAK,MAAM,SAAS,mBACtB,MAAK,MAAM,aAAa,KAAK,MAAM,YAAY;AAC7C,UAAI,UAAU,SAAS,WAAY;MACnC,MAAM,WAAW,eAAe,UAAU,MAAM;AAChD,UAAI,SACF,YAAW,KAAK,UAAU,UAAU,MAAM;;;;GAMrD;;CAEJ,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { createRule } from "../create-rule.
|
|
2
|
-
import { PRESET_MODIFIERS } from "../constants.
|
|
3
|
-
import { TastyContext } from "../context.
|
|
4
|
-
import { getKeyName, getStringValue } from "../utils.
|
|
1
|
+
import { createRule } from "../create-rule.js";
|
|
2
|
+
import { PRESET_MODIFIERS } from "../constants.js";
|
|
3
|
+
import { TastyContext } from "../context.js";
|
|
4
|
+
import { getKeyName, getStringValue } from "../utils.js";
|
|
5
5
|
|
|
6
6
|
//#region src/rules/valid-preset.ts
|
|
7
7
|
var valid_preset_default = createRule({
|
|
@@ -61,4 +61,4 @@ var valid_preset_default = createRule({
|
|
|
61
61
|
|
|
62
62
|
//#endregion
|
|
63
63
|
export { valid_preset_default as default };
|
|
64
|
-
//# sourceMappingURL=valid-preset.
|
|
64
|
+
//# sourceMappingURL=valid-preset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"valid-preset.js","names":[],"sources":["../../src/rules/valid-preset.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 { PRESET_MODIFIERS } from '../constants.js';\n\ntype MessageIds = 'unknownPreset' | 'unknownModifier';\n\nexport default createRule<[], MessageIds>({\n name: 'valid-preset',\n meta: {\n type: 'problem',\n docs: {\n description: 'Validate preset property values against config',\n },\n messages: {\n unknownPreset: \"Unknown preset '{{name}}'.\",\n unknownModifier: \"Unknown preset modifier '{{modifier}}'.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n\n function checkPresetValue(value: string, node: TSESTree.Node): void {\n const parts = value.trim().split(/\\s+/);\n if (parts.length === 0) return;\n\n const [presetName, ...modifiers] = parts;\n\n if (\n ctx.config.presets.length > 0 &&\n !ctx.config.presets.includes(presetName)\n ) {\n context.report({\n node,\n messageId: 'unknownPreset',\n data: { name: presetName },\n });\n }\n\n for (const mod of modifiers) {\n if (!PRESET_MODIFIERS.has(mod)) {\n context.report({\n node,\n messageId: 'unknownModifier',\n data: { modifier: mod },\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' || prop.computed) continue;\n\n const key = getKeyName(prop.key);\n if (key !== 'preset') continue;\n\n // Direct string value\n const str = getStringValue(prop.value);\n if (str) {\n checkPresetValue(str, prop.value);\n continue;\n }\n\n // true is always valid\n if (prop.value.type === 'Literal' && prop.value.value === true) {\n continue;\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 checkPresetValue(stateStr, stateProp.value);\n }\n }\n }\n }\n },\n };\n },\n});\n"],"mappings":";;;;;;AAQA,2BAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aAAa,kDACd;EACD,UAAU;GACR,eAAe;GACf,iBAAiB;GAClB;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;EAErC,SAAS,iBAAiB,OAAe,MAA2B;GAClE,MAAM,QAAQ,MAAM,MAAM,CAAC,MAAM,MAAM;AACvC,OAAI,MAAM,WAAW,EAAG;GAExB,MAAM,CAAC,YAAY,GAAG,aAAa;AAEnC,OACE,IAAI,OAAO,QAAQ,SAAS,KAC5B,CAAC,IAAI,OAAO,QAAQ,SAAS,WAAW,CAExC,SAAQ,OAAO;IACb;IACA,WAAW;IACX,MAAM,EAAE,MAAM,YAAY;IAC3B,CAAC;AAGJ,QAAK,MAAM,OAAO,UAChB,KAAI,CAAC,iBAAiB,IAAI,IAAI,CAC5B,SAAQ,OAAO;IACb;IACA,WAAW;IACX,MAAM,EAAE,UAAU,KAAK;IACxB,CAAC;;AAKR,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;AAG/C,SADY,WAAW,KAAK,IAAI,KACpB,SAAU;KAGtB,MAAM,MAAM,eAAe,KAAK,MAAM;AACtC,SAAI,KAAK;AACP,uBAAiB,KAAK,KAAK,MAAM;AACjC;;AAIF,SAAI,KAAK,MAAM,SAAS,aAAa,KAAK,MAAM,UAAU,KACxD;AAIF,SAAI,KAAK,MAAM,SAAS,mBACtB,MAAK,MAAM,aAAa,KAAK,MAAM,YAAY;AAC7C,UAAI,UAAU,SAAS,WAAY;MACnC,MAAM,WAAW,eAAe,UAAU,MAAM;AAChD,UAAI,SACF,kBAAiB,UAAU,UAAU,MAAM;;;;GAMtD;;CAEJ,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { createRule } from "../create-rule.
|
|
2
|
-
import { RADIUS_SHAPES } from "../constants.
|
|
3
|
-
import { TastyContext } from "../context.
|
|
4
|
-
import { getKeyName, getStringValue } from "../utils.
|
|
1
|
+
import { createRule } from "../create-rule.js";
|
|
2
|
+
import { RADIUS_SHAPES } from "../constants.js";
|
|
3
|
+
import { TastyContext } from "../context.js";
|
|
4
|
+
import { getKeyName, getStringValue } from "../utils.js";
|
|
5
5
|
|
|
6
6
|
//#region src/rules/valid-radius-shape.ts
|
|
7
7
|
const SHAPE_LIKE = /^[a-z]+$/;
|
|
@@ -74,4 +74,4 @@ var valid_radius_shape_default = createRule({
|
|
|
74
74
|
|
|
75
75
|
//#endregion
|
|
76
76
|
export { valid_radius_shape_default as default };
|
|
77
|
-
//# sourceMappingURL=valid-radius-shape.
|
|
77
|
+
//# sourceMappingURL=valid-radius-shape.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"valid-radius-shape.js","names":[],"sources":["../../src/rules/valid-radius-shape.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 { RADIUS_SHAPES } from '../constants.js';\n\ntype MessageIds = 'unknownShape';\n\nconst SHAPE_LIKE = /^[a-z]+$/;\n\nexport default createRule<[], MessageIds>({\n name: 'valid-radius-shape',\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Validate special shape keywords used with the radius property',\n },\n messages: {\n unknownShape:\n \"Unknown radius shape '{{shape}}'. Valid shapes: {{valid}}.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n\n function checkRadiusValue(value: string, node: TSESTree.Node): void {\n const trimmed = value.trim();\n // Only check single-word values that look like keywords\n if (!SHAPE_LIKE.test(trimmed)) return;\n\n // Known valid keywords\n if (RADIUS_SHAPES.has(trimmed)) return;\n if (trimmed === 'true' || trimmed === 'false') return;\n if (trimmed === 'none' || trimmed === 'inherit' || trimmed === 'initial')\n return;\n\n // Check if it's a directional modifier (handled elsewhere)\n const directions = new Set([\n 'top',\n 'right',\n 'bottom',\n 'left',\n 'top-left',\n 'top-right',\n 'bottom-left',\n 'bottom-right',\n ]);\n if (directions.has(trimmed)) return;\n\n // It looks like they tried to use a shape keyword\n const suggestion = findClosestShape(trimmed);\n const validList = [...RADIUS_SHAPES].join(', ');\n\n context.report({\n node,\n messageId: 'unknownShape',\n data: {\n shape: trimmed,\n valid:\n validList + (suggestion ? `. Did you mean '${suggestion}'?` : ''),\n },\n });\n }\n\n function findClosestShape(input: string): string | null {\n for (const shape of RADIUS_SHAPES) {\n if (\n shape.startsWith(input.slice(0, 3)) ||\n input.startsWith(shape.slice(0, 3))\n ) {\n return shape;\n }\n }\n return null;\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' || prop.computed) continue;\n\n const key = getKeyName(prop.key);\n if (key !== 'radius') continue;\n\n const str = getStringValue(prop.value);\n if (str) {\n checkRadiusValue(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 checkRadiusValue(stateStr, stateProp.value);\n }\n }\n }\n }\n },\n };\n },\n});\n"],"mappings":";;;;;;AAQA,MAAM,aAAa;AAEnB,iCAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aACE,iEACH;EACD,UAAU,EACR,cACE,8DACH;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;EAErC,SAAS,iBAAiB,OAAe,MAA2B;GAClE,MAAM,UAAU,MAAM,MAAM;AAE5B,OAAI,CAAC,WAAW,KAAK,QAAQ,CAAE;AAG/B,OAAI,cAAc,IAAI,QAAQ,CAAE;AAChC,OAAI,YAAY,UAAU,YAAY,QAAS;AAC/C,OAAI,YAAY,UAAU,YAAY,aAAa,YAAY,UAC7D;AAaF,OAVmB,IAAI,IAAI;IACzB;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD,CAAC,CACa,IAAI,QAAQ,CAAE;GAG7B,MAAM,aAAa,iBAAiB,QAAQ;GAC5C,MAAM,YAAY,CAAC,GAAG,cAAc,CAAC,KAAK,KAAK;AAE/C,WAAQ,OAAO;IACb;IACA,WAAW;IACX,MAAM;KACJ,OAAO;KACP,OACE,aAAa,aAAa,mBAAmB,WAAW,MAAM;KACjE;IACF,CAAC;;EAGJ,SAAS,iBAAiB,OAA8B;AACtD,QAAK,MAAM,SAAS,cAClB,KACE,MAAM,WAAW,MAAM,MAAM,GAAG,EAAE,CAAC,IACnC,MAAM,WAAW,MAAM,MAAM,GAAG,EAAE,CAAC,CAEnC,QAAO;AAGX,UAAO;;AAGT,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;AAG/C,SADY,WAAW,KAAK,IAAI,KACpB,SAAU;KAEtB,MAAM,MAAM,eAAe,KAAK,MAAM;AACtC,SAAI,KAAK;AACP,uBAAiB,KAAK,KAAK,MAAM;AACjC;;AAGF,SAAI,KAAK,MAAM,SAAS,mBACtB,MAAK,MAAM,aAAa,KAAK,MAAM,YAAY;AAC7C,UAAI,UAAU,SAAS,WAAY;MACnC,MAAM,WAAW,eAAe,UAAU,MAAM;AAChD,UAAI,SACF,kBAAiB,UAAU,UAAU,MAAM;;;;GAMtD;;CAEJ,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { createRule } from "../create-rule.
|
|
2
|
-
import { TastyContext } from "../context.
|
|
3
|
-
import { getKeyName, getStringValue } from "../utils.
|
|
1
|
+
import { createRule } from "../create-rule.js";
|
|
2
|
+
import { TastyContext } from "../context.js";
|
|
3
|
+
import { getKeyName, getStringValue } from "../utils.js";
|
|
4
4
|
|
|
5
5
|
//#region src/rules/valid-recipe.ts
|
|
6
6
|
var valid_recipe_default = createRule({
|
|
@@ -48,4 +48,4 @@ var valid_recipe_default = createRule({
|
|
|
48
48
|
|
|
49
49
|
//#endregion
|
|
50
50
|
export { valid_recipe_default as default };
|
|
51
|
-
//# sourceMappingURL=valid-recipe.
|
|
51
|
+
//# sourceMappingURL=valid-recipe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"valid-recipe.js","names":[],"sources":["../../src/rules/valid-recipe.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 = 'unknownRecipe';\n\nexport default createRule<[], MessageIds>({\n name: 'valid-recipe',\n meta: {\n type: 'problem',\n docs: {\n description: 'Validate recipe property values against config',\n },\n messages: {\n unknownRecipe: \"Unknown recipe '{{name}}'.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n\n function checkRecipeValue(value: string, node: TSESTree.Node): void {\n if (ctx.config.recipes.length === 0) return;\n\n // Split by / for pre/post merge separation\n const sections = value.split('/');\n for (const section of sections) {\n const names = section.trim().split(/\\s+/);\n for (const name of names) {\n if (name.length === 0 || name === 'none') continue;\n if (!ctx.config.recipes.includes(name)) {\n context.report({\n node,\n messageId: 'unknownRecipe',\n data: { name },\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\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 !== 'recipe') continue;\n\n const str = getStringValue(prop.value);\n if (str) {\n checkRecipeValue(str, prop.value);\n }\n }\n },\n };\n },\n});\n"],"mappings":";;;;;AAOA,2BAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aAAa,kDACd;EACD,UAAU,EACR,eAAe,8BAChB;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;EAErC,SAAS,iBAAiB,OAAe,MAA2B;AAClE,OAAI,IAAI,OAAO,QAAQ,WAAW,EAAG;GAGrC,MAAM,WAAW,MAAM,MAAM,IAAI;AACjC,QAAK,MAAM,WAAW,UAAU;IAC9B,MAAM,QAAQ,QAAQ,MAAM,CAAC,MAAM,MAAM;AACzC,SAAK,MAAM,QAAQ,OAAO;AACxB,SAAI,KAAK,WAAW,KAAK,SAAS,OAAQ;AAC1C,SAAI,CAAC,IAAI,OAAO,QAAQ,SAAS,KAAK,CACpC,SAAQ,OAAO;MACb;MACA,WAAW;MACX,MAAM,EAAE,MAAM;MACf,CAAC;;;;AAMV,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;AAG/C,SADY,WAAW,KAAK,IAAI,KACpB,SAAU;KAEtB,MAAM,MAAM,eAAe,KAAK,MAAM;AACtC,SAAI,IACF,kBAAiB,KAAK,KAAK,MAAM;;;GAIxC;;CAEJ,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { createRule } from "../create-rule.
|
|
2
|
-
import { TastyContext } from "../context.
|
|
3
|
-
import { getKeyName, getStringValue } from "../utils.
|
|
1
|
+
import { createRule } from "../create-rule.js";
|
|
2
|
+
import { TastyContext } from "../context.js";
|
|
3
|
+
import { getKeyName, getStringValue } from "../utils.js";
|
|
4
4
|
import { createStateParserContext, parseStateKey, setGlobalPredefinedStates } from "@tenphi/tasty/core";
|
|
5
5
|
|
|
6
6
|
//#region src/rules/valid-state-key.ts
|
|
@@ -139,4 +139,4 @@ var valid_state_key_default = createRule({
|
|
|
139
139
|
|
|
140
140
|
//#endregion
|
|
141
141
|
export { valid_state_key_default as default };
|
|
142
|
-
//# sourceMappingURL=valid-state-key.
|
|
142
|
+
//# sourceMappingURL=valid-state-key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"valid-state-key.js","names":[],"sources":["../../src/rules/valid-state-key.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport {\n parseStateKey,\n createStateParserContext,\n setGlobalPredefinedStates,\n} from '@tenphi/tasty/core';\nimport type { ConditionNode } from '@tenphi/tasty/core';\nimport { createRule } from '../create-rule.js';\nimport { TastyContext } from '../context.js';\nimport { getKeyName, getStringValue } from '../utils.js';\n\ntype MessageIds =\n | 'unparseable'\n | 'emptyAdvancedState'\n | 'unresolvablePredefined'\n | 'ownOutsideSubElement';\n\nfunction collectIssues(\n node: ConditionNode,\n knownPredefined: Set<string>,\n): string[] {\n const issues: string[] = [];\n\n function walk(n: ConditionNode): void {\n if (n.kind === 'true' || n.kind === 'false') return;\n\n if (n.kind === 'compound') {\n for (const child of n.children) {\n walk(child);\n }\n return;\n }\n\n switch (n.type) {\n case 'media':\n if (\n n.subtype === 'dimension' &&\n !n.dimension &&\n !n.lowerBound &&\n !n.upperBound\n ) {\n issues.push(`Empty or invalid @media dimension query in '${n.raw}'.`);\n }\n break;\n\n case 'container':\n if (\n n.subtype === 'dimension' &&\n !n.dimension &&\n !n.lowerBound &&\n !n.upperBound\n ) {\n issues.push(\n `Empty or invalid container dimension query in '${n.raw}'.`,\n );\n }\n break;\n\n case 'own':\n walk(n.innerCondition);\n break;\n\n case 'pseudo':\n if (n.raw.startsWith('@') && !knownPredefined.has(n.raw)) {\n issues.push(`Unresolvable predefined state '${n.raw}'.`);\n }\n break;\n\n default:\n break;\n }\n }\n\n walk(node);\n return issues;\n}\n\nfunction hasOwnState(node: ConditionNode): boolean {\n if (node.kind === 'true' || node.kind === 'false') return false;\n if (node.kind === 'compound') {\n return node.children.some(hasOwnState);\n }\n if (node.type === 'own') return true;\n return false;\n}\n\n/**\n * Matches the same tokens as tasty's internal STATE_TOKEN_PATTERN.\n * Characters not covered by this pattern (excluding whitespace/commas)\n * are flagged as unrecognized.\n */\nconst STATE_TOKEN_PATTERN =\n /([&|!^])|([()])|(@media:[a-z]+)|(@media\\([^)]+\\))|(@supports\\([^()]*(?:\\([^)]*\\))?[^)]*\\))|(@root\\([^)]+\\))|(@parent\\([^)]+\\))|(@own\\([^)]+\\))|(@\\([^()]*(?:\\([^)]*\\))?[^)]*\\))|(@starting)|(@[A-Za-z][A-Za-z0-9-]*)|([a-z][a-z0-9-]*(?:\\^=|\\$=|\\*=|=)(?:\"[^\"]*\"|'[^']*'|[^\\s&|!^()]+))|([a-z][a-z0-9-]+)|(:[-a-z][a-z0-9-]*(?:\\([^)]+\\))?)|(\\.[a-z][a-z0-9-]+)|(\\[[^\\]]+\\])/gi;\n\nfunction hasUnrecognizedTokens(stateKey: string): string | null {\n if (!stateKey.trim()) return null;\n\n const covered = new Set<number>();\n\n STATE_TOKEN_PATTERN.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = STATE_TOKEN_PATTERN.exec(stateKey)) !== null) {\n for (let i = match.index; i < match.index + match[0].length; i++) {\n covered.add(i);\n }\n }\n\n const uncovered: string[] = [];\n for (let i = 0; i < stateKey.length; i++) {\n const ch = stateKey[i];\n if (ch === ' ' || ch === '\\t' || ch === ',') continue;\n if (!covered.has(i)) {\n uncovered.push(ch);\n }\n }\n\n if (uncovered.length > 0) {\n const chars = [...new Set(uncovered)].join('');\n return `Unrecognized characters '${chars}' in state key '${stateKey}'.`;\n }\n\n return null;\n}\n\nexport default createRule<[], MessageIds>({\n name: 'valid-state-key',\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Validate state key syntax in style mapping objects using the tasty state parser',\n },\n messages: {\n unparseable: '{{reason}}',\n emptyAdvancedState: '{{reason}}',\n unresolvablePredefined: '{{reason}}',\n ownOutsideSubElement:\n '@own() can only be used inside sub-element styles.',\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n\n const predefinedStates: Record<string, string> = {};\n for (const alias of ctx.config.states) {\n predefinedStates[alias] = alias;\n }\n setGlobalPredefinedStates(predefinedStates);\n\n const knownPredefined = new Set(ctx.config.states);\n\n function isInsideSubElement(node: TSESTree.Node): boolean {\n let current: TSESTree.Node | undefined = node.parent;\n while (current) {\n if (\n current.type === 'Property' &&\n !current.computed &&\n current.key.type === 'Identifier' &&\n /^[A-Z]/.test(current.key.name)\n ) {\n return true;\n }\n current = current.parent;\n }\n return false;\n }\n\n function checkStateKey(\n key: string,\n keyNode: TSESTree.Node,\n insideSubElement: boolean,\n ): void {\n if (key === '') return;\n\n const tokenError = hasUnrecognizedTokens(key);\n if (tokenError) {\n context.report({\n node: keyNode,\n messageId: 'unparseable',\n data: { reason: tokenError },\n });\n return;\n }\n\n const parserContext = createStateParserContext();\n const result = parseStateKey(key, { context: parserContext });\n\n if (hasOwnState(result) && !insideSubElement) {\n context.report({\n node: keyNode,\n messageId: 'ownOutsideSubElement',\n });\n }\n\n const issues = collectIssues(result, knownPredefined);\n for (const reason of issues) {\n const messageId = reason.startsWith('Unresolvable')\n ? 'unresolvablePredefined'\n : reason.startsWith('Empty')\n ? 'emptyAdvancedState'\n : 'unparseable';\n\n context.report({\n node: keyNode,\n messageId,\n data: { reason },\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 const insideSubElement = isInsideSubElement(node);\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 if (/^[A-Z]/.test(key) || key.startsWith('@') || key.startsWith('&'))\n continue;\n\n if (prop.value.type !== 'ObjectExpression') continue;\n\n for (const stateProp of prop.value.properties) {\n if (stateProp.type !== 'Property') continue;\n\n const stateKey = !stateProp.computed\n ? getKeyName(stateProp.key)\n : getStringValue(stateProp.key);\n if (stateKey === null) continue;\n\n checkStateKey(stateKey, stateProp.key, insideSubElement);\n }\n }\n },\n };\n },\n});\n"],"mappings":";;;;;;AAiBA,SAAS,cACP,MACA,iBACU;CACV,MAAM,SAAmB,EAAE;CAE3B,SAAS,KAAK,GAAwB;AACpC,MAAI,EAAE,SAAS,UAAU,EAAE,SAAS,QAAS;AAE7C,MAAI,EAAE,SAAS,YAAY;AACzB,QAAK,MAAM,SAAS,EAAE,SACpB,MAAK,MAAM;AAEb;;AAGF,UAAQ,EAAE,MAAV;GACE,KAAK;AACH,QACE,EAAE,YAAY,eACd,CAAC,EAAE,aACH,CAAC,EAAE,cACH,CAAC,EAAE,WAEH,QAAO,KAAK,+CAA+C,EAAE,IAAI,IAAI;AAEvE;GAEF,KAAK;AACH,QACE,EAAE,YAAY,eACd,CAAC,EAAE,aACH,CAAC,EAAE,cACH,CAAC,EAAE,WAEH,QAAO,KACL,kDAAkD,EAAE,IAAI,IACzD;AAEH;GAEF,KAAK;AACH,SAAK,EAAE,eAAe;AACtB;GAEF,KAAK;AACH,QAAI,EAAE,IAAI,WAAW,IAAI,IAAI,CAAC,gBAAgB,IAAI,EAAE,IAAI,CACtD,QAAO,KAAK,kCAAkC,EAAE,IAAI,IAAI;AAE1D;GAEF,QACE;;;AAIN,MAAK,KAAK;AACV,QAAO;;AAGT,SAAS,YAAY,MAA8B;AACjD,KAAI,KAAK,SAAS,UAAU,KAAK,SAAS,QAAS,QAAO;AAC1D,KAAI,KAAK,SAAS,WAChB,QAAO,KAAK,SAAS,KAAK,YAAY;AAExC,KAAI,KAAK,SAAS,MAAO,QAAO;AAChC,QAAO;;;;;;;AAQT,MAAM,sBACJ;AAEF,SAAS,sBAAsB,UAAiC;AAC9D,KAAI,CAAC,SAAS,MAAM,CAAE,QAAO;CAE7B,MAAM,0BAAU,IAAI,KAAa;AAEjC,qBAAoB,YAAY;CAChC,IAAI;AACJ,SAAQ,QAAQ,oBAAoB,KAAK,SAAS,MAAM,KACtD,MAAK,IAAI,IAAI,MAAM,OAAO,IAAI,MAAM,QAAQ,MAAM,GAAG,QAAQ,IAC3D,SAAQ,IAAI,EAAE;CAIlB,MAAM,YAAsB,EAAE;AAC9B,MAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;EACxC,MAAM,KAAK,SAAS;AACpB,MAAI,OAAO,OAAO,OAAO,OAAQ,OAAO,IAAK;AAC7C,MAAI,CAAC,QAAQ,IAAI,EAAE,CACjB,WAAU,KAAK,GAAG;;AAItB,KAAI,UAAU,SAAS,EAErB,QAAO,4BADO,CAAC,GAAG,IAAI,IAAI,UAAU,CAAC,CAAC,KAAK,GAAG,CACL,kBAAkB,SAAS;AAGtE,QAAO;;AAGT,8BAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aACE,mFACH;EACD,UAAU;GACR,aAAa;GACb,oBAAoB;GACpB,wBAAwB;GACxB,sBACE;GACH;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;EAErC,MAAM,mBAA2C,EAAE;AACnD,OAAK,MAAM,SAAS,IAAI,OAAO,OAC7B,kBAAiB,SAAS;AAE5B,4BAA0B,iBAAiB;EAE3C,MAAM,kBAAkB,IAAI,IAAI,IAAI,OAAO,OAAO;EAElD,SAAS,mBAAmB,MAA8B;GACxD,IAAI,UAAqC,KAAK;AAC9C,UAAO,SAAS;AACd,QACE,QAAQ,SAAS,cACjB,CAAC,QAAQ,YACT,QAAQ,IAAI,SAAS,gBACrB,SAAS,KAAK,QAAQ,IAAI,KAAK,CAE/B,QAAO;AAET,cAAU,QAAQ;;AAEpB,UAAO;;EAGT,SAAS,cACP,KACA,SACA,kBACM;AACN,OAAI,QAAQ,GAAI;GAEhB,MAAM,aAAa,sBAAsB,IAAI;AAC7C,OAAI,YAAY;AACd,YAAQ,OAAO;KACb,MAAM;KACN,WAAW;KACX,MAAM,EAAE,QAAQ,YAAY;KAC7B,CAAC;AACF;;GAIF,MAAM,SAAS,cAAc,KAAK,EAAE,SADd,0BAA0B,EACY,CAAC;AAE7D,OAAI,YAAY,OAAO,IAAI,CAAC,iBAC1B,SAAQ,OAAO;IACb,MAAM;IACN,WAAW;IACZ,CAAC;GAGJ,MAAM,SAAS,cAAc,QAAQ,gBAAgB;AACrD,QAAK,MAAM,UAAU,QAAQ;IAC3B,MAAM,YAAY,OAAO,WAAW,eAAe,GAC/C,2BACA,OAAO,WAAW,QAAQ,GACxB,uBACA;AAEN,YAAQ,OAAO;KACb,MAAM;KACN;KACA,MAAM,EAAE,QAAQ;KACjB,CAAC;;;AAIN,SAAO;GACL,kBAAkB,MAAM;AACtB,QAAI,YAAY,KAAK;;GAGvB,kCAAkC,MAAiC;AACjE,QAAI,CAAC,IAAI,cAAc,KAAK,CAAE;IAE9B,MAAM,mBAAmB,mBAAmB,KAAK;AAEjD,SAAK,MAAM,QAAQ,KAAK,YAAY;AAClC,SAAI,KAAK,SAAS,cAAc,KAAK,SAAU;KAE/C,MAAM,MAAM,WAAW,KAAK,IAAI;AAChC,SAAI,QAAQ,KAAM;AAElB,SAAI,SAAS,KAAK,IAAI,IAAI,IAAI,WAAW,IAAI,IAAI,IAAI,WAAW,IAAI,CAClE;AAEF,SAAI,KAAK,MAAM,SAAS,mBAAoB;AAE5C,UAAK,MAAM,aAAa,KAAK,MAAM,YAAY;AAC7C,UAAI,UAAU,SAAS,WAAY;MAEnC,MAAM,WAAW,CAAC,UAAU,WACxB,WAAW,UAAU,IAAI,GACzB,eAAe,UAAU,IAAI;AACjC,UAAI,aAAa,KAAM;AAEvB,oBAAc,UAAU,UAAU,KAAK,iBAAiB;;;;GAI/D;;CAEJ,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { createRule } from "../create-rule.
|
|
2
|
-
import { TastyContext } from "../context.
|
|
3
|
-
import { getKeyName, getStringValue } from "../utils.
|
|
1
|
+
import { createRule } from "../create-rule.js";
|
|
2
|
+
import { TastyContext } from "../context.js";
|
|
3
|
+
import { getKeyName, getStringValue } from "../utils.js";
|
|
4
4
|
|
|
5
5
|
//#region src/rules/valid-styles-structure.ts
|
|
6
6
|
var valid_styles_structure_default = createRule({
|
|
@@ -76,4 +76,4 @@ var valid_styles_structure_default = createRule({
|
|
|
76
76
|
|
|
77
77
|
//#endregion
|
|
78
78
|
export { valid_styles_structure_default as default };
|
|
79
|
-
//# sourceMappingURL=valid-styles-structure.
|
|
79
|
+
//# sourceMappingURL=valid-styles-structure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"valid-styles-structure.js","names":[],"sources":["../../src/rules/valid-styles-structure.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 =\n | 'stateKeyAtTopLevel'\n | 'invalidKeyframesStructure'\n | 'invalidPropertiesStructure'\n | 'recipeNotString';\n\nexport default createRule<[], MessageIds>({\n name: 'valid-styles-structure',\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Validate overall structure of styles object passed to tasty APIs',\n },\n messages: {\n stateKeyAtTopLevel:\n \"State key '{{key}}' at top level is not valid. State maps belong inside property values, not at the root of the styles object.\",\n invalidKeyframesStructure:\n '@keyframes value must be an object of { name: { step: styles } }.',\n invalidPropertiesStructure:\n '@properties value must be an object of { name: { syntax, inherits, initialValue } }.',\n recipeNotString: \"'recipe' value must be a string.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n\n const STATE_KEY_PATTERNS = [\n /^:/, // pseudo-class\n /^\\./, // class selector\n /^\\[/, // attribute selector\n ];\n\n function looksLikeStateKey(key: string): boolean {\n if (key === '') return true;\n return STATE_KEY_PATTERNS.some((p) => p.test(key));\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' || prop.computed) continue;\n\n const key = getKeyName(prop.key);\n if (key === null) continue;\n\n // Check for state keys at top level (common mistake)\n if (looksLikeStateKey(key)) {\n context.report({\n node: prop.key,\n messageId: 'stateKeyAtTopLevel',\n data: { key },\n });\n continue;\n }\n\n // Validate @keyframes structure\n if (key === '@keyframes') {\n if (prop.value.type !== 'ObjectExpression') {\n context.report({\n node: prop.value,\n messageId: 'invalidKeyframesStructure',\n });\n }\n continue;\n }\n\n // Validate @properties structure\n if (key === '@properties') {\n if (prop.value.type !== 'ObjectExpression') {\n context.report({\n node: prop.value,\n messageId: 'invalidPropertiesStructure',\n });\n }\n continue;\n }\n\n // Validate recipe is a string\n if (key === 'recipe') {\n const str = getStringValue(prop.value);\n if (str === null && prop.value.type !== 'Literal') {\n // Allow string literals, template literals without expressions\n if (\n prop.value.type !== 'TemplateLiteral' ||\n prop.value.expressions.length > 0\n ) {\n context.report({\n node: prop.value,\n messageId: 'recipeNotString',\n });\n }\n }\n }\n }\n },\n };\n },\n});\n"],"mappings":";;;;;AAWA,qCAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aACE,oEACH;EACD,UAAU;GACR,oBACE;GACF,2BACE;GACF,4BACE;GACF,iBAAiB;GAClB;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;EAErC,MAAM,qBAAqB;GACzB;GACA;GACA;GACD;EAED,SAAS,kBAAkB,KAAsB;AAC/C,OAAI,QAAQ,GAAI,QAAO;AACvB,UAAO,mBAAmB,MAAM,MAAM,EAAE,KAAK,IAAI,CAAC;;AAGpD,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,kBAAkB,IAAI,EAAE;AAC1B,cAAQ,OAAO;OACb,MAAM,KAAK;OACX,WAAW;OACX,MAAM,EAAE,KAAK;OACd,CAAC;AACF;;AAIF,SAAI,QAAQ,cAAc;AACxB,UAAI,KAAK,MAAM,SAAS,mBACtB,SAAQ,OAAO;OACb,MAAM,KAAK;OACX,WAAW;OACZ,CAAC;AAEJ;;AAIF,SAAI,QAAQ,eAAe;AACzB,UAAI,KAAK,MAAM,SAAS,mBACtB,SAAQ,OAAO;OACb,MAAM,KAAK;OACX,WAAW;OACZ,CAAC;AAEJ;;AAIF,SAAI,QAAQ,UAEV;UADY,eAAe,KAAK,MAAM,KAC1B,QAAQ,KAAK,MAAM,SAAS,WAEtC;WACE,KAAK,MAAM,SAAS,qBACpB,KAAK,MAAM,YAAY,SAAS,EAEhC,SAAQ,OAAO;QACb,MAAM,KAAK;QACX,WAAW;QACZ,CAAC;;;;;GAMb;;CAEJ,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { createRule } from "../create-rule.
|
|
2
|
-
import { TastyContext } from "../context.
|
|
3
|
-
import { getKeyName } from "../utils.
|
|
1
|
+
import { createRule } from "../create-rule.js";
|
|
2
|
+
import { TastyContext } from "../context.js";
|
|
3
|
+
import { getKeyName } from "../utils.js";
|
|
4
4
|
|
|
5
5
|
//#region src/rules/valid-sub-element.ts
|
|
6
6
|
var valid_sub_element_default = createRule({
|
|
@@ -43,4 +43,4 @@ var valid_sub_element_default = createRule({
|
|
|
43
43
|
|
|
44
44
|
//#endregion
|
|
45
45
|
export { valid_sub_element_default as default };
|
|
46
|
-
//# sourceMappingURL=valid-sub-element.
|
|
46
|
+
//# sourceMappingURL=valid-sub-element.js.map
|
|
@@ -0,0 +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 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 || !/^[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 },\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;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,QAAQ,CAAC,SAAS,KAAK,IAAI,CAAE;AAEzC,SAAI,KAAK,MAAM,SAAS,oBAAoB;MAC1C,MAAM,YACJ,KAAK,MAAM,SAAS,YAChB,OAAO,KAAK,MAAM,QAClB,KAAK,MAAM;AAEjB,cAAQ,OAAO;OACb,MAAM,KAAK;OACX,WAAW;OACX,MAAM;QAAE,MAAM;QAAK,MAAM;QAAW;OACrC,CAAC;;;;GAIT;;CAEJ,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { createRule } from "../create-rule.
|
|
2
|
-
import { KNOWN_CSS_PROPERTIES, SEMANTIC_TRANSITIONS } from "../constants.
|
|
3
|
-
import { TastyContext } from "../context.
|
|
4
|
-
import { getKeyName, getStringValue } from "../utils.
|
|
1
|
+
import { createRule } from "../create-rule.js";
|
|
2
|
+
import { KNOWN_CSS_PROPERTIES, SEMANTIC_TRANSITIONS } from "../constants.js";
|
|
3
|
+
import { TastyContext } from "../context.js";
|
|
4
|
+
import { getKeyName, getStringValue } from "../utils.js";
|
|
5
5
|
|
|
6
6
|
//#region src/rules/valid-transition.ts
|
|
7
7
|
var valid_transition_default = createRule({
|
|
@@ -59,4 +59,4 @@ var valid_transition_default = createRule({
|
|
|
59
59
|
|
|
60
60
|
//#endregion
|
|
61
61
|
export { valid_transition_default as default };
|
|
62
|
-
//# sourceMappingURL=valid-transition.
|
|
62
|
+
//# sourceMappingURL=valid-transition.js.map
|
|
@@ -0,0 +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 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 !== '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 },\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;;;AAKR,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;AAG/C,SADY,WAAW,KAAK,IAAI,KACpB,aAAc;KAE1B,MAAM,MAAM,eAAe,KAAK,MAAM;AACtC,SAAI,KAAK;AACP,2BAAqB,KAAK,KAAK,MAAM;AACrC;;AAGF,SAAI,KAAK,MAAM,SAAS,mBACtB,MAAK,MAAM,aAAa,KAAK,MAAM,YAAY;AAC7C,UAAI,UAAU,SAAS,WAAY;MACnC,MAAM,WAAW,eAAe,UAAU,MAAM;AAChD,UAAI,SACF,sBAAqB,UAAU,UAAU,MAAM;;;;GAM1D;;CAEJ,CAAC"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { createRule } from "../create-rule.
|
|
2
|
-
import { TastyContext } from "../context.
|
|
3
|
-
import { getKeyName, getStringValue } from "../utils.
|
|
4
|
-
import { getParser } from "../parser.
|
|
5
|
-
import { getExpectation } from "../property-expectations.
|
|
1
|
+
import { createRule } from "../create-rule.js";
|
|
2
|
+
import { TastyContext } from "../context.js";
|
|
3
|
+
import { getKeyName, getStringValue } from "../utils.js";
|
|
4
|
+
import { getParser } from "../parser.js";
|
|
5
|
+
import { getExpectation } from "../property-expectations.js";
|
|
6
6
|
|
|
7
7
|
//#region src/rules/valid-value.ts
|
|
8
8
|
var valid_value_default = createRule({
|
|
@@ -120,4 +120,4 @@ var valid_value_default = createRule({
|
|
|
120
120
|
|
|
121
121
|
//#endregion
|
|
122
122
|
export { valid_value_default as default };
|
|
123
|
-
//# sourceMappingURL=valid-value.
|
|
123
|
+
//# sourceMappingURL=valid-value.js.map
|
|
@@ -0,0 +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\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 if (expectation.acceptsMods === false && group.mods.length > 0) {\n for (const mod of group.mods) {\n context.report({\n node,\n messageId: 'unexpectedMod',\n data: { property, mod },\n });\n }\n } else if (\n Array.isArray(expectation.acceptsMods) &&\n group.mods.length > 0\n ) {\n const allowed = new Set(expectation.acceptsMods);\n for (const mod of group.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":";;;;;;;AAcA,0BAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aACE,8FACH;EACD,UAAU;GACR,kBAAkB;GAClB,qBACE;GACF,eACE;GACF,iBACE;GACF,YACE;GACH;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;EAErC,SAAS,kBAAkB,OAAe,MAA8B;GACtE,IAAI,QAAQ;AACZ,QAAK,MAAM,QAAQ,OAAO;AACxB,QAAI,SAAS,IAAK;AAClB,QAAI,SAAS,IAAK;AAClB,QAAI,QAAQ,GAAG;AACb,aAAQ,OAAO;MAAE;MAAM,WAAW;MAAoB,CAAC;AACvD,YAAO;;;AAGX,OAAI,UAAU,GAAG;AACf,YAAQ,OAAO;KAAE;KAAM,WAAW;KAAoB,CAAC;AACvD,WAAO;;AAET,UAAO;;EAGT,SAAS,WACP,OACA,UACA,MACM;AACN,OAAI,CAAC,kBAAkB,OAAO,KAAK,CAAE;AAErC,OAAI,MAAM,SAAS,aAAa,EAAE;AAChC,YAAQ,OAAO;KAAE;KAAM,WAAW;KAAuB,CAAC;AAC1D;;AAGF,OAAI,CAAC,SAAU;GAGf,MAAM,SADS,UAAU,IAAI,OAAO,CACd,QAAQ,MAAM;GACpC,MAAM,cAAc,eAAe,SAAS;AAE5C,QAAK,MAAM,SAAS,OAAO,QAAQ;AACjC,QAAI,CAAC,YAAY,gBAAgB,MAAM,OAAO,SAAS,EACrD,MAAK,MAAM,SAAS,MAAM,OACxB,SAAQ,OAAO;KACb;KACA,WAAW;KACX,MAAM;MAAE;MAAU;MAAO;KAC1B,CAAC;AAIN,QAAI,YAAY,gBAAgB,SAAS,MAAM,KAAK,SAAS,EAC3D,MAAK,MAAM,OAAO,MAAM,KACtB,SAAQ,OAAO;KACb;KACA,WAAW;KACX,MAAM;MAAE;MAAU;MAAK;KACxB,CAAC;aAGJ,MAAM,QAAQ,YAAY,YAAY,IACtC,MAAM,KAAK,SAAS,GACpB;KACA,MAAM,UAAU,IAAI,IAAI,YAAY,YAAY;AAChD,UAAK,MAAM,OAAO,MAAM,KACtB,KAAI,CAAC,QAAQ,IAAI,IAAI,CACnB,SAAQ,OAAO;MACb;MACA,WAAW;MACX,MAAM;OACJ;OACA;OACA,UAAU,YAAY,YAAY,KAAK,KAAK;OAC7C;MACF,CAAC;;;;EAOZ,SAAS,gBAAgB,MAA+B;GACtD,MAAM,MAAM,CAAC,KAAK,WAAW,WAAW,KAAK,IAAI,GAAG;AAEpD,OAAI,QAAQ,SAAS,KAAK,IAAI,IAAI,IAAI,WAAW,IAAI,EAAG;AACxD,OAAI,QAAQ,IAAI,WAAW,IAAI,IAAI,IAAI,WAAW,IAAI,EAAG;AACzD,OAAI,OAAO,IAAI,WAAW,IAAI,CAAE;GAEhC,MAAM,MAAM,eAAe,KAAK,MAAM;AACtC,OAAI,KAAK;AACP,eAAW,KAAK,KAAK,KAAK,MAAM;AAChC;;AAIF,OAAI,KAAK,MAAM,SAAS,mBACtB,MAAK,MAAM,aAAa,KAAK,MAAM,YAAY;AAC7C,QAAI,UAAU,SAAS,WAAY;IACnC,MAAM,WAAW,eAAe,UAAU,MAAM;AAChD,QAAI,SACF,YAAW,UAAU,KAAK,UAAU,MAAM;;;AAMlD,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,WAAY;AAC9B,qBAAgB,KAAK;;;GAG1B;;CAEJ,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BUILT_IN_STATE_PREFIXES, BUILT_IN_UNITS, CSS_UNITS } from "./constants.
|
|
1
|
+
import { BUILT_IN_STATE_PREFIXES, BUILT_IN_UNITS, CSS_UNITS } from "./constants.js";
|
|
2
2
|
|
|
3
3
|
//#region src/utils.ts
|
|
4
4
|
/**
|
|
@@ -112,4 +112,4 @@ function isValidSelector(selector) {
|
|
|
112
112
|
|
|
113
113
|
//#endregion
|
|
114
114
|
export { extractCustomUnit, getKeyName, getStringValue, isKnownStateAlias, isRawHexColor, isStaticValue, isValidSelector, isValidUnit, validateColorTokenSyntax };
|
|
115
|
-
//# sourceMappingURL=utils.
|
|
115
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport {\n BUILT_IN_UNITS,\n CSS_UNITS,\n BUILT_IN_STATE_PREFIXES,\n} from './constants.js';\nimport type { ResolvedConfig } from './types.js';\n\n/**\n * Gets the string value of a property key node.\n */\nexport function getKeyName(key: TSESTree.Node): string | null {\n if (key.type === 'Identifier') return key.name;\n if (key.type === 'Literal' && typeof key.value === 'string') return key.value;\n if (key.type === 'Literal' && typeof key.value === 'number')\n return String(key.value);\n return null;\n}\n\n/**\n * Gets the string value of a node if it is a string literal.\n */\nexport function getStringValue(node: TSESTree.Node): string | null {\n if (node.type === 'Literal' && typeof node.value === 'string') {\n return node.value;\n }\n if (node.type === 'TemplateLiteral' && node.expressions.length === 0) {\n return node.quasis[0].value.cooked ?? null;\n }\n return null;\n}\n\n/**\n * Checks if a value node is a static literal.\n */\nexport function isStaticValue(node: TSESTree.Node): boolean {\n if (node.type === 'Literal') return true;\n if (\n node.type === 'UnaryExpression' &&\n node.operator === '-' &&\n node.argument.type === 'Literal'\n ) {\n return true;\n }\n if (node.type === 'TemplateLiteral' && node.expressions.length === 0) {\n return true;\n }\n if (node.type === 'ArrayExpression') {\n return node.elements.every((el) => el !== null && isStaticValue(el));\n }\n if (node.type === 'ObjectExpression') {\n return node.properties.every(\n (prop) =>\n prop.type === 'Property' && !prop.computed && isStaticValue(prop.value),\n );\n }\n return false;\n}\n\n/**\n * Validates color token syntax.\n * Returns null if valid, or an error message if invalid.\n */\nexport function validateColorTokenSyntax(token: string): string | null {\n // Strip leading # or ##\n let name = token;\n if (name.startsWith('##')) {\n name = name.slice(2);\n } else if (name.startsWith('#')) {\n name = name.slice(1);\n } else {\n return 'Color token must start with #';\n }\n\n if (name.length === 0) return 'Empty color token name';\n\n // Check for opacity suffix\n const dotIndex = name.indexOf('.');\n if (dotIndex !== -1) {\n const tokenName = name.slice(0, dotIndex);\n const opacitySuffix = name.slice(dotIndex + 1);\n\n if (tokenName.length === 0) return 'Empty color token name before opacity';\n\n if (opacitySuffix.startsWith('$')) {\n // Dynamic opacity from CSS custom property — always valid\n return null;\n }\n\n if (opacitySuffix.length === 0) return 'Trailing dot with no opacity value';\n\n const opacity = Number(opacitySuffix);\n if (isNaN(opacity)) return `Invalid opacity value '${opacitySuffix}'`;\n if (opacity < 0) return 'Opacity cannot be negative';\n if (opacity > 100) return `Opacity '${opacitySuffix}' exceeds 100`;\n }\n\n return null;\n}\n\n/**\n * Checks if a string looks like a raw hex color (not a token).\n * Hex colors: #fff, #ffff, #ffffff, #ffffffff (3, 4, 6, or 8 hex chars).\n */\nexport function isRawHexColor(value: string): boolean {\n if (!value.startsWith('#')) return false;\n const hex = value.slice(1).split('.')[0];\n if (![3, 4, 6, 8].includes(hex.length)) return false;\n return /^[0-9a-fA-F]+$/.test(hex);\n}\n\n/**\n * Extracts custom unit from a value token like \"2x\", \"1.5r\", \"3cols\".\n * Returns the unit name, or null if not a custom-unit value.\n */\nexport function extractCustomUnit(token: string): string | null {\n const match = token.match(/^-?[\\d.]+([a-zA-Z]+)$/);\n if (!match) return null;\n return match[1];\n}\n\n/**\n * Checks if a unit is valid (built-in, CSS, or in config).\n */\nexport function isValidUnit(unit: string, config: ResolvedConfig): boolean {\n if (config.units === false) return true;\n if (BUILT_IN_UNITS.has(unit)) return true;\n if (CSS_UNITS.has(unit)) return true;\n if (Array.isArray(config.units) && config.units.includes(unit)) return true;\n return false;\n}\n\n/**\n * Checks if a state alias key (starting with @) is known.\n */\nexport function isKnownStateAlias(\n key: string,\n config: ResolvedConfig,\n): boolean {\n // Built-in prefixes\n for (const prefix of BUILT_IN_STATE_PREFIXES) {\n if (key === prefix || key.startsWith(prefix + '(')) return true;\n }\n // Container query shorthand\n if (key.startsWith('@(')) return true;\n // Config aliases\n return config.states.includes(key);\n}\n\n/**\n * Checks if a CSS selector string is basically valid.\n */\nexport function isValidSelector(selector: string): string | null {\n if (selector.length === 0) return 'Selector cannot be empty';\n\n // Check balanced brackets\n let depth = 0;\n for (const char of selector) {\n if (char === '(' || char === '[') depth++;\n if (char === ')' || char === ']') depth--;\n if (depth < 0) return 'Unbalanced brackets in selector';\n }\n if (depth !== 0) return 'Unbalanced brackets in selector';\n\n return null;\n}\n\n/**\n * Finds a property by key name in an object expression.\n */\nexport function findProperty(\n obj: TSESTree.ObjectExpression,\n name: string,\n): TSESTree.Property | undefined {\n for (const prop of obj.properties) {\n if (prop.type === 'Property' && !prop.computed) {\n const keyName = getKeyName(prop.key);\n if (keyName === name) return prop;\n }\n }\n return undefined;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
package/dist/config.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config.mjs","names":[],"sources":["../src/config.ts"],"sourcesContent":["import { existsSync, readFileSync, statSync } from 'fs';\nimport { dirname, join, resolve } from 'path';\nimport type { ResolvedConfig, TastyValidationConfig } from './types.js';\nimport { DEFAULT_IMPORT_SOURCES } from './constants.js';\n\nconst CONFIG_FILENAMES = [\n 'tasty.config.ts',\n 'tasty.config.js',\n 'tasty.config.mjs',\n 'tasty.config.json',\n];\n\ninterface CachedConfig {\n config: ResolvedConfig;\n mtimes: Map<string, number>;\n}\n\nlet cachedConfig: CachedConfig | null = null;\n\nfunction findProjectRoot(startDir: string): string | null {\n let dir = startDir;\n while (dir !== dirname(dir)) {\n if (existsSync(join(dir, 'package.json'))) {\n return dir;\n }\n dir = dirname(dir);\n }\n return null;\n}\n\nfunction findConfigFile(projectRoot: string): string | null {\n for (const name of CONFIG_FILENAMES) {\n const path = join(projectRoot, name);\n if (existsSync(path)) {\n return path;\n }\n }\n return null;\n}\n\nfunction loadRawConfig(configPath: string): TastyValidationConfig {\n const content = readFileSync(configPath, 'utf-8');\n\n if (configPath.endsWith('.json')) {\n return JSON.parse(content) as TastyValidationConfig;\n }\n\n // For TS/JS files, extract JSON-like config or use a simple parser.\n // In a real implementation, we'd use jiti or tsx to load TS configs.\n // For now, attempt JSON parse of the default export pattern.\n const jsonMatch = content.match(\n /export\\s+default\\s+({[\\s\\S]*?})\\s*(?:;|\\n|$)/,\n );\n if (jsonMatch) {\n try {\n const fn = new Function(`return (${jsonMatch[1]})`);\n return fn() as TastyValidationConfig;\n } catch {\n // fall through\n }\n }\n\n return {};\n}\n\nfunction mergeConfigs(\n parent: TastyValidationConfig,\n child: TastyValidationConfig,\n): TastyValidationConfig {\n const result: TastyValidationConfig = { ...parent };\n\n const arrayKeys = [\n 'tokens',\n 'units',\n 'funcs',\n 'states',\n 'presets',\n 'recipes',\n 'styles',\n 'importSources',\n ] as const;\n\n for (const key of arrayKeys) {\n const childVal = child[key];\n if (childVal === undefined) continue;\n\n if (childVal === false) {\n (result as Record<string, unknown>)[key] = false;\n continue;\n }\n\n const parentVal = parent[key];\n if (Array.isArray(parentVal) && Array.isArray(childVal)) {\n (result as Record<string, unknown>)[key] = [\n ...new Set([...parentVal, ...childVal]),\n ];\n } else {\n (result as Record<string, unknown>)[key] = childVal;\n }\n }\n\n return result;\n}\n\nfunction resolveConfigChain(\n configPath: string,\n visited = new Set<string>(),\n): TastyValidationConfig {\n const absPath = resolve(configPath);\n if (visited.has(absPath)) return {};\n visited.add(absPath);\n\n const config = loadRawConfig(absPath);\n\n if (!config.extends) return config;\n\n let parentPath: string;\n if (config.extends.startsWith('.') || config.extends.startsWith('/')) {\n parentPath = resolve(dirname(absPath), config.extends);\n } else {\n try {\n parentPath = require.resolve(config.extends);\n } catch {\n return config;\n }\n }\n\n const parentConfig = resolveConfigChain(parentPath, visited);\n return mergeConfigs(parentConfig, config);\n}\n\nfunction toResolved(config: TastyValidationConfig): ResolvedConfig {\n return {\n tokens: config.tokens ?? [],\n units: config.units ?? [],\n funcs: config.funcs ?? [],\n states: config.states ?? [],\n presets: config.presets ?? [],\n recipes: config.recipes ?? [],\n styles: config.styles ?? [],\n importSources: config.importSources ?? DEFAULT_IMPORT_SOURCES,\n };\n}\n\nconst DEFAULT_CONFIG: ResolvedConfig = {\n tokens: [],\n units: [],\n funcs: [],\n states: [],\n presets: [],\n recipes: [],\n styles: [],\n importSources: DEFAULT_IMPORT_SOURCES,\n};\n\nexport function loadConfig(filePath: string): ResolvedConfig {\n const projectRoot = findProjectRoot(dirname(resolve(filePath)));\n if (!projectRoot) return DEFAULT_CONFIG;\n\n const configFile = findConfigFile(projectRoot);\n if (!configFile) return DEFAULT_CONFIG;\n\n const currentMtime = statSync(configFile).mtimeMs;\n\n if (cachedConfig) {\n const cachedMtime = cachedConfig.mtimes.get(configFile);\n if (cachedMtime === currentMtime) {\n return cachedConfig.config;\n }\n }\n\n const rawConfig = resolveConfigChain(configFile);\n const resolved = toResolved(rawConfig);\n\n cachedConfig = {\n config: resolved,\n mtimes: new Map([[configFile, currentMtime]]),\n };\n\n return resolved;\n}\n"],"mappings":"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"}
|