@kbach/react 0.6.15 → 0.6.17

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.
@@ -671,25 +671,6 @@ function resolveColor(value, colors, isArbitrary) {
671
671
  }
672
672
  }
673
673
  }
674
- if (hex && !hex.startsWith("#") && !hex.startsWith("rgb") && hex !== "transparent" && hex !== "currentColor") {
675
- if (hex in colors) {
676
- const alias = colors[hex];
677
- if (typeof alias === "string") hex = alias;
678
- else if (typeof alias === "object" && "6" in alias) hex = alias["6"] ?? hex;
679
- } else {
680
- const aliasLastDash = hex.lastIndexOf("-");
681
- if (aliasLastDash > 0) {
682
- const aliasColorName = hex.slice(0, aliasLastDash);
683
- const aliasShade = hex.slice(aliasLastDash + 1);
684
- const aliasScale = colors[aliasColorName];
685
- if (aliasScale && typeof aliasScale === "object") {
686
- hex = aliasScale[aliasShade] ?? null;
687
- } else if (typeof aliasScale === "string") {
688
- hex = aliasScale;
689
- }
690
- }
691
- }
692
- }
693
674
  if (!hex) return null;
694
675
  if (!opacityPart) {
695
676
  if (!getEffectiveIsWeb() && hex.startsWith("#") && hex.length === 9) {
@@ -705,6 +686,8 @@ function resolveColor(value, colors, isArbitrary) {
705
686
  alpha = parseFloat(opacityPart) / 100;
706
687
  }
707
688
  if (isNaN(alpha)) return hex;
689
+ if (alpha < 0) alpha = 0;
690
+ else if (alpha > 1) alpha = 1;
708
691
  return hexToRgba(hex, alpha);
709
692
  }
710
693
  function parseHexRgb(hex) {
@@ -2519,6 +2502,14 @@ function isKnownModifier(name) {
2519
2502
  }
2520
2503
 
2521
2504
  // src/core/parser.ts
2505
+ var UNSAFE_ARBITRARY_VALUE = /[{};\\]|\/\*|\*\//;
2506
+ function isSafeArbitraryValue(value) {
2507
+ if (!UNSAFE_ARBITRARY_VALUE.test(value)) return true;
2508
+ if (process.env.NODE_ENV !== "production") {
2509
+ kbachWarn(`Unsafe arbitrary value rejected: "${value}"`);
2510
+ }
2511
+ return false;
2512
+ }
2522
2513
  function parseClass(className) {
2523
2514
  const trimmed = className.trim();
2524
2515
  if (!trimmed) return null;
@@ -2557,6 +2548,7 @@ function parseClass(className) {
2557
2548
  if (bracketStart > 0 && remaining[bracketStart - 1] === "-" && remaining.endsWith("]")) {
2558
2549
  const utility = remaining.slice(0, bracketStart - 1);
2559
2550
  const value = remaining.slice(bracketStart + 1, -1).replace(/_/g, " ");
2551
+ if (!isSafeArbitraryValue(value)) return null;
2560
2552
  return { original: trimmed, modifiers, negative, important, utility, value, isArbitrary: true };
2561
2553
  }
2562
2554
  if (remaining.endsWith("]") && bracketStart === -1) {
@@ -2566,6 +2558,7 @@ function parseClass(className) {
2566
2558
  }
2567
2559
  if (remaining.startsWith("[") && remaining.endsWith("]")) {
2568
2560
  const value = remaining.slice(1, -1).replace(/_/g, " ");
2561
+ if (!isSafeArbitraryValue(value)) return null;
2569
2562
  return { original: trimmed, modifiers, negative, important, utility: "", value, isArbitrary: true };
2570
2563
  }
2571
2564
  if (getBuiltinStandaloneNames().has(remaining)) {
@@ -2701,17 +2694,20 @@ function setDefaultFontFamily(font) {
2701
2694
  globalThis[FONT_KEY] = font;
2702
2695
  }
2703
2696
  var _styleEl = null;
2697
+ var _ruleIndexByKey = /* @__PURE__ */ new Map();
2704
2698
  function evictInjectedRule(rule) {
2699
+ const idx = _ruleIndexByKey.get(rule);
2700
+ _ruleIndexByKey.delete(rule);
2701
+ if (idx === void 0) return;
2705
2702
  const sheet = _styleEl?.sheet;
2706
2703
  if (!sheet) return;
2707
- for (let i = 0; i < sheet.cssRules.length; i++) {
2708
- if (sheet.cssRules[i].cssText === rule) {
2709
- try {
2710
- sheet.deleteRule(i);
2711
- } catch {
2712
- }
2713
- return;
2714
- }
2704
+ try {
2705
+ sheet.deleteRule(idx);
2706
+ } catch {
2707
+ return;
2708
+ }
2709
+ for (const [key, i] of _ruleIndexByKey) {
2710
+ if (i > idx) _ruleIndexByKey.set(key, i - 1);
2715
2711
  }
2716
2712
  }
2717
2713
  var _injectedRules = new LRUCache(5e4, evictInjectedRule);
@@ -3156,26 +3152,37 @@ function buildColorVarMap(theme, usedCSS) {
3156
3152
  const hexRe = /#[0-9a-fA-F]{3,8}\b/g;
3157
3153
  let hm;
3158
3154
  while ((hm = hexRe.exec(usedCSS)) !== null) usedHexValues.add(hm[0].toLowerCase());
3159
- const map = /* @__PURE__ */ new Map();
3155
+ const replacements = /* @__PURE__ */ new Map();
3156
+ const declarations = /* @__PURE__ */ new Map();
3160
3157
  for (const [name, shades] of Object.entries(theme.colors ?? {})) {
3161
3158
  const entries = typeof shades === "string" ? [[name, shades]] : Object.entries(shades).map(([s, v]) => [`${name}-${s}`, v]);
3162
3159
  for (const [fullName, val] of entries) {
3163
- if (typeof val === "string" && /^#[0-9a-fA-F]{3,8}$/.test(val)) {
3164
- if (usedHexValues.has(val.toLowerCase())) {
3165
- map.set(val, `--color-${fullName}`);
3166
- }
3160
+ if (typeof val !== "string" || !/^#[0-9a-fA-F]{3,8}$/.test(val)) continue;
3161
+ if (usedHexValues.has(val.toLowerCase())) {
3162
+ const varName = `--color-${fullName}`;
3163
+ replacements.set(val, `var(${varName})`);
3164
+ declarations.set(varName, val);
3165
+ }
3166
+ const rgb = parseHexRgb(val);
3167
+ if (!rgb) continue;
3168
+ const triplet = `${rgb[0]},${rgb[1]},${rgb[2]}`;
3169
+ const rgbaPrefix = `rgba(${triplet},`;
3170
+ if (usedCSS.includes(rgbaPrefix)) {
3171
+ const rgbVarName = `--color-${fullName}-rgb`;
3172
+ replacements.set(rgbaPrefix, `rgba(var(${rgbVarName}),`);
3173
+ declarations.set(rgbVarName, triplet);
3167
3174
  }
3168
3175
  }
3169
3176
  }
3170
- return map;
3177
+ return { replacements, declarations };
3171
3178
  }
3172
3179
  function formatKbachCSS(tokenCSS, theme, responsiveRe) {
3173
3180
  const allRaw = [...tokenCSS.values()].filter(Boolean).join("\n");
3174
- const colorVars = buildColorVarMap(theme, allRaw);
3175
- const sortedColorVars = [...colorVars].sort(([a], [b]) => b.length - a.length);
3181
+ const { replacements, declarations } = buildColorVarMap(theme, allRaw);
3182
+ const sortedReplacements = [...replacements].sort(([a], [b]) => b.length - a.length);
3176
3183
  function applyVars(css) {
3177
- for (const [val, varName] of sortedColorVars)
3178
- css = css.split(val).join(`var(${varName})`);
3184
+ for (const [pattern, replacement] of sortedReplacements)
3185
+ css = css.split(pattern).join(replacement);
3179
3186
  return css;
3180
3187
  }
3181
3188
  const groups = new Map(GROUP_ORDER.map((g) => [g, []]));
@@ -3186,9 +3193,9 @@ function formatKbachCSS(tokenCSS, theme, responsiveRe) {
3186
3193
  groups.get(label).push(applyVars(css));
3187
3194
  }
3188
3195
  const out = ["/* Generated by Kbach \u2014 do not edit */"];
3189
- if (colorVars.size > 0) {
3196
+ if (declarations.size > 0) {
3190
3197
  out.push("\n:root {");
3191
- for (const [val, varName] of colorVars) out.push(` ${varName}: ${val};`);
3198
+ for (const [varName, val] of declarations) out.push(` ${varName}: ${val};`);
3192
3199
  out.push("}");
3193
3200
  }
3194
3201
  const globalCSS = buildThemeGlobal(theme);
@@ -637,25 +637,6 @@ function resolveColor(value, colors, isArbitrary) {
637
637
  }
638
638
  }
639
639
  }
640
- if (hex && !hex.startsWith("#") && !hex.startsWith("rgb") && hex !== "transparent" && hex !== "currentColor") {
641
- if (hex in colors) {
642
- const alias = colors[hex];
643
- if (typeof alias === "string") hex = alias;
644
- else if (typeof alias === "object" && "6" in alias) hex = alias["6"] ?? hex;
645
- } else {
646
- const aliasLastDash = hex.lastIndexOf("-");
647
- if (aliasLastDash > 0) {
648
- const aliasColorName = hex.slice(0, aliasLastDash);
649
- const aliasShade = hex.slice(aliasLastDash + 1);
650
- const aliasScale = colors[aliasColorName];
651
- if (aliasScale && typeof aliasScale === "object") {
652
- hex = aliasScale[aliasShade] ?? null;
653
- } else if (typeof aliasScale === "string") {
654
- hex = aliasScale;
655
- }
656
- }
657
- }
658
- }
659
640
  if (!hex) return null;
660
641
  if (!opacityPart) {
661
642
  if (!getEffectiveIsWeb() && hex.startsWith("#") && hex.length === 9) {
@@ -671,6 +652,8 @@ function resolveColor(value, colors, isArbitrary) {
671
652
  alpha = parseFloat(opacityPart) / 100;
672
653
  }
673
654
  if (isNaN(alpha)) return hex;
655
+ if (alpha < 0) alpha = 0;
656
+ else if (alpha > 1) alpha = 1;
674
657
  return hexToRgba(hex, alpha);
675
658
  }
676
659
  function parseHexRgb(hex) {
@@ -2485,6 +2468,14 @@ function isKnownModifier(name) {
2485
2468
  }
2486
2469
 
2487
2470
  // src/core/parser.ts
2471
+ var UNSAFE_ARBITRARY_VALUE = /[{};\\]|\/\*|\*\//;
2472
+ function isSafeArbitraryValue(value) {
2473
+ if (!UNSAFE_ARBITRARY_VALUE.test(value)) return true;
2474
+ if (process.env.NODE_ENV !== "production") {
2475
+ kbachWarn(`Unsafe arbitrary value rejected: "${value}"`);
2476
+ }
2477
+ return false;
2478
+ }
2488
2479
  function parseClass(className) {
2489
2480
  const trimmed = className.trim();
2490
2481
  if (!trimmed) return null;
@@ -2523,6 +2514,7 @@ function parseClass(className) {
2523
2514
  if (bracketStart > 0 && remaining[bracketStart - 1] === "-" && remaining.endsWith("]")) {
2524
2515
  const utility = remaining.slice(0, bracketStart - 1);
2525
2516
  const value = remaining.slice(bracketStart + 1, -1).replace(/_/g, " ");
2517
+ if (!isSafeArbitraryValue(value)) return null;
2526
2518
  return { original: trimmed, modifiers, negative, important, utility, value, isArbitrary: true };
2527
2519
  }
2528
2520
  if (remaining.endsWith("]") && bracketStart === -1) {
@@ -2532,6 +2524,7 @@ function parseClass(className) {
2532
2524
  }
2533
2525
  if (remaining.startsWith("[") && remaining.endsWith("]")) {
2534
2526
  const value = remaining.slice(1, -1).replace(/_/g, " ");
2527
+ if (!isSafeArbitraryValue(value)) return null;
2535
2528
  return { original: trimmed, modifiers, negative, important, utility: "", value, isArbitrary: true };
2536
2529
  }
2537
2530
  if (getBuiltinStandaloneNames().has(remaining)) {
@@ -2667,17 +2660,20 @@ function setDefaultFontFamily(font) {
2667
2660
  globalThis[FONT_KEY] = font;
2668
2661
  }
2669
2662
  var _styleEl = null;
2663
+ var _ruleIndexByKey = /* @__PURE__ */ new Map();
2670
2664
  function evictInjectedRule(rule) {
2665
+ const idx = _ruleIndexByKey.get(rule);
2666
+ _ruleIndexByKey.delete(rule);
2667
+ if (idx === void 0) return;
2671
2668
  const sheet = _styleEl?.sheet;
2672
2669
  if (!sheet) return;
2673
- for (let i = 0; i < sheet.cssRules.length; i++) {
2674
- if (sheet.cssRules[i].cssText === rule) {
2675
- try {
2676
- sheet.deleteRule(i);
2677
- } catch {
2678
- }
2679
- return;
2680
- }
2670
+ try {
2671
+ sheet.deleteRule(idx);
2672
+ } catch {
2673
+ return;
2674
+ }
2675
+ for (const [key, i] of _ruleIndexByKey) {
2676
+ if (i > idx) _ruleIndexByKey.set(key, i - 1);
2681
2677
  }
2682
2678
  }
2683
2679
  var _injectedRules = new LRUCache(5e4, evictInjectedRule);
@@ -3122,26 +3118,37 @@ function buildColorVarMap(theme, usedCSS) {
3122
3118
  const hexRe = /#[0-9a-fA-F]{3,8}\b/g;
3123
3119
  let hm;
3124
3120
  while ((hm = hexRe.exec(usedCSS)) !== null) usedHexValues.add(hm[0].toLowerCase());
3125
- const map = /* @__PURE__ */ new Map();
3121
+ const replacements = /* @__PURE__ */ new Map();
3122
+ const declarations = /* @__PURE__ */ new Map();
3126
3123
  for (const [name, shades] of Object.entries(theme.colors ?? {})) {
3127
3124
  const entries = typeof shades === "string" ? [[name, shades]] : Object.entries(shades).map(([s, v]) => [`${name}-${s}`, v]);
3128
3125
  for (const [fullName, val] of entries) {
3129
- if (typeof val === "string" && /^#[0-9a-fA-F]{3,8}$/.test(val)) {
3130
- if (usedHexValues.has(val.toLowerCase())) {
3131
- map.set(val, `--color-${fullName}`);
3132
- }
3126
+ if (typeof val !== "string" || !/^#[0-9a-fA-F]{3,8}$/.test(val)) continue;
3127
+ if (usedHexValues.has(val.toLowerCase())) {
3128
+ const varName = `--color-${fullName}`;
3129
+ replacements.set(val, `var(${varName})`);
3130
+ declarations.set(varName, val);
3131
+ }
3132
+ const rgb = parseHexRgb(val);
3133
+ if (!rgb) continue;
3134
+ const triplet = `${rgb[0]},${rgb[1]},${rgb[2]}`;
3135
+ const rgbaPrefix = `rgba(${triplet},`;
3136
+ if (usedCSS.includes(rgbaPrefix)) {
3137
+ const rgbVarName = `--color-${fullName}-rgb`;
3138
+ replacements.set(rgbaPrefix, `rgba(var(${rgbVarName}),`);
3139
+ declarations.set(rgbVarName, triplet);
3133
3140
  }
3134
3141
  }
3135
3142
  }
3136
- return map;
3143
+ return { replacements, declarations };
3137
3144
  }
3138
3145
  function formatKbachCSS(tokenCSS, theme, responsiveRe) {
3139
3146
  const allRaw = [...tokenCSS.values()].filter(Boolean).join("\n");
3140
- const colorVars = buildColorVarMap(theme, allRaw);
3141
- const sortedColorVars = [...colorVars].sort(([a], [b]) => b.length - a.length);
3147
+ const { replacements, declarations } = buildColorVarMap(theme, allRaw);
3148
+ const sortedReplacements = [...replacements].sort(([a], [b]) => b.length - a.length);
3142
3149
  function applyVars(css) {
3143
- for (const [val, varName] of sortedColorVars)
3144
- css = css.split(val).join(`var(${varName})`);
3150
+ for (const [pattern, replacement] of sortedReplacements)
3151
+ css = css.split(pattern).join(replacement);
3145
3152
  return css;
3146
3153
  }
3147
3154
  const groups = new Map(GROUP_ORDER.map((g) => [g, []]));
@@ -3152,9 +3159,9 @@ function formatKbachCSS(tokenCSS, theme, responsiveRe) {
3152
3159
  groups.get(label).push(applyVars(css));
3153
3160
  }
3154
3161
  const out = ["/* Generated by Kbach \u2014 do not edit */"];
3155
- if (colorVars.size > 0) {
3162
+ if (declarations.size > 0) {
3156
3163
  out.push("\n:root {");
3157
- for (const [val, varName] of colorVars) out.push(` ${varName}: ${val};`);
3164
+ for (const [varName, val] of declarations) out.push(` ${varName}: ${val};`);
3158
3165
  out.push("}");
3159
3166
  }
3160
3167
  const globalCSS = buildThemeGlobal(theme);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kbach/react",
3
- "version": "0.6.15",
3
+ "version": "0.6.17",
4
4
  "description": "React / React Native components and hooks for the Kbach framework",
5
5
  "source": "./src/index.ts",
6
6
  "main": "./dist/index.js",