@kbach/react 0.6.5 → 0.6.6

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/README.md CHANGED
@@ -88,7 +88,7 @@ import './kbach.css';
88
88
 
89
89
  The plugin scans your source files and writes generated CSS between the `/* kbach:start */`/`/* kbach:end */` markers. Importing `kbach.css` automatically disables runtime injection — all styles come from the file instead. On HMR, only the changed file is rescanned; unchanged tokens reuse cached CSS.
90
90
 
91
- While it's scanning, the plugin also indexes every `.css`/`.scss`/`.sass`/`.less` file under the same directories and warns in the terminal (not the browser console) for any class that's neither a real Kbach utility nor defined anywhere in those stylesheets — likely a typo. A class you've defined yourself elsewhere (CSS Modules, styled-components, a third-party component's class) is recognized as soon as something in the project literally has a `.that-class-name` rule, so it won't get flagged just because Kbach itself doesn't know it.
91
+ While it's scanning, the plugin also indexes every `.css`/`.scss`/`.sass`/`.less` file under the same directories and warns in the terminal (not the browser console) for any class that's neither a real Kbach utility nor defined anywhere in those stylesheets — likely a typo. A class you've defined yourself elsewhere (CSS Modules, styled-components, a third-party component's class) is recognized as soon as something in the project literally has a `.that-class-name` rule, so it won't get flagged just because Kbach itself doesn't know it. Each warning includes a `file:line:column` location that most terminals (VS Code's integrated terminal included) turn into a clickable link straight to that class.
92
92
 
93
93
  ## Next.js
94
94
 
@@ -23,7 +23,7 @@ import {
23
23
  transformToWebProps,
24
24
  useConditionalWidth,
25
25
  useGlobalDarkMode
26
- } from "./chunk-BAUO36PM.mjs";
26
+ } from "./chunk-LXOMVLQI.mjs";
27
27
 
28
28
  // src/jsx-runtime.tsx
