@opentf/web 0.5.0 → 0.6.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 +43 -0
- package/components/Link.jsx +18 -4
- package/core/reactive.js +190 -0
- package/core/signals.js +19 -0
- package/index.js +1 -0
- package/package.json +1 -1
- package/runtime/clipboard.js +47 -0
- package/runtime/code-block.js +47 -0
- package/runtime/context.js +13 -3
- package/runtime/dom.js +109 -21
- package/runtime/error-boundary.js +5 -5
- package/runtime/hydrate.js +121 -0
- package/runtime/index.js +7 -1
- package/runtime/portal.js +4 -4
- package/runtime/raw-html.js +33 -0
- package/runtime/router.js +156 -10
- package/server/builtins.js +8 -3
- package/server/head.js +191 -0
- package/server/index.js +1 -0
- package/server/render.js +35 -13
- package/server/ssg-runtime.js +4 -1
package/server/render.js
CHANGED
|
@@ -14,22 +14,28 @@ import {
|
|
|
14
14
|
routes,
|
|
15
15
|
setRouteState,
|
|
16
16
|
} from "../runtime/router.js";
|
|
17
|
+
import { resolveMetadata } from "./head.js";
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
|
-
* Render `pathname` to
|
|
20
|
-
*
|
|
21
|
-
*
|
|
20
|
+
* Render `pathname` to `{ html, metadata, status }`: the markup for inside `#app`,
|
|
21
|
+
* the resolved SEO metadata (for the `<head>`), and an HTTP `status` (200 when the
|
|
22
|
+
* path matched a real route, 404 when it fell back to the registered 404 page — the
|
|
23
|
+
* SSR server uses it; SSG ignores it). `params` (from `getStaticPaths`) override the
|
|
24
|
+
* matched route's params for dynamic routes. Returns `null` if there's no match and
|
|
25
|
+
* no 404 page.
|
|
22
26
|
*/
|
|
23
|
-
export async function
|
|
27
|
+
export async function renderRoute(pathname, params = null, search = "") {
|
|
28
|
+
const real = matchRoute(pathname);
|
|
24
29
|
const match =
|
|
25
|
-
|
|
26
|
-
(routes.notFound ? { entry: routes.notFound, params: {}, route: null } : null);
|
|
30
|
+
real || (routes.notFound ? { entry: routes.notFound, params: {}, route: null } : null);
|
|
27
31
|
if (!match) return null;
|
|
32
|
+
if (params) match.params = params;
|
|
28
33
|
|
|
29
34
|
// Let a page reading `router.params`/`pathname`/`query` resolve to this route.
|
|
30
35
|
setRouteState({ pathname, search, params: match.params });
|
|
31
36
|
|
|
32
|
-
const
|
|
37
|
+
const query = Object.fromEntries(new URLSearchParams(search));
|
|
38
|
+
const props = { params: match.params, query };
|
|
33
39
|
let html = (await resolveFactory(match.entry))(props);
|
|
34
40
|
|
|
35
41
|
// Wrap with layouts, most-specific inward to root outermost.
|
|
@@ -38,7 +44,20 @@ export async function renderToString(pathname, search = "") {
|
|
|
38
44
|
const layout = await resolveFactory(chain[i]);
|
|
39
45
|
html = layout({ ...props, children: html });
|
|
40
46
|
}
|
|
41
|
-
|
|
47
|
+
|
|
48
|
+
const metadata = await resolveMetadata({
|
|
49
|
+
route: match.route,
|
|
50
|
+
entry: match.entry,
|
|
51
|
+
params: match.params,
|
|
52
|
+
query,
|
|
53
|
+
});
|
|
54
|
+
return { html, metadata, status: real ? 200 : 404 };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Back-compat / convenience: render just the `#app` markup for `pathname`. */
|
|
58
|
+
export async function renderToString(pathname, search = "") {
|
|
59
|
+
const result = await renderRoute(pathname, null, search);
|
|
60
|
+
return result ? result.html : null;
|
|
42
61
|
}
|
|
43
62
|
|
|
44
63
|
/** Substitute `[param]` / `[...rest]` in a route with concrete values. */
|
|
@@ -49,16 +68,18 @@ function fillRoute(route, params) {
|
|
|
49
68
|
}
|
|
50
69
|
|
|
51
70
|
/**
|
|
52
|
-
* Enumerate the concrete paths to pre-render
|
|
53
|
-
* dynamic routes (`[param]`) are expanded via the
|
|
54
|
-
* `getStaticPaths()` (returning `[{ params }]`),
|
|
71
|
+
* Enumerate the concrete paths to pre-render as `{ path, params }`. Static routes
|
|
72
|
+
* are taken as-is (`params: {}`); dynamic routes (`[param]`) are expanded via the
|
|
73
|
+
* page module's optional `getStaticPaths()` (returning `[{ params }]`), carrying the
|
|
74
|
+
* params forward so the renderer/`generateMetadata` see them. Dynamic routes without
|
|
75
|
+
* `getStaticPaths` are collected as `skipped`.
|
|
55
76
|
*/
|
|
56
77
|
export async function collectRoutePaths() {
|
|
57
78
|
const paths = [];
|
|
58
79
|
const skipped = [];
|
|
59
80
|
for (const route in routes.pages) {
|
|
60
81
|
if (!route.includes("[")) {
|
|
61
|
-
paths.push(route);
|
|
82
|
+
paths.push({ path: route, params: {} });
|
|
62
83
|
continue;
|
|
63
84
|
}
|
|
64
85
|
const ns = routes.pages[route];
|
|
@@ -66,7 +87,8 @@ export async function collectRoutePaths() {
|
|
|
66
87
|
ns && (ns.getStaticPaths || (ns.default && ns.default.getStaticPaths));
|
|
67
88
|
if (typeof getStaticPaths === "function") {
|
|
68
89
|
for (const entry of (await getStaticPaths()) || []) {
|
|
69
|
-
|
|
90
|
+
const params = entry.params || entry;
|
|
91
|
+
paths.push({ path: fillRoute(route, params), params });
|
|
70
92
|
}
|
|
71
93
|
} else {
|
|
72
94
|
skipped.push(route);
|
package/server/ssg-runtime.js
CHANGED
|
@@ -103,7 +103,10 @@ export function ssgComponent(tag, props, children) {
|
|
|
103
103
|
} catch {
|
|
104
104
|
inner = ""; // fail soft (client renders/handles it)
|
|
105
105
|
}
|
|
106
|
-
|
|
106
|
+
// Stamp the stable styling hook on the host (mirrors CSR's `classList.add`) so
|
|
107
|
+
// tag-hashed components are still styleable by a readable class name.
|
|
108
|
+
const cls = render && render.hostClass;
|
|
109
|
+
return cls ? `<${tag} class="${cls}">${inner}</${tag}>` : `<${tag}>${inner}</${tag}>`;
|
|
107
110
|
}
|
|
108
111
|
|
|
109
112
|
export { VOID };
|