@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.
@@ -0,0 +1,79 @@
1
+ import {
2
+ DEFAULT_SCAN_DIRS,
3
+ createKbachStaticCssEngine,
4
+ resolveEffectiveTheme
5
+ } from "./chunk-J2M6QWJW.mjs";
6
+ import "./chunk-4Y2QXNJY.mjs";
7
+ import "./chunk-EOSN5CYG.mjs";
8
+
9
+ // src/postcss-plugin/index.ts
10
+ import postcss from "postcss";
11
+ import { resolve as resolvePath } from "path";
12
+ var KBACH_START_TEXT = "kbach:start";
13
+ var KBACH_END_TEXT = "kbach:end";
14
+ function isKbachMarker(node, text) {
15
+ return !!node && node.type === "comment" && node.text === text;
16
+ }
17
+ function spliceGeneratedCss(root, generatedCss) {
18
+ const nodes = root.nodes;
19
+ let startIdx = -1;
20
+ let endIdx = -1;
21
+ for (let i = 0; i < nodes.length; i++) {
22
+ if (startIdx === -1 && isKbachMarker(nodes[i], KBACH_START_TEXT)) {
23
+ startIdx = i;
24
+ continue;
25
+ }
26
+ if (startIdx !== -1 && isKbachMarker(nodes[i], KBACH_END_TEXT)) {
27
+ endIdx = i;
28
+ break;
29
+ }
30
+ }
31
+ if (startIdx === -1 || endIdx === -1) {
32
+ console.warn(
33
+ `[kbach] postcss plugin: no "/* kbach:start */" / "/* kbach:end */" marker pair found \u2014 add both to the file this plugin processes (see @kbach/react/vite's own convention). Nothing was generated.`
34
+ );
35
+ return;
36
+ }
37
+ for (const stale of nodes.slice(startIdx + 1, endIdx)) stale.remove();
38
+ const parsed = postcss.parse(generatedCss);
39
+ const newNodes = [...parsed.nodes];
40
+ let after = nodes[startIdx];
41
+ for (const node of newNodes) {
42
+ root.insertAfter(after, node);
43
+ after = node;
44
+ }
45
+ }
46
+ function kbachPostcss(options = {}) {
47
+ return {
48
+ postcssPlugin: "kbach",
49
+ async Once(root, { result }) {
50
+ const projectRoot = options.root ?? process.cwd();
51
+ const theme = await resolveEffectiveTheme(projectRoot, options);
52
+ const includeDirs = options.include ?? DEFAULT_SCAN_DIRS;
53
+ const safelist = options.safelist ?? [];
54
+ const engine = createKbachStaticCssEngine({
55
+ root: projectRoot,
56
+ theme,
57
+ themeJson: JSON.stringify(theme),
58
+ includeDirs,
59
+ safelist
60
+ });
61
+ engine.scanProjectCssSelectorsOnce();
62
+ engine.initialScan();
63
+ engine.processSafelist();
64
+ for (const dir of includeDirs) {
65
+ result.messages.push({
66
+ type: "dir-dependency",
67
+ plugin: "kbach",
68
+ dir: resolvePath(projectRoot, dir),
69
+ glob: "**/*"
70
+ });
71
+ }
72
+ spliceGeneratedCss(root, engine.generateCSS());
73
+ }
74
+ };
75
+ }
76
+ kbachPostcss.postcss = true;
77
+ export {
78
+ kbachPostcss as default
79
+ };
@@ -1,9 +1,23 @@
1
1
  import { Plugin } from 'vite';
2
- import { T as ThemeConfig } from './theme-BKAb78KP.mjs';
2
+ import { T as ThemeConfig, K as KbachConfig } from './config-BQ_b-qxF.mjs';
3
3
 
