tkeron 4.0.0-beta.15 → 4.0.0-beta.17

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/bun.lock CHANGED
@@ -5,7 +5,7 @@
5
5
  "": {
6
6
  "name": "tkeron",
7
7
  "dependencies": {
8
- "@modelcontextprotocol/sdk": "^1.25.3",
8
+ "@modelcontextprotocol/sdk": "^1.26.0",
9
9
  "@tkeron/commands": "0.3.1",
10
10
  "@tkeron/html-parser": "1.5.3",
11
11
  "@tkeron/tools": "^0.2.2",
@@ -20,7 +20,7 @@
20
20
  "packages": {
21
21
  "@hono/node-server": ["@hono/node-server@1.19.9", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw=="],
22
22
 
23
- "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="],
23
+ "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="],
24
24
 
25
25
  "@tkeron/commands": ["@tkeron/commands@0.3.1", "", { "peerDependencies": { "typescript": "^5.9.3" } }, "sha512-b7YpdpwVrq8dwnvztpFYCNhjYjiQhX5xix1XXL1Dvt2oHcWiZ3RnG/6yzH0woL3Vbc7shpRNfIexlxmtEkhnIQ=="],
26
26
 
@@ -86,7 +86,7 @@
86
86
 
87
87
  "express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="],
88
88
 
89
- "express-rate-limit": ["express-rate-limit@7.5.1", "", { "peerDependencies": { "express": ">= 4.11" } }, "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw=="],
89
+ "express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="],
90
90
 
91
91
  "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
92
92
 
@@ -118,6 +118,8 @@
118
118
 
119
119
  "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
120
120
 
121
+ "ip-address": ["ip-address@10.0.1", "", {}, "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA=="],
122
+
121
123
  "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
122
124
 
123
125
  "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
package/changelog.md CHANGED
@@ -1,3 +1,60 @@
1
+ # v4.0.0-beta.17
2
+
3
+ ## MCP Server Updates
4
+
5
+ ### API Migration
6
+
7
+ - **Changed**: Migrated MCP server to new SDK API (`McpServer`, `registerResource`, `registerTool`)
8
+ - **Removed**: Legacy API (`Server`, `setRequestHandler`)
9
+ - **Benefit**: Better compatibility with MCP SDK 1.26.0, cleaner code structure
10
+
11
+ ### Tools Simplification
12
+
13
+ - **Removed**: `tkeron_init`, `tkeron_build`, `tkeron_develop` tools
14
+ - **Rationale**: MCP server now focuses on documentation and examples only
15
+ - **Alternative**: AI agents should use terminal commands (`tk init`, `tk build`, `tk dev`) instead
16
+ - **Kept**: `list_examples` and `get_example` tools (read-only)
17
+ - **Annotations**: Added `readOnlyHint: true` to remaining tools
18
+
19
+ ### New Testing Documentation
20
+
21
+ - **New**: `tkeron://testing` resource with comprehensive testing guide
22
+ - **Location**: `docs/testing.md`
23
+ - **Coverage**:
24
+ - How to use `getBuildResult()` for testing tkeron projects
25
+ - Proper test structure with `beforeAll()` for concurrent safety
26
+ - Testing patterns: file existence, DOM elements, bounded content, transformations
27
+ - Complete examples with best practices
28
+ - Rules summary (8 key rules)
29
+ - **Purpose**: Help users write reliable tests for their tkeron projects
30
+
31
+ ### Documentation Updates
32
+
33
+ - **Updated**: `docs/mcp-server.md` to reflect new tools approach
34
+ - **Clarified**: AI agents can access examples but must use terminal for project operations
35
+ - **Updated**: MCP server tests to include new `testing` resource
36
+
37
+ ### Tests
38
+
39
+ - **New**: `examples/basic_build/basic_build.test.ts` - complete test suite demonstrating testing patterns
40
+ - **Purpose**: Reference implementation for testing tkeron projects
41
+
42
+ ## Dependency Updates
43
+
44
+ - **Updated**: `@modelcontextprotocol/sdk` from `^1.25.3` to `^1.26.0`
45
+
46
+ # v4.0.0-beta.16
47
+
48
+ ## Improvements
49
+
50
+ ### Component Naming Validation
51
+
52
+ - **New**: Build now validates that component names contain at least one hyphen (custom element requirement)
53
+ - **Error message**: Clear error with file path and rename suggestion when invalid names are detected
54
+ - **Example**: `greeting.com.html` → shows error suggesting `tk-greeting.com.html`
55
+ - **Applies to**: Both `.com.html` and `.com.ts` components
56
+ - **Documentation**: Updated TypeScript components docs to clarify naming rules
57
+
1
58
  # v4.0.0-beta.15
