@kbach/react 1.0.0-beta.0 → 1.0.0-beta.10
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 +216 -73
- package/dist/chunk-4Y2QXNJY.mjs +115 -0
- package/dist/{chunk-AFY36PRX.mjs → chunk-EOSN5CYG.mjs} +69 -10
- package/dist/chunk-J2M6QWJW.mjs +1385 -0
- package/dist/{chunk-VBPQAIZL.mjs → chunk-L6B4DTLD.mjs} +1 -1
- package/dist/{chunk-G3RJJNSY.mjs → chunk-ROKZPEHA.mjs} +1 -1
- package/dist/config-BQ_b-qxF.d.mts +105 -0
- package/dist/config-BQ_b-qxF.d.ts +105 -0
- package/dist/index.d.mts +65 -34
- package/dist/index.d.ts +65 -34
- package/dist/index.js +260 -44
- package/dist/index.mjs +90 -33
- package/dist/jsx-dev-runtime.js +69 -10
- package/dist/jsx-dev-runtime.mjs +3 -3
- package/dist/jsx-runtime.js +69 -10
- package/dist/jsx-runtime.mjs +3 -3
- package/dist/postcss-plugin.d.mts +36 -0
- package/dist/postcss-plugin.d.ts +36 -0
- package/dist/postcss-plugin.js +1152 -0
- package/dist/postcss-plugin.mjs +79 -0
- package/dist/vite-plugin.d.mts +108 -6
- package/dist/vite-plugin.d.ts +108 -6
- package/dist/vite-plugin.js +545 -226
- package/dist/vite-plugin.mjs +15 -1181
- package/package.json +13 -2
- package/dist/theme-BKAb78KP.d.mts +0 -20
- package/dist/theme-BKAb78KP.d.ts +0 -20
package/dist/vite-plugin.js
CHANGED
|
@@ -20,120 +20,18 @@ 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
|
+
findMainCSSFile: () => findMainCSSFile,
|
|
26
|
+
kbach: () => kbach,
|
|
27
|
+
loadConfigFile: () => loadConfigFile,
|
|
28
|
+
resolveEffectiveTheme: () => resolveEffectiveTheme
|
|
24
29
|
});
|
|
25
30
|
module.exports = __toCommonJS(vite_plugin_exports);
|
|
26
|
-
var
|
|
27
|
-
var
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
var import_node = require("@kbach/core-engine/node");
|
|
31
|
-
function generateCssForToken(token, themeJson) {
|
|
32
|
-
return JSON.parse((0, import_node.generate_css)(token, themeJson));
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// src/vite-plugin/scan.ts
|
|
36
|
-
var import_fs = require("fs");
|
|
37
|
-
var import_path = require("path");
|
|
38
|
-
function splitClassTokens(str) {
|
|
39
|
-
return str.split(/\s+/).filter(Boolean);
|
|
40
|
-
}
|
|
41
|
-
var NOT_A_CLASS_NAME_RE = /[${}]|-$|^[?:&|!"'=<>]+$/;
|
|
42
|
-
function pushTokens(str, into) {
|
|
43
|
-
for (const tok of splitClassTokens(str)) {
|
|
44
|
-
if (tok && !NOT_A_CLASS_NAME_RE.test(tok)) into.add(tok);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
function pushTemplateLiteralBody(body, into) {
|
|
48
|
-
const interpolationRe = /\$\{(?:[^{}]|\{[^{}]*\})*\}/g;
|
|
49
|
-
let im;
|
|
50
|
-
while ((im = interpolationRe.exec(body)) !== null) {
|
|
51
|
-
pushClassLikeStrings(im[0], into);
|
|
52
|
-
}
|
|
53
|
-
pushTokens(body.replace(/\$\{(?:[^{}]|\{[^{}]*\})*\}/g, () => "$"), into);
|
|
54
|
-
}
|
|
55
|
-
function pushClassLikeStrings(text, into) {
|
|
56
|
-
const quotedStringRe = /(["'`])((?:(?!\1).)*)\1/g;
|
|
57
|
-
let qm;
|
|
58
|
-
while ((qm = quotedStringRe.exec(text)) !== null) {
|
|
59
|
-
const [, quote, content] = qm;
|
|
60
|
-
if (quote === "`") pushTemplateLiteralBody(content, into);
|
|
61
|
-
else pushTokens(content, into);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
var JSX_TAG_RE = /<([a-z][a-zA-Z0-9]*)[\s/>]/g;
|
|
65
|
-
function scanUsedTags(code) {
|
|
66
|
-
const tags = /* @__PURE__ */ new Set();
|
|
67
|
-
let m;
|
|
68
|
-
JSX_TAG_RE.lastIndex = 0;
|
|
69
|
-
while ((m = JSX_TAG_RE.exec(code)) !== null) tags.add(m[1]);
|
|
70
|
-
return tags;
|
|
71
|
-
}
|
|
72
|
-
function extractClassStrings(code) {
|
|
73
|
-
const found = /* @__PURE__ */ new Set();
|
|
74
|
-
const simpleRe = /(?:className|kb)=(["'])((?:(?!\1).)*)\1/g;
|
|
75
|
-
let m;
|
|
76
|
-
while ((m = simpleRe.exec(code)) !== null) pushTokens(m[2], found);
|
|
77
|
-
const jsxExprRe = /(?:className|kb)=\{/g;
|
|
78
|
-
while ((m = jsxExprRe.exec(code)) !== null) {
|
|
79
|
-
let depth = 1;
|
|
80
|
-
let i = m.index + m[0].length;
|
|
81
|
-
let block = "";
|
|
82
|
-
while (i < code.length && depth > 0) {
|
|
83
|
-
const ch = code[i];
|
|
84
|
-
if (ch === "{") depth++;
|
|
85
|
-
else if (ch === "}") {
|
|
86
|
-
if (--depth === 0) break;
|
|
87
|
-
}
|
|
88
|
-
block += ch;
|
|
89
|
-
i++;
|
|
90
|
-
}
|
|
91
|
-
jsxExprRe.lastIndex = i + 1;
|
|
92
|
-
pushClassLikeStrings(block, found);
|
|
93
|
-
}
|
|
94
|
-
const classComposerCallRe = /(?:clsx|cn|classnames|cx|kb)\(/g;
|
|
95
|
-
while ((m = classComposerCallRe.exec(code)) !== null) {
|
|
96
|
-
let depth = 1;
|
|
97
|
-
let i = m.index + m[0].length;
|
|
98
|
-
let block = "";
|
|
99
|
-
while (i < code.length && depth > 0) {
|
|
100
|
-
const ch = code[i];
|
|
101
|
-
if (ch === "(") depth++;
|
|
102
|
-
else if (ch === ")") {
|
|
103
|
-
if (--depth === 0) break;
|
|
104
|
-
}
|
|
105
|
-
block += ch;
|
|
106
|
-
i++;
|
|
107
|
-
}
|
|
108
|
-
classComposerCallRe.lastIndex = i + 1;
|
|
109
|
-
pushClassLikeStrings(block, found);
|
|
110
|
-
}
|
|
111
|
-
const templateRe = /`([^`]{1,2000})`/g;
|
|
112
|
-
while ((m = templateRe.exec(code)) !== null) pushTemplateLiteralBody(m[1], found);
|
|
113
|
-
return [...found];
|
|
114
|
-
}
|
|
115
|
-
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "out", ".next", ".output", ".git", ".svn", ".cache", "coverage"]);
|
|
116
|
-
var MAX_SCAN_DEPTH = 10;
|
|
117
|
-
var DEFAULT_FILE_MATCH = /\.(tsx?|jsx?)$/;
|
|
118
|
-
function scanDir(dir, onFile, depth = 0, fileMatch = DEFAULT_FILE_MATCH) {
|
|
119
|
-
if (depth > MAX_SCAN_DEPTH) return;
|
|
120
|
-
let entries;
|
|
121
|
-
try {
|
|
122
|
-
entries = (0, import_fs.readdirSync)(dir);
|
|
123
|
-
} catch {
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
for (const entry of entries) {
|
|
127
|
-
if (entry.startsWith(".") || SKIP_DIRS.has(entry)) continue;
|
|
128
|
-
const full = (0, import_path.join)(dir, entry);
|
|
129
|
-
try {
|
|
130
|
-
const st = (0, import_fs.statSync)(full);
|
|
131
|
-
if (st.isDirectory()) scanDir(full, onFile, depth + 1, fileMatch);
|
|
132
|
-
else if (fileMatch.test(entry)) onFile(full, (0, import_fs.readFileSync)(full, "utf-8"));
|
|
133
|
-
} catch {
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
31
|
+
var import_fs3 = require("fs");
|
|
32
|
+
var import_path4 = require("path");
|
|
33
|
+
var import_url = require("url");
|
|
34
|
+
var import_module = require("module");
|
|
137
35
|
|
|
138
36
|
// src/vite-plugin/cssVars.ts
|
|
139
37
|
function hexToRgbTriplet(hex) {
|
|
@@ -395,10 +293,10 @@ function formatKbachCSS(tokenRules, theme, usedTags) {
|
|
|
395
293
|
lines.push("", prettifyCSS(text));
|
|
396
294
|
return lines.join("\n");
|
|
397
295
|
}
|
|
398
|
-
function writeKbachToFile(filePath, css,
|
|
296
|
+
function writeKbachToFile(filePath, css, readFile, writeFile) {
|
|
399
297
|
let existing = "";
|
|
400
298
|
try {
|
|
401
|
-
existing =
|
|
299
|
+
existing = readFile(filePath);
|
|
402
300
|
} catch {
|
|
403
301
|
}
|
|
404
302
|
const block = `${KBACH_START}
|
|
@@ -410,66 +308,10 @@ ${KBACH_END}`;
|
|
|
410
308
|
|
|
411
309
|
${block}` : block;
|
|
412
310
|
if (next === existing) return false;
|
|
413
|
-
|
|
311
|
+
writeFile(filePath, next);
|
|
414
312
|
return true;
|
|
415
313
|
}
|
|
416
314
|
|
|
417
|
-
// src/vite-plugin/unknownClassWarnings.ts
|
|
418
|
-
var import_path2 = require("path");
|
|
419
|
-
var CSS_CLASS_SELECTOR_RE = /\.(-?[a-zA-Z_][a-zA-Z0-9_-]*)/g;
|
|
420
|
-
var CSS_FILE_RE = /\.(css|scss|sass|less)$/;
|
|
421
|
-
var useColor = !!process.stdout?.isTTY && !process.env.NO_COLOR;
|
|
422
|
-
var paint = (code, s) => useColor ? `\x1B[${code}m${s}\x1B[0m` : s;
|
|
423
|
-
var green = (s) => paint("32", s);
|
|
424
|
-
var blue = (s) => paint("34", s);
|
|
425
|
-
var white = (s) => paint("97", s);
|
|
426
|
-
var gray = (s) => paint("90", s);
|
|
427
|
-
var bold = (s) => paint("1", s);
|
|
428
|
-
var ARROW = () => green("\u2192");
|
|
429
|
-
var TAG = () => bold(green("[kbach]"));
|
|
430
|
-
var highlight = (s) => bold(white(s));
|
|
431
|
-
function findLineCol(code, needle) {
|
|
432
|
-
const index = code.indexOf(needle);
|
|
433
|
-
if (index === -1) return null;
|
|
434
|
-
let line = 1;
|
|
435
|
-
let lastNewline = -1;
|
|
436
|
-
for (let i = 0; i < index; i++) {
|
|
437
|
-
if (code.charCodeAt(i) === 10) {
|
|
438
|
-
line++;
|
|
439
|
-
lastNewline = i;
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
return { line, column: index - lastNewline };
|
|
443
|
-
}
|
|
444
|
-
function scanCssFileInto(code, into) {
|
|
445
|
-
const re = new RegExp(CSS_CLASS_SELECTOR_RE.source, "g");
|
|
446
|
-
let m;
|
|
447
|
-
while ((m = re.exec(code)) !== null) into.add(m[1]);
|
|
448
|
-
}
|
|
449
|
-
function scanProjectCssSelectors(root, includeDirs) {
|
|
450
|
-
const projectCssClasses = /* @__PURE__ */ new Set();
|
|
451
|
-
for (const dir of includeDirs) {
|
|
452
|
-
scanDir((0, import_path2.join)(root, dir), (_filePath, code) => scanCssFileInto(code, projectCssClasses), 0, CSS_FILE_RE);
|
|
453
|
-
}
|
|
454
|
-
return projectCssClasses;
|
|
455
|
-
}
|
|
456
|
-
function stripModifierPrefix(token) {
|
|
457
|
-
return token.replace(/^(?:[a-zA-Z0-9_/-]+:)+/, "");
|
|
458
|
-
}
|
|
459
|
-
var MARKER_CLASSES = /* @__PURE__ */ new Set(["group", "peer"]);
|
|
460
|
-
function warnIfUnknownClass(token, filePath, code, isResolvable, projectCssClasses, warnedTokens) {
|
|
461
|
-
if (isResolvable || warnedTokens.has(token)) return;
|
|
462
|
-
const base = stripModifierPrefix(token);
|
|
463
|
-
if (!base || projectCssClasses.has(base) || MARKER_CLASSES.has(base)) return;
|
|
464
|
-
warnedTokens.add(token);
|
|
465
|
-
const pos = findLineCol(code, token);
|
|
466
|
-
const location = pos ? `${blue(filePath)}${green(`:${pos.line}:${pos.column}`)}` : blue(filePath);
|
|
467
|
-
console.warn(
|
|
468
|
-
`${ARROW()} ${TAG()} ${gray("Unknown class")} ${highlight(token)} ${gray("\u2014 no Kbach utility or project CSS rule matches it. Typo?")}
|
|
469
|
-
${bold("at")} ${location}`
|
|
470
|
-
);
|
|
471
|
-
}
|
|
472
|
-
|
|
473
315
|
// src/vite-plugin/classNameHints.ts
|
|
474
316
|
var STRUCTURAL_TOKENS = [
|
|
475
317
|
// layout.rs
|
|
@@ -1017,6 +859,41 @@ var STRUCTURAL_TOKENS = [
|
|
|
1017
859
|
"aria-required:",
|
|
1018
860
|
"aria-selected:",
|
|
1019
861
|
"aria-busy:",
|
|
862
|
+
// registry.rs modifiers, added later — same "fixed/static, finite" bar
|
|
863
|
+
// as the Phase 24 block above. Structural -of-type/empty, form-state and
|
|
864
|
+
// validation pseudo-classes, other native pseudo-classes with no aria-*
|
|
865
|
+
// counterpart, a pseudo-element, pointer media variants, and the
|
|
866
|
+
// direct-children/all-descendants variants. "supports-[...]:"/"in-*:" are
|
|
867
|
+
// deliberately excluded here for the same "no finite enumerable set"
|
|
868
|
+
// reason as has-[...]:/group-<pseudo>: above.
|
|
869
|
+
"first-of-type:",
|
|
870
|
+
"last-of-type:",
|
|
871
|
+
"only-of-type:",
|
|
872
|
+
"empty:",
|
|
873
|
+
"enabled:",
|
|
874
|
+
"read-only:",
|
|
875
|
+
"required:",
|
|
876
|
+
"optional:",
|
|
877
|
+
"valid:",
|
|
878
|
+
"invalid:",
|
|
879
|
+
"in-range:",
|
|
880
|
+
"out-of-range:",
|
|
881
|
+
"default:",
|
|
882
|
+
"indeterminate:",
|
|
883
|
+
"placeholder-shown:",
|
|
884
|
+
"autofill:",
|
|
885
|
+
"user-valid:",
|
|
886
|
+
"user-invalid:",
|
|
887
|
+
"target:",
|
|
888
|
+
"fullscreen:",
|
|
889
|
+
"popover-open:",
|
|
890
|
+
"details-content:",
|
|
891
|
+
"pointer-coarse:",
|
|
892
|
+
"pointer-fine:",
|
|
893
|
+
"any-pointer-coarse:",
|
|
894
|
+
"any-pointer-fine:",
|
|
895
|
+
"*:",
|
|
896
|
+
"**:",
|
|
1020
897
|
// Phase 25 (container queries & arbitrary properties). "@container" is a
|
|
1021
898
|
// real UTILITY (container-type: inline-size), not a modifier — included
|
|
1022
899
|
// as a bare token like "flex"/"hidden" above, not with the trailing ":"
|
|
@@ -1421,21 +1298,54 @@ var defaultTheme = {
|
|
|
1421
1298
|
...PALETTE,
|
|
1422
1299
|
surface: { light: "#f9fafb", dark: "#111827" }
|
|
1423
1300
|
},
|
|
1301
|
+
// Real Tailwind's default spacing scale in full — every step is exactly
|
|
1302
|
+
// `n * 4px` (Tailwind's base spacing unit), including the fractional
|
|
1303
|
+
// `.5` steps (`1.5` -> 6px, `2.5` -> 10px, `3.5` -> 14px) and the "px"
|
|
1304
|
+
// literal (a bare 1px, for `p-px`-style hairline spacing). This table is
|
|
1305
|
+
// only the "usual stops", though — the Rust/JS engines' shared
|
|
1306
|
+
// `spacing_px`/`spacingPx` helper falls back to Tailwind v4's own live
|
|
1307
|
+
// `n * 4px` formula for any bare numeric step NOT listed here (`p-0.25`,
|
|
1308
|
+
// `gap-1.75`, `w-13`, ...), so this table exists to name the common
|
|
1309
|
+
// values and give a theme customization something to override, not to
|
|
1310
|
+
// enumerate every resolvable step. "px" is the one exception that must
|
|
1311
|
+
// stay listed explicitly — it isn't a number, so the formula can't
|
|
1312
|
+
// derive it.
|
|
1424
1313
|
spacing: {
|
|
1425
|
-
// "0" is always a hardcoded zero in real Tailwind's own scale too,
|
|
1426
|
-
// regardless of the base spacing unit — needed for `p-0`/`m-0`/
|
|
1427
|
-
// `top-0`/`inset-0`/etc., and specifically for `sticky top-0` (a
|
|
1428
|
-
// sticky element needs an explicit offset to have any stick point at
|
|
1429
|
-
// all; without one it behaves just like `relative`, no different from
|
|
1430
|
-
// omitting `top-0` outright). Confirmed missing by hand: this
|
|
1431
|
-
// sandbox's own header used `sticky top-0`, which silently never
|
|
1432
|
-
// resolved (no "0" spacing key existed) until this was added.
|
|
1433
1314
|
"0": 0,
|
|
1315
|
+
"px": 1,
|
|
1316
|
+
"0.5": 2,
|
|
1434
1317
|
"1": 4,
|
|
1318
|
+
"1.5": 6,
|
|
1435
1319
|
"2": 8,
|
|
1320
|
+
"2.5": 10,
|
|
1321
|
+
"3": 12,
|
|
1322
|
+
"3.5": 14,
|
|
1436
1323
|
"4": 16,
|
|
1324
|
+
"5": 20,
|
|
1437
1325
|
"6": 24,
|
|
1438
|
-
"
|
|
1326
|
+
"7": 28,
|
|
1327
|
+
"8": 32,
|
|
1328
|
+
"9": 36,
|
|
1329
|
+
"10": 40,
|
|
1330
|
+
"11": 44,
|
|
1331
|
+
"12": 48,
|
|
1332
|
+
"14": 56,
|
|
1333
|
+
"16": 64,
|
|
1334
|
+
"20": 80,
|
|
1335
|
+
"24": 96,
|
|
1336
|
+
"28": 112,
|
|
1337
|
+
"32": 128,
|
|
1338
|
+
"36": 144,
|
|
1339
|
+
"40": 160,
|
|
1340
|
+
"44": 176,
|
|
1341
|
+
"48": 192,
|
|
1342
|
+
"52": 208,
|
|
1343
|
+
"56": 224,
|
|
1344
|
+
"60": 240,
|
|
1345
|
+
"64": 256,
|
|
1346
|
+
"72": 288,
|
|
1347
|
+
"80": 320,
|
|
1348
|
+
"96": 384
|
|
1439
1349
|
},
|
|
1440
1350
|
screens: {
|
|
1441
1351
|
sm: 640,
|
|
@@ -1444,33 +1354,350 @@ var defaultTheme = {
|
|
|
1444
1354
|
xl: 1280,
|
|
1445
1355
|
"2xl": 1536
|
|
1446
1356
|
},
|
|
1447
|
-
|
|
1357
|
+
// Byte-for-byte the same three stacks
|
|
1358
|
+
// packages/core-engine/src/resolvers/typography.rs's `font_family_value`
|
|
1359
|
+
// falls back to — kept here too (rather than leaving this empty and
|
|
1360
|
+
// relying purely on the Rust-side fallback) so `defaultTheme.fontFamily.sans`
|
|
1361
|
+
// is a real, readable value for anything that wants to extend rather
|
|
1362
|
+
// than replace it (see `KbachConfig.extend.fontFamily` in config.ts).
|
|
1363
|
+
fontFamily: {
|
|
1364
|
+
sans: 'ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',
|
|
1365
|
+
serif: 'ui-serif, Georgia, Cambria, "Times New Roman", Times, serif',
|
|
1366
|
+
mono: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace'
|
|
1367
|
+
},
|
|
1368
|
+
// Matches real Tailwind's own default (`darkMode: 'media'`): pure
|
|
1369
|
+
// `@media (prefers-color-scheme: dark)`, works with zero JS — a project
|
|
1370
|
+
// using only static `className` strings gets working `dark:` classes
|
|
1371
|
+
// out of the box, with no runtime import required to write `data-theme`/
|
|
1372
|
+
// `.dark` onto `<html>` (see darkModeStore.ts's `applyToDom`). The
|
|
1373
|
+
// tradeoff: `'media'` can't be overridden by a manual light/dark toggle —
|
|
1374
|
+
// `useTheme()`/`toggleGlobalDarkMode()` still track `isDark` correctly
|
|
1375
|
+
// for JS consumers, but flipping it has no visible effect on `dark:`
|
|
1376
|
+
// classes, since the browser's media query only ever reflects the real
|
|
1377
|
+
// OS setting. Opt into `darkMode: 'class'` or `'attribute'` explicitly
|
|
1378
|
+
// (via `kbach.config.js` or the `config`/`theme` plugin option) as soon
|
|
1379
|
+
// as an app wants a real in-app toggle.
|
|
1380
|
+
darkMode: "media",
|
|
1381
|
+
// Empty by default, matching real Tailwind's own defaults (no forced
|
|
1382
|
+
// centering/padding) — see `ContainerConfig`'s own doc comment.
|
|
1383
|
+
container: {}
|
|
1448
1384
|
};
|
|
1449
1385
|
var activeThemeJson = JSON.stringify(defaultTheme);
|
|
1450
1386
|
|
|
1451
|
-
// src/
|
|
1452
|
-
|
|
1387
|
+
// src/colorVariables.ts
|
|
1388
|
+
function parseHexRgb(hex) {
|
|
1389
|
+
const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(hex.trim());
|
|
1390
|
+
if (!m) return null;
|
|
1391
|
+
let h = m[1];
|
|
1392
|
+
if (h.length === 3) h = h.split("").map((c) => c + c).join("");
|
|
1393
|
+
const n = parseInt(h, 16);
|
|
1394
|
+
return [n >> 16 & 255, n >> 8 & 255, n & 255];
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
// src/config.ts
|
|
1398
|
+
function applyOpacityToHex(hex, opacityText) {
|
|
1399
|
+
const rgb = parseHexRgb(hex);
|
|
1400
|
+
if (!rgb) return hex;
|
|
1401
|
+
const a = Number(opacityText) / 100;
|
|
1402
|
+
const clamped = Number.isFinite(a) ? Math.min(1, Math.max(0, a)) : 1;
|
|
1403
|
+
return `rgba(${rgb.join(",")},${clamped})`;
|
|
1404
|
+
}
|
|
1405
|
+
function resolveColorEntry(value, lookup) {
|
|
1406
|
+
if (typeof value !== "string") return value;
|
|
1407
|
+
const slash = value.indexOf("/");
|
|
1408
|
+
const name = slash === -1 ? value : value.slice(0, slash);
|
|
1409
|
+
const opacity = slash === -1 ? void 0 : value.slice(slash + 1);
|
|
1410
|
+
const base = lookup(name);
|
|
1411
|
+
if (base === void 0) return value;
|
|
1412
|
+
if (opacity === void 0) return base;
|
|
1413
|
+
return typeof base === "string" ? applyOpacityToHex(base, opacity) : { light: applyOpacityToHex(base.light, opacity), dark: applyOpacityToHex(base.dark, opacity) };
|
|
1414
|
+
}
|
|
1415
|
+
function resolveColors(extend, existing) {
|
|
1416
|
+
const resolved = {};
|
|
1417
|
+
const lookup = (name) => resolved[name] ?? existing[name];
|
|
1418
|
+
for (const [name, value] of Object.entries(extend)) {
|
|
1419
|
+
resolved[name] = resolveColorEntry(value, lookup);
|
|
1420
|
+
}
|
|
1421
|
+
return resolved;
|
|
1422
|
+
}
|
|
1423
|
+
function resolveKbachConfig(config) {
|
|
1424
|
+
let theme = { ...defaultTheme, ...config.theme };
|
|
1425
|
+
if (config.extend?.colors) {
|
|
1426
|
+
theme = { ...theme, colors: { ...theme.colors, ...resolveColors(config.extend.colors, theme.colors) } };
|
|
1427
|
+
}
|
|
1428
|
+
if (config.extend?.spacing) {
|
|
1429
|
+
theme = { ...theme, spacing: { ...theme.spacing, ...config.extend.spacing } };
|
|
1430
|
+
}
|
|
1431
|
+
if (config.extend?.screens) {
|
|
1432
|
+
theme = { ...theme, screens: { ...theme.screens, ...config.extend.screens } };
|
|
1433
|
+
}
|
|
1434
|
+
if (config.extend?.fontFamily) {
|
|
1435
|
+
theme = { ...theme, fontFamily: { ...theme.fontFamily, ...config.extend.fontFamily } };
|
|
1436
|
+
}
|
|
1437
|
+
if (config.extend?.container) {
|
|
1438
|
+
theme = { ...theme, container: { ...theme.container, ...config.extend.container } };
|
|
1439
|
+
}
|
|
1440
|
+
if (config.darkMode) {
|
|
1441
|
+
theme = { ...theme, darkMode: config.darkMode };
|
|
1442
|
+
}
|
|
1443
|
+
return theme;
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
// src/staticCss/engine.ts
|
|
1447
|
+
var import_fs2 = require("fs");
|
|
1448
|
+
var import_path3 = require("path");
|
|
1449
|
+
|
|
1450
|
+
// src/vite-plugin/wasmNode.ts
|
|
1451
|
+
var import_node = require("@kbach/core-engine/node");
|
|
1452
|
+
function generateCssForToken(token, themeJson) {
|
|
1453
|
+
return JSON.parse((0, import_node.generate_css)(token, themeJson));
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
// src/vite-plugin/scan.ts
|
|
1457
|
+
var import_fs = require("fs");
|
|
1458
|
+
var import_path = require("path");
|
|
1459
|
+
function stripComments(code) {
|
|
1460
|
+
let out = "";
|
|
1461
|
+
let i = 0;
|
|
1462
|
+
const n = code.length;
|
|
1463
|
+
while (i < n) {
|
|
1464
|
+
const two = code[i] + (code[i + 1] ?? "");
|
|
1465
|
+
if (two === "//") {
|
|
1466
|
+
i += 2;
|
|
1467
|
+
while (i < n && code[i] !== "\n") i++;
|
|
1468
|
+
continue;
|
|
1469
|
+
}
|
|
1470
|
+
if (two === "/*") {
|
|
1471
|
+
i += 2;
|
|
1472
|
+
while (i < n && code[i] + (code[i + 1] ?? "") !== "*/") i++;
|
|
1473
|
+
i += 2;
|
|
1474
|
+
continue;
|
|
1475
|
+
}
|
|
1476
|
+
const ch = code[i];
|
|
1477
|
+
if (ch === '"' || ch === "'") {
|
|
1478
|
+
out += ch;
|
|
1479
|
+
i++;
|
|
1480
|
+
while (i < n && code[i] !== ch) {
|
|
1481
|
+
if (code[i] === "\\") {
|
|
1482
|
+
out += code[i] + (code[i + 1] ?? "");
|
|
1483
|
+
i += 2;
|
|
1484
|
+
continue;
|
|
1485
|
+
}
|
|
1486
|
+
out += code[i];
|
|
1487
|
+
i++;
|
|
1488
|
+
}
|
|
1489
|
+
out += code[i] ?? "";
|
|
1490
|
+
i++;
|
|
1491
|
+
continue;
|
|
1492
|
+
}
|
|
1493
|
+
if (ch === "`") {
|
|
1494
|
+
out += ch;
|
|
1495
|
+
i++;
|
|
1496
|
+
let interpolationDepth = 0;
|
|
1497
|
+
while (i < n) {
|
|
1498
|
+
if (interpolationDepth === 0 && code[i] === "`") {
|
|
1499
|
+
out += code[i];
|
|
1500
|
+
i++;
|
|
1501
|
+
break;
|
|
1502
|
+
}
|
|
1503
|
+
if (interpolationDepth === 0 && code[i] === "\\") {
|
|
1504
|
+
out += code[i] + (code[i + 1] ?? "");
|
|
1505
|
+
i += 2;
|
|
1506
|
+
continue;
|
|
1507
|
+
}
|
|
1508
|
+
if (interpolationDepth === 0 && code[i] + (code[i + 1] ?? "") === "${") {
|
|
1509
|
+
out += "${";
|
|
1510
|
+
i += 2;
|
|
1511
|
+
interpolationDepth = 1;
|
|
1512
|
+
continue;
|
|
1513
|
+
}
|
|
1514
|
+
if (interpolationDepth > 0) {
|
|
1515
|
+
if (code[i] === "{") interpolationDepth++;
|
|
1516
|
+
else if (code[i] === "}") {
|
|
1517
|
+
interpolationDepth--;
|
|
1518
|
+
if (interpolationDepth === 0) {
|
|
1519
|
+
out += "}";
|
|
1520
|
+
i++;
|
|
1521
|
+
continue;
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
out += code[i];
|
|
1526
|
+
i++;
|
|
1527
|
+
}
|
|
1528
|
+
continue;
|
|
1529
|
+
}
|
|
1530
|
+
out += ch;
|
|
1531
|
+
i++;
|
|
1532
|
+
}
|
|
1533
|
+
return out;
|
|
1534
|
+
}
|
|
1535
|
+
function splitClassTokens(str) {
|
|
1536
|
+
return str.split(/\s+/).filter(Boolean);
|
|
1537
|
+
}
|
|
1538
|
+
var NOT_A_CLASS_NAME_RE = /[${}]|-$|^[?:&|!"'=<>]+$/;
|
|
1539
|
+
function pushTokens(str, into) {
|
|
1540
|
+
for (const tok of splitClassTokens(str)) {
|
|
1541
|
+
if (tok && !NOT_A_CLASS_NAME_RE.test(tok)) into.add(tok);
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
function pushTemplateLiteralBody(body, into) {
|
|
1545
|
+
const interpolationRe = /\$\{(?:[^{}]|\{[^{}]*\})*\}/g;
|
|
1546
|
+
let im;
|
|
1547
|
+
while ((im = interpolationRe.exec(body)) !== null) {
|
|
1548
|
+
pushClassLikeStrings(im[0], into);
|
|
1549
|
+
}
|
|
1550
|
+
pushTokens(body.replace(/\$\{(?:[^{}]|\{[^{}]*\})*\}/g, () => "$"), into);
|
|
1551
|
+
}
|
|
1552
|
+
function pushClassLikeStrings(text, into) {
|
|
1553
|
+
const quotedStringRe = /(["'`])((?:(?!\1).)*)\1/g;
|
|
1554
|
+
let qm;
|
|
1555
|
+
while ((qm = quotedStringRe.exec(text)) !== null) {
|
|
1556
|
+
const [, quote, content] = qm;
|
|
1557
|
+
if (quote === "`") pushTemplateLiteralBody(content, into);
|
|
1558
|
+
else pushTokens(content, into);
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
var JSX_TAG_RE = /<([a-z][a-zA-Z0-9]*)[\s/>]/g;
|
|
1562
|
+
function scanUsedTags(code) {
|
|
1563
|
+
const cleaned = stripComments(code);
|
|
1564
|
+
const tags = /* @__PURE__ */ new Set();
|
|
1565
|
+
let m;
|
|
1566
|
+
JSX_TAG_RE.lastIndex = 0;
|
|
1567
|
+
while ((m = JSX_TAG_RE.exec(cleaned)) !== null) tags.add(m[1]);
|
|
1568
|
+
return tags;
|
|
1569
|
+
}
|
|
1570
|
+
function extractClassStrings(rawCode) {
|
|
1571
|
+
const code = stripComments(rawCode);
|
|
1572
|
+
const found = /* @__PURE__ */ new Set();
|
|
1573
|
+
const simpleRe = /(?:className|kb)=(["'])((?:(?!\1).)*)\1/g;
|
|
1574
|
+
let m;
|
|
1575
|
+
while ((m = simpleRe.exec(code)) !== null) pushTokens(m[2], found);
|
|
1576
|
+
const jsxExprRe = /(?:className|kb)=\{/g;
|
|
1577
|
+
while ((m = jsxExprRe.exec(code)) !== null) {
|
|
1578
|
+
let depth = 1;
|
|
1579
|
+
let i = m.index + m[0].length;
|
|
1580
|
+
let block = "";
|
|
1581
|
+
while (i < code.length && depth > 0) {
|
|
1582
|
+
const ch = code[i];
|
|
1583
|
+
if (ch === "{") depth++;
|
|
1584
|
+
else if (ch === "}") {
|
|
1585
|
+
if (--depth === 0) break;
|
|
1586
|
+
}
|
|
1587
|
+
block += ch;
|
|
1588
|
+
i++;
|
|
1589
|
+
}
|
|
1590
|
+
jsxExprRe.lastIndex = i + 1;
|
|
1591
|
+
pushClassLikeStrings(block, found);
|
|
1592
|
+
}
|
|
1593
|
+
const classComposerCallRe = /(?:clsx|cn|classnames|cx|kb)\(/g;
|
|
1594
|
+
while ((m = classComposerCallRe.exec(code)) !== null) {
|
|
1595
|
+
let depth = 1;
|
|
1596
|
+
let i = m.index + m[0].length;
|
|
1597
|
+
let block = "";
|
|
1598
|
+
while (i < code.length && depth > 0) {
|
|
1599
|
+
const ch = code[i];
|
|
1600
|
+
if (ch === "(") depth++;
|
|
1601
|
+
else if (ch === ")") {
|
|
1602
|
+
if (--depth === 0) break;
|
|
1603
|
+
}
|
|
1604
|
+
block += ch;
|
|
1605
|
+
i++;
|
|
1606
|
+
}
|
|
1607
|
+
classComposerCallRe.lastIndex = i + 1;
|
|
1608
|
+
pushClassLikeStrings(block, found);
|
|
1609
|
+
}
|
|
1610
|
+
const templateRe = /`([^`]{1,2000})`/g;
|
|
1611
|
+
while ((m = templateRe.exec(code)) !== null) pushTemplateLiteralBody(m[1], found);
|
|
1612
|
+
return [...found];
|
|
1613
|
+
}
|
|
1614
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "out", ".next", ".output", ".git", ".svn", ".cache", "coverage"]);
|
|
1615
|
+
var MAX_SCAN_DEPTH = 10;
|
|
1616
|
+
var DEFAULT_FILE_MATCH = /\.(tsx?|jsx?)$/;
|
|
1617
|
+
function scanDir(dir, onFile, depth = 0, fileMatch = DEFAULT_FILE_MATCH) {
|
|
1618
|
+
if (depth > MAX_SCAN_DEPTH) return;
|
|
1619
|
+
let entries;
|
|
1620
|
+
try {
|
|
1621
|
+
entries = (0, import_fs.readdirSync)(dir);
|
|
1622
|
+
} catch {
|
|
1623
|
+
return;
|
|
1624
|
+
}
|
|
1625
|
+
for (const entry of entries) {
|
|
1626
|
+
if (entry.startsWith(".") || SKIP_DIRS.has(entry)) continue;
|
|
1627
|
+
const full = (0, import_path.join)(dir, entry);
|
|
1628
|
+
try {
|
|
1629
|
+
const st = (0, import_fs.statSync)(full);
|
|
1630
|
+
if (st.isDirectory()) scanDir(full, onFile, depth + 1, fileMatch);
|
|
1631
|
+
else if (fileMatch.test(entry)) onFile(full, (0, import_fs.readFileSync)(full, "utf-8"));
|
|
1632
|
+
} catch {
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
// src/vite-plugin/unknownClassWarnings.ts
|
|
1638
|
+
var import_path2 = require("path");
|
|
1639
|
+
var CSS_CLASS_SELECTOR_RE = /\.(-?[a-zA-Z_][a-zA-Z0-9_-]*)/g;
|
|
1640
|
+
var CSS_FILE_RE = /\.(css|scss|sass|less)$/;
|
|
1641
|
+
var useColor = !!process.stdout?.isTTY && !process.env.NO_COLOR;
|
|
1642
|
+
var paint = (code, s) => useColor ? `\x1B[${code}m${s}\x1B[0m` : s;
|
|
1643
|
+
var green = (s) => paint("32", s);
|
|
1644
|
+
var blue = (s) => paint("34", s);
|
|
1645
|
+
var white = (s) => paint("97", s);
|
|
1646
|
+
var gray = (s) => paint("90", s);
|
|
1647
|
+
var bold = (s) => paint("1", s);
|
|
1648
|
+
var ARROW = () => green("\u2192");
|
|
1649
|
+
var TAG = () => bold(green("[kbach]"));
|
|
1650
|
+
var highlight = (s) => bold(white(s));
|
|
1651
|
+
function findLineCol(code, needle) {
|
|
1652
|
+
const index = code.indexOf(needle);
|
|
1653
|
+
if (index === -1) return null;
|
|
1654
|
+
let line = 1;
|
|
1655
|
+
let lastNewline = -1;
|
|
1656
|
+
for (let i = 0; i < index; i++) {
|
|
1657
|
+
if (code.charCodeAt(i) === 10) {
|
|
1658
|
+
line++;
|
|
1659
|
+
lastNewline = i;
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
return { line, column: index - lastNewline };
|
|
1663
|
+
}
|
|
1664
|
+
function scanCssFileInto(code, into) {
|
|
1665
|
+
const re = new RegExp(CSS_CLASS_SELECTOR_RE.source, "g");
|
|
1666
|
+
let m;
|
|
1667
|
+
while ((m = re.exec(code)) !== null) into.add(m[1]);
|
|
1668
|
+
}
|
|
1669
|
+
function scanProjectCssSelectors(root, includeDirs) {
|
|
1670
|
+
const projectCssClasses = /* @__PURE__ */ new Set();
|
|
1671
|
+
for (const dir of includeDirs) {
|
|
1672
|
+
scanDir((0, import_path2.join)(root, dir), (_filePath, code) => scanCssFileInto(code, projectCssClasses), 0, CSS_FILE_RE);
|
|
1673
|
+
}
|
|
1674
|
+
return projectCssClasses;
|
|
1675
|
+
}
|
|
1676
|
+
function stripModifierPrefix(token) {
|
|
1677
|
+
return token.replace(/^(?:[a-zA-Z0-9_/-]+:)+/, "");
|
|
1678
|
+
}
|
|
1679
|
+
var MARKER_CLASSES = /* @__PURE__ */ new Set(["group", "peer"]);
|
|
1680
|
+
function warnIfUnknownClass(token, filePath, code, isResolvable, projectCssClasses, warnedTokens) {
|
|
1681
|
+
if (isResolvable || warnedTokens.has(token)) return;
|
|
1682
|
+
const base = stripModifierPrefix(token);
|
|
1683
|
+
if (!base || projectCssClasses.has(base) || MARKER_CLASSES.has(base)) return;
|
|
1684
|
+
warnedTokens.add(token);
|
|
1685
|
+
const pos = findLineCol(code, token);
|
|
1686
|
+
const location = pos ? `${blue(filePath)}${green(`:${pos.line}:${pos.column}`)}` : blue(filePath);
|
|
1687
|
+
console.warn(
|
|
1688
|
+
`${ARROW()} ${TAG()} ${gray("Unknown class")} ${highlight(token)} ${gray("\u2014 no Kbach utility or project CSS rule matches it. Typo?")}
|
|
1689
|
+
${bold("at")} ${location}`
|
|
1690
|
+
);
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
// src/staticCss/engine.ts
|
|
1453
1694
|
function normPath(p) {
|
|
1454
1695
|
const fwd = p.replace(/\\/g, "/");
|
|
1455
1696
|
return process.platform === "win32" ? fwd.toLowerCase() : fwd;
|
|
1456
1697
|
}
|
|
1457
1698
|
var SAFELIST_KEY = "\0kbach-safelist";
|
|
1458
|
-
function
|
|
1459
|
-
|
|
1460
|
-
}
|
|
1461
|
-
function writeFile(path, content) {
|
|
1462
|
-
(0, import_fs2.writeFileSync)(path, content, "utf-8");
|
|
1463
|
-
}
|
|
1464
|
-
function writeFileEnsuringDir(path, content) {
|
|
1465
|
-
(0, import_fs2.mkdirSync)((0, import_path3.dirname)(path), { recursive: true });
|
|
1466
|
-
(0, import_fs2.writeFileSync)(path, content, "utf-8");
|
|
1467
|
-
}
|
|
1468
|
-
function kbach(options = {}) {
|
|
1469
|
-
const theme = options.theme ?? defaultTheme;
|
|
1470
|
-
const themeJson = JSON.stringify(theme);
|
|
1471
|
-
const includeDirs = options.include ?? DEFAULT_SCAN_DIRS;
|
|
1472
|
-
const safelist = options.safelist ?? [];
|
|
1473
|
-
let root = process.cwd();
|
|
1699
|
+
function createKbachStaticCssEngine(options) {
|
|
1700
|
+
const { root, theme, themeJson, includeDirs, safelist } = options;
|
|
1474
1701
|
const fileTokens = /* @__PURE__ */ new Map();
|
|
1475
1702
|
const tokenRules = /* @__PURE__ */ new Map();
|
|
1476
1703
|
const fileTags = /* @__PURE__ */ new Map();
|
|
@@ -1494,6 +1721,10 @@ function kbach(options = {}) {
|
|
|
1494
1721
|
fileTokens.set(normPath(filePath), tokens);
|
|
1495
1722
|
fileTags.set(normPath(filePath), scanUsedTags(code));
|
|
1496
1723
|
}
|
|
1724
|
+
function removeFile(filePath) {
|
|
1725
|
+
fileTokens.delete(normPath(filePath));
|
|
1726
|
+
fileTags.delete(normPath(filePath));
|
|
1727
|
+
}
|
|
1497
1728
|
function processSafelist() {
|
|
1498
1729
|
if (safelist.length === 0) return;
|
|
1499
1730
|
const tokens = new Set(safelist);
|
|
@@ -1517,42 +1748,128 @@ function kbach(options = {}) {
|
|
|
1517
1748
|
const rules = tokenRules.get(tok);
|
|
1518
1749
|
if (rules) view.set(tok, rules);
|
|
1519
1750
|
}
|
|
1751
|
+
for (const tok of tokenRules.keys()) {
|
|
1752
|
+
if (!active.has(tok)) tokenRules.delete(tok);
|
|
1753
|
+
}
|
|
1520
1754
|
return formatKbachCSS(view, theme, activeTags());
|
|
1521
1755
|
}
|
|
1756
|
+
function initialScan() {
|
|
1757
|
+
for (const dir of includeDirs) {
|
|
1758
|
+
scanDir((0, import_path3.join)(root, dir), processFile);
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
function scanProjectCssSelectorsOnce() {
|
|
1762
|
+
projectCssClasses = scanProjectCssSelectors(root, includeDirs);
|
|
1763
|
+
}
|
|
1764
|
+
return { processFile, removeFile, initialScan, processSafelist, generateCSS, scanProjectCssSelectorsOnce };
|
|
1765
|
+
}
|
|
1766
|
+
function readSourceFile(path) {
|
|
1767
|
+
return (0, import_fs2.readFileSync)(path, "utf-8");
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
// src/vite-plugin/index.ts
|
|
1771
|
+
function findMainCSSFile(root, includeDirs, explicitCssFile) {
|
|
1772
|
+
if (explicitCssFile) return (0, import_path4.join)(root, explicitCssFile);
|
|
1773
|
+
const searchDirs = [...includeDirs, "."];
|
|
1774
|
+
for (const dir of searchDirs) {
|
|
1775
|
+
const candidate = (0, import_path4.join)(root, dir, "kbach.css");
|
|
1776
|
+
if (!(0, import_fs3.existsSync)(candidate)) continue;
|
|
1777
|
+
try {
|
|
1778
|
+
const content = (0, import_fs3.readFileSync)(candidate, "utf-8");
|
|
1779
|
+
if (content.includes(KBACH_START) && content.includes(KBACH_END)) return candidate;
|
|
1780
|
+
} catch {
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
for (const dir of includeDirs) {
|
|
1784
|
+
const full = (0, import_path4.join)(root, dir);
|
|
1785
|
+
try {
|
|
1786
|
+
if ((0, import_fs3.statSync)(full).isDirectory()) return (0, import_path4.join)(full, "kbach.css");
|
|
1787
|
+
} catch {
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
return (0, import_path4.join)(root, includeDirs[0] ?? "src", "kbach.css");
|
|
1791
|
+
}
|
|
1792
|
+
var DEFAULT_SCAN_DIRS = ["src", "app", "pages", "components"];
|
|
1793
|
+
var CSS_SYNC_DEBOUNCE_MS = 50;
|
|
1794
|
+
function writeFileEnsuringDir(path, content) {
|
|
1795
|
+
(0, import_fs3.mkdirSync)((0, import_path4.dirname)(path), { recursive: true });
|
|
1796
|
+
(0, import_fs3.writeFileSync)(path, content, "utf-8");
|
|
1797
|
+
}
|
|
1798
|
+
function isEsmRequireFailure(err) {
|
|
1799
|
+
if (!(err instanceof Error)) return false;
|
|
1800
|
+
return err.code === "ERR_REQUIRE_ESM" || /is not defined in ES module scope/.test(err.message);
|
|
1801
|
+
}
|
|
1802
|
+
async function importConfigModule(path) {
|
|
1803
|
+
const mod = await import((0, import_url.pathToFileURL)(path).href);
|
|
1804
|
+
return mod.default ?? mod;
|
|
1805
|
+
}
|
|
1806
|
+
function requireConfigModule(root, path) {
|
|
1807
|
+
const mod = (0, import_module.createRequire)(root)(path);
|
|
1808
|
+
return mod.default ?? mod;
|
|
1809
|
+
}
|
|
1810
|
+
async function loadConfigFile(root) {
|
|
1811
|
+
const cjsPath = (0, import_path4.join)(root, "kbach.config.cjs");
|
|
1812
|
+
if ((0, import_fs3.existsSync)(cjsPath)) return requireConfigModule(root, cjsPath);
|
|
1813
|
+
const mjsPath = (0, import_path4.join)(root, "kbach.config.mjs");
|
|
1814
|
+
if ((0, import_fs3.existsSync)(mjsPath)) return importConfigModule(mjsPath);
|
|
1815
|
+
const jsPath = (0, import_path4.join)(root, "kbach.config.js");
|
|
1816
|
+
if (!(0, import_fs3.existsSync)(jsPath)) return void 0;
|
|
1817
|
+
try {
|
|
1818
|
+
return requireConfigModule(root, jsPath);
|
|
1819
|
+
} catch (err) {
|
|
1820
|
+
if (!isEsmRequireFailure(err)) throw err;
|
|
1821
|
+
return importConfigModule(jsPath);
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
async function resolveEffectiveTheme(root, options) {
|
|
1825
|
+
if (options.theme) return options.theme;
|
|
1826
|
+
if (options.config) return resolveKbachConfig(options.config);
|
|
1827
|
+
const discovered = await loadConfigFile(root);
|
|
1828
|
+
return discovered ? resolveKbachConfig(discovered) : defaultTheme;
|
|
1829
|
+
}
|
|
1830
|
+
function kbach(options = {}) {
|
|
1831
|
+
const includeDirs = options.include ?? DEFAULT_SCAN_DIRS;
|
|
1832
|
+
const safelist = options.safelist ?? [];
|
|
1833
|
+
let root = process.cwd();
|
|
1834
|
+
let theme = defaultTheme;
|
|
1835
|
+
let engine = createKbachStaticCssEngine({ root, theme, themeJson: JSON.stringify(theme), includeDirs, safelist });
|
|
1836
|
+
let mainCSSFilePath = (0, import_path4.join)(root, "src", "kbach.css");
|
|
1522
1837
|
function mainCSSFile() {
|
|
1523
|
-
return
|
|
1838
|
+
return mainCSSFilePath;
|
|
1524
1839
|
}
|
|
1525
1840
|
function classNameHintsFile() {
|
|
1526
|
-
return (0,
|
|
1841
|
+
return (0, import_path4.join)(root, ".kbach", "kbach-classnames.d.ts");
|
|
1527
1842
|
}
|
|
1528
1843
|
function writeClassNameHints() {
|
|
1529
1844
|
const dts = buildClassNameHintsDts(buildClassTokens(theme));
|
|
1530
1845
|
writeFileEnsuringDir(classNameHintsFile(), dts);
|
|
1531
1846
|
}
|
|
1532
1847
|
function syncMainCSSFile(server) {
|
|
1533
|
-
const
|
|
1534
|
-
for (const tok of tokenRules.keys()) {
|
|
1535
|
-
if (!active.has(tok)) tokenRules.delete(tok);
|
|
1536
|
-
}
|
|
1537
|
-
const changed = writeKbachToFile(mainCSSFile(), generateCSS(), readFile, writeFile);
|
|
1848
|
+
const changed = writeKbachToFile(mainCSSFile(), engine.generateCSS(), readSourceFile, writeFileEnsuringDir);
|
|
1538
1849
|
if (changed && server) server.watcher.emit("change", mainCSSFile());
|
|
1539
1850
|
}
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1851
|
+
let syncTimer = null;
|
|
1852
|
+
function scheduleSync(server) {
|
|
1853
|
+
if (syncTimer) clearTimeout(syncTimer);
|
|
1854
|
+
syncTimer = setTimeout(() => {
|
|
1855
|
+
syncTimer = null;
|
|
1856
|
+
syncMainCSSFile(server);
|
|
1857
|
+
}, CSS_SYNC_DEBOUNCE_MS);
|
|
1544
1858
|
}
|
|
1545
1859
|
return {
|
|
1546
1860
|
name: "kbach",
|
|
1547
1861
|
enforce: "pre",
|
|
1548
|
-
configResolved(resolved) {
|
|
1862
|
+
async configResolved(resolved) {
|
|
1549
1863
|
root = resolved.root;
|
|
1864
|
+
theme = await resolveEffectiveTheme(root, options);
|
|
1865
|
+
engine = createKbachStaticCssEngine({ root, theme, themeJson: JSON.stringify(theme), includeDirs, safelist });
|
|
1866
|
+
mainCSSFilePath = findMainCSSFile(root, includeDirs, options.cssFile);
|
|
1550
1867
|
},
|
|
1551
1868
|
buildStart() {
|
|
1552
|
-
|
|
1553
|
-
initialScan();
|
|
1554
|
-
processSafelist();
|
|
1555
|
-
writeKbachToFile(mainCSSFile(), generateCSS(),
|
|
1869
|
+
engine.scanProjectCssSelectorsOnce();
|
|
1870
|
+
engine.initialScan();
|
|
1871
|
+
engine.processSafelist();
|
|
1872
|
+
writeKbachToFile(mainCSSFile(), engine.generateCSS(), readSourceFile, writeFileEnsuringDir);
|
|
1556
1873
|
writeClassNameHints();
|
|
1557
1874
|
},
|
|
1558
1875
|
// handleHotUpdate covers edits; add/unlink (create/delete) land on the
|
|
@@ -1563,17 +1880,15 @@ function kbach(options = {}) {
|
|
|
1563
1880
|
if (file.includes("node_modules")) return;
|
|
1564
1881
|
if (!/\.(tsx?|jsx?)$/.test(file)) return;
|
|
1565
1882
|
if (isUnlink) {
|
|
1566
|
-
|
|
1567
|
-
fileTags.delete(normPath(file));
|
|
1883
|
+
engine.removeFile(file);
|
|
1568
1884
|
} else {
|
|
1569
1885
|
try {
|
|
1570
|
-
processFile(file,
|
|
1886
|
+
engine.processFile(file, readSourceFile(file));
|
|
1571
1887
|
} catch {
|
|
1572
|
-
|
|
1573
|
-
fileTags.delete(normPath(file));
|
|
1888
|
+
engine.removeFile(file);
|
|
1574
1889
|
}
|
|
1575
1890
|
}
|
|
1576
|
-
|
|
1891
|
+
scheduleSync(server);
|
|
1577
1892
|
};
|
|
1578
1893
|
server.watcher.on("add", (file) => onAddOrUnlink(file, false));
|
|
1579
1894
|
server.watcher.on("unlink", (file) => onAddOrUnlink(file, true));
|
|
@@ -1582,16 +1897,20 @@ function kbach(options = {}) {
|
|
|
1582
1897
|
if (file.includes("node_modules")) return;
|
|
1583
1898
|
if (!/\.(tsx?|jsx?)$/.test(file)) return;
|
|
1584
1899
|
try {
|
|
1585
|
-
processFile(file,
|
|
1900
|
+
engine.processFile(file, readSourceFile(file));
|
|
1586
1901
|
} catch {
|
|
1587
|
-
|
|
1588
|
-
fileTags.delete(normPath(file));
|
|
1902
|
+
engine.removeFile(file);
|
|
1589
1903
|
}
|
|
1590
|
-
|
|
1904
|
+
scheduleSync(server);
|
|
1591
1905
|
}
|
|
1592
1906
|
};
|
|
1593
1907
|
}
|
|
1594
1908
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1595
1909
|
0 && (module.exports = {
|
|
1596
|
-
|
|
1910
|
+
CSS_SYNC_DEBOUNCE_MS,
|
|
1911
|
+
DEFAULT_SCAN_DIRS,
|
|
1912
|
+
findMainCSSFile,
|
|
1913
|
+
kbach,
|
|
1914
|
+
loadConfigFile,
|
|
1915
|
+
resolveEffectiveTheme
|
|
1597
1916
|
});
|