pasika 0.10.133 → 0.10.135

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-")) {
@@ -10206,6 +10306,7 @@ function themeClasses(name) {
10206
10306
  if (name.startsWith("--aspect-")) return [`aspect-${name.slice("--aspect-".length)}`];
10207
10307
  if (name.startsWith("--leading-")) return [`leading-${name.slice("--leading-".length)}`];
10208
10308
  if (name.startsWith("--animate-")) return [`animate-${name.slice("--animate-".length)}`];
10309
+ if (name.startsWith("--inset-shadow-")) return [`inset-shadow-${name.slice("--inset-shadow-".length)}`];
10209
10310
  if (name.startsWith("--radius-")) return [`rounded-${name.slice("--radius-".length)}`];
10210
10311
  if (name.startsWith("--text-") || name.startsWith("--tracking-") || name.startsWith("--font-")) {
10211
10312
  return [name.slice(2)];
@@ -10266,7 +10367,7 @@ function buildCssDependencyGraph(options) {
10266
10367
  } else if (node.name === "theme" && node.loc) {
10267
10368
  const source = file.content.slice(node.loc.start.offset, node.loc.end.offset).replace(/\/\*[\s\S]*?\*\//gu, (comment) => comment.replace(/[^\r\n]/gu, " "));
10268
10369
  for (const found of source.matchAll(
10269
- /(?:^|(?<=[{;\r\n]))\s*(?<name>--[a-z][\w-]*)\s*:\s*(?<value>[^;}\r\n]+)(?=;|[}])/gu
10370
+ /(?:^|(?<=[{;\r\n]))\s*(?<name>--[a-z][\w-]*)\s*:\s*(?<value>[^;}]+)(?=;|[}])/gu
10270
10371
  )) {
10271
10372
  const name = found.groups?.name;
10272
10373
  const value = found.groups?.value;
@@ -10691,27 +10792,28 @@ var noUtilityAliasRule = {
10691
10792
 
10692
10793
  // eslint/rules/tailwind/root-variables.ts
10693
10794
  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;
10795
+ import { parse as parse6, walk as walk3 } from "@eslint/css-tree";
10796
+
10797
+ // eslint/rules/tailwind/external-mapping.ts
10798
+ function externalMapping(value, token, authoredVariables) {
10799
+ const match = /^var\s*\(\s*(?<source>--[a-z][\w-]*)\s*,\s*(?<fallback>[\s\S]+)\)$/u.exec(value.trim());
10800
+ const source = match?.groups?.source;
10801
+ const fallback = match?.groups?.fallback?.trim();
10802
+ return Boolean(
10803
+ source && source !== token && !authoredVariables.has(source) && fallback && !/\b(?:var|env|attr|calc|min|max|clamp|color-mix|light-dark)\s*\(/iu.test(fallback)
10804
+ );
10708
10805
  }
10806
+
10807
+ // eslint/rules/tailwind/root-variables.ts
10808
+ var INLINE_ONLY_MESSAGE = "Define all Tailwind theme tokens in @theme inline, including fixed values and resets.";
10809
+ 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.`;
10810
+ var REDUNDANT_INLINE_MESSAGE = (name) => `Tailwind token "${name}" has no @variant override; define its fixed value directly in @theme inline.`;
10709
10811
  var rootVariablesRule = {
10710
10812
  meta: {
10711
10813
  schema: [],
10712
10814
  type: "problem",
10713
10815
  docs: {
10714
- description: "Require @theme for fixed tokens and fixed-token aliases, matching :root variables for @theme inline tokens, and no other token redeclarations."
10816
+ description: "Require @theme inline for fixed tokens, variant-driven same-name mappings, and external variables with fallbacks."
10715
10817
  }
10716
10818
  },
10717
10819
  create(context) {
@@ -10734,7 +10836,11 @@ var rootVariablesRule = {
10734
10836
  const names = new Set(
10735
10837
  graph.definitions.filter((symbol) => symbol.kind === "theme-token").map((symbol) => symbol.name)
10736
10838
  );
10737
- function isFixedThemeExpression(name, value, visited = /* @__PURE__ */ new Set([name])) {
10839
+ function isComposableThemeExpression(name, value, allowRuntimeReferences, visited = /* @__PURE__ */ new Set([name])) {
10840
+ if (externalMapping(value, name, authoredVariables)) return true;
10841
+ if (mirroredTokens.has(name) && new RegExp(`^\\s*var\\(\\s*${name}\\s*\\)\\s*$`, "u").test(value)) {
10842
+ return allowRuntimeReferences && defaultRootNames.has(name) && variantNames.has(name);
10843
+ }
10738
10844
  const references = [...value.matchAll(/\bvar\s*\(\s*(?<target>--[a-z][\w-]*)/giu)].map(
10739
10845
  (match) => match.groups?.target
10740
10846
  );
@@ -10743,41 +10849,15 @@ var rootVariablesRule = {
10743
10849
  if (!target || visited.has(target)) return false;
10744
10850
  const next = themeValues.get(target);
10745
10851
  if (!next) return false;
10746
- return isFixedThemeExpression(target, next, /* @__PURE__ */ new Set([...visited, target]));
10852
+ return isComposableThemeExpression(target, next, allowRuntimeReferences, /* @__PURE__ */ new Set([...visited, target]));
10747
10853
  });
10748
10854
  }
10749
10855
  const mirroredTokens = /* @__PURE__ */ new Set();
10750
- let hasPlainThemeBlocks = false;
10751
- let hasInlineThemeBlocks = false;
10752
- let inlineBlocksAreExternalFonts = true;
10753
10856
  for (const file of [...stylesheets.filter((candidate) => path72.normalize(candidate) !== current), current]) {
10754
10857
  const source = (path72.normalize(file) === current ? context.sourceCode.text : textOf(file)).replace(
10755
10858
  /\/\*[\s\S]*?\*\//gu,
10756
10859
  " "
10757
10860
  );
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
10861
  for (const theme of source.matchAll(/@theme(?<options>(?:\s+[\w-]+)*)\s*\{(?<body>[^{}]*)\}/gu)) {
10782
10862
  const inline = /\binline\b/u.test(theme.groups?.options ?? "");
10783
10863
  for (const declaration of (theme.groups?.body ?? "").matchAll(
@@ -10786,42 +10866,41 @@ var rootVariablesRule = {
10786
10866
  const { name, value } = declaration.groups ?? {};
10787
10867
  if (!name) continue;
10788
10868
  names.add(name);
10789
- if (inline && /\bvar\s*\(/u.test(value ?? "")) {
10869
+ if (inline && new RegExp(`^\\s*var\\(\\s*${name}\\s*\\)\\s*$`, "u").test(value ?? "")) {
10790
10870
  mirroredTokens.add(name);
10791
10871
  }
10792
10872
  }
10793
10873
  }
10794
10874
  }
10795
- const overriddenNames = new Set(
10796
- graph.definitions.filter((symbol) => symbol.kind === "root-variable" && symbol.scope === "conditional").map((symbol) => symbol.name)
10797
- );
10875
+ const authoredVariables = new Set(names);
10876
+ const variantNames = /* @__PURE__ */ new Set();
10798
10877
  for (const file of [...stylesheets.filter((candidate) => path72.normalize(candidate) !== current), current]) {
10799
10878
  const css2 = path72.normalize(file) === current ? context.sourceCode.text : textOf(file);
10800
10879
  const tree = parse6(css2, { parseAtrulePrelude: false });
10880
+ let variantDepth = 0;
10801
10881
  walk3(tree, {
10802
10882
  enter(node) {
10883
+ if (node.type === "Atrule" && node.name === "variant") variantDepth += 1;
10803
10884
  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);
10885
+ authoredVariables.add(node.property);
10886
+ if (variantDepth > 0) variantNames.add(node.property);
10887
+ },
10888
+ leave(node) {
10889
+ if (node.type === "Atrule" && node.name === "variant") variantDepth -= 1;
10806
10890
  }
10807
10891
  });
10808
10892
  }
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
10893
  const defaultRootNames = new Set(
10815
10894
  graph.definitions.filter((symbol) => symbol.kind === "root-variable" && symbol.scope === "default").map((symbol) => symbol.name)
10816
10895
  );
10817
10896
  return {
10818
10897
  "StyleSheet:exit"(node) {
10819
10898
  const localThemes = atrulesNamed(node, "theme");
10820
- if (hasPlainThemeBlocks && hasInlineThemeBlocks && !inlineBlocksAreExternalFonts && localThemes[0]) {
10821
- context.report({ node: localThemes[0], message: MIXED_THEME_MESSAGE });
10822
- }
10823
10899
  for (const theme of localThemes) {
10824
- const inline = preludeIdentifiers(theme).includes("inline");
10900
+ if (!preludeIdentifiers(theme).includes("inline")) {
10901
+ context.report({ node: theme, message: INLINE_ONLY_MESSAGE });
10902
+ continue;
10903
+ }
10825
10904
  const source = context.sourceCode.getText(theme).replace(/\/\*[\s\S]*?\*\//gu, (comment) => comment.replace(/[^\r\n]/gu, " "));
10826
10905
  for (const match of source.matchAll(
10827
10906
  /(?:^|[{;\r\n])\s*(?<name>--[a-z][\w-]*)\s*:\s*(?<value>[^;{}]*)(?=;|[}])/gu
@@ -10830,60 +10909,48 @@ var rootVariablesRule = {
10830
10909
  if (!name || value === void 0) continue;
10831
10910
  const unquoted = value.replace(/"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'/gu, "");
10832
10911
  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)) {
10912
+ const directReference = /^var\s*\(\s*(?<target>--[a-z][\w-]*)\s*\)$/u.exec(unquoted.trim())?.groups?.target;
10913
+ if (directReference) {
10914
+ if (directReference !== name) {
10915
+ context.report({
10916
+ node: theme,
10917
+ message: `Tailwind token "${name}" must reference its same-named CSS variable, not "${directReference}".`
10918
+ });
10919
+ } else if (!defaultRootNames.has(name)) {
10920
+ context.report({
10921
+ node: theme,
10922
+ message: `Tailwind token "${name}" references "${name}" without a default :root declaration.`
10923
+ });
10924
+ } else if (!variantNames.has(name)) {
10859
10925
  context.report({ node: theme, message: REDUNDANT_INLINE_MESSAGE(name) });
10860
10926
  }
10927
+ continue;
10928
+ }
10929
+ if (externalMapping(value, name, authoredVariables)) continue;
10930
+ const fallbackTarget = /^var\s*\(\s*(?<target>--[a-z][\w-]*)\s*,/u.exec(unquoted.trim())?.groups?.target;
10931
+ if (fallbackTarget && authoredVariables.has(fallbackTarget)) {
10932
+ context.report({ node: theme, message: INLINE_MAPPING_MESSAGE(name) });
10933
+ continue;
10934
+ }
10935
+ const runtimeEffect = /^--(?:shadow|inset-shadow|drop-shadow|text-shadow|background-image)-/u.test(name);
10936
+ if (referencesVariable && !isComposableThemeExpression(name, unquoted, runtimeEffect)) {
10937
+ context.report({ node: theme, message: INLINE_MAPPING_MESSAGE(name) });
10861
10938
  }
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
10939
  }
10876
10940
  }
10877
- function checkTokenRedeclarations(currentNode, conditional = false) {
10941
+ function checkTokenRedeclarations(currentNode, conditional = false, inVariant = false) {
10878
10942
  if (currentNode.type === "Atrule" && ["theme", "utility", "keyframes"].includes(currentNode.name)) {
10879
10943
  return;
10880
10944
  }
10881
10945
  const isConditional = conditional || currentNode.type === "Atrule" && currentNode.name !== "layer";
10946
+ const isVariant = inVariant || currentNode.type === "Atrule" && currentNode.name === "variant";
10882
10947
  const isDefaultRoot = currentNode.type === "Rule" && context.sourceCode.getText(currentNode.prelude).trim() === ":root" && !isConditional;
10883
10948
  if (currentNode.type === "Rule" && !isDefaultRoot || currentNode.type === "Atrule" && currentNode.name === "variant") {
10884
10949
  for (const declaration of blockChildren2(currentNode)) {
10885
10950
  if (declaration.type !== "Declaration") continue;
10886
- if (mirroredTokens.has(declaration.property)) continue;
10951
+ if (mirroredTokens.has(declaration.property) && (isVariant || !variantNames.has(declaration.property))) {
10952
+ continue;
10953
+ }
10887
10954
  if (!names.has(declaration.property) && !declaration.property.startsWith("--color-")) continue;
10888
10955
  context.report({
10889
10956
  node: declaration,
@@ -10893,7 +10960,9 @@ var rootVariablesRule = {
10893
10960
  }
10894
10961
  const children = currentNode.type === "StyleSheet" ? currentNode.children : blockChildren2(currentNode);
10895
10962
  for (const child of children) {
10896
- if (child.type === "Atrule" || child.type === "Rule") checkTokenRedeclarations(child, isConditional);
10963
+ if (child.type === "Atrule" || child.type === "Rule") {
10964
+ checkTokenRedeclarations(child, isConditional, isVariant);
10965
+ }
10897
10966
  }
10898
10967
  }
10899
10968
  checkTokenRedeclarations(node);
@@ -10916,9 +10985,21 @@ var rootVariablesRule = {
10916
10985
 
10917
10986
  // eslint/rules/tailwind/stylesheet-ordering.ts
10918
10987
  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"];
10988
+ import { generate as generate3 } from "@eslint/css-tree";
10989
+ var SECTION_ORDER = [
10990
+ "Tailwind import",
10991
+ "theme",
10992
+ "feature imports",
10993
+ "custom-variant",
10994
+ "root",
10995
+ "utility",
10996
+ "base styles",
10997
+ "keyframes"
10998
+ ];
10921
10999
  var LATE_RESET_ERROR = "The global --*: initial theme reset must precede feature stylesheet imports, or it will erase their theme tokens.";
11000
+ var DUPLICATE_THEME_ERROR = "globals.css must use one @theme inline block for the reset and all global tokens; merge the theme blocks.";
11001
+ var RESET_FIRST_ERROR = "The --*: initial reset must be the first declaration in the globals.css @theme inline block.";
11002
+ 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
11003
  function themeBody(node, source) {
10923
11004
  if (node.type !== "Atrule" || node.name !== "theme" || !node.loc) return void 0;
10924
11005
  const raw = source.slice(node.loc.start.offset, node.loc.end.offset);
@@ -10930,12 +11011,9 @@ function themeBody(node, source) {
10930
11011
  function containsThemeReset(body) {
10931
11012
  return body !== void 0 && /(?:^|[;{])\s*--\*\s*:\s*initial\s*(?:;|$)/u.test(body);
10932
11013
  }
10933
- function isStandaloneThemeReset(body) {
10934
- return body !== void 0 && /^--\*\s*:\s*initial\s*;?$/u.test(body);
10935
- }
10936
11014
  function isTailwindImport(node) {
10937
11015
  if (node.type !== "Atrule" || node.name !== "import" || !node.prelude) return false;
10938
- return /^["']tailwindcss["'](?:\s|$)/u.test(generate4(node.prelude).trim());
11016
+ return /^["']tailwindcss["'](?:\s|$)/u.test(generate3(node.prelude).trim());
10939
11017
  }
10940
11018
  function scopesCustomProperties(node) {
10941
11019
  if (node.type === "Declaration") return node.property.startsWith("--");
@@ -10957,80 +11035,68 @@ var stylesheetOrderingRule = {
10957
11035
  if (path73.normalize(path73.resolve(context.filename)) !== entryPoint) return {};
10958
11036
  return {
10959
11037
  "StyleSheet:exit"(node) {
11038
+ const topLevel = [...node.children];
11039
+ const first = topLevel[0];
11040
+ const second = topLevel[1];
11041
+ if (first && isTailwindImport(first) && second?.type === "Atrule" && second.name === "import" && !isTailwindImport(second) && topLevel.some((child) => containsThemeReset(themeBody(child, context.sourceCode.text)))) {
11042
+ context.report({ node, message: LATE_RESET_ERROR });
11043
+ return;
11044
+ }
11045
+ if (!first || !isTailwindImport(first) || second?.type !== "Atrule" || second.name !== "theme" || !preludeIdentifiers(second).includes("inline")) {
11046
+ context.report({ node, message: ORDER_ERROR });
11047
+ return;
11048
+ }
10960
11049
  const sections = [];
10961
- let index = 0;
10962
- let sawTailwindImport = false;
10963
11050
  let sawFeatureImport = false;
10964
- let sawNonImport = false;
10965
- let sawEarlyReset = false;
11051
+ let themeBlocks2 = 0;
10966
11052
  for (const child of node.children) {
10967
11053
  const body = themeBody(child, context.sourceCode.text);
10968
- let isEarlyReset = false;
11054
+ if (containsThemeReset(body) && !/^--\*\s*:\s*initial\s*(?:;|$)/u.test(body ?? "")) {
11055
+ context.report({ node: child, message: RESET_FIRST_ERROR });
11056
+ return;
11057
+ }
10969
11058
  if (containsThemeReset(body) && sawFeatureImport) {
10970
11059
  context.report({ node: child, message: LATE_RESET_ERROR });
10971
11060
  return;
10972
11061
  }
10973
11062
  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 });
11063
+ if (isTailwindImport(child)) {
11064
+ sections.push("Tailwind import");
11065
+ } else {
11066
+ sawFeatureImport = true;
11067
+ sections.push("feature imports");
11068
+ }
10982
11069
  } else if (child.type === "Atrule" && child.name === "theme") {
10983
- sections.push({ name: "theme", at: index });
11070
+ themeBlocks2 += 1;
11071
+ sections.push("theme");
11072
+ } else if (child.type === "Atrule" && child.name === "custom-variant") {
11073
+ sections.push("custom-variant");
10984
11074
  } else if (child.type === "Atrule" && child.name === "utility") {
10985
- sections.push({ name: "utility", at: index });
11075
+ sections.push("utility");
10986
11076
  } else if (child.type === "Atrule" && child.name === "keyframes") {
10987
- sections.push({ name: "keyframes", at: index });
11077
+ sections.push("keyframes");
10988
11078
  } else if (child.type === "Atrule" && child.name === "layer") {
10989
11079
  if (preludeIdentifiers(child).includes("base")) {
10990
- sections.push({ name: "base styles", at: index });
11080
+ sections.push("base styles");
10991
11081
  }
10992
11082
  } else if (child.type === "Rule" && (selectorNames(child).includes("root") || scopesCustomProperties(child))) {
10993
- sections.push({ name: "root", at: index });
11083
+ sections.push("root");
10994
11084
  } 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;
11085
+ sections.push("root");
10999
11086
  }
11000
- index += 1;
11001
11087
  }
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;
11088
+ if (themeBlocks2 > 1) {
11089
+ context.report({ node, message: DUPLICATE_THEME_ERROR });
11090
+ return;
11018
11091
  }
11019
11092
  let furthestSection = -1;
11020
- let furthestName;
11021
11093
  for (const section of sections) {
11022
- const order = SECTION_ORDER.findIndex((name) => name === section.name);
11094
+ const order = SECTION_ORDER.findIndex((name) => name === section);
11023
11095
  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
- });
11096
+ context.report({ node, message: ORDER_ERROR });
11028
11097
  return;
11029
11098
  }
11030
- if (order > furthestSection) {
11031
- furthestSection = order;
11032
- furthestName = section.name;
11033
- }
11099
+ furthestSection = order;
11034
11100
  }
11035
11101
  }
11036
11102
  };
@@ -11039,7 +11105,7 @@ var stylesheetOrderingRule = {
11039
11105
 
11040
11106
  // eslint/rules/tailwind/theme-mode.ts
11041
11107
  import path74 from "path";
11042
- import { generate as generate5 } from "@eslint/css-tree";
11108
+ import { generate as generate4 } from "@eslint/css-tree";
11043
11109
  var MODE_NAME = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u;
11044
11110
  var MODE_REFERENCE = /(?:^|[^a-z0-9-])(?<name>[a-z][a-z0-9]*(?:-[a-z0-9]+)*):(?=[a-z[(])/gu;
11045
11111
  var MODE_ATTRIBUTE = /data-theme\s*=\s*["'](?<name>[a-z][a-z0-9]*(?:-[a-z0-9]+)*)["']/gu;
@@ -11086,7 +11152,7 @@ var themeModeRule = {
11086
11152
  const registered = /* @__PURE__ */ new Set();
11087
11153
  for (const variant of atrulesNamed(node, "custom-variant")) {
11088
11154
  if (variant.type !== "Atrule") continue;
11089
- const prelude = variant.prelude ? generate5(variant.prelude) : "";
11155
+ const prelude = variant.prelude ? generate4(variant.prelude) : "";
11090
11156
  const name = variantName(prelude);
11091
11157
  if (!name) continue;
11092
11158
  if (!isModeVariant(name, prelude)) continue;
@@ -11115,14 +11181,14 @@ var themeModeRule = {
11115
11181
  const globalRegistrations = current === globalFile ? registered : registeredModes(read(globalFile));
11116
11182
  for (const variant of atrulesNamed(node, "variant")) {
11117
11183
  if (variant.type !== "Atrule" || !variant.prelude) continue;
11118
- const name = variantName(generate5(variant.prelude));
11184
+ const name = variantName(generate4(variant.prelude));
11119
11185
  if (name && (name === "dark" || name.startsWith("theme-") || globalRegistrations.has(name) || modeHints.has(name)) && !globalRegistrations.has(name)) {
11120
11186
  context.report({ node: variant, message: MISSING_CSS_VARIANT(name) });
11121
11187
  }
11122
11188
  }
11123
11189
  for (const rule of rules(node)) {
11124
11190
  if (rule.type !== "Rule") continue;
11125
- const selector = generate5(rule.prelude);
11191
+ const selector = generate4(rule.prelude);
11126
11192
  if (/\[\s*data-theme(?:\s*[~|^$*]?=|\s*\])/u.test(selector)) {
11127
11193
  context.report({ node: rule, message: SELECTOR_OVERRIDE });
11128
11194
  } else if (/\.dark(?![\w-])/u.test(selector)) {
@@ -11131,7 +11197,7 @@ var themeModeRule = {
11131
11197
  }
11132
11198
  walkNodes(node, (child) => {
11133
11199
  if (child.type !== "Atrule" || child.name !== "media" || !child.prelude) return;
11134
- if (/\bprefers-color-scheme\b/u.test(generate5(child.prelude))) {
11200
+ if (/\bprefers-color-scheme\b/u.test(generate4(child.prelude))) {
11135
11201
  context.report({ node: child, message: SYSTEM_OVERRIDE });
11136
11202
  }
11137
11203
  });
@@ -11161,15 +11227,16 @@ var themeModeRule = {
11161
11227
 
11162
11228
  // eslint/rules/tailwind/theme-reset.ts
11163
11229
  import path75 from "path";
11164
- import { generate as generate6 } from "@eslint/css-tree";
11230
+ import { generate as generate5 } from "@eslint/css-tree";
11165
11231
  function isThemeReset(node) {
11166
11232
  if (!node) return false;
11167
- if (node.type === "Declaration") return node.property === "--*" && generate6(node.value).trim() === "initial";
11233
+ if (node.type === "Declaration") return node.property === "--*" && generate5(node.value).trim() === "initial";
11168
11234
  if (node.type === "Raw") return /(?:^|[;{])\s*--\*\s*:\s*initial\s*(?:;|$)/u.test(node.value);
11169
11235
  return false;
11170
11236
  }
11171
- function hasThemeReset(node) {
11237
+ function hasThemeReset(node, inlineOnly = false) {
11172
11238
  return atrulesNamed(node, "theme").some((theme) => {
11239
+ if (inlineOnly && !preludeIdentifiers(theme).includes("inline")) return false;
11173
11240
  if (blockChildren2(theme).some(isThemeReset)) return true;
11174
11241
  let nested = false;
11175
11242
  walkNodes(theme, (child) => {
@@ -11203,10 +11270,10 @@ var themeResetRule = {
11203
11270
  }
11204
11271
  return;
11205
11272
  }
11206
- if (!hasReset) {
11273
+ if (!hasThemeReset(node, true)) {
11207
11274
  context.report({
11208
11275
  node,
11209
- message: "globals.css must reset Tailwind's default theme with --*: initial inside @theme or @theme inline."
11276
+ message: "globals.css must reset Tailwind's default theme with --*: initial inside @theme inline."
11210
11277
  });
11211
11278
  }
11212
11279
  }
@@ -11295,13 +11362,226 @@ var uniqueCssDefinitionRule = {
11295
11362
 
11296
11363
  // eslint/rules/tailwind/unique-css-token-value.ts
11297
11364
  import path77 from "path";
11365
+
11366
+ // eslint/rules/tailwind/css-named-colors.ts
11367
+ var CSS_NAMED_COLORS = {
11368
+ aliceblue: [240, 248, 255],
11369
+ antiquewhite: [250, 235, 215],
11370
+ aqua: [0, 255, 255],
11371
+ aquamarine: [127, 255, 212],
11372
+ azure: [240, 255, 255],
11373
+ beige: [245, 245, 220],
11374
+ bisque: [255, 228, 196],
11375
+ black: [0, 0, 0],
11376
+ blanchedalmond: [255, 235, 205],
11377
+ blue: [0, 0, 255],
11378
+ blueviolet: [138, 43, 226],
11379
+ brown: [165, 42, 42],
11380
+ burlywood: [222, 184, 135],
11381
+ cadetblue: [95, 158, 160],
11382
+ chartreuse: [127, 255, 0],
11383
+ chocolate: [210, 105, 30],
11384
+ coral: [255, 127, 80],
11385
+ cornflowerblue: [100, 149, 237],
11386
+ cornsilk: [255, 248, 220],
11387
+ crimson: [220, 20, 60],
11388
+ cyan: [0, 255, 255],
11389
+ darkblue: [0, 0, 139],
11390
+ darkcyan: [0, 139, 139],
11391
+ darkgoldenrod: [184, 134, 11],
11392
+ darkgray: [169, 169, 169],
11393
+ darkgreen: [0, 100, 0],
11394
+ darkgrey: [169, 169, 169],
11395
+ darkkhaki: [189, 183, 107],
11396
+ darkmagenta: [139, 0, 139],
11397
+ darkolivegreen: [85, 107, 47],
11398
+ darkorange: [255, 140, 0],
11399
+ darkorchid: [153, 50, 204],
11400
+ darkred: [139, 0, 0],
11401
+ darksalmon: [233, 150, 122],
11402
+ darkseagreen: [143, 188, 143],
11403
+ darkslateblue: [72, 61, 139],
11404
+ darkslategray: [47, 79, 79],
11405
+ darkslategrey: [47, 79, 79],
11406
+ darkturquoise: [0, 206, 209],
11407
+ darkviolet: [148, 0, 211],
11408
+ deeppink: [255, 20, 147],
11409
+ deepskyblue: [0, 191, 255],
11410
+ dimgray: [105, 105, 105],
11411
+ dimgrey: [105, 105, 105],
11412
+ dodgerblue: [30, 144, 255],
11413
+ firebrick: [178, 34, 34],
11414
+ floralwhite: [255, 250, 240],
11415
+ forestgreen: [34, 139, 34],
11416
+ fuchsia: [255, 0, 255],
11417
+ gainsboro: [220, 220, 220],
11418
+ ghostwhite: [248, 248, 255],
11419
+ gold: [255, 215, 0],
11420
+ goldenrod: [218, 165, 32],
11421
+ gray: [128, 128, 128],
11422
+ green: [0, 128, 0],
11423
+ greenyellow: [173, 255, 47],
11424
+ grey: [128, 128, 128],
11425
+ honeydew: [240, 255, 240],
11426
+ hotpink: [255, 105, 180],
11427
+ indianred: [205, 92, 92],
11428
+ indigo: [75, 0, 130],
11429
+ ivory: [255, 255, 240],
11430
+ khaki: [240, 230, 140],
11431
+ lavender: [230, 230, 250],
11432
+ lavenderblush: [255, 240, 245],
11433
+ lawngreen: [124, 252, 0],
11434
+ lemonchiffon: [255, 250, 205],
11435
+ lightblue: [173, 216, 230],
11436
+ lightcoral: [240, 128, 128],
11437
+ lightcyan: [224, 255, 255],
11438
+ lightgoldenrodyellow: [250, 250, 210],
11439
+ lightgray: [211, 211, 211],
11440
+ lightgreen: [144, 238, 144],
11441
+ lightgrey: [211, 211, 211],
11442
+ lightpink: [255, 182, 193],
11443
+ lightsalmon: [255, 160, 122],
11444
+ lightseagreen: [32, 178, 170],
11445
+ lightskyblue: [135, 206, 250],
11446
+ lightslategray: [119, 136, 153],
11447
+ lightslategrey: [119, 136, 153],
11448
+ lightsteelblue: [176, 196, 222],
11449
+ lightyellow: [255, 255, 224],
11450
+ lime: [0, 255, 0],
11451
+ limegreen: [50, 205, 50],
11452
+ linen: [250, 240, 230],
11453
+ magenta: [255, 0, 255],
11454
+ maroon: [128, 0, 0],
11455
+ mediumaquamarine: [102, 205, 170],
11456
+ mediumblue: [0, 0, 205],
11457
+ mediumorchid: [186, 85, 211],
11458
+ mediumpurple: [147, 112, 219],
11459
+ mediumseagreen: [60, 179, 113],
11460
+ mediumslateblue: [123, 104, 238],
11461
+ mediumspringgreen: [0, 250, 154],
11462
+ mediumturquoise: [72, 209, 204],
11463
+ mediumvioletred: [199, 21, 133],
11464
+ midnightblue: [25, 25, 112],
11465
+ mintcream: [245, 255, 250],
11466
+ mistyrose: [255, 228, 225],
11467
+ moccasin: [255, 228, 181],
11468
+ navajowhite: [255, 222, 173],
11469
+ navy: [0, 0, 128],
11470
+ oldlace: [253, 245, 230],
11471
+ olive: [128, 128, 0],
11472
+ olivedrab: [107, 142, 35],
11473
+ orange: [255, 165, 0],
11474
+ orangered: [255, 69, 0],
11475
+ orchid: [218, 112, 214],
11476
+ palegoldenrod: [238, 232, 170],
11477
+ palegreen: [152, 251, 152],
11478
+ paleturquoise: [175, 238, 238],
11479
+ palevioletred: [219, 112, 147],
11480
+ papayawhip: [255, 239, 213],
11481
+ peachpuff: [255, 218, 185],
11482
+ peru: [205, 133, 63],
11483
+ pink: [255, 192, 203],
11484
+ plum: [221, 160, 221],
11485
+ powderblue: [176, 224, 230],
11486
+ purple: [128, 0, 128],
11487
+ rebeccapurple: [102, 51, 153],
11488
+ red: [255, 0, 0],
11489
+ rosybrown: [188, 143, 143],
11490
+ royalblue: [65, 105, 225],
11491
+ saddlebrown: [139, 69, 19],
11492
+ salmon: [250, 128, 114],
11493
+ sandybrown: [244, 164, 96],
11494
+ seagreen: [46, 139, 87],
11495
+ seashell: [255, 245, 238],
11496
+ sienna: [160, 82, 45],
11497
+ silver: [192, 192, 192],
11498
+ skyblue: [135, 206, 235],
11499
+ slateblue: [106, 90, 205],
11500
+ slategray: [112, 128, 144],
11501
+ slategrey: [112, 128, 144],
11502
+ snow: [255, 250, 250],
11503
+ springgreen: [0, 255, 127],
11504
+ steelblue: [70, 130, 180],
11505
+ tan: [210, 180, 140],
11506
+ teal: [0, 128, 128],
11507
+ thistle: [216, 191, 216],
11508
+ tomato: [255, 99, 71],
11509
+ turquoise: [64, 224, 208],
11510
+ violet: [238, 130, 238],
11511
+ wheat: [245, 222, 179],
11512
+ white: [255, 255, 255],
11513
+ whitesmoke: [245, 245, 245],
11514
+ yellow: [255, 255, 0],
11515
+ yellowgreen: [154, 205, 50]
11516
+ };
11517
+
11518
+ // eslint/rules/tailwind/unique-css-token-value.ts
11519
+ function normalizedColor(text) {
11520
+ const hex = /^#(?<digits>[\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$/iu.exec(text)?.groups?.digits;
11521
+ if (hex) {
11522
+ const expanded = hex.length <= 4 ? Array.from(hex.matchAll(/./gu), (match) => match[0] + match[0]).join("") : hex;
11523
+ const channels2 = [0, 2, 4].map((start) => Number.parseInt(expanded.slice(start, start + 2), 16));
11524
+ const alpha2 = expanded.length === 8 ? Number.parseInt(expanded.slice(6, 8), 16) / 255 : 1;
11525
+ return `srgb:${channels2.join(",")}/${String(alpha2)}`;
11526
+ }
11527
+ const named = CSS_NAMED_COLORS[text.toLowerCase()];
11528
+ if (Array.isArray(named)) return `srgb:${named.join(",")}/1`;
11529
+ const expression = /^(?<kind>rgba?|hsla?)\(\s*(?<body>[^()]*)\s*\)$/iu.exec(text);
11530
+ if (!expression?.groups) return void 0;
11531
+ const parts = (expression.groups.body ?? "").replace(/,/gu, " ").replace(/\//gu, " / ").trim().split(/\s+/u);
11532
+ const alphaIndex = parts[3] === "/" ? 4 : 3;
11533
+ if (parts.length !== 3 && !(parts.length === 4 && parts[3] !== "/") && !(parts.length === 5 && parts[3] === "/")) {
11534
+ return void 0;
11535
+ }
11536
+ const alphaText = parts[alphaIndex];
11537
+ const parseNumeric = (raw) => {
11538
+ if (!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)%?$/u.test(raw)) return void 0;
11539
+ const numeric = Number.parseFloat(raw);
11540
+ return Number.isFinite(numeric) ? numeric : void 0;
11541
+ };
11542
+ const alpha = alphaText === void 0 ? 1 : parseNumeric(alphaText);
11543
+ if (alpha === void 0 || alpha < 0 || (alphaText?.endsWith("%") ? alpha > 100 : alpha > 1)) return void 0;
11544
+ const opacity = alphaText?.endsWith("%") ? alpha / 100 : alpha;
11545
+ const kind = expression.groups.kind?.toLowerCase();
11546
+ let channels;
11547
+ if (kind === "rgb" || kind === "rgba") {
11548
+ const rgb = parts.slice(0, 3).map((part) => {
11549
+ const value = parseNumeric(part);
11550
+ if (value === void 0) return Number.NaN;
11551
+ if (part.endsWith("%")) return value * 255 / 100;
11552
+ return value;
11553
+ });
11554
+ if (rgb.some((value) => !Number.isFinite(value) || value < 0 || value > 255)) return void 0;
11555
+ channels = rgb;
11556
+ } else {
11557
+ const hue = parseNumeric(parts[0] ?? "");
11558
+ const saturation = parseNumeric(parts[1] ?? "");
11559
+ const lightness = parseNumeric(parts[2] ?? "");
11560
+ if (hue === void 0 || saturation === void 0 || lightness === void 0) return void 0;
11561
+ if (parts[0]?.endsWith("%") || !parts[1]?.endsWith("%") || !parts[2]?.endsWith("%")) return void 0;
11562
+ if (saturation < 0 || saturation > 100 || lightness < 0 || lightness > 100) return void 0;
11563
+ const h = (hue % 360 + 360) % 360;
11564
+ const s = saturation / 100;
11565
+ const l = lightness / 100;
11566
+ const chroma = (1 - Math.abs(2 * l - 1)) * s;
11567
+ const x = chroma * (1 - Math.abs(h / 60 % 2 - 1));
11568
+ const m = l - chroma / 2;
11569
+ let rgb;
11570
+ if (h < 60) rgb = [chroma, x, 0];
11571
+ else if (h < 120) rgb = [x, chroma, 0];
11572
+ else if (h < 180) rgb = [0, chroma, x];
11573
+ else if (h < 240) rgb = [0, x, chroma];
11574
+ else if (h < 300) rgb = [x, 0, chroma];
11575
+ else rgb = [chroma, 0, x];
11576
+ const exact = rgb.map((channel) => (channel + m) * 255);
11577
+ if (exact.some((channel) => Math.abs(channel - Math.round(channel)) > 1e-10)) return void 0;
11578
+ channels = exact.map(Math.round);
11579
+ }
11580
+ return `srgb:${channels.join(",")}/${String(opacity)}`;
11581
+ }
11298
11582
  function valueIdentity(value) {
11299
11583
  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}`;
11584
+ return normalizedColor(text) ?? text;
11305
11585
  }
11306
11586
  function isComparable(symbol) {
11307
11587
  if (symbol.value === void 0 || symbol.value.trim() === "") return false;
@@ -11345,10 +11625,11 @@ var uniqueCssTokenValueRule = {
11345
11625
  const redundant = /* @__PURE__ */ new Map();
11346
11626
  for (const group of byValue.values()) {
11347
11627
  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);
11628
+ const seen = [];
11629
+ for (const definition of group) {
11630
+ const original = seen.find((prior) => prior.name !== definition.name);
11631
+ if (original) redundant.set(definition, original);
11632
+ seen.push(definition);
11352
11633
  }
11353
11634
  }
11354
11635
  return {
@@ -12042,7 +12323,7 @@ var utilityMappingRule = {
12042
12323
  import { spawnSync as spawnSync2 } from "child_process";
12043
12324
  import { existsSync as existsSync13 } from "fs";
12044
12325
  import path80 from "path";
12045
- import { generate as generate7, parse as parse7, toPlainObject as toPlainObject2 } from "@eslint/css-tree";
12326
+ import { generate as generate6, parse as parse7, toPlainObject as toPlainObject2 } from "@eslint/css-tree";
12046
12327
  function numericPart(value) {
12047
12328
  const match = /^(?<negative>-?)(?<number>(?:\d+(?:\.\d+)?|\.\d+))(?<unit>%|px|rem|em|vw|vh|dvw|dvh|ch|fr|s|ms|deg|turn)?$/u.exec(
12048
12329
  value
@@ -12052,7 +12333,7 @@ function numericPart(value) {
12052
12333
  const sign = match.groups.negative === "-" ? "neg-" : "";
12053
12334
  return sign + number + (match.groups.unit === "%" ? "pct" : match.groups.unit ?? "");
12054
12335
  }
12055
- function readableValue(value) {
12336
+ function simpleValueSlug(value) {
12056
12337
  const words = value.trim().replace(/,/gu, "_comma_").split(/[_\s]+/u).filter(Boolean);
12057
12338
  if (words.length === 0 || words.length > 8) return void 0;
12058
12339
  const parts = [];
@@ -12148,16 +12429,17 @@ function compiledOperation(compiled, index) {
12148
12429
  (child) => child.type === "Declaration" && child.property === "--tw-shadow"
12149
12430
  );
12150
12431
  if (shadow?.type === "Declaration") {
12151
- return { property: "box-shadow", value: generate7(shadow.value).trim() };
12432
+ return { property: "box-shadow", value: generate6(shadow.value).trim() };
12152
12433
  }
12153
12434
  }
12154
- return { property: declaration.property, value: generate7(declaration.value).trim() };
12435
+ return { property: declaration.property, value: generate6(declaration.value).trim() };
12436
+ }
12437
+ function propertyPrefix(property) {
12438
+ return property.replace(/^-+/u, "").replace(/^background-/u, "bg-");
12155
12439
  }
12156
12440
  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}`;
12441
+ const value = simpleValueSlug(operation.value);
12442
+ return value ? `${propertyPrefix(operation.property)}-${value}` : void 0;
12161
12443
  }
12162
12444
  function singleApply(utility, textOf) {
12163
12445
  const children = blockChildren2(utility);
@@ -12171,7 +12453,7 @@ var utilityNameParityRule = {
12171
12453
  schema: [],
12172
12454
  type: "problem",
12173
12455
  docs: {
12174
- description: "Use property/value-derived names for readable values and semantic names for complex values."
12456
+ description: "Use property/value-derived names for simple values and property-prefixed semantic names for complex values."
12175
12457
  }
12176
12458
  },
12177
12459
  create(context) {
@@ -12205,8 +12487,16 @@ var utilityNameParityRule = {
12205
12487
  if (/-hash-[a-f0-9]{12}$/u.test(candidate.name)) {
12206
12488
  context.report({
12207
12489
  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.`
12490
+ 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
12491
  });
12492
+ } else {
12493
+ const prefix = `${propertyPrefix(operation.property)}-`;
12494
+ if (!candidate.name.startsWith(prefix) || candidate.name.length === prefix.length) {
12495
+ context.report({
12496
+ node: candidate.utility,
12497
+ message: `Custom utility "${candidate.name}" maps a complex ${operation.property} value; use a semantic name starting with "${prefix}" to preserve the CSS property.`
12498
+ });
12499
+ }
12210
12500
  }
12211
12501
  continue;
12212
12502
  }
@@ -12227,6 +12517,7 @@ var tailwindRules = {
12227
12517
  "theme-reset": themeResetRule,
12228
12518
  "theme-mode": themeModeRule,
12229
12519
  "color-role-usage": colorRoleUsageRule,
12520
+ "color-literal-usage": colorLiteralUsageRule,
12230
12521
  "root-variables": rootVariablesRule,
12231
12522
  "apply-usage": applyUsageRule,
12232
12523
  "base-layer-pair": baseLayerPairRule,
@@ -12465,7 +12756,9 @@ var SPACING_UTILITY_PREFIXES = /* @__PURE__ */ new Set([
12465
12756
  "start",
12466
12757
  "end",
12467
12758
  "space-x",
12468
- "space-y"
12759
+ "space-y",
12760
+ "translate-x",
12761
+ "translate-y"
12469
12762
  ]);
12470
12763
  var PREFIX_BUILTINS = {
12471
12764
  bg: [
@@ -12790,6 +13083,7 @@ function readInventory(files) {
12790
13083
  const themeTokensByNamespace = /* @__PURE__ */ new Map();
12791
13084
  const plainClasses = /* @__PURE__ */ new Set();
12792
13085
  let defaultsReset = false;
13086
+ let baseSpacing = false;
12793
13087
  for (const file of files) {
12794
13088
  let css2;
12795
13089
  try {
@@ -12808,6 +13102,7 @@ function readInventory(files) {
12808
13102
  for (const plainClass of classSelectors(css2)) plainClasses.add(plainClass);
12809
13103
  for (const block of themeBlocks(css2)) {
12810
13104
  if (/--\*\s*:\s*initial\b/.test(block)) defaultsReset = true;
13105
+ if (/(?:^|[;{])\s*--spacing\s*:/.test(block)) baseSpacing = true;
12811
13106
  for (const decl of block.matchAll(/--(?<namespace>[a-z][a-z0-9]*)-(?<token>[a-z0-9][a-z0-9_-]*)\s*:/g)) {
12812
13107
  let namespace = decl.groups?.namespace;
12813
13108
  let token = decl.groups?.token;
@@ -12843,6 +13138,7 @@ function readInventory(files) {
12843
13138
  utilityPrefixes,
12844
13139
  themeTokens: themeTokensByNamespace,
12845
13140
  defaultsReset,
13141
+ baseSpacing,
12846
13142
  plainClasses
12847
13143
  };
12848
13144
  }
@@ -12871,7 +13167,13 @@ function isKnown(className, inventory) {
12871
13167
  const namespaces = PREFIX_NAMESPACES[prefix];
12872
13168
  if (!namespaces) {
12873
13169
  if (STATIC_VALUE_TOKENS[prefix]?.includes(token)) return true;
12874
- if (SPACING_UTILITY_PREFIXES.has(prefix) && inventory.themeTokens.get("spacing")?.has(token)) return true;
13170
+ if (SPACING_UTILITY_PREFIXES.has(prefix)) {
13171
+ const spacing = inventory.themeTokens.get("spacing");
13172
+ if (/^(?:\d+(?:\.\d+)?|\.\d+)$/u.test(token) && inventory.defaultsReset && !inventory.baseSpacing) {
13173
+ return false;
13174
+ }
13175
+ if (spacing?.has(token)) return true;
13176
+ }
12875
13177
  const projectTokens = inventory.themeTokens.get(prefix);
12876
13178
  if (projectTokens) {
12877
13179
  if (projectTokens.has(token)) return true;
@@ -13620,7 +13922,7 @@ var tailwindStructureRules = {
13620
13922
  languageOptions: { tolerant: true },
13621
13923
  rules: Object.fromEntries(
13622
13924
  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"
13925
+ (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
13926
  ).map((id) => [id, "error"])
13625
13927
  )
13626
13928
  };
@@ -13643,6 +13945,7 @@ var tailwindImportGraph = {
13643
13945
  "pasika/no-utility-alias": "error",
13644
13946
  "pasika/root-variables": "error",
13645
13947
  "pasika/color-role-usage": "error",
13948
+ "pasika/color-literal-usage": "error",
13646
13949
  "pasika/theme-mode": "error"
13647
13950
  }
13648
13951
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pasika",
3
- "version": "0.10.133",
3
+ "version": "0.10.135",
4
4
  "description": "Reusable agent setup package",
5
5
  "repository": {
6
6
  "type": "git",