tkeron 5.0.2 → 5.1.1

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.
Files changed (82) hide show
  1. package/bun.lock +13 -13
  2. package/changelog.md +12 -0
  3. package/docs/components-markdown.md +2 -0
  4. package/docs/components-typescript.md +59 -0
  5. package/docs/testing.md +1 -1
  6. package/examples/with_com_html_in_ts/websrc/index.html +19 -0
  7. package/examples/with_com_html_in_ts/websrc/info-panel.com.html +12 -0
  8. package/examples/with_com_html_in_ts/websrc/info-panel.com.ts +14 -0
  9. package/examples/with_com_html_in_ts/websrc/user-card.com.html +12 -0
  10. package/examples/with_com_html_in_ts/websrc/user-card.com.ts +8 -0
  11. package/mcp-server.ts +5 -7
  12. package/package.json +10 -8
  13. package/src/develop.ts +28 -13
  14. package/src/processComTs.ts +40 -0
  15. /package/examples/basic_build/{src → websrc}/index.html +0 -0
  16. /package/examples/basic_build/{src → websrc}/index.ts +0 -0
  17. /package/examples/with_assets/{src → websrc}/index.html +0 -0
  18. /package/examples/with_assets/{src → websrc}/index.ts +0 -0
  19. /package/examples/with_assets/{src → websrc}/profile.png +0 -0
  20. /package/examples/with_assets/{src → websrc}/section/index.html +0 -0
  21. /package/examples/with_com_html_priority/{src → websrc}/admin/admin-panel.com.html +0 -0
  22. /package/examples/with_com_html_priority/{src → websrc}/admin/dashboard.html +0 -0
  23. /package/examples/with_com_html_priority/{src → websrc}/admin/dashboard.ts +0 -0
  24. /package/examples/with_com_html_priority/{src → websrc}/admin/nested-stat.com.html +0 -0
  25. /package/examples/with_com_html_priority/{src → websrc}/admin/site-header.com.html +0 -0
  26. /package/examples/with_com_html_priority/{src → websrc}/blog/comment-item.com.html +0 -0
  27. /package/examples/with_com_html_priority/{src → websrc}/blog/comment-section.com.html +0 -0
  28. /package/examples/with_com_html_priority/{src → websrc}/blog/post.html +0 -0
  29. /package/examples/with_com_html_priority/{src → websrc}/blog/post.ts +0 -0
  30. /package/examples/with_com_html_priority/{src → websrc}/index.html +0 -0
  31. /package/examples/with_com_html_priority/{src → websrc}/index.ts +0 -0
  32. /package/examples/with_com_html_priority/{src → websrc}/info-box.com.html +0 -0
  33. /package/examples/with_com_html_priority/{src → websrc}/site-header.com.html +0 -0
  34. /package/examples/with_com_mixed_priority/{src → websrc}/dashboard/main.html +0 -0
  35. /package/examples/with_com_mixed_priority/{src → websrc}/dashboard/main.ts +0 -0
  36. /package/examples/with_com_mixed_priority/{src → websrc}/dashboard/page-header.com.ts +0 -0
  37. /package/examples/with_com_mixed_priority/{src → websrc}/dashboard/stats-widget.com.ts +0 -0
  38. /package/examples/with_com_mixed_priority/{src → websrc}/feature-card.com.ts +0 -0
  39. /package/examples/with_com_mixed_priority/{src → websrc}/footer-info.com.html +0 -0
  40. /package/examples/with_com_mixed_priority/{src → websrc}/index.html +0 -0
  41. /package/examples/with_com_mixed_priority/{src → websrc}/index.ts +0 -0
  42. /package/examples/with_com_mixed_priority/{src → websrc}/page-header.com.html +0 -0
  43. /package/examples/with_com_mixed_priority/{src → websrc}/settings/config.html +0 -0
  44. /package/examples/with_com_mixed_priority/{src → websrc}/settings/config.ts +0 -0
  45. /package/examples/with_com_mixed_priority/{src → websrc}/settings/priority-test.com.html +0 -0
  46. /package/examples/with_com_mixed_priority/{src → websrc}/settings/priority-test.com.ts +0 -0
  47. /package/examples/with_com_mixed_priority/{src → websrc}/users/profiles/activity-feed.com.ts +0 -0
  48. /package/examples/with_com_mixed_priority/{src → websrc}/users/profiles/page-header.com.html +0 -0
  49. /package/examples/with_com_mixed_priority/{src → websrc}/users/profiles/profile-actions.com.html +0 -0
  50. /package/examples/with_com_mixed_priority/{src → websrc}/users/profiles/user-profile.com.ts +0 -0
  51. /package/examples/with_com_mixed_priority/{src → websrc}/users/profiles/view.html +0 -0
  52. /package/examples/with_com_mixed_priority/{src → websrc}/users/profiles/view.ts +0 -0
  53. /package/examples/with_com_ts/{src → websrc}/dynamic-list.com.ts +0 -0
  54. /package/examples/with_com_ts/{src → websrc}/greeting-message.com.ts +0 -0
  55. /package/examples/with_com_ts/{src → websrc}/index.html +0 -0
  56. /package/examples/with_com_ts/{src → websrc}/user-card.com.ts +0 -0
  57. /package/examples/with_com_ts_priority/{src → websrc}/analytics/chart-widget.com.ts +0 -0
  58. /package/examples/with_com_ts_priority/{src → websrc}/analytics/report.html +0 -0
  59. /package/examples/with_com_ts_priority/{src → websrc}/analytics/report.ts +0 -0
  60. /package/examples/with_com_ts_priority/{src → websrc}/analytics/user-stats.com.ts +0 -0
  61. /package/examples/with_com_ts_priority/{src → websrc}/data-table.com.ts +0 -0
  62. /package/examples/with_com_ts_priority/{src → websrc}/index.html +0 -0
  63. /package/examples/with_com_ts_priority/{src → websrc}/index.ts +0 -0
  64. /package/examples/with_com_ts_priority/{src → websrc}/sales/regional/data-table.com.ts +0 -0
  65. /package/examples/with_com_ts_priority/{src → websrc}/sales/regional/overview.html +0 -0
  66. /package/examples/with_com_ts_priority/{src → websrc}/sales/regional/overview.ts +0 -0
  67. /package/examples/with_com_ts_priority/{src → websrc}/sales/regional/sales-summary.com.ts +0 -0
  68. /package/examples/with_com_ts_priority/{src → websrc}/user-stats.com.ts +0 -0
  69. /package/examples/with_component_iteration/{src → websrc}/card-list.com.ts +0 -0
  70. /package/examples/with_component_iteration/{src → websrc}/engagement-meter.com.ts +0 -0
  71. /package/examples/with_component_iteration/{src → websrc}/index.html +0 -0
  72. /package/examples/with_component_iteration/{src → websrc}/index.ts +0 -0
  73. /package/examples/with_component_iteration/{src → websrc}/note-box.com.html +0 -0
  74. /package/examples/with_component_iteration/{src → websrc}/status-badge.com.ts +0 -0
  75. /package/examples/with_component_iteration/{src → websrc}/user-card.com.ts +0 -0
  76. /package/examples/with_pre/{src → websrc}/contact.pre.ts +0 -0
  77. /package/examples/with_pre/{src → websrc}/index.html +0 -0
  78. /package/examples/with_pre/{src → websrc}/index.ts +0 -0
  79. /package/examples/with_pre/{src → websrc}/profile.png +0 -0
  80. /package/examples/with_pre/{src → websrc}/section/index.html +0 -0
  81. /package/examples/with_pre/{src → websrc}/section/index.pre.ts +0 -0
  82. /package/examples/with_pre/{src → websrc}/section/no-linked-profile.png +0 -0
