tkeron 4.0.0-beta.9 → 4.0.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/{readme.md → README.md} +36 -4
- package/bun.lock +23 -21
- package/changelog.md +274 -8
- package/docs/best-practices.md +127 -101
- package/docs/cli-reference.md +58 -38
- package/docs/components-html.md +30 -24
- package/docs/components-typescript.md +110 -79
- package/docs/getting-started.md +24 -18
- package/docs/mcp-server.md +19 -36
- package/docs/overview.md +16 -10
- package/docs/pre-rendering.md +163 -157
- package/docs/testing.md +331 -0
- package/examples/basic_build/src/index.ts +5 -5
- package/examples/with_assets/src/index.ts +5 -5
- package/examples/with_com_html_priority/src/admin/admin-panel.com.html +1 -1
- package/examples/with_com_html_priority/src/admin/dashboard.html +16 -16
- package/examples/with_com_html_priority/src/admin/nested-stat.com.html +1 -1
- package/examples/with_com_html_priority/src/admin/site-header.com.html +1 -1
- package/examples/with_com_html_priority/src/blog/comment-item.com.html +1 -1
- package/examples/with_com_html_priority/src/blog/comment-section.com.html +1 -1
- package/examples/with_com_html_priority/src/blog/post.html +22 -19
- package/examples/with_com_html_priority/src/index.html +15 -15
- package/examples/with_com_html_priority/src/info-box.com.html +1 -1
- package/examples/with_com_html_priority/src/site-header.com.html +1 -1
- package/examples/with_com_mixed_priority/src/dashboard/main.html +17 -17
- package/examples/with_com_mixed_priority/src/dashboard/stats-widget.com.ts +1 -1
- package/examples/with_com_mixed_priority/src/footer-info.com.html +3 -1
- package/examples/with_com_mixed_priority/src/index.html +16 -16
- package/examples/with_com_mixed_priority/src/page-header.com.html +8 -1
- package/examples/with_com_mixed_priority/src/settings/config.html +16 -16
- package/examples/with_com_mixed_priority/src/settings/priority-test.com.html +3 -1
- package/examples/with_com_mixed_priority/src/users/profiles/activity-feed.com.ts +7 -3
- package/examples/with_com_mixed_priority/src/users/profiles/page-header.com.html +8 -1
- package/examples/with_com_mixed_priority/src/users/profiles/profile-actions.com.html +45 -4
- package/examples/with_com_mixed_priority/src/users/profiles/user-profile.com.ts +1 -1
- package/examples/with_com_mixed_priority/src/users/profiles/view.html +17 -17
- package/examples/with_com_ts/src/index.html +15 -11
- package/examples/with_com_ts_priority/src/analytics/chart-widget.com.ts +7 -5
- package/examples/with_com_ts_priority/src/analytics/report.html +16 -16
- package/examples/with_com_ts_priority/src/analytics/user-stats.com.ts +1 -1
- package/examples/with_com_ts_priority/src/data-table.com.ts +7 -4
- package/examples/with_com_ts_priority/src/index.html +15 -15
- package/examples/with_com_ts_priority/src/sales/regional/data-table.com.ts +8 -5
- package/examples/with_com_ts_priority/src/sales/regional/overview.html +16 -16
- package/examples/with_com_ts_priority/src/sales/regional/sales-summary.com.ts +7 -4
- package/examples/with_com_ts_priority/src/user-stats.com.ts +1 -1
- package/examples/with_component_iteration/src/card-list.com.ts +40 -12
- package/examples/with_component_iteration/src/engagement-meter.com.ts +11 -6
- package/examples/with_component_iteration/src/index.html +108 -51
- package/examples/with_component_iteration/src/index.ts +1 -1
- package/examples/with_component_iteration/src/note-box.com.html +12 -2
- package/examples/with_component_iteration/src/status-badge.com.ts +14 -7
- package/examples/with_component_iteration/src/user-card.com.ts +10 -9
- package/examples/with_pre/src/contact.pre.ts +3 -3
- package/examples/with_pre/src/index.ts +5 -5
- package/examples/with_pre/src/section/index.pre.ts +2 -4
- package/index.ts +31 -40
- package/mcp-server.ts +168 -445
- package/package.json +19 -8
- package/src/banner.ts +5 -4
- package/src/build.ts +29 -29
- package/src/buildDir.ts +36 -18
- package/src/buildEntrypoints.ts +17 -6
- package/src/buildWrapper.ts +21 -0
- package/src/cleanupOrphanedTempDirs.ts +31 -0
- package/src/createTestLogger.ts +24 -0
- package/src/develop.ts +47 -46
- package/src/getBuildResult.ts +146 -0
- package/src/index.ts +7 -0
- package/src/init.ts +39 -42
- package/src/initWrapper.ts +39 -26
- package/src/logger.ts +4 -11
- package/src/loggerObj.ts +13 -0
- package/src/processCom.ts +136 -108
- package/src/processComTs.ts +173 -151
- package/src/processPre.ts +24 -27
- package/src/promptUser.ts +15 -0
- package/src/setupSigintHandler.ts +6 -0
- package/src/silentLogger.ts +8 -0
- package/tests/test-helpers.ts +30 -0
- package/funciones.md +0 -63
package/docs/pre-rendering.md
CHANGED
|
@@ -8,9 +8,9 @@ Create a `.pre.ts` file with the same base name as your `.html` file:
|
|
|
8
8
|
|
|
9
9
|
```typescript
|
|
10
10
|
// index.pre.ts
|
|
11
|
-
const title = document.querySelector(
|
|
11
|
+
const title = document.querySelector("title");
|
|
12
12
|
if (title) {
|
|
13
|
-
title.textContent =
|
|
13
|
+
title.textContent = "My Awesome Site";
|
|
14
14
|
}
|
|
15
15
|
```
|
|
16
16
|
|
|
@@ -20,8 +20,10 @@ The corresponding `index.html` is automatically loaded and modified:
|
|
|
20
20
|
<!-- index.html - before -->
|
|
21
21
|
<!DOCTYPE html>
|
|
22
22
|
<html>
|
|
23
|
-
<head
|
|
24
|
-
<
|
|
23
|
+
<head>
|
|
24
|
+
<title>Document</title>
|
|
25
|
+
</head>
|
|
26
|
+
<body></body>
|
|
25
27
|
</html>
|
|
26
28
|
```
|
|
27
29
|
|
|
@@ -31,8 +33,10 @@ After build:
|
|
|
31
33
|
<!-- index.html - after -->
|
|
32
34
|
<!DOCTYPE html>
|
|
33
35
|
<html>
|
|
34
|
-
<head
|
|
35
|
-
<
|
|
36
|
+
<head>
|
|
37
|
+
<title>My Awesome Site</title>
|
|
38
|
+
</head>
|
|
39
|
+
<body></body>
|
|
36
40
|
</html>
|
|
37
41
|
```
|
|
38
42
|
|
|
@@ -42,10 +46,10 @@ After build:
|
|
|
42
46
|
|
|
43
47
|
Pre-rendering files are paired with HTML files:
|
|
44
48
|
|
|
45
|
-
| Pre-render File
|
|
46
|
-
|
|
47
|
-
| `index.pre.ts`
|
|
48
|
-
| `about.pre.ts`
|
|
49
|
+
| Pre-render File | Processes |
|
|
50
|
+
| ------------------ | ---------------- |
|
|
51
|
+
| `index.pre.ts` | `index.html` |
|
|
52
|
+
| `about.pre.ts` | `about.html` |
|
|
49
53
|
| `blog/post.pre.ts` | `blog/post.html` |
|
|
50
54
|
|
|
51
55
|
### The `document` Variable
|
|
@@ -54,11 +58,11 @@ Every `.pre.ts` file has access to a special `document` variable:
|
|
|
54
58
|
|
|
55
59
|
```typescript
|
|
56
60
|
// Access the full DOM
|
|
57
|
-
document.querySelector(
|
|
58
|
-
document.getElementById(
|
|
59
|
-
document.querySelectorAll(
|
|
60
|
-
document.body
|
|
61
|
-
document.documentElement
|
|
61
|
+
document.querySelector("h1");
|
|
62
|
+
document.getElementById("content");
|
|
63
|
+
document.querySelectorAll(".items");
|
|
64
|
+
document.body;
|
|
65
|
+
document.documentElement;
|
|
62
66
|
```
|
|
63
67
|
|
|
64
68
|
This is the parsed DOM from the corresponding `.html` file.
|
|
@@ -82,13 +86,12 @@ If the `.html` file doesn't exist, Tkeron creates a default one:
|
|
|
82
86
|
```html
|
|
83
87
|
<!DOCTYPE html>
|
|
84
88
|
<html lang="en">
|
|
85
|
-
<head>
|
|
86
|
-
<meta charset="UTF-8"
|
|
87
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0"
|
|
89
|
+
<head>
|
|
90
|
+
<meta charset="UTF-8" />
|
|
91
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
88
92
|
<title>Document</title>
|
|
89
|
-
</head>
|
|
90
|
-
<body>
|
|
91
|
-
</body>
|
|
93
|
+
</head>
|
|
94
|
+
<body></body>
|
|
92
95
|
</html>
|
|
93
96
|
```
|
|
94
97
|
|
|
@@ -100,9 +103,9 @@ Then your `.pre.ts` file runs on this template.
|
|
|
100
103
|
|
|
101
104
|
```typescript
|
|
102
105
|
// index.pre.ts
|
|
103
|
-
const title = document.querySelector(
|
|
106
|
+
const title = document.querySelector("title");
|
|
104
107
|
if (title) {
|
|
105
|
-
title.textContent =
|
|
108
|
+
title.textContent = "Welcome to My Site";
|
|
106
109
|
}
|
|
107
110
|
```
|
|
108
111
|
|
|
@@ -110,12 +113,12 @@ if (title) {
|
|
|
110
113
|
|
|
111
114
|
```typescript
|
|
112
115
|
// index.pre.ts
|
|
113
|
-
const head = document.querySelector(
|
|
116
|
+
const head = document.querySelector("head");
|
|
114
117
|
|
|
115
118
|
if (head) {
|
|
116
|
-
const meta = document.createElement(
|
|
117
|
-
meta.setAttribute(
|
|
118
|
-
meta.setAttribute(
|
|
119
|
+
const meta = document.createElement("meta");
|
|
120
|
+
meta.setAttribute("name", "description");
|
|
121
|
+
meta.setAttribute("content", "A description of my site");
|
|
119
122
|
head.appendChild(meta);
|
|
120
123
|
}
|
|
121
124
|
```
|
|
@@ -124,11 +127,11 @@ if (head) {
|
|
|
124
127
|
|
|
125
128
|
```typescript
|
|
126
129
|
// index.pre.ts
|
|
127
|
-
const body = document.querySelector(
|
|
130
|
+
const body = document.querySelector("body");
|
|
128
131
|
|
|
129
132
|
if (body) {
|
|
130
|
-
const header = document.createElement(
|
|
131
|
-
header.innerHTML =
|
|
133
|
+
const header = document.createElement("header");
|
|
134
|
+
header.innerHTML = "<h1>Welcome!</h1>";
|
|
132
135
|
body.insertBefore(header, body.firstChild);
|
|
133
136
|
}
|
|
134
137
|
```
|
|
@@ -137,7 +140,7 @@ if (body) {
|
|
|
137
140
|
|
|
138
141
|
```typescript
|
|
139
142
|
// index.pre.ts
|
|
140
|
-
const buildTimeElement = document.getElementById(
|
|
143
|
+
const buildTimeElement = document.getElementById("build-time");
|
|
141
144
|
|
|
142
145
|
if (buildTimeElement) {
|
|
143
146
|
const now = new Date();
|
|
@@ -172,15 +175,15 @@ interface BlogPost {
|
|
|
172
175
|
}
|
|
173
176
|
|
|
174
177
|
const posts: BlogPost[] = [
|
|
175
|
-
{ title:
|
|
176
|
-
{ title:
|
|
178
|
+
{ title: "First Post", date: "2025-01-01", excerpt: "This is my first post" },
|
|
179
|
+
{ title: "Second Post", date: "2025-01-15", excerpt: "Another great post" },
|
|
177
180
|
];
|
|
178
181
|
|
|
179
|
-
const container = document.getElementById(
|
|
182
|
+
const container = document.getElementById("blog-posts");
|
|
180
183
|
|
|
181
184
|
if (container) {
|
|
182
|
-
posts.forEach(post => {
|
|
183
|
-
const article = document.createElement(
|
|
185
|
+
posts.forEach((post) => {
|
|
186
|
+
const article = document.createElement("article");
|
|
184
187
|
article.innerHTML = `
|
|
185
188
|
<h2>${post.title}</h2>
|
|
186
189
|
<time>${post.date}</time>
|
|
@@ -195,11 +198,11 @@ if (container) {
|
|
|
195
198
|
|
|
196
199
|
```typescript
|
|
197
200
|
// index.pre.ts
|
|
198
|
-
import { getBuildMetadata } from
|
|
201
|
+
import { getBuildMetadata } from "./build-utils";
|
|
199
202
|
|
|
200
203
|
const metadata = await getBuildMetadata();
|
|
201
204
|
|
|
202
|
-
const footer = document.querySelector(
|
|
205
|
+
const footer = document.querySelector("footer");
|
|
203
206
|
if (footer) {
|
|
204
207
|
footer.innerHTML = `
|
|
205
208
|
<p>Built: ${metadata.timestamp}</p>
|
|
@@ -213,7 +216,7 @@ if (footer) {
|
|
|
213
216
|
export async function getBuildMetadata() {
|
|
214
217
|
return {
|
|
215
218
|
timestamp: new Date().toISOString(),
|
|
216
|
-
version: process.env.TKERON_VERSION ||
|
|
219
|
+
version: process.env.TKERON_VERSION || "unknown",
|
|
217
220
|
};
|
|
218
221
|
}
|
|
219
222
|
```
|
|
@@ -231,17 +234,17 @@ interface Product {
|
|
|
231
234
|
}
|
|
232
235
|
|
|
233
236
|
async function getProducts(): Promise<Product[]> {
|
|
234
|
-
const response = await fetch(
|
|
237
|
+
const response = await fetch("https://api.example.com/products");
|
|
235
238
|
return response.json();
|
|
236
239
|
}
|
|
237
240
|
|
|
238
241
|
const products = await getProducts();
|
|
239
|
-
const container = document.getElementById(
|
|
242
|
+
const container = document.getElementById("products");
|
|
240
243
|
|
|
241
244
|
if (container) {
|
|
242
|
-
products.forEach(product => {
|
|
243
|
-
const card = document.createElement(
|
|
244
|
-
card.className =
|
|
245
|
+
products.forEach((product) => {
|
|
246
|
+
const card = document.createElement("div");
|
|
247
|
+
card.className = "product-card";
|
|
245
248
|
card.innerHTML = `
|
|
246
249
|
<h3>${product.name}</h3>
|
|
247
250
|
<p>$${product.price}</p>
|
|
@@ -257,15 +260,15 @@ if (container) {
|
|
|
257
260
|
|
|
258
261
|
```typescript
|
|
259
262
|
// blog-post.pre.ts
|
|
260
|
-
import { marked } from
|
|
261
|
-
import { readFileSync } from
|
|
262
|
-
import { join } from
|
|
263
|
+
import { marked } from "marked";
|
|
264
|
+
import { readFileSync } from "fs";
|
|
265
|
+
import { join } from "path";
|
|
263
266
|
|
|
264
|
-
const markdownPath = join(import.meta.dir,
|
|
265
|
-
const markdown = readFileSync(markdownPath,
|
|
267
|
+
const markdownPath = join(import.meta.dir, "post-content.md");
|
|
268
|
+
const markdown = readFileSync(markdownPath, "utf-8");
|
|
266
269
|
const html = marked(markdown);
|
|
267
270
|
|
|
268
|
-
const article = document.querySelector(
|
|
271
|
+
const article = document.querySelector("article");
|
|
269
272
|
if (article) {
|
|
270
273
|
article.innerHTML = html;
|
|
271
274
|
}
|
|
@@ -284,9 +287,9 @@ Access environment variables:
|
|
|
284
287
|
```typescript
|
|
285
288
|
// index.pre.ts
|
|
286
289
|
const version = process.env.TKERON_VERSION;
|
|
287
|
-
const nodeEnv = process.env.NODE_ENV ||
|
|
290
|
+
const nodeEnv = process.env.NODE_ENV || "development";
|
|
288
291
|
|
|
289
|
-
const versionElement = document.getElementById(
|
|
292
|
+
const versionElement = document.getElementById("version");
|
|
290
293
|
if (versionElement) {
|
|
291
294
|
versionElement.textContent = `v${version} (${nodeEnv})`;
|
|
292
295
|
}
|
|
@@ -296,7 +299,7 @@ if (versionElement) {
|
|
|
296
299
|
|
|
297
300
|
```typescript
|
|
298
301
|
// index.pre.ts
|
|
299
|
-
const buildInfo = document.getElementById(
|
|
302
|
+
const buildInfo = document.getElementById("build-info");
|
|
300
303
|
|
|
301
304
|
if (buildInfo) {
|
|
302
305
|
buildInfo.innerHTML = `
|
|
@@ -313,11 +316,11 @@ Inject components based on logic:
|
|
|
313
316
|
|
|
314
317
|
```typescript
|
|
315
318
|
// dashboard.pre.ts
|
|
316
|
-
const isDevelopment = process.env.NODE_ENV ===
|
|
317
|
-
const body = document.querySelector(
|
|
319
|
+
const isDevelopment = process.env.NODE_ENV === "development";
|
|
320
|
+
const body = document.querySelector("body");
|
|
318
321
|
|
|
319
322
|
if (body && isDevelopment) {
|
|
320
|
-
const debugPanel = document.createElement(
|
|
323
|
+
const debugPanel = document.createElement("debug-panel");
|
|
321
324
|
body.appendChild(debugPanel);
|
|
322
325
|
}
|
|
323
326
|
```
|
|
@@ -340,24 +343,24 @@ interface Post {
|
|
|
340
343
|
|
|
341
344
|
async function getPosts(): Promise<Post[]> {
|
|
342
345
|
try {
|
|
343
|
-
const response = await fetch(
|
|
346
|
+
const response = await fetch("https://api.example.com/posts");
|
|
344
347
|
return response.json();
|
|
345
348
|
} catch (error) {
|
|
346
|
-
console.warn(
|
|
349
|
+
console.warn("Failed to fetch posts, using fallback");
|
|
347
350
|
return [];
|
|
348
351
|
}
|
|
349
352
|
}
|
|
350
353
|
|
|
351
354
|
const posts = await getPosts();
|
|
352
|
-
const blogList = document.getElementById(
|
|
355
|
+
const blogList = document.getElementById("blog-list");
|
|
353
356
|
|
|
354
357
|
if (blogList) {
|
|
355
358
|
if (posts.length === 0) {
|
|
356
|
-
blogList.innerHTML =
|
|
359
|
+
blogList.innerHTML = "<p>No posts available</p>";
|
|
357
360
|
} else {
|
|
358
|
-
posts.forEach(post => {
|
|
359
|
-
const article = document.createElement(
|
|
360
|
-
article.className =
|
|
361
|
+
posts.forEach((post) => {
|
|
362
|
+
const article = document.createElement("article");
|
|
363
|
+
article.className = "blog-preview";
|
|
361
364
|
article.innerHTML = `
|
|
362
365
|
<h2><a href="/blog/${post.slug}">${post.title}</a></h2>
|
|
363
366
|
<time>${new Date(post.date).toLocaleDateString()}</time>
|
|
@@ -381,41 +384,41 @@ interface ProductData {
|
|
|
381
384
|
}
|
|
382
385
|
|
|
383
386
|
const productData: ProductData = {
|
|
384
|
-
name:
|
|
385
|
-
description:
|
|
386
|
-
image:
|
|
387
|
+
name: "Amazing Product",
|
|
388
|
+
description: "The best product you can buy",
|
|
389
|
+
image: "https://example.com/product.jpg",
|
|
387
390
|
price: 99.99,
|
|
388
391
|
};
|
|
389
392
|
|
|
390
|
-
const head = document.querySelector(
|
|
393
|
+
const head = document.querySelector("head");
|
|
391
394
|
|
|
392
395
|
if (head) {
|
|
393
396
|
// Title
|
|
394
|
-
const title = document.querySelector(
|
|
397
|
+
const title = document.querySelector("title");
|
|
395
398
|
if (title) {
|
|
396
399
|
title.textContent = `${productData.name} - My Store`;
|
|
397
400
|
}
|
|
398
401
|
|
|
399
402
|
// Meta description
|
|
400
|
-
const metaDesc = document.createElement(
|
|
401
|
-
metaDesc.setAttribute(
|
|
402
|
-
metaDesc.setAttribute(
|
|
403
|
+
const metaDesc = document.createElement("meta");
|
|
404
|
+
metaDesc.setAttribute("name", "description");
|
|
405
|
+
metaDesc.setAttribute("content", productData.description);
|
|
403
406
|
head.appendChild(metaDesc);
|
|
404
407
|
|
|
405
408
|
// Open Graph
|
|
406
|
-
const ogTitle = document.createElement(
|
|
407
|
-
ogTitle.setAttribute(
|
|
408
|
-
ogTitle.setAttribute(
|
|
409
|
+
const ogTitle = document.createElement("meta");
|
|
410
|
+
ogTitle.setAttribute("property", "og:title");
|
|
411
|
+
ogTitle.setAttribute("content", productData.name);
|
|
409
412
|
head.appendChild(ogTitle);
|
|
410
413
|
|
|
411
|
-
const ogImage = document.createElement(
|
|
412
|
-
ogImage.setAttribute(
|
|
413
|
-
ogImage.setAttribute(
|
|
414
|
+
const ogImage = document.createElement("meta");
|
|
415
|
+
ogImage.setAttribute("property", "og:image");
|
|
416
|
+
ogImage.setAttribute("content", productData.image);
|
|
414
417
|
head.appendChild(ogImage);
|
|
415
418
|
|
|
416
|
-
const ogPrice = document.createElement(
|
|
417
|
-
ogPrice.setAttribute(
|
|
418
|
-
ogPrice.setAttribute(
|
|
419
|
+
const ogPrice = document.createElement("meta");
|
|
420
|
+
ogPrice.setAttribute("property", "og:price:amount");
|
|
421
|
+
ogPrice.setAttribute("content", productData.price.toString());
|
|
419
422
|
head.appendChild(ogPrice);
|
|
420
423
|
}
|
|
421
424
|
```
|
|
@@ -425,22 +428,22 @@ if (head) {
|
|
|
425
428
|
```typescript
|
|
426
429
|
// sitemap.pre.ts
|
|
427
430
|
const pages = [
|
|
428
|
-
{ url:
|
|
429
|
-
{ url:
|
|
430
|
-
{ url:
|
|
431
|
+
{ url: "/", priority: 1.0 },
|
|
432
|
+
{ url: "/about", priority: 0.8 },
|
|
433
|
+
{ url: "/contact", priority: 0.5 },
|
|
431
434
|
];
|
|
432
435
|
|
|
433
|
-
const urlset = document.createElement(
|
|
434
|
-
urlset.setAttribute(
|
|
436
|
+
const urlset = document.createElement("urlset");
|
|
437
|
+
urlset.setAttribute("xmlns", "http://www.sitemaps.org/schemas/sitemap/0.9");
|
|
435
438
|
|
|
436
|
-
pages.forEach(page => {
|
|
437
|
-
const url = document.createElement(
|
|
438
|
-
const loc = document.createElement(
|
|
439
|
+
pages.forEach((page) => {
|
|
440
|
+
const url = document.createElement("url");
|
|
441
|
+
const loc = document.createElement("loc");
|
|
439
442
|
loc.textContent = `https://example.com${page.url}`;
|
|
440
|
-
|
|
441
|
-
const priority = document.createElement(
|
|
443
|
+
|
|
444
|
+
const priority = document.createElement("priority");
|
|
442
445
|
priority.textContent = page.priority.toString();
|
|
443
|
-
|
|
446
|
+
|
|
444
447
|
url.appendChild(loc);
|
|
445
448
|
url.appendChild(priority);
|
|
446
449
|
urlset.appendChild(url);
|
|
@@ -453,13 +456,13 @@ document.documentElement.replaceWith(urlset);
|
|
|
453
456
|
|
|
454
457
|
```typescript
|
|
455
458
|
// documentation.pre.ts
|
|
456
|
-
import { readFileSync } from
|
|
457
|
-
import { join } from
|
|
459
|
+
import { readFileSync } from "fs";
|
|
460
|
+
import { join } from "path";
|
|
458
461
|
|
|
459
|
-
const changelogPath = join(import.meta.dir,
|
|
460
|
-
const changelog = readFileSync(changelogPath,
|
|
462
|
+
const changelogPath = join(import.meta.dir, "..", "CHANGELOG.md");
|
|
463
|
+
const changelog = readFileSync(changelogPath, "utf-8");
|
|
461
464
|
|
|
462
|
-
const changelogElement = document.getElementById(
|
|
465
|
+
const changelogElement = document.getElementById("changelog");
|
|
463
466
|
if (changelogElement) {
|
|
464
467
|
changelogElement.innerHTML = `<pre>${changelog}</pre>`;
|
|
465
468
|
}
|
|
@@ -469,20 +472,21 @@ if (changelogElement) {
|
|
|
469
472
|
|
|
470
473
|
```typescript
|
|
471
474
|
// index.pre.ts
|
|
472
|
-
const isProd = process.env.NODE_ENV ===
|
|
473
|
-
const body = document.querySelector(
|
|
475
|
+
const isProd = process.env.NODE_ENV === "production";
|
|
476
|
+
const body = document.querySelector("body");
|
|
474
477
|
|
|
475
478
|
if (body) {
|
|
476
479
|
if (isProd) {
|
|
477
480
|
// Add analytics
|
|
478
|
-
const script = document.createElement(
|
|
479
|
-
script.src =
|
|
481
|
+
const script = document.createElement("script");
|
|
482
|
+
script.src = "https://analytics.example.com/script.js";
|
|
480
483
|
body.appendChild(script);
|
|
481
484
|
} else {
|
|
482
485
|
// Add development banner
|
|
483
|
-
const banner = document.createElement(
|
|
484
|
-
banner.style.cssText =
|
|
485
|
-
|
|
486
|
+
const banner = document.createElement("div");
|
|
487
|
+
banner.style.cssText =
|
|
488
|
+
"background: #fef2f2; padding: 1rem; text-align: center;";
|
|
489
|
+
banner.textContent = "⚠️ Development Mode";
|
|
486
490
|
body.insertBefore(banner, body.firstChild);
|
|
487
491
|
}
|
|
488
492
|
}
|
|
@@ -493,37 +497,37 @@ if (body) {
|
|
|
493
497
|
### Creating Elements
|
|
494
498
|
|
|
495
499
|
```typescript
|
|
496
|
-
const div = document.createElement(
|
|
497
|
-
div.className =
|
|
498
|
-
div.id =
|
|
499
|
-
div.innerHTML =
|
|
500
|
+
const div = document.createElement("div");
|
|
501
|
+
div.className = "container";
|
|
502
|
+
div.id = "main";
|
|
503
|
+
div.innerHTML = "<p>Content</p>";
|
|
500
504
|
```
|
|
501
505
|
|
|
502
506
|
### Modifying Attributes
|
|
503
507
|
|
|
504
508
|
```typescript
|
|
505
|
-
const img = document.querySelector(
|
|
509
|
+
const img = document.querySelector("img");
|
|
506
510
|
if (img) {
|
|
507
|
-
img.setAttribute(
|
|
508
|
-
img.setAttribute(
|
|
511
|
+
img.setAttribute("loading", "lazy");
|
|
512
|
+
img.setAttribute("alt", "Description");
|
|
509
513
|
}
|
|
510
514
|
```
|
|
511
515
|
|
|
512
516
|
### Adding Classes
|
|
513
517
|
|
|
514
518
|
```typescript
|
|
515
|
-
const element = document.getElementById(
|
|
519
|
+
const element = document.getElementById("content");
|
|
516
520
|
if (element) {
|
|
517
|
-
element.classList.add(
|
|
518
|
-
element.classList.remove(
|
|
521
|
+
element.classList.add("active");
|
|
522
|
+
element.classList.remove("hidden");
|
|
519
523
|
}
|
|
520
524
|
```
|
|
521
525
|
|
|
522
526
|
### Inserting Content
|
|
523
527
|
|
|
524
528
|
```typescript
|
|
525
|
-
const parent = document.querySelector(
|
|
526
|
-
const child = document.createElement(
|
|
529
|
+
const parent = document.querySelector(".parent");
|
|
530
|
+
const child = document.createElement("div");
|
|
527
531
|
|
|
528
532
|
// Append to end
|
|
529
533
|
parent?.appendChild(child);
|
|
@@ -532,19 +536,19 @@ parent?.appendChild(child);
|
|
|
532
536
|
parent?.insertBefore(child, parent.firstChild);
|
|
533
537
|
|
|
534
538
|
// Insert before specific element
|
|
535
|
-
const ref = document.querySelector(
|
|
539
|
+
const ref = document.querySelector(".reference");
|
|
536
540
|
parent?.insertBefore(child, ref);
|
|
537
541
|
```
|
|
538
542
|
|
|
539
543
|
### Removing Elements
|
|
540
544
|
|
|
541
545
|
```typescript
|
|
542
|
-
const element = document.querySelector(
|
|
546
|
+
const element = document.querySelector(".remove-me");
|
|
543
547
|
element?.remove();
|
|
544
548
|
|
|
545
549
|
// Or via parent
|
|
546
|
-
const parent = document.querySelector(
|
|
547
|
-
const child = document.querySelector(
|
|
550
|
+
const parent = document.querySelector(".parent");
|
|
551
|
+
const child = document.querySelector(".child");
|
|
548
552
|
if (parent && child) {
|
|
549
553
|
parent.removeChild(child);
|
|
550
554
|
}
|
|
@@ -558,10 +562,10 @@ Pre-rendering runs in Node/Bun, not the browser:
|
|
|
558
562
|
|
|
559
563
|
```typescript
|
|
560
564
|
// ❌ These won't work
|
|
561
|
-
window.location
|
|
562
|
-
localStorage
|
|
563
|
-
sessionStorage
|
|
564
|
-
navigator
|
|
565
|
+
window.location;
|
|
566
|
+
localStorage;
|
|
567
|
+
sessionStorage;
|
|
568
|
+
navigator;
|
|
565
569
|
```
|
|
566
570
|
|
|
567
571
|
### ❌ No User Input
|
|
@@ -570,7 +574,7 @@ Can't access runtime user data:
|
|
|
570
574
|
|
|
571
575
|
```typescript
|
|
572
576
|
// ❌ No access to form data or user state
|
|
573
|
-
const username = document.getElementById(
|
|
577
|
+
const username = document.getElementById("username-input")?.value;
|
|
574
578
|
```
|
|
575
579
|
|
|
576
580
|
### ❌ No Event Listeners
|
|
@@ -579,8 +583,8 @@ Event listeners are not preserved:
|
|
|
579
583
|
|
|
580
584
|
```typescript
|
|
581
585
|
// ❌ This listener won't exist in the browser
|
|
582
|
-
document.querySelector(
|
|
583
|
-
console.log(
|
|
586
|
+
document.querySelector("button")?.addEventListener("click", () => {
|
|
587
|
+
console.log("clicked");
|
|
584
588
|
});
|
|
585
589
|
```
|
|
586
590
|
|
|
@@ -591,6 +595,7 @@ document.querySelector('button')?.addEventListener('click', () => {
|
|
|
591
595
|
### ✅ Use for Build-Time Data
|
|
592
596
|
|
|
593
597
|
Perfect for:
|
|
598
|
+
|
|
594
599
|
- Fetching content from CMS/APIs
|
|
595
600
|
- Generating static content
|
|
596
601
|
- Injecting build metadata
|
|
@@ -603,13 +608,13 @@ Perfect for:
|
|
|
603
608
|
// index.pre.ts
|
|
604
609
|
async function fetchData() {
|
|
605
610
|
try {
|
|
606
|
-
const response = await fetch(
|
|
611
|
+
const response = await fetch("https://api.example.com/data");
|
|
607
612
|
if (!response.ok) {
|
|
608
613
|
throw new Error(`HTTP ${response.status}`);
|
|
609
614
|
}
|
|
610
615
|
return response.json();
|
|
611
616
|
} catch (error) {
|
|
612
|
-
console.error(
|
|
617
|
+
console.error("Failed to fetch data:", error);
|
|
613
618
|
return null;
|
|
614
619
|
}
|
|
615
620
|
}
|
|
@@ -620,9 +625,9 @@ if (data) {
|
|
|
620
625
|
// Use data
|
|
621
626
|
} else {
|
|
622
627
|
// Show fallback content
|
|
623
|
-
const main = document.querySelector(
|
|
628
|
+
const main = document.querySelector("main");
|
|
624
629
|
if (main) {
|
|
625
|
-
main.innerHTML =
|
|
630
|
+
main.innerHTML = "<p>Content temporarily unavailable</p>";
|
|
626
631
|
}
|
|
627
632
|
}
|
|
628
633
|
```
|
|
@@ -631,10 +636,10 @@ if (data) {
|
|
|
631
636
|
|
|
632
637
|
```typescript
|
|
633
638
|
// index.pre.ts
|
|
634
|
-
import { renderBlogPosts } from
|
|
639
|
+
import { renderBlogPosts } from "./blog-renderer";
|
|
635
640
|
|
|
636
641
|
const posts = await fetchPosts();
|
|
637
|
-
const container = document.getElementById(
|
|
642
|
+
const container = document.getElementById("blog");
|
|
638
643
|
|
|
639
644
|
if (container) {
|
|
640
645
|
renderBlogPosts(container, posts);
|
|
@@ -653,9 +658,9 @@ interface Product {
|
|
|
653
658
|
|
|
654
659
|
function isValidProduct(obj: any): obj is Product {
|
|
655
660
|
return (
|
|
656
|
-
typeof obj.id ===
|
|
657
|
-
typeof obj.name ===
|
|
658
|
-
typeof obj.price ===
|
|
661
|
+
typeof obj.id === "number" &&
|
|
662
|
+
typeof obj.name === "string" &&
|
|
663
|
+
typeof obj.price === "number" &&
|
|
659
664
|
obj.price >= 0
|
|
660
665
|
);
|
|
661
666
|
}
|
|
@@ -667,6 +672,7 @@ const products = rawData.filter(isValidProduct);
|
|
|
667
672
|
### ❌ Don't Overuse
|
|
668
673
|
|
|
669
674
|
If you just need to modify specific elements, consider:
|
|
675
|
+
|
|
670
676
|
- [HTML components](./components-html.md) for static reusable markup
|
|
671
677
|
- [TypeScript components](./components-typescript.md) for dynamic elements
|
|
672
678
|
- Regular TypeScript for browser interactivity
|
|
@@ -679,13 +685,13 @@ Pre-rendering is for **document-level** transformations.
|
|
|
679
685
|
|
|
680
686
|
```typescript
|
|
681
687
|
// index.pre.ts
|
|
682
|
-
console.log(
|
|
688
|
+
console.log("Pre-rendering index.html");
|
|
683
689
|
|
|
684
|
-
const title = document.querySelector(
|
|
685
|
-
console.log(
|
|
690
|
+
const title = document.querySelector("title");
|
|
691
|
+
console.log("Current title:", title?.textContent);
|
|
686
692
|
|
|
687
|
-
title.textContent =
|
|
688
|
-
console.log(
|
|
693
|
+
title.textContent = "New Title";
|
|
694
|
+
console.log("Updated title:", title.textContent);
|
|
689
695
|
```
|
|
690
696
|
|
|
691
697
|
These logs appear in your terminal during build.
|
|
@@ -715,7 +721,7 @@ View the final HTML to confirm your transformations worked.
|
|
|
715
721
|
You have access to the `@tkeron/html-parser` package:
|
|
716
722
|
|
|
717
723
|
```typescript
|
|
718
|
-
import { parseHTML } from
|
|
724
|
+
import { parseHTML } from "@tkeron/html-parser";
|
|
719
725
|
|
|
720
726
|
// This is what Tkeron uses internally
|
|
721
727
|
// You usually don't need it in .pre.ts since 'document' is already parsed
|
|
@@ -731,9 +737,9 @@ import { parseHTML } from '@tkeron/html-parser';
|
|
|
731
737
|
|
|
732
738
|
```typescript
|
|
733
739
|
// index.pre.ts
|
|
734
|
-
console.log(
|
|
735
|
-
console.log(
|
|
736
|
-
console.log(
|
|
740
|
+
console.log("Tkeron version:", process.env.TKERON_VERSION);
|
|
741
|
+
console.log("Node env:", process.env.NODE_ENV);
|
|
742
|
+
console.log("Custom var:", process.env.MY_CUSTOM_VAR);
|
|
737
743
|
```
|
|
738
744
|
|
|
739
745
|
Run with custom env:
|
|
@@ -751,8 +757,8 @@ Generate pages from data:
|
|
|
751
757
|
```typescript
|
|
752
758
|
// pages.pre.ts
|
|
753
759
|
const pages = [
|
|
754
|
-
{ slug:
|
|
755
|
-
{ slug:
|
|
760
|
+
{ slug: "about", title: "About Us", content: "..." },
|
|
761
|
+
{ slug: "contact", title: "Contact", content: "..." },
|
|
756
762
|
];
|
|
757
763
|
|
|
758
764
|
// This example shows the concept - in practice, you'd generate
|
|
@@ -763,27 +769,27 @@ const pages = [
|
|
|
763
769
|
|
|
764
770
|
```typescript
|
|
765
771
|
// docs.pre.ts
|
|
766
|
-
import { readFileSync, readdirSync } from
|
|
767
|
-
import { join } from
|
|
768
|
-
import { marked } from
|
|
772
|
+
import { readFileSync, readdirSync } from "fs";
|
|
773
|
+
import { join } from "path";
|
|
774
|
+
import { marked } from "marked";
|
|
769
775
|
|
|
770
|
-
const docsDir = join(import.meta.dir,
|
|
771
|
-
const files = readdirSync(docsDir).filter(f => f.endsWith(
|
|
776
|
+
const docsDir = join(import.meta.dir, "docs");
|
|
777
|
+
const files = readdirSync(docsDir).filter((f) => f.endsWith(".md"));
|
|
772
778
|
|
|
773
|
-
const nav = document.getElementById(
|
|
774
|
-
const content = document.getElementById(
|
|
779
|
+
const nav = document.getElementById("doc-nav");
|
|
780
|
+
const content = document.getElementById("doc-content");
|
|
775
781
|
|
|
776
782
|
if (nav) {
|
|
777
|
-
files.forEach(file => {
|
|
778
|
-
const li = document.createElement(
|
|
779
|
-
li.innerHTML = `<a href="#${file}">${file.replace(
|
|
783
|
+
files.forEach((file) => {
|
|
784
|
+
const li = document.createElement("li");
|
|
785
|
+
li.innerHTML = `<a href="#${file}">${file.replace(".md", "")}</a>`;
|
|
780
786
|
nav.appendChild(li);
|
|
781
787
|
});
|
|
782
788
|
}
|
|
783
789
|
|
|
784
790
|
if (content) {
|
|
785
791
|
const firstFile = files[0];
|
|
786
|
-
const markdown = readFileSync(join(docsDir, firstFile),
|
|
792
|
+
const markdown = readFileSync(join(docsDir, firstFile), "utf-8");
|
|
787
793
|
content.innerHTML = marked(markdown);
|
|
788
794
|
}
|
|
789
795
|
```
|