2
59
 
3
60
  ## Bug Fixes
@@ -7,7 +7,7 @@ TypeScript components (`.com.ts` files) let you create dynamic, logic-driven com
7
7
  Create a `.com.ts` file:
8
8
 
9
9
  ```typescript
10
- // greeting.com.ts
10
+ // user-greeting.com.ts
11
11
  const name = com.getAttribute("name") || "World";
12
12
  com.innerHTML = `<h1>Hello, ${name}!</h1>`;
13
13
  ```
@@ -15,9 +15,9 @@ com.innerHTML = `<h1>Hello, ${name}!</h1>`;
15
15
  Use it with attributes:
16
16
 
17
17
  ```html
18
- <greeting name="Alice"></greeting>
19
- <greeting name="Bob"></greeting>
20
- <greeting></greeting>
18
+ <user-greeting name="Alice"></user-greeting>
19
+ <user-greeting name="Bob"></user-greeting>
20
+ <user-greeting></user-greeting>
21
21
  ```
22
22
 
23
23
  Build output:
@@ -58,6 +58,27 @@ Browser sees only:
58
58
  <div class="card">John</div>
59
59
  ```
60
60
 
61
+ ### Component Naming
62
+
63
+ Component filenames must:
64
+
65
+ - End with `.com.ts`
66
+ - Match the custom element name (without `.com.ts`)
67
+ - **Use lowercase with hyphens** (custom element requirement)
68
+
69
+ **Examples:**
70
+
71
+ | Filename | Custom Element | Valid? |
72
+ | --------------------- | --------------- | ------------------------- |
73
+ | `user-card.com.ts` | `<user-card>` | ✅ Yes |
74
+ | `nav-menu.com.ts` | `<nav-menu>` | ✅ Yes |
75
+ | `greeting.com.ts` | `<greeting>` | ❌ No (no hyphen) |
76
+ | `UserCard.com.ts` | `<user-card>` | ✅ Yes (case-insensitive) |
77
+
78
+ **Why hyphens are required:**
79
+
80
+ Custom elements in HTML must contain at least one hyphen to distinguish them from standard HTML elements. This is a web standard requirement. Tkeron validates this at build time and shows a clear error message if a component name doesn't follow this rule.
81
+
61
82
  ### Component Resolution
62
83
 
63
84
  Same as HTML components:
@@ -99,6 +99,7 @@ The MCP server exposes the following documentation resources:
99
99
  | `tkeron://pre-rendering` | Transform HTML at build time with .pre.ts files |
100
100
  | `tkeron://cli-reference` | Complete command-line interface documentation |
101
101
  | `tkeron://best-practices` | Patterns, anti-patterns, and limitations |
102
+ | `tkeron://testing` | How to test tkeron projects with getBuildResult() |
102
103
 
103
104
  ## What AI Agents Can Do
104
105
 
@@ -111,25 +112,19 @@ With access to the MCP server, AI agents can:
111
112
  - ✅ Provide accurate CLI commands and options
112
113
  - ✅ Debug common issues with components and pre-rendering
113
114
  - ✅ Recommend best practices and avoid anti-patterns
114
- - ✅ **Initialize new Tkeron projects directly**
115
- - ✅ **Build and develop Tkeron projects**
116
115
  - ✅ **List and explore example projects**