package/bun.lock CHANGED
@@ -5,30 +5,30 @@
5
5
  "": {
6
6
  "name": "tkeron",
7
7
  "dependencies": {
8
- "@modelcontextprotocol/sdk": "^1.26.0",
9
- "@tkeron/commands": "0.4.6",
10
- "@tkeron/html-parser": "1.5.5",
11
- "@tkeron/tools": "^0.4.0",
8
+ "@modelcontextprotocol/sdk": "^1.29.0",
9
+ "@tkeron/commands": "0.4.7",
10
+ "@tkeron/html-parser": "1.5.6",
11
+ "@tkeron/tools": "^0.4.1",
12
12
  },
13
13
  "devDependencies": {
14
- "@types/bun": "^1.3.9",
14
+ "@types/bun": "^1.3.11",
15
15
  "prettier": "^3.8.1",
16
- "typescript": "^5.9.3",
16
+ "typescript": "^6.0.2",
17
17
  },
18
18
  },
19
19
  },
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.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=="],
23
+ "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.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-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="],
24
24
 
25
- "@tkeron/commands": ["@tkeron/commands@0.4.6", "", { "peerDependencies": { "typescript": "^5.9.3" } }, "sha512-Bojs3GmIpvd/4Mcw7FvbohRcDyHEzs3ZJHxwIeXE0diQzN+ER3R30xIFovgzwE8QfWvC9uFDaN2VTk4O+0ffyA=="],
25
+ "@tkeron/commands": ["@tkeron/commands@0.4.7", "", { "peerDependencies": { "typescript": "^5.9.3" } }, "sha512-QGYLnPcOFir5cMJpeUuLMZdW5IrcDyY/jjbML3o23yATI8Y3XzwYhRDW2l8kex0waLV7KsAwnGsb2zl8sRn1PQ=="],
26
26
 
