@kbach/react 0.5.20 → 0.5.21

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.
@@ -5,7 +5,7 @@ import { F as FrameworkConfig } from './types-CV5vXnSH.mjs';
5
5
  * @kbach/react/vite
6
6
  *
7
7
  * Vite plugin — scans source files for Kbach className/kb props and generates CSS.
8
- * Zero setup: just add kbach() to your plugins array. CSS is auto-injected.
8
+ * Zero setup: just add kbach() to your plugins array.
9
9
  * Only generates CSS for classes you actually use (like Tailwind).
10
10
  */
11
11
 
@@ -5,7 +5,7 @@ import { F as FrameworkConfig } from './types-CV5vXnSH.js';
5
5
  * @kbach/react/vite
6
6
  *
7
7
  * Vite plugin — scans source files for Kbach className/kb props and generates CSS.
8
- * Zero setup: just add kbach() to your plugins array. CSS is auto-injected.
8
+ * Zero setup: just add kbach() to your plugins array.
9
9
  * Only generates CSS for classes you actually use (like Tailwind).
10
10
  */
11
11
 
@@ -3397,7 +3397,6 @@ function kbach(userConfig) {
3397
3397
  const tokenCSS = /* @__PURE__ */ new Map();
3398
3398
  let root = process.cwd();
3399
3399
  let server = null;
3400
- let isDev = false;
3401
3400
  function processClassString(classStr) {
3402
3401
  let added = false;
3403
3402
  for (const token of classStr.split(/\s+/).filter(Boolean)) {
@@ -3423,42 +3422,53 @@ function kbach(userConfig) {
3423
3422
  }
3424
3423
  return parts.join("\n");
3425
3424
  }
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
+ }
3426
3430
  return {
3427
3431
  name: "kbach",
3428
3432
  enforce: "pre",
3429
- config(_c, env) {
3430
- isDev = env.command === "serve";
3431
- },
3432
3433
  configResolved(resolved) {
3433
3434
  root = resolved.root;
3434
3435
  },
3435
- configureServer(s) {
3436
- server = s;
3437
- },
3438
3436
  buildStart() {
3439
3437
  for (const dir of ["src", "app", "pages", "components", "views", "layouts"]) {
3440
3438
  scanDir((0, import_path.join)(root, dir), (code) => extractFromCode(code));
3441
3439
  }
3442
3440
  },
3443
- // ── Primary injection: inject a <style> tag into every HTML response ──────
3444
- // React Router v8's dev middleware calls server.transformIndexHtml() on
3445
- // every SSR HTML response, so this hook fires on every page load.
3446
- transformIndexHtml: {
3447
- order: "post",
3448
- handler() {
3449
- const css = getAllCSS();
3450
- if (!css) return [];
3451
- return [{
3452
- tag: "style",
3453
- attrs: { "data-kbach": "" },
3454
- children: css,
3455
- injectTo: "head"
3456
- }];
3457
- }
3441
+ // ── DEV: intercept every HTTP response and inject <style data-kbach> ──────
3442
+ // React Router v8's SSR middleware generates HTML without going through
3443
+ // transformIndexHtml, so we intercept at the Node.js response level.
3444
+ configureServer(s) {
3445
+ server = s;
3446
+ s.middlewares.use((_req, res, next) => {
3447
+ const origEnd = res.end.bind(res);
3448
+ res.end = function(chunk, encoding, cb) {
3449
+ try {
3450
+ if (chunk != null) {
3451
+ const str = Buffer.isBuffer(chunk) ? chunk.toString("utf-8") : chunk;
3452
+ if (typeof str === "string" && str.includes("</head>")) {
3453
+ const injected = injectIntoHtml(str);
3454
+ if (injected !== str) {
3455
+ chunk = Buffer.isBuffer(chunk) ? Buffer.from(injected, "utf-8") : injected;
3456
+ if (res.hasHeader("content-length")) {
3457
+ res.setHeader("content-length", Buffer.byteLength(
3458
+ Buffer.isBuffer(chunk) ? chunk : String(chunk)
3459
+ ));
3460
+ }
3461
+ }
3462
+ }
3463
+ }
3464
+ } catch {
3465
+ }
3466
+ return origEnd(chunk, encoding, cb);
3467
+ };
3468
+ next();
3469
+ });
3458
3470
  },
3459
- // ── Secondary: also inject into CSS files (for production builds) ─────────
3460
- // In production, Vite extracts CSS from JS into separate .css files.
3461
- // Appending to the user's CSS ensures it ends up in the final CSS bundle.
3471
+ // ── PROD: append to CSS files so they land in the extracted CSS bundle ────
3462
3472
  transform(code, id) {
3463
3473
  const cleanId = id.split("?")[0];
3464
3474
  if (cleanId.endsWith(".css") && !id.includes("node_modules") && !id.includes("\0")) {
@@ -3483,7 +3493,7 @@ function kbach(userConfig) {
3483
3493
  try {
3484
3494
  const added = extractFromCode((0, import_fs.readFileSync)(file, "utf-8"));
3485
3495
  if (added && server) {
3486
- server.hot?.send({ type: "full-reload" }) ?? server.ws?.send({ type: "full-reload" });
3496
+ (server.hot ?? server.ws)?.send({ type: "full-reload" });
3487
3497
  }
3488
3498
  } catch {
3489
3499
  }
@@ -55,7 +55,6 @@ function kbach(userConfig) {
55
55
  const tokenCSS = /* @__PURE__ */ new Map();
56
56
  let root = process.cwd();
57
57
  let server = null;
58
- let isDev = false;
59
58
  function processClassString(classStr) {
60
59
  let added = false;
61
60
  for (const token of classStr.split(/\s+/).filter(Boolean)) {
@@ -81,42 +80,53 @@ function kbach(userConfig) {
81
80
  }
82
81
  return parts.join("\n");
83
82
  }
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
+ }
84
88
  return {
85
89
  name: "kbach",
86
90
  enforce: "pre",
87
- config(_c, env) {
88
- isDev = env.command === "serve";
89
- },
90
91
  configResolved(resolved) {
91
92
  root = resolved.root;
92
93
  },
93
- configureServer(s) {
94
- server = s;
95
- },
96
94
  buildStart() {
97
95
  for (const dir of ["src", "app", "pages", "components", "views", "layouts"]) {
98
96
  scanDir(join(root, dir), (code) => extractFromCode(code));
99
97
  }
100
98
  },
101
- // ── Primary injection: inject a <style> tag into every HTML response ──────
102
- // React Router v8's dev middleware calls server.transformIndexHtml() on
103
- // every SSR HTML response, so this hook fires on every page load.
104
- transformIndexHtml: {
105
- order: "post",
106
- handler() {
107
- const css = getAllCSS();
108
- if (!css) return [];
109
- return [{
110
- tag: "style",
111
- attrs: { "data-kbach": "" },
112
- children: css,
113
- injectTo: "head"
114
- }];
115
- }
99
+ // ── DEV: intercept every HTTP response and inject <style data-kbach> ──────
100
+ // React Router v8's SSR middleware generates HTML without going through
101
+ // transformIndexHtml, so we intercept at the Node.js response level.
102
+ configureServer(s) {
103
+ server = s;
104
+ s.middlewares.use((_req, res, next) => {
105
+ const origEnd = res.end.bind(res);
106
+ res.end = function(chunk, encoding, cb) {
107
+ try {
108
+ if (chunk != null) {
109
+ const str = Buffer.isBuffer(chunk) ? chunk.toString("utf-8") : chunk;
110
+ if (typeof str === "string" && str.includes("</head>")) {
111
+ const injected = injectIntoHtml(str);
112
+ if (injected !== str) {
113
+ chunk = Buffer.isBuffer(chunk) ? Buffer.from(injected, "utf-8") : injected;
114
+ if (res.hasHeader("content-length")) {
115
+ res.setHeader("content-length", Buffer.byteLength(
116
+ Buffer.isBuffer(chunk) ? chunk : String(chunk)
117
+ ));
118
+ }
119
+ }
120
+ }
121
+ }
122
+ } catch {
123
+ }
124
+ return origEnd(chunk, encoding, cb);
125
+ };
126
+ next();
127
+ });
116
128
  },
117
- // ── Secondary: also inject into CSS files (for production builds) ─────────
118
- // In production, Vite extracts CSS from JS into separate .css files.
119
- // Appending to the user's CSS ensures it ends up in the final CSS bundle.
129
+ // ── PROD: append to CSS files so they land in the extracted CSS bundle ────
120
130
  transform(code, id) {
121
131
  const cleanId = id.split("?")[0];
122
132
  if (cleanId.endsWith(".css") && !id.includes("node_modules") && !id.includes("\0")) {
@@ -141,7 +151,7 @@ function kbach(userConfig) {
141
151
  try {
142
152
  const added = extractFromCode(readFileSync(file, "utf-8"));
143
153
  if (added && server) {
144
- server.hot?.send({ type: "full-reload" }) ?? server.ws?.send({ type: "full-reload" });
154
+ (server.hot ?? server.ws)?.send({ type: "full-reload" });
145
155
  }
146
156
  } catch {
147
157
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kbach/react",
3
- "version": "0.5.20",
3
+ "version": "0.5.21",
4
4
  "description": "React / React Native components and hooks for the Kbach framework",
5
5
  "source": "./src/index.ts",
6
6
  "main": "./dist/index.js",