twynejs 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 +3 -0
- package/dist/index.d.ts +180 -0
- package/dist/index.js +1122 -0
- package/dist/jsx-runtime.d.ts +20 -0
- package/dist/jsx-runtime.js +132 -0
- package/dist/styles.d.ts +1 -0
- package/dist/styles.js +17 -0
- package/package.json +67 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
declare const HTML: unique symbol;
|
|
2
|
+
export interface HtmlValue {
|
|
3
|
+
readonly [HTML]: true;
|
|
4
|
+
readonly value: string;
|
|
5
|
+
toString(): string;
|
|
6
|
+
}
|
|
7
|
+
export declare function jsx(tag: string | ((props: any) => any), props: Record<string, any> | null, ...children: any[]): HtmlValue | string;
|
|
8
|
+
export declare function jsxs(tag: string | ((props: any) => any), props: Record<string, any> | null): HtmlValue | string;
|
|
9
|
+
export declare function Fragment(props: {
|
|
10
|
+
children?: any[];
|
|
11
|
+
}): HtmlValue;
|
|
12
|
+
export declare function escapeHtml(value: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Erzeugt einen HtmlValue aus rohem String OHNE Escaping.
|
|
15
|
+
* Nur für vertrauenswürdigen Inhalt verwenden (eigenes CSS/JS, nie User-Input!).
|
|
16
|
+
* Damit lässt sich z.B. ein <script>{raw(js)}</script> einbetten, ohne
|
|
17
|
+
* dass `<`/`>`/`&` im JS/CSS kaputt escaped werden.
|
|
18
|
+
*/
|
|
19
|
+
export declare function raw(value: string): HtmlValue;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// Custom JSX Runtime for the SSG Pages Plugin
|
|
2
|
+
const HTML = Symbol("html");
|
|
3
|
+
function renderChildren(children) {
|
|
4
|
+
return children
|
|
5
|
+
.flat(Infinity)
|
|
6
|
+
.filter((child) => child != null && child !== false)
|
|
7
|
+
.map((child) => {
|
|
8
|
+
if (isHtml(child)) {
|
|
9
|
+
return child.value;
|
|
10
|
+
}
|
|
11
|
+
return escapeText(String(child));
|
|
12
|
+
})
|
|
13
|
+
.join("");
|
|
14
|
+
}
|
|
15
|
+
export function jsx(tag, props, ...children) {
|
|
16
|
+
// Automatic JSX runtime: children kommen als props.children (Array) rein.
|
|
17
|
+
if (Array.isArray(props?.children)) {
|
|
18
|
+
children = props.children;
|
|
19
|
+
props = { ...props };
|
|
20
|
+
delete props.children;
|
|
21
|
+
}
|
|
22
|
+
if (typeof tag === "function") {
|
|
23
|
+
return tag({
|
|
24
|
+
...(props ?? {}),
|
|
25
|
+
children,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
const booleanHtmlAttributes = new Set([
|
|
29
|
+
"disabled",
|
|
30
|
+
"checked",
|
|
31
|
+
"selected",
|
|
32
|
+
"readonly",
|
|
33
|
+
"required",
|
|
34
|
+
"multiple",
|
|
35
|
+
"hidden",
|
|
36
|
+
"autofocus",
|
|
37
|
+
"open",
|
|
38
|
+
]);
|
|
39
|
+
const attributes = Object.entries(props ?? {})
|
|
40
|
+
.filter(([key]) => key !== "children" && key !== "key")
|
|
41
|
+
.map(([key, value]) => {
|
|
42
|
+
if (value == null) {
|
|
43
|
+
return "";
|
|
44
|
+
}
|
|
45
|
+
const attribute = key === "className" ? "class" : key;
|
|
46
|
+
// Echte HTML-Boolean-Attribute: Präsenz = wahr, sonst weglassen.
|
|
47
|
+
if (typeof value === "boolean" && booleanHtmlAttributes.has(attribute)) {
|
|
48
|
+
return value ? ` ${attribute}` : "";
|
|
49
|
+
}
|
|
50
|
+
// Alles andere (inkl. data-*/aria-*) bekommt immer einen expliziten
|
|
51
|
+
// String-Wert, damit z.B. dataset.nested === "false" verlässlich geht.
|
|
52
|
+
if (typeof value === "boolean") {
|
|
53
|
+
return ` ${attribute}="${value}"`;
|
|
54
|
+
}
|
|
55
|
+
if (value === false) {
|
|
56
|
+
return "";
|
|
57
|
+
}
|
|
58
|
+
return ` ${attribute}="${escapeAttribute(String(value))}"`;
|
|
59
|
+
})
|
|
60
|
+
.join("");
|
|
61
|
+
const content = renderChildren(children);
|
|
62
|
+
const voidElements = new Set([
|
|
63
|
+
"area",
|
|
64
|
+
"base",
|
|
65
|
+
"br",
|
|
66
|
+
"col",
|
|
67
|
+
"embed",
|
|
68
|
+
"hr",
|
|
69
|
+
"img",
|
|
70
|
+
"input",
|
|
71
|
+
"link",
|
|
72
|
+
"meta",
|
|
73
|
+
"param",
|
|
74
|
+
"source",
|
|
75
|
+
"track",
|
|
76
|
+
"wbr",
|
|
77
|
+
]);
|
|
78
|
+
const html = voidElements.has(tag)
|
|
79
|
+
? `<${tag}${attributes}>`
|
|
80
|
+
: `<${tag}${attributes}>${content}</${tag}>`;
|
|
81
|
+
return createHtml(html);
|
|
82
|
+
}
|
|
83
|
+
// Automatic JSX runtime nutzt jsxs für statische Elemente (gleiche Semantik).
|
|
84
|
+
export function jsxs(tag, props) {
|
|
85
|
+
return jsx(tag, props);
|
|
86
|
+
}
|
|
87
|
+
export function Fragment(props) {
|
|
88
|
+
const content = renderChildren(props.children ?? []);
|
|
89
|
+
return createHtml(content);
|
|
90
|
+
}
|
|
91
|
+
function createHtml(value) {
|
|
92
|
+
return {
|
|
93
|
+
[HTML]: true,
|
|
94
|
+
value,
|
|
95
|
+
toString() {
|
|
96
|
+
return value;
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function isHtml(value) {
|
|
101
|
+
return typeof value === "object" && value !== null && HTML in value;
|
|
102
|
+
}
|
|
103
|
+
function escapeAttribute(value) {
|
|
104
|
+
return value
|
|
105
|
+
.replace(/&/g, "&")
|
|
106
|
+
.replace(/"/g, """)
|
|
107
|
+
.replace(/</g, "<")
|
|
108
|
+
.replace(/>/g, ">");
|
|
109
|
+
}
|
|
110
|
+
function escapeText(value) {
|
|
111
|
+
return value
|
|
112
|
+
.replace(/&/g, "&")
|
|
113
|
+
.replace(/</g, "<")
|
|
114
|
+
.replace(/>/g, ">");
|
|
115
|
+
}
|
|
116
|
+
export function escapeHtml(value) {
|
|
117
|
+
return value
|
|
118
|
+
.replace(/&/g, "&")
|
|
119
|
+
.replace(/</g, "<")
|
|
120
|
+
.replace(/>/g, ">")
|
|
121
|
+
.replace(/"/g, """)
|
|
122
|
+
.replace(/'/g, "'");
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Erzeugt einen HtmlValue aus rohem String OHNE Escaping.
|
|
126
|
+
* Nur für vertrauenswürdigen Inhalt verwenden (eigenes CSS/JS, nie User-Input!).
|
|
127
|
+
* Damit lässt sich z.B. ein <script>{raw(js)}</script> einbetten, ohne
|
|
128
|
+
* dass `<`/`>`/`&` im JS/CSS kaputt escaped werden.
|
|
129
|
+
*/
|
|
130
|
+
export function raw(value) {
|
|
131
|
+
return createHtml(value);
|
|
132
|
+
}
|
package/dist/styles.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function loadStyles(styles: string[]): void;
|
package/dist/styles.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Function is from the lib thats why it in jsx runtime
|
|
2
|
+
export function loadStyles(styles) {
|
|
3
|
+
if (import.meta.env.DEV) {
|
|
4
|
+
console.log("[pages] loading styles:", styles);
|
|
5
|
+
}
|
|
6
|
+
for (const href of styles) {
|
|
7
|
+
const url = new URL(href, import.meta.url).href;
|
|
8
|
+
if (document.head.querySelector(`link[data-page-style="${CSS.escape(url)}"]`)) {
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
const link = document.createElement("link");
|
|
12
|
+
link.rel = "stylesheet";
|
|
13
|
+
link.href = url;
|
|
14
|
+
link.dataset.pageStyle = url;
|
|
15
|
+
document.head.appendChild(link);
|
|
16
|
+
}
|
|
17
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "twynejs",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "twine A Vite SSG plugin that turns .ts/.tsx pages, Markdown docs and Liquid/EJS templates into a multi-page static build with a virtual:pages module.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./jsx-runtime": {
|
|
14
|
+
"types": "./dist/jsx-runtime.d.ts",
|
|
15
|
+
"import": "./dist/jsx-runtime.js"
|
|
16
|
+
},
|
|
17
|
+
"./styles": {
|
|
18
|
+
"types": "./dist/styles.d.ts",
|
|
19
|
+
"import": "./dist/styles.js"
|
|
20
|
+
},
|
|
21
|
+
"./package.json": "./package.json"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"vite",
|
|
31
|
+
"plugin",
|
|
32
|
+
"ssg",
|
|
33
|
+
"static-site",
|
|
34
|
+
"pages",
|
|
35
|
+
"markdown",
|
|
36
|
+
"liquid",
|
|
37
|
+
"ejs"
|
|
38
|
+
],
|
|
39
|
+
"author": "Shadowdara",
|
|
40
|
+
"license": "Apache-2.0",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "https://github.com/shadowdara/finder",
|
|
44
|
+
"directory": "npm/pages-plugin"
|
|
45
|
+
},
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/shadowdara/finder/issues"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"vite": ">=5.0.0"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@shadowdara/dlib": "^0.0.4",
|
|
54
|
+
"ejs": "^6.0.1",
|
|
55
|
+
"highlight.js": "^11.12.0",
|
|
56
|
+
"html-minifier-terser": "^7.2.0",
|
|
57
|
+
"liquidjs": "^10.29.0",
|
|
58
|
+
"tsx": "^4.23.13"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@types/ejs": "^3.1.5",
|
|
62
|
+
"@types/html-minifier-terser": "^7.0.2",
|
|
63
|
+
"@types/node": "^26.5.0",
|
|
64
|
+
"typescript": "^5.5.0",
|
|
65
|
+
"vite": "6.4.3"
|
|
66
|
+
}
|
|
67
|
+
}
|