pasika 0.10.133 → 0.10.134

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.
@@ -124,6 +124,7 @@ declare const tailwindRules: {
124
124
  "theme-reset": _eslint_css.CSSRuleDefinition;
125
125
  "theme-mode": _eslint_css.CSSRuleDefinition;
126
126
  "color-role-usage": _eslint_css.CSSRuleDefinition;
127
+ "color-literal-usage": _eslint_css.CSSRuleDefinition;
127
128
  "root-variables": _eslint_css.CSSRuleDefinition;
128
129
  "apply-usage": _eslint_css.CSSRuleDefinition;
129
130
  "base-layer-pair": _eslint_css.CSSRuleDefinition;
@@ -166,6 +167,7 @@ declare const pasikaPlugin: {
166
167
  "theme-reset": _eslint_css.CSSRuleDefinition;
167
168
  "theme-mode": _eslint_css.CSSRuleDefinition;
168
169
  "color-role-usage": _eslint_css.CSSRuleDefinition;
170
+ "color-literal-usage": _eslint_css.CSSRuleDefinition;
169
171
  "root-variables": _eslint_css.CSSRuleDefinition;
170
172
  "apply-usage": _eslint_css.CSSRuleDefinition;
171
173
  "base-layer-pair": _eslint_css.CSSRuleDefinition;
@@ -9909,6 +9909,96 @@ var baseLayerPairRule = {
9909
9909
  }
9910
9910
  };
9911
9911
 
9912
+ // eslint/rules/tailwind/color-literal-usage.ts
9913
+ var COLOR_FUNCTION = /\b(?:rgba?|hsla?|hwb|lab|lch|oklab|oklch|color)\s*\(/giu;
9914
+ var HEX_COLOR = /#(?:[\da-f]{8}|[\da-f]{6}|[\da-f]{4}|[\da-f]{3})(?![\da-f])/giu;
9915
+ var NAMED_COLORS = new Set(
9916
+ "aliceblue antiquewhite aqua aquamarine azure beige bisque black blanchedalmond blue blueviolet brown burlywood cadetblue chartreuse chocolate coral cornflowerblue cornsilk crimson cyan darkblue darkcyan darkgoldenrod darkgray darkgreen darkgrey darkkhaki darkmagenta darkolivegreen darkorange darkorchid darkred darksalmon darkseagreen darkslateblue darkslategray darkslategrey darkturquoise darkviolet deeppink deepskyblue dimgray dimgrey dodgerblue firebrick floralwhite forestgreen fuchsia gainsboro ghostwhite gold goldenrod gray green greenyellow grey honeydew hotpink indianred indigo ivory khaki lavender lavenderblush lawngreen lemonchiffon lightblue lightcoral lightcyan lightgoldenrodyellow lightgray lightgreen lightgrey lightpink lightsalmon lightseagreen lightskyblue lightslategray lightslategrey lightsteelblue lightyellow lime limegreen linen magenta maroon mediumaquamarine mediumblue mediumorchid mediumpurple mediumseagreen mediumslateblue mediumspringgreen mediumturquoise mediumvioletred midnightblue mintcream mistyrose moccasin navajowhite navy oldlace olive olivedrab orange orangered orchid palegoldenrod palegreen paleturquoise palevioletred papayawhip peachpuff peru pink plum powderblue purple rebeccapurple red rosybrown royalblue saddlebrown salmon sandybrown seagreen seashell sienna silver skyblue slateblue slategray slategrey snow springgreen steelblue tan teal thistle tomato turquoise violet wheat white whitesmoke yellow yellowgreen".split(" ")
9917
+ );
9918
+ var DECLARATION = /(?:^|[;{}\n])\s*(?<property>--[\w-]+|[a-z][\w-]*)\s*:\s*(?<value>[^;{}]*)/gmu;
9919
+ var APPLY = /@apply\s+(?<value>[^;{}]*);/gu;
9920
+ function maskNonColors(source) {
9921
+ const spaces = (text) => text.replace(/[^\r\n]/gu, " ");
9922
+ const masked = source.replace(/\/\*[\s\S]*?\*\/|"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'/gu, spaces);
9923
+ return masked.replace(/\burl\s*\([^)]*\)/giu, spaces);
9924
+ }
9925
+ function matchingClose(text, open) {
9926
+ let depth = 0;
9927
+ for (let index = open; index < text.length; index++) {
9928
+ if (text[index] === "(") depth++;
9929
+ if (text[index] === ")" && --depth === 0) return index + 1;
9930
+ }
9931
+ return -1;
9932
+ }
9933
+ function colorLiterals(value) {
9934
+ const matches = [];
9935
+ const occupied = [];
9936
+ for (const functionMatch of value.matchAll(COLOR_FUNCTION)) {
9937
+ const start = functionMatch.index;
9938
+ if (occupied.some((range) => start >= range.start && start < range.end)) continue;
9939
+ const open = start + functionMatch[0].lastIndexOf("(");
9940
+ const end = matchingClose(value, open);
9941
+ if (end < 0) continue;
9942
+ const text = value.slice(start, end);
9943
+ occupied.push({ start, end });
9944
+ if (!/\b(?:var|env|attr)\s*\(/iu.test(text)) matches.push({ text, offset: start });
9945
+ }
9946
+ for (const match of value.matchAll(HEX_COLOR)) {
9947
+ if (occupied.some((range) => match.index >= range.start && match.index < range.end)) continue;
9948
+ if (match.index > 0 && /[\w#-]/u.test(value[match.index - 1] ?? "")) continue;
9949
+ matches.push({ text: match[0], offset: match.index });
9950
+ }
9951
+ for (const word of value.matchAll(/\b[a-z]+\b/giu)) {
9952
+ if (!NAMED_COLORS.has(word[0].toLowerCase())) continue;
9953
+ if (occupied.some((range) => word.index >= range.start && word.index < range.end)) continue;
9954
+ if (word.index > 0 && /[-\w]/u.test(value[word.index - 1] ?? "")) continue;
9955
+ if (/[-\w]/u.test(value[word.index + word[0].length] ?? "")) continue;
9956
+ matches.push({ text: word[0], offset: word.index });
9957
+ }
9958
+ return matches.sort((a, b) => a.offset - b.offset);
9959
+ }
9960
+ var colorLiteralUsageRule = {
9961
+ meta: {
9962
+ schema: [],
9963
+ type: "problem",
9964
+ docs: {
9965
+ description: "Require fixed CSS color literals to live only in --color-* token definitions."
9966
+ }
9967
+ },
9968
+ create(context) {
9969
+ return {
9970
+ "StyleSheet:exit"(node) {
9971
+ const masked = maskNonColors(context.sourceCode.text);
9972
+ const reported = /* @__PURE__ */ new Set();
9973
+ const reportValue = (value, start) => {
9974
+ for (const color of colorLiterals(value)) {
9975
+ const offset = start + color.offset;
9976
+ if (reported.has(offset)) continue;
9977
+ reported.add(offset);
9978
+ const loc = context.sourceCode.getLocFromIndex(offset);
9979
+ context.report({
9980
+ node,
9981
+ loc: { start: loc, end: context.sourceCode.getLocFromIndex(offset + color.text.length) },
9982
+ message: `Move literal color "${color.text}" into a --color-* token and reference it with var().`
9983
+ });
9984
+ }
9985
+ };
9986
+ for (const match of masked.matchAll(DECLARATION)) {
9987
+ const property = match.groups?.property;
9988
+ const value = match.groups?.value;
9989
+ if (!property || value === void 0 || property.startsWith("--color-")) continue;
9990
+ reportValue(value, match.index + match[0].lastIndexOf(value));
9991
+ }
9992
+ for (const match of masked.matchAll(APPLY)) {
9993
+ const value = match.groups?.value;
9994
+ if (value === void 0) continue;
9995
+ reportValue(value, match.index + match[0].indexOf(value));
9996
+ }
9997
+ }
9998
+ };
9999
+ }
10000
+ };
10001
+
9912
10002
  // eslint/rules/color-role.ts
9913
10003
  function colorRoleUsageMessage(className) {
9914
10004
  const segments = className.replace(/!+$/u, "").split(":");
@@ -9933,14 +10023,14 @@ function colorRoleUsageMessage(className) {
9933
10023
  }
9934
10024
 
9935
10025
  // eslint/rules/tailwind/color-role-usage.ts
9936
- var COLOR_TOKEN = /^--color-[a-z][a-z0-9-]*-(?:canvas|ink|edge)$/u;
10026
+ var COLOR_TOKEN = /^--color-[a-z][a-z0-9-]*(?:-(?:canvas|ink|edge))?$/u;
9937
10027
  var BACKGROUND_IMAGE_TOKEN = /^--background-image-[a-z][a-z0-9-]*-canvas$/u;
9938
10028
  var colorRoleUsageRule = {
9939
10029
  meta: {
9940
10030
  schema: [],
9941
10031
  type: "problem",
9942
10032
  docs: {
9943
- description: "Require semantic color and background-image token names and matching Tailwind color utilities."
10033
+ description: "Allow shared color tokens while enforcing explicit semantic roles and background-image naming."
9944
10034
  }
9945
10035
  },
9946
10036
  create(context) {
@@ -9963,7 +10053,7 @@ var colorRoleUsageRule = {
9963
10053
  } else if (!COLOR_TOKEN.test(token)) {
9964
10054
  context.report({
9965
10055
  node: theme,
9966
- message: `Color token "${token}" needs a semantic role ending in -canvas, -ink, or -edge.`
10056
+ message: `Color token "${token}" must use a valid --color-* name, optionally ending in -canvas, -ink, or -edge.`
9967
10057
  });
9968
10058
  }
9969
10059
  }
@@ -10149,14 +10239,24 @@ function themeClasses(name) {
10149
10239
  if (name.includes("--", 2)) return [];
10150
10240
  if (name.startsWith("--color-")) {
10151
10241
  const stem = name.slice("--color-".length);
10152
- if (stem.endsWith("-canvas")) {
10153
- return ["bg", "fill", "from", "via", "to", "ring-offset"].map((prefix) => `${prefix}-${stem}`);
10154
- }
10155
- if (stem.endsWith("-ink")) return ["text", "caret", "decoration"].map((prefix) => `${prefix}-${stem}`);
10156
- if (stem.endsWith("-edge")) {
10157
- return ["border", "outline", "ring", "divide", "stroke"].map((prefix) => `${prefix}-${stem}`);
10158
- }
10159
- return ["bg", "text", "border", "fill", "stroke", "ring"].map((prefix) => `${prefix}-${stem}`);
10242
+ return [
10243
+ "bg",
10244
+ "text",
10245
+ "border",
10246
+ "ring",
10247
+ "fill",
10248
+ "stroke",
10249
+ "accent",
10250
+ "caret",
10251
+ "decoration",
10252
+ "outline",
10253
+ "divide",
10254
+ "from",
10255
+ "via",
10256
+ "to",
10257
+ "shadow",
10258
+ "ring-offset"
10259
+ ].map((prefix) => `${prefix}-${stem}`);
10160
10260
  }
10161
10261
  if (name.startsWith("--background-image-")) return [`bg-${name.slice("--background-image-".length)}`];
10162
10262
  if (name.startsWith("--spacing-")) {
@@ -10266,7 +10366,7 @@ function buildCssDependencyGraph(options) {
10266
10366
  } else if (node.name === "theme" && node.loc) {
10267
10367
  const source = file.content.slice(node.loc.start.offset, node.loc.end.offset).replace(/\/\*[\s\S]*?\*\//gu, (comment) => comment.replace(/[^\r\n]/gu, " "));
10268
10368
  for (const found of source.matchAll(
10269
- /(?:^|(?<=[{;\r\n]))\s*(?<name>--[a-z][\w-]*)\s*:\s*(?<value>[^;}\r\n]+)(?=;|[}])/gu
10369
+ /(?:^|(?<=[{;\r\n]))\s*(?<name>--[a-z][\w-]*)\s*:\s*(?<value>[^;}]+)(?=;|[}])/gu
10270
10370
  )) {
10271
10371
  const name = found.groups?.name;
10272
10372
  const value = found.groups?.value;
@@ -10691,27 +10791,28 @@ var noUtilityAliasRule = {
10691
10791
 
10692
10792
  // eslint/rules/tailwind/root-variables.ts
10693
10793
  import path72 from "path";
10694
- import { generate as generate3, parse as parse6, walk as walk3 } from "@eslint/css-tree";
10695
- var INLINE_MESSAGE = "Define design tokens with direct values in @theme, not @theme inline.";
10696
- var INDIRECT_MESSAGE = "Define design tokens that reference CSS variables via var() in @theme inline.";
10697
- var MIXED_THEME_MESSAGE = "Use either @theme or @theme inline across the project; external font-loader mappings with valid fallbacks are the only exception.";
10698
- var REDUNDANT_INLINE_MESSAGE = (name) => `Tailwind token "${name}" has no runtime overrides; define its value directly in @theme instead of using :root and @theme inline.`;
10699
- var EXTERNAL_FONT_FALLBACK_MESSAGE = (name, variable) => `Tailwind font token "${name}" references external font "${variable}" without a fallback inside var(); use var(${variable}, Arial) or another font-family fallback.`;
10700
- function hasVarFallback(source, variable) {
10701
- for (const match of source.matchAll(/\bvar\s*\(\s*(?<name>--[\w-]+)\s*,/gu)) {
10702
- if (match.groups?.name !== variable) continue;
10703
- const start = match.index + match[0].length;
10704
- const close = source.indexOf(")", start);
10705
- if (close !== -1 && /[^,\s]/u.test(source.slice(start, close))) return true;
10706
- }
10707
- return false;
10794
+ import { parse as parse6, walk as walk3 } from "@eslint/css-tree";
10795
+
10796
+ // eslint/rules/tailwind/external-mapping.ts
10797
+ function externalMapping(value, token, authoredVariables) {
10798
+ const match = /^var\s*\(\s*(?<source>--[a-z][\w-]*)\s*,\s*(?<fallback>[\s\S]+)\)$/u.exec(value.trim());
10799
+ const source = match?.groups?.source;
10800
+ const fallback = match?.groups?.fallback?.trim();
10801
+ return Boolean(
10802
+ source && source !== token && !authoredVariables.has(source) && fallback && !/\b(?:var|env|attr|calc|min|max|clamp|color-mix|light-dark)\s*\(/iu.test(fallback)
10803
+ );
10708
10804
  }
10805
+
10806
+ // eslint/rules/tailwind/root-variables.ts
10807
+ var INLINE_ONLY_MESSAGE = "Define all Tailwind theme tokens in @theme inline, including fixed values and resets.";
10808
+ var INLINE_MAPPING_MESSAGE = (name) => `Tailwind token "${name}" must use a fixed value, same-named var(${name}), or an external var() with fallback in @theme inline.`;
10809
+ var REDUNDANT_INLINE_MESSAGE = (name) => `Tailwind token "${name}" has no @variant override; define its fixed value directly in @theme inline.`;
10709
10810
  var rootVariablesRule = {
10710
10811
  meta: {
10711
10812
  schema: [],
10712
10813
  type: "problem",
10713
10814
  docs: {
10714
- description: "Require @theme for fixed tokens and fixed-token aliases, matching :root variables for @theme inline tokens, and no other token redeclarations."
10815
+ description: "Require @theme inline for fixed tokens, variant-driven same-name mappings, and external variables with fallbacks."
10715
10816
  }
10716
10817
  },
10717
10818
  create(context) {
@@ -10734,7 +10835,11 @@ var rootVariablesRule = {
10734
10835
  const names = new Set(
10735
10836
  graph.definitions.filter((symbol) => symbol.kind === "theme-token").map((symbol) => symbol.name)
10736
10837
  );
10737
- function isFixedThemeExpression(name, value, visited = /* @__PURE__ */ new Set([name])) {
10838
+ function isComposableThemeExpression(name, value, allowRuntimeReferences, visited = /* @__PURE__ */ new Set([name])) {
10839
+ if (externalMapping(value, name, authoredVariables)) return true;
10840
+ if (mirroredTokens.has(name) && new RegExp(`^\\s*var\\(\\s*${name}\\s*\\)\\s*$`, "u").test(value)) {
10841
+ return allowRuntimeReferences && defaultRootNames.has(name) && variantNames.has(name);
10842
+ }
10738
10843
  const references = [...value.matchAll(/\bvar\s*\(\s*(?<target>--[a-z][\w-]*)/giu)].map(
10739
10844
  (match) => match.groups?.target
10740
10845
  );
@@ -10743,41 +10848,15 @@ var rootVariablesRule = {
10743
10848
  if (!target || visited.has(target)) return false;
10744
10849
  const next = themeValues.get(target);
10745
10850
  if (!next) return false;
10746
- return isFixedThemeExpression(target, next, /* @__PURE__ */ new Set([...visited, target]));
10851
+ return isComposableThemeExpression(target, next, allowRuntimeReferences, /* @__PURE__ */ new Set([...visited, target]));
10747
10852
  });
10748
10853
  }
10749
10854
  const mirroredTokens = /* @__PURE__ */ new Set();
10750
- let hasPlainThemeBlocks = false;
10751
- let hasInlineThemeBlocks = false;
10752
- let inlineBlocksAreExternalFonts = true;
10753
10855
  for (const file of [...stylesheets.filter((candidate) => path72.normalize(candidate) !== current), current]) {
10754
10856
  const source = (path72.normalize(file) === current ? context.sourceCode.text : textOf(file)).replace(
10755
10857
  /\/\*[\s\S]*?\*\//gu,
10756
10858
  " "
10757
10859
  );
10758
- for (const header of source.matchAll(/@theme(?<options>(?:\s+[\w-]+)*)\s*\{/gu)) {
10759
- if (/\binline\b/u.test(header.groups?.options ?? "")) hasInlineThemeBlocks = true;
10760
- else hasPlainThemeBlocks = true;
10761
- }
10762
- const inlineHeaders = [...source.matchAll(/@theme\s+inline\s*\{/gu)];
10763
- const inlineBlocks = [...source.matchAll(/@theme\s+inline\s*\{(?<body>[^{}]*)\}/gu)];
10764
- if (inlineHeaders.length !== inlineBlocks.length) inlineBlocksAreExternalFonts = false;
10765
- for (const block of inlineBlocks) {
10766
- const declarations = (block.groups?.body ?? "").split(";").map((part) => part.trim()).filter(Boolean);
10767
- if (declarations.length === 0) inlineBlocksAreExternalFonts = false;
10768
- for (const declaration of declarations) {
10769
- const match = /^(?<token>--font-[a-z][\w-]*)\s*:\s*(?<value>.+)$/u.exec(declaration);
10770
- const name = match?.groups?.token;
10771
- const value = match?.groups?.value ?? "";
10772
- const references = [...value.matchAll(/\bvar\s*\(\s*(?<name>--[\w-]+)/gu)].map(
10773
- (reference) => reference.groups?.name
10774
- );
10775
- const runtime = references[0];
10776
- if (!name || references.length !== 1 || !runtime?.startsWith("--font-") || runtime === name || names.has(runtime) || !hasVarFallback(value, runtime)) {
10777
- inlineBlocksAreExternalFonts = false;
10778
- }
10779
- }
10780
- }
10781
10860
  for (const theme of source.matchAll(/@theme(?<options>(?:\s+[\w-]+)*)\s*\{(?<body>[^{}]*)\}/gu)) {
10782
10861
  const inline = /\binline\b/u.test(theme.groups?.options ?? "");
10783
10862
  for (const declaration of (theme.groups?.body ?? "").matchAll(
@@ -10786,42 +10865,41 @@ var rootVariablesRule = {
10786
10865
  const { name, value } = declaration.groups ?? {};
10787
10866
  if (!name) continue;
10788
10867
  names.add(name);
10789
- if (inline && /\bvar\s*\(/u.test(value ?? "")) {
10868
+ if (inline && new RegExp(`^\\s*var\\(\\s*${name}\\s*\\)\\s*$`, "u").test(value ?? "")) {
10790
10869
  mirroredTokens.add(name);
10791
10870
  }
10792
10871
  }
10793
10872
  }
10794
10873
  }
10795
- const overriddenNames = new Set(
10796
- graph.definitions.filter((symbol) => symbol.kind === "root-variable" && symbol.scope === "conditional").map((symbol) => symbol.name)
10797
- );
10874
+ const authoredVariables = new Set(names);
10875
+ const variantNames = /* @__PURE__ */ new Set();
10798
10876
  for (const file of [...stylesheets.filter((candidate) => path72.normalize(candidate) !== current), current]) {
10799
10877
  const css2 = path72.normalize(file) === current ? context.sourceCode.text : textOf(file);
10800
10878
  const tree = parse6(css2, { parseAtrulePrelude: false });
10879
+ let variantDepth = 0;
10801
10880
  walk3(tree, {
10802
10881
  enter(node) {
10882
+ if (node.type === "Atrule" && node.name === "variant") variantDepth += 1;
10803
10883
  if (node.type !== "Declaration" || !node.property.startsWith("--")) return;
10804
- if (this.rule?.type !== "Rule") return;
10805
- if (generate3(this.rule.prelude).trim() !== ":root") overriddenNames.add(node.property);
10884
+ authoredVariables.add(node.property);
10885
+ if (variantDepth > 0) variantNames.add(node.property);
10886
+ },
10887
+ leave(node) {
10888
+ if (node.type === "Atrule" && node.name === "variant") variantDepth -= 1;
10806
10889
  }
10807
10890
  });
10808
10891
  }
10809
- for (const file of findFiles(sourceRoot, MODULE_EXTENSIONS4)) {
10810
- for (const found of textOf(file).matchAll(/\.setProperty\s*\(\s*["'\x60](?<name>--[\w-]+)["'\x60]/gu)) {
10811
- if (found.groups?.name) overriddenNames.add(found.groups.name);
10812
- }
10813
- }
10814
10892
  const defaultRootNames = new Set(
10815
10893
  graph.definitions.filter((symbol) => symbol.kind === "root-variable" && symbol.scope === "default").map((symbol) => symbol.name)
10816
10894
  );
10817
10895
  return {
10818
10896
  "StyleSheet:exit"(node) {
10819
10897
  const localThemes = atrulesNamed(node, "theme");
10820
- if (hasPlainThemeBlocks && hasInlineThemeBlocks && !inlineBlocksAreExternalFonts && localThemes[0]) {
10821
- context.report({ node: localThemes[0], message: MIXED_THEME_MESSAGE });
10822
- }
10823
10898
  for (const theme of localThemes) {
10824
- const inline = preludeIdentifiers(theme).includes("inline");
10899
+ if (!preludeIdentifiers(theme).includes("inline")) {
10900
+ context.report({ node: theme, message: INLINE_ONLY_MESSAGE });
10901
+ continue;
10902
+ }
10825
10903
  const source = context.sourceCode.getText(theme).replace(/\/\*[\s\S]*?\*\//gu, (comment) => comment.replace(/[^\r\n]/gu, " "));
10826
10904
  for (const match of source.matchAll(
10827
10905
  /(?:^|[{;\r\n])\s*(?<name>--[a-z][\w-]*)\s*:\s*(?<value>[^;{}]*)(?=;|[}])/gu
@@ -10830,60 +10908,48 @@ var rootVariablesRule = {
10830
10908
  if (!name || value === void 0) continue;
10831
10909
  const unquoted = value.replace(/"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'/gu, "");
10832
10910
  const referencesVariable = /\bvar\s*\(/iu.test(unquoted);
10833
- if (inline && referencesVariable) {
10834
- for (const reference of unquoted.matchAll(/\bvar\(\s*(?<variable>--[\w-]+)/gu)) {
10835
- const runtime = reference.groups?.variable;
10836
- if (runtime && runtime !== name) {
10837
- const externalFont = name.startsWith("--font-") && runtime.startsWith("--font-");
10838
- if (externalFont && !hasVarFallback(value, runtime)) {
10839
- context.report({ node: theme, message: EXTERNAL_FONT_FALLBACK_MESSAGE(name, runtime) });
10840
- } else if (!externalFont) {
10841
- context.report({
10842
- node: theme,
10843
- message: `Tailwind token "${name}" must reference its same-named CSS variable, not "${runtime}".`
10844
- });
10845
- }
10846
- } else if (runtime && !defaultRootNames.has(runtime)) {
10847
- context.report({
10848
- node: theme,
10849
- message: `Tailwind token "${name}" references "${runtime}" without a default :root declaration.`
10850
- });
10851
- }
10852
- }
10853
- }
10854
- if (inline && referencesVariable && defaultRootNames.has(name) && !overriddenNames.has(name)) {
10855
- const references = [...unquoted.matchAll(/\bvar\(\s*(?<variable>--[\w-]+)/gu)].map(
10856
- (reference) => reference.groups?.variable
10857
- );
10858
- if (references.length > 0 && references.every((variable) => variable === name)) {
10911
+ const directReference = /^var\s*\(\s*(?<target>--[a-z][\w-]*)\s*\)$/u.exec(unquoted.trim())?.groups?.target;
10912
+ if (directReference) {
10913
+ if (directReference !== name) {
10914
+ context.report({
10915
+ node: theme,
10916
+ message: `Tailwind token "${name}" must reference its same-named CSS variable, not "${directReference}".`
10917
+ });
10918
+ } else if (!defaultRootNames.has(name)) {
10919
+ context.report({
10920
+ node: theme,
10921
+ message: `Tailwind token "${name}" references "${name}" without a default :root declaration.`
10922
+ });
10923
+ } else if (!variantNames.has(name)) {
10859
10924
  context.report({ node: theme, message: REDUNDANT_INLINE_MESSAGE(name) });
10860
10925
  }
10926
+ continue;
10927
+ }
10928
+ if (externalMapping(value, name, authoredVariables)) continue;
10929
+ const fallbackTarget = /^var\s*\(\s*(?<target>--[a-z][\w-]*)\s*,/u.exec(unquoted.trim())?.groups?.target;
10930
+ if (fallbackTarget && authoredVariables.has(fallbackTarget)) {
10931
+ context.report({ node: theme, message: INLINE_MAPPING_MESSAGE(name) });
10932
+ continue;
10933
+ }
10934
+ const runtimeEffect = /^--(?:shadow|inset-shadow|drop-shadow|text-shadow|background-image)-/u.test(name);
10935
+ if (referencesVariable && !isComposableThemeExpression(name, unquoted, runtimeEffect)) {
10936
+ context.report({ node: theme, message: INLINE_MAPPING_MESSAGE(name) });
10861
10937
  }
10862
- if (inline === referencesVariable || !inline && isFixedThemeExpression(name, unquoted)) continue;
10863
- const offset = (theme.loc?.start.offset ?? 0) + match.index + match[0].indexOf(name);
10864
- const preceding = context.sourceCode.text.slice(0, offset);
10865
- const line = preceding.split("\n").length;
10866
- const column = offset - preceding.lastIndexOf("\n");
10867
- context.report({
10868
- node: theme,
10869
- loc: {
10870
- start: { line, column },
10871
- end: { line, column: column + name.length }
10872
- },
10873
- message: inline ? INLINE_MESSAGE : INDIRECT_MESSAGE
10874
- });
10875
10938
  }
10876
10939
  }
10877
- function checkTokenRedeclarations(currentNode, conditional = false) {
10940
+ function checkTokenRedeclarations(currentNode, conditional = false, inVariant = false) {
10878
10941
  if (currentNode.type === "Atrule" && ["theme", "utility", "keyframes"].includes(currentNode.name)) {
10879
10942
  return;
10880
10943
  }
10881
10944
  const isConditional = conditional || currentNode.type === "Atrule" && currentNode.name !== "layer";
10945
+ const isVariant = inVariant || currentNode.type === "Atrule" && currentNode.name === "variant";
10882
10946
  const isDefaultRoot = currentNode.type === "Rule" && context.sourceCode.getText(currentNode.prelude).trim() === ":root" && !isConditional;
10883
10947
  if (currentNode.type === "Rule" && !isDefaultRoot || currentNode.type === "Atrule" && currentNode.name === "variant") {
10884
10948
  for (const declaration of blockChildren2(currentNode)) {
10885
10949
  if (declaration.type !== "Declaration") continue;
10886
- if (mirroredTokens.has(declaration.property)) continue;
10950
+ if (mirroredTokens.has(declaration.property) && (isVariant || !variantNames.has(declaration.property))) {
10951
+ continue;
10952
+ }
10887
10953
  if (!names.has(declaration.property) && !declaration.property.startsWith("--color-")) continue;
10888
10954
  context.report({
10889
10955
  node: declaration,
@@ -10893,7 +10959,9 @@ var rootVariablesRule = {
10893
10959
  }
10894
10960
  const children = currentNode.type === "StyleSheet" ? currentNode.children : blockChildren2(currentNode);
10895
10961
  for (const child of children) {
10896
- if (child.type === "Atrule" || child.type === "Rule") checkTokenRedeclarations(child, isConditional);
10962
+ if (child.type === "Atrule" || child.type === "Rule") {
10963
+ checkTokenRedeclarations(child, isConditional, isVariant);
10964
+ }
10897
10965
  }
10898
10966
  }
10899
10967
  checkTokenRedeclarations(node);
@@ -10916,9 +10984,21 @@ var rootVariablesRule = {
10916
10984
 
10917
10985
  // eslint/rules/tailwind/stylesheet-ordering.ts
10918
10986
  import path73 from "path";
10919
- import { generate as generate4 } from "@eslint/css-tree";
10920
- var SECTION_ORDER = ["imports", "custom-variant", "root", "theme", "utility", "base styles", "keyframes"];
10987
+ import { generate as generate3 } from "@eslint/css-tree";
10988
+ var SECTION_ORDER = [
10989
+ "Tailwind import",
10990
+ "theme",
10991
+ "feature imports",
10992
+ "custom-variant",
10993
+ "root",
10994
+ "utility",
10995
+ "base styles",
10996
+ "keyframes"
10997
+ ];
10921
10998
  var LATE_RESET_ERROR = "The global --*: initial theme reset must precede feature stylesheet imports, or it will erase their theme tokens.";
10999
+ var DUPLICATE_THEME_ERROR = "globals.css must use one @theme inline block for the reset and all global tokens; merge the theme blocks.";
11000
+ var RESET_FIRST_ERROR = "The --*: initial reset must be the first declaration in the globals.css @theme inline block.";
11001
+ var ORDER_ERROR = "Global stylesheet sections must be ordered: Tailwind import, one @theme inline block, feature imports, @custom-variant, :root/@variant, custom utilities, base styles, keyframes.";
10922
11002
  function themeBody(node, source) {
10923
11003
  if (node.type !== "Atrule" || node.name !== "theme" || !node.loc) return void 0;
10924
11004
  const raw = source.slice(node.loc.start.offset, node.loc.end.offset);
@@ -10930,12 +11010,9 @@ function themeBody(node, source) {
10930
11010
  function containsThemeReset(body) {
10931
11011
  return body !== void 0 && /(?:^|[;{])\s*--\*\s*:\s*initial\s*(?:;|$)/u.test(body);
10932
11012
  }
10933
- function isStandaloneThemeReset(body) {
10934
- return body !== void 0 && /^--\*\s*:\s*initial\s*;?$/u.test(body);
10935
- }
10936
11013
  function isTailwindImport(node) {
10937
11014
  if (node.type !== "Atrule" || node.name !== "import" || !node.prelude) return false;
10938
- return /^["']tailwindcss["'](?:\s|$)/u.test(generate4(node.prelude).trim());
11015
+ return /^["']tailwindcss["'](?:\s|$)/u.test(generate3(node.prelude).trim());
10939
11016
  }
10940
11017
  function scopesCustomProperties(node) {
10941
11018
  if (node.type === "Declaration") return node.property.startsWith("--");
@@ -10957,80 +11034,68 @@ var stylesheetOrderingRule = {
10957
11034
  if (path73.normalize(path73.resolve(context.filename)) !== entryPoint) return {};
10958
11035
  return {
10959
11036
  "StyleSheet:exit"(node) {
11037
+ const topLevel = [...node.children];
11038
+ const first = topLevel[0];
11039
+ const second = topLevel[1];
11040
+ if (first && isTailwindImport(first) && second?.type === "Atrule" && second.name === "import" && !isTailwindImport(second) && topLevel.some((child) => containsThemeReset(themeBody(child, context.sourceCode.text)))) {
11041
+ context.report({ node, message: LATE_RESET_ERROR });
11042
+ return;
11043
+ }
11044
+ if (!first || !isTailwindImport(first) || second?.type !== "Atrule" || second.name !== "theme" || !preludeIdentifiers(second).includes("inline")) {
11045
+ context.report({ node, message: ORDER_ERROR });
11046
+ return;
11047
+ }
10960
11048
  const sections = [];
10961
- let index = 0;
10962
- let sawTailwindImport = false;
10963
11049
  let sawFeatureImport = false;
10964
- let sawNonImport = false;
10965
- let sawEarlyReset = false;
11050
+ let themeBlocks2 = 0;
10966
11051
  for (const child of node.children) {
10967
11052
  const body = themeBody(child, context.sourceCode.text);
10968
- let isEarlyReset = false;
11053
+ if (containsThemeReset(body) && !/^--\*\s*:\s*initial\s*(?:;|$)/u.test(body ?? "")) {
11054
+ context.report({ node: child, message: RESET_FIRST_ERROR });
11055
+ return;
11056
+ }
10969
11057
  if (containsThemeReset(body) && sawFeatureImport) {
10970
11058
  context.report({ node: child, message: LATE_RESET_ERROR });
10971
11059
  return;
10972
11060
  }
10973
11061
  if (child.type === "Atrule" && child.name === "import") {
10974
- sections.push({ name: "imports", at: index });
10975
- if (isTailwindImport(child)) sawTailwindImport = true;
10976
- else sawFeatureImport = true;
10977
- } else if (isStandaloneThemeReset(body) && sawTailwindImport && !sawFeatureImport && !sawNonImport && !sawEarlyReset) {
10978
- sawEarlyReset = true;
10979
- isEarlyReset = true;
10980
- } else if (child.type === "Atrule" && child.name === "custom-variant") {
10981
- sections.push({ name: "custom-variant", at: index });
11062
+ if (isTailwindImport(child)) {
11063
+ sections.push("Tailwind import");
11064
+ } else {
11065
+ sawFeatureImport = true;
11066
+ sections.push("feature imports");
11067
+ }
10982
11068
  } else if (child.type === "Atrule" && child.name === "theme") {
10983
- sections.push({ name: "theme", at: index });
11069
+ themeBlocks2 += 1;
11070
+ sections.push("theme");
11071
+ } else if (child.type === "Atrule" && child.name === "custom-variant") {
11072
+ sections.push("custom-variant");
10984
11073
  } else if (child.type === "Atrule" && child.name === "utility") {
10985
- sections.push({ name: "utility", at: index });
11074
+ sections.push("utility");
10986
11075
  } else if (child.type === "Atrule" && child.name === "keyframes") {
10987
- sections.push({ name: "keyframes", at: index });
11076
+ sections.push("keyframes");
10988
11077
  } else if (child.type === "Atrule" && child.name === "layer") {
10989
11078
  if (preludeIdentifiers(child).includes("base")) {
10990
- sections.push({ name: "base styles", at: index });
11079
+ sections.push("base styles");
10991
11080
  }
10992
11081
  } else if (child.type === "Rule" && (selectorNames(child).includes("root") || scopesCustomProperties(child))) {
10993
- sections.push({ name: "root", at: index });
11082
+ sections.push("root");
10994
11083
  } else if (child.type === "Atrule" && scopesCustomProperties(child)) {
10995
- sections.push({ name: "root", at: index });
10996
- }
10997
- if (child.type !== "Comment" && !(child.type === "Atrule" && child.name === "import") && !isEarlyReset) {
10998
- sawNonImport = true;
11084
+ sections.push("root");
10999
11085
  }
11000
- index += 1;
11001
11086
  }
11002
- const firstAt = /* @__PURE__ */ new Map();
11003
- for (const section of sections) {
11004
- if (!firstAt.has(section.name)) firstAt.set(section.name, section.at ?? 0);
11005
- }
11006
- let lastSeen = -1;
11007
- for (const name of SECTION_ORDER) {
11008
- const at = firstAt.get(name);
11009
- if (at === void 0) continue;
11010
- if (at < lastSeen) {
11011
- context.report({
11012
- node,
11013
- message: `Global stylesheet sections must be ordered: imports, @custom-variant, :root, @theme, custom utilities, base styles, keyframes. Found "${name}" before an earlier section.`
11014
- });
11015
- return;
11016
- }
11017
- lastSeen = at;
11087
+ if (themeBlocks2 > 1) {
11088
+ context.report({ node, message: DUPLICATE_THEME_ERROR });
11089
+ return;
11018
11090
  }
11019
11091
  let furthestSection = -1;
11020
- let furthestName;
11021
11092
  for (const section of sections) {
11022
- const order = SECTION_ORDER.findIndex((name) => name === section.name);
11093
+ const order = SECTION_ORDER.findIndex((name) => name === section);
11023
11094
  if (order < furthestSection) {
11024
- context.report({
11025
- node,
11026
- message: `Global stylesheet section "${section.name}" cannot reappear after "${String(furthestName)}". Keep all section occurrences in the required order.`
11027
- });
11095
+ context.report({ node, message: ORDER_ERROR });
11028
11096
  return;
11029
11097
  }
11030
- if (order > furthestSection) {
11031
- furthestSection = order;
11032
- furthestName = section.name;
11033
- }
11098
+ furthestSection = order;
11034
11099
  }
11035
11100
  }
11036
11101
  };
@@ -11039,7 +11104,7 @@ var stylesheetOrderingRule = {
11039
11104
 
11040
11105
  // eslint/rules/tailwind/theme-mode.ts
11041
11106
  import path74 from "path";
11042
- import { generate as generate5 } from "@eslint/css-tree";
11107
+ import { generate as generate4 } from "@eslint/css-tree";
11043
11108
  var MODE_NAME = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u;
11044
11109
  var MODE_REFERENCE = /(?:^|[^a-z0-9-])(?<name>[a-z][a-z0-9]*(?:-[a-z0-9]+)*):(?=[a-z[(])/gu;
11045
11110
  var MODE_ATTRIBUTE = /data-theme\s*=\s*["'](?<name>[a-z][a-z0-9]*(?:-[a-z0-9]+)*)["']/gu;
@@ -11086,7 +11151,7 @@ var themeModeRule = {
11086
11151
  const registered = /* @__PURE__ */ new Set();
11087
11152
  for (const variant of atrulesNamed(node, "custom-variant")) {
11088
11153
  if (variant.type !== "Atrule") continue;
11089
- const prelude = variant.prelude ? generate5(variant.prelude) : "";
11154
+ const prelude = variant.prelude ? generate4(variant.prelude) : "";
11090
11155
  const name = variantName(prelude);
11091
11156
  if (!name) continue;
11092
11157
  if (!isModeVariant(name, prelude)) continue;
@@ -11115,14 +11180,14 @@ var themeModeRule = {
11115
11180
  const globalRegistrations = current === globalFile ? registered : registeredModes(read(globalFile));
11116
11181
  for (const variant of atrulesNamed(node, "variant")) {
11117
11182
  if (variant.type !== "Atrule" || !variant.prelude) continue;
11118
- const name = variantName(generate5(variant.prelude));
11183
+ const name = variantName(generate4(variant.prelude));
11119
11184
  if (name && (name === "dark" || name.startsWith("theme-") || globalRegistrations.has(name) || modeHints.has(name)) && !globalRegistrations.has(name)) {
11120
11185
  context.report({ node: variant, message: MISSING_CSS_VARIANT(name) });
11121
11186
  }
11122
11187
  }
11123
11188
  for (const rule of rules(node)) {
11124
11189
  if (rule.type !== "Rule") continue;
11125
- const selector = generate5(rule.prelude);
11190
+ const selector = generate4(rule.prelude);
11126
11191
  if (/\[\s*data-theme(?:\s*[~|^$*]?=|\s*\])/u.test(selector)) {
11127
11192
  context.report({ node: rule, message: SELECTOR_OVERRIDE });
11128
11193
  } else if (/\.dark(?![\w-])/u.test(selector)) {
@@ -11131,7 +11196,7 @@ var themeModeRule = {
11131
11196
  }
11132
11197
  walkNodes(node, (child) => {
11133
11198
  if (child.type !== "Atrule" || child.name !== "media" || !child.prelude) return;
11134
- if (/\bprefers-color-scheme\b/u.test(generate5(child.prelude))) {
11199
+ if (/\bprefers-color-scheme\b/u.test(generate4(child.prelude))) {
11135
11200
  context.report({ node: child, message: SYSTEM_OVERRIDE });
11136
11201
  }
11137
11202
  });
@@ -11161,15 +11226,16 @@ var themeModeRule = {
11161
11226
 
11162
11227
  // eslint/rules/tailwind/theme-reset.ts
11163
11228
  import path75 from "path";
11164
- import { generate as generate6 } from "@eslint/css-tree";
11229
+ import { generate as generate5 } from "@eslint/css-tree";
11165
11230
  function isThemeReset(node) {
11166
11231
  if (!node) return false;
11167
- if (node.type === "Declaration") return node.property === "--*" && generate6(node.value).trim() === "initial";
11232
+ if (node.type === "Declaration") return node.property === "--*" && generate5(node.value).trim() === "initial";
11168
11233
  if (node.type === "Raw") return /(?:^|[;{])\s*--\*\s*:\s*initial\s*(?:;|$)/u.test(node.value);
11169
11234
  return false;
11170
11235
  }
11171
- function hasThemeReset(node) {
11236
+ function hasThemeReset(node, inlineOnly = false) {
11172
11237
  return atrulesNamed(node, "theme").some((theme) => {
11238
+ if (inlineOnly && !preludeIdentifiers(theme).includes("inline")) return false;
11173
11239
  if (blockChildren2(theme).some(isThemeReset)) return true;
11174
11240
  let nested = false;
11175
11241
  walkNodes(theme, (child) => {
@@ -11203,10 +11269,10 @@ var themeResetRule = {
11203
11269
  }
11204
11270
  return;
11205
11271
  }
11206
- if (!hasReset) {
11272
+ if (!hasThemeReset(node, true)) {
11207
11273
  context.report({
11208
11274
  node,
11209
- message: "globals.css must reset Tailwind's default theme with --*: initial inside @theme or @theme inline."
11275
+ message: "globals.css must reset Tailwind's default theme with --*: initial inside @theme inline."
11210
11276
  });
11211
11277
  }
11212
11278
  }
@@ -11295,13 +11361,226 @@ var uniqueCssDefinitionRule = {
11295
11361
 
11296
11362
  // eslint/rules/tailwind/unique-css-token-value.ts
11297
11363
  import path77 from "path";
11364
+
11365
+ // eslint/rules/tailwind/css-named-colors.ts
11366
+ var CSS_NAMED_COLORS = {
11367
+ aliceblue: [240, 248, 255],
11368
+ antiquewhite: [250, 235, 215],
11369
+ aqua: [0, 255, 255],
11370
+ aquamarine: [127, 255, 212],
11371
+ azure: [240, 255, 255],
11372
+ beige: [245, 245, 220],
11373
+ bisque: [255, 228, 196],
11374
+ black: [0, 0, 0],
11375
+ blanchedalmond: [255, 235, 205],
11376
+ blue: [0, 0, 255],
11377
+ blueviolet: [138, 43, 226],
11378
+ brown: [165, 42, 42],
11379
+ burlywood: [222, 184, 135],
11380
+ cadetblue: [95, 158, 160],
11381
+ chartreuse: [127, 255, 0],
11382
+ chocolate: [210, 105, 30],
11383
+ coral: [255, 127, 80],
11384
+ cornflowerblue: [100, 149, 237],
11385
+ cornsilk: [255, 248, 220],
11386
+ crimson: [220, 20, 60],
11387
+ cyan: [0, 255, 255],
11388
+ darkblue: [0, 0, 139],
11389
+ darkcyan: [0, 139, 139],
11390
+ darkgoldenrod: [184, 134, 11],
11391
+ darkgray: [169, 169, 169],
11392
+ darkgreen: [0, 100, 0],
11393
+ darkgrey: [169, 169, 169],
11394
+ darkkhaki: [189, 183, 107],
11395
+ darkmagenta: [139, 0, 139],
11396
+ darkolivegreen: [85, 107, 47],
11397
+ darkorange: [255, 140, 0],
11398
+ darkorchid: [153, 50, 204],
11399
+ darkred: [139, 0, 0],
11400
+ darksalmon: [233, 150, 122],
11401
+ darkseagreen: [143, 188, 143],
11402
+ darkslateblue: [72, 61, 139],
11403
+ darkslategray: [47, 79, 79],
11404
+ darkslategrey: [47, 79, 79],
11405
+ darkturquoise: [0, 206, 209],
11406
+ darkviolet: [148, 0, 211],
11407
+ deeppink: [255, 20, 147],
11408
+ deepskyblue: [0, 191, 255],
11409
+ dimgray: [105, 105, 105],
11410
+ dimgrey: [105, 105, 105],
11411
+ dodgerblue: [30, 144, 255],
11412
+ firebrick: [178, 34, 34],
11413
+ floralwhite: [255, 250, 240],
11414
+ forestgreen: [34, 139, 34],
11415
+ fuchsia: [255, 0, 255],
11416
+ gainsboro: [220, 220, 220],
11417
+ ghostwhite: [248, 248, 255],
11418
+ gold: [255, 215, 0],
11419
+ goldenrod: [218, 165, 32],
11420
+ gray: [128, 128, 128],
11421
+ green: [0, 128, 0],
11422
+ greenyellow: [173, 255, 47],
11423
+ grey: [128, 128, 128],
11424
+ honeydew: [240, 255, 240],
11425
+ hotpink: [255, 105, 180],
11426
+ indianred: [205, 92, 92],
11427
+ indigo: [75, 0, 130],
11428
+ ivory: [255, 255, 240],
11429
+ khaki: [240, 230, 140],
11430
+ lavender: [230, 230, 250],
11431
+ lavenderblush: [255, 240, 245],
11432
+ lawngreen: [124, 252, 0],
11433
+ lemonchiffon: [255, 250, 205],
11434
+ lightblue: [173, 216, 230],
11435
+ lightcoral: [240, 128, 128],
11436
+ lightcyan: [224, 255, 255],
11437
+ lightgoldenrodyellow: [250, 250, 210],
11438
+ lightgray: [211, 211, 211],
11439
+ lightgreen: [144, 238, 144],
11440
+ lightgrey: [211, 211, 211],
11441
+ lightpink: [255, 182, 193],
11442
+ lightsalmon: [255, 160, 122],
11443
+ lightseagreen: [32, 178, 170],
11444
+ lightskyblue: [135, 206, 250],
11445
+ lightslategray: [119, 136, 153],
11446
+ lightslategrey: [119, 136, 153],
11447
+ lightsteelblue: [176, 196, 222],
11448
+ lightyellow: [255, 255, 224],
11449
+ lime: [0, 255, 0],
11450
+ limegreen: [50, 205, 50],
11451
+ linen: [250, 240, 230],
11452
+ magenta: [255, 0, 255],
11453
+ maroon: [128, 0, 0],
11454
+ mediumaquamarine: [102, 205, 170],
11455
+ mediumblue: [0, 0, 205],
11456
+ mediumorchid: [186, 85, 211],
11457
+ mediumpurple: [147, 112, 219],
11458
+ mediumseagreen: [60, 179, 113],
11459
+ mediumslateblue: [123, 104, 238],
11460
+ mediumspringgreen: [0, 250, 154],
11461
+ mediumturquoise: [72, 209, 204],
11462
+ mediumvioletred: [199, 21, 133],
11463
+ midnightblue: [25, 25, 112],
11464
+ mintcream: [245, 255, 250],
11465
+ mistyrose: [255, 228, 225],
11466
+ moccasin: [255, 228, 181],
11467
+ navajowhite: [255, 222, 173],
11468
+ navy: [0, 0, 128],
11469
+ oldlace: [253, 245, 230],
11470
+ olive: [128, 128, 0],
11471
+ olivedrab: [107, 142, 35],
11472
+ orange: [255, 165, 0],
11473
+ orangered: [255, 69, 0],
11474
+ orchid: [218, 112, 214],
11475
+ palegoldenrod: [238, 232, 170],
11476
+ palegreen: [152, 251, 152],
11477
+ paleturquoise: [175, 238, 238],
11478
+ palevioletred: [219, 112, 147],
11479
+ papayawhip: [255, 239, 213],
11480
+ peachpuff: [255, 218, 185],
11481
+ peru: [205, 133, 63],
11482
+ pink: [255, 192, 203],
11483
+ plum: [221, 160, 221],
11484
+ powderblue: [176, 224, 230],
11485
+ purple: [128, 0, 128],
11486
+ rebeccapurple: [102, 51, 153],
11487
+ red: [255, 0, 0],
11488
+ rosybrown: [188, 143, 143],
11489
+ royalblue: [65, 105, 225],
11490
+ saddlebrown: [139, 69, 19],
11491
+ salmon: [250, 128, 114],
11492
+ sandybrown: [244, 164, 96],
11493
+ seagreen: [46, 139, 87],
11494
+ seashell: [255, 245, 238],
11495
+ sienna: [160, 82, 45],
11496
+ silver: [192, 192, 192],
11497
+ skyblue: [135, 206, 235],
11498
+ slateblue: [106, 90, 205],
11499
+ slategray: [112, 128, 144],
11500
+ slategrey: [112, 128, 144],
11501
+ snow: [255, 250, 250],
11502
+ springgreen: [0, 255, 127],
11503
+ steelblue: [70, 130, 180],
11504
+ tan: [210, 180, 140],
11505
+ teal: [0, 128, 128],
11506
+ thistle: [216, 191, 216],
11507
+ tomato: [255, 99, 71],
11508
+ turquoise: [64, 224, 208],
11509
+ violet: [238, 130, 238],
11510
+ wheat: [245, 222, 179],
11511
+ white: [255, 255, 255],
11512
+ whitesmoke: [245, 245, 245],
11513
+ yellow: [255, 255, 0],
11514
+ yellowgreen: [154, 205, 50]
11515
+ };
11516
+
11517
+ // eslint/rules/tailwind/unique-css-token-value.ts
11518
+ function normalizedColor(text) {
11519
+ const hex = /^#(?<digits>[\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$/iu.exec(text)?.groups?.digits;
11520
+ if (hex) {
11521
+ const expanded = hex.length <= 4 ? Array.from(hex.matchAll(/./gu), (match) => match[0] + match[0]).join("") : hex;
11522
+ const channels2 = [0, 2, 4].map((start) => Number.parseInt(expanded.slice(start, start + 2), 16));
11523
+ const alpha2 = expanded.length === 8 ? Number.parseInt(expanded.slice(6, 8), 16) / 255 : 1;
11524
+ return `srgb:${channels2.join(",")}/${String(alpha2)}`;
11525
+ }
11526
+ const named = CSS_NAMED_COLORS[text.toLowerCase()];
11527
+ if (Array.isArray(named)) return `srgb:${named.join(",")}/1`;
11528
+ const expression = /^(?<kind>rgba?|hsla?)\(\s*(?<body>[^()]*)\s*\)$/iu.exec(text);
11529
+ if (!expression?.groups) return void 0;
11530
+ const parts = (expression.groups.body ?? "").replace(/,/gu, " ").replace(/\//gu, " / ").trim().split(/\s+/u);
11531
+ const alphaIndex = parts[3] === "/" ? 4 : 3;
11532
+ if (parts.length !== 3 && !(parts.length === 4 && parts[3] !== "/") && !(parts.length === 5 && parts[3] === "/")) {
11533
+ return void 0;
11534
+ }
11535
+ const alphaText = parts[alphaIndex];
11536
+ const parseNumeric = (raw) => {
11537
+ if (!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)%?$/u.test(raw)) return void 0;
11538
+ const numeric = Number.parseFloat(raw);
11539
+ return Number.isFinite(numeric) ? numeric : void 0;
11540
+ };
11541
+ const alpha = alphaText === void 0 ? 1 : parseNumeric(alphaText);
11542
+ if (alpha === void 0 || alpha < 0 || (alphaText?.endsWith("%") ? alpha > 100 : alpha > 1)) return void 0;
11543
+ const opacity = alphaText?.endsWith("%") ? alpha / 100 : alpha;
11544
+ const kind = expression.groups.kind?.toLowerCase();
11545
+ let channels;
11546
+ if (kind === "rgb" || kind === "rgba") {
11547
+ const rgb = parts.slice(0, 3).map((part) => {
11548
+ const value = parseNumeric(part);
11549
+ if (value === void 0) return Number.NaN;
11550
+ if (part.endsWith("%")) return value * 255 / 100;
11551
+ return value;
11552
+ });
11553
+ if (rgb.some((value) => !Number.isFinite(value) || value < 0 || value > 255)) return void 0;
11554
+ channels = rgb;
11555
+ } else {
11556
+ const hue = parseNumeric(parts[0] ?? "");
11557
+ const saturation = parseNumeric(parts[1] ?? "");
11558
+ const lightness = parseNumeric(parts[2] ?? "");
11559
+ if (hue === void 0 || saturation === void 0 || lightness === void 0) return void 0;
11560
+ if (parts[0]?.endsWith("%") || !parts[1]?.endsWith("%") || !parts[2]?.endsWith("%")) return void 0;
11561
+ if (saturation < 0 || saturation > 100 || lightness < 0 || lightness > 100) return void 0;
11562
+ const h = (hue % 360 + 360) % 360;
11563
+ const s = saturation / 100;
11564
+ const l = lightness / 100;
11565
+ const chroma = (1 - Math.abs(2 * l - 1)) * s;
11566
+ const x = chroma * (1 - Math.abs(h / 60 % 2 - 1));
11567
+ const m = l - chroma / 2;
11568
+ let rgb;
11569
+ if (h < 60) rgb = [chroma, x, 0];
11570
+ else if (h < 120) rgb = [x, chroma, 0];
11571
+ else if (h < 180) rgb = [0, chroma, x];
11572
+ else if (h < 240) rgb = [0, x, chroma];
11573
+ else if (h < 300) rgb = [x, 0, chroma];
11574
+ else rgb = [chroma, 0, x];
11575
+ const exact = rgb.map((channel) => (channel + m) * 255);
11576
+ if (exact.some((channel) => Math.abs(channel - Math.round(channel)) > 1e-10)) return void 0;
11577
+ channels = exact.map(Math.round);
11578
+ }
11579
+ return `srgb:${channels.join(",")}/${String(opacity)}`;
11580
+ }
11298
11581
  function valueIdentity(value) {
11299
11582
  const text = value.trim();
11300
- const hex = /^#(?<digits>[\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$/iu.exec(text)?.groups?.digits;
11301
- if (!hex) return text;
11302
- const lower = hex.toLowerCase();
11303
- const full = lower.length <= 4 ? Array.from(lower, (digit) => digit + digit).join("") : lower;
11304
- return `#${full.length === 8 && full.endsWith("ff") ? full.slice(0, -2) : full}`;
11583
+ return normalizedColor(text) ?? text;
11305
11584
  }
11306
11585
  function isComparable(symbol) {
11307
11586
  if (symbol.value === void 0 || symbol.value.trim() === "") return false;
@@ -11345,10 +11624,11 @@ var uniqueCssTokenValueRule = {
11345
11624
  const redundant = /* @__PURE__ */ new Map();
11346
11625
  for (const group of byValue.values()) {
11347
11626
  group.sort(compareLocations);
11348
- const first = group[0];
11349
- if (!first) continue;
11350
- for (const definition of group.slice(1)) {
11351
- if (definition.name !== first.name) redundant.set(definition, first);
11627
+ const seen = [];
11628
+ for (const definition of group) {
11629
+ const original = seen.find((prior) => prior.name !== definition.name);
11630
+ if (original) redundant.set(definition, original);
11631
+ seen.push(definition);
11352
11632
  }
11353
11633
  }
11354
11634
  return {
@@ -12042,7 +12322,7 @@ var utilityMappingRule = {
12042
12322
  import { spawnSync as spawnSync2 } from "child_process";
12043
12323
  import { existsSync as existsSync13 } from "fs";
12044
12324
  import path80 from "path";
12045
- import { generate as generate7, parse as parse7, toPlainObject as toPlainObject2 } from "@eslint/css-tree";
12325
+ import { generate as generate6, parse as parse7, toPlainObject as toPlainObject2 } from "@eslint/css-tree";
12046
12326
  function numericPart(value) {
12047
12327
  const match = /^(?<negative>-?)(?<number>(?:\d+(?:\.\d+)?|\.\d+))(?<unit>%|px|rem|em|vw|vh|dvw|dvh|ch|fr|s|ms|deg|turn)?$/u.exec(
12048
12328
  value
@@ -12052,7 +12332,7 @@ function numericPart(value) {
12052
12332
  const sign = match.groups.negative === "-" ? "neg-" : "";
12053
12333
  return sign + number + (match.groups.unit === "%" ? "pct" : match.groups.unit ?? "");
12054
12334
  }
12055
- function readableValue(value) {
12335
+ function simpleValueSlug(value) {
12056
12336
  const words = value.trim().replace(/,/gu, "_comma_").split(/[_\s]+/u).filter(Boolean);
12057
12337
  if (words.length === 0 || words.length > 8) return void 0;
12058
12338
  const parts = [];
@@ -12148,16 +12428,17 @@ function compiledOperation(compiled, index) {
12148
12428
  (child) => child.type === "Declaration" && child.property === "--tw-shadow"
12149
12429
  );
12150
12430
  if (shadow?.type === "Declaration") {
12151
- return { property: "box-shadow", value: generate7(shadow.value).trim() };
12431
+ return { property: "box-shadow", value: generate6(shadow.value).trim() };
12152
12432
  }
12153
12433
  }
12154
- return { property: declaration.property, value: generate7(declaration.value).trim() };
12434
+ return { property: declaration.property, value: generate6(declaration.value).trim() };
12435
+ }
12436
+ function propertyPrefix(property) {
12437
+ return property.replace(/^-+/u, "").replace(/^background-/u, "bg-");
12155
12438
  }
12156
12439
  function expectedName(operation) {
12157
- const value = readableValue(operation.value);
12158
- if (!value) return void 0;
12159
- const property = operation.property.replace(/^-+/u, "").replace(/^background-/u, "bg-");
12160
- return `${property}-${value}`;
12440
+ const value = simpleValueSlug(operation.value);
12441
+ return value ? `${propertyPrefix(operation.property)}-${value}` : void 0;
12161
12442
  }
12162
12443
  function singleApply(utility, textOf) {
12163
12444
  const children = blockChildren2(utility);
@@ -12171,7 +12452,7 @@ var utilityNameParityRule = {
12171
12452
  schema: [],
12172
12453
  type: "problem",
12173
12454
  docs: {
12174
- description: "Use property/value-derived names for readable values and semantic names for complex values."
12455
+ description: "Use property/value-derived names for simple values and property-prefixed semantic names for complex values."
12175
12456
  }
12176
12457
  },
12177
12458
  create(context) {
@@ -12205,8 +12486,16 @@ var utilityNameParityRule = {
12205
12486
  if (/-hash-[a-f0-9]{12}$/u.test(candidate.name)) {
12206
12487
  context.report({
12207
12488
  node: candidate.utility,
12208
- message: `Custom utility "${candidate.name}" uses an opaque hash for a complex ${operation.property} value; use a semantic name, or prefer a theme-generated utility where supported.`
12489
+ message: `Custom utility "${candidate.name}" uses an opaque hash for a complex ${operation.property} value; use a semantic name with a property prefix, or prefer a theme-generated utility where supported.`
12209
12490
  });
12491
+ } else {
12492
+ const prefix = `${propertyPrefix(operation.property)}-`;
12493
+ if (!candidate.name.startsWith(prefix) || candidate.name.length === prefix.length) {
12494
+ context.report({
12495
+ node: candidate.utility,
12496
+ message: `Custom utility "${candidate.name}" maps a complex ${operation.property} value; use a semantic name starting with "${prefix}" to preserve the CSS property.`
12497
+ });
12498
+ }
12210
12499
  }
12211
12500
  continue;
12212
12501
  }
@@ -12227,6 +12516,7 @@ var tailwindRules = {
12227
12516
  "theme-reset": themeResetRule,
12228
12517
  "theme-mode": themeModeRule,
12229
12518
  "color-role-usage": colorRoleUsageRule,
12519
+ "color-literal-usage": colorLiteralUsageRule,
12230
12520
  "root-variables": rootVariablesRule,
12231
12521
  "apply-usage": applyUsageRule,
12232
12522
  "base-layer-pair": baseLayerPairRule,
@@ -12465,7 +12755,9 @@ var SPACING_UTILITY_PREFIXES = /* @__PURE__ */ new Set([
12465
12755
  "start",
12466
12756
  "end",
12467
12757
  "space-x",
12468
- "space-y"
12758
+ "space-y",
12759
+ "translate-x",
12760
+ "translate-y"
12469
12761
  ]);
12470
12762
  var PREFIX_BUILTINS = {
12471
12763
  bg: [
@@ -12790,6 +13082,7 @@ function readInventory(files) {
12790
13082
  const themeTokensByNamespace = /* @__PURE__ */ new Map();
12791
13083
  const plainClasses = /* @__PURE__ */ new Set();
12792
13084
  let defaultsReset = false;
13085
+ let baseSpacing = false;
12793
13086
  for (const file of files) {
12794
13087
  let css2;
12795
13088
  try {
@@ -12808,6 +13101,7 @@ function readInventory(files) {
12808
13101
  for (const plainClass of classSelectors(css2)) plainClasses.add(plainClass);
12809
13102
  for (const block of themeBlocks(css2)) {
12810
13103
  if (/--\*\s*:\s*initial\b/.test(block)) defaultsReset = true;
13104
+ if (/(?:^|[;{])\s*--spacing\s*:/.test(block)) baseSpacing = true;
12811
13105
  for (const decl of block.matchAll(/--(?<namespace>[a-z][a-z0-9]*)-(?<token>[a-z0-9][a-z0-9_-]*)\s*:/g)) {
12812
13106
  let namespace = decl.groups?.namespace;
12813
13107
  let token = decl.groups?.token;
@@ -12843,6 +13137,7 @@ function readInventory(files) {
12843
13137
  utilityPrefixes,
12844
13138
  themeTokens: themeTokensByNamespace,
12845
13139
  defaultsReset,
13140
+ baseSpacing,
12846
13141
  plainClasses
12847
13142
  };
12848
13143
  }
@@ -12871,7 +13166,13 @@ function isKnown(className, inventory) {
12871
13166
  const namespaces = PREFIX_NAMESPACES[prefix];
12872
13167
  if (!namespaces) {
12873
13168
  if (STATIC_VALUE_TOKENS[prefix]?.includes(token)) return true;
12874
- if (SPACING_UTILITY_PREFIXES.has(prefix) && inventory.themeTokens.get("spacing")?.has(token)) return true;
13169
+ if (SPACING_UTILITY_PREFIXES.has(prefix)) {
13170
+ const spacing = inventory.themeTokens.get("spacing");
13171
+ if (/^(?:\d+(?:\.\d+)?|\.\d+)$/u.test(token) && inventory.defaultsReset && !inventory.baseSpacing) {
13172
+ return false;
13173
+ }
13174
+ if (spacing?.has(token)) return true;
13175
+ }
12875
13176
  const projectTokens = inventory.themeTokens.get(prefix);
12876
13177
  if (projectTokens) {
12877
13178
  if (projectTokens.has(token)) return true;
@@ -13620,7 +13921,7 @@ var tailwindStructureRules = {
13620
13921
  languageOptions: { tolerant: true },
13621
13922
  rules: Object.fromEntries(
13622
13923
  tailwindRuleIds.filter(
13623
- (id) => id !== "pasika/css-entry-point" && id !== "pasika/feature-css-placement" && id !== "pasika/unique-css-definition" && id !== "pasika/utility-mapping" && id !== "pasika/utility-name-parity" && id !== "pasika/no-utility-alias" && id !== "pasika/root-variables" && id !== "pasika/color-role-usage" && id !== "pasika/theme-mode"
13924
+ (id) => id !== "pasika/css-entry-point" && id !== "pasika/feature-css-placement" && id !== "pasika/unique-css-definition" && id !== "pasika/utility-mapping" && id !== "pasika/utility-name-parity" && id !== "pasika/no-utility-alias" && id !== "pasika/root-variables" && id !== "pasika/color-role-usage" && id !== "pasika/color-literal-usage" && id !== "pasika/theme-mode"
13624
13925
  ).map((id) => [id, "error"])
13625
13926
  )
13626
13927
  };
@@ -13643,6 +13944,7 @@ var tailwindImportGraph = {
13643
13944
  "pasika/no-utility-alias": "error",
13644
13945
  "pasika/root-variables": "error",
13645
13946
  "pasika/color-role-usage": "error",
13947
+ "pasika/color-literal-usage": "error",
13646
13948
  "pasika/theme-mode": "error"
13647
13949
  }
13648
13950
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pasika",
3
- "version": "0.10.133",
3
+ "version": "0.10.134",
4
4
  "description": "Reusable agent setup package",
5
5
  "repository": {
6
6
  "type": "git",