27
- "@tkeron/html-parser": ["@tkeron/html-parser@1.5.5", "", { "peerDependencies": { "typescript": "^5.9.3" } }, "sha512-GSmlscPWxb2XqlmGNvtQ9ArETIPQmt3vIFlUgpTftLglkcCVgsA/pVZNW9AKJ8OZMfy8Jfmq+tf8QpbUWBVnsQ=="],
27
+ "@tkeron/html-parser": ["@tkeron/html-parser@1.5.6", "", { "peerDependencies": { "typescript": "^5.9.3" } }, "sha512-dATXoSjxOuUjyjde5ocrZdd5xKfpKf7R9dwRklXHVCAvGwPR2UlJfISWmfmYIwlwFuidg+jmujXmLlLwA7hQgA=="],
28
28
 
29
- "@tkeron/tools": ["@tkeron/tools@0.4.0", "", { "peerDependencies": { "typescript": "^5.9.3" } }, "sha512-lceNx2BGfEvL0U79BgLAb9ObGMwqeXoFYTdwbks1fDrxHaIinE1XWsQzF4k6jOSw00GFD++byUQMKgpiUilY5w=="],
29
+ "@tkeron/tools": ["@tkeron/tools@0.4.1", "", { "peerDependencies": { "typescript": "^5.9.3" } }, "sha512-nsmguSq/Ukx930ChwO11V433R786aJ/tniugzDdlfT/rzLrFxPJoHZerA71XGOIuD8/FRlvS7kJR9YI0CcxmqQ=="],
30
30
 
31
- "@types/bun": ["@types/bun@1.3.9", "", { "dependencies": { "bun-types": "1.3.9" } }, "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw=="],
31
+ "@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
32
32
 
33
33
  "@types/node": ["@types/node@25.3.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A=="],
34
34
 
@@ -40,7 +40,7 @@
40
40
 
41
41
  "body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],
42
42
 
43
- "bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="],
43
+ "bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
44
44
 
45
45
  "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
46
46
 
@@ -202,7 +202,7 @@
202
202
 
203
203
  "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
204
204
 
205
- "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
205
+ "typescript": ["typescript@6.0.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ=="],
206
206
 
207
207
  "undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
208
208
 
