Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/quick-clis-bundle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cloudflare/kumo": patch
---

Build the Kumo CLI and package assets with tsdown instead of separate esbuild and asset build scripts.
5 changes: 2 additions & 3 deletions packages/kumo/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ kumo/
├── scripts/
│ ├── component-registry/ # Registry codegen (13 sub-modules, 930+ lines orchestrator)
│ ├── theme-generator/ # Theme CSS codegen from config.ts
│ ├── generate-primitives.ts
│ └── css-build.ts # Post-Vite CSS processing
│ └── generate-primitives.ts
├── lint/ # 5 custom oxlint rules (superset of root lint/)
├── tests/imports/ # Structural validation: export paths, package.json, build entries
├── vite.config.ts # Library mode, dynamic primitive discovery, PLOP marker
Expand All @@ -50,7 +49,7 @@ kumo/

### Build System

- **Three-step build**: `vp build` (Vite+/Rolldown) → `css-build.ts` (copies CSS + `@tailwindcss/cli`) → `build-cli.ts` (esbuild)
- **Build pipeline**: `codegen:registry` → `vp pack` (browser library, Node CLI, declarations, package assets)
- **Bundled deps**: `@base-ui/react`, `clsx`, `tailwind-merge` are bundled (not external)
- **External peers**: `react`, `react-dom`, `@phosphor-icons/react` only
- **`"use client"` banner**: Injected on ALL output chunks for RSC compatibility
Expand Down
3 changes: 1 addition & 2 deletions packages/kumo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@
},
"scripts": {
"prepublishOnly": "pnpm run build && pnpm run test:unit",
"build": "pnpm run codegen:registry && tsx scripts/css-build.ts && vp pack && tsx src/command-line/build-cli.ts",
"build": "pnpm run codegen:registry && vp pack",
"dev": "vp pack --watch",
"new:component": "plop component",
"clean": "rm -rf dist",
Expand Down Expand Up @@ -479,7 +479,6 @@
"@vitejs/plugin-react": "~6.0.3",
"@vitest/browser-playwright": "catalog:",
"@vitest/ui": "catalog:",
"esbuild": "0.27.2",
"glob": "13.0.0",
"happy-dom": "catalog:",
"playwright": "catalog:",
Expand Down
105 changes: 0 additions & 105 deletions packages/kumo/scripts/css-build.ts

This file was deleted.

80 changes: 0 additions & 80 deletions packages/kumo/src/command-line/build-cli.ts

This file was deleted.

76 changes: 74 additions & 2 deletions packages/kumo/tests/imports/export-path-validation.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { describe, it, expect } from "vite-plus/test";
import { existsSync, readdirSync } from "fs";
import { existsSync, readFileSync, readdirSync } from "fs";
import { spawnSync } from "child_process";
import { join, dirname } from "path";
import { fileURLToPath } from "url";
import { readFileSync } from "fs";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
Expand Down Expand Up @@ -241,6 +241,78 @@ describe.skipIf(!isBuilt)("Export Path Validation (Post-Build)", () => {
expect(mapFiles).toEqual([]);
});

it("should ship one CLI bundle that the package bin can launch", () => {
const packageDir = join(__dirname, "../..");
const cliDir = join(packageDir, "dist/command-line");
const cliPath = join(cliDir, "cli.js");
const binPath = join(
packageDir,
packageJson.bin.kumo.replace(/^\.\//, ""),
);

expect(readdirSync(cliDir).sort()).toEqual(["cli.js"]);
expect(readFileSync(cliPath, "utf-8")).toMatch(
/^#!\/usr\/bin\/env node\n/,
);

const result = spawnSync(process.execPath, [binPath, "help"], {
cwd: packageDir,
encoding: "utf-8",
});

expect(result.status).toBe(0);
expect(result.stderr).toBe("");
expect(result.stdout).toContain(
"Kumo CLI - Component registry and blocks distribution",
);
});

it("should ship raw and compiled CSS assets", () => {
const packageDir = join(__dirname, "../..");
const sourceDir = join(packageDir, "src/styles");
const outputDir = join(packageDir, "dist/styles");
const rawFiles = [
"kumo.css",
"kumo-binding.css",
"theme-kumo.css",
"theme-fedramp.css",
];

for (const file of rawFiles) {
expect(readFileSync(join(outputDir, file), "utf-8")).toBe(
readFileSync(join(sourceDir, file), "utf-8"),
);
}

const standaloneCss = readFileSync(
join(outputDir, "kumo-standalone.css"),
"utf-8",
);
expect(standaloneCss).not.toContain('@import "tailwindcss"');
expect(standaloneCss).toContain("--color-kumo-base");
});

it("should ship block sources used by the CLI", () => {
const packageDir = join(__dirname, "../..");
const sourceDir = join(packageDir, "src/blocks");
const outputDir = join(packageDir, "dist/blocks-source");

for (const block of readdirSync(sourceDir, { withFileTypes: true })) {
if (!block.isDirectory()) continue;

const blockDir = join(sourceDir, block.name);
const files = readdirSync(blockDir).filter((file) =>
file.endsWith(".tsx"),
);

for (const file of files) {
expect(readFileSync(join(outputDir, block.name, file), "utf-8")).toBe(
readFileSync(join(blockDir, file), "utf-8"),
);
}
}
});

it("primitive JS files should import from bundled chunks", async () => {
const sliderJs = join(__dirname, "../../dist/primitives/slider.js");
const content = readFileSync(sliderJs, "utf-8");
Expand Down
Loading