4
4
  interface KbachPluginOptions {
5
5
  /** Defaults to the same defaultTheme the runtime uses — pass your own to keep both in sync. */
6
6
  theme?: ThemeConfig;
7
+ /**
8
+ * A `kbach.config.js`-style config object, resolved the same way
9
+ * `applyKbachConfig()` resolves it for the runtime — import the SAME
10
+ * config file here and in your app's own `applyKbachConfig()` call, and
11
+ * build-time CSS generation and runtime resolution always agree, with no
12
+ * synchronization step needed (`resolveKbachConfig` is a pure function:
13
+ * same input, same output, in Node.js here and in the browser there).
14
+ * Ignored if `theme` is also supplied. Only needed for an explicit
15
+ * override, though — if BOTH this and `theme` are omitted, `kbach()`
16
+ * auto-discovers a `kbach.config.js` at the project root on its own (see
17
+ * `loadConfigFile`'s own doc comment), the same zero-config convenience
18
+ * `tailwind.config.js` gets from Tailwind's own PostCSS plugin.
19
+ */
20
+ config?: KbachConfig;
7
21
  /** Directories to scan for class strings (relative to Vite root). */
8
22
  include?: string[];
9
23
  /**
@@ -13,16 +27,104 @@ interface KbachPluginOptions {
13
27
  * purpose as Tailwind's `safelist`.
14
28
  */
15
29
  safelist?: string[];
30
+ /**
31
+ * Path to the `kbach:start`/`kbach:end` marker file, relative to Vite's
32
+ * resolved root. Not needed for the common case — `kbach()` auto-detects
33
+ * it (see `findMainCSSFile`'s own doc comment) by finding wherever you
34
+ * actually put the two marker comments, regardless of whether your
35
+ * project's source root is `src/`, `app/` (React Router v7's framework
36
+ * mode), or anything else in `include`. Set this explicitly only to
37
+ * disambiguate a genuinely unusual layout (a marker file outside every
38
+ * `include` directory, or more than one candidate present at once).
39
+ */
40
+ cssFile?: string;
16
41
  }