29
29
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
@@ -565,6 +565,15 @@ function escapeCSSSelector(cls) {
565
565
  return cls.replace(/[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g, "\\$&");
566
566
  }
567
567
 
568
+ // src/core/devWarn.ts
569
+ function kbachWarn(message) {
570
+ console.warn(
571
+ "%c[kbach]%c " + message,
572
+ "color:#8b5cf6;font-weight:700",
573
+ "color:inherit;font-weight:400"
574
+ );
575
+ }
576
+
568
577
  // src/core/registry.ts
569
578
  var BUILTIN_MODIFIERS = {
570
579
  // ── Mode ─────────────────────────────────────────────────────────────────
@@ -640,7 +649,7 @@ function _invalidate() {
640
649
  }
641
650
  function registerModifier(name, def) {
642
651
  if (process.env.NODE_ENV !== "production" && name in BUILTIN_MODIFIERS) {
643
- console.warn(`[kbach] registerModifier: "${name}" is a built-in modifier and cannot be overridden. Use a different name.`);
652
+ kbachWarn(`"${name}" is built-in \u2014 pick another modifier name`);
644
653
  return;
645
654
  }
646
655
  _pluginModifiers[name] = def;
@@ -2434,7 +2443,7 @@ function parseClass(className) {
2434
2443
  }
2435
2444
  if (remaining.endsWith("]") && bracketStart === -1) {
2436
2445
  if (process.env.NODE_ENV !== "production") {
2437
- console.warn(`[kbach] Malformed class string: "${trimmed}" \u2014 unbalanced brackets`);
2446
+ kbachWarn(`Unbalanced brackets: "${trimmed}"`);
2438
2447
  }
2439
2448
  }
2440
2449
  if (remaining.startsWith("[") && remaining.endsWith("]")) {
@@ -2901,7 +2910,7 @@ function resolve(classString, theme, darkMode = "attribute") {
2901
2910
  const styles = resolveUtility(parsed, theme);
2902
2911
  if (!styles) {
2903
2912
  if (process.env.NODE_ENV !== "production" && !parsed.isArbitrary && !isKnownUtility(parsed.utility) && !parsed.original.startsWith("__")) {
2904
- console.warn(`[kbach] Unknown utility "${parsed.utility}" in class "${parsed.original}"`);
2913
+ kbachWarn(`Unknown class "${parsed.original}"`);
2905
2914
  }
2906
2915
  continue;
2907
2916
  }
@@ -3056,7 +3065,7 @@ function buildConfig(userConfig) {
3056
3065
  for (const [key, value] of Object.entries(ext)) {
3057
3066
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
3058
3067
  if (process.env.NODE_ENV !== "production") {
3059
- console.warn(`[kbach] config.extend.${key} must be a plain object, got ${typeof value}. Skipping.`);
3068
+ kbachWarn(`extend.${key} should be an object, got ${typeof value} \u2014 skipped`);
3060
3069
  }
3061
3070
  continue;
3062
3071
  }
package/dist/index.js CHANGED
@@ -690,6 +690,15 @@ function escapeCSSSelector(cls) {
690
690
  return cls.replace(/[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g, "\\$&");
691
691
  }
692
692
 
693
+ // src/core/devWarn.ts
694
+ function kbachWarn(message) {
695
+ console.warn(
696
+ "%c[kbach]%c " + message,
697
+ "color:#8b5cf6;font-weight:700",
698
+ "color:inherit;font-weight:400"
699
+ );
700
+ }
701
+
693
702
  // src/core/registry.ts
694
703
  var BUILTIN_MODIFIERS = {
695
704
  // ── Mode ─────────────────────────────────────────────────────────────────
@@ -765,7 +774,7 @@ function _invalidate() {
765
774
  }
766
775
  function registerModifier(name, def) {
767
776
  if (process.env.NODE_ENV !== "production" && name in BUILTIN_MODIFIERS) {
768
- console.warn(`[kbach] registerModifier: "${name}" is a built-in modifier and cannot be overridden. Use a different name.`);
777
+ kbachWarn(`"${name}" is built-in \u2014 pick another modifier name`);
769
778
  return;
770
779
  }
771
780
  _pluginModifiers[name] = def;
@@ -2553,7 +2562,7 @@ function parseClass(className) {
2553
2562
  }
2554
2563
  if (remaining.endsWith("]") && bracketStart === -1) {
2555
2564
  if (process.env.NODE_ENV !== "production") {
2556
- console.warn(`[kbach] Malformed class string: "${trimmed}" \u2014 unbalanced brackets`);
2565
+ kbachWarn(`Unbalanced brackets: "${trimmed}"`);
2557
2566
  }
2558
2567
  }
2559
2568
  if (remaining.startsWith("[") && remaining.endsWith("]")) {
@@ -2979,7 +2988,7 @@ function resolve(classString, theme, darkMode = "attribute") {
2979
2988
  const styles = resolveUtility(parsed, theme);
2980
2989
  if (!styles) {
2981
2990
  if (process.env.NODE_ENV !== "production" && !parsed.isArbitrary && !isKnownUtility(parsed.utility) && !parsed.original.startsWith("__")) {
2982
- console.warn(`[kbach] Unknown utility "${parsed.utility}" in class "${parsed.original}"`);
2991
+ kbachWarn(`Unknown class "${parsed.original}"`);
2983
2992
  }
2984
2993
  continue;
2985
2994
  }
@@ -3134,7 +3143,7 @@ function buildConfig(userConfig) {
3134
3143
  for (const [key, value] of Object.entries(ext)) {
3135
3144
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
3136
3145
  if (process.env.NODE_ENV !== "production") {
3137
- console.warn(`[kbach] config.extend.${key} must be a plain object, got ${typeof value}. Skipping.`);
3146
+ kbachWarn(`extend.${key} should be an object, got ${typeof value} \u2014 skipped`);
3138
3147
  }
3139
3148
  continue;
3140
3149
  }
package/dist/index.mjs CHANGED
@@ -36,7 +36,7 @@ import {
36
36
  useGlobalDarkMode,
37
37
  useGlobalWidth,
38
38
  useSyncExternalStore
39
- } from "./chunk-BAUO36PM.mjs";
39
+ } from "./chunk-LXOMVLQI.mjs";
40
40
 
41
41
  // src/context.tsx
42
42
  import { createContext, useContext } from "react";
@@ -643,6 +643,15 @@ function escapeCSSSelector(cls) {
643
643
  return cls.replace(/[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g, "\\$&");
644
644
  }
645
645
 
646
+ // src/core/devWarn.ts
647
+ function kbachWarn(message) {
648
+ console.warn(
649
+ "%c[kbach]%c " + message,
650
+ "color:#8b5cf6;font-weight:700",
651
+ "color:inherit;font-weight:400"
652
+ );
653
+ }
654
+
646
655
  // src/core/registry.ts
647
656
  var BUILTIN_MODIFIERS = {
648
657
  // ── Mode ─────────────────────────────────────────────────────────────────
@@ -718,7 +727,7 @@ function _invalidate() {
718
727
  }
719
728
  function registerModifier(name, def) {
720
729
  if (process.env.NODE_ENV !== "production" && name in BUILTIN_MODIFIERS) {
721
- console.warn(`[kbach] registerModifier: "${name}" is a built-in modifier and cannot be overridden. Use a different name.`);
730
+ kbachWarn(`"${name}" is built-in \u2014 pick another modifier name`);
722
731
  return;
723
732
  }
724
733
  _pluginModifiers[name] = def;
@@ -2512,7 +2521,7 @@ function parseClass(className) {
2512
2521
  }
2513
2522
  if (remaining.endsWith("]") && bracketStart === -1) {
2514
2523
  if (process.env.NODE_ENV !== "production") {
2515
- console.warn(`[kbach] Malformed class string: "${trimmed}" \u2014 unbalanced brackets`);
2524
+ kbachWarn(`Unbalanced brackets: "${trimmed}"`);
2516
2525
  }
2517
2526
  }
2518
2527
  if (remaining.startsWith("[") && remaining.endsWith("]")) {
@@ -2923,7 +2932,7 @@ function resolve(classString, theme, darkMode = "attribute") {
2923
2932
  const styles = resolveUtility(parsed, theme);
2924
2933
  if (!styles) {
2925
2934
  if (process.env.NODE_ENV !== "production" && !parsed.isArbitrary && !isKnownUtility(parsed.utility) && !parsed.original.startsWith("__")) {
2926
- console.warn(`[kbach] Unknown utility "${parsed.utility}" in class "${parsed.original}"`);
2935
+ kbachWarn(`Unknown class "${parsed.original}"`);
2927
2936
  }
2928
2937
  continue;
2929
2938
  }
@@ -3067,7 +3076,7 @@ function buildConfig(userConfig) {
3067
3076
  for (const [key, value] of Object.entries(ext)) {
3068
3077
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
3069
3078
  if (process.env.NODE_ENV !== "production") {
3070
- console.warn(`[kbach] config.extend.${key} must be a plain object, got ${typeof value}. Skipping.`);
3079
+ kbachWarn(`extend.${key} should be an object, got ${typeof value} \u2014 skipped`);
3071
3080
  }
3072
3081
  continue;
3073
3082
  }
@@ -3,8 +3,8 @@ import {
3
3
  Fragment,
4
4
  jsx,
5
5
  jsxs
6
- } from "./chunk-FXDK5Z6V.mjs";
7
- import "./chunk-BAUO36PM.mjs";
6
+ } from "./chunk-5YN6PIXQ.mjs";
7
+ import "./chunk-LXOMVLQI.mjs";
8
8
 
9
9
  // src/jsx-dev-runtime.tsx
10
10
  function jsxDEV(type, props, key, isStaticChildren, source, self) {
@@ -644,6 +644,15 @@ function escapeCSSSelector(cls) {
644
644
  return cls.replace(/[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g, "\\$&");
645
645
  }
646
646
 
647
+ // src/core/devWarn.ts
648
+ function kbachWarn(message) {
649
+ console.warn(
650
+ "%c[kbach]%c " + message,
651
+ "color:#8b5cf6;font-weight:700",
652
+ "color:inherit;font-weight:400"
653
+ );
654
+ }
655
+
647
656
  // src/core/registry.ts
648
657
  var BUILTIN_MODIFIERS = {
649
658
  // ── Mode ─────────────────────────────────────────────────────────────────
@@ -719,7 +728,7 @@ function _invalidate() {
719
728
  }
720
729
  function registerModifier(name, def) {
721
730
  if (process.env.NODE_ENV !== "production" && name in BUILTIN_MODIFIERS) {
722
- console.warn(`[kbach] registerModifier: "${name}" is a built-in modifier and cannot be overridden. Use a different name.`);
731
+ kbachWarn(`"${name}" is built-in \u2014 pick another modifier name`);
723
732
  return;
724
733
  }
725
734
  _pluginModifiers[name] = def;
@@ -2513,7 +2522,7 @@ function parseClass(className) {
2513
2522
  }
2514
2523
  if (remaining.endsWith("]") && bracketStart === -1) {
2515
2524
  if (process.env.NODE_ENV !== "production") {
2516
- console.warn(`[kbach] Malformed class string: "${trimmed}" \u2014 unbalanced brackets`);
2525
+ kbachWarn(`Unbalanced brackets: "${trimmed}"`);
2517
2526
  }
2518
2527
  }
2519
2528
  if (remaining.startsWith("[") && remaining.endsWith("]")) {
@@ -2924,7 +2933,7 @@ function resolve(classString, theme, darkMode = "attribute") {
2924
2933
  const styles = resolveUtility(parsed, theme);
2925
2934
  if (!styles) {
2926
2935
  if (process.env.NODE_ENV !== "production" && !parsed.isArbitrary && !isKnownUtility(parsed.utility) && !parsed.original.startsWith("__")) {
2927
- console.warn(`[kbach] Unknown utility "${parsed.utility}" in class "${parsed.original}"`);
2936
+ kbachWarn(`Unknown class "${parsed.original}"`);
2928
2937
  }
2929
2938
  continue;
2930
2939
  }
@@ -3068,7 +3077,7 @@ function buildConfig(userConfig) {
3068
3077
  for (const [key, value] of Object.entries(ext)) {
3069
3078
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
3070
3079
  if (process.env.NODE_ENV !== "production") {
3071
- console.warn(`[kbach] config.extend.${key} must be a plain object, got ${typeof value}. Skipping.`);
3080
+ kbachWarn(`extend.${key} should be an object, got ${typeof value} \u2014 skipped`);
3072
3081
  }
3073
3082
  continue;
3074
3083
  }
@@ -4,10 +4,10 @@ import {
4
4
  _invalidateDefaultFontCache,
5
5
  jsx,
6
6
  jsxs
7
- } from "./chunk-FXDK5Z6V.mjs";
7
+ } from "./chunk-5YN6PIXQ.mjs";
8
8
  import {
9
9
  registerWebElement
10
- } from "./chunk-BAUO36PM.mjs";
10
+ } from "./chunk-LXOMVLQI.mjs";
11
11
  export {
12
12
  Fragment,
13
13
  _invalidateDefaultFontCache,
@@ -2307,6 +2307,15 @@ function getBuiltinStandaloneNames() {
2307
2307
  return _standaloneNames;
2308
2308
  }
2309
2309
 
2310
+ // src/core/devWarn.ts
2311
+ function kbachWarn(message) {
2312
+ console.warn(
2313
+ "%c[kbach]%c " + message,
2314
+ "color:#8b5cf6;font-weight:700",
2315
+ "color:inherit;font-weight:400"
2316
+ );
2317
+ }
2318
+
2310
2319
  // src/core/registry.ts
2311
2320
  var BUILTIN_MODIFIERS = {
2312
2321
  // ── Mode ─────────────────────────────────────────────────────────────────
@@ -2382,7 +2391,7 @@ function _invalidate() {
2382
2391
  }
2383
2392
  function registerModifier(name, def) {
2384
2393
  if (process.env.NODE_ENV !== "production" && name in BUILTIN_MODIFIERS) {
2385
- console.warn(`[kbach] registerModifier: "${name}" is a built-in modifier and cannot be overridden. Use a different name.`);
2394
+ kbachWarn(`"${name}" is built-in \u2014 pick another modifier name`);
2386
2395
  return;
2387
2396
  }
2388
2397
  _pluginModifiers[name] = def;
@@ -2461,7 +2470,7 @@ function parseClass(className) {
2461
2470
  }
2462
2471
  if (remaining.endsWith("]") && bracketStart === -1) {
2463
2472
  if (process.env.NODE_ENV !== "production") {
2464
- console.warn(`[kbach] Malformed class string: "${trimmed}" \u2014 unbalanced brackets`);
2473
+ kbachWarn(`Unbalanced brackets: "${trimmed}"`);
2465
2474
  }
2466
2475
  }
2467
2476
  if (remaining.startsWith("[") && remaining.endsWith("]")) {
@@ -2897,7 +2906,7 @@ function buildConfig(userConfig) {
2897
2906
  for (const [key, value] of Object.entries(ext)) {
2898
2907
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
2899
2908
  if (process.env.NODE_ENV !== "production") {
2900
- console.warn(`[kbach] config.extend.${key} must be a plain object, got ${typeof value}. Skipping.`);
2909
+ kbachWarn(`extend.${key} should be an object, got ${typeof value} \u2014 skipped`);
2901
2910
  }
2902
2911
  continue;
2903
2912
  }
@@ -2996,6 +3005,39 @@ function normPath(p) {
2996
3005
  return process2.platform === "win32" ? fwd.toLowerCase() : fwd;
2997
3006
  }
2998
3007
  setCSSGenMode(true);
3008
+ var useColor = !!process2.stdout?.isTTY && !process2.env.NO_COLOR;
3009
+ var paint = (code, s) => useColor ? `\x1B[${code}m${s}\x1B[0m` : s;
3010
+ var purple = (s) => paint("35", s);
3011
+ var yellow = (s) => paint("33", s);
3012
+ var dim = (s) => paint("2", s);
3013
+ var bold = (s) => paint("1", s);
3014
+ var TAG = () => `${bold(purple("[kbach]"))}`;
3015
+ function warn(message) {
3016
+ console.warn(`${TAG()} ${yellow(message)}`);
3017
+ }
3018
+ function warnBlock(header, ...lines) {
3019
+ console.warn([`${TAG()} ${yellow(header)}`, ...lines].join("\n"));
3020
+ }
3021
+ function findLineCol(code, needle) {
3022
+ const index = code.indexOf(needle);
3023
+ if (index === -1) return null;
3024
+ let line = 1;
3025
+ let lastNewline = -1;
3026
+ for (let i = 0; i < index; i++) {
3027
+ if (code.charCodeAt(i) === 10) {
3028
+ line++;
3029
+ lastNewline = i;
3030
+ }
3031
+ }
3032
+ return { line, column: index - lastNewline };
3033
+ }
3034
+ function warnAt(message, root, filePath, code, needle) {
3035
+ const pos = findLineCol(code, needle);
3036
+ const relPath = (0, import_path.relative)(root, filePath).replace(/\\/g, "/");
3037
+ const location = pos ? `${relPath}:${pos.line}:${pos.column}` : relPath;
3038
+ console.warn(`${TAG()} ${yellow(message)}
3039
+ ${dim("at")} ${location}`);
3040
+ }
2999
3041
  var KBACH_START = "/* kbach:start */";
3000
3042
  var KBACH_END = "/* kbach:end */";
3001
3043
  var GROUPS = [
@@ -3118,8 +3160,9 @@ function findKbachCSS(root) {
3118
3160
  if (found.length === 0) return null;
3119
3161
  found.sort((a, b) => a.split(/[\\/]/g).length - b.split(/[\\/]/g).length);
3120
3162
  if (found.length > 1) {
3121
- console.warn(
3122
- "[kbach] Multiple kbach.css files found \u2014 only the shallowest will receive generated styles.\n Remove duplicates to avoid confusion:\n" + found.map((f) => ` ${f}`).join("\n")
3163
+ warnBlock(
3164
+ `Multiple kbach.css found, using the shallowest \u2014 remove the rest:`,
3165
+ ...found.map((f) => dim(` ${f}`))
3123
3166
  );
3124
3167
  }
3125
3168
  return found[0];
@@ -3208,7 +3251,7 @@ function scanDir(dir, onFile) {
3208
3251
  entries = (0, import_fs.readdirSync)(dir);
3209
3252
  } catch (err) {
3210
3253
  if (err.code !== "ENOENT") {
3211
- console.warn(`[kbach] Cannot read directory ${dir}:`, err.message);
3254
+ warn(`Can't read ${dir}: ${err.message}`);
3212
3255
  }
3213
3256
  return;
3214
3257
  }
@@ -3220,7 +3263,7 @@ function scanDir(dir, onFile) {
3220
3263
  if (st.isDirectory()) scanDir(full, onFile);
3221
3264
  else if (/\.(tsx?|jsx?)$/.test(entry)) onFile(full, (0, import_fs.readFileSync)(full, "utf-8"));
3222
3265
  } catch (err) {
3223
- console.warn(`[kbach] Cannot process ${full}:`, err.message);
3266
+ warn(`Can't process ${full}: ${err.message}`);
3224
3267
  }
3225
3268
  }
3226
3269
  }
@@ -3272,16 +3315,14 @@ function kbach(userConfigOrOptions) {
3272
3315
  const tokenCSS = /* @__PURE__ */ new Map();
3273
3316
  const projectCssClasses = /* @__PURE__ */ new Set();
3274
3317
  const warnedTokens = /* @__PURE__ */ new Set();
3275
- function checkUnknownToken(tok) {
3318
+ function checkUnknownToken(tok, filePath, code) {
3276
3319
  if (process2.env.NODE_ENV === "production" || warnedTokens.has(tok) || tok.startsWith("__")) return;
3277
3320
  const parsed = parseClass(tok);
3278
3321
  if (parsed && resolveUtility(parsed, cfg.theme) !== null) return;
3279
3322
  const base = stripModifierPrefix(tok);
3280
3323
  if (!base || projectCssClasses.has(base)) return;
3281
3324
  warnedTokens.add(tok);
3282
- console.warn(
3283
- `[kbach] "${tok}" isn't a Kbach utility and no matching CSS rule was found anywhere in the project (checked .css/.scss/.sass/.less files) \u2014 possible typo? If this is intentional (a third-party class, one defined via CSS-in-JS, etc.), this warning is safe to ignore.`
3284
- );
3325
+ warnAt(`Unknown class "${tok}" \u2014 no Kbach utility or project CSS rule matches it. Typo?`, root, filePath, code, tok);
3285
3326
  }
3286
3327
  function processFile(filePath, code) {
3287
3328
  const key = normPath(filePath);
@@ -3291,7 +3332,7 @@ function kbach(userConfigOrOptions) {
3291
3332
  if (!tokenCSS.has(tok)) {
3292
3333
  tokenCSS.set(tok, generateClassCSS(tok, cfg.theme, cfg.darkMode, screens));
3293
3334
  }
3294
- checkUnknownToken(tok);
3335
+ checkUnknownToken(tok, filePath, code);
3295
3336
  }
3296
3337
  fileTokens.set(key, tokens);
3297
3338
  }
@@ -3323,8 +3364,10 @@ function kbach(userConfigOrOptions) {
3323
3364
  root = resolved.root;
3324
3365
  const names = new Set(resolved.plugins.map((p) => p.name));
3325
3366
  if (names.has("react-router") && names.has("vite:react-refresh")) {
3326
- console.warn(
3327
- `[kbach] Detected both @vitejs/plugin-react and @react-router/dev's reactRouter() plugin active together. reactRouter() already includes its own JSX transform and Fast Refresh integration \u2014 having both crashes the page at runtime with "Identifier 'RefreshRuntime' has already been declared" before React hydrates, which looks like every class silently failing to style. Remove the react() plugin from vite.config.ts; jsxImportSource in tsconfig.json is enough on its own.`
3367
+ warnBlock(
3368
+ "@vitejs/plugin-react + reactRouter() conflict \u2014 page will crash before styles apply.",
3369
+ dim(' Both inject Fast Refresh ("RefreshRuntime already declared").'),
3370
+ dim(" Fix: remove react() from vite.config.ts \u2014 tsconfig jsxImportSource is enough.")
3328
3371
  );
3329
3372
  }
3330
3373
  },
@@ -1,6 +1,6 @@
1
1
  // src/vite-plugin.ts
2
2
  import { readFileSync, writeFileSync, readdirSync, statSync } from "fs";
3
- import { join } from "path";
3
+ import { join, relative } from "path";
4
4
  import processImport from "process";
5
5
 
6
6
  // src/core/platform.ts
@@ -2273,6 +2273,15 @@ function getBuiltinStandaloneNames() {
2273
2273
  return _standaloneNames;
2274
2274
  }
2275
2275
 
2276
+ // src/core/devWarn.ts
2277
+ function kbachWarn(message) {
2278
+ console.warn(
2279
+ "%c[kbach]%c " + message,
2280
+ "color:#8b5cf6;font-weight:700",
2281
+ "color:inherit;font-weight:400"
2282
+ );
2283
+ }
2284
+
2276
2285
  // src/core/registry.ts
2277
2286
  var BUILTIN_MODIFIERS = {
2278
2287
  // ── Mode ─────────────────────────────────────────────────────────────────
@@ -2348,7 +2357,7 @@ function _invalidate() {
2348
2357
  }
2349
2358
  function registerModifier(name, def) {
2350
2359
  if (process.env.NODE_ENV !== "production" && name in BUILTIN_MODIFIERS) {
2351
- console.warn(`[kbach] registerModifier: "${name}" is a built-in modifier and cannot be overridden. Use a different name.`);
2360
+ kbachWarn(`"${name}" is built-in \u2014 pick another modifier name`);
2352
2361
  return;
2353
2362
  }
2354
2363
  _pluginModifiers[name] = def;
@@ -2427,7 +2436,7 @@ function parseClass(className) {
2427
2436
  }
2428
2437
  if (remaining.endsWith("]") && bracketStart === -1) {
2429
2438
  if (process.env.NODE_ENV !== "production") {
2430
- console.warn(`[kbach] Malformed class string: "${trimmed}" \u2014 unbalanced brackets`);
2439
+ kbachWarn(`Unbalanced brackets: "${trimmed}"`);
2431
2440
  }
2432
2441
  }
2433
2442
  if (remaining.startsWith("[") && remaining.endsWith("]")) {
@@ -2863,7 +2872,7 @@ function buildConfig(userConfig) {
2863
2872
  for (const [key, value] of Object.entries(ext)) {
2864
2873
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
2865
2874
  if (process.env.NODE_ENV !== "production") {
2866
- console.warn(`[kbach] config.extend.${key} must be a plain object, got ${typeof value}. Skipping.`);
2875
+ kbachWarn(`extend.${key} should be an object, got ${typeof value} \u2014 skipped`);
2867
2876
  }
2868
2877
  continue;
2869
2878
  }
@@ -2962,6 +2971,39 @@ function normPath(p) {
2962
2971
  return process2.platform === "win32" ? fwd.toLowerCase() : fwd;
2963
2972
  }
2964
2973
  setCSSGenMode(true);
2974
+ var useColor = !!process2.stdout?.isTTY && !process2.env.NO_COLOR;
2975
+ var paint = (code, s) => useColor ? `\x1B[${code}m${s}\x1B[0m` : s;
2976
+ var purple = (s) => paint("35", s);
2977
+ var yellow = (s) => paint("33", s);
2978
+ var dim = (s) => paint("2", s);
2979
+ var bold = (s) => paint("1", s);
2980
+ var TAG = () => `${bold(purple("[kbach]"))}`;
2981
+ function warn(message) {
2982
+ console.warn(`${TAG()} ${yellow(message)}`);
2983
+ }
2984
+ function warnBlock(header, ...lines) {
2985
+ console.warn([`${TAG()} ${yellow(header)}`, ...lines].join("\n"));
2986
+ }
2987
+ function findLineCol(code, needle) {
2988
+ const index = code.indexOf(needle);
2989
+ if (index === -1) return null;
2990
+ let line = 1;
2991
+ let lastNewline = -1;
2992
+ for (let i = 0; i < index; i++) {
2993
+ if (code.charCodeAt(i) === 10) {
2994
+ line++;
2995
+ lastNewline = i;
2996
+ }
2997
+ }
2998
+ return { line, column: index - lastNewline };
2999
+ }
3000
+ function warnAt(message, root, filePath, code, needle) {
3001
+ const pos = findLineCol(code, needle);
3002
+ const relPath = relative(root, filePath).replace(/\\/g, "/");
3003
+ const location = pos ? `${relPath}:${pos.line}:${pos.column}` : relPath;
3004
+ console.warn(`${TAG()} ${yellow(message)}
3005
+ ${dim("at")} ${location}`);
3006
+ }
2965
3007
  var KBACH_START = "/* kbach:start */";
2966
3008
  var KBACH_END = "/* kbach:end */";
2967
3009
  var GROUPS = [
@@ -3084,8 +3126,9 @@ function findKbachCSS(root) {
3084
3126
  if (found.length === 0) return null;
3085
3127
  found.sort((a, b) => a.split(/[\\/]/g).length - b.split(/[\\/]/g).length);
3086
3128
  if (found.length > 1) {
3087
- console.warn(
3088
- "[kbach] Multiple kbach.css files found \u2014 only the shallowest will receive generated styles.\n Remove duplicates to avoid confusion:\n" + found.map((f) => ` ${f}`).join("\n")
3129
+ warnBlock(
3130
+ `Multiple kbach.css found, using the shallowest \u2014 remove the rest:`,
3131
+ ...found.map((f) => dim(` ${f}`))
3089
3132
  );
3090
3133
  }
3091
3134
  return found[0];
@@ -3174,7 +3217,7 @@ function scanDir(dir, onFile) {
3174
3217
  entries = readdirSync(dir);
3175
3218
  } catch (err) {
3176
3219
  if (err.code !== "ENOENT") {
3177
- console.warn(`[kbach] Cannot read directory ${dir}:`, err.message);
3220
+ warn(`Can't read ${dir}: ${err.message}`);
3178
3221
  }
3179
3222
  return;
3180
3223
  }
@@ -3186,7 +3229,7 @@ function scanDir(dir, onFile) {
3186
3229
  if (st.isDirectory()) scanDir(full, onFile);
3187
3230
  else if (/\.(tsx?|jsx?)$/.test(entry)) onFile(full, readFileSync(full, "utf-8"));
3188
3231
  } catch (err) {
3189
- console.warn(`[kbach] Cannot process ${full}:`, err.message);
3232
+ warn(`Can't process ${full}: ${err.message}`);
3190
3233
  }
3191
3234
  }
3192
3235
  }
@@ -3238,16 +3281,14 @@ function kbach(userConfigOrOptions) {
3238
3281
  const tokenCSS = /* @__PURE__ */ new Map();
3239
3282
  const projectCssClasses = /* @__PURE__ */ new Set();
3240
3283
  const warnedTokens = /* @__PURE__ */ new Set();
3241
- function checkUnknownToken(tok) {
3284
+ function checkUnknownToken(tok, filePath, code) {
3242
3285
  if (process2.env.NODE_ENV === "production" || warnedTokens.has(tok) || tok.startsWith("__")) return;
3243
3286
  const parsed = parseClass(tok);
3244
3287
  if (parsed && resolveUtility(parsed, cfg.theme) !== null) return;
3245
3288
  const base = stripModifierPrefix(tok);
3246
3289
  if (!base || projectCssClasses.has(base)) return;
3247
3290
  warnedTokens.add(tok);
3248
- console.warn(
3249
- `[kbach] "${tok}" isn't a Kbach utility and no matching CSS rule was found anywhere in the project (checked .css/.scss/.sass/.less files) \u2014 possible typo? If this is intentional (a third-party class, one defined via CSS-in-JS, etc.), this warning is safe to ignore.`
3250
- );
3291
+ warnAt(`Unknown class "${tok}" \u2014 no Kbach utility or project CSS rule matches it. Typo?`, root, filePath, code, tok);
3251
3292
  }
3252
3293
  function processFile(filePath, code) {
3253
3294
  const key = normPath(filePath);
@@ -3257,7 +3298,7 @@ function kbach(userConfigOrOptions) {
3257
3298
  if (!tokenCSS.has(tok)) {
3258
3299
  tokenCSS.set(tok, generateClassCSS(tok, cfg.theme, cfg.darkMode, screens));
3259
3300
  }
3260
- checkUnknownToken(tok);
3301
+ checkUnknownToken(tok, filePath, code);
3261
3302
  }
3262
3303
  fileTokens.set(key, tokens);
3263
3304
  }
@@ -3289,8 +3330,10 @@ function kbach(userConfigOrOptions) {
3289
3330
  root = resolved.root;
3290
3331
  const names = new Set(resolved.plugins.map((p) => p.name));
3291
3332
  if (names.has("react-router") && names.has("vite:react-refresh")) {
3292
- console.warn(
3293
- `[kbach] Detected both @vitejs/plugin-react and @react-router/dev's reactRouter() plugin active together. reactRouter() already includes its own JSX transform and Fast Refresh integration \u2014 having both crashes the page at runtime with "Identifier 'RefreshRuntime' has already been declared" before React hydrates, which looks like every class silently failing to style. Remove the react() plugin from vite.config.ts; jsxImportSource in tsconfig.json is enough on its own.`
3333
+ warnBlock(
3334
+ "@vitejs/plugin-react + reactRouter() conflict \u2014 page will crash before styles apply.",
3335
+ dim(' Both inject Fast Refresh ("RefreshRuntime already declared").'),
3336
+ dim(" Fix: remove react() from vite.config.ts \u2014 tsconfig jsxImportSource is enough.")
3294
3337
  );
3295
3338
  }
3296
3339
  },
package/kbach-react.md CHANGED
@@ -67,7 +67,7 @@ The `kbach()` Vite plugin scans your source files and writes generated CSS betwe
67
67
  - Runs on `buildStart` (initial load) and on every HMR file change
68
68
  - Stateless: rescans all files fresh every time — removed classes are immediately evicted
69
69
  - Output is grouped by category with CSS custom properties for theme colors
70
- - Also indexes every `.css`/`.scss`/`.sass`/`.less` file the same `include` dirs cover, and warns (`console.warn`, dev-server terminal, not the browser) for any class that's neither a real Kbach utility NOR defined anywhere in those stylesheets — a likely typo. Classes intentionally handled elsewhere (CSS Modules, styled-components, a third-party component's own class) are recognized once anything in the project literally defines `.that-class-name` and stay silent.
70
+ - Also indexes every `.css`/`.scss`/`.sass`/`.less` file the same `include` dirs cover, and warns (`console.warn`, dev-server terminal, not the browser) for any class that's neither a real Kbach utility NOR defined anywhere in those stylesheets — a likely typo. Classes intentionally handled elsewhere (CSS Modules, styled-components, a third-party component's own class) are recognized once anything in the project literally defines `.that-class-name` and stay silent. Each warning prints a `file:line:column` location that terminals with clickable-link support (VS Code's included) turn into a jump-to-that-class link.
71
71
 
72
72
  ```ts
73
73
  // vite.config.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kbach/react",
3
- "version": "0.6.5",
3
+ "version": "0.6.6",
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",