tkeron 5.0.2 → 5.1.0

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.26.0",
8
+ "@modelcontextprotocol/sdk": "^1.27.0",
9
9
  "@tkeron/commands": "0.4.6",
10
10
  "@tkeron/html-parser": "1.5.5",
11
11
  "@tkeron/tools": "^0.4.0",
@@ -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.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.27.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-qOdO524oPMkUsOJTrsH9vz/HN3B5pKyW+9zIW51A9kDMVe7ON70drz1ouoyoyOcfzc+oxhkQ6jWmbyKnlWmYqA=="],
24
24
 
25
25
  "@tkeron/commands": ["@tkeron/commands@0.4.6", "", { "peerDependencies": { "typescript": "^5.9.3" } }, "sha512-Bojs3GmIpvd/4Mcw7FvbohRcDyHEzs3ZJHxwIeXE0diQzN+ER3R30xIFovgzwE8QfWvC9uFDaN2VTk4O+0ffyA=="],
26
26
 
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
@@ -0,0 +1,20 @@
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 data-title="Features">
15
+ <li>HTML templates for structure</li>
16
+ <li>TypeScript for logic</li>
17
+ <li>Clean separation of concerns</li>
18
+ </info-panel>
19
+ </body>
20
+ </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,3 @@
1
+ const title = com.getAttribute("data-title") || "Info";
2
+ const titleEl = com.querySelector(".title");
3
+ if (titleEl) titleEl.textContent = title;
@@ -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
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tkeron",
3
- "version": "5.0.2",
3
+ "version": "5.1.0",
4
4
  "description": "CLI build tool for vanilla web development with TypeScript.",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -26,12 +26,13 @@
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
+ "@modelcontextprotocol/sdk": "^1.27.0",
35
36
  "@tkeron/commands": "0.4.6",
36
37
  "@tkeron/html-parser": "1.5.5",
37
38
  "@tkeron/tools": "^0.4.0"
@@ -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 {