42
+ /**
43
+ * Finds where to read/write the `kbach:start`/`kbach:end` marker file,
44
+ * with no configuration required for the common case:
45
+ *
46
+ * 1. An explicit `cssFile` always wins (the one supported escape hatch).
47
+ * 2. Otherwise, look for a `kbach.css` already containing BOTH markers in
48
+ * each `include` directory (in order) and the project root itself —
49
+ * wherever you actually created it is where this plugin keeps using it,
50
+ * regardless of which framework convention (`src/`, `app/`, ...) that
51
+ * is. This is what makes React Router v7's framework mode (`app/
52
+ * kbach.css`) and a plain Vite app (`src/kbach.css`) both "just work"
53
+ * with the exact same `kbach()` call — no per-framework option needed.
54
+ * 3. No marked file exists yet (first run in a fresh project) — pick the
55
+ * first `include` directory that already exists as a real directory on
56
+ * disk, so the file this plugin is about to CREATE lands somewhere that
57
+ * won't need its own parent directory made up out of nothing.
58
+ * 4. Nothing in `include` exists yet either (a genuinely empty project,
59
+ * scaffolded before its first source file) — fall back to the first
60
+ * configured `include` entry regardless; `writeMainCSSFile` (below)
61
+ * creates its parent directory unconditionally, so this never crashes
62
+ * even here.
63
+ */
64
+ declare function findMainCSSFile(root: string, includeDirs: string[], explicitCssFile?: string): string;
65
+ declare const DEFAULT_SCAN_DIRS: string[];
66
+ declare const CSS_SYNC_DEBOUNCE_MS = 50;
67
+ /**
68
+ * Auto-discovers a `kbach.config.*` file at the project root — `undefined`
69
+ * if none exists (the common case: most projects have no customization at
70
+ * all, so this is a silent, expected no-op, not a warning).
71
+ *
72
+ * Checks three filenames, in order, mirroring Node's own module-format
73
+ * conventions rather than assuming one: `kbach.config.cjs` (always
74
+ * CommonJS, `module.exports = {...}` — the one unambiguous choice
75
+ * regardless of the project's package.json) and `kbach.config.mjs`
76
+ * (always real ESM, `export default {...}`) are tried first, each loaded
77
+ * the one way that's guaranteed correct for that extension. Only the bare
78
+ * `kbach.config.js` — whose actual meaning depends on the nearest
79
+ * package.json's `"type"` field, exactly like every other `.js` file in
80
+ * the project — needs the fallback below: try `require()` first (correct
81
+ * for `"type": "commonjs"`/no `"type"` field, the more common case), and
82
+ * only on a failure that specifically indicates an ESM/CJS mismatch (see
83
+ * `isEsmRequireFailure`), retry via a real dynamic `import()` instead. A
84
+ * genuine syntax error in an otherwise-correctly-formatted config file is
85
+ * NOT caught here — it propagates as a real, actionable build failure,
86
+ * rather than being silently swallowed into a confusing second attempt.
87
+ *
88
+ * `require()` is built via `createRequire(root)` rather than
89
+ * `createRequire(import.meta.url)` — this file is written as ESM source,
90
+ * but tsup's CJS build output has no real `import.meta.url` (esbuild
91
+ * leaves it `undefined` there), which would make `createRequire` throw for
92
+ * anyone consuming this package's `require('@kbach/react/vite')` entry.
93
+ * `root` works as `createRequire`'s base in either build: it's always an
94
+ * absolute, already-`existsSync`-validated directory, and `require()`
95
+ * given an already-absolute `path` doesn't consult the base for
96
+ * resolution anyway — the base only matters for a relative specifier,
97
+ * which this never passes it. `import()` needs a real `file://` URL, not
98
+ * a bare Windows path (`C:\...` isn't a valid ESM specifier) — hence
99
+ * `pathToFileURL`.
100
+ */
101
+ declare function loadConfigFile(root: string): Promise<KbachConfig | undefined>;
102
+ /**
103
+ * Resolves the theme to use, applying the same "explicit theme/config
104
+ * always wins over kbach.config.js auto-discovery" precedence both
105
+ * vite-plugin and postcss-plugin need identically.
106
+ */
107
+ declare function resolveEffectiveTheme(root: string, options: {
108
+ theme?: ThemeConfig;
109
+ config?: KbachConfig;
110
+ }): Promise<ThemeConfig>;
17
111
  /**
18
112
  * Build-time static CSS generation for Vite — scans source files for
19
113
  * Kbach class strings, resolves them through the same Rust engine the
20
114
  * runtime uses (via the Node-target WASM build, see wasmNode.ts), and
21
- * writes a real kbach.css. Simplified vs old-kbach's version: expects
22
- * `src/kbach.css` to exist at a fixed conventional path (create it with
23
- * `/* kbach:start *\/` / `/* kbach:end *\/` markers) rather than scanning
24
- * the whole project for a file named kbach.css.
115
+ * writes a real kbach.css. Auto-detects WHERE that file is (or should be
116
+ * created) via `findMainCSSFile` — no per-framework config needed for
117
+ * `src/`, `app/` (React Router v7's framework mode), or any other
118
+ * `include` convention; create a file with `/* kbach:start *\/` / `/*
119
+ * kbach:end *\/` markers wherever your project's source actually lives
120
+ * and this plugin finds it there.
121
+ *
122
+ * The actual scanning/resolution/generation logic lives in
123
+ * `staticCss/engine.ts`, shared with `postcss-plugin/index.ts` (Next.js/
124
+ * webpack/Turbopack) — this file is now just the Vite-specific wiring:
125
+ * plugin lifecycle hooks, the dev-server watcher, and the debounced
126
+ * file-write.
25
127
  */
26
128
  declare function kbach(options?: KbachPluginOptions): Plugin;
27
129
 
28
- export { type KbachPluginOptions, kbach };
130
+ export { CSS_SYNC_DEBOUNCE_MS, DEFAULT_SCAN_DIRS, type KbachPluginOptions, findMainCSSFile, kbach, loadConfigFile, resolveEffectiveTheme };
@@ -1,9 +1,23 @@
1
1
  import { Plugin } from 'vite';
2
- import { T as ThemeConfig } from './theme-BKAb78KP.js';
2
+ import { T as ThemeConfig, K as KbachConfig } from './config-BQ_b-qxF.js';
3
3
 
