@jamx-framework/renderer 0.1.0 → 1.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 +560 -0
- package/package.json +37 -12
- package/.turbo/turbo-build.log +0 -4
- package/src/error/boundary.ts +0 -80
- package/src/error/error-page.ts +0 -137
- package/src/error/types.ts +0 -6
- package/src/html/escape.ts +0 -90
- package/src/html/serializer.ts +0 -161
- package/src/index.ts +0 -33
- package/src/jsx/jsx-runtime.ts +0 -247
- package/src/pipeline/context.ts +0 -45
- package/src/pipeline/renderer.ts +0 -138
- package/test/unit/error/boundary.test.ts +0 -78
- package/test/unit/error/error-page.test.ts +0 -63
- package/test/unit/html/escape.test.ts +0 -34
- package/test/unit/html/renderer.test.ts +0 -93
- package/test/unit/html/serializer.test.ts +0 -141
- package/tsconfig.json +0 -15
- package/vitest.config.ts +0 -4
package/src/error/error-page.ts
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import type { RenderError } from "./types.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Renderiza una página de error HTML.
|
|
5
|
-
* En producción no muestra el stack trace.
|
|
6
|
-
*/
|
|
7
|
-
export function renderErrorPage(error: RenderError, isDev: boolean): string {
|
|
8
|
-
const title = errorTitle(error.statusCode);
|
|
9
|
-
const stack =
|
|
10
|
-
isDev && error.stack
|
|
11
|
-
? `<pre class="stack">${escapeHtml(error.stack)}</pre>`
|
|
12
|
-
: "";
|
|
13
|
-
|
|
14
|
-
return `<!DOCTYPE html>
|
|
15
|
-
<html lang="en">
|
|
16
|
-
<head>
|
|
17
|
-
<meta charset="UTF-8">
|
|
18
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
19
|
-
<title>${error.statusCode} — ${title}</title>
|
|
20
|
-
<style>
|
|
21
|
-
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0 }
|
|
22
|
-
|
|
23
|
-
body {
|
|
24
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
25
|
-
background: #0f0f0f;
|
|
26
|
-
color: #e5e5e5;
|
|
27
|
-
min-height: 100vh;
|
|
28
|
-
display: flex;
|
|
29
|
-
align-items: center;
|
|
30
|
-
justify-content: center;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
.container {
|
|
34
|
-
max-width: 640px;
|
|
35
|
-
width: 100%;
|
|
36
|
-
padding: 2rem;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
.code {
|
|
40
|
-
font-size: 6rem;
|
|
41
|
-
font-weight: 800;
|
|
42
|
-
color: #3b82f6;
|
|
43
|
-
line-height: 1;
|
|
44
|
-
letter-spacing: -4px;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
.title {
|
|
48
|
-
font-size: 1.5rem;
|
|
49
|
-
font-weight: 600;
|
|
50
|
-
margin-top: 0.5rem;
|
|
51
|
-
color: #f5f5f5;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
.message {
|
|
55
|
-
margin-top: 1rem;
|
|
56
|
-
color: #a3a3a3;
|
|
57
|
-
font-size: 0.95rem;
|
|
58
|
-
line-height: 1.6;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
.path {
|
|
62
|
-
margin-top: 0.5rem;
|
|
63
|
-
font-size: 0.8rem;
|
|
64
|
-
color: #525252;
|
|
65
|
-
font-family: monospace;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
.stack {
|
|
69
|
-
margin-top: 1.5rem;
|
|
70
|
-
padding: 1rem;
|
|
71
|
-
background: #171717;
|
|
72
|
-
border: 1px solid #262626;
|
|
73
|
-
border-radius: 8px;
|
|
74
|
-
font-size: 0.75rem;
|
|
75
|
-
color: #f87171;
|
|
76
|
-
overflow-x: auto;
|
|
77
|
-
white-space: pre-wrap;
|
|
78
|
-
word-break: break-word;
|
|
79
|
-
line-height: 1.6;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
.divider {
|
|
83
|
-
margin-top: 2rem;
|
|
84
|
-
border: none;
|
|
85
|
-
border-top: 1px solid #262626;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
.footer {
|
|
89
|
-
margin-top: 1rem;
|
|
90
|
-
font-size: 0.75rem;
|
|
91
|
-
color: #404040;
|
|
92
|
-
}
|
|
93
|
-
</style>
|
|
94
|
-
</head>
|
|
95
|
-
<body>
|
|
96
|
-
<div class="container">
|
|
97
|
-
<div class="code">${error.statusCode}</div>
|
|
98
|
-
<div class="title">${escapeHtml(title)}</div>
|
|
99
|
-
<div class="message">${escapeHtml(error.message)}</div>
|
|
100
|
-
${error.path ? `<div class="path">${escapeHtml(error.path)}</div>` : ""}
|
|
101
|
-
${stack}
|
|
102
|
-
<hr class="divider">
|
|
103
|
-
<div class="footer">JAMX Framework</div>
|
|
104
|
-
</div>
|
|
105
|
-
</body>
|
|
106
|
-
</html>`;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function errorTitle(statusCode: number): string {
|
|
110
|
-
switch (statusCode) {
|
|
111
|
-
case 400:
|
|
112
|
-
return "Bad Request";
|
|
113
|
-
case 401:
|
|
114
|
-
return "Unauthorized";
|
|
115
|
-
case 403:
|
|
116
|
-
return "Forbidden";
|
|
117
|
-
case 404:
|
|
118
|
-
return "Not Found";
|
|
119
|
-
case 422:
|
|
120
|
-
return "Unprocessable Entity";
|
|
121
|
-
case 500:
|
|
122
|
-
return "Internal Server Error";
|
|
123
|
-
case 503:
|
|
124
|
-
return "Service Unavailable";
|
|
125
|
-
default:
|
|
126
|
-
return statusCode >= 500 ? "Server Error" : "Client Error";
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function escapeHtml(str: string): string {
|
|
131
|
-
return str
|
|
132
|
-
.replace(/&/g, "&")
|
|
133
|
-
.replace(/</g, "<")
|
|
134
|
-
.replace(/>/g, ">")
|
|
135
|
-
.replace(/"/g, """)
|
|
136
|
-
.replace(/'/g, "'");
|
|
137
|
-
}
|
package/src/error/types.ts
DELETED
package/src/html/escape.ts
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
// packages/renderer/src/html/escape.ts
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Escapa caracteres especiales HTML para prevenir XSS.
|
|
5
|
-
* Siempre usar esto al renderizar contenido de usuario.
|
|
6
|
-
*/
|
|
7
|
-
export function escapeHtml(value: unknown): string {
|
|
8
|
-
const str = String(value);
|
|
9
|
-
let result = "";
|
|
10
|
-
|
|
11
|
-
for (let i = 0; i < str.length; i++) {
|
|
12
|
-
const char = str[i];
|
|
13
|
-
switch (char) {
|
|
14
|
-
case "&":
|
|
15
|
-
result += "&";
|
|
16
|
-
break;
|
|
17
|
-
case "<":
|
|
18
|
-
result += "<";
|
|
19
|
-
break;
|
|
20
|
-
case ">":
|
|
21
|
-
result += ">";
|
|
22
|
-
break;
|
|
23
|
-
case '"':
|
|
24
|
-
result += """;
|
|
25
|
-
break;
|
|
26
|
-
case "'":
|
|
27
|
-
result += "'";
|
|
28
|
-
break;
|
|
29
|
-
default:
|
|
30
|
-
result += char;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return result;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Tags HTML que no tienen closing tag.
|
|
39
|
-
*/
|
|
40
|
-
export const VOID_ELEMENTS = new Set([
|
|
41
|
-
"area",
|
|
42
|
-
"base",
|
|
43
|
-
"br",
|
|
44
|
-
"col",
|
|
45
|
-
"embed",
|
|
46
|
-
"hr",
|
|
47
|
-
"img",
|
|
48
|
-
"input",
|
|
49
|
-
"link",
|
|
50
|
-
"meta",
|
|
51
|
-
"param",
|
|
52
|
-
"source",
|
|
53
|
-
"track",
|
|
54
|
-
"wbr",
|
|
55
|
-
]);
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Tags cuyo contenido se renderiza tal cual (sin escapar).
|
|
59
|
-
*/
|
|
60
|
-
export const RAW_TEXT_ELEMENTS = new Set(["script", "style"]);
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Atributos booleanos — se renderizan sin valor si son true.
|
|
64
|
-
* Ej: <input disabled /> en lugar de <input disabled="true" />
|
|
65
|
-
*/
|
|
66
|
-
export const BOOLEAN_ATTRIBUTES = new Set([
|
|
67
|
-
"allowfullscreen",
|
|
68
|
-
"async",
|
|
69
|
-
"autofocus",
|
|
70
|
-
"autoplay",
|
|
71
|
-
"checked",
|
|
72
|
-
"controls",
|
|
73
|
-
"default",
|
|
74
|
-
"defer",
|
|
75
|
-
"disabled",
|
|
76
|
-
"formnovalidate",
|
|
77
|
-
"hidden",
|
|
78
|
-
"ismap",
|
|
79
|
-
"loop",
|
|
80
|
-
"multiple",
|
|
81
|
-
"muted",
|
|
82
|
-
"nomodule",
|
|
83
|
-
"novalidate",
|
|
84
|
-
"open",
|
|
85
|
-
"readonly",
|
|
86
|
-
"required",
|
|
87
|
-
"reversed",
|
|
88
|
-
"selected",
|
|
89
|
-
"typemustmatch",
|
|
90
|
-
]);
|
package/src/html/serializer.ts
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
escapeHtml,
|
|
3
|
-
VOID_ELEMENTS,
|
|
4
|
-
RAW_TEXT_ELEMENTS,
|
|
5
|
-
BOOLEAN_ATTRIBUTES,
|
|
6
|
-
} from "./escape.js";
|
|
7
|
-
import { Fragment } from "../jsx/jsx-runtime.js";
|
|
8
|
-
import type {
|
|
9
|
-
JamxNode,
|
|
10
|
-
JamxElement,
|
|
11
|
-
ComponentFn,
|
|
12
|
-
Props,
|
|
13
|
-
} from "../jsx/jsx-runtime.js";
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Serializa un árbol JSX a una string HTML.
|
|
17
|
-
*
|
|
18
|
-
* Proceso:
|
|
19
|
-
* 1. Si el nodo es un string/number → escapar y retornar
|
|
20
|
-
* 2. Si el nodo es un componente (función) → llamarlo y serializar el resultado
|
|
21
|
-
* 3. Si el nodo es un elemento HTML → serializar tag + props + children
|
|
22
|
-
* 4. Si el nodo es un Fragment → serializar solo los children
|
|
23
|
-
*/
|
|
24
|
-
export class HtmlSerializer {
|
|
25
|
-
serialize(node: JamxNode): string {
|
|
26
|
-
// Nodos primitivos
|
|
27
|
-
if (node === null || node === undefined || node === false) return "";
|
|
28
|
-
if (node === true) return "";
|
|
29
|
-
if (typeof node === "number") return String(node);
|
|
30
|
-
if (typeof node === "string") return escapeHtml(node);
|
|
31
|
-
|
|
32
|
-
// Arrays
|
|
33
|
-
if (Array.isArray(node)) {
|
|
34
|
-
return node.map((child) => this.serialize(child)).join("");
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Elemento JSX
|
|
38
|
-
return this.serializeElement(node);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
private serializeElement(element: JamxElement): string {
|
|
42
|
-
const { type, props } = element;
|
|
43
|
-
|
|
44
|
-
// Componente funcional — ejecutar y serializar el resultado
|
|
45
|
-
if (typeof type === "function") {
|
|
46
|
-
return this.serializeComponent(type, props);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Fragment — solo los hijos
|
|
50
|
-
if (type === Fragment) {
|
|
51
|
-
return this.serializeChildren(props.children);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Elemento HTML
|
|
55
|
-
return this.serializeHtmlElement(type, props);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
private serializeComponent(fn: ComponentFn, props: Props): string {
|
|
59
|
-
try {
|
|
60
|
-
const result = fn(props);
|
|
61
|
-
return this.serialize(result);
|
|
62
|
-
} catch (err) {
|
|
63
|
-
throw new Error(
|
|
64
|
-
`Error rendering component '${fn.name || "anonymous"}': ${String(err)}`,
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
private serializeHtmlElement(tag: string, props: Props): string {
|
|
70
|
-
const { children, ...attrs } = props;
|
|
71
|
-
|
|
72
|
-
const attrString = this.serializeAttributes(attrs);
|
|
73
|
-
const openTag = attrString ? `<${tag} ${attrString}>` : `<${tag}>`;
|
|
74
|
-
|
|
75
|
-
// Elementos void — no tienen closing tag ni children
|
|
76
|
-
if (VOID_ELEMENTS.has(tag)) {
|
|
77
|
-
return openTag;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Elementos con contenido raw (script, style)
|
|
81
|
-
if (RAW_TEXT_ELEMENTS.has(tag)) {
|
|
82
|
-
const rawContent = typeof children === "string" ? children : "";
|
|
83
|
-
return `${openTag}${rawContent}</${tag}>`;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const childrenHtml = this.serializeChildren(children);
|
|
87
|
-
return `${openTag}${childrenHtml}</${tag}>`;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
private serializeAttributes(attrs: Record<string, unknown>): string {
|
|
91
|
-
const parts: string[] = [];
|
|
92
|
-
|
|
93
|
-
for (const [key, value] of Object.entries(attrs)) {
|
|
94
|
-
// Ignorar atributos internos y undefined/null
|
|
95
|
-
if (key === "key" || value === undefined || value === null) continue;
|
|
96
|
-
|
|
97
|
-
// className → class
|
|
98
|
-
const attrName =
|
|
99
|
-
key === "className"
|
|
100
|
-
? "class"
|
|
101
|
-
: key === "htmlFor"
|
|
102
|
-
? "for"
|
|
103
|
-
: key === "tabIndex"
|
|
104
|
-
? "tabindex"
|
|
105
|
-
: camelToKebab(key);
|
|
106
|
-
|
|
107
|
-
// Atributos booleanos
|
|
108
|
-
if (BOOLEAN_ATTRIBUTES.has(attrName)) {
|
|
109
|
-
if (value === true) parts.push(attrName);
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// style como objeto
|
|
114
|
-
if (key === "style" && typeof value === "object" && value !== null) {
|
|
115
|
-
const styleStr = this.serializeStyle(value as Record<string, string>);
|
|
116
|
-
if (styleStr) parts.push(`style="${escapeHtml(styleStr)}"`);
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// false → omitir el atributo
|
|
121
|
-
if (value === false) continue;
|
|
122
|
-
|
|
123
|
-
// true → atributo sin valor (no booleano, ej: data-active)
|
|
124
|
-
if (value === true) {
|
|
125
|
-
parts.push(attrName);
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
parts.push(`${attrName}="${escapeHtml(value)}"`);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return parts.join(" ");
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
private serializeStyle(style: Record<string, string>): string {
|
|
136
|
-
return Object.entries(style)
|
|
137
|
-
.map(([prop, value]) => `${camelToKebab(prop)}: ${value}`)
|
|
138
|
-
.join("; ");
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
private serializeChildren(children: unknown): string {
|
|
142
|
-
if (children === undefined || children === null) return "";
|
|
143
|
-
if (Array.isArray(children)) {
|
|
144
|
-
return children
|
|
145
|
-
.map((child) => this.serialize(child as JamxNode))
|
|
146
|
-
.join("");
|
|
147
|
-
}
|
|
148
|
-
return this.serialize(children as JamxNode);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// ── HELPERS ───────────────────────────────────────────────────────────────
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Convierte camelCase a kebab-case para atributos HTML.
|
|
156
|
-
* backgroundColor → background-color
|
|
157
|
-
* dataTestId → data-test-id (los data- ya vienen bien)
|
|
158
|
-
*/
|
|
159
|
-
function camelToKebab(str: string): string {
|
|
160
|
-
return str.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);
|
|
161
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
export { SSRRenderer } from "./pipeline/renderer.js";
|
|
2
|
-
export { HtmlSerializer } from "./html/serializer.js";
|
|
3
|
-
export {
|
|
4
|
-
escapeHtml,
|
|
5
|
-
VOID_ELEMENTS,
|
|
6
|
-
BOOLEAN_ATTRIBUTES,
|
|
7
|
-
} from "./html/escape.js";
|
|
8
|
-
export { jsx, jsxs, jsxDEV, Fragment } from "./jsx/jsx-runtime.js";
|
|
9
|
-
|
|
10
|
-
export type {
|
|
11
|
-
JamxNode,
|
|
12
|
-
JamxElement,
|
|
13
|
-
ComponentFn,
|
|
14
|
-
Props,
|
|
15
|
-
JSX,
|
|
16
|
-
HtmlAttributes,
|
|
17
|
-
} from "./jsx/jsx-runtime.js";
|
|
18
|
-
|
|
19
|
-
export type {
|
|
20
|
-
RenderContext,
|
|
21
|
-
RenderResult,
|
|
22
|
-
PageHead,
|
|
23
|
-
} from "./pipeline/context.js";
|
|
24
|
-
|
|
25
|
-
export type {
|
|
26
|
-
PageComponentLike,
|
|
27
|
-
LayoutComponentLike,
|
|
28
|
-
} from "./pipeline/renderer.js";
|
|
29
|
-
|
|
30
|
-
export { ErrorBoundary } from "./error/boundary.js";
|
|
31
|
-
export { renderErrorPage } from "./error/error-page.js";
|
|
32
|
-
export type { RenderError } from "./error/types.js";
|
|
33
|
-
export type { BoundaryOptions, BoundaryResult } from "./error/boundary.js";
|
package/src/jsx/jsx-runtime.ts
DELETED
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
// ── TIPOS DEL ÁRBOL JSX ───────────────────────────────────────────────────
|
|
2
|
-
|
|
3
|
-
export type JamxNode =
|
|
4
|
-
| string
|
|
5
|
-
| number
|
|
6
|
-
| boolean
|
|
7
|
-
| null
|
|
8
|
-
| undefined
|
|
9
|
-
| JamxElement
|
|
10
|
-
| JamxNode[];
|
|
11
|
-
|
|
12
|
-
export interface JamxElement {
|
|
13
|
-
type: string | ComponentFn;
|
|
14
|
-
props: Props;
|
|
15
|
-
key: string | null;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export type Props = Record<string, unknown> & {
|
|
19
|
-
children?: JamxNode | JamxNode[];
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export type ComponentFn = (props: Props) => JamxNode;
|
|
23
|
-
|
|
24
|
-
// ── JSX RUNTIME ───────────────────────────────────────────────────────────
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Función principal del JSX runtime.
|
|
28
|
-
* TypeScript llama esto cuando encuentra <Tag prop="value" />
|
|
29
|
-
*
|
|
30
|
-
* En lugar de React.createElement, JAMX usa esta función
|
|
31
|
-
* para construir el árbol de nodos que luego serializa a HTML.
|
|
32
|
-
*/
|
|
33
|
-
export function jsx(
|
|
34
|
-
type: string | ComponentFn,
|
|
35
|
-
props: Props,
|
|
36
|
-
key?: string,
|
|
37
|
-
): JamxElement {
|
|
38
|
-
return {
|
|
39
|
-
type,
|
|
40
|
-
props: props ?? {},
|
|
41
|
-
key: key ?? null,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Versión para elementos con múltiples hijos.
|
|
47
|
-
* TypeScript la usa cuando hay más de un hijo.
|
|
48
|
-
*/
|
|
49
|
-
export const jsxs = jsx;
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Versión dev con información adicional para debugging.
|
|
53
|
-
* En producción es igual a jsx.
|
|
54
|
-
*/
|
|
55
|
-
export const jsxDEV = jsx;
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Fragment — permite retornar múltiples elementos sin wrapper.
|
|
59
|
-
* Uso: <> <div/> <div/> </>
|
|
60
|
-
*/
|
|
61
|
-
export const Fragment = "jamx.fragment" as const;
|
|
62
|
-
|
|
63
|
-
// ── TIPOS GLOBALES JSX ────────────────────────────────────────────────────
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Declaraciones de tipos para que TypeScript entienda el JSX de JAMX.
|
|
67
|
-
* Estas interfaces definen qué props acepta cada elemento HTML.
|
|
68
|
-
*/
|
|
69
|
-
export namespace JSX {
|
|
70
|
-
export type Element = JamxElement;
|
|
71
|
-
|
|
72
|
-
export interface ElementChildrenAttribute {
|
|
73
|
-
children: JamxNode | JamxNode[];
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export interface IntrinsicElements {
|
|
77
|
-
// Document
|
|
78
|
-
html: HtmlAttributes & { lang?: string };
|
|
79
|
-
head: HtmlAttributes;
|
|
80
|
-
body: HtmlAttributes;
|
|
81
|
-
title: HtmlAttributes;
|
|
82
|
-
meta: MetaAttributes;
|
|
83
|
-
link: LinkAttributes;
|
|
84
|
-
script: ScriptAttributes;
|
|
85
|
-
style: HtmlAttributes;
|
|
86
|
-
// Sections
|
|
87
|
-
main: HtmlAttributes;
|
|
88
|
-
header: HtmlAttributes;
|
|
89
|
-
footer: HtmlAttributes;
|
|
90
|
-
nav: HtmlAttributes;
|
|
91
|
-
section: HtmlAttributes;
|
|
92
|
-
article: HtmlAttributes;
|
|
93
|
-
aside: HtmlAttributes;
|
|
94
|
-
// Headings
|
|
95
|
-
h1: HtmlAttributes;
|
|
96
|
-
h2: HtmlAttributes;
|
|
97
|
-
h3: HtmlAttributes;
|
|
98
|
-
h4: HtmlAttributes;
|
|
99
|
-
h5: HtmlAttributes;
|
|
100
|
-
h6: HtmlAttributes;
|
|
101
|
-
// Text
|
|
102
|
-
p: HtmlAttributes;
|
|
103
|
-
span: HtmlAttributes;
|
|
104
|
-
strong: HtmlAttributes;
|
|
105
|
-
em: HtmlAttributes;
|
|
106
|
-
small: HtmlAttributes;
|
|
107
|
-
code: HtmlAttributes;
|
|
108
|
-
pre: HtmlAttributes;
|
|
109
|
-
// Lists
|
|
110
|
-
ul: HtmlAttributes;
|
|
111
|
-
ol: HtmlAttributes;
|
|
112
|
-
li: HtmlAttributes;
|
|
113
|
-
// Interactive
|
|
114
|
-
a: AnchorAttributes;
|
|
115
|
-
button: ButtonAttributes;
|
|
116
|
-
input: InputAttributes;
|
|
117
|
-
textarea: TextareaAttributes;
|
|
118
|
-
select: HtmlAttributes;
|
|
119
|
-
option: HtmlAttributes & { value?: string; selected?: boolean };
|
|
120
|
-
form: FormAttributes;
|
|
121
|
-
label: HtmlAttributes & { for?: string; htmlFor?: string };
|
|
122
|
-
// Media
|
|
123
|
-
img: ImgAttributes;
|
|
124
|
-
video: HtmlAttributes & {
|
|
125
|
-
src?: string;
|
|
126
|
-
controls?: boolean;
|
|
127
|
-
autoplay?: boolean;
|
|
128
|
-
};
|
|
129
|
-
audio: HtmlAttributes & { src?: string; controls?: boolean };
|
|
130
|
-
canvas: HtmlAttributes & { width?: number; height?: number };
|
|
131
|
-
// Layout
|
|
132
|
-
div: HtmlAttributes;
|
|
133
|
-
table: HtmlAttributes;
|
|
134
|
-
thead: HtmlAttributes;
|
|
135
|
-
tbody: HtmlAttributes;
|
|
136
|
-
tr: HtmlAttributes;
|
|
137
|
-
th: HtmlAttributes & { colSpan?: number; rowSpan?: number };
|
|
138
|
-
td: HtmlAttributes & { colSpan?: number; rowSpan?: number };
|
|
139
|
-
// Misc
|
|
140
|
-
br: HtmlAttributes;
|
|
141
|
-
hr: HtmlAttributes;
|
|
142
|
-
slot: HtmlAttributes & { name?: string };
|
|
143
|
-
// Generic fallback
|
|
144
|
-
[tag: string]: HtmlAttributes;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// ── TIPOS DE ATRIBUTOS ────────────────────────────────────────────────────
|
|
149
|
-
|
|
150
|
-
export interface HtmlAttributes {
|
|
151
|
-
id?: string;
|
|
152
|
-
class?: string;
|
|
153
|
-
className?: string;
|
|
154
|
-
style?: string | Record<string, string>;
|
|
155
|
-
children?: JamxNode | JamxNode[];
|
|
156
|
-
// Data attributes
|
|
157
|
-
[key: `data-${string}`]: string | undefined;
|
|
158
|
-
// ARIA
|
|
159
|
-
role?: string;
|
|
160
|
-
"aria-label"?: string;
|
|
161
|
-
"aria-hidden"?: boolean;
|
|
162
|
-
"aria-expanded"?: boolean;
|
|
163
|
-
"aria-disabled"?: boolean;
|
|
164
|
-
"aria-selected"?: boolean;
|
|
165
|
-
"aria-controls"?: string;
|
|
166
|
-
"aria-describedby"?: string;
|
|
167
|
-
tabIndex?: number;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
export interface AnchorAttributes extends HtmlAttributes {
|
|
171
|
-
href?: string;
|
|
172
|
-
target?: "_blank" | "_self" | "_parent" | "_top";
|
|
173
|
-
rel?: string;
|
|
174
|
-
download?: boolean | string;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
export interface ButtonAttributes extends HtmlAttributes {
|
|
178
|
-
type?: "button" | "submit" | "reset";
|
|
179
|
-
disabled?: boolean;
|
|
180
|
-
name?: string;
|
|
181
|
-
value?: string;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
export interface InputAttributes extends HtmlAttributes {
|
|
185
|
-
type?: string;
|
|
186
|
-
name?: string;
|
|
187
|
-
value?: string | number;
|
|
188
|
-
placeholder?: string;
|
|
189
|
-
required?: boolean;
|
|
190
|
-
disabled?: boolean;
|
|
191
|
-
readonly?: boolean;
|
|
192
|
-
checked?: boolean;
|
|
193
|
-
min?: string | number;
|
|
194
|
-
max?: string | number;
|
|
195
|
-
step?: string | number;
|
|
196
|
-
pattern?: string;
|
|
197
|
-
autocomplete?: string;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
export interface TextareaAttributes extends HtmlAttributes {
|
|
201
|
-
name?: string;
|
|
202
|
-
rows?: number;
|
|
203
|
-
cols?: number;
|
|
204
|
-
placeholder?: string;
|
|
205
|
-
required?: boolean;
|
|
206
|
-
disabled?: boolean;
|
|
207
|
-
readonly?: boolean;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
export interface FormAttributes extends HtmlAttributes {
|
|
211
|
-
action?: string;
|
|
212
|
-
method?: "get" | "post";
|
|
213
|
-
enctype?: string;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
export interface ImgAttributes extends HtmlAttributes {
|
|
217
|
-
src?: string;
|
|
218
|
-
alt?: string;
|
|
219
|
-
width?: number | string;
|
|
220
|
-
height?: number | string;
|
|
221
|
-
loading?: "lazy" | "eager";
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
export interface MetaAttributes extends HtmlAttributes {
|
|
225
|
-
name?: string;
|
|
226
|
-
content?: string;
|
|
227
|
-
charset?: string;
|
|
228
|
-
httpEquiv?: string;
|
|
229
|
-
property?: string;
|
|
230
|
-
"http-equiv"?: string;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
export interface LinkAttributes extends HtmlAttributes {
|
|
234
|
-
rel?: string;
|
|
235
|
-
href?: string;
|
|
236
|
-
type?: string;
|
|
237
|
-
media?: string;
|
|
238
|
-
as?: string;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
export interface ScriptAttributes extends HtmlAttributes {
|
|
242
|
-
src?: string;
|
|
243
|
-
type?: string;
|
|
244
|
-
async?: boolean;
|
|
245
|
-
defer?: boolean;
|
|
246
|
-
module?: boolean;
|
|
247
|
-
}
|