117
116
  - ✅ **Retrieve example code and structures**
117
+ - ✅ **Initialize, build, and develop projects via terminal** (`tk init`, `tk build`, `tk dev`)
118
118
 
119
119
  ## Available Tools
120
120
 
121
121
  The MCP server provides the following tools for AI agents:
122
122
 
123
- ### Documentation Tool
124
-
125
- - **`get_tkeron_docs`**: Retrieve specific documentation topics
126
- - Parameters: `topic` (overview, getting-started, components-html, etc.)
127
- - Returns the full markdown content of the requested documentation
128
-
129
123
  ### Example Management Tools
130
124
 
131
125
  - **`list_examples`**: List all available Tkeron example projects
132
- - Shows example names, descriptions, and features
126
+ - No parameters required
127
+ - Returns example names, descriptions, and paths
133
128
  - Helps users discover what's possible with Tkeron
134
129
 
135
130
  - **`get_example`**: Get the complete source code of an example
@@ -137,22 +132,7 @@ The MCP server provides the following tools for AI agents:
137
132
  - Returns file structure and all source code
138
133
  - Useful for learning patterns and bootstrapping projects
139
134
 
140
- ### Project Management Tools
141
-
142
- - **`tkeron_init`**: Initialize a new Tkeron project
143
- - Parameters: `projectPath` (absolute path), `force` (optional boolean)
144
- - Creates the standard Tkeron structure (websrc/ with sample files)
145
- - AI agents can create projects without user intervention
146
-
147
- - **`tkeron_build`**: Build a Tkeron project
148
- - Parameters: `sourceDir`, `targetDir` (optional)
149
- - Processes all `.pre.ts`, `.com.html`, and `.com.ts` files
150
- - Generates production-ready output
151
-
152
- - **`tkeron_develop`**: Start the development server
153
- - Parameters: `sourceDir`, `outputDir`, `port`, `host` (all optional)
154
- - Launches hot-reload development server
155
- - AI agents can test projects immediately after creation
135
+ > **Note:** The MCP server only provides documentation and examples. It does NOT modify files. The AI agent should use terminal commands (`tk init`, `tk build`, `tk dev`) to manage projects.
156
136
 
157
137
  ## Benefits
158
138
 