4
4
  interface KbachPluginOptions {
5
5
  /** Defaults to the same defaultTheme the runtime uses — pass your own to keep both in sync. */
6
6
  theme?: ThemeConfig;
7
+ /**
8
+ * A `kbach.config.js`-style config object, resolved the same way
9
+ * `applyKbachConfig()` resolves it for the runtime — import the SAME
10
+ * config file here and in your app's own `applyKbachConfig()` call, and
11
+ * build-time CSS generation and runtime resolution always agree, with no
12
+ * synchronization step needed (`resolveKbachConfig` is a pure function:
13
+ * same input, same output, in Node.js here and in the browser there).
14
+ * Ignored if `theme` is also supplied. Only needed for an explicit
15
+ * override, though — if BOTH this and `theme` are omitted, `kbach()`
16
+ * auto-discovers a `kbach.config.js` at the project root on its own (see
17
+ * `loadConfigFile`'s own doc comment), the same zero-config convenience
18
+ * `tailwind.config.js` gets from Tailwind's own PostCSS plugin.
19
+ */
20
+ config?: KbachConfig;
7
21
  /** Directories to scan for class strings (relative to Vite root). */
8
22
  include?: string[];
9
23
  /**
@@ -13,16 +27,104 @@ interface KbachPluginOptions {
13
27
  * purpose as Tailwind's `safelist`.
14
28
  */
15
29
  safelist?: string[];
30
+ /**
31
+ * Path to the `kbach:start`/`kbach:end` marker file, relative to Vite's
32
+ * resolved root. Not needed for the common case — `kbach()` auto-detects
33
+ * it (see `findMainCSSFile`'s own doc comment) by finding wherever you
34
+ * actually put the two marker comments, regardless of whether your
35
+ * project's source root is `src/`, `app/` (React Router v7's framework
36
+ * mode), or anything else in `include`. Set this explicitly only to
37
+ * disambiguate a genuinely unusual layout (a marker file outside every
38
+ * `include` directory, or more than one candidate present at once).
39
+ */
40
+ cssFile?: string;
16
41
  }
