pasika 0.10.132 → 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.
@@ -120,9 +120,11 @@ declare const nextjsPackageJsonRules: {
120
120
  };
121
121
 
122
122
  declare const tailwindRules: {
123
+ "at-rule-spacing": _eslint_css.CSSRuleDefinition;
123
124
  "theme-reset": _eslint_css.CSSRuleDefinition;
124
125
  "theme-mode": _eslint_css.CSSRuleDefinition;
125
126
  "color-role-usage": _eslint_css.CSSRuleDefinition;
127
+ "color-literal-usage": _eslint_css.CSSRuleDefinition;
126
128
  "root-variables": _eslint_css.CSSRuleDefinition;
127
129
  "apply-usage": _eslint_css.CSSRuleDefinition;
128
130
  "base-layer-pair": _eslint_css.CSSRuleDefinition;
@@ -161,9 +163,11 @@ declare const pasikaPlugin: {
161
163
  "exact-version": _eslint_json.JSONRuleDefinition;
162
164
  "lint-setup": _eslint_json.JSONRuleDefinition;
163
165
  "vitest-coverage": _eslint_json.JSONRuleDefinition;
166
+ "at-rule-spacing": _eslint_css.CSSRuleDefinition;
164
167
  "theme-reset": _eslint_css.CSSRuleDefinition;
165
168
  "theme-mode": _eslint_css.CSSRuleDefinition;
166
169
  "color-role-usage": _eslint_css.CSSRuleDefinition;
170
+ "color-literal-usage": _eslint_css.CSSRuleDefinition;
167
171
  "root-variables": _eslint_css.CSSRuleDefinition;
168
172
  "apply-usage": _eslint_css.CSSRuleDefinition;
169
173
  "base-layer-pair": _eslint_css.CSSRuleDefinition;
@@ -9720,6 +9720,46 @@ var applyUsageRule = {
9720
9720
  }
9721
9721
  };
9722
9722
 
9723
+ // eslint/rules/tailwind/at-rule-spacing.ts
9724
+ var EMPTY_LINE = /\n[ \t]*\n/u;
9725
+ var LINE_BREAK = /\r\n|\n|\r/u;
9726
+ var atRuleSpacingRule = {
9727
+ meta: {
9728
+ schema: [],
9729
+ type: "layout",
9730
+ fixable: "whitespace",
9731
+ docs: {
9732
+ description: "Require a blank line between adjacent top-level CSS at-rules, excluding grouped imports."
9733
+ }
9734
+ },
9735
+ create(context) {
9736
+ return {
9737
+ "StyleSheet:exit"(node) {
9738
+ const source = context.sourceCode.text;
9739
+ const newline = source.includes("\r\n") ? "\r\n" : "\n";
9740
+ for (let index = 1; index < node.children.length; index += 1) {
9741
+ const previous = node.children[index - 1];
9742
+ const current = node.children[index];
9743
+ if (previous?.type !== "Atrule" || current?.type !== "Atrule") continue;
9744
+ if (previous.name === "import" && current.name === "import") continue;
9745
+ if (!previous.loc || !current.loc) continue;
9746
+ const start = previous.loc.end.offset;
9747
+ const end = current.loc.start.offset;
9748
+ const between = source.slice(start, end);
9749
+ if (EMPTY_LINE.test(between.replace(/\r\n|\r/gu, "\n"))) continue;
9750
+ context.report({
9751
+ node: current,
9752
+ message: "Adjacent top-level @ rules must have a blank line between them.",
9753
+ fix(fixer) {
9754
+ return fixer.insertTextBeforeRange([end, end], LINE_BREAK.test(between) ? newline : newline + newline);
9755
+ }
9756
+ });
9757
+ }
9758
+ }
9759
+ };
9760
+ }
9761
+ };
9762
+
9723
9763
  // eslint/rules/tailwind/base-layer-pair.ts
9724
9764
  import { existsSync as existsSync8 } from "fs";
9725
9765
  import path66 from "path";
@@ -9869,6 +9909,96 @@ var baseLayerPairRule = {
9869
9909
  }
9870
9910
  };
9871
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
+
9872
10002
  // eslint/rules/color-role.ts
