@kbach/react 0.5.26 → 0.5.28
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/vite-plugin.d.mts +0 -15
- package/dist/vite-plugin.d.ts +0 -15
- package/dist/vite-plugin.js +89 -37
- package/dist/vite-plugin.mjs +89 -37
- package/package.json +1 -1
package/dist/vite-plugin.d.mts
CHANGED
|
@@ -1,21 +1,6 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
2
|
import { F as FrameworkConfig } from './types-CV5vXnSH.mjs';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* @kbach/react/vite
|
|
6
|
-
*
|
|
7
|
-
* Vite plugin — scans source files for Kbach className/kb props and generates CSS.
|
|
8
|
-
* Zero setup: just add kbach() to your plugins array.
|
|
9
|
-
* Only generates CSS for classes you actually use (like Tailwind).
|
|
10
|
-
*
|
|
11
|
-
* How it works (same as Tailwind):
|
|
12
|
-
* 1. Scans your source files at startup for className/kb props
|
|
13
|
-
* 2. Generates CSS rules for only those classes
|
|
14
|
-
* 3. Appends them to your imported CSS file (e.g. app.css) via Vite's transform
|
|
15
|
-
* 4. Vite serves the CSS through its normal pipeline → <link rel="stylesheet">
|
|
16
|
-
* 5. When you add a new class in dev, the CSS file is hot-reloaded
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
4
|
declare const VIRTUAL_ID = "virtual:kbach/styles.css";
|
|
20
5
|
declare function kbach(userConfig?: FrameworkConfig): Plugin;
|
|
21
6
|
|
package/dist/vite-plugin.d.ts
CHANGED
|
@@ -1,21 +1,6 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
2
|
import { F as FrameworkConfig } from './types-CV5vXnSH.js';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* @kbach/react/vite
|
|
6
|
-
*
|
|
7
|
-
* Vite plugin — scans source files for Kbach className/kb props and generates CSS.
|
|
8
|
-
* Zero setup: just add kbach() to your plugins array.
|
|
9
|
-
* Only generates CSS for classes you actually use (like Tailwind).
|
|
10
|
-
*
|
|
11
|
-
* How it works (same as Tailwind):
|
|
12
|
-
* 1. Scans your source files at startup for className/kb props
|
|
13
|
-
* 2. Generates CSS rules for only those classes
|
|
14
|
-
* 3. Appends them to your imported CSS file (e.g. app.css) via Vite's transform
|
|
15
|
-
* 4. Vite serves the CSS through its normal pipeline → <link rel="stylesheet">
|
|
16
|
-
* 5. When you add a new class in dev, the CSS file is hot-reloaded
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
4
|
declare const VIRTUAL_ID = "virtual:kbach/styles.css";
|
|
20
5
|
declare function kbach(userConfig?: FrameworkConfig): Plugin;
|
|
21
6
|
|
package/dist/vite-plugin.js
CHANGED
|
@@ -3352,14 +3352,89 @@ var VIRTUAL_ID = "virtual:kbach/styles.css";
|
|
|
3352
3352
|
var RESOLVED_ID = "\0virtual:kbach/styles.css";
|
|
3353
3353
|
var KBACH_START = "/* kbach:start */";
|
|
3354
3354
|
var KBACH_END = "/* kbach:end */";
|
|
3355
|
+
var GROUPS = [
|
|
3356
|
+
["Layout", /^(flex(?!-\d|-auto|-none|-initial|-1)|flex-(col|row|wrap|nowrap)|grid|block|inline|hidden|table|contents|overflow|items-|justify-|self-|place-|grow|shrink|float-|clear-|static|fixed|absolute|relative|sticky|z-|top-|right-|bottom-|left-|inset|aspect)/],
|
|
3357
|
+
["Sizing", /^(w-|h-|min-w-|max-w-|min-h-|max-h-|size-|flex-1|flex-auto|flex-none|flex-initial|basis-)/],
|
|
3358
|
+
["Spacing", /^(p-|px-|py-|pt-|pb-|pl-|pr-|m-|mx-|my-|mt-|mb-|ml-|mr-|gap-|space-)/],
|
|
3359
|
+
["Typography", /^(text-(xs|sm|base|lg|\dxl)|font-|leading-|tracking-|uppercase|lowercase|capitalize|normal-case|italic|not-italic|underline|overline|line-through|no-underline|truncate|whitespace-|text-(left|center|right|justify|start|end)|list-|align-|line-clamp)/],
|
|
3360
|
+
["Colors", /^(bg-|text-|color-|fill-|stroke-|caret-|accent-)/],
|
|
3361
|
+
["Borders", /^(border|rounded|ring|outline|divide)/],
|
|
3362
|
+
["Effects", /^(shadow|opacity|blur|brightness|contrast|grayscale|transition|duration|ease|delay|animate|scale|rotate|translate|transform|cursor|pointer|select-)/]
|
|
3363
|
+
];
|
|
3364
|
+
var GROUP_ORDER = ["Layout", "Sizing", "Spacing", "Typography", "Colors", "Borders", "Effects", "Utilities", "Dark Mode", "Responsive"];
|
|
3365
|
+
function classifyToken(token) {
|
|
3366
|
+
if (/^dark:/.test(token)) return "Dark Mode";
|
|
3367
|
+
if (/^(sm|md|lg|xl|2xl):/.test(token)) return "Responsive";
|
|
3368
|
+
const base = token.replace(/^(hover|focus|active|group-hover|peer-hover):/, "");
|
|
3369
|
+
for (const [label, re] of GROUPS) {
|
|
3370
|
+
if (re.test(base)) return label;
|
|
3371
|
+
}
|
|
3372
|
+
return "Utilities";
|
|
3373
|
+
}
|
|
3374
|
+
function buildColorVarMap(theme, usedCSS) {
|
|
3375
|
+
const map = /* @__PURE__ */ new Map();
|
|
3376
|
+
for (const [name, shades] of Object.entries(theme.colors ?? {})) {
|
|
3377
|
+
const entries = typeof shades === "string" ? [[name, shades]] : Object.entries(shades).map(([s, v]) => [`${name}-${s}`, v]);
|
|
3378
|
+
for (const [fullName, val] of entries) {
|
|
3379
|
+
if (typeof val === "string" && /^#[0-9a-fA-F]{3,8}$/.test(val) && usedCSS.includes(val)) {
|
|
3380
|
+
map.set(val, `--color-${fullName}`);
|
|
3381
|
+
}
|
|
3382
|
+
}
|
|
3383
|
+
}
|
|
3384
|
+
return map;
|
|
3385
|
+
}
|
|
3386
|
+
function formatKbachCSS(tokenCSS, theme) {
|
|
3387
|
+
const allRaw = [...tokenCSS.values()].filter(Boolean).join("\n");
|
|
3388
|
+
const colorVars = buildColorVarMap(theme, allRaw);
|
|
3389
|
+
function applyVars(css) {
|
|
3390
|
+
for (const [val, varName] of colorVars) {
|
|
3391
|
+
css = css.split(val).join(`var(${varName})`);
|
|
3392
|
+
}
|
|
3393
|
+
return css;
|
|
3394
|
+
}
|
|
3395
|
+
const groups = new Map(GROUP_ORDER.map((g) => [g, []]));
|
|
3396
|
+
for (const [token, css] of tokenCSS) {
|
|
3397
|
+
if (!css) continue;
|
|
3398
|
+
const label = classifyToken(token);
|
|
3399
|
+
if (!groups.has(label)) groups.set(label, []);
|
|
3400
|
+
groups.get(label).push(applyVars(css));
|
|
3401
|
+
}
|
|
3402
|
+
const out = ["/* Generated by Kbach \u2014 do not edit */"];
|
|
3403
|
+
if (colorVars.size > 0) {
|
|
3404
|
+
out.push("\n:root {");
|
|
3405
|
+
for (const [val, varName] of colorVars) {
|
|
3406
|
+
out.push(` ${varName}: ${val};`);
|
|
3407
|
+
}
|
|
3408
|
+
out.push("}");
|
|
3409
|
+
}
|
|
3410
|
+
const globalCSS = buildThemeGlobal(theme);
|
|
3411
|
+
if (globalCSS) {
|
|
3412
|
+
out.push(`
|
|
3413
|
+
/* Global */
|
|
3414
|
+
${globalCSS}`);
|
|
3415
|
+
}
|
|
3416
|
+
for (const label of GROUP_ORDER) {
|
|
3417
|
+
const items = groups.get(label) ?? [];
|
|
3418
|
+
if (!items.length) continue;
|
|
3419
|
+
out.push(`
|
|
3420
|
+
/* ${label} */`);
|
|
3421
|
+
out.push(items.join("\n"));
|
|
3422
|
+
}
|
|
3423
|
+
return out.join("\n");
|
|
3424
|
+
}
|
|
3425
|
+
function buildThemeGlobal(theme) {
|
|
3426
|
+
const ff = theme.fontFamily;
|
|
3427
|
+
if (!ff?.sans || ff.sans === "System") return "";
|
|
3428
|
+
const family = Array.isArray(ff.sans) ? ff.sans.join(", ") : ff.sans;
|
|
3429
|
+
return `body { font-family: ${family}; }`;
|
|
3430
|
+
}
|
|
3355
3431
|
function findMainCSSFile(root) {
|
|
3356
|
-
const
|
|
3432
|
+
for (const c of [
|
|
3357
3433
|
(0, import_path.join)(root, "app", "app.css"),
|
|
3358
3434
|
(0, import_path.join)(root, "src", "index.css"),
|
|
3359
3435
|
(0, import_path.join)(root, "src", "app.css"),
|
|
3360
3436
|
(0, import_path.join)(root, "app", "index.css")
|
|
3361
|
-
]
|
|
3362
|
-
for (const c of candidates) {
|
|
3437
|
+
]) {
|
|
3363
3438
|
if ((0, import_fs.existsSync)(c)) return c;
|
|
3364
3439
|
}
|
|
3365
3440
|
return null;
|
|
@@ -3393,11 +3468,8 @@ function scanDir(dir, onFile) {
|
|
|
3393
3468
|
const full = (0, import_path.join)(dir, entry);
|
|
3394
3469
|
try {
|
|
3395
3470
|
const st = (0, import_fs.statSync)(full);
|
|
3396
|
-
if (st.isDirectory())
|
|
3397
|
-
|
|
3398
|
-
} else if (/\.(tsx?|jsx?)$/.test(entry)) {
|
|
3399
|
-
onFile((0, import_fs.readFileSync)(full, "utf-8"));
|
|
3400
|
-
}
|
|
3471
|
+
if (st.isDirectory()) scanDir(full, onFile);
|
|
3472
|
+
else if (/\.(tsx?|jsx?)$/.test(entry)) onFile((0, import_fs.readFileSync)(full, "utf-8"));
|
|
3401
3473
|
} catch {
|
|
3402
3474
|
}
|
|
3403
3475
|
}
|
|
@@ -3414,12 +3486,6 @@ function extractClassStrings(code) {
|
|
|
3414
3486
|
}
|
|
3415
3487
|
return found;
|
|
3416
3488
|
}
|
|
3417
|
-
function buildGlobalCSS(theme) {
|
|
3418
|
-
const ff = theme.fontFamily;
|
|
3419
|
-
if (!ff?.sans || ff.sans === "System") return "";
|
|
3420
|
-
const family = Array.isArray(ff.sans) ? ff.sans.join(", ") : ff.sans;
|
|
3421
|
-
return `body { font-family: ${family}; }`;
|
|
3422
|
-
}
|
|
3423
3489
|
function kbach(userConfig) {
|
|
3424
3490
|
const cfg = buildConfig(userConfig ?? {});
|
|
3425
3491
|
const screens = toNumericScreens(cfg.theme.screens ?? {});
|
|
@@ -3433,21 +3499,13 @@ function kbach(userConfig) {
|
|
|
3433
3499
|
}
|
|
3434
3500
|
function extractFromCode(code) {
|
|
3435
3501
|
let added = false;
|
|
3436
|
-
for (const cls of extractClassStrings(code))
|
|
3437
|
-
for (const token of cls.split(/\s+/).filter(Boolean))
|
|
3502
|
+
for (const cls of extractClassStrings(code))
|
|
3503
|
+
for (const token of cls.split(/\s+/).filter(Boolean))
|
|
3438
3504
|
if (processToken(token)) added = true;
|
|
3439
|
-
}
|
|
3440
|
-
}
|
|
3441
3505
|
return added;
|
|
3442
3506
|
}
|
|
3443
|
-
function
|
|
3444
|
-
|
|
3445
|
-
const global = buildGlobalCSS(cfg.theme);
|
|
3446
|
-
if (global) parts.push(global);
|
|
3447
|
-
for (const css of tokenCSS.values()) {
|
|
3448
|
-
if (css) parts.push(css);
|
|
3449
|
-
}
|
|
3450
|
-
return parts.join("\n");
|
|
3507
|
+
function getFormattedCSS() {
|
|
3508
|
+
return formatKbachCSS(tokenCSS, cfg.theme);
|
|
3451
3509
|
}
|
|
3452
3510
|
return {
|
|
3453
3511
|
name: "kbach",
|
|
@@ -3456,33 +3514,27 @@ function kbach(userConfig) {
|
|
|
3456
3514
|
root = resolved.root;
|
|
3457
3515
|
},
|
|
3458
3516
|
buildStart() {
|
|
3459
|
-
for (const dir of ["src", "app", "pages", "components", "views", "layouts"])
|
|
3517
|
+
for (const dir of ["src", "app", "pages", "components", "views", "layouts"])
|
|
3460
3518
|
scanDir((0, import_path.join)(root, dir), (code) => extractFromCode(code));
|
|
3461
|
-
}
|
|
3462
3519
|
mainCSSFile = findMainCSSFile(root);
|
|
3463
|
-
if (mainCSSFile) writeKbachToFile(mainCSSFile,
|
|
3520
|
+
if (mainCSSFile) writeKbachToFile(mainCSSFile, getFormattedCSS());
|
|
3464
3521
|
},
|
|
3465
3522
|
transform(code, id) {
|
|
3466
3523
|
const cleanId = id.split("?")[0];
|
|
3467
|
-
if (/\.(tsx?|jsx?)$/.test(cleanId) && !id.includes("node_modules"))
|
|
3524
|
+
if (/\.(tsx?|jsx?)$/.test(cleanId) && !id.includes("node_modules"))
|
|
3468
3525
|
extractFromCode(code);
|
|
3469
|
-
}
|
|
3470
3526
|
},
|
|
3471
|
-
// ── Virtual module (opt-in explicit import) ───────────────────────────────
|
|
3472
3527
|
resolveId(id) {
|
|
3473
3528
|
if (id === VIRTUAL_ID) return RESOLVED_ID;
|
|
3474
3529
|
},
|
|
3475
3530
|
load(id) {
|
|
3476
|
-
if (id === RESOLVED_ID) return
|
|
3531
|
+
if (id === RESOLVED_ID) return getFormattedCSS();
|
|
3477
3532
|
},
|
|
3478
|
-
// ── HMR: write updated CSS to file when new classes are found ────────────
|
|
3479
|
-
// Vite's own file watcher detects the change and hot-reloads the CSS —
|
|
3480
|
-
// no manual invalidation needed.
|
|
3481
3533
|
handleHotUpdate({ file }) {
|
|
3482
3534
|
if (file.includes("node_modules") || !/\.(tsx?|jsx?)$/.test(file)) return;
|
|
3483
3535
|
try {
|
|
3484
3536
|
const added = extractFromCode((0, import_fs.readFileSync)(file, "utf-8"));
|
|
3485
|
-
if (added && mainCSSFile) writeKbachToFile(mainCSSFile,
|
|
3537
|
+
if (added && mainCSSFile) writeKbachToFile(mainCSSFile, getFormattedCSS());
|
|
3486
3538
|
} catch {
|
|
3487
3539
|
}
|
|
3488
3540
|
}
|
package/dist/vite-plugin.mjs
CHANGED
|
@@ -10,14 +10,89 @@ var VIRTUAL_ID = "virtual:kbach/styles.css";
|
|
|
10
10
|
var RESOLVED_ID = "\0virtual:kbach/styles.css";
|
|
11
11
|
var KBACH_START = "/* kbach:start */";
|
|
12
12
|
var KBACH_END = "/* kbach:end */";
|
|
13
|
+
var GROUPS = [
|
|
14
|
+
["Layout", /^(flex(?!-\d|-auto|-none|-initial|-1)|flex-(col|row|wrap|nowrap)|grid|block|inline|hidden|table|contents|overflow|items-|justify-|self-|place-|grow|shrink|float-|clear-|static|fixed|absolute|relative|sticky|z-|top-|right-|bottom-|left-|inset|aspect)/],
|
|
15
|
+
["Sizing", /^(w-|h-|min-w-|max-w-|min-h-|max-h-|size-|flex-1|flex-auto|flex-none|flex-initial|basis-)/],
|
|
16
|
+
["Spacing", /^(p-|px-|py-|pt-|pb-|pl-|pr-|m-|mx-|my-|mt-|mb-|ml-|mr-|gap-|space-)/],
|
|
17
|
+
["Typography", /^(text-(xs|sm|base|lg|\dxl)|font-|leading-|tracking-|uppercase|lowercase|capitalize|normal-case|italic|not-italic|underline|overline|line-through|no-underline|truncate|whitespace-|text-(left|center|right|justify|start|end)|list-|align-|line-clamp)/],
|
|
18
|
+
["Colors", /^(bg-|text-|color-|fill-|stroke-|caret-|accent-)/],
|
|
19
|
+
["Borders", /^(border|rounded|ring|outline|divide)/],
|
|
20
|
+
["Effects", /^(shadow|opacity|blur|brightness|contrast|grayscale|transition|duration|ease|delay|animate|scale|rotate|translate|transform|cursor|pointer|select-)/]
|
|
21
|
+
];
|
|
22
|
+
var GROUP_ORDER = ["Layout", "Sizing", "Spacing", "Typography", "Colors", "Borders", "Effects", "Utilities", "Dark Mode", "Responsive"];
|
|
23
|
+
function classifyToken(token) {
|
|
24
|
+
if (/^dark:/.test(token)) return "Dark Mode";
|
|
25
|
+
if (/^(sm|md|lg|xl|2xl):/.test(token)) return "Responsive";
|
|
26
|
+
const base = token.replace(/^(hover|focus|active|group-hover|peer-hover):/, "");
|
|
27
|
+
for (const [label, re] of GROUPS) {
|
|
28
|
+
if (re.test(base)) return label;
|
|
29
|
+
}
|
|
30
|
+
return "Utilities";
|
|
31
|
+
}
|
|
32
|
+
function buildColorVarMap(theme, usedCSS) {
|
|
33
|
+
const map = /* @__PURE__ */ new Map();
|
|
34
|
+
for (const [name, shades] of Object.entries(theme.colors ?? {})) {
|
|
35
|
+
const entries = typeof shades === "string" ? [[name, shades]] : Object.entries(shades).map(([s, v]) => [`${name}-${s}`, v]);
|
|
36
|
+
for (const [fullName, val] of entries) {
|
|
37
|
+
if (typeof val === "string" && /^#[0-9a-fA-F]{3,8}$/.test(val) && usedCSS.includes(val)) {
|
|
38
|
+
map.set(val, `--color-${fullName}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return map;
|
|
43
|
+
}
|
|
44
|
+
function formatKbachCSS(tokenCSS, theme) {
|
|
45
|
+
const allRaw = [...tokenCSS.values()].filter(Boolean).join("\n");
|
|
46
|
+
const colorVars = buildColorVarMap(theme, allRaw);
|
|
47
|
+
function applyVars(css) {
|
|
48
|
+
for (const [val, varName] of colorVars) {
|
|
49
|
+
css = css.split(val).join(`var(${varName})`);
|
|
50
|
+
}
|
|
51
|
+
return css;
|
|
52
|
+
}
|
|
53
|
+
const groups = new Map(GROUP_ORDER.map((g) => [g, []]));
|
|
54
|
+
for (const [token, css] of tokenCSS) {
|
|
55
|
+
if (!css) continue;
|
|
56
|
+
const label = classifyToken(token);
|
|
57
|
+
if (!groups.has(label)) groups.set(label, []);
|
|
58
|
+
groups.get(label).push(applyVars(css));
|
|
59
|
+
}
|
|
60
|
+
const out = ["/* Generated by Kbach \u2014 do not edit */"];
|
|
61
|
+
if (colorVars.size > 0) {
|
|
62
|
+
out.push("\n:root {");
|
|
63
|
+
for (const [val, varName] of colorVars) {
|
|
64
|
+
out.push(` ${varName}: ${val};`);
|
|
65
|
+
}
|
|
66
|
+
out.push("}");
|
|
67
|
+
}
|
|
68
|
+
const globalCSS = buildThemeGlobal(theme);
|
|
69
|
+
if (globalCSS) {
|
|
70
|
+
out.push(`
|
|
71
|
+
/* Global */
|
|
72
|
+
${globalCSS}`);
|
|
73
|
+
}
|
|
74
|
+
for (const label of GROUP_ORDER) {
|
|
75
|
+
const items = groups.get(label) ?? [];
|
|
76
|
+
if (!items.length) continue;
|
|
77
|
+
out.push(`
|
|
78
|
+
/* ${label} */`);
|
|
79
|
+
out.push(items.join("\n"));
|
|
80
|
+
}
|
|
81
|
+
return out.join("\n");
|
|
82
|
+
}
|
|
83
|
+
function buildThemeGlobal(theme) {
|
|
84
|
+
const ff = theme.fontFamily;
|
|
85
|
+
if (!ff?.sans || ff.sans === "System") return "";
|
|
86
|
+
const family = Array.isArray(ff.sans) ? ff.sans.join(", ") : ff.sans;
|
|
87
|
+
return `body { font-family: ${family}; }`;
|
|
88
|
+
}
|
|
13
89
|
function findMainCSSFile(root) {
|
|
14
|
-
const
|
|
90
|
+
for (const c of [
|
|
15
91
|
join(root, "app", "app.css"),
|
|
16
92
|
join(root, "src", "index.css"),
|
|
17
93
|
join(root, "src", "app.css"),
|
|
18
94
|
join(root, "app", "index.css")
|
|
19
|
-
]
|
|
20
|
-
for (const c of candidates) {
|
|
95
|
+
]) {
|
|
21
96
|
if (existsSync(c)) return c;
|
|
22
97
|
}
|
|
23
98
|
return null;
|
|
@@ -51,11 +126,8 @@ function scanDir(dir, onFile) {
|
|
|
51
126
|
const full = join(dir, entry);
|
|
52
127
|
try {
|
|
53
128
|
const st = statSync(full);
|
|
54
|
-
if (st.isDirectory())
|
|
55
|
-
|
|
56
|
-
} else if (/\.(tsx?|jsx?)$/.test(entry)) {
|
|
57
|
-
onFile(readFileSync(full, "utf-8"));
|
|
58
|
-
}
|
|
129
|
+
if (st.isDirectory()) scanDir(full, onFile);
|
|
130
|
+
else if (/\.(tsx?|jsx?)$/.test(entry)) onFile(readFileSync(full, "utf-8"));
|
|
59
131
|
} catch {
|
|
60
132
|
}
|
|
61
133
|
}
|
|
@@ -72,12 +144,6 @@ function extractClassStrings(code) {
|
|
|
72
144
|
}
|
|
73
145
|
return found;
|
|
74
146
|
}
|
|
75
|
-
function buildGlobalCSS(theme) {
|
|
76
|
-
const ff = theme.fontFamily;
|
|
77
|
-
if (!ff?.sans || ff.sans === "System") return "";
|
|
78
|
-
const family = Array.isArray(ff.sans) ? ff.sans.join(", ") : ff.sans;
|
|
79
|
-
return `body { font-family: ${family}; }`;
|
|
80
|
-
}
|
|
81
147
|
function kbach(userConfig) {
|
|
82
148
|
const cfg = buildConfig(userConfig ?? {});
|
|
83
149
|
const screens = toNumericScreens(cfg.theme.screens ?? {});
|
|
@@ -91,21 +157,13 @@ function kbach(userConfig) {
|
|
|
91
157
|
}
|
|
92
158
|
function extractFromCode(code) {
|
|
93
159
|
let added = false;
|
|
94
|
-
for (const cls of extractClassStrings(code))
|
|
95
|
-
for (const token of cls.split(/\s+/).filter(Boolean))
|
|
160
|
+
for (const cls of extractClassStrings(code))
|
|
161
|
+
for (const token of cls.split(/\s+/).filter(Boolean))
|
|
96
162
|
if (processToken(token)) added = true;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
163
|
return added;
|
|
100
164
|
}
|
|
101
|
-
function
|
|
102
|
-
|
|
103
|
-
const global = buildGlobalCSS(cfg.theme);
|
|
104
|
-
if (global) parts.push(global);
|
|
105
|
-
for (const css of tokenCSS.values()) {
|
|
106
|
-
if (css) parts.push(css);
|
|
107
|
-
}
|
|
108
|
-
return parts.join("\n");
|
|
165
|
+
function getFormattedCSS() {
|
|
166
|
+
return formatKbachCSS(tokenCSS, cfg.theme);
|
|
109
167
|
}
|
|
110
168
|
return {
|
|
111
169
|
name: "kbach",
|
|
@@ -114,33 +172,27 @@ function kbach(userConfig) {
|
|
|
114
172
|
root = resolved.root;
|
|
115
173
|
},
|
|
116
174
|
buildStart() {
|
|
117
|
-
for (const dir of ["src", "app", "pages", "components", "views", "layouts"])
|
|
175
|
+
for (const dir of ["src", "app", "pages", "components", "views", "layouts"])
|
|
118
176
|
scanDir(join(root, dir), (code) => extractFromCode(code));
|
|
119
|
-
}
|
|
120
177
|
mainCSSFile = findMainCSSFile(root);
|
|
121
|
-
if (mainCSSFile) writeKbachToFile(mainCSSFile,
|
|
178
|
+
if (mainCSSFile) writeKbachToFile(mainCSSFile, getFormattedCSS());
|
|
122
179
|
},
|
|
123
180
|
transform(code, id) {
|
|
124
181
|
const cleanId = id.split("?")[0];
|
|
125
|
-
if (/\.(tsx?|jsx?)$/.test(cleanId) && !id.includes("node_modules"))
|
|
182
|
+
if (/\.(tsx?|jsx?)$/.test(cleanId) && !id.includes("node_modules"))
|
|
126
183
|
extractFromCode(code);
|
|
127
|
-
}
|
|
128
184
|
},
|
|
129
|
-
// ── Virtual module (opt-in explicit import) ───────────────────────────────
|
|
130
185
|
resolveId(id) {
|
|
131
186
|
if (id === VIRTUAL_ID) return RESOLVED_ID;
|
|
132
187
|
},
|
|
133
188
|
load(id) {
|
|
134
|
-
if (id === RESOLVED_ID) return
|
|
189
|
+
if (id === RESOLVED_ID) return getFormattedCSS();
|
|
135
190
|
},
|
|
136
|
-
// ── HMR: write updated CSS to file when new classes are found ────────────
|
|
137
|
-
// Vite's own file watcher detects the change and hot-reloads the CSS —
|
|
138
|
-
// no manual invalidation needed.
|
|
139
191
|
handleHotUpdate({ file }) {
|
|
140
192
|
if (file.includes("node_modules") || !/\.(tsx?|jsx?)$/.test(file)) return;
|
|
141
193
|
try {
|
|
142
194
|
const added = extractFromCode(readFileSync(file, "utf-8"));
|
|
143
|
-
if (added && mainCSSFile) writeKbachToFile(mainCSSFile,
|
|
195
|
+
if (added && mainCSSFile) writeKbachToFile(mainCSSFile, getFormattedCSS());
|
|
144
196
|
} catch {
|
|
145
197
|
}
|
|
146
198
|
}
|