package/changelog.md CHANGED
@@ -1,3 +1,15 @@
1
+ # v5.1.0
2
+
3
+ ## Feature: .com.html template injection into .com.ts components
4
+
5
+ - When both `name.com.html` and `name.com.ts` exist for the same component, the `.com.html` content is loaded as `com.innerHTML` before executing the `.com.ts` code
6
+ - The `.com.html` acts as a static template that `.com.ts` can read, modify, or override
7
+ - Template resolution uses the same priority system: adjacent to `.com.ts` first, then fallback to root
8
+ - Fully backwards compatible: `.com.ts`-only components work exactly as before
9
+ - Added example `with_com_html_in_ts` demonstrating the feature
10
+
11
+ ---
12
+
1
13
  # v5.0.2
2
14
 
3
15
  ## Performance: component processing optimization
@@ -76,6 +76,8 @@ When multiple component types exist for the same name, Tkeron uses this priority
76
76
  2. **`.com.html`** (HTML components)
77
77
  3. **`.com.md`** (Markdown components — lowest priority)
78
78
 
79
+ **Special case:** When both `.com.ts` and `.com.html` exist for the same name, the `.com.html` is loaded as the template (`com.innerHTML`) before the `.com.ts` executes. See [TypeScript Components — HTML Template + TypeScript Logic](./components-typescript.md#html-template--typescript-logic).
80
+
79
81
  If both `hero-text.com.html` and `hero-text.com.md` exist, the `.com.html` version is used and `.com.md` is ignored.
80
82
 
81
83
  ### 4. Build Process
@@ -540,6 +540,65 @@ If a component doesn't need attributes or logic, use [HTML components](./compone
540
540
  </footer>
541
541
  ```
542
542
 
543
+ ## HTML Template + TypeScript Logic
544
+
545
+ When both `name.com.html` and `name.com.ts` exist for the same component, the `.com.html` content is loaded as `com.innerHTML` **before** the `.com.ts` code executes. This lets you separate structure from logic.
546
+
547
+ ### How It Works
548
+
549
+ ```
550
+ 1. Tkeron finds <user-card name="Alice">
551
+ 2. Loads user-card.com.html content into com.innerHTML
552
+ 3. Executes user-card.com.ts (com already has the template)
553
+ 4. Replaces <user-card> with the final com.innerHTML
554
+ ```
555
+
556
+ ### Example
557
+
558
+ **Template** (`user-card.com.html`):
559
+
560
+ ```html
561
+ <div class="card">
562
+ <h3 class="name"></h3>
563
+ <p class="role"></p>
564
+ </div>
565
+ ```
566
+
567
+ **Logic** (`user-card.com.ts`):
568
+
569
+ ```typescript
570
+ const name = com.getAttribute("data-name") || "Unknown";
571
+ const role = com.getAttribute("data-role") || "N/A";
572
+
573
+ const nameEl = com.querySelector(".name");
574
+ const roleEl = com.querySelector(".role");
575
+
576
+ if (nameEl) nameEl.textContent = name;
577
+ if (roleEl) roleEl.textContent = `Role: ${role}`;
578
+ ```
579
+
580
+ **Usage:**
581
+
582
+ ```html
583
+ <user-card data-name="Alice" data-role="Developer"></user-card>
584
+ ```
585
+
586
+ **Output:**
587
+
588
+ ```html
589
+ <div class="card">
590
+ <h3 class="name">Alice</h3>
591
+ <p class="role">Role: Developer</p>
592
+ </div>
593
+ ```
594
+
595
+ ### Key Points
596
+
597
+ - The `.com.ts` can read, modify, or completely override the template
598
+ - If only `.com.ts` exists (no `.com.html`), it works exactly as before
599
+ - Template resolution follows the same directory priority as components (adjacent first, then root)
600
+ - Great for separating HTML structure from dynamic logic
601
+
543
602
  ## Debugging
544
603
 
545
604
  ### Log at Build Time
package/docs/testing.md CHANGED
@@ -61,7 +61,7 @@ import { getBuildResult, type BuildResult } from "tkeron";
61
61
  import { join } from "path";
62
62
 
63
63
  describe("my project", () => {
64
- const sourcePath = join(import.meta.dir, "src");
64
+ const sourcePath = join(import.meta.dir, "websrc");
65
65
  let result: BuildResult;
66
66
 
67
67
  beforeAll(async () => {
@@ -0,0 +1,19 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>HTML Template in TS Component Example</title>
7
+ </head>
8
+ <body>
9
+ <h1>.com.html Template + .com.ts Logic</h1>
10
+
11
+ <user-card data-name="Alice" data-role="Developer"></user-card>
12
+ <user-card data-name="Bob" data-role="Designer"></user-card>
13
+
14
+ <info-panel
15
+ data-title="Features"
16
+ data-items="HTML templates for structure,TypeScript for logic,Clean separation of concerns"
17
+ ></info-panel>
18
+ </body>
19
+ </html>
@@ -0,0 +1,12 @@
1
+ <div
2
+ style="
3
+ background: #f0f9ff;
4
+ border: 1px solid #0284c7;
5
+ border-radius: 8px;
6
+ padding: 16px;
7
+ margin: 16px 0;
8
+ "
9
+ >
10
+ <h3 class="title" style="margin-top: 0; color: #0284c7"></h3>
11
+ <ul class="items"></ul>
12
+ </div>
@@ -0,0 +1,14 @@
1
+ const title = com.getAttribute("data-title") || "Info";
2
+ const rawItems = com.getAttribute("data-items") || "";
3
+ const items = rawItems
4
+ .split(",")
5
+ .map((s: string) => s.trim())
6
+ .filter((s: string) => s.length > 0);
7
+
8
+ const titleEl = com.querySelector(".title");
9
+ if (titleEl) titleEl.textContent = title;
10
+
11
+ const itemsEl = com.querySelector(".items");
12
+ if (itemsEl) {
13
+ itemsEl.innerHTML = items.map((item: string) => `<li>${item}</li>`).join("");
14
+ }
@@ -0,0 +1,12 @@
1
+ <div
2
+ style="
3
+ border: 2px solid #333;
4
+ border-radius: 8px;
5
+ padding: 16px;
6
+ margin: 16px 0;
7
+ max-width: 300px;
8
+ "
9
+ >
10
+ <h3 class="name" style="margin-top: 0; color: #2563eb"></h3>
11
+ <p class="role"></p>
12
+ </div>
@@ -0,0 +1,8 @@
1
+ const name = com.getAttribute("data-name") || "Unknown";
2
+ const role = com.getAttribute("data-role") || "N/A";
3
+
4
+ const nameEl = com.querySelector(".name");
5
+ const roleEl = com.querySelector(".role");
6
+
7
+ if (nameEl) nameEl.textContent = name;
8
+ if (roleEl) roleEl.textContent = `Role: ${role}`;
package/mcp-server.ts CHANGED
@@ -106,6 +106,8 @@ const EXAMPLE_DESCRIPTIONS: { [key: string]: string } = {
106
106
  "TypeScript components (.com.ts) with local override priority",
107
107
  with_com_mixed_priority:
108
108
  "Mixed .com.html and .com.ts showing processing order",
109
+ with_com_html_in_ts:
110
+ "HTML template (.com.html) injected into .com.ts as com.innerHTML before execution",
109
111
  with_component_iteration:
110
112
  "Component iteration: .com.ts with logic, .com.html for templates",
111
113
  };
@@ -175,8 +177,7 @@ server.registerTool(
175
177
  const examplePath = join(EXAMPLES_DIR, name);
176
178
  return (
177
179
  statSync(examplePath).isDirectory() &&
178
- (existsSync(join(examplePath, "src")) ||
179
- existsSync(join(examplePath, "websrc")))
180
+ existsSync(join(examplePath, "websrc"))
180
181
  );
181
182
  });
182
183
 
@@ -226,17 +227,14 @@ server.registerTool(
226
227
  };
227
228
  }
228
229
 
229
- let srcPath = join(examplePath, "src");
230
- if (!existsSync(srcPath)) {
231
- srcPath = join(examplePath, "websrc");
232
- }
230
+ const srcPath = join(examplePath, "websrc");
233
231
 
234
232
  if (!existsSync(srcPath)) {
235
233
  return {
236
234
  content: [
237
235
  {
238
236
  type: "text" as const,
239
- text: `Example ${exampleName} does not have a src or websrc directory`,
237
+ text: `Example ${exampleName} does not have a websrc directory`,
240
238
  },
241
239
  ],
242
240
  isError: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tkeron",
3
- "version": "5.0.2",
3
+ "version": "5.1.1",
4
4
  "description": "CLI build tool for vanilla web development with TypeScript.",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -26,20 +26,21 @@
26
26
  "sample5": "bun . build examples/with_com_ts/src",
27
27
  "sample6": "bun . build examples/with_com_ts_priority/src",
28
28
  "sample7": "bun . build examples/with_com_mixed_priority/src",
29
- "build_examples": "bun run sample1 && bun run sample2 && bun run sample3 && bun run sample4 && bun run sample5 && bun run sample6 && bun run sample7"
29
+ "sample8": "bun . build examples/with_com_html_in_ts/src",
30
+ "build_examples": "bun run sample1 && bun run sample2 && bun run sample3 && bun run sample4 && bun run sample5 && bun run sample6 && bun run sample7 && bun run sample8"
30
31
  },
31
32
  "author": "tkeron",
32
33
  "license": "MIT",
33
34
  "dependencies": {
34
- "@modelcontextprotocol/sdk": "^1.26.0",
35
- "@tkeron/commands": "0.4.6",
36
- "@tkeron/html-parser": "1.5.5",
37
- "@tkeron/tools": "^0.4.0"
35
+ "@modelcontextprotocol/sdk": "^1.29.0",
36
+ "@tkeron/commands": "0.4.7",
37
+ "@tkeron/html-parser": "1.5.6",
38
+ "@tkeron/tools": "^0.4.1"
38
39
  },
39
40
  "devDependencies": {
40
- "@types/bun": "^1.3.9",
41
+ "@types/bun": "^1.3.11",
41
42
  "prettier": "^3.8.1",
42
- "typescript": "^5.9.3"
43
+ "typescript": "^6.0.2"
43
44
  },
44
45
  "keywords": [
45
46
  "cli",
@@ -56,6 +57,7 @@
56
57
  "publishConfig": {
57
58
  "access": "public"
58
59
  },
60
+ "homepage": "https://tkeron.com",
59
61
  "repository": {
60
62
  "type": "git",
61
63
  "url": "git+https://github.com/tkeron/tkeron.git"
package/src/develop.ts CHANGED
@@ -130,30 +130,45 @@ export const develop = async (
130
130
 
131
131
  log.log(`🚀 Development server running at http://${host}:${port}`);
132
132
 
133
+ let currentBuild: Promise<void> | null = null;
134
+ let isStopped = false;
135
+
133
136
  const watcher = watch(
134
137
  source,
135
138
  { recursive: true },
136
139
  async (event, filename) => {
137
- if (filename) {
138
- log.log(`📝 File changed: ${filename}`);
139
- log.log("🔨 Rebuilding...");
140
- await build({ sourceDir: source, targetDir: target, logger: log });
141
- log.log("✅ Build complete!");
142
-
143
- reloadClients.forEach((controller) => {
144
- try {
145
- controller.enqueue("data: reload\n\n");
146
- } catch (e) {
147
- reloadClients.delete(controller);
148
- }
149
- });
140
+ if (!filename || isStopped || currentBuild) return;
141
+ log.log(`📝 File changed: ${filename}`);
142
+ log.log("🔨 Rebuilding...");
143
+ const buildPromise = build({
144
+ sourceDir: source,
145
+ targetDir: target,
146
+ logger: log,
147
+ });
148
+ currentBuild = buildPromise;
149
+ try {
150
+ await buildPromise;
151
+ } finally {
152
+ if (currentBuild === buildPromise) currentBuild = null;
150
153
  }
154
+ if (isStopped) return;
155
+ log.log("✅ Build complete!");
156
+
157
+ reloadClients.forEach((controller) => {
158
+ try {
159
+ controller.enqueue("data: reload\n\n");
160
+ } catch (e) {
161
+ reloadClients.delete(controller);
162
+ }
163
+ });
151
164
  },
152
165
  );
153
166
 
154
167
  const stop = async () => {
155
168
  log.log("\n👋 Shutting down server...");
169
+ isStopped = true;
156
170
  watcher.close();
171
+ await currentBuild?.catch(() => {});
157
172
  server.stop();
158
173
 
159
174
  await new Promise((resolve) => setTimeout(resolve, 100));
@@ -14,6 +14,7 @@ const ensureHtmlDocument = (html: string): string => {
14
14
 
15
15
  interface ComponentCache {
16
16
  componentMap: Map<string, string[]>;
17
+ htmlTemplateMap: Map<string, string[]>;
17
18
  contentCache: Map<string, string>;
18
19
  transpileCache: Map<string, string>;
19
20
  }
@@ -30,6 +31,18 @@ const buildComponentMap = (rootDir: string): Map<string, string[]> => {
30
31
  return map;
31
32
  };
32
33
 
34
+ const buildHtmlTemplateMap = (rootDir: string): Map<string, string[]> => {
35
+ const allFiles = getFilePaths(rootDir, "**/*.com.html", true);
36
+ const map = new Map<string, string[]>();
37
+ for (const file of allFiles) {
38
+ const basename = file.split("/").pop()!;
39
+ const tagName = basename.replace(/\.com\.html$/, "");
40
+ if (!map.has(tagName)) map.set(tagName, []);
41
+ map.get(tagName)!.push(file);
42
+ }
43
+ return map;
44
+ };
45
+
33
46
  const getCachedContent = async (
34
47
  path: string,
35
48
  cache: Map<string, string>,
@@ -69,8 +82,10 @@ export const processComTs = async (
69
82
  }
70
83
 
71
84
  const componentMap = buildComponentMap(tempDir);
85
+ const htmlTemplateMap = buildHtmlTemplateMap(tempDir);
72
86
  const cache: ComponentCache = {
73
87
  componentMap,
88
+ htmlTemplateMap,
74
89
  contentCache: new Map(),
75
90
  transpileCache: new Map(),
76
91
  };
@@ -123,6 +138,18 @@ const resolveComponent = (
123
138
  return matches[0]!;
124
139
  };
125
140
 
141
+ const resolveHtmlTemplate = (
142
+ tagName: string,
143
+ tsDir: string,
144
+ htmlTemplateMap: Map<string, string[]>,
145
+ ): string | null => {
146
+ const matches = htmlTemplateMap.get(tagName);
147
+ if (!matches || matches.length === 0) return null;
148
+ const adjacentPath = join(tsDir, `${tagName}.com.html`);
149
+ if (matches.includes(adjacentPath)) return adjacentPath;
150
+ return matches[0]!;
151
+ };
152
+
126
153
  const processComponentsTs = async (
127
154
  element: any,
128
155
  currentDir: string,
@@ -209,6 +236,19 @@ const processComponentsTs = async (
209
236
  );
210
237
  }
211
238
 
239
+ const htmlTemplatePath = resolveHtmlTemplate(
240
+ tagName,
241
+ dirname(componentPath),
242
+ cache.htmlTemplateMap,
243
+ );
244
+ if (htmlTemplatePath) {
245
+ const templateContent = await getCachedContent(
246
+ htmlTemplatePath,
247
+ cache.contentCache,
248
+ );
249
+ com.innerHTML = templateContent;
250
+ }
251
+
212
252
  const hasImports = /^\s*import\s+/m.test(originalComponentCode);
213
253
 
214
254
  try {
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes