@nextwebwg/htmlkit 1.0.0-alpha.28
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/LICENSE +21 -0
- package/README.md +249 -0
- package/dist/application.d.ts +4 -0
- package/dist/application.d.ts.map +1 -0
- package/dist/application.js +188 -0
- package/dist/application.js.map +1 -0
- package/dist/browser.d.ts +7 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +76 -0
- package/dist/browser.js.map +1 -0
- package/dist/build.d.ts +3 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.js +154 -0
- package/dist/build.js.map +1 -0
- package/dist/bundle-worker.d.ts +2 -0
- package/dist/bundle-worker.d.ts.map +1 -0
- package/dist/bundle-worker.js +24 -0
- package/dist/bundle-worker.js.map +1 -0
- package/dist/bundle.d.ts +9 -0
- package/dist/bundle.d.ts.map +1 -0
- package/dist/bundle.js +23 -0
- package/dist/bundle.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +58 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +13 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +30 -0
- package/dist/config.js.map +1 -0
- package/dist/document.d.ts +5 -0
- package/dist/document.d.ts.map +1 -0
- package/dist/document.js +18 -0
- package/dist/document.js.map +1 -0
- package/dist/head.d.ts +5 -0
- package/dist/head.d.ts.map +1 -0
- package/dist/head.js +88 -0
- package/dist/head.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/resource.d.ts +14 -0
- package/dist/resource.d.ts.map +1 -0
- package/dist/resource.js +65 -0
- package/dist/resource.js.map +1 -0
- package/dist/routes.d.ts +9 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +175 -0
- package/dist/routes.js.map +1 -0
- package/dist/serve.d.ts +4 -0
- package/dist/serve.d.ts.map +1 -0
- package/dist/serve.js +204 -0
- package/dist/serve.js.map +1 -0
- package/dist/types.d.ts +112 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +53 -0
package/dist/build.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { lstat, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, writeFile, copyFile } from "node:fs/promises";
|
|
2
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
3
|
+
import { createApplication } from "./application.js";
|
|
4
|
+
import { browserSource, stylesheetSources } from "./browser.js";
|
|
5
|
+
import { bundleBrowser } from "./bundle.js";
|
|
6
|
+
import { configure, HtmlKitError, within } from "./config.js";
|
|
7
|
+
import { documentHTML, escapeHTML } from "./document.js";
|
|
8
|
+
import { matchRoute } from "./routes.js";
|
|
9
|
+
async function exists(path) {
|
|
10
|
+
try {
|
|
11
|
+
await lstat(path);
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
if (error.code === "ENOENT")
|
|
16
|
+
return false;
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
async function copyPublic(source, output) {
|
|
21
|
+
if (!await exists(source))
|
|
22
|
+
return;
|
|
23
|
+
if ((await lstat(source)).isSymbolicLink())
|
|
24
|
+
throw new HtmlKitError("Public assets must not be symbolic links.", source);
|
|
25
|
+
for (const file of await readdir(source, { withFileTypes: true })) {
|
|
26
|
+
const from = join(source, file.name);
|
|
27
|
+
const to = join(output, file.name);
|
|
28
|
+
if (file.isSymbolicLink())
|
|
29
|
+
throw new HtmlKitError("Public assets must not be symbolic links.", from);
|
|
30
|
+
if (file.isDirectory()) {
|
|
31
|
+
await mkdir(to, { recursive: true });
|
|
32
|
+
await copyPublic(from, to);
|
|
33
|
+
}
|
|
34
|
+
else if (file.isFile()) {
|
|
35
|
+
await mkdir(dirname(to), { recursive: true });
|
|
36
|
+
await copyFile(from, to);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function canonicalDestination(path) {
|
|
41
|
+
try {
|
|
42
|
+
return await realpath(path);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (error.code !== "ENOENT")
|
|
46
|
+
throw error;
|
|
47
|
+
return join(await canonicalDestination(dirname(path)), basename(path));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export async function buildApplication(options = {}) {
|
|
51
|
+
const config = configure(options);
|
|
52
|
+
const root = await realpath(config.root);
|
|
53
|
+
const destination = await canonicalDestination(config.outDir);
|
|
54
|
+
if (within(destination, root) || (await Promise.all(["app", "src", "public", "node_modules", ".git"].map(path => canonicalDestination(join(root, path))))).some(path => within(path, destination))) {
|
|
55
|
+
throw new HtmlKitError("Unsafe output directory through a filesystem alias.", config.outDir);
|
|
56
|
+
}
|
|
57
|
+
if (await exists(config.outDir) && !(await lstat(config.outDir)).isDirectory())
|
|
58
|
+
throw new HtmlKitError("Output must be a directory.", config.outDir);
|
|
59
|
+
if (await exists(config.outDir) && (await readdir(config.outDir)).length > 0 && !await exists(join(config.outDir, "_htmlkit/manifest.json"))) {
|
|
60
|
+
throw new HtmlKitError("Refusing to replace nonempty output without an HTMLKit build manifest.", config.outDir);
|
|
61
|
+
}
|
|
62
|
+
await mkdir(dirname(config.outDir), { recursive: true });
|
|
63
|
+
const application = await createApplication(options);
|
|
64
|
+
// Sibling staging keeps rename on the same volume. No failed render or bundle replaces dist.
|
|
65
|
+
let stage;
|
|
66
|
+
try {
|
|
67
|
+
stage = await mkdtemp(join(dirname(config.outDir), ".htmlkit-build-"));
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
await application.close();
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
const backup = stage + "-previous";
|
|
74
|
+
try {
|
|
75
|
+
const routes = await application.entries();
|
|
76
|
+
if (routes.length === 0)
|
|
77
|
+
throw new HtmlKitError("No pages found in app/pages or registered routes.", config.root);
|
|
78
|
+
await copyPublic(resolve(config.root, "public"), stage);
|
|
79
|
+
if (await exists(join(stage, "_htmlkit")))
|
|
80
|
+
throw new HtmlKitError("Public asset collision: _htmlkit is reserved for generated assets.");
|
|
81
|
+
const pages = [];
|
|
82
|
+
const sources = new Map();
|
|
83
|
+
for (const pathname of routes) {
|
|
84
|
+
const page = await application.render(pathname);
|
|
85
|
+
pages.push(page);
|
|
86
|
+
sources.set(`virtual:htmlkit/page-${pages.length - 1}`, browserSource(page.components, config.base));
|
|
87
|
+
for (const [id, css] of stylesheetSources(page.components))
|
|
88
|
+
sources.set(id, css);
|
|
89
|
+
}
|
|
90
|
+
const browserInputs = await bundleBrowser({ root: config.root, base: config.base, outDir: stage, sources });
|
|
91
|
+
const manifest = JSON.parse(await readFile(join(stage, "_htmlkit/vite-manifest.json"), "utf8"));
|
|
92
|
+
const bundles = new Map(Object.values(manifest).filter(chunk => chunk.isEntry).map(chunk => [chunk.name, chunk]));
|
|
93
|
+
const delivery = [];
|
|
94
|
+
for (let i = 0; i < pages.length; i++) {
|
|
95
|
+
const page = pages[i];
|
|
96
|
+
const path = join(stage, decodeURIComponent(page.pathname.slice(config.base.length)), "index.html");
|
|
97
|
+
if (await exists(path))
|
|
98
|
+
throw new HtmlKitError(`Public asset collision with route ${page.pathname}.`, path);
|
|
99
|
+
const entry = bundles.get(`page-${i}`);
|
|
100
|
+
if (entry === undefined)
|
|
101
|
+
throw new HtmlKitError(`Missing browser bundle for ${page.pathname}.`);
|
|
102
|
+
const route = matchRoute(application.routes, page.pathname, config.base).route;
|
|
103
|
+
delivery.push({ pathname: page.pathname, pageName: route.pageName, browserModule: config.base + entry.file });
|
|
104
|
+
// Vite's manifest includes CSS from the entry and its shared static imports.
|
|
105
|
+
const styles = new Set();
|
|
106
|
+
const seen = new Set();
|
|
107
|
+
const collect = (chunk) => {
|
|
108
|
+
if (seen.has(chunk))
|
|
109
|
+
return;
|
|
110
|
+
seen.add(chunk);
|
|
111
|
+
for (const css of chunk.css ?? [])
|
|
112
|
+
styles.add(css);
|
|
113
|
+
for (const id of chunk.imports ?? []) {
|
|
114
|
+
const imported = manifest[id];
|
|
115
|
+
if (imported !== undefined)
|
|
116
|
+
collect(imported);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
collect(entry);
|
|
120
|
+
const assets = [...styles].map(css => `<link rel="stylesheet" href="${escapeHTML(config.base + css)}">`).join("") +
|
|
121
|
+
`<script type="module" src="${escapeHTML(config.base + entry.file)}"></script>`;
|
|
122
|
+
await mkdir(dirname(path), { recursive: true });
|
|
123
|
+
await writeFile(path, documentHTML(page.body, page.head, assets));
|
|
124
|
+
}
|
|
125
|
+
await rm(join(stage, "_htmlkit/vite-manifest.json"));
|
|
126
|
+
if (await exists(join(stage, "404.html")))
|
|
127
|
+
throw new HtmlKitError("Public asset collision: 404.html is generated.");
|
|
128
|
+
await writeFile(join(stage, "404.html"), documentHTML('<main><h1>Page not found</h1></main>', { title: "Page not found" }));
|
|
129
|
+
await writeFile(join(stage, "_htmlkit/manifest.json"), JSON.stringify({ version: 1, base: config.base, routes, pages: delivery }, null, 2) + "\n");
|
|
130
|
+
const previous = await exists(config.outDir);
|
|
131
|
+
if (previous)
|
|
132
|
+
await rename(config.outDir, backup);
|
|
133
|
+
try {
|
|
134
|
+
await rename(stage, config.outDir);
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
if (previous)
|
|
138
|
+
await rename(backup, config.outDir);
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
if (previous)
|
|
142
|
+
await rm(backup, { recursive: true, force: true });
|
|
143
|
+
return { outDir: config.outDir, routes, browserInputs };
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
try {
|
|
147
|
+
await application.close();
|
|
148
|
+
}
|
|
149
|
+
finally {
|
|
150
|
+
await rm(stage, { recursive: true, force: true });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundle-worker.d.ts","sourceRoot":"","sources":["../src/bundle-worker.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
2
|
+
import { build } from "vite";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { browserPlugin } from "./browser.js";
|
|
5
|
+
const options = workerData;
|
|
6
|
+
try {
|
|
7
|
+
const inputs = new Set();
|
|
8
|
+
await build({ root: options.root, configFile: false, mode: "production", base: options.base, publicDir: join(options.root, "public"),
|
|
9
|
+
logLevel: "silent", plugins: [browserPlugin(options.sources, inputs)],
|
|
10
|
+
build: { outDir: options.outDir, emptyOutDir: false, copyPublicDir: false, assetsDir: "_htmlkit", target: "es2022", manifest: "_htmlkit/vite-manifest.json", modulePreload: false,
|
|
11
|
+
rolldownOptions: { input: Object.fromEntries([...options.sources.keys()].filter(id => id.startsWith("virtual:htmlkit/")).map((id, i) => [`page-${i}`, id])),
|
|
12
|
+
output: { entryFileNames: "_htmlkit/[name]-[hash].js", chunkFileNames: "_htmlkit/[name]-[hash].js", assetFileNames: "_htmlkit/[name]-[hash][extname]" } } },
|
|
13
|
+
});
|
|
14
|
+
// oxlint-disable-next-line unicorn/require-post-message-target-origin -- Node MessagePort has no origin.
|
|
15
|
+
parentPort.postMessage({ inputs: [...inputs].sort() });
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
// oxlint-disable-next-line unicorn/require-post-message-target-origin -- Node MessagePort has no origin.
|
|
19
|
+
parentPort.postMessage({ error: error instanceof Error ? error.message : String(error) });
|
|
20
|
+
}
|
|
21
|
+
finally {
|
|
22
|
+
parentPort.close();
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=bundle-worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundle-worker.js","sourceRoot":"","sources":["../src/bundle-worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAG7C,MAAM,OAAO,GAAG,UAA0B,CAAC;AAC3C,IAAI,CAAC;IACH,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IACjC,MAAM,KAAK,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;QAClI,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACrE,KAAK,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,6BAA6B,EAAE,aAAa,EAAE,KAAK;YAC/K,eAAe,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;gBACzJ,MAAM,EAAE,EAAE,cAAc,EAAE,2BAA2B,EAAE,cAAc,EAAE,2BAA2B,EAAE,cAAc,EAAE,iCAAiC,EAAE,EAAE,EAAE;KAChK,CAAC,CAAC;IACH,yGAAyG;IACzG,UAAW,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AAC1D,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACf,yGAAyG;IACzG,UAAW,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AAC7F,CAAC;QAAS,CAAC;IAAC,UAAW,CAAC,KAAK,EAAE,CAAC;AAAC,CAAC"}
|
package/dist/bundle.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface BrowserBuild {
|
|
2
|
+
readonly root: string;
|
|
3
|
+
readonly base: string;
|
|
4
|
+
readonly outDir: string;
|
|
5
|
+
readonly sources: ReadonlyMap<string, string>;
|
|
6
|
+
}
|
|
7
|
+
/** Vite's build and dev APIs both use NODE_ENV. Keep the production build in its own realm. */
|
|
8
|
+
export declare function bundleBrowser(options: BrowserBuild): Promise<readonly string[]>;
|
|
9
|
+
//# sourceMappingURL=bundle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/C;AAED,+FAA+F;AAC/F,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAgB/E"}
|
package/dist/bundle.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Worker } from "node:worker_threads";
|
|
2
|
+
/** Vite's build and dev APIs both use NODE_ENV. Keep the production build in its own realm. */
|
|
3
|
+
export function bundleBrowser(options) {
|
|
4
|
+
const source = import.meta.url.endsWith(".ts");
|
|
5
|
+
const entry = new URL(source ? "./bundle-worker.ts" : "./bundle-worker.js", import.meta.url);
|
|
6
|
+
const workerEntry = source ? new URL(`data:text/javascript,${encodeURIComponent(`import { tsImport } from ${JSON.stringify(import.meta.resolve("tsx/esm/api"))}; await tsImport(${JSON.stringify(entry.href)}, ${JSON.stringify(import.meta.url)});`)}`) : entry;
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
let replied = false;
|
|
9
|
+
const worker = new Worker(workerEntry, { workerData: options, execArgv: [], env: { ...process.env, NODE_ENV: "production" } });
|
|
10
|
+
worker.once("message", (reply) => {
|
|
11
|
+
replied = true;
|
|
12
|
+
void worker.terminate();
|
|
13
|
+
if ("error" in reply)
|
|
14
|
+
reject(new Error(reply.error));
|
|
15
|
+
else
|
|
16
|
+
resolve(reply.inputs);
|
|
17
|
+
});
|
|
18
|
+
worker.once("error", reject);
|
|
19
|
+
worker.once("exit", code => { if (!replied)
|
|
20
|
+
reject(new Error(`Browser build worker exited without a result (code ${code}).`)); });
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=bundle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundle.js","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAS7C,+FAA+F;AAC/F,MAAM,UAAU,aAAa,CAAC,OAAqB;IACjD,MAAM,MAAM,GAAG,OAAO,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/C,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,oBAAoB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;IAC7F,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,wBAAwB,kBAAkB,CAC7E,4BAA4B,IAAI,CAAC,SAAS,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,oBAAoB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CACrK,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACb,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,WAAW,EAAE,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,CAAC,CAAC;QAC/H,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,KAA+C,EAAE,EAAE;YACzE,OAAO,GAAG,IAAI,CAAC;YAAC,KAAK,MAAM,CAAC,SAAS,EAAE,CAAC;YACxC,IAAI,OAAO,IAAI,KAAK;gBAAE,MAAM,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;;gBAAM,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACnF,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,OAAO;YAAE,MAAM,CAAC,IAAI,KAAK,CAAC,sDAAsD,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACpI,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { parseArgs } from "node:util";
|
|
5
|
+
import { createServer } from "vite";
|
|
6
|
+
import { buildApplication } from "./build.js";
|
|
7
|
+
import { HtmlKitError } from "./config.js";
|
|
8
|
+
import { devApplication, previewApplication } from "./serve.js";
|
|
9
|
+
async function main() {
|
|
10
|
+
const { values, positionals } = parseArgs({ allowPositionals: true, options: {
|
|
11
|
+
base: { type: "string" }, origin: { type: "string" }, "out-dir": { type: "string" },
|
|
12
|
+
port: { type: "string" }, host: { type: "string" }, help: { type: "boolean", short: "h" },
|
|
13
|
+
} });
|
|
14
|
+
if (values.help || positionals.length === 0) {
|
|
15
|
+
console.log("Usage: htmlkit <dev|build|preview> [root] [--base /docs/] [--origin https://example.com] [--out-dir dist] [--port 3000] [--host 127.0.0.1]");
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const command = positionals[0];
|
|
19
|
+
if (!["dev", "build", "preview"].includes(command) || positionals.length > 2)
|
|
20
|
+
throw new HtmlKitError("Choose dev, build, or preview and an optional application root.");
|
|
21
|
+
const root = resolve(positionals[1] ?? process.cwd());
|
|
22
|
+
let configured = {};
|
|
23
|
+
const files = ["ts", "js"].map(ext => resolve(root, `htmlkit.config.${ext}`)).filter(existsSync);
|
|
24
|
+
if (files.length > 1)
|
|
25
|
+
throw new HtmlKitError("Choose one htmlkit.config.ts or .js.", root);
|
|
26
|
+
if (files[0] !== undefined) {
|
|
27
|
+
const loader = await createServer({ root, configFile: false, appType: "custom", logLevel: "silent",
|
|
28
|
+
server: { middlewareMode: true, watch: null, hmr: false }, optimizeDeps: { noDiscovery: true, include: [] } });
|
|
29
|
+
try {
|
|
30
|
+
configured = (await loader.ssrLoadModule(files[0])).default;
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
await loader.close();
|
|
34
|
+
}
|
|
35
|
+
if (configured === null || typeof configured !== "object")
|
|
36
|
+
throw new HtmlKitError("Configuration must export a default ApplicationOptions object.", files[0]);
|
|
37
|
+
}
|
|
38
|
+
const options = { ...configured, root,
|
|
39
|
+
...(values.base === undefined ? {} : { base: values.base }),
|
|
40
|
+
...(values.origin === undefined ? {} : { origin: values.origin }),
|
|
41
|
+
...(values["out-dir"] === undefined ? {} : { outDir: values["out-dir"] }),
|
|
42
|
+
...(values.host === undefined ? {} : { host: values.host }),
|
|
43
|
+
...(values.port === undefined ? {} : { port: Number(values.port) }) };
|
|
44
|
+
if (options.port !== undefined && (!Number.isInteger(options.port) || options.port < 0 || options.port > 65535))
|
|
45
|
+
throw new HtmlKitError("port must be an integer from 0 to 65535.");
|
|
46
|
+
if (command === "build") {
|
|
47
|
+
const result = await buildApplication(options);
|
|
48
|
+
console.log(`Generated ${result.routes.length} pages in ${result.outDir}`);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const server = await (command === "dev" ? devApplication(options) : previewApplication(options));
|
|
52
|
+
console.log(`HTMLKit ${command}: ${server.url}`);
|
|
53
|
+
const stop = () => { void server.close().then(() => { process.exitCode = 0; }).catch(error => { console.error(String(error)); process.exitCode = 1; }); };
|
|
54
|
+
process.once("SIGINT", stop);
|
|
55
|
+
process.once("SIGTERM", stop);
|
|
56
|
+
}
|
|
57
|
+
void main().catch(error => { console.error(error instanceof Error ? error.message : String(error)); process.exitCode = 1; });
|
|
58
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ApplicationOptions } from "./types.js";
|
|
2
|
+
export declare class HtmlKitError extends Error {
|
|
3
|
+
readonly source?: string | undefined;
|
|
4
|
+
constructor(message: string, source?: string | undefined);
|
|
5
|
+
}
|
|
6
|
+
export declare function within(parent: string, child: string): boolean;
|
|
7
|
+
export declare function configure(options: ApplicationOptions): {
|
|
8
|
+
root: string;
|
|
9
|
+
base: string;
|
|
10
|
+
origin: string;
|
|
11
|
+
outDir: string;
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,qBAAa,YAAa,SAAQ,KAAK;IACR,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM;IAArD,YAAY,OAAO,EAAE,MAAM,EAAW,MAAM,CAAC,EAAE,MAAM,YAAA,EAGpD;CACF;AACD,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAG7D;AACD,wBAAgB,SAAS,CAAC,OAAO,EAAE,kBAAkB;;;;;EAcpD"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
2
|
+
export class HtmlKitError extends Error {
|
|
3
|
+
source;
|
|
4
|
+
constructor(message, source) {
|
|
5
|
+
super(source === undefined ? message : `${source}: ${message}`);
|
|
6
|
+
this.source = source;
|
|
7
|
+
this.name = "HtmlKitError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export function within(parent, child) {
|
|
11
|
+
const path = relative(parent, child);
|
|
12
|
+
return path === "" || (!isAbsolute(path) && path !== ".." && !path.startsWith(`..${sep}`));
|
|
13
|
+
}
|
|
14
|
+
export function configure(options) {
|
|
15
|
+
const root = resolve(options.root ?? process.cwd());
|
|
16
|
+
const base = options.base ?? "/";
|
|
17
|
+
if (!/^\/(?:[A-Za-z0-9_-]+\/)*$/.test(base))
|
|
18
|
+
throw new HtmlKitError("base must be an absolute path with a trailing slash, for example /docs/.");
|
|
19
|
+
const origin = new URL(options.origin ?? "http://localhost");
|
|
20
|
+
if (!/^https?:$/.test(origin.protocol) || origin.username || origin.password || origin.pathname !== "/" || origin.search || origin.hash) {
|
|
21
|
+
throw new HtmlKitError("origin must be an HTTP(S) origin without a path, credentials, query, or fragment.");
|
|
22
|
+
}
|
|
23
|
+
const outDir = resolve(root, options.outDir ?? "dist");
|
|
24
|
+
// A failed build must never erase source, the application, or its dependencies.
|
|
25
|
+
if (within(outDir, root) || ["app", "src", "public", "node_modules", ".git"].some(path => within(resolve(root, path), outDir))) {
|
|
26
|
+
throw new HtmlKitError("Unsafe output directory; choose dist or a separate deployment directory.", outDir);
|
|
27
|
+
}
|
|
28
|
+
return { root, base, origin: origin.origin, outDir };
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAG/D,MAAM,OAAO,YAAa,SAAQ,KAAK;IACC,MAAM;IAA5C,YAAY,OAAe,EAAW,MAAe;QACnD,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,KAAK,OAAO,EAAE,CAAC,CAAC;sBAD5B,MAAM;QAE1C,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AACD,MAAM,UAAU,MAAM,CAAC,MAAc,EAAE,KAAa;IAClD,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACrC,OAAO,IAAI,KAAK,EAAE,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC;AAC7F,CAAC;AACD,MAAM,UAAU,SAAS,CAAC,OAA2B;IACnD,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,GAAG,CAAC;IACjC,IAAI,CAAC,2BAA2B,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,YAAY,CAAC,0EAA0E,CAAC,CAAC;IAChJ,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,kBAAkB,CAAC,CAAC;IAC7D,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,KAAK,GAAG,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QACxI,MAAM,IAAI,YAAY,CAAC,mFAAmF,CAAC,CAAC;IAC9G,CAAC;IACD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,IAAI,MAAM,CAAC,CAAC;IACvD,gFAAgF;IAChF,IAAI,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;QAC/H,MAAM,IAAI,YAAY,CAAC,0EAA0E,EAAE,MAAM,CAAC,CAAC;IAC7G,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { HeadElement, RenderedHead } from "./types.js";
|
|
2
|
+
export declare const defaultHeadElements: readonly HeadElement[];
|
|
3
|
+
export declare function escapeHTML(value: string): string;
|
|
4
|
+
export declare function documentHTML(body: string, head: RenderedHead, assets?: string): string;
|
|
5
|
+
//# sourceMappingURL=document.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC5D,eAAO,MAAM,mBAAmB,EAAE,SAAS,WAAW,EAGrD,CAAC;AACF,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AACD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,SAAK,GAAG,MAAM,CASlF"}
|
package/dist/document.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const defaultHeadElements = [
|
|
2
|
+
{ tag: "meta", attributes: { charset: "utf-8" } },
|
|
3
|
+
{ tag: "meta", attributes: { name: "viewport", content: "width=device-width, initial-scale=1" } },
|
|
4
|
+
];
|
|
5
|
+
export function escapeHTML(value) {
|
|
6
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
7
|
+
}
|
|
8
|
+
export function documentHTML(body, head, assets = "") {
|
|
9
|
+
const elements = head.elements ?? defaultHeadElements;
|
|
10
|
+
const metadata = [...elements.filter(element => element.tag === "meta" && element.attributes.charset !== undefined),
|
|
11
|
+
...elements.filter(element => element.tag !== "meta" || element.attributes.charset === undefined)]
|
|
12
|
+
.map(element => `<${element.tag}${Object.entries(element.attributes).map(([name, value]) => ` ${name}="${escapeHTML(value)}"`).join("")}>`).join("");
|
|
13
|
+
return `<!doctype html>\n<html lang="${escapeHTML(head.lang ?? "en")}"><head>${metadata}` +
|
|
14
|
+
`<title>${escapeHTML(head.title ?? "HTMLKit")}</title>` +
|
|
15
|
+
(head.description === undefined ? "" : `<meta name="description" content="${escapeHTML(head.description)}">`) +
|
|
16
|
+
`${assets}</head><body>${body}</body></html>`;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=document.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document.js","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"AACA,MAAM,CAAC,MAAM,mBAAmB,GAA2B;IACzD,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE;IACjD,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,qCAAqC,EAAE,EAAE;CAClG,CAAC;AACF,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACjI,CAAC;AACD,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,IAAkB,EAAE,MAAM,GAAG,EAAE;IACxE,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,mBAAmB,CAAC;IACtD,MAAM,QAAQ,GAAG,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,KAAK,MAAM,IAAI,OAAO,CAAC,UAAU,CAAC,OAAO,KAAK,SAAS,CAAC;QACjH,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,KAAK,MAAM,IAAI,OAAO,CAAC,UAAU,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC;SACjG,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,OAAO,CAAC,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,IAAI,IAAI,KAAK,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvJ,OAAO,gCAAgC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,WAAW,QAAQ,EAAE;QACvF,UAAU,UAAU,CAAC,IAAI,CAAC,KAAK,IAAI,SAAS,CAAC,UAAU;QACvD,CAAC,IAAI,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,qCAAqC,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;QAC7G,GAAG,MAAM,gBAAgB,IAAI,gBAAgB,CAAC;AAClD,CAAC"}
|
package/dist/head.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type ComponentDefinition } from "@nextwebwg/html-next";
|
|
2
|
+
import type { applicationResource } from "./resource.js";
|
|
3
|
+
import type { LoaderResult, RenderedHead } from "./types.js";
|
|
4
|
+
export declare function renderHead(resource: ReturnType<typeof applicationResource>, definition: ComponentDefinition, result: LoaderResult, previous: RenderedHead, url: string, invoke: (definition: ComponentDefinition, result: LoaderResult) => string): Promise<RenderedHead>;
|
|
5
|
+
//# sourceMappingURL=head.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"head.d.ts","sourceRoot":"","sources":["../src/head.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAKhF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,KAAK,EAAe,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAiB1E,wBAAsB,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,EAAE,UAAU,EAAE,mBAAmB,EAChH,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,EACzD,MAAM,EAAE,CAAC,UAAU,EAAE,mBAAmB,EAAE,MAAM,EAAE,YAAY,KAAK,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAgDlG"}
|
package/dist/head.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { parseComponent } from "@nextwebwg/html-next";
|
|
2
|
+
import { renderComponents } from "@nextwebwg/html-next/server";
|
|
3
|
+
import { parseFragment, serialize } from "parse5";
|
|
4
|
+
import { HtmlKitError } from "./config.js";
|
|
5
|
+
import { defaultHeadElements } from "./document.js";
|
|
6
|
+
function identity(element) {
|
|
7
|
+
const attrs = element.attributes;
|
|
8
|
+
if (element.tag === "meta") {
|
|
9
|
+
if (attrs.charset !== undefined)
|
|
10
|
+
return "charset";
|
|
11
|
+
const name = attrs.name ?? attrs.property;
|
|
12
|
+
// Social image arrays and their structured properties preserve authored order.
|
|
13
|
+
if (name?.toLowerCase().startsWith("og:image"))
|
|
14
|
+
return "meta:property:og:image";
|
|
15
|
+
return name === undefined ? undefined : `meta:${attrs.name === undefined ? "property" : "name"}:${name.toLowerCase()}`;
|
|
16
|
+
}
|
|
17
|
+
const relations = attrs.rel?.toLowerCase().split(/\s+/) ?? [];
|
|
18
|
+
if (relations.includes("canonical"))
|
|
19
|
+
return "canonical";
|
|
20
|
+
if (relations.includes("alternate") && attrs.hreflang !== undefined)
|
|
21
|
+
return `alternate:${attrs.hreflang}:${attrs.type ?? ""}:${attrs.media ?? ""}`;
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
export async function renderHead(resource, definition, result, previous, url, invoke) {
|
|
25
|
+
let title = previous.title;
|
|
26
|
+
let description = previous.description;
|
|
27
|
+
const elements = [...(previous.elements ?? defaultHeadElements)];
|
|
28
|
+
const metadata = resource.components.get(definition.contract.tag);
|
|
29
|
+
if (metadata.head !== "") {
|
|
30
|
+
// Reuse the ordinary parser, declared prop contracts, serializer and DOM renderer.
|
|
31
|
+
// No state, data reads, controllers, or browser subscriptions enter this head scope.
|
|
32
|
+
// Component bodies exclude head elements. Inert spans carry the same attribute/text
|
|
33
|
+
// bindings through the renderer, then become native metadata after evaluation.
|
|
34
|
+
const fragment = parseFragment(metadata.head);
|
|
35
|
+
for (const node of fragment.childNodes) {
|
|
36
|
+
if (!("tagName" in node))
|
|
37
|
+
continue;
|
|
38
|
+
node.attrs.push({ name: "data-htmlkit-head", value: node.tagName });
|
|
39
|
+
node.tagName = "span";
|
|
40
|
+
node.nodeName = "span";
|
|
41
|
+
}
|
|
42
|
+
const headDefinition = parseComponent(`<template component="htmlkit-head"><defs>${metadata.propDeclarations}</defs><div>${serialize(fragment)}</div></template>`, definition.source.file);
|
|
43
|
+
const rendered = await renderComponents(invoke(headDefinition, { props: result.props ?? {} }), { definitions: [headDefinition], url });
|
|
44
|
+
const root = parseFragment(rendered.html).childNodes.find(node => "tagName" in node);
|
|
45
|
+
if (root === undefined || !("childNodes" in root))
|
|
46
|
+
throw new HtmlKitError("Head rendering produced no root.", definition.source.file);
|
|
47
|
+
const text = (node) => "value" in node ? node.value : "childNodes" in node ? node.childNodes.map(text).join("") : "";
|
|
48
|
+
const current = [];
|
|
49
|
+
for (const node of root.childNodes) {
|
|
50
|
+
if (!("tagName" in node))
|
|
51
|
+
continue;
|
|
52
|
+
const tag = node.attrs.find(attribute => attribute.name === "data-htmlkit-head")?.value;
|
|
53
|
+
if (tag === "title") {
|
|
54
|
+
title = text(node);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (tag !== "meta" && tag !== "link")
|
|
58
|
+
throw new HtmlKitError("Head content must render title, meta, or link elements.", definition.source.file);
|
|
59
|
+
const attributes = Object.fromEntries(node.attrs.filter(attribute => attribute.name !== "data-htmlkit-head").map(attribute => [attribute.name, attribute.value]));
|
|
60
|
+
if (Object.keys(attributes).some(name => name.startsWith("on")) || (tag === "meta" && attributes["http-equiv"] !== undefined)) {
|
|
61
|
+
throw new HtmlKitError("Head metadata cannot activate event handlers or http-equiv policies.", definition.source.file);
|
|
62
|
+
}
|
|
63
|
+
if (tag === "meta" && attributes.name?.toLowerCase() === "description") {
|
|
64
|
+
description = attributes.content ?? "";
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
current.push({ tag, attributes });
|
|
68
|
+
}
|
|
69
|
+
const singletons = new Set();
|
|
70
|
+
for (let i = current.length - 1; i >= 0; i--) {
|
|
71
|
+
const key = identity(current[i]);
|
|
72
|
+
if (key === undefined || key === "meta:property:og:image")
|
|
73
|
+
continue;
|
|
74
|
+
if (singletons.has(key))
|
|
75
|
+
current.splice(i, 1);
|
|
76
|
+
else
|
|
77
|
+
singletons.add(key);
|
|
78
|
+
}
|
|
79
|
+
const replaced = new Set(current.flatMap(element => identity(element) ?? []));
|
|
80
|
+
for (let i = elements.length - 1; i >= 0; i--)
|
|
81
|
+
if (replaced.has(identity(elements[i]) ?? ""))
|
|
82
|
+
elements.splice(i, 1);
|
|
83
|
+
elements.push(...current);
|
|
84
|
+
}
|
|
85
|
+
return { ...previous, ...(title === undefined ? {} : { title }), ...(description === undefined ? {} : { description }), ...result.head,
|
|
86
|
+
elements };
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=head.js.map
|
package/dist/head.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"head.js","sourceRoot":"","sources":["../src/head.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { createApplication } from "./application.js";
|
|
2
|
+
export { buildApplication } from "./build.js";
|
|
3
|
+
export { devApplication, previewApplication } from "./serve.js";
|
|
4
|
+
export { discoverRoutes } from "./routes.js";
|
|
5
|
+
export { HtmlKitError } from "./config.js";
|
|
6
|
+
import type { ApplicationOptions } from "./types.js";
|
|
7
|
+
/** Type-check an htmlkit.config.ts file without changing its values. */
|
|
8
|
+
export declare function defineConfig(options: ApplicationOptions): ApplicationOptions;
|
|
9
|
+
export type { Application, ApplicationOptions, ApplicationRoute, ApplicationServer, BrowserDefinition, BuildResult, HeadElement, LoadContext, LoaderResult, PageHead, RenderedHead, RenderedPage, RouteInput, RouteLayer, ServerModule, ServerOptions } from "./types.js";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,OAAO,EAAE,kBAAkB,GAAG,kBAAkB,CAAoB;AACjG,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,WAAW,EAChH,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { createApplication } from "./application.js";
|
|
2
|
+
export { buildApplication } from "./build.js";
|
|
3
|
+
export { devApplication, previewApplication } from "./serve.js";
|
|
4
|
+
export { discoverRoutes } from "./routes.js";
|
|
5
|
+
export { HtmlKitError } from "./config.js";
|
|
6
|
+
/** Type-check an htmlkit.config.ts file without changing its values. */
|
|
7
|
+
export function defineConfig(options) { return options; }
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,wEAAwE;AACxE,MAAM,UAAU,YAAY,CAAC,OAA2B,IAAwB,OAAO,OAAO,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ComponentDefinition } from "@nextwebwg/html-next";
|
|
2
|
+
/** Native HTML parsing determines resource nodes; HTML Next owns component syntax. */
|
|
3
|
+
export declare function applicationResource(source: string, file: string): {
|
|
4
|
+
definitions: readonly ComponentDefinition[];
|
|
5
|
+
dependencies: readonly string[];
|
|
6
|
+
configuration: Map<string, string>;
|
|
7
|
+
components: Map<string, {
|
|
8
|
+
readonly layout?: string;
|
|
9
|
+
readonly head: string;
|
|
10
|
+
readonly propDeclarations: string;
|
|
11
|
+
}>;
|
|
12
|
+
};
|
|
13
|
+
export declare function pageDefinition(resource: ReturnType<typeof applicationResource>, file: string): ComponentDefinition;
|
|
14
|
+
//# sourceMappingURL=resource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAIxF,sFAAsF;AACtF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;;;;;0BAGP,MAAM;uBAAiB,MAAM;mCAA6B,MAAM;;EAoCxH;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,mBAAmB,CAQlH"}
|