@kbach/react 1.0.0-beta.4 → 1.0.0-beta.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 +103 -7
- package/dist/chunk-GPLCUNFX.mjs +1340 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/postcss-plugin.d.mts +36 -0
- package/dist/postcss-plugin.d.ts +36 -0
- package/dist/postcss-plugin.js +1119 -0
- package/dist/postcss-plugin.mjs +79 -0
- package/dist/vite-plugin.d.mts +37 -1
- package/dist/vite-plugin.d.ts +37 -1
- package/dist/vite-plugin.js +369 -228
- package/dist/vite-plugin.mjs +13 -1198
- package/package.json +13 -2
package/dist/vite-plugin.js
CHANGED
|
@@ -20,122 +20,17 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/vite-plugin/index.ts
|
|
21
21
|
var vite_plugin_exports = {};
|
|
22
22
|
__export(vite_plugin_exports, {
|
|
23
|
-
|
|
23
|
+
CSS_SYNC_DEBOUNCE_MS: () => CSS_SYNC_DEBOUNCE_MS,
|
|
24
|
+
DEFAULT_SCAN_DIRS: () => DEFAULT_SCAN_DIRS,
|
|
25
|
+
kbach: () => kbach,
|
|
26
|
+
loadConfigFile: () => loadConfigFile,
|
|
27
|
+
resolveEffectiveTheme: () => resolveEffectiveTheme
|
|
24
28
|
});
|
|
25
29
|
module.exports = __toCommonJS(vite_plugin_exports);
|
|
26
|
-
var
|
|
27
|
-
var
|
|
30
|
+
var import_fs3 = require("fs");
|
|
31
|
+
var import_path4 = require("path");
|
|
28
32
|
var import_module = require("module");
|
|
29
33
|
|
|
30
|
-
// src/vite-plugin/wasmNode.ts
|
|
31
|
-
var import_node = require("@kbach/core-engine/node");
|
|
32
|
-
function generateCssForToken(token, themeJson) {
|
|
33
|
-
return JSON.parse((0, import_node.generate_css)(token, themeJson));
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// src/vite-plugin/scan.ts
|
|
37
|
-
var import_fs = require("fs");
|
|
38
|
-
var import_path = require("path");
|
|
39
|
-
function splitClassTokens(str) {
|
|
40
|
-
return str.split(/\s+/).filter(Boolean);
|
|
41
|
-
}
|
|
42
|
-
var NOT_A_CLASS_NAME_RE = /[${}]|-$|^[?:&|!"'=<>]+$/;
|
|
43
|
-
function pushTokens(str, into) {
|
|
44
|
-
for (const tok of splitClassTokens(str)) {
|
|
45
|
-
if (tok && !NOT_A_CLASS_NAME_RE.test(tok)) into.add(tok);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
function pushTemplateLiteralBody(body, into) {
|
|
49
|
-
const interpolationRe = /\$\{(?:[^{}]|\{[^{}]*\})*\}/g;
|
|
50
|
-
let im;
|
|
51
|
-
while ((im = interpolationRe.exec(body)) !== null) {
|
|
52
|
-
pushClassLikeStrings(im[0], into);
|
|
53
|
-
}
|
|
54
|
-
pushTokens(body.replace(/\$\{(?:[^{}]|\{[^{}]*\})*\}/g, () => "$"), into);
|
|
55
|
-
}
|
|
56
|
-
function pushClassLikeStrings(text, into) {
|
|
57
|
-
const quotedStringRe = /(["'`])((?:(?!\1).)*)\1/g;
|
|
58
|
-
let qm;
|
|
59
|
-
while ((qm = quotedStringRe.exec(text)) !== null) {
|
|
60
|
-
const [, quote, content] = qm;
|
|
61
|
-
if (quote === "`") pushTemplateLiteralBody(content, into);
|
|
62
|
-
else pushTokens(content, into);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
var JSX_TAG_RE = /<([a-z][a-zA-Z0-9]*)[\s/>]/g;
|
|
66
|
-
function scanUsedTags(code) {
|
|
67
|
-
const tags = /* @__PURE__ */ new Set();
|
|
68
|
-
let m;
|
|
69
|
-
JSX_TAG_RE.lastIndex = 0;
|
|
70
|
-
while ((m = JSX_TAG_RE.exec(code)) !== null) tags.add(m[1]);
|
|
71
|
-
return tags;
|
|
72
|
-
}
|
|
73
|
-
function extractClassStrings(code) {
|
|
74
|
-
const found = /* @__PURE__ */ new Set();
|
|
75
|
-
const simpleRe = /(?:className|kb)=(["'])((?:(?!\1).)*)\1/g;
|
|
76
|
-
let m;
|
|
77
|
-
while ((m = simpleRe.exec(code)) !== null) pushTokens(m[2], found);
|
|
78
|
-
const jsxExprRe = /(?:className|kb)=\{/g;
|
|
79
|
-
while ((m = jsxExprRe.exec(code)) !== null) {
|
|
80
|
-
let depth = 1;
|
|
81
|
-
let i = m.index + m[0].length;
|
|
82
|
-
let block = "";
|
|
83
|
-
while (i < code.length && depth > 0) {
|
|
84
|
-
const ch = code[i];
|
|
85
|
-
if (ch === "{") depth++;
|
|
86
|
-
else if (ch === "}") {
|
|
87
|
-
if (--depth === 0) break;
|
|
88
|
-
}
|
|
89
|
-
block += ch;
|
|
90
|
-
i++;
|
|
91
|
-
}
|
|
92
|
-
jsxExprRe.lastIndex = i + 1;
|
|
93
|
-
pushClassLikeStrings(block, found);
|
|
94
|
-
}
|
|
95
|
-
const classComposerCallRe = /(?:clsx|cn|classnames|cx|kb)\(/g;
|
|
96
|
-
while ((m = classComposerCallRe.exec(code)) !== null) {
|
|
97
|
-
let depth = 1;
|
|
98
|
-
let i = m.index + m[0].length;
|
|
99
|
-
let block = "";
|
|
100
|
-
while (i < code.length && depth > 0) {
|
|
101
|
-
const ch = code[i];
|
|
102
|
-
if (ch === "(") depth++;
|
|
103
|
-
else if (ch === ")") {
|
|
104
|
-
if (--depth === 0) break;
|
|
105
|
-
}
|
|
106
|
-
block += ch;
|
|
107
|
-
i++;
|
|
108
|
-
}
|
|
109
|
-
classComposerCallRe.lastIndex = i + 1;
|
|
110
|
-
pushClassLikeStrings(block, found);
|
|
111
|
-
}
|
|
112
|
-
const templateRe = /`([^`]{1,2000})`/g;
|
|
113
|
-
while ((m = templateRe.exec(code)) !== null) pushTemplateLiteralBody(m[1], found);
|
|
114
|
-
return [...found];
|
|
115
|
-
}
|
|
116
|
-
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "out", ".next", ".output", ".git", ".svn", ".cache", "coverage"]);
|
|
117
|
-
var MAX_SCAN_DEPTH = 10;
|
|
118
|
-
var DEFAULT_FILE_MATCH = /\.(tsx?|jsx?)$/;
|
|
119
|
-
function scanDir(dir, onFile, depth = 0, fileMatch = DEFAULT_FILE_MATCH) {
|
|
120
|
-
if (depth > MAX_SCAN_DEPTH) return;
|
|
121
|
-
let entries;
|
|
122
|
-
try {
|
|
123
|
-
entries = (0, import_fs.readdirSync)(dir);
|
|
124
|
-
} catch {
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
for (const entry of entries) {
|
|
128
|
-
if (entry.startsWith(".") || SKIP_DIRS.has(entry)) continue;
|
|
129
|
-
const full = (0, import_path.join)(dir, entry);
|
|
130
|
-
try {
|
|
131
|
-
const st = (0, import_fs.statSync)(full);
|
|
132
|
-
if (st.isDirectory()) scanDir(full, onFile, depth + 1, fileMatch);
|
|
133
|
-
else if (fileMatch.test(entry)) onFile(full, (0, import_fs.readFileSync)(full, "utf-8"));
|
|
134
|
-
} catch {
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
34
|
// src/vite-plugin/cssVars.ts
|
|
140
35
|
function hexToRgbTriplet(hex) {
|
|
141
36
|
const h = hex.replace("#", "");
|
|
@@ -396,10 +291,10 @@ function formatKbachCSS(tokenRules, theme, usedTags) {
|
|
|
396
291
|
lines.push("", prettifyCSS(text));
|
|
397
292
|
return lines.join("\n");
|
|
398
293
|
}
|
|
399
|
-
function writeKbachToFile(filePath, css,
|
|
294
|
+
function writeKbachToFile(filePath, css, readFile, writeFile) {
|
|
400
295
|
let existing = "";
|
|
401
296
|
try {
|
|
402
|
-
existing =
|
|
297
|
+
existing = readFile(filePath);
|
|
403
298
|
} catch {
|
|
404
299
|
}
|
|
405
300
|
const block = `${KBACH_START}
|
|
@@ -411,66 +306,10 @@ ${KBACH_END}`;
|
|
|
411
306
|
|
|
412
307
|
${block}` : block;
|
|
413
308
|
if (next === existing) return false;
|
|
414
|
-
|
|
309
|
+
writeFile(filePath, next);
|
|
415
310
|
return true;
|
|
416
311
|
}
|
|
417
312
|
|
|
418
|
-
// src/vite-plugin/unknownClassWarnings.ts
|
|
419
|
-
var import_path2 = require("path");
|
|
420
|
-
var CSS_CLASS_SELECTOR_RE = /\.(-?[a-zA-Z_][a-zA-Z0-9_-]*)/g;
|
|
421
|
-
var CSS_FILE_RE = /\.(css|scss|sass|less)$/;
|
|
422
|
-
var useColor = !!process.stdout?.isTTY && !process.env.NO_COLOR;
|
|
423
|
-
var paint = (code, s) => useColor ? `\x1B[${code}m${s}\x1B[0m` : s;
|
|
424
|
-
var green = (s) => paint("32", s);
|
|
425
|
-
var blue = (s) => paint("34", s);
|
|
426
|
-
var white = (s) => paint("97", s);
|
|
427
|
-
var gray = (s) => paint("90", s);
|
|
428
|
-
var bold = (s) => paint("1", s);
|
|
429
|
-
var ARROW = () => green("\u2192");
|
|
430
|
-
var TAG = () => bold(green("[kbach]"));
|
|
431
|
-
var highlight = (s) => bold(white(s));
|
|
432
|
-
function findLineCol(code, needle) {
|
|
433
|
-
const index = code.indexOf(needle);
|
|
434
|
-
if (index === -1) return null;
|
|
435
|
-
let line = 1;
|
|
436
|
-
let lastNewline = -1;
|
|
437
|
-
for (let i = 0; i < index; i++) {
|
|
438
|
-
if (code.charCodeAt(i) === 10) {
|
|
439
|
-
line++;
|
|
440
|
-
lastNewline = i;
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
return { line, column: index - lastNewline };
|
|
444
|
-
}
|
|
445
|
-
function scanCssFileInto(code, into) {
|
|
446
|
-
const re = new RegExp(CSS_CLASS_SELECTOR_RE.source, "g");
|
|
447
|
-
let m;
|
|
448
|
-
while ((m = re.exec(code)) !== null) into.add(m[1]);
|
|
449
|
-
}
|
|
450
|
-
function scanProjectCssSelectors(root, includeDirs) {
|
|
451
|
-
const projectCssClasses = /* @__PURE__ */ new Set();
|
|
452
|
-
for (const dir of includeDirs) {
|
|
453
|
-
scanDir((0, import_path2.join)(root, dir), (_filePath, code) => scanCssFileInto(code, projectCssClasses), 0, CSS_FILE_RE);
|
|
454
|
-
}
|
|
455
|
-
return projectCssClasses;
|
|
456
|
-
}
|
|
457
|
-
function stripModifierPrefix(token) {
|
|
458
|
-
return token.replace(/^(?:[a-zA-Z0-9_/-]+:)+/, "");
|
|
459
|
-
}
|
|
460
|
-
var MARKER_CLASSES = /* @__PURE__ */ new Set(["group", "peer"]);
|
|
461
|
-
function warnIfUnknownClass(token, filePath, code, isResolvable, projectCssClasses, warnedTokens) {
|
|
462
|
-
if (isResolvable || warnedTokens.has(token)) return;
|
|
463
|
-
const base = stripModifierPrefix(token);
|
|
464
|
-
if (!base || projectCssClasses.has(base) || MARKER_CLASSES.has(base)) return;
|
|
465
|
-
warnedTokens.add(token);
|
|
466
|
-
const pos = findLineCol(code, token);
|
|
467
|
-
const location = pos ? `${blue(filePath)}${green(`:${pos.line}:${pos.column}`)}` : blue(filePath);
|
|
468
|
-
console.warn(
|
|
469
|
-
`${ARROW()} ${TAG()} ${gray("Unknown class")} ${highlight(token)} ${gray("\u2014 no Kbach utility or project CSS rule matches it. Typo?")}
|
|
470
|
-
${bold("at")} ${location}`
|
|
471
|
-
);
|
|
472
|
-
}
|
|
473
|
-
|
|
474
313
|
// src/vite-plugin/classNameHints.ts
|
|
475
314
|
var STRUCTURAL_TOKENS = [
|
|
476
315
|
// layout.rs
|
|
@@ -1018,6 +857,41 @@ var STRUCTURAL_TOKENS = [
|
|
|
1018
857
|
"aria-required:",
|
|
1019
858
|
"aria-selected:",
|
|
1020
859
|
"aria-busy:",
|
|
860
|
+
// registry.rs modifiers, added later — same "fixed/static, finite" bar
|
|
861
|
+
// as the Phase 24 block above. Structural -of-type/empty, form-state and
|
|
862
|
+
// validation pseudo-classes, other native pseudo-classes with no aria-*
|
|
863
|
+
// counterpart, a pseudo-element, pointer media variants, and the
|
|
864
|
+
// direct-children/all-descendants variants. "supports-[...]:"/"in-*:" are
|
|
865
|
+
// deliberately excluded here for the same "no finite enumerable set"
|
|
866
|
+
// reason as has-[...]:/group-<pseudo>: above.
|
|
867
|
+
"first-of-type:",
|
|
868
|
+
"last-of-type:",
|
|
869
|
+
"only-of-type:",
|
|
870
|
+
"empty:",
|
|
871
|
+
"enabled:",
|
|
872
|
+
"read-only:",
|
|
873
|
+
"required:",
|
|
874
|
+
"optional:",
|
|
875
|
+
"valid:",
|
|
876
|
+
"invalid:",
|
|
877
|
+
"in-range:",
|
|
878
|
+
"out-of-range:",
|
|
879
|
+
"default:",
|
|
880
|
+
"indeterminate:",
|
|
881
|
+
"placeholder-shown:",
|
|
882
|
+
"autofill:",
|
|
883
|
+
"user-valid:",
|
|
884
|
+
"user-invalid:",
|
|
885
|
+
"target:",
|
|
886
|
+
"fullscreen:",
|
|
887
|
+
"popover-open:",
|
|
888
|
+
"details-content:",
|
|
889
|
+
"pointer-coarse:",
|
|
890
|
+
"pointer-fine:",
|
|
891
|
+
"any-pointer-coarse:",
|
|
892
|
+
"any-pointer-fine:",
|
|
893
|
+
"*:",
|
|
894
|
+
"**:",
|
|
1021
895
|
// Phase 25 (container queries & arbitrary properties). "@container" is a
|
|
1022
896
|
// real UTILITY (container-type: inline-size), not a modifier — included
|
|
1023
897
|
// as a bare token like "flex"/"hidden" above, not with the trailing ":"
|
|
@@ -1555,35 +1429,261 @@ function resolveKbachConfig(config) {
|
|
|
1555
1429
|
return theme;
|
|
1556
1430
|
}
|
|
1557
1431
|
|
|
1558
|
-
// src/
|
|
1559
|
-
var
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1432
|
+
// src/staticCss/engine.ts
|
|
1433
|
+
var import_fs2 = require("fs");
|
|
1434
|
+
var import_path3 = require("path");
|
|
1435
|
+
|
|
1436
|
+
// src/vite-plugin/wasmNode.ts
|
|
1437
|
+
var import_node = require("@kbach/core-engine/node");
|
|
1438
|
+
function generateCssForToken(token, themeJson) {
|
|
1439
|
+
return JSON.parse((0, import_node.generate_css)(token, themeJson));
|
|
1563
1440
|
}
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1441
|
+
|
|
1442
|
+
// src/vite-plugin/scan.ts
|
|
1443
|
+
var import_fs = require("fs");
|
|
1444
|
+
var import_path = require("path");
|
|
1445
|
+
function stripComments(code) {
|
|
1446
|
+
let out = "";
|
|
1447
|
+
let i = 0;
|
|
1448
|
+
const n = code.length;
|
|
1449
|
+
while (i < n) {
|
|
1450
|
+
const two = code[i] + (code[i + 1] ?? "");
|
|
1451
|
+
if (two === "//") {
|
|
1452
|
+
i += 2;
|
|
1453
|
+
while (i < n && code[i] !== "\n") i++;
|
|
1454
|
+
continue;
|
|
1455
|
+
}
|
|
1456
|
+
if (two === "/*") {
|
|
1457
|
+
i += 2;
|
|
1458
|
+
while (i < n && code[i] + (code[i + 1] ?? "") !== "*/") i++;
|
|
1459
|
+
i += 2;
|
|
1460
|
+
continue;
|
|
1461
|
+
}
|
|
1462
|
+
const ch = code[i];
|
|
1463
|
+
if (ch === '"' || ch === "'") {
|
|
1464
|
+
out += ch;
|
|
1465
|
+
i++;
|
|
1466
|
+
while (i < n && code[i] !== ch) {
|
|
1467
|
+
if (code[i] === "\\") {
|
|
1468
|
+
out += code[i] + (code[i + 1] ?? "");
|
|
1469
|
+
i += 2;
|
|
1470
|
+
continue;
|
|
1471
|
+
}
|
|
1472
|
+
out += code[i];
|
|
1473
|
+
i++;
|
|
1474
|
+
}
|
|
1475
|
+
out += code[i] ?? "";
|
|
1476
|
+
i++;
|
|
1477
|
+
continue;
|
|
1478
|
+
}
|
|
1479
|
+
if (ch === "`") {
|
|
1480
|
+
out += ch;
|
|
1481
|
+
i++;
|
|
1482
|
+
let interpolationDepth = 0;
|
|
1483
|
+
while (i < n) {
|
|
1484
|
+
if (interpolationDepth === 0 && code[i] === "`") {
|
|
1485
|
+
out += code[i];
|
|
1486
|
+
i++;
|
|
1487
|
+
break;
|
|
1488
|
+
}
|
|
1489
|
+
if (interpolationDepth === 0 && code[i] === "\\") {
|
|
1490
|
+
out += code[i] + (code[i + 1] ?? "");
|
|
1491
|
+
i += 2;
|
|
1492
|
+
continue;
|
|
1493
|
+
}
|
|
1494
|
+
if (interpolationDepth === 0 && code[i] + (code[i + 1] ?? "") === "${") {
|
|
1495
|
+
out += "${";
|
|
1496
|
+
i += 2;
|
|
1497
|
+
interpolationDepth = 1;
|
|
1498
|
+
continue;
|
|
1499
|
+
}
|
|
1500
|
+
if (interpolationDepth > 0) {
|
|
1501
|
+
if (code[i] === "{") interpolationDepth++;
|
|
1502
|
+
else if (code[i] === "}") {
|
|
1503
|
+
interpolationDepth--;
|
|
1504
|
+
if (interpolationDepth === 0) {
|
|
1505
|
+
out += "}";
|
|
1506
|
+
i++;
|
|
1507
|
+
continue;
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
out += code[i];
|
|
1512
|
+
i++;
|
|
1513
|
+
}
|
|
1514
|
+
continue;
|
|
1515
|
+
}
|
|
1516
|
+
out += ch;
|
|
1517
|
+
i++;
|
|
1518
|
+
}
|
|
1519
|
+
return out;
|
|
1567
1520
|
}
|
|
1568
|
-
function
|
|
1569
|
-
|
|
1521
|
+
function splitClassTokens(str) {
|
|
1522
|
+
return str.split(/\s+/).filter(Boolean);
|
|
1570
1523
|
}
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1524
|
+
var NOT_A_CLASS_NAME_RE = /[${}]|-$|^[?:&|!"'=<>]+$/;
|
|
1525
|
+
function pushTokens(str, into) {
|
|
1526
|
+
for (const tok of splitClassTokens(str)) {
|
|
1527
|
+
if (tok && !NOT_A_CLASS_NAME_RE.test(tok)) into.add(tok);
|
|
1528
|
+
}
|
|
1574
1529
|
}
|
|
1575
|
-
function
|
|
1576
|
-
const
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1530
|
+
function pushTemplateLiteralBody(body, into) {
|
|
1531
|
+
const interpolationRe = /\$\{(?:[^{}]|\{[^{}]*\})*\}/g;
|
|
1532
|
+
let im;
|
|
1533
|
+
while ((im = interpolationRe.exec(body)) !== null) {
|
|
1534
|
+
pushClassLikeStrings(im[0], into);
|
|
1535
|
+
}
|
|
1536
|
+
pushTokens(body.replace(/\$\{(?:[^{}]|\{[^{}]*\})*\}/g, () => "$"), into);
|
|
1580
1537
|
}
|
|
1581
|
-
function
|
|
1582
|
-
|
|
1583
|
-
let
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1538
|
+
function pushClassLikeStrings(text, into) {
|
|
1539
|
+
const quotedStringRe = /(["'`])((?:(?!\1).)*)\1/g;
|
|
1540
|
+
let qm;
|
|
1541
|
+
while ((qm = quotedStringRe.exec(text)) !== null) {
|
|
1542
|
+
const [, quote, content] = qm;
|
|
1543
|
+
if (quote === "`") pushTemplateLiteralBody(content, into);
|
|
1544
|
+
else pushTokens(content, into);
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
var JSX_TAG_RE = /<([a-z][a-zA-Z0-9]*)[\s/>]/g;
|
|
1548
|
+
function scanUsedTags(code) {
|
|
1549
|
+
const cleaned = stripComments(code);
|
|
1550
|
+
const tags = /* @__PURE__ */ new Set();
|
|
1551
|
+
let m;
|
|
1552
|
+
JSX_TAG_RE.lastIndex = 0;
|
|
1553
|
+
while ((m = JSX_TAG_RE.exec(cleaned)) !== null) tags.add(m[1]);
|
|
1554
|
+
return tags;
|
|
1555
|
+
}
|
|
1556
|
+
function extractClassStrings(rawCode) {
|
|
1557
|
+
const code = stripComments(rawCode);
|
|
1558
|
+
const found = /* @__PURE__ */ new Set();
|
|
1559
|
+
const simpleRe = /(?:className|kb)=(["'])((?:(?!\1).)*)\1/g;
|
|
1560
|
+
let m;
|
|
1561
|
+
while ((m = simpleRe.exec(code)) !== null) pushTokens(m[2], found);
|
|
1562
|
+
const jsxExprRe = /(?:className|kb)=\{/g;
|
|
1563
|
+
while ((m = jsxExprRe.exec(code)) !== null) {
|
|
1564
|
+
let depth = 1;
|
|
1565
|
+
let i = m.index + m[0].length;
|
|
1566
|
+
let block = "";
|
|
1567
|
+
while (i < code.length && depth > 0) {
|
|
1568
|
+
const ch = code[i];
|
|
1569
|
+
if (ch === "{") depth++;
|
|
1570
|
+
else if (ch === "}") {
|
|
1571
|
+
if (--depth === 0) break;
|
|
1572
|
+
}
|
|
1573
|
+
block += ch;
|
|
1574
|
+
i++;
|
|
1575
|
+
}
|
|
1576
|
+
jsxExprRe.lastIndex = i + 1;
|
|
1577
|
+
pushClassLikeStrings(block, found);
|
|
1578
|
+
}
|
|
1579
|
+
const classComposerCallRe = /(?:clsx|cn|classnames|cx|kb)\(/g;
|
|
1580
|
+
while ((m = classComposerCallRe.exec(code)) !== null) {
|
|
1581
|
+
let depth = 1;
|
|
1582
|
+
let i = m.index + m[0].length;
|
|
1583
|
+
let block = "";
|
|
1584
|
+
while (i < code.length && depth > 0) {
|
|
1585
|
+
const ch = code[i];
|
|
1586
|
+
if (ch === "(") depth++;
|
|
1587
|
+
else if (ch === ")") {
|
|
1588
|
+
if (--depth === 0) break;
|
|
1589
|
+
}
|
|
1590
|
+
block += ch;
|
|
1591
|
+
i++;
|
|
1592
|
+
}
|
|
1593
|
+
classComposerCallRe.lastIndex = i + 1;
|
|
1594
|
+
pushClassLikeStrings(block, found);
|
|
1595
|
+
}
|
|
1596
|
+
const templateRe = /`([^`]{1,2000})`/g;
|
|
1597
|
+
while ((m = templateRe.exec(code)) !== null) pushTemplateLiteralBody(m[1], found);
|
|
1598
|
+
return [...found];
|
|
1599
|
+
}
|
|
1600
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "out", ".next", ".output", ".git", ".svn", ".cache", "coverage"]);
|
|
1601
|
+
var MAX_SCAN_DEPTH = 10;
|
|
1602
|
+
var DEFAULT_FILE_MATCH = /\.(tsx?|jsx?)$/;
|
|
1603
|
+
function scanDir(dir, onFile, depth = 0, fileMatch = DEFAULT_FILE_MATCH) {
|
|
1604
|
+
if (depth > MAX_SCAN_DEPTH) return;
|
|
1605
|
+
let entries;
|
|
1606
|
+
try {
|
|
1607
|
+
entries = (0, import_fs.readdirSync)(dir);
|
|
1608
|
+
} catch {
|
|
1609
|
+
return;
|
|
1610
|
+
}
|
|
1611
|
+
for (const entry of entries) {
|
|
1612
|
+
if (entry.startsWith(".") || SKIP_DIRS.has(entry)) continue;
|
|
1613
|
+
const full = (0, import_path.join)(dir, entry);
|
|
1614
|
+
try {
|
|
1615
|
+
const st = (0, import_fs.statSync)(full);
|
|
1616
|
+
if (st.isDirectory()) scanDir(full, onFile, depth + 1, fileMatch);
|
|
1617
|
+
else if (fileMatch.test(entry)) onFile(full, (0, import_fs.readFileSync)(full, "utf-8"));
|
|
1618
|
+
} catch {
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
// src/vite-plugin/unknownClassWarnings.ts
|
|
1624
|
+
var import_path2 = require("path");
|
|
1625
|
+
var CSS_CLASS_SELECTOR_RE = /\.(-?[a-zA-Z_][a-zA-Z0-9_-]*)/g;
|
|
1626
|
+
var CSS_FILE_RE = /\.(css|scss|sass|less)$/;
|
|
1627
|
+
var useColor = !!process.stdout?.isTTY && !process.env.NO_COLOR;
|
|
1628
|
+
var paint = (code, s) => useColor ? `\x1B[${code}m${s}\x1B[0m` : s;
|
|
1629
|
+
var green = (s) => paint("32", s);
|
|
1630
|
+
var blue = (s) => paint("34", s);
|
|
1631
|
+
var white = (s) => paint("97", s);
|
|
1632
|
+
var gray = (s) => paint("90", s);
|
|
1633
|
+
var bold = (s) => paint("1", s);
|
|
1634
|
+
var ARROW = () => green("\u2192");
|
|
1635
|
+
var TAG = () => bold(green("[kbach]"));
|
|
1636
|
+
var highlight = (s) => bold(white(s));
|
|
1637
|
+
function findLineCol(code, needle) {
|
|
1638
|
+
const index = code.indexOf(needle);
|
|
1639
|
+
if (index === -1) return null;
|
|
1640
|
+
let line = 1;
|
|
1641
|
+
let lastNewline = -1;
|
|
1642
|
+
for (let i = 0; i < index; i++) {
|
|
1643
|
+
if (code.charCodeAt(i) === 10) {
|
|
1644
|
+
line++;
|
|
1645
|
+
lastNewline = i;
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
return { line, column: index - lastNewline };
|
|
1649
|
+
}
|
|
1650
|
+
function scanCssFileInto(code, into) {
|
|
1651
|
+
const re = new RegExp(CSS_CLASS_SELECTOR_RE.source, "g");
|
|
1652
|
+
let m;
|
|
1653
|
+
while ((m = re.exec(code)) !== null) into.add(m[1]);
|
|
1654
|
+
}
|
|
1655
|
+
function scanProjectCssSelectors(root, includeDirs) {
|
|
1656
|
+
const projectCssClasses = /* @__PURE__ */ new Set();
|
|
1657
|
+
for (const dir of includeDirs) {
|
|
1658
|
+
scanDir((0, import_path2.join)(root, dir), (_filePath, code) => scanCssFileInto(code, projectCssClasses), 0, CSS_FILE_RE);
|
|
1659
|
+
}
|
|
1660
|
+
return projectCssClasses;
|
|
1661
|
+
}
|
|
1662
|
+
function stripModifierPrefix(token) {
|
|
1663
|
+
return token.replace(/^(?:[a-zA-Z0-9_/-]+:)+/, "");
|
|
1664
|
+
}
|
|
1665
|
+
var MARKER_CLASSES = /* @__PURE__ */ new Set(["group", "peer"]);
|
|
1666
|
+
function warnIfUnknownClass(token, filePath, code, isResolvable, projectCssClasses, warnedTokens) {
|
|
1667
|
+
if (isResolvable || warnedTokens.has(token)) return;
|
|
1668
|
+
const base = stripModifierPrefix(token);
|
|
1669
|
+
if (!base || projectCssClasses.has(base) || MARKER_CLASSES.has(base)) return;
|
|
1670
|
+
warnedTokens.add(token);
|
|
1671
|
+
const pos = findLineCol(code, token);
|
|
1672
|
+
const location = pos ? `${blue(filePath)}${green(`:${pos.line}:${pos.column}`)}` : blue(filePath);
|
|
1673
|
+
console.warn(
|
|
1674
|
+
`${ARROW()} ${TAG()} ${gray("Unknown class")} ${highlight(token)} ${gray("\u2014 no Kbach utility or project CSS rule matches it. Typo?")}
|
|
1675
|
+
${bold("at")} ${location}`
|
|
1676
|
+
);
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
// src/staticCss/engine.ts
|
|
1680
|
+
function normPath(p) {
|
|
1681
|
+
const fwd = p.replace(/\\/g, "/");
|
|
1682
|
+
return process.platform === "win32" ? fwd.toLowerCase() : fwd;
|
|
1683
|
+
}
|
|
1684
|
+
var SAFELIST_KEY = "\0kbach-safelist";
|
|
1685
|
+
function createKbachStaticCssEngine(options) {
|
|
1686
|
+
const { root, theme, themeJson, includeDirs, safelist } = options;
|
|
1587
1687
|
const fileTokens = /* @__PURE__ */ new Map();
|
|
1588
1688
|
const tokenRules = /* @__PURE__ */ new Map();
|
|
1589
1689
|
const fileTags = /* @__PURE__ */ new Map();
|
|
@@ -1607,6 +1707,10 @@ function kbach(options = {}) {
|
|
|
1607
1707
|
fileTokens.set(normPath(filePath), tokens);
|
|
1608
1708
|
fileTags.set(normPath(filePath), scanUsedTags(code));
|
|
1609
1709
|
}
|
|
1710
|
+
function removeFile(filePath) {
|
|
1711
|
+
fileTokens.delete(normPath(filePath));
|
|
1712
|
+
fileTags.delete(normPath(filePath));
|
|
1713
|
+
}
|
|
1610
1714
|
function processSafelist() {
|
|
1611
1715
|
if (safelist.length === 0) return;
|
|
1612
1716
|
const tokens = new Set(safelist);
|
|
@@ -1630,49 +1734,85 @@ function kbach(options = {}) {
|
|
|
1630
1734
|
const rules = tokenRules.get(tok);
|
|
1631
1735
|
if (rules) view.set(tok, rules);
|
|
1632
1736
|
}
|
|
1737
|
+
for (const tok of tokenRules.keys()) {
|
|
1738
|
+
if (!active.has(tok)) tokenRules.delete(tok);
|
|
1739
|
+
}
|
|
1633
1740
|
return formatKbachCSS(view, theme, activeTags());
|
|
1634
1741
|
}
|
|
1742
|
+
function initialScan() {
|
|
1743
|
+
for (const dir of includeDirs) {
|
|
1744
|
+
scanDir((0, import_path3.join)(root, dir), processFile);
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
function scanProjectCssSelectorsOnce() {
|
|
1748
|
+
projectCssClasses = scanProjectCssSelectors(root, includeDirs);
|
|
1749
|
+
}
|
|
1750
|
+
return { processFile, removeFile, initialScan, processSafelist, generateCSS, scanProjectCssSelectorsOnce };
|
|
1751
|
+
}
|
|
1752
|
+
function readSourceFile(path) {
|
|
1753
|
+
return (0, import_fs2.readFileSync)(path, "utf-8");
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
// src/vite-plugin/index.ts
|
|
1757
|
+
var DEFAULT_SCAN_DIRS = ["src", "app", "pages", "components"];
|
|
1758
|
+
var CSS_SYNC_DEBOUNCE_MS = 50;
|
|
1759
|
+
function writeFileEnsuringDir(path, content) {
|
|
1760
|
+
(0, import_fs3.mkdirSync)((0, import_path4.dirname)(path), { recursive: true });
|
|
1761
|
+
(0, import_fs3.writeFileSync)(path, content, "utf-8");
|
|
1762
|
+
}
|
|
1763
|
+
function loadConfigFile(root) {
|
|
1764
|
+
const path = (0, import_path4.join)(root, "kbach.config.js");
|
|
1765
|
+
if (!(0, import_fs3.existsSync)(path)) return void 0;
|
|
1766
|
+
const mod = (0, import_module.createRequire)(root)(path);
|
|
1767
|
+
return mod.default ?? mod;
|
|
1768
|
+
}
|
|
1769
|
+
function resolveEffectiveTheme(root, options) {
|
|
1770
|
+
if (options.theme) return options.theme;
|
|
1771
|
+
if (options.config) return resolveKbachConfig(options.config);
|
|
1772
|
+
const discovered = loadConfigFile(root);
|
|
1773
|
+
return discovered ? resolveKbachConfig(discovered) : defaultTheme;
|
|
1774
|
+
}
|
|
1775
|
+
function kbach(options = {}) {
|
|
1776
|
+
const includeDirs = options.include ?? DEFAULT_SCAN_DIRS;
|
|
1777
|
+
const safelist = options.safelist ?? [];
|
|
1778
|
+
let root = process.cwd();
|
|
1779
|
+
let theme = defaultTheme;
|
|
1780
|
+
let engine = createKbachStaticCssEngine({ root, theme, themeJson: JSON.stringify(theme), includeDirs, safelist });
|
|
1635
1781
|
function mainCSSFile() {
|
|
1636
|
-
return (0,
|
|
1782
|
+
return (0, import_path4.join)(root, "src", "kbach.css");
|
|
1637
1783
|
}
|
|
1638
1784
|
function classNameHintsFile() {
|
|
1639
|
-
return (0,
|
|
1785
|
+
return (0, import_path4.join)(root, ".kbach", "kbach-classnames.d.ts");
|
|
1640
1786
|
}
|
|
1641
1787
|
function writeClassNameHints() {
|
|
1642
1788
|
const dts = buildClassNameHintsDts(buildClassTokens(theme));
|
|
1643
1789
|
writeFileEnsuringDir(classNameHintsFile(), dts);
|
|
1644
1790
|
}
|
|
1645
1791
|
function syncMainCSSFile(server) {
|
|
1646
|
-
const
|
|
1647
|
-
for (const tok of tokenRules.keys()) {
|
|
1648
|
-
if (!active.has(tok)) tokenRules.delete(tok);
|
|
1649
|
-
}
|
|
1650
|
-
const changed = writeKbachToFile(mainCSSFile(), generateCSS(), readFile, writeFile);
|
|
1792
|
+
const changed = writeKbachToFile(mainCSSFile(), engine.generateCSS(), readSourceFile, (p, c) => (0, import_fs3.writeFileSync)(p, c, "utf-8"));
|
|
1651
1793
|
if (changed && server) server.watcher.emit("change", mainCSSFile());
|
|
1652
1794
|
}
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1795
|
+
let syncTimer = null;
|
|
1796
|
+
function scheduleSync(server) {
|
|
1797
|
+
if (syncTimer) clearTimeout(syncTimer);
|
|
1798
|
+
syncTimer = setTimeout(() => {
|
|
1799
|
+
syncTimer = null;
|
|
1800
|
+
syncMainCSSFile(server);
|
|
1801
|
+
}, CSS_SYNC_DEBOUNCE_MS);
|
|
1657
1802
|
}
|
|
1658
1803
|
return {
|
|
1659
1804
|
name: "kbach",
|
|
1660
1805
|
enforce: "pre",
|
|
1661
1806
|
configResolved(resolved) {
|
|
1662
1807
|
root = resolved.root;
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
if (discovered) {
|
|
1666
|
-
theme = resolveKbachConfig(discovered);
|
|
1667
|
-
themeJson = JSON.stringify(theme);
|
|
1668
|
-
}
|
|
1669
|
-
}
|
|
1808
|
+
theme = resolveEffectiveTheme(root, options);
|
|
1809
|
+
engine = createKbachStaticCssEngine({ root, theme, themeJson: JSON.stringify(theme), includeDirs, safelist });
|
|
1670
1810
|
},
|
|
1671
1811
|
buildStart() {
|
|
1672
|
-
|
|
1673
|
-
initialScan();
|
|
1674
|
-
processSafelist();
|
|
1675
|
-
writeKbachToFile(mainCSSFile(), generateCSS(),
|
|
1812
|
+
engine.scanProjectCssSelectorsOnce();
|
|
1813
|
+
engine.initialScan();
|
|
1814
|
+
engine.processSafelist();
|
|
1815
|
+
writeKbachToFile(mainCSSFile(), engine.generateCSS(), readSourceFile, (p, c) => (0, import_fs3.writeFileSync)(p, c, "utf-8"));
|
|
1676
1816
|
writeClassNameHints();
|
|
1677
1817
|
},
|
|
1678
1818
|
// handleHotUpdate covers edits; add/unlink (create/delete) land on the
|
|
@@ -1683,17 +1823,15 @@ function kbach(options = {}) {
|
|
|
1683
1823
|
if (file.includes("node_modules")) return;
|
|
1684
1824
|
if (!/\.(tsx?|jsx?)$/.test(file)) return;
|
|
1685
1825
|
if (isUnlink) {
|
|
1686
|
-
|
|
1687
|
-
fileTags.delete(normPath(file));
|
|
1826
|
+
engine.removeFile(file);
|
|
1688
1827
|
} else {
|
|
1689
1828
|
try {
|
|
1690
|
-
processFile(file,
|
|
1829
|
+
engine.processFile(file, readSourceFile(file));
|
|
1691
1830
|
} catch {
|
|
1692
|
-
|
|
1693
|
-
fileTags.delete(normPath(file));
|
|
1831
|
+
engine.removeFile(file);
|
|
1694
1832
|
}
|
|
1695
1833
|
}
|
|
1696
|
-
|
|
1834
|
+
scheduleSync(server);
|
|
1697
1835
|
};
|
|
1698
1836
|
server.watcher.on("add", (file) => onAddOrUnlink(file, false));
|
|
1699
1837
|
server.watcher.on("unlink", (file) => onAddOrUnlink(file, true));
|
|
@@ -1702,16 +1840,19 @@ function kbach(options = {}) {
|
|
|
1702
1840
|
if (file.includes("node_modules")) return;
|
|
1703
1841
|
if (!/\.(tsx?|jsx?)$/.test(file)) return;
|
|
1704
1842
|
try {
|
|
1705
|
-
processFile(file,
|
|
1843
|
+
engine.processFile(file, readSourceFile(file));
|
|
1706
1844
|
} catch {
|
|
1707
|
-
|
|
1708
|
-
fileTags.delete(normPath(file));
|
|
1845
|
+
engine.removeFile(file);
|
|
1709
1846
|
}
|
|
1710
|
-
|
|
1847
|
+
scheduleSync(server);
|
|
1711
1848
|
}
|
|
1712
1849
|
};
|
|
1713
1850
|
}
|
|
1714
1851
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1715
1852
|
0 && (module.exports = {
|
|
1716
|
-
|
|
1853
|
+
CSS_SYNC_DEBOUNCE_MS,
|
|
1854
|
+
DEFAULT_SCAN_DIRS,
|
|
1855
|
+
kbach,
|
|
1856
|
+
loadConfigFile,
|
|
1857
|
+
resolveEffectiveTheme
|
|
1717
1858
|
});
|