@@ -0,0 +1,331 @@
1
+ # Testing Tkeron Projects
2
+
3
+ ## Overview
4
+
5
+ Tkeron provides a `getBuildResult()` function that lets you test your built projects programmatically. This is useful for verifying that your components, pre-rendering scripts, and build output work correctly.
6
+
7
+ **Important:** These tests validate YOUR project's build output — not tkeron internals. You're testing that your HTML, components, and scripts produce the expected result after the build.
8
+
9
+ ## Setup
10
+
11
+ Install tkeron as a dependency in your project:
12
+
13
+ ```bash
14
+ bun add -d tkeron
15
+ ```
16
+
17
+ ## API
18
+
19
+ ```typescript
20
+ import { getBuildResult, type BuildResult } from "tkeron";
21
+
22
+ const result: BuildResult = await getBuildResult("./websrc");
23
+ ```
24
+
25
+ ### BuildResult
26
+
27
+ `getBuildResult()` returns a `Record<string, FileInfo>` indexed by relative path:
28
+
29
+ ```typescript
30
+ type BuildResult = Record<string, FileInfo>;
31
+
32
+ interface FileInfo {
33
+ fileName: string; // "index.html"
34
+ filePath: string; // "styles" or "" for root files
35
+ path: string; // "styles/main.css" (the key)
36
+ type: string; // MIME type: "text/html", "text/css", etc.
37
+ size: number; // size in bytes
38
+ fileHash: string; // SHA-256 hash
39
+ getContentAsString?: () => string; // text files only
40
+ dom?: Document; // HTML files only - parsed DOM
41
+ }
42
+ ```
43
+
44
+ - `dom` is only present on `.html` files. It provides full DOM API: `querySelector()`, `getElementById()`, `querySelectorAll()`, etc.
45
+ - `getContentAsString()` is only present on text files (html, css, js, json, txt, svg, xml, ts, mjs, cjs, md, map).
46
+ - Binary files (images, fonts) only have metadata — no `dom` or `getContentAsString`.
47
+
48
+ ## Test Structure
49
+
50
+ ### Single Build with beforeAll
51
+
52
+ **Always** build once and share the result across tests. This is:
53
+
54
+ - Concurrent-safe (`bun test --concurrent`)
55
+ - Efficient (1 build instead of N builds)
56
+ - Reliable (no race conditions)
57
+
58
+ ```typescript
59
+ import { describe, it, expect, beforeAll } from "bun:test";
60
+ import { getBuildResult, type BuildResult } from "tkeron";
61
+ import { join } from "path";
62
+
63
+ describe("my project", () => {
64
+ const sourcePath = join(import.meta.dir, "src");
65
+ let result: BuildResult;
66
+
67
+ beforeAll(async () => {
68
+ result = await getBuildResult(sourcePath);
69
+ });
70
+
71
+ it("should generate index.html", () => {
72
+ expect(result?.["index.html"]).toBeDefined();
73
+ });
74
+ });
75
+ ```
76
+
77
+ **Never** call `getBuildResult()` inside each `it()` — this causes race conditions in concurrent mode.
78
+
79
+ ## Testing Patterns
80
+
81
+ ### 1. Verify File Existence
82
+
83
+ Check that expected output files exist:
84
+
85
+ ```typescript
86
+ it("should generate expected files", () => {
87
+ expect(result?.["index.html"]).toBeDefined();
88
+ expect(result?.["index.js"]).toBeDefined();
89
+ expect(result?.["styles/main.css"]).toBeDefined();
90
+ });
91
+ ```
92
+
93
+ ### 2. Verify Element Existence (Always Check First)
94
+
95
+ **Rule:** Always verify an element exists BEFORE checking its properties.
96
+
97
+ ```typescript
98
+ // ✅ CORRECT: check existence first, then properties
99
+ it("should have login button", () => {
100
+ const dom = result?.["index.html"]?.dom;
101
+
102
+ const button = dom?.querySelector("#login-button");
103
+ expect(button).toBeDefined();
104
+ expect(button!.textContent).toBe("Log In");
105
+ });
106
+
107
+ // ❌ WRONG: accessing properties without checking existence
108
+ it("should have login button", () => {
109
+ const dom = result?.["index.html"]?.dom;
110
+ expect(dom!.querySelector("#login-button")!.textContent).toBe("Log In");
111
+ });
112
+ ```
113
+
114
+ ### 3. Verify Bounded Content
115
+
116
+ Test specific, short, predictable values — not full output strings.
117
+
118
+ ```typescript
119
+ // ✅ GOOD: specific, bounded values
120
+ it("should have correct title", () => {
121
+ const dom = result?.["index.html"]?.dom;
122
+
123
+ const title = dom?.querySelector("title");
124
+ expect(title).toBeDefined();
125
+ expect(title!.textContent).toBe("My App");
126
+ });
127
+
128
+ // ✅ GOOD: keyword presence in compiled JS
129
+ it("should contain event handling logic", () => {
130
+ const js = result?.["index.js"]?.getContentAsString!();
131
+ expect(js).toContain("addEventListener");
132
+ });
133
+
134
+ // ❌ BAD: comparing full minified JS output (fragile, breaks on any optimization change)
135
+ it("should match exact JS", () => {
136
+ const js = result?.["index.js"]?.getContentAsString!();
137
+ expect(js).toBe('var t=0,e=document.querySelector("button")...');
138
+ });
139
+
140
+ // ❌ BAD: comparing full HTML output
141
+ it("should match exact HTML", () => {
142
+ const html = result?.["index.html"]?.getContentAsString!();
143
+ expect(html).toBe("<!doctype html><html>...");
144
+ });
145
+ ```
146
+
147
+ **Why bounded:** Build output changes with optimizations, minification, and bundling. Test the **meaning**, not the **exact format**.
148
+
149
+ ### 4. Verify DOM Elements (Specific Queries)
150
+
151
+ Test specific elements that matter to your project:
152
+
153
+ ```typescript
154
+ it("should have navigation with correct links", () => {
155
+ const dom = result?.["index.html"]?.dom;
156
+
157
+ const homeLink = dom?.querySelector('nav a[href="/"]');
158
+ expect(homeLink).toBeDefined();
159
+ expect(homeLink!.textContent).toBe("Home");
160
+
161
+ const aboutLink = dom?.querySelector('nav a[href="/about"]');
162
+ expect(aboutLink).toBeDefined();
163
+ });
164
+
165
+ it("should have user profile section", () => {
166
+ const dom = result?.["index.html"]?.dom;
167
+
168
+ const profile = dom?.querySelector("#user-profile");
169
+ expect(profile).toBeDefined();
170
+
171
+ const avatar = profile?.querySelector("img.avatar");
172
+ expect(avatar).toBeDefined();
173
+ expect(avatar!.getAttribute("alt")).toBe("User avatar");
174
+ });
175
+ ```
176
+
177
+ ### 5. Verify Tkeron Transformations
178
+
179
+ Test that tkeron's build features work correctly in your project:
180
+
181
+ **Script injection (TypeScript → JavaScript):**
182
+
183
+ ```typescript
184
+ it("should have compiled script in head", () => {
185
+ const dom = result?.["index.html"]?.dom;
186
+
187
+ const script = dom?.querySelector('script[type="module"]');
188
+ expect(script).toBeDefined();
189
+ expect(script!.getAttribute("src")).toBe("./index.js");
190
+ expect(script!.parentElement!.tagName).toBe("HEAD");
191
+ });
192
+ ```
193
+
194
+ **Component substitution (.com.html):**
195
+
196
+ ```typescript
197
+ it("should have processed nav component", () => {
198
+ const dom = result?.["index.html"]?.dom;
199
+
200
+ // The <nav-menu> tag should be replaced by its .com.html content
201
+ const nav = dom?.querySelector("nav");
202
+ expect(nav).toBeDefined();
203
+
204
+ const links = dom?.querySelectorAll("nav a");
205
+ expect(links).toBeDefined();
206
+ expect(links!.length).toBeGreaterThan(0);
207
+ });
208
+ ```
209
+
210
+ **TypeScript component (.com.ts):**
211
+
212
+ ```typescript
213
+ it("should have dynamically generated list", () => {
214
+ const dom = result?.["index.html"]?.dom;
215
+
216
+ const list = dom?.querySelector("#item-list");
217
+ expect(list).toBeDefined();
218
+
219
+ const items = list?.querySelectorAll("li");
220
+ expect(items).toBeDefined();
221
+ expect(items!.length).toBe(5);
222
+ });
223
+ ```
224
+
225
+ **Pre-rendering (.pre.ts):**
226
+
227
+ ```typescript
228
+ it("should have pre-rendered content", () => {
229
+ const dom = result?.["index.html"]?.dom;
230
+
231
+ const generated = dom?.querySelector("#generated-content");
232
+ expect(generated).toBeDefined();
233
+ expect(generated!.textContent).not.toBe("");
234
+ });
235
+ ```
236
+
237
+ ### 6. Verify File Metadata
238
+
239
+ ```typescript
240
+ it("should have correct metadata", () => {
241
+ expect(result?.["index.html"]?.type).toBe("text/html");
242
+ expect(result?.["index.html"]?.size).toBeGreaterThan(0);
243
+ expect(result?.["index.html"]?.fileHash).toBeDefined();
244
+ });
245
+
246
+ it("should not have DOM on non-HTML files", () => {
247
+ expect(result?.["index.js"]?.dom).toBeUndefined();
248
+ });
249
+
250
+ it("should have correct path info for nested files", () => {
251
+ expect(result?.["pages/about.html"]?.fileName).toBe("about.html");
252
+ expect(result?.["pages/about.html"]?.filePath).toBe("pages");
253
+ });
254
+ ```
255
+
256
+ ## Complete Example
257
+
258
+ Here's a complete test file for a tkeron project with components:
259
+
260
+ ```typescript
261
+ import { describe, it, expect, beforeAll } from "bun:test";
262
+ import { getBuildResult, type BuildResult } from "tkeron";
263
+ import { join } from "path";
264
+
265
+ describe("my-website build", () => {
266
+ const sourcePath = join(import.meta.dir, "websrc");
267
+ let result: BuildResult;
268
+
269
+ beforeAll(async () => {
270
+ result = await getBuildResult(sourcePath);
271
+ });
272
+
273
+ it("should generate expected files", () => {
274
+ expect(result?.["index.html"]).toBeDefined();
275
+ expect(result?.["index.js"]).toBeDefined();
276
+ expect(result?.["about.html"]).toBeDefined();
277
+ });
278
+
279
+ it("should have page title", () => {
280
+ const dom = result?.["index.html"]?.dom;
281
+
282
+ const title = dom?.querySelector("title");
283
+ expect(title).toBeDefined();
284
+ expect(title!.textContent).toBe("My Website");
285
+ });
286
+
287
+ it("should have navigation from component", () => {
288
+ const dom = result?.["index.html"]?.dom;
289
+
290
+ const nav = dom?.querySelector("nav");
291
+ expect(nav).toBeDefined();
292
+
293
+ const homeLink = dom?.querySelector('nav a[href="/"]');
294
+ expect(homeLink).toBeDefined();
295
+ expect(homeLink!.textContent).toBe("Home");
296
+ });
297
+
298
+ it("should have footer from component", () => {
299
+ const dom = result?.["index.html"]?.dom;
300
+
301
+ const footer = dom?.querySelector("footer");
302
+ expect(footer).toBeDefined();
303
+ });
304
+
305
+ it("should have compiled JavaScript", () => {
306
+ const js = result?.["index.js"]?.getContentAsString!();
307
+
308
+ expect(js).toBeDefined();
309
+ expect(js!.length).toBeGreaterThan(0);
310
+ });
311
+
312
+ it("should have script injected in head", () => {
313
+ const dom = result?.["index.html"]?.dom;
314
+
315
+ const script = dom?.querySelector('head script[type="module"]');
316
+ expect(script).toBeDefined();
317
+ expect(script!.getAttribute("src")).toBe("./index.js");
318
+ });
319
+ });
320
+ ```
321
+
322
+ ## Rules Summary
323
+
324
+ 1. **Build once** with `beforeAll()` — never inside individual tests
325
+ 2. **Check existence first** — always `expect(element).toBeDefined()` before accessing properties
326
+ 3. **Use optional chaining** (`?.`) for safe property access on result entries
327
+ 4. **Test bounded content** — specific values, keywords, attributes; not full output strings
328
+ 5. **Test specific elements** — query for elements that matter to your project
329
+ 6. **Verify transformations** — components processed, scripts injected, pre-rendering applied
330
+ 7. **Don't test the parser** — you're testing your project, not tkeron internals
331
+ 8. **Concurrent-safe** — tests must work with `bun test --concurrent`
package/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
  import { getCommands } from "@tkeron/commands";
3
- import { build } from "./src/build";
3
+ import { buildWrapper } from "./src/buildWrapper";
4
4
  import { develop } from "./src/develop";
5
5
  import { initWrapper } from "./src/initWrapper";
6
6
  import { showBanner } from "./src/banner";
@@ -17,7 +17,7 @@ getCommands()
17
17
  .addDescription("Build the project")
18
18
  .addPositionedArgument("sourceDir")
19
19
  .addPositionedArgument("targetDir")
20
- .setCallback(build)
20
+ .setCallback(buildWrapper)
21
21
 
22
22
  .commands()
23
23