42
+ /**
43
+ * Finds where to read/write the `kbach:start`/`kbach:end` marker file,
44
+ * with no configuration required for the common case:
45
+ *
46
+ * 1. An explicit `cssFile` always wins (the one supported escape hatch).
47
+ * 2. Otherwise, look for a `kbach.css` already containing BOTH markers in
48
+ * each `include` directory (in order) and the project root itself —
49
+ * wherever you actually created it is where this plugin keeps using it,
50
+ * regardless of which framework convention (`src/`, `app/`, ...) that
51
+ * is. This is what makes React Router v7's framework mode (`app/
52
+ * kbach.css`) and a plain Vite app (`src/kbach.css`) both "just work"
53
+ * with the exact same `kbach()` call — no per-framework option needed.
54
+ * 3. No marked file exists yet (first run in a fresh project) — pick the
55
+ * first `include` directory that already exists as a real directory on
56
+ * disk, so the file this plugin is about to CREATE lands somewhere that
57
+ * won't need its own parent directory made up out of nothing.
58
+ * 4. Nothing in `include` exists yet either (a genuinely empty project,
59
+ * scaffolded before its first source file) — fall back to the first
60
+ * configured `include` entry regardless; `writeMainCSSFile` (below)
61
+ * creates its parent directory unconditionally, so this never crashes
62
+ * even here.
63
+ */
64
+ declare function findMainCSSFile(root: string, includeDirs: string[], explicitCssFile?: string): string;
65
+ declare const DEFAULT_SCAN_DIRS: string[];
66
+ declare const CSS_SYNC_DEBOUNCE_MS = 50;
67
+ /**
68
+ * Auto-discovers a `kbach.config.*` file at the project root — `undefined`
69
+ * if none exists (the common case: most projects have no customization at
70
+ * all, so this is a silent, expected no-op, not a warning).
71
+ *
72
+ * Checks three filenames, in order, mirroring Node's own module-format
73
+ * conventions rather than assuming one: `kbach.config.cjs` (always
74
+ * CommonJS, `module.exports = {...}` — the one unambiguous choice
75
+ * regardless of the project's package.json) and `kbach.config.mjs`
76
+ * (always real ESM, `export default {...}`) are tried first, each loaded
77
+ * the one way that's guaranteed correct for that extension. Only the bare
78
+ * `kbach.config.js` — whose actual meaning depends on the nearest
79
+ * package.json's `"type"` field, exactly like every other `.js` file in
80
+ * the project — needs the fallback below: try `require()` first (correct
81
+ * for `"type": "commonjs"`/no `"type"` field, the more common case), and
82
+ * only on a failure that specifically indicates an ESM/CJS mismatch (see
83
+ * `isEsmRequireFailure`), retry via a real dynamic `import()` instead. A
84
+ * genuine syntax error in an otherwise-correctly-formatted config file is
85
+ * NOT caught here — it propagates as a real, actionable build failure,
86
+ * rather than being silently swallowed into a confusing second attempt.
87
+ *
88
+ * `require()` is built via `createRequire(root)` rather than
89
+ * `createRequire(import.meta.url)` — this file is written as ESM source,
90
+ * but tsup's CJS build output has no real `import.meta.url` (esbuild
91
+ * leaves it `undefined` there), which would make `createRequire` throw for
92
+ * anyone consuming this package's `require('@kbach/react/vite')` entry.
93
+ * `root` works as `createRequire`'s base in either build: it's always an
94
+ * absolute, already-`existsSync`-validated directory, and `require()`
95
+ * given an already-absolute `path` doesn't consult the base for
96
+ * resolution anyway — the base only matters for a relative specifier,
97
+ * which this never passes it. `import()` needs a real `file://` URL, not
98
+ * a bare Windows path (`C:\...` isn't a valid ESM specifier) — hence
99
+ * `pathToFileURL`.
100
+ */
101
+ declare function loadConfigFile(root: string): Promise<KbachConfig | undefined>;
102
+ /**
103
+ * Resolves the theme to use, applying the same "explicit theme/config
104
+ * always wins over kbach.config.js auto-discovery" precedence both
105
+ * vite-plugin and postcss-plugin need identically.
106
+ */
107
+ declare function resolveEffectiveTheme(root: string, options: {
108
+ theme?: ThemeConfig;
109
+ config?: KbachConfig;
110
+ }): Promise<ThemeConfig>;
17
111
  /**
18
112
  * Build-time static CSS generation for Vite — scans source files for
19
113
  * Kbach class strings, resolves them through the same Rust engine the
20
114
  * runtime uses (via the Node-target WASM build, see wasmNode.ts), and
21
- * writes a real kbach.css. Simplified vs old-kbach's version: expects
22
- * `src/kbach.css` to exist at a fixed conventional path (create it with
23
- * `/* kbach:start *\/` / `/* kbach:end *\/` markers) rather than scanning
24
- * the whole project for a file named kbach.css.
115
+ * writes a real kbach.css. Auto-detects WHERE that file is (or should be
116
+ * created) via `findMainCSSFile` — no per-framework config needed for
117
+ * `src/`, `app/` (React Router v7's framework mode), or any other
118
+ * `include` convention; create a file with `/* kbach:start *\/` / `/*
119
+ * kbach:end *\/` markers wherever your project's source actually lives
120
+ * and this plugin finds it there.
121
+ *
122
+ * The actual scanning/resolution/generation logic lives in
123
+ * `staticCss/engine.ts`, shared with `postcss-plugin/index.ts` (Next.js/
124
+ * webpack/Turbopack) — this file is now just the Vite-specific wiring:
125
+ * plugin lifecycle hooks, the dev-server watcher, and the debounced
126
+ * file-write.
25
127
  */
26
128
  declare function kbach(options?: KbachPluginOptions): Plugin;
27
129
 
28
- export { type KbachPluginOptions, kbach };
130
+ export { CSS_SYNC_DEBOUNCE_MS, DEFAULT_SCAN_DIRS, type KbachPluginOptions, findMainCSSFile, kbach, loadConfigFile, resolveEffectiveTheme };