@barocss/kit 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -181,6 +181,8 @@ export declare type AstNode = {
181
181
 
182
182
  export declare function comment(text: string, source?: string): AstNode;
183
183
 
184
+ export declare function compareKeys(a: RuleKey, b: RuleKey): number;
185
+
184
186
  export declare interface Config {
185
187
  prefix?: string;
186
188
  cssVarPrefix?: string;
@@ -268,20 +270,6 @@ export declare type AstNode = {
268
270
 
269
271
  export declare function functionalModifier(match: ModifierRegistration['match'], modifySelector: ModifierRegistration['modifySelector'], wrap?: ModifierRegistration['wrap'], options?: Partial<ModifierRegistration>, ctx?: Context): void;
270
272
 
271
- /**
272
- * functionalUtility: A helper that registers dynamic utilities (theme, arbitrary, custom, negative, fraction, etc.) directly
273
- *
274
- * Example:
275
- * functionalUtility({
276
- * name: 'z',
277
- * supportsNegative: true,
278
- * themeKeys: ['--z-index'],
279
- * handleBareValue: ({ value }) => isPositiveInteger(value) ? value : null,
280
- * handle: (value) => [decl('z-index', value)],
281
- * description: 'z-index utility',
282
- * category: 'layout',
283
- * });
284
- */
285
273
  export declare function functionalUtility(opts: FunctionalUtilityOptions, ctx?: Context): void;
286
274
 
287
275
  export declare type FunctionalUtilityExtra = {
@@ -420,6 +408,12 @@ export declare type AstNode = {
420
408
  * handleNegativeBareValue: ({ value }) => isPositiveInteger(value) ? value : null,
421
409
  * ```
422
410
  */
411
+ /**
412
+ * #261: the utility uses the spacing scale, so a named `theme.spacing` key (`p-gutter`) resolves to
413
+ * `var(--spacing-<key>)` (negative: `calc(var(--spacing-<key>) * -1)`), as in Tailwind 4. Only tried
414
+ * after the bare-value handler rejects the value, so built-in keywords keep precedence.
415
+ */
416
+ spacingKeys?: boolean;
423
417
  handleNegativeBareValue?: (args: {
424
418
  value: string;
425
419
  ctx: Context;
@@ -552,6 +546,13 @@ export declare type AstNode = {
552
546
 
553
547
  export declare function getUtility(ctx?: Context): UtilityRegistration[];
554
548
 
549
+ /**
550
+ * #273: true when an emitted selector or at-rule prelude contains a comment opener or closer outside a CSS escape.
551
+ * Backslash-escape pairs are skipped, so an escaped `\/` or `\*` from a class name never counts. Used by the
552
+ * serializer on the final, composed string, where adjacent pieces that were each safe alone can join into one.
553
+ */
554
+ export declare function hasCommentDelimiter(text: string): boolean;
555
+
555
556
  /**
556
557
  * #224: true when a utility value (or a whole utility token) cannot change the structure of the declaration block it
557
558
  * is pasted into. Rejects, outside quotes: `{`, `}`, `;`, unbalanced or mismatched ()/[], and a quote left open.
@@ -771,6 +772,13 @@ export declare type AstNode = {
771
772
  * @param cls - The CSS class name to mark as processed
772
773
  */
773
774
  markProcessed(cls: string): void;
775
+ /**
776
+ * Forgets that a class was processed, so a later request generates it again
777
+ * (used when the browser runtime reclaims an unused class's rules, #269).
778
+ *
779
+ * @param cls - The CSS class name to forget
780
+ */
781
+ unmarkProcessed(cls: string): void;
774
782
  /**
775
783
  * Process classes synchronously and update BrowserRuntime cache
776
784
  * This method is used by ChangeDetector for scan operations
@@ -973,6 +981,27 @@ export declare type AstNode = {
973
981
 
974
982
  export declare function rule(selector: string, nodes: AstNode[], source?: string): AstNode;
975
983
 
984
+ /**
985
+ * Tailwind-compatible cascade order for runtime-inserted rules (#254); shared by @barocss/server (#267).
986
+ *
987
+ * The runtime discovers classes in DOM order, so without sorting `lg:px-8`
988
+ * seen before `sm:px-6` would land earlier and lose at >= 1024px. Each rule
989
+ * gets a sort key derived from its leading `@media` / `@container` preludes:
990
+ *
991
+ * 0 base, state media (hover), motion/contrast, unknown
992
+ * 1 max-* breakpoints (larger width first)
993
+ * 2 min-* breakpoints (smaller width first)
994
+ * 3 @max-* container queries (larger width first)
995
+ * 4 @min-* container queries (smaller width first)
996
+ * 5 orientation, dark (prefers-color-scheme), print, forced-colors
997
+ *
998
+ * Nested at-rules (e.g. `sm:dark:`) contribute one key pair per level, so
999
+ * `sm:` < `sm:dark:` < `md:`. Equal keys keep discovery order.
1000
+ */
1001
+ export declare type RuleKey = number[];
1002
+
1003
+ export declare function ruleSortKey(rule: string): RuleKey;
1004
+
976
1005
  /** Internal hook for caches owned by contexts. */
977
1006
  export declare function setContextCacheReset(reset: () => void): void;
978
1007
 
@@ -1087,6 +1116,9 @@ export declare type AstNode = {
1087
1116
  */
1088
1117
  export declare function tokenize(className: string): Token[];
1089
1118
 
1119
+ /** Index after the last key <= `key` (stable upper bound) in sorted `keys`. */
1120
+ export declare function upperBound(keys: RuleKey[], key: RuleKey): number;
1121
+
1090
1122
  /**
1091
1123
  * Utility cache management
1092
1124
  */
package/dist/index.js CHANGED
@@ -337,6 +337,11 @@ function staticUtility(name, decls, opts, ctx) {
337
337
  priority: opts?.priority
338
338
  }, ctx);
339
339
  }
340
+ function spacingKeyValue(ctx, key, negative) {
341
+ if (key === "px" || !/^[a-zA-Z][\w-]*$/.test(key) || ctx.theme("spacing", key) == null) return null;
342
+ const ref = `var(--spacing-${key})`;
343
+ return negative ? `calc(${ref} * -1)` : ref;
344
+ }
340
345
  function functionalUtility(opts, ctx) {
341
346
  registerUtility({
342
347
  name: opts.name,
@@ -405,14 +410,17 @@ function functionalUtility(opts, ctx) {
405
410
  if (opts.supportsFraction && /^-?\d+\/\d+$/.test(value)) {
406
411
  finalValue = value;
407
412
  }
413
+ const spacingKey = opts.spacingKeys ? spacingKeyValue(ctx2, String(finalValue).replace(/^-/, ""), !!parsedUtility.negative) : null;
408
414
  if (parsedUtility.negative && opts.supportsNegative && opts.handleNegativeBareValue) {
409
- const bare = opts.handleNegativeBareValue({ value: String(finalValue).replace(/^-/, ""), ctx: ctx2, token, extra });
415
+ const bare = opts.handleNegativeBareValue({ value: String(finalValue).replace(/^-/, ""), ctx: ctx2, token, extra }) ?? spacingKey;
410
416
  if (bare == null) return [];
411
417
  finalValue = bare;
412
418
  } else if (opts.handleBareValue) {
413
- const bare = opts.handleBareValue({ value: finalValue, ctx: ctx2, token, extra });
419
+ const bare = opts.handleBareValue({ value: finalValue, ctx: ctx2, token, extra }) ?? spacingKey;
414
420
  if (bare == null) return [];
415
421
  finalValue = bare;
422
+ } else if (spacingKey) {
423
+ finalValue = spacingKey;
416
424
  } else if (!/^-?(\d|\.\d)/.test(String(finalValue))) {
417
425
  return [];
418
426
  }
@@ -623,6 +631,18 @@ function isSafeVariantToken(value) {
623
631
  function hasCommentToken(value) {
624
632
  return value.includes("/*") || value.includes("*/");
625
633
  }
634
+ function hasCommentDelimiter(text) {
635
+ for (let i = 0; i < text.length - 1; i++) {
636
+ const c = text[i];
637
+ if (c === "\\") {
638
+ i++;
639
+ continue;
640
+ }
641
+ const n = text[i + 1];
642
+ if (c === "/" && n === "*" || c === "*" && n === "/") return true;
643
+ }
644
+ return false;
645
+ }
626
646
  function isStructureSafeValue(value) {
627
647
  if (hasCommentToken(value)) return false;
628
648
  return isSafeVariantValue(value, true);
@@ -770,6 +790,7 @@ function parseUtility(value, ctx) {
770
790
  priority
771
791
  };
772
792
  }
793
+ const isSafePrelude = (text) => !hasCommentDelimiter(String(text ?? ""));
773
794
  const isSafeDecl = (prop, value) => isStructureSafeValue(String(prop)) && isStructureSafeValue(String(value ?? ""));
774
795
  const importantPrefix = "!important";
775
796
  function astToCss(ast, baseSelector, opts, _indent = "") {
@@ -833,6 +854,7 @@ function astToCss(ast, baseSelector, opts, _indent = "") {
833
854
  }).join(", ");
834
855
  }
835
856
  }
857
+ if (!isSafePrelude(selector)) return "";
836
858
  if (minify) {
837
859
  const css = `${indent}${selector}{${astToCss(
838
860
  node.nodes,
@@ -857,6 +879,7 @@ ${astToCss(
857
879
  }
858
880
  }
859
881
  case "style-rule": {
882
+ if (!isSafePrelude(node.selector)) return "";
860
883
  if (minify) {
861
884
  const css = `${indent}${node.selector} {${astToCss(
862
885
  node.nodes,
@@ -881,6 +904,7 @@ ${astToCss(
881
904
  }
882
905
  }
883
906
  case "at-rule": {
907
+ if (!isSafePrelude(node.name) || !isSafePrelude(node.params)) return "";
884
908
  if (minify) {
885
909
  const css = `${indent}@${node.name} ${node.params}{${astToCss(
886
910
  node.nodes,
@@ -934,7 +958,7 @@ function rootToCss(nodes, opts) {
934
958
  if (isSafeDecl(node.prop, node.value)) {
935
959
  list.push(minify ? `${node.prop}:${node.value};` : `${node.prop}: ${node.value};`);
936
960
  }
937
- } else if (node.type === "at-rule") {
961
+ } else if (node.type === "at-rule" && isSafePrelude(node.name) && isSafePrelude(node.params)) {
938
962
  if (minify) {
939
963
  const body = node.nodes.filter((child) => child.type === "decl" && isSafeDecl(child.prop, child.value)).map((child) => child.type === "decl" ? `${child.prop}:${child.value};` : "").join("");
940
964
  list.push(`@${node.name} ${node.params}{${body}}`);
@@ -1165,8 +1189,13 @@ function themeToCssVarsAll(theme) {
1165
1189
  // keyframes handled separately
1166
1190
  };
1167
1191
  }
1192
+ function isSelfReferencingVar(name, value) {
1193
+ if (typeof value !== "string") return false;
1194
+ const m = /^var\(\s*(--[\w-]+)\s*(?:,[\s\S]*)?\)$/.exec(value.trim());
1195
+ return !!m && m[1] === name.trim();
1196
+ }
1168
1197
  function toCssVarsBlock(vars, extra = "") {
1169
- return ":root,:host {\n" + Object.entries(vars).map(([k, v2]) => ` ${k}: ${v2};`).join("\n") + "\n}\n" + extra + "\n";
1198
+ return ":root,:host {\n" + Object.entries(vars).filter(([k, v2]) => !isSelfReferencingVar(k, v2)).map(([k, v2]) => ` ${k}: ${v2};`).join("\n") + "\n}\n" + extra + "\n";
1170
1199
  }
1171
1200
  const BARO_VAR = /--baro-/g;
1172
1201
  const PREFIXED_KEYS = /* @__PURE__ */ new Set(["prop", "value", "params", "selector", "nodes", "items"]);
@@ -1841,6 +1870,15 @@ class IncrementalParser {
1841
1870
  markProcessed(cls) {
1842
1871
  this.processedClasses.add(cls);
1843
1872
  }
1873
+ /**
1874
+ * Forgets that a class was processed, so a later request generates it again
1875
+ * (used when the browser runtime reclaims an unused class's rules, #269).
1876
+ *
1877
+ * @param cls - The CSS class name to forget
1878
+ */
1879
+ unmarkProcessed(cls) {
1880
+ this.processedClasses.delete(cls);
1881
+ }
1844
1882
  /**
1845
1883
  * Process classes synchronously and update BrowserRuntime cache
1846
1884
  * This method is used by ChangeDetector for scan operations
@@ -3387,6 +3425,7 @@ staticUtility("snap-proximity", [["--baro-scroll-snap-strictness", "proximity"]]
3387
3425
  ].forEach(([name, prop]) => {
3388
3426
  functionalUtility({
3389
3427
  name: `scroll-${name}`,
3428
+ spacingKeys: true,
3390
3429
  prop,
3391
3430
  supportsArbitrary: true,
3392
3431
  supportsCustomProperty: true,
@@ -3414,6 +3453,7 @@ staticUtility("snap-proximity", [["--baro-scroll-snap-strictness", "proximity"]]
3414
3453
  ].forEach(([name, prop]) => {
3415
3454
  functionalUtility({
3416
3455
  name: `scroll-${name}`,
3456
+ spacingKeys: true,
3417
3457
  prop,
3418
3458
  supportsArbitrary: true,
3419
3459
  supportsCustomProperty: true,
@@ -4947,6 +4987,7 @@ staticUtility("sticky", [["position", "sticky"]], { category: "layout" });
4947
4987
  staticUtility(`-${name}-px`, [[prop, "-1px"]], { category: "layout" });
4948
4988
  functionalUtility({
4949
4989
  name,
4990
+ spacingKeys: true,
4950
4991
  prop,
4951
4992
  supportsNegative: true,
4952
4993
  supportsFraction: true,
@@ -4977,6 +5018,7 @@ staticUtility("invisible", [["visibility", "hidden"]], { category: "layout" });
4977
5018
  staticUtility("collapse", [["visibility", "collapse"]], { category: "layout" });
4978
5019
  functionalUtility({
4979
5020
  name: "gap-x",
5021
+ spacingKeys: true,
4980
5022
  prop: "column-gap",
4981
5023
  supportsArbitrary: true,
4982
5024
  // gap-x-[10vw]
@@ -4993,6 +5035,7 @@ functionalUtility({
4993
5035
  });
4994
5036
  functionalUtility({
4995
5037
  name: "gap-y",
5038
+ spacingKeys: true,
4996
5039
  prop: "row-gap",
4997
5040
  supportsArbitrary: true,
4998
5041
  // gap-y-[10vw]
@@ -5009,6 +5052,7 @@ functionalUtility({
5009
5052
  });
5010
5053
  functionalUtility({
5011
5054
  name: "gap",
5055
+ spacingKeys: true,
5012
5056
  prop: "gap",
5013
5057
  supportsArbitrary: true,
5014
5058
  // gap-[10vw]
@@ -5526,6 +5570,7 @@ functionalUtility({
5526
5570
  ].forEach(([name, prop]) => {
5527
5571
  staticUtility(`${name}-px`, [[prop, "1px"]], { category: "spacing" });
5528
5572
  functionalUtility({
5573
+ spacingKeys: true,
5529
5574
  name,
5530
5575
  prop,
5531
5576
  supportsArbitrary: true,
@@ -5550,6 +5595,7 @@ functionalUtility({
5550
5595
  staticUtility(`${name}-px`, [[prop, "1px"]], { category: "spacing" });
5551
5596
  staticUtility(`-${name}-px`, [[prop, "-1px"]], { category: "spacing" });
5552
5597
  functionalUtility({
5598
+ spacingKeys: true,
5553
5599
  name,
5554
5600
  prop,
5555
5601
  supportsNegative: true,
@@ -5580,6 +5626,7 @@ const SPACE_SELECTOR = ":where(& > :not(:last-child))";
5580
5626
  () => rule(SPACE_SELECTOR, [decl(rev, "1")])
5581
5627
  ], { category: "spacing" });
5582
5628
  functionalUtility({
5629
+ spacingKeys: true,
5583
5630
  name,
5584
5631
  supportsNegative: true,
5585
5632
  supportsArbitrary: true,
@@ -5629,6 +5676,7 @@ const SPACE_SELECTOR = ":where(& > :not(:last-child))";
5629
5676
  staticUtility(name, [["width", value]]);
5630
5677
  });
5631
5678
  functionalUtility({
5679
+ spacingKeys: true,
5632
5680
  name: "w",
5633
5681
  prop: "width",
5634
5682
  supportsArbitrary: true,
@@ -5661,6 +5709,7 @@ functionalUtility({
5661
5709
  staticUtility(name, [["width", w], ["height", h]]);
5662
5710
  });
5663
5711
  functionalUtility({
5712
+ spacingKeys: true,
5664
5713
  name: "size",
5665
5714
  supportsArbitrary: true,
5666
5715
  supportsCustomProperty: true,
@@ -5700,6 +5749,7 @@ functionalUtility({
5700
5749
  staticUtility(name, [["height", value]]);
5701
5750
  });
5702
5751
  functionalUtility({
5752
+ spacingKeys: true,
5703
5753
  name: "h",
5704
5754
  prop: "height",
5705
5755
  supportsArbitrary: true,
@@ -5730,6 +5780,7 @@ functionalUtility({
5730
5780
  staticUtility(name, [["min-height", value]], { category: "sizing" });
5731
5781
  });
5732
5782
  functionalUtility({
5783
+ spacingKeys: true,
5733
5784
  name: "min-h",
5734
5785
  prop: "min-height",
5735
5786
  supportsArbitrary: true,
@@ -5760,6 +5811,7 @@ functionalUtility({
5760
5811
  staticUtility(name, [["max-height", value]], { category: "sizing" });
5761
5812
  });
5762
5813
  functionalUtility({
5814
+ spacingKeys: true,
5763
5815
  name: "max-h",
5764
5816
  prop: "max-height",
5765
5817
  supportsArbitrary: true,
@@ -5806,6 +5858,7 @@ functionalUtility({
5806
5858
  staticUtility(name, [["min-width", value]], { category: "sizing" });
5807
5859
  });
5808
5860
  functionalUtility({
5861
+ spacingKeys: true,
5809
5862
  name: "min-w",
5810
5863
  prop: "min-width",
5811
5864
  supportsArbitrary: true,
@@ -5841,6 +5894,7 @@ functionalUtility({
5841
5894
  staticUtility(name, [["max-width", value]], { category: "sizing" });
5842
5895
  });
5843
5896
  functionalUtility({
5897
+ spacingKeys: true,
5844
5898
  name: "max-w",
5845
5899
  prop: "max-width",
5846
5900
  supportsArbitrary: true,
@@ -8264,6 +8318,51 @@ functionalModifier(
8264
8318
  },
8265
8319
  void 0
8266
8320
  );
8321
+ const LEADING_AT = /^\s*@(media|container)\s+([^{]*)\{/;
8322
+ const LATE_MEDIA = /prefers-color-scheme|\bprint\b|forced-colors|orientation/;
8323
+ const MIN_W = /(?:min-width\s*:\s*|width\s*>=?\s*)([\d.]+)(px|rem|em)?/;
8324
+ const MAX_W = /(?:max-width\s*:\s*|width\s*<=?\s*)([\d.]+)(px|rem|em)?/;
8325
+ function toPx(n, unit) {
8326
+ const v = parseFloat(n);
8327
+ return unit === "rem" || unit === "em" ? v * 16 : v;
8328
+ }
8329
+ function preludeKey(kind, prelude) {
8330
+ const container = kind === "container";
8331
+ const min = MIN_W.exec(prelude);
8332
+ if (min) return [container ? 4 : 2, toPx(min[1], min[2])];
8333
+ const max = MAX_W.exec(prelude);
8334
+ if (max) return [container ? 3 : 1, -toPx(max[1], max[2])];
8335
+ if (!container && LATE_MEDIA.test(prelude)) return [5, 0];
8336
+ return [0, 0];
8337
+ }
8338
+ function ruleSortKey(rule2) {
8339
+ const key = [];
8340
+ let rest = rule2;
8341
+ let m;
8342
+ while (m = LEADING_AT.exec(rest)) {
8343
+ const [g, v] = preludeKey(m[1], m[2]);
8344
+ key.push(g, v);
8345
+ rest = rest.slice(m[0].length);
8346
+ }
8347
+ return key;
8348
+ }
8349
+ function compareKeys(a, b) {
8350
+ const n = Math.min(a.length, b.length);
8351
+ for (let i = 0; i < n; i++) {
8352
+ if (a[i] !== b[i]) return a[i] - b[i];
8353
+ }
8354
+ return a.length - b.length;
8355
+ }
8356
+ function upperBound(keys, key) {
8357
+ let lo = 0;
8358
+ let hi = keys.length;
8359
+ while (lo < hi) {
8360
+ const mid = lo + hi >> 1;
8361
+ if (compareKeys(keys[mid], key) <= 0) lo = mid + 1;
8362
+ else hi = mid;
8363
+ }
8364
+ return lo;
8365
+ }
8267
8366
  export {
8268
8367
  AstCache,
8269
8368
  IncrementalParser,
@@ -8279,6 +8378,7 @@ export {
8279
8378
  clearAstCache,
8280
8379
  collectDeclPaths,
8281
8380
  comment,
8381
+ compareKeys,
8282
8382
  configGetter,
8283
8383
  createContext,
8284
8384
  decl,
@@ -8296,6 +8396,7 @@ export {
8296
8396
  getModifier,
8297
8397
  getPreflightCSS,
8298
8398
  getUtility,
8399
+ hasCommentDelimiter,
8299
8400
  hasCommentToken,
8300
8401
  hasPreset,
8301
8402
  isDebug,
@@ -8317,6 +8418,7 @@ export {
8317
8418
  resolveTheme,
8318
8419
  rootToCss,
8319
8420
  rule,
8421
+ ruleSortKey,
8320
8422
  setContextCacheReset,
8321
8423
  setDebug,
8322
8424
  staticModifier,
@@ -8325,6 +8427,7 @@ export {
8325
8427
  themeGetter,
8326
8428
  themeToCssVars,
8327
8429
  tokenize,
8430
+ upperBound,
8328
8431
  utilityCache
8329
8432
  };
8330
8433
  //# sourceMappingURL=index.js.map