@kbach/react 0.5.23 → 0.5.25
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 +7 -0
- package/dist/vite-plugin.d.ts +7 -0
- package/dist/vite-plugin.js +46 -90
- package/dist/vite-plugin.mjs +47 -91
- package/package.json +1 -1
package/dist/vite-plugin.d.mts
CHANGED
|
@@ -7,6 +7,13 @@ import { F as FrameworkConfig } from './types-CV5vXnSH.mjs';
|
|
|
7
7
|
* Vite plugin — scans source files for Kbach className/kb props and generates CSS.
|
|
8
8
|
* Zero setup: just add kbach() to your plugins array.
|
|
9
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
|
|
10
17
|
*/
|
|
11
18
|
|
|
12
19
|
declare const VIRTUAL_ID = "virtual:kbach/styles.css";
|
package/dist/vite-plugin.d.ts
CHANGED
|
@@ -7,6 +7,13 @@ import { F as FrameworkConfig } from './types-CV5vXnSH.js';
|
|
|
7
7
|
* Vite plugin — scans source files for Kbach className/kb props and generates CSS.
|
|
8
8
|
* Zero setup: just add kbach() to your plugins array.
|
|
9
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
|
|
10
17
|
*/
|
|
11
18
|
|
|
12
19
|
declare const VIRTUAL_ID = "virtual:kbach/styles.css";
|
package/dist/vite-plugin.js
CHANGED
|
@@ -3350,6 +3350,35 @@ var customVariants = new Proxy(
|
|
|
3350
3350
|
// src/vite-plugin.ts
|
|
3351
3351
|
var VIRTUAL_ID = "virtual:kbach/styles.css";
|
|
3352
3352
|
var RESOLVED_ID = "\0virtual:kbach/styles.css";
|
|
3353
|
+
var KBACH_START = "/* kbach:start */";
|
|
3354
|
+
var KBACH_END = "/* kbach:end */";
|
|
3355
|
+
function findMainCSSFile(root) {
|
|
3356
|
+
const candidates = [
|
|
3357
|
+
(0, import_path.join)(root, "app", "app.css"),
|
|
3358
|
+
(0, import_path.join)(root, "src", "index.css"),
|
|
3359
|
+
(0, import_path.join)(root, "src", "app.css"),
|
|
3360
|
+
(0, import_path.join)(root, "app", "index.css")
|
|
3361
|
+
];
|
|
3362
|
+
for (const c of candidates) {
|
|
3363
|
+
if ((0, import_fs.existsSync)(c)) return c;
|
|
3364
|
+
}
|
|
3365
|
+
return null;
|
|
3366
|
+
}
|
|
3367
|
+
function writeKbachToFile(filePath, css) {
|
|
3368
|
+
let existing = "";
|
|
3369
|
+
try {
|
|
3370
|
+
existing = (0, import_fs.readFileSync)(filePath, "utf-8");
|
|
3371
|
+
} catch {
|
|
3372
|
+
}
|
|
3373
|
+
const block = `${KBACH_START}
|
|
3374
|
+
${css}
|
|
3375
|
+
${KBACH_END}`;
|
|
3376
|
+
const re = new RegExp(`${KBACH_START}[\\s\\S]*?${KBACH_END}`);
|
|
3377
|
+
const next = existing.includes(KBACH_START) ? existing.replace(re, block) : existing ? `${existing}
|
|
3378
|
+
|
|
3379
|
+
${block}` : block;
|
|
3380
|
+
if (next !== existing) (0, import_fs.writeFileSync)(filePath, next, "utf-8");
|
|
3381
|
+
}
|
|
3353
3382
|
function toNumericScreens(screens) {
|
|
3354
3383
|
const out = {};
|
|
3355
3384
|
for (const [k, v] of Object.entries(screens)) {
|
|
@@ -3397,19 +3426,18 @@ function kbach(userConfig) {
|
|
|
3397
3426
|
const tokenCSS = /* @__PURE__ */ new Map();
|
|
3398
3427
|
let root = process.cwd();
|
|
3399
3428
|
let server = null;
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
added = true;
|
|
3406
|
-
}
|
|
3407
|
-
return added;
|
|
3429
|
+
let mainCSSFile = null;
|
|
3430
|
+
function processToken(token) {
|
|
3431
|
+
if (tokenCSS.has(token)) return false;
|
|
3432
|
+
tokenCSS.set(token, generateClassCSS(token, cfg.theme, cfg.darkMode, screens));
|
|
3433
|
+
return true;
|
|
3408
3434
|
}
|
|
3409
3435
|
function extractFromCode(code) {
|
|
3410
3436
|
let added = false;
|
|
3411
3437
|
for (const cls of extractClassStrings(code)) {
|
|
3412
|
-
|
|
3438
|
+
for (const token of cls.split(/\s+/).filter(Boolean)) {
|
|
3439
|
+
if (processToken(token)) added = true;
|
|
3440
|
+
}
|
|
3413
3441
|
}
|
|
3414
3442
|
return added;
|
|
3415
3443
|
}
|
|
@@ -3422,96 +3450,24 @@ function kbach(userConfig) {
|
|
|
3422
3450
|
}
|
|
3423
3451
|
return parts.join("\n");
|
|
3424
3452
|
}
|
|
3425
|
-
function injectIntoHtml(html) {
|
|
3426
|
-
const css = getAllCSS();
|
|
3427
|
-
if (!css || html.includes("data-kbach")) return html;
|
|
3428
|
-
return html.replace("</head>", `<style data-kbach>${css}</style></head>`);
|
|
3429
|
-
}
|
|
3430
3453
|
return {
|
|
3431
3454
|
name: "kbach",
|
|
3432
3455
|
enforce: "pre",
|
|
3433
3456
|
configResolved(resolved) {
|
|
3434
3457
|
root = resolved.root;
|
|
3435
3458
|
},
|
|
3459
|
+
configureServer(s) {
|
|
3460
|
+
server = s;
|
|
3461
|
+
},
|
|
3436
3462
|
buildStart() {
|
|
3437
3463
|
for (const dir of ["src", "app", "pages", "components", "views", "layouts"]) {
|
|
3438
3464
|
scanDir((0, import_path.join)(root, dir), (code) => extractFromCode(code));
|
|
3439
3465
|
}
|
|
3466
|
+
mainCSSFile = findMainCSSFile(root);
|
|
3467
|
+
if (mainCSSFile) writeKbachToFile(mainCSSFile, getAllCSS());
|
|
3440
3468
|
},
|
|
3441
|
-
// ── DEV: intercept every HTML response and inject <style data-kbach> ──────
|
|
3442
|
-
// React Router v8 uses chunked streaming SSR — </head> comes in an early
|
|
3443
|
-
// res.write() chunk, not res.end(). Buffer all writes, concatenate at end,
|
|
3444
|
-
// inject CSS into the full HTML, then send in one go.
|
|
3445
|
-
configureServer(s) {
|
|
3446
|
-
server = s;
|
|
3447
|
-
s.middlewares.use((req, res, next) => {
|
|
3448
|
-
const url = req.url ?? "";
|
|
3449
|
-
if (/\.(js|ts|jsx|tsx|css|png|jpg|svg|ico|woff2?|ttf|json|map)(\?|$)/.test(url) || url.startsWith("/@") || url.startsWith("/__")) {
|
|
3450
|
-
return next();
|
|
3451
|
-
}
|
|
3452
|
-
const origWrite = res.write.bind(res);
|
|
3453
|
-
const origEnd = res.end.bind(res);
|
|
3454
|
-
const chunks = [];
|
|
3455
|
-
let done = false;
|
|
3456
|
-
function toBuffer(chunk, enc) {
|
|
3457
|
-
if (chunk == null) return Buffer.alloc(0);
|
|
3458
|
-
if (typeof chunk === "string") {
|
|
3459
|
-
return Buffer.from(chunk, typeof enc === "string" ? enc : "utf-8");
|
|
3460
|
-
}
|
|
3461
|
-
if (ArrayBuffer.isView(chunk)) {
|
|
3462
|
-
return Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength);
|
|
3463
|
-
}
|
|
3464
|
-
if (chunk instanceof ArrayBuffer) return Buffer.from(chunk);
|
|
3465
|
-
return Buffer.from(String(chunk));
|
|
3466
|
-
}
|
|
3467
|
-
res.write = function(chunk, enc, cb) {
|
|
3468
|
-
if (done) return origWrite(chunk, enc, cb);
|
|
3469
|
-
if (chunk != null) chunks.push(toBuffer(chunk, enc));
|
|
3470
|
-
if (typeof enc === "function") enc();
|
|
3471
|
-
else if (typeof cb === "function") cb();
|
|
3472
|
-
return true;
|
|
3473
|
-
};
|
|
3474
|
-
res.end = function(chunk, enc, cb) {
|
|
3475
|
-
done = true;
|
|
3476
|
-
if (typeof chunk === "function") {
|
|
3477
|
-
cb = chunk;
|
|
3478
|
-
chunk = null;
|
|
3479
|
-
enc = null;
|
|
3480
|
-
} else if (typeof enc === "function") {
|
|
3481
|
-
cb = enc;
|
|
3482
|
-
enc = null;
|
|
3483
|
-
}
|
|
3484
|
-
if (chunk != null) chunks.push(toBuffer(chunk, enc));
|
|
3485
|
-
if (chunks.length > 0) {
|
|
3486
|
-
const html = Buffer.concat(chunks).toString("utf-8");
|
|
3487
|
-
if (html.includes("</head>")) {
|
|
3488
|
-
const injected = injectIntoHtml(html);
|
|
3489
|
-
const buf = Buffer.from(injected, "utf-8");
|
|
3490
|
-
try {
|
|
3491
|
-
res.removeHeader("transfer-encoding");
|
|
3492
|
-
} catch {
|
|
3493
|
-
}
|
|
3494
|
-
try {
|
|
3495
|
-
res.setHeader("content-length", buf.length);
|
|
3496
|
-
} catch {
|
|
3497
|
-
}
|
|
3498
|
-
return origEnd(buf, cb);
|
|
3499
|
-
}
|
|
3500
|
-
return origEnd(Buffer.concat(chunks), enc, cb);
|
|
3501
|
-
}
|
|
3502
|
-
return origEnd(void 0, enc, cb);
|
|
3503
|
-
};
|
|
3504
|
-
next();
|
|
3505
|
-
});
|
|
3506
|
-
},
|
|
3507
|
-
// ── PROD: append to CSS files so they land in the extracted CSS bundle ────
|
|
3508
3469
|
transform(code, id) {
|
|
3509
3470
|
const cleanId = id.split("?")[0];
|
|
3510
|
-
if (cleanId.endsWith(".css") && !id.includes("node_modules") && !id.includes("\0")) {
|
|
3511
|
-
const kbachCSS = getAllCSS();
|
|
3512
|
-
if (!kbachCSS) return null;
|
|
3513
|
-
return { code: code + "\n/* kbach */\n" + kbachCSS, map: null };
|
|
3514
|
-
}
|
|
3515
3471
|
if (/\.(tsx?|jsx?)$/.test(cleanId) && !id.includes("node_modules")) {
|
|
3516
3472
|
extractFromCode(code);
|
|
3517
3473
|
}
|
|
@@ -3523,14 +3479,14 @@ function kbach(userConfig) {
|
|
|
3523
3479
|
load(id) {
|
|
3524
3480
|
if (id === RESOLVED_ID) return getAllCSS();
|
|
3525
3481
|
},
|
|
3526
|
-
// ── HMR:
|
|
3482
|
+
// ── HMR: write updated CSS to file when new classes are found ────────────
|
|
3483
|
+
// Vite's own file watcher detects the change and hot-reloads the CSS —
|
|
3484
|
+
// no manual invalidation needed.
|
|
3527
3485
|
handleHotUpdate({ file }) {
|
|
3528
3486
|
if (file.includes("node_modules") || !/\.(tsx?|jsx?)$/.test(file)) return;
|
|
3529
3487
|
try {
|
|
3530
3488
|
const added = extractFromCode((0, import_fs.readFileSync)(file, "utf-8"));
|
|
3531
|
-
if (added &&
|
|
3532
|
-
(server.hot ?? server.ws)?.send({ type: "full-reload" });
|
|
3533
|
-
}
|
|
3489
|
+
if (added && mainCSSFile) writeKbachToFile(mainCSSFile, getAllCSS());
|
|
3534
3490
|
} catch {
|
|
3535
3491
|
}
|
|
3536
3492
|
}
|
package/dist/vite-plugin.mjs
CHANGED
|
@@ -4,10 +4,39 @@ import {
|
|
|
4
4
|
} from "./chunk-SOBLKZGE.mjs";
|
|
5
5
|
|
|
6
6
|
// src/vite-plugin.ts
|
|
7
|
-
import { readFileSync, readdirSync, statSync } from "fs";
|
|
7
|
+
import { readFileSync, writeFileSync, existsSync, readdirSync, statSync } from "fs";
|
|
8
8
|
import { join } from "path";
|
|
9
9
|
var VIRTUAL_ID = "virtual:kbach/styles.css";
|
|
10
10
|
var RESOLVED_ID = "\0virtual:kbach/styles.css";
|
|
11
|
+
var KBACH_START = "/* kbach:start */";
|
|
12
|
+
var KBACH_END = "/* kbach:end */";
|
|
13
|
+
function findMainCSSFile(root) {
|
|
14
|
+
const candidates = [
|
|
15
|
+
join(root, "app", "app.css"),
|
|
16
|
+
join(root, "src", "index.css"),
|
|
17
|
+
join(root, "src", "app.css"),
|
|
18
|
+
join(root, "app", "index.css")
|
|
19
|
+
];
|
|
20
|
+
for (const c of candidates) {
|
|
21
|
+
if (existsSync(c)) return c;
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
function writeKbachToFile(filePath, css) {
|
|
26
|
+
let existing = "";
|
|
27
|
+
try {
|
|
28
|
+
existing = readFileSync(filePath, "utf-8");
|
|
29
|
+
} catch {
|
|
30
|
+
}
|
|
31
|
+
const block = `${KBACH_START}
|
|
32
|
+
${css}
|
|
33
|
+
${KBACH_END}`;
|
|
34
|
+
const re = new RegExp(`${KBACH_START}[\\s\\S]*?${KBACH_END}`);
|
|
35
|
+
const next = existing.includes(KBACH_START) ? existing.replace(re, block) : existing ? `${existing}
|
|
36
|
+
|
|
37
|
+
${block}` : block;
|
|
38
|
+
if (next !== existing) writeFileSync(filePath, next, "utf-8");
|
|
39
|
+
}
|
|
11
40
|
function toNumericScreens(screens) {
|
|
12
41
|
const out = {};
|
|
13
42
|
for (const [k, v] of Object.entries(screens)) {
|
|
@@ -55,19 +84,18 @@ function kbach(userConfig) {
|
|
|
55
84
|
const tokenCSS = /* @__PURE__ */ new Map();
|
|
56
85
|
let root = process.cwd();
|
|
57
86
|
let server = null;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
added = true;
|
|
64
|
-
}
|
|
65
|
-
return added;
|
|
87
|
+
let mainCSSFile = null;
|
|
88
|
+
function processToken(token) {
|
|
89
|
+
if (tokenCSS.has(token)) return false;
|
|
90
|
+
tokenCSS.set(token, generateClassCSS(token, cfg.theme, cfg.darkMode, screens));
|
|
91
|
+
return true;
|
|
66
92
|
}
|
|
67
93
|
function extractFromCode(code) {
|
|
68
94
|
let added = false;
|
|
69
95
|
for (const cls of extractClassStrings(code)) {
|
|
70
|
-
|
|
96
|
+
for (const token of cls.split(/\s+/).filter(Boolean)) {
|
|
97
|
+
if (processToken(token)) added = true;
|
|
98
|
+
}
|
|
71
99
|
}
|
|
72
100
|
return added;
|
|
73
101
|
}
|
|
@@ -80,96 +108,24 @@ function kbach(userConfig) {
|
|
|
80
108
|
}
|
|
81
109
|
return parts.join("\n");
|
|
82
110
|
}
|
|
83
|
-
function injectIntoHtml(html) {
|
|
84
|
-
const css = getAllCSS();
|
|
85
|
-
if (!css || html.includes("data-kbach")) return html;
|
|
86
|
-
return html.replace("</head>", `<style data-kbach>${css}</style></head>`);
|
|
87
|
-
}
|
|
88
111
|
return {
|
|
89
112
|
name: "kbach",
|
|
90
113
|
enforce: "pre",
|
|
91
114
|
configResolved(resolved) {
|
|
92
115
|
root = resolved.root;
|
|
93
116
|
},
|
|
117
|
+
configureServer(s) {
|
|
118
|
+
server = s;
|
|
119
|
+
},
|
|
94
120
|
buildStart() {
|
|
95
121
|
for (const dir of ["src", "app", "pages", "components", "views", "layouts"]) {
|
|
96
122
|
scanDir(join(root, dir), (code) => extractFromCode(code));
|
|
97
123
|
}
|
|
124
|
+
mainCSSFile = findMainCSSFile(root);
|
|
125
|
+
if (mainCSSFile) writeKbachToFile(mainCSSFile, getAllCSS());
|
|
98
126
|
},
|
|
99
|
-
// ── DEV: intercept every HTML response and inject <style data-kbach> ──────
|
|
100
|
-
// React Router v8 uses chunked streaming SSR — </head> comes in an early
|
|
101
|
-
// res.write() chunk, not res.end(). Buffer all writes, concatenate at end,
|
|
102
|
-
// inject CSS into the full HTML, then send in one go.
|
|
103
|
-
configureServer(s) {
|
|
104
|
-
server = s;
|
|
105
|
-
s.middlewares.use((req, res, next) => {
|
|
106
|
-
const url = req.url ?? "";
|
|
107
|
-
if (/\.(js|ts|jsx|tsx|css|png|jpg|svg|ico|woff2?|ttf|json|map)(\?|$)/.test(url) || url.startsWith("/@") || url.startsWith("/__")) {
|
|
108
|
-
return next();
|
|
109
|
-
}
|
|
110
|
-
const origWrite = res.write.bind(res);
|
|
111
|
-
const origEnd = res.end.bind(res);
|
|
112
|
-
const chunks = [];
|
|
113
|
-
let done = false;
|
|
114
|
-
function toBuffer(chunk, enc) {
|
|
115
|
-
if (chunk == null) return Buffer.alloc(0);
|
|
116
|
-
if (typeof chunk === "string") {
|
|
117
|
-
return Buffer.from(chunk, typeof enc === "string" ? enc : "utf-8");
|
|
118
|
-
}
|
|
119
|
-
if (ArrayBuffer.isView(chunk)) {
|
|
120
|
-
return Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength);
|
|
121
|
-
}
|
|
122
|
-
if (chunk instanceof ArrayBuffer) return Buffer.from(chunk);
|
|
123
|
-
return Buffer.from(String(chunk));
|
|
124
|
-
}
|
|
125
|
-
res.write = function(chunk, enc, cb) {
|
|
126
|
-
if (done) return origWrite(chunk, enc, cb);
|
|
127
|
-
if (chunk != null) chunks.push(toBuffer(chunk, enc));
|
|
128
|
-
if (typeof enc === "function") enc();
|
|
129
|
-
else if (typeof cb === "function") cb();
|
|
130
|
-
return true;
|
|
131
|
-
};
|
|
132
|
-
res.end = function(chunk, enc, cb) {
|
|
133
|
-
done = true;
|
|
134
|
-
if (typeof chunk === "function") {
|
|
135
|
-
cb = chunk;
|
|
136
|
-
chunk = null;
|
|
137
|
-
enc = null;
|
|
138
|
-
} else if (typeof enc === "function") {
|
|
139
|
-
cb = enc;
|
|
140
|
-
enc = null;
|
|
141
|
-
}
|
|
142
|
-
if (chunk != null) chunks.push(toBuffer(chunk, enc));
|
|
143
|
-
if (chunks.length > 0) {
|
|
144
|
-
const html = Buffer.concat(chunks).toString("utf-8");
|
|
145
|
-
if (html.includes("</head>")) {
|
|
146
|
-
const injected = injectIntoHtml(html);
|
|
147
|
-
const buf = Buffer.from(injected, "utf-8");
|
|
148
|
-
try {
|
|
149
|
-
res.removeHeader("transfer-encoding");
|
|
150
|
-
} catch {
|
|
151
|
-
}
|
|
152
|
-
try {
|
|
153
|
-
res.setHeader("content-length", buf.length);
|
|
154
|
-
} catch {
|
|
155
|
-
}
|
|
156
|
-
return origEnd(buf, cb);
|
|
157
|
-
}
|
|
158
|
-
return origEnd(Buffer.concat(chunks), enc, cb);
|
|
159
|
-
}
|
|
160
|
-
return origEnd(void 0, enc, cb);
|
|
161
|
-
};
|
|
162
|
-
next();
|
|
163
|
-
});
|
|
164
|
-
},
|
|
165
|
-
// ── PROD: append to CSS files so they land in the extracted CSS bundle ────
|
|
166
127
|
transform(code, id) {
|
|
167
128
|
const cleanId = id.split("?")[0];
|
|
168
|
-
if (cleanId.endsWith(".css") && !id.includes("node_modules") && !id.includes("\0")) {
|
|
169
|
-
const kbachCSS = getAllCSS();
|
|
170
|
-
if (!kbachCSS) return null;
|
|
171
|
-
return { code: code + "\n/* kbach */\n" + kbachCSS, map: null };
|
|
172
|
-
}
|
|
173
129
|
if (/\.(tsx?|jsx?)$/.test(cleanId) && !id.includes("node_modules")) {
|
|
174
130
|
extractFromCode(code);
|
|
175
131
|
}
|
|
@@ -181,14 +137,14 @@ function kbach(userConfig) {
|
|
|
181
137
|
load(id) {
|
|
182
138
|
if (id === RESOLVED_ID) return getAllCSS();
|
|
183
139
|
},
|
|
184
|
-
// ── HMR:
|
|
140
|
+
// ── HMR: write updated CSS to file when new classes are found ────────────
|
|
141
|
+
// Vite's own file watcher detects the change and hot-reloads the CSS —
|
|
142
|
+
// no manual invalidation needed.
|
|
185
143
|
handleHotUpdate({ file }) {
|
|
186
144
|
if (file.includes("node_modules") || !/\.(tsx?|jsx?)$/.test(file)) return;
|
|
187
145
|
try {
|
|
188
146
|
const added = extractFromCode(readFileSync(file, "utf-8"));
|
|
189
|
-
if (added &&
|
|
190
|
-
(server.hot ?? server.ws)?.send({ type: "full-reload" });
|
|
191
|
-
}
|
|
147
|
+
if (added && mainCSSFile) writeKbachToFile(mainCSSFile, getAllCSS());
|
|
192
148
|
} catch {
|
|
193
149
|
}
|
|
194
150
|
}
|