9873
10003
  function colorRoleUsageMessage(className) {
9874
10004
  const segments = className.replace(/!+$/u, "").split(":");
@@ -9893,14 +10023,14 @@ function colorRoleUsageMessage(className) {
9893
10023
  }
9894
10024
 
9895
10025
  // eslint/rules/tailwind/color-role-usage.ts
9896
- 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;
9897
10027
  var BACKGROUND_IMAGE_TOKEN = /^--background-image-[a-z][a-z0-9-]*-canvas$/u;
9898
10028
  var colorRoleUsageRule = {
9899
10029
  meta: {
9900
10030
  schema: [],
9901
10031
  type: "problem",
9902
10032
  docs: {
9903
- 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."
9904
10034
  }
9905
10035
  },
9906
10036
  create(context) {
@@ -9923,7 +10053,7 @@ var colorRoleUsageRule = {
9923
10053
  } else if (!COLOR_TOKEN.test(token)) {
9924
10054
  context.report({
9925
10055
  node: theme,
9926
- 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.`
9927
10057
  });
9928
10058
  }
9929
10059
  }
@@ -10109,14 +10239,24 @@ function themeClasses(name) {
10109
10239
  if (name.includes("--", 2)) return [];
10110
10240
  if (name.startsWith("--color-")) {
10111
10241
  const stem = name.slice("--color-".length);
10112
- if (stem.endsWith("-canvas")) {
10113
- return ["bg", "fill", "from", "via", "to", "ring-offset"].map((prefix) => `${prefix}-${stem}`);
10114
- }
10115
- if (stem.endsWith("-ink")) return ["text", "caret", "decoration"].map((prefix) => `${prefix}-${stem}`);
10116
- if (stem.endsWith("-edge")) {
10117
- return ["border", "outline", "ring", "divide", "stroke"].map((prefix) => `${prefix}-${stem}`);
10118
- }
10119
- 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}`);
10120
10260
  }
10121
10261
  if (name.startsWith("--background-image-")) return [`bg-${name.slice("--background-image-".length)}`];
10122
10262
  if (name.startsWith("--spacing-")) {
@@ -10226,7 +10366,7 @@ function buildCssDependencyGraph(options) {
10226
10366
  } else if (node.name === "theme" && node.loc) {
10227
10367
  const source = file.content.slice(node.loc.start.offset, node.loc.end.offset).replace(/\/\*[\s\S]*?\*\//gu, (comment) => comment.replace(/[^\r\n]/gu, " "));
10228
10368
  for (const found of source.matchAll(
10229
- /(?:^|(?<=[{;\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
10230
10370
  )) {
10231
10371
  const name = found.groups?.name;
10232
10372
  const value = found.groups?.value;
@@ -10651,27 +10791,28 @@ var noUtilityAliasRule = {
10651
10791
 
10652
10792
  // eslint/rules/tailwind/root-variables.ts
10653
10793
  import path72 from "path";
10654
- import { generate as generate3, parse as parse6, walk as walk3 } from "@eslint/css-tree";
10655
- var INLINE_MESSAGE = "Define design tokens with direct values in @theme, not @theme inline.";
10656
- var INDIRECT_MESSAGE = "Define design tokens that reference CSS variables via var() in @theme inline.";
10657
- var MIXED_THEME_MESSAGE = "Use either @theme or @theme inline across the project; external font-loader mappings with valid fallbacks are the only exception.";
10658
- 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.`;
10659
- 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.`;
10660
- function hasVarFallback(source, variable) {
10661
- for (const match of source.matchAll(/\bvar\s*\(\s*(?<name>--[\w-]+)\s*,/gu)) {
10662
- if (match.groups?.name !== variable) continue;
10663
- const start = match.index + match[0].length;
10664
- const close = source.indexOf(")", start);
10665
- if (close !== -1 && /[^,\s]/u.test(source.slice(start, close))) return true;
10666
- }
10667
- 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
+ );
10668
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.`;
10669
10810
  var rootVariablesRule = {
10670
10811
  meta: {
10671
10812
  schema: [],
10672
10813
  type: "problem",
10673
10814
  docs: {
10674
- 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."
10675
10816
  }
10676
10817
  },
10677
10818
  create(context) {
@@ -10694,7 +10835,11 @@ var rootVariablesRule = {
10694
10835
  const names = new Set(
10695
10836
  graph.definitions.filter((symbol) => symbol.kind === "theme-token").map((symbol) => symbol.name)
10696
10837
  );
10697
- 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
+ }
10698
10843
  const references = [...value.matchAll(/\bvar\s*\(\s*(?<target>--[a-z][\w-]*)/giu)].map(
10699
10844
  (match) => match.groups?.target
10700
10845
  );
@@ -10703,41 +10848,15 @@ var rootVariablesRule = {
10703
10848
  if (!target || visited.has(target)) return false;
10704
10849
  const next = themeValues.get(target);
10705
10850
  if (!next) return false;
10706
- return isFixedThemeExpression(target, next, /* @__PURE__ */ new Set([...visited, target]));
10851
+ return isComposableThemeExpression(target, next, allowRuntimeReferences, /* @__PURE__ */ new Set([...visited, target]));
10707
10852
  });
10708
10853
  }
10709
10854
  const mirroredTokens = /* @__PURE__ */ new Set();
10710
- let hasPlainThemeBlocks = false;
10711
- let hasInlineThemeBlocks = false;
10712
- let inlineBlocksAreExternalFonts = true;
10713
10855
  for (const file of [...stylesheets.filter((candidate) => path72.normalize(candidate) !== current), current]) {
10714
10856
  const source = (path72.normalize(file) === current ? context.sourceCode.text : textOf(file)).replace(
10715
10857
  /\/\*[\s\S]*?\*\//gu,
10716
10858
  " "
10717
10859
  );
10718
- for (const header of source.matchAll(/@theme(?<options>(?:\s+[\w-]+)*)\s*\{/gu)) {
10719
- if (/\binline\b/u.test(header.groups?.options ?? "")) hasInlineThemeBlocks = true;
10720
- else hasPlainThemeBlocks = true;
10721
- }
10722
- const inlineHeaders = [...source.matchAll(/@theme\s+inline\s*\{/gu)];
10723
- const inlineBlocks = [...source.matchAll(/@theme\s+inline\s*\{(?<body>[^{}]*)\}/gu)];
10724
- if (inlineHeaders.length !== inlineBlocks.length) inlineBlocksAreExternalFonts = false;
10725
- for (const block of inlineBlocks) {
10726
- const declarations = (block.groups?.body ?? "").split(";").map((part) => part.trim()).filter(Boolean);
10727
- if (declarations.length === 0) inlineBlocksAreExternalFonts = false;
10728
- for (const declaration of declarations) {
10729
- const match = /^(?<token>--font-[a-z][\w-]*)\s*:\s*(?<value>.+)$/u.exec(declaration);
10730
- const name = match?.groups?.token;
10731
- const value = match?.groups?.value ?? "";
10732
- const references = [...value.matchAll(/\bvar\s*\(\s*(?<name>--[\w-]+)/gu)].map(
10733
- (reference) => reference.groups?.name
10734
- );
10735
- const runtime = references[0];
10736
- if (!name || references.length !== 1 || !runtime?.startsWith("--font-") || runtime === name || names.has(runtime) || !hasVarFallback(value, runtime)) {
10737
- inlineBlocksAreExternalFonts = false;
10738
- }
10739
- }
10740
- }
10741
10860
  for (const theme of source.matchAll(/@theme(?<options>(?:\s+[\w-]+)*)\s*\{(?<body>[^{}]*)\}/gu)) {
10742
10861
  const inline = /\binline\b/u.test(theme.groups?.options ?? "");
10743
10862
  for (const declaration of (theme.groups?.body ?? "").matchAll(
@@ -10746,42 +10865,41 @@ var rootVariablesRule = {
10746
10865
  const { name, value } = declaration.groups ?? {};
10747
10866
  if (!name) continue;
10748
10867
  names.add(name);
10749
- if (inline && /\bvar\s*\(/u.test(value ?? "")) {
10868
+ if (inline && new RegExp(`^\\s*var\\(\\s*${name}\\s*\\)\\s*$`, "u").test(value ?? "")) {
10750
10869
  mirroredTokens.add(name);
10751
10870
  }
10752
10871
  }
10753
10872
  }
10754
10873
  }
10755
- const overriddenNames = new Set(
10756
- graph.definitions.filter((symbol) => symbol.kind === "root-variable" && symbol.scope === "conditional").map((symbol) => symbol.name)
10757
- );
10874
+ const authoredVariables = new Set(names);
10875
+ const variantNames = /* @__PURE__ */ new Set();
10758
10876
  for (const file of [...stylesheets.filter((candidate) => path72.normalize(candidate) !== current), current]) {
10759
10877
  const css2 = path72.normalize(file) === current ? context.sourceCode.text : textOf(file);
10760
10878
  const tree = parse6(css2, { parseAtrulePrelude: false });
10879
+ let variantDepth = 0;
10761
10880
  walk3(tree, {
10762
10881
  enter(node) {
10882
+ if (node.type === "Atrule" && node.name === "variant") variantDepth += 1;
10763
10883
  if (node.type !== "Declaration" || !node.property.startsWith("--")) return;
10764
- if (this.rule?.type !== "Rule") return;
10765
- 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;
10766
10889
  }
10767
10890
  });
10768
10891
  }
10769
- for (const file of findFiles(sourceRoot, MODULE_EXTENSIONS4)) {
10770
- for (const found of textOf(file).matchAll(/\.setProperty\s*\(\s*["'\x60](?<name>--[\w-]+)["'\x60]/gu)) {
10771
- if (found.groups?.name) overriddenNames.add(found.groups.name);
10772
- }
10773
- }
10774
10892
  const defaultRootNames = new Set(
10775
10893
  graph.definitions.filter((symbol) => symbol.kind === "root-variable" && symbol.scope === "default").map((symbol) => symbol.name)
10776
10894
  );
10777
10895
  return {
10778
10896
  "StyleSheet:exit"(node) {
10779
10897
  const localThemes = atrulesNamed(node, "theme");
10780
- if (hasPlainThemeBlocks && hasInlineThemeBlocks && !inlineBlocksAreExternalFonts && localThemes[0]) {
10781
- context.report({ node: localThemes[0], message: MIXED_THEME_MESSAGE });
10782
- }
10783
10898
  for (const theme of localThemes) {
10784
- const inline = preludeIdentifiers(theme).includes("inline");
10899
+ if (!preludeIdentifiers(theme).includes("inline")) {
10900
+ context.report({ node: theme, message: INLINE_ONLY_MESSAGE });
10901
+ continue;
10902
+ }
10785
10903
  const source = context.sourceCode.getText(theme).replace(/\/\*[\s\S]*?\*\//gu, (comment) => comment.replace(/[^\r\n]/gu, " "));
10786
10904
  for (const match of source.matchAll(
10787
10905
  /(?:^|[{;\r\n])\s*(?<name>--[a-z][\w-]*)\s*:\s*(?<value>[^;{}]*)(?=;|[}])/gu
@@ -10790,60 +10908,48 @@ var rootVariablesRule = {
10790
10908
  if (!name || value === void 0) continue;
10791
10909
  const unquoted = value.replace(/"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'/gu, "");
10792
10910
  const referencesVariable = /\bvar\s*\(/iu.test(unquoted);
10793
- if (inline && referencesVariable) {
10794
- for (const reference of unquoted.matchAll(/\bvar\(\s*(?<variable>--[\w-]+)/gu)) {
10795
- const runtime = reference.groups?.variable;
10796
- if (runtime && runtime !== name) {
10797
- const externalFont = name.startsWith("--font-") && runtime.startsWith("--font-");
10798
- if (externalFont && !hasVarFallback(value, runtime)) {
10799
- context.report({ node: theme, message: EXTERNAL_FONT_FALLBACK_MESSAGE(name, runtime) });
10800
- } else if (!externalFont) {
10801
- context.report({
10802
- node: theme,
10803
- message: `Tailwind token "${name}" must reference its same-named CSS variable, not "${runtime}".`
10804
- });
10805
- }
10806
- } else if (runtime && !defaultRootNames.has(runtime)) {
10807
- context.report({
10808
- node: theme,
10809
- message: `Tailwind token "${name}" references "${runtime}" without a default :root declaration.`
10810
- });
10811
- }
10812
- }
10813
- }
10814
- if (inline && referencesVariable && defaultRootNames.has(name) && !overriddenNames.has(name)) {
10815
- const references = [...unquoted.matchAll(/\bvar\(\s*(?<variable>--[\w-]+)/gu)].map(
10816
- (reference) => reference.groups?.variable
10817
- );
10818
- 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)) {
10819
10924
  context.report({ node: theme, message: REDUNDANT_INLINE_MESSAGE(name) });
10820
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) });
10821
10937
  }
10822
- if (inline === referencesVariable || !inline && isFixedThemeExpression(name, unquoted)) continue;
10823
- const offset = (theme.loc?.start.offset ?? 0) + match.index + match[0].indexOf(name);
10824
- const preceding = context.sourceCode.text.slice(0, offset);
10825
- const line = preceding.split("\n").length;
10826
- const column = offset - preceding.lastIndexOf("\n");
10827
- context.report({
10828
- node: theme,
10829
- loc: {
10830
- start: { line, column },
10831
- end: { line, column: column + name.length }
10832
- },
10833
- message: inline ? INLINE_MESSAGE : INDIRECT_MESSAGE
10834
- });
10835
10938
  }
10836
10939
  }
10837
- function checkTokenRedeclarations(currentNode, conditional = false) {
10940
+ function checkTokenRedeclarations(currentNode, conditional = false, inVariant = false) {
10838
10941
  if (currentNode.type === "Atrule" && ["theme", "utility", "keyframes"].includes(currentNode.name)) {
10839
10942
  return;
10840
10943
  }
10841
10944
  const isConditional = conditional || currentNode.type === "Atrule" && currentNode.name !== "layer";
10945
+ const isVariant = inVariant || currentNode.type === "Atrule" && currentNode.name === "variant";
10842
10946
  const isDefaultRoot = currentNode.type === "Rule" && context.sourceCode.getText(currentNode.prelude).trim() === ":root" && !isConditional;
10843
10947
  if (currentNode.type === "Rule" && !isDefaultRoot || currentNode.type === "Atrule" && currentNode.name === "variant") {
10844
10948
  for (const declaration of blockChildren2(currentNode)) {
10845
10949
  if (declaration.type !== "Declaration") continue;
10846
- if (mirroredTokens.has(declaration.property)) continue;
10950
+ if (mirroredTokens.has(declaration.property) && (isVariant || !variantNames.has(declaration.property))) {
10951
+ continue;
10952
+ }
10847
10953
  if (!names.has(declaration.property) && !declaration.property.startsWith("--color-")) continue;
10848
10954
  context.report({
10849
10955
  node: declaration,
@@ -10853,7 +10959,9 @@ var rootVariablesRule = {
10853
10959
  }
10854
10960
  const children = currentNode.type === "StyleSheet" ? currentNode.children : blockChildren2(currentNode);
10855
10961
  for (const child of children) {
10856
- if (child.type === "Atrule" || child.type === "Rule") checkTokenRedeclarations(child, isConditional);
10962
+ if (child.type === "Atrule" || child.type === "Rule") {
10963
+ checkTokenRedeclarations(child, isConditional, isVariant);
10964
+ }
10857
10965
  }
10858
10966
  }
10859
10967
  checkTokenRedeclarations(node);
@@ -10876,9 +10984,21 @@ var rootVariablesRule = {
10876
10984
 
10877
10985
  // eslint/rules/tailwind/stylesheet-ordering.ts
10878
10986
  import path73 from "path";
10879
- import { generate as generate4 } from "@eslint/css-tree";
10880
- 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
+ ];
10881
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.";
10882
11002
  function themeBody(node, source) {
10883
11003
  if (node.type !== "Atrule" || node.name !== "theme" || !node.loc) return void 0;
10884
11004
  const raw = source.slice(node.loc.start.offset, node.loc.end.offset);
@@ -10890,12 +11010,9 @@ function themeBody(node, source) {
10890
11010
  function containsThemeReset(body) {
10891
11011
  return body !== void 0 && /(?:^|[;{])\s*--\*\s*:\s*initial\s*(?:;|$)/u.test(body);
10892
11012
  }
10893
- function isStandaloneThemeReset(body) {
10894
- return body !== void 0 && /^--\*\s*:\s*initial\s*;?$/u.test(body);
10895
- }
10896
11013
  function isTailwindImport(node) {
10897
11014
  if (node.type !== "Atrule" || node.name !== "import" || !node.prelude) return false;
10898
- return /^["']tailwindcss["'](?:\s|$)/u.test(generate4(node.prelude).trim());
11015
+ return /^["']tailwindcss["'](?:\s|$)/u.test(generate3(node.prelude).trim());
10899
11016
  }
10900
11017
  function scopesCustomProperties(node) {
10901
11018
  if (node.type === "Declaration") return node.property.startsWith("--");
@@ -10917,80 +11034,68 @@ var stylesheetOrderingRule = {
10917
11034
  if (path73.normalize(path73.resolve(context.filename)) !== entryPoint) return {};
10918
11035
  return {
10919
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
+ }
10920
11048
  const sections = [];
10921
- let index = 0;
10922
- let sawTailwindImport = false;
10923
11049
  let sawFeatureImport = false;
10924
- let sawNonImport = false;
10925
- let sawEarlyReset = false;
11050
+ let themeBlocks2 = 0;
10926
11051
  for (const child of node.children) {
10927
11052
  const body = themeBody(child, context.sourceCode.text);
10928
- 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
+ }
10929
11057
  if (containsThemeReset(body) && sawFeatureImport) {
10930
11058
  context.report({ node: child, message: LATE_RESET_ERROR });
10931
11059
  return;
10932
11060
  }
10933
11061
  if (child.type === "Atrule" && child.name === "import") {
10934
- sections.push({ name: "imports", at: index });
10935
- if (isTailwindImport(child)) sawTailwindImport = true;
10936
- else sawFeatureImport = true;
10937
- } else if (isStandaloneThemeReset(body) && sawTailwindImport && !sawFeatureImport && !sawNonImport && !sawEarlyReset) {
10938
- sawEarlyReset = true;
10939
- isEarlyReset = true;
10940
- } else if (child.type === "Atrule" && child.name === "custom-variant") {
10941
- 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
+ }
10942
11068
  } else if (child.type === "Atrule" && child.name === "theme") {
10943
- 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");
10944
11073
  } else if (child.type === "Atrule" && child.name === "utility") {
10945
- sections.push({ name: "utility", at: index });
11074
+ sections.push("utility");
10946
11075
  } else if (child.type === "Atrule" && child.name === "keyframes") {
10947
- sections.push({ name: "keyframes", at: index });
11076
+ sections.push("keyframes");
10948
11077
  } else if (child.type === "Atrule" && child.name === "layer") {
10949
11078
  if (preludeIdentifiers(child).includes("base")) {
10950
- sections.push({ name: "base styles", at: index });
11079
+ sections.push("base styles");
10951
11080
  }
10952
11081
  } else if (child.type === "Rule" && (selectorNames(child).includes("root") || scopesCustomProperties(child))) {
10953
- sections.push({ name: "root", at: index });
11082
+ sections.push("root");
10954
11083
  } else if (child.type === "Atrule" && scopesCustomProperties(child)) {
10955
- sections.push({ name: "root", at: index });
11084
+ sections.push("root");
10956
11085
  }
10957
- if (child.type !== "Comment" && !(child.type === "Atrule" && child.name === "import") && !isEarlyReset) {
10958
- sawNonImport = true;
10959
- }
10960
- index += 1;
10961
- }
10962
- const firstAt = /* @__PURE__ */ new Map();
10963
- for (const section of sections) {
10964
- if (!firstAt.has(section.name)) firstAt.set(section.name, section.at ?? 0);
10965
11086
  }
10966
- let lastSeen = -1;
10967
- for (const name of SECTION_ORDER) {
10968
- const at = firstAt.get(name);
10969
- if (at === void 0) continue;
10970
- if (at < lastSeen) {
10971
- context.report({
10972
- node,
10973
- message: `Global stylesheet sections must be ordered: imports, @custom-variant, :root, @theme, custom utilities, base styles, keyframes. Found "${name}" before an earlier section.`
10974
- });
10975
- return;
10976
- }
10977
- lastSeen = at;
11087
+ if (themeBlocks2 > 1) {
11088
+ context.report({ node, message: DUPLICATE_THEME_ERROR });
11089
+ return;
10978
11090
  }
10979
11091
  let furthestSection = -1;
10980
- let furthestName;
10981
11092
  for (const section of sections) {
10982
- const order = SECTION_ORDER.findIndex((name) => name === section.name);
11093
+ const order = SECTION_ORDER.findIndex((name) => name === section);
10983
11094
  if (order < furthestSection) {
10984
- context.report({
10985
- node,
10986
- message: `Global stylesheet section "${section.name}" cannot reappear after "${String(furthestName)}". Keep all section occurrences in the required order.`
10987
- });
11095
+ context.report({ node, message: ORDER_ERROR });
10988
11096
  return;
10989
11097
  }
10990
- if (order > furthestSection) {
10991
- furthestSection = order;
10992
- furthestName = section.name;
10993
- }
11098
+ furthestSection = order;
10994
11099
  }
10995
11100
  }
10996
11101
  };
@@ -10999,7 +11104,7 @@ var stylesheetOrderingRule = {
10999
11104
 
11000
11105
  // eslint/rules/tailwind/theme-mode.ts
11001
11106
  import path74 from "path";
11002
- import { generate as generate5 } from "@eslint/css-tree";
11107
+ import { generate as generate4 } from "@eslint/css-tree";
11003
11108
  var MODE_NAME = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u;
11004
11109
  var MODE_REFERENCE = /(?:^|[^a-z0-9-])(?<name>[a-z][a-z0-9]*(?:-[a-z0-9]+)*):(?=[a-z[(])/gu;
11005
11110
  var MODE_ATTRIBUTE = /data-theme\s*=\s*["'](?<name>[a-z][a-z0-9]*(?:-[a-z0-9]+)*)["']/gu;
@@ -11046,7 +11151,7 @@ var themeModeRule = {
11046
11151
  const registered = /* @__PURE__ */ new Set();
11047
11152
  for (const variant of atrulesNamed(node, "custom-variant")) {
11048
11153
  if (variant.type !== "Atrule") continue;
11049
- const prelude = variant.prelude ? generate5(variant.prelude) : "";
11154
+ const prelude = variant.prelude ? generate4(variant.prelude) : "";
11050
11155
  const name = variantName(prelude);
11051
11156
  if (!name) continue;
11052
11157
  if (!isModeVariant(name, prelude)) continue;
@@ -11075,14 +11180,14 @@ var themeModeRule = {
11075
11180
  const globalRegistrations = current === globalFile ? registered : registeredModes(read(globalFile));
11076
11181
  for (const variant of atrulesNamed(node, "variant")) {
11077
11182
  if (variant.type !== "Atrule" || !variant.prelude) continue;
11078
- const name = variantName(generate5(variant.prelude));
11183
+ const name = variantName(generate4(variant.prelude));
11079
11184
  if (name && (name === "dark" || name.startsWith("theme-") || globalRegistrations.has(name) || modeHints.has(name)) && !globalRegistrations.has(name)) {
11080
11185
  context.report({ node: variant, message: MISSING_CSS_VARIANT(name) });
11081
11186
  }
11082
11187
  }
11083
11188
  for (const rule of rules(node)) {
11084
11189
  if (rule.type !== "Rule") continue;
11085
- const selector = generate5(rule.prelude);
11190
+ const selector = generate4(rule.prelude);
11086
11191
  if (/\[\s*data-theme(?:\s*[~|^$*]?=|\s*\])/u.test(selector)) {
11087
11192
  context.report({ node: rule, message: SELECTOR_OVERRIDE });
11088
11193
  } else if (/\.dark(?![\w-])/u.test(selector)) {
@@ -11091,7 +11196,7 @@ var themeModeRule = {
11091
11196
  }
11092
11197
  walkNodes(node, (child) => {
11093
11198
  if (child.type !== "Atrule" || child.name !== "media" || !child.prelude) return;
11094
- if (/\bprefers-color-scheme\b/u.test(generate5(child.prelude))) {
11199
+ if (/\bprefers-color-scheme\b/u.test(generate4(child.prelude))) {
11095
11200
  context.report({ node: child, message: SYSTEM_OVERRIDE });
11096
11201
  }
11097
11202
  });
@@ -11121,15 +11226,16 @@ var themeModeRule = {
11121
11226
 
11122
11227
  // eslint/rules/tailwind/theme-reset.ts
11123
11228
  import path75 from "path";
11124
- import { generate as generate6 } from "@eslint/css-tree";
11229
+ import { generate as generate5 } from "@eslint/css-tree";
11125
11230
  function isThemeReset(node) {
11126
11231
  if (!node) return false;
11127
- 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";
11128
11233
  if (node.type === "Raw") return /(?:^|[;{])\s*--\*\s*:\s*initial\s*(?:;|$)/u.test(node.value);
11129
11234
  return false;
11130
11235
  }
11131
- function hasThemeReset(node) {
11236
+ function hasThemeReset(node, inlineOnly = false) {
11132
11237
  return atrulesNamed(node, "theme").some((theme) => {
11238
+ if (inlineOnly && !preludeIdentifiers(theme).includes("inline")) return false;
11133
11239
  if (blockChildren2(theme).some(isThemeReset)) return true;
11134
11240
  let nested = false;
11135
11241
  walkNodes(theme, (child) => {
@@ -11163,10 +11269,10 @@ var themeResetRule = {
11163
11269
  }
11164
11270
  return;
11165
11271
  }
11166
- if (!hasReset) {
11272
+ if (!hasThemeReset(node, true)) {
11167
11273
  context.report({
11168
11274
  node,
11169
- 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."
11170
11276
  });
11171
11277
  }
11172
11278
  }
@@ -11255,13 +11361,226 @@ var uniqueCssDefinitionRule = {
11255
11361
 
11256
11362
  // eslint/rules/tailwind/unique-css-token-value.ts
11257
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
+ }
11258
11581
  function valueIdentity(value) {
11259
11582
  const text = value.trim();
11260
- const hex = /^#(?<digits>[\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$/iu.exec(text)?.groups?.digits;
11261
- if (!hex) return text;
11262
- const lower = hex.toLowerCase();
11263
- const full = lower.length <= 4 ? Array.from(lower, (digit) => digit + digit).join("") : lower;
11264
- return `#${full.length === 8 && full.endsWith("ff") ? full.slice(0, -2) : full}`;
11583
+ return normalizedColor(text) ?? text;
11265
11584
  }
11266
11585
  function isComparable(symbol) {
11267
11586
  if (symbol.value === void 0 || symbol.value.trim() === "") return false;
@@ -11305,10 +11624,11 @@ var uniqueCssTokenValueRule = {
11305
11624
  const redundant = /* @__PURE__ */ new Map();
11306
11625
  for (const group of byValue.values()) {
11307
11626
  group.sort(compareLocations);
11308
- const first = group[0];
11309
- if (!first) continue;
11310
- for (const definition of group.slice(1)) {
11311
- 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);
11312
11632
  }
11313
11633
  }
11314
11634
  return {
@@ -12002,7 +12322,7 @@ var utilityMappingRule = {
12002
12322
  import { spawnSync as spawnSync2 } from "child_process";
12003
12323
  import { existsSync as existsSync13 } from "fs";
12004
12324
  import path80 from "path";
12005
- 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";
12006
12326
  function numericPart(value) {
12007
12327
  const match = /^(?<negative>-?)(?<number>(?:\d+(?:\.\d+)?|\.\d+))(?<unit>%|px|rem|em|vw|vh|dvw|dvh|ch|fr|s|ms|deg|turn)?$/u.exec(
12008
12328
  value
@@ -12012,7 +12332,7 @@ function numericPart(value) {
12012
12332
  const sign = match.groups.negative === "-" ? "neg-" : "";
12013
12333
  return sign + number + (match.groups.unit === "%" ? "pct" : match.groups.unit ?? "");
12014
12334
  }
12015
- function readableValue(value) {
12335
+ function simpleValueSlug(value) {
12016
12336
  const words = value.trim().replace(/,/gu, "_comma_").split(/[_\s]+/u).filter(Boolean);
12017
12337
  if (words.length === 0 || words.length > 8) return void 0;
12018
12338
  const parts = [];
@@ -12108,16 +12428,17 @@ function compiledOperation(compiled, index) {
12108
12428
  (child) => child.type === "Declaration" && child.property === "--tw-shadow"
12109
12429
  );
12110
12430
  if (shadow?.type === "Declaration") {
12111
- return { property: "box-shadow", value: generate7(shadow.value).trim() };
12431
+ return { property: "box-shadow", value: generate6(shadow.value).trim() };
12112
12432
  }
12113
12433
  }
12114
- 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-");
12115
12438
  }
12116
12439
  function expectedName(operation) {
12117
- const value = readableValue(operation.value);
12118
- if (!value) return void 0;
12119
- const property = operation.property.replace(/^-+/u, "").replace(/^background-/u, "bg-");
12120
- return `${property}-${value}`;
12440
+ const value = simpleValueSlug(operation.value);
12441
+ return value ? `${propertyPrefix(operation.property)}-${value}` : void 0;
12121
12442
  }
12122
12443
  function singleApply(utility, textOf) {
12123
12444
  const children = blockChildren2(utility);
@@ -12131,7 +12452,7 @@ var utilityNameParityRule = {
12131
12452
  schema: [],
12132
12453
  type: "problem",
12133
12454
  docs: {
12134
- 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."
12135
12456
  }
12136
12457
  },
12137
12458
  create(context) {
@@ -12165,8 +12486,16 @@ var utilityNameParityRule = {
12165
12486
  if (/-hash-[a-f0-9]{12}$/u.test(candidate.name)) {
12166
12487
  context.report({
12167
12488
  node: candidate.utility,
12168
- 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.`
12169
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
+ }
12170
12499
  }
12171
12500
  continue;
12172
12501
  }
@@ -12183,9 +12512,11 @@ var utilityNameParityRule = {
12183
12512
 
12184
12513
  // eslint/rules/tailwind/index.ts
12185
12514
  var tailwindRules = {
12515
+ "at-rule-spacing": atRuleSpacingRule,
12186
12516
  "theme-reset": themeResetRule,
12187
12517
  "theme-mode": themeModeRule,
12188
12518
  "color-role-usage": colorRoleUsageRule,
12519
+ "color-literal-usage": colorLiteralUsageRule,
12189
12520
  "root-variables": rootVariablesRule,
12190
12521
  "apply-usage": applyUsageRule,
12191
12522
  "base-layer-pair": baseLayerPairRule,
@@ -12424,7 +12755,9 @@ var SPACING_UTILITY_PREFIXES = /* @__PURE__ */ new Set([
12424
12755
  "start",
12425
12756
  "end",
12426
12757
  "space-x",
12427
- "space-y"
12758
+ "space-y",
12759
+ "translate-x",
12760
+ "translate-y"
12428
12761
  ]);
12429
12762
  var PREFIX_BUILTINS = {
12430
12763
  bg: [
@@ -12749,6 +13082,7 @@ function readInventory(files) {
12749
13082
  const themeTokensByNamespace = /* @__PURE__ */ new Map();
12750
13083
  const plainClasses = /* @__PURE__ */ new Set();
12751
13084
  let defaultsReset = false;
13085
+ let baseSpacing = false;
12752
13086
  for (const file of files) {
12753
13087
  let css2;
12754
13088
  try {
@@ -12767,6 +13101,7 @@ function readInventory(files) {
12767
13101
  for (const plainClass of classSelectors(css2)) plainClasses.add(plainClass);
12768
13102
  for (const block of themeBlocks(css2)) {
12769
13103
  if (/--\*\s*:\s*initial\b/.test(block)) defaultsReset = true;
13104
+ if (/(?:^|[;{])\s*--spacing\s*:/.test(block)) baseSpacing = true;
12770
13105
  for (const decl of block.matchAll(/--(?<namespace>[a-z][a-z0-9]*)-(?<token>[a-z0-9][a-z0-9_-]*)\s*:/g)) {
12771
13106
  let namespace = decl.groups?.namespace;
12772
13107
  let token = decl.groups?.token;
@@ -12802,6 +13137,7 @@ function readInventory(files) {
12802
13137
  utilityPrefixes,
12803
13138
  themeTokens: themeTokensByNamespace,
12804
13139
  defaultsReset,
13140
+ baseSpacing,
12805
13141
  plainClasses
12806
13142
  };
12807
13143
  }
@@ -12830,7 +13166,13 @@ function isKnown(className, inventory) {
12830
13166
  const namespaces = PREFIX_NAMESPACES[prefix];
12831
13167
  if (!namespaces) {
12832
13168
  if (STATIC_VALUE_TOKENS[prefix]?.includes(token)) return true;
12833
- 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
+ }
12834
13176
  const projectTokens = inventory.themeTokens.get(prefix);
12835
13177
  if (projectTokens) {
12836
13178
  if (projectTokens.has(token)) return true;
@@ -13579,7 +13921,7 @@ var tailwindStructureRules = {
13579
13921
  languageOptions: { tolerant: true },
13580
13922
  rules: Object.fromEntries(
13581
13923
  tailwindRuleIds.filter(
13582
- (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"
13583
13925
  ).map((id) => [id, "error"])
13584
13926
  )
13585
13927
  };
@@ -13592,6 +13934,7 @@ var tailwindImportGraph = {
13592
13934
  language: "css/css",
13593
13935
  languageOptions: { tolerant: true },
13594
13936
  rules: {
13937
+ "pasika/at-rule-spacing": "error",
13595
13938
  "pasika/css-entry-point": "error",
13596
13939
  "pasika/feature-css-placement": "error",
13597
13940
  "pasika/unique-css-definition": "error",
@@ -13601,6 +13944,7 @@ var tailwindImportGraph = {
13601
13944
  "pasika/no-utility-alias": "error",
13602
13945
  "pasika/root-variables": "error",
13603
13946
  "pasika/color-role-usage": "error",
13947
+ "pasika/color-literal-usage": "error",
13604
13948
  "pasika/theme-mode": "error"
13605
13949
  }
13606
13950
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pasika",
3
- "version": "0.10.132",
3
+ "version": "0.10.134",
4
4
  "description": "Reusable agent setup package",
5
5
  "repository": {
6
6
  "type": "git",