@pracht/core 0.8.1 → 0.9.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 +5 -1
- package/dist/app-BN3pjxtw.d.mts +24 -0
- package/dist/{app-w-P1wf5T.mjs → app-CGRKjZ6Z.mjs} +96 -7
- package/dist/app-graph-BlaCcGUZ.mjs +52 -0
- package/dist/app-graph-DBoDsHJ5.d.mts +38 -0
- package/dist/browser.d.mts +7 -6
- package/dist/browser.mjs +8 -7
- package/dist/client.d.mts +3 -3
- package/dist/client.mjs +3 -3
- package/dist/dev-404.d.mts +26 -0
- package/dist/dev-404.mjs +166 -0
- package/dist/devtools.d.mts +8 -0
- package/dist/devtools.mjs +156 -0
- package/dist/error-overlay.d.mts +38 -1
- package/dist/error-overlay.mjs +134 -5
- package/dist/index.d.mts +9 -7
- package/dist/index.mjs +9 -7
- package/dist/manifest.d.mts +2 -1
- package/dist/manifest.mjs +1 -1
- package/dist/{runtime-context-B5pREhcM.mjs → navigation-state-BjwXTeVz.mjs} +74 -2
- package/dist/{prefetch-D9amIQtw.mjs → prefetch-COYeuvQK.mjs} +44 -27
- package/dist/prefetch-api-Bf-P7XFo.d.mts +39 -0
- package/dist/prefetch-api-ChSCTbEB.mjs +69 -0
- package/dist/{prefetch-cache-DzP2Bj9H.mjs → prefetch-cache-BNLEp9H5.mjs} +12 -1
- package/dist/{prerender-B8_CqYwd.d.mts → prerender-BV42B9jK.d.mts} +31 -2
- package/dist/{prerender-CoMyuJKm.mjs → prerender-C8BpANz3.mjs} +53 -9
- package/dist/{router-BcUmg1kB.d.mts → router-B_Aj5TtY.d.mts} +2 -2
- package/dist/{router-BkkyNjqB.mjs → router-E5Wh-dOR.mjs} +251 -58
- package/dist/{runtime-context-ytVd7GmZ.d.mts → runtime-context-BYBwVA0M.d.mts} +1 -1
- package/dist/{runtime-middleware-aeBdgjYr.d.mts → runtime-middleware-pYubngnL.d.mts} +57 -4
- package/dist/server.d.mts +7 -5
- package/dist/server.mjs +6 -5
- package/dist/{app-BWriseLj.d.mts → types-B-sfVjaH.d.mts} +42 -22
- package/dist/{types-DQv2poC5.mjs → types-DEPXdbyp.mjs} +43 -15
- package/package.json +9 -1
- package/dist/hydration-M1QzbQ1O.d.mts +0 -23
- /package/dist/{forwardRef-grZ6t4GS.mjs → forwardRef-B0cSkHwH.mjs} +0 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { i as serializeAppRoutes, n as detectApiMethods, r as serializeApiRoutes, t as buildAppGraph } from "./app-graph-BlaCcGUZ.mjs";
|
|
2
|
+
//#region src/devtools.ts
|
|
3
|
+
const DEVTOOLS_PATH = "/_pracht";
|
|
4
|
+
const DEVTOOLS_JSON_PATH = "/_pracht.json";
|
|
5
|
+
function buildDevtoolsHtml(graph) {
|
|
6
|
+
const routeRows = graph.routes.map((route) => `<tr>
|
|
7
|
+
<td>${routeLinkHtml(route)}</td>
|
|
8
|
+
<td>${escapeHtml(route.render ?? "ssr")}</td>
|
|
9
|
+
<td>${escapeHtml(route.shell ?? "—")}</td>
|
|
10
|
+
<td>${escapeHtml(route.middleware.length > 0 ? route.middleware.join(" → ") : "—")}</td>
|
|
11
|
+
<td class="file">${escapeHtml(route.file)}</td>
|
|
12
|
+
</tr>`).join("\n");
|
|
13
|
+
const apiRows = graph.api.map((route) => `<tr>
|
|
14
|
+
<td>${apiLinkHtml(route)}</td>
|
|
15
|
+
<td>${escapeHtml(route.methods.length > 0 ? route.methods.join(", ") : "—")}</td>
|
|
16
|
+
<td class="file">${escapeHtml(route.file)}</td>
|
|
17
|
+
</tr>`).join("\n");
|
|
18
|
+
return `<!DOCTYPE html>
|
|
19
|
+
<html>
|
|
20
|
+
<head>
|
|
21
|
+
<meta charset="utf-8">
|
|
22
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
23
|
+
<meta name="robots" content="noindex">
|
|
24
|
+
<title>pracht devtools</title>
|
|
25
|
+
<style>
|
|
26
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
27
|
+
body {
|
|
28
|
+
font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, monospace;
|
|
29
|
+
background: #1a1a2e;
|
|
30
|
+
color: #e0e0e0;
|
|
31
|
+
padding: 32px;
|
|
32
|
+
line-height: 1.5;
|
|
33
|
+
}
|
|
34
|
+
.devtools {
|
|
35
|
+
max-width: 1100px;
|
|
36
|
+
margin: 0 auto;
|
|
37
|
+
}
|
|
38
|
+
.header {
|
|
39
|
+
display: flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
gap: 12px;
|
|
42
|
+
margin-bottom: 24px;
|
|
43
|
+
padding-bottom: 16px;
|
|
44
|
+
border-bottom: 1px solid #333;
|
|
45
|
+
}
|
|
46
|
+
.badge {
|
|
47
|
+
background: #4c6ef5;
|
|
48
|
+
color: #fff;
|
|
49
|
+
font-size: 11px;
|
|
50
|
+
font-weight: 700;
|
|
51
|
+
text-transform: uppercase;
|
|
52
|
+
letter-spacing: 0.05em;
|
|
53
|
+
padding: 4px 10px;
|
|
54
|
+
border-radius: 4px;
|
|
55
|
+
}
|
|
56
|
+
.title {
|
|
57
|
+
font-size: 14px;
|
|
58
|
+
color: #888;
|
|
59
|
+
}
|
|
60
|
+
.title a {
|
|
61
|
+
color: #a0c4ff;
|
|
62
|
+
}
|
|
63
|
+
h2 {
|
|
64
|
+
font-size: 14px;
|
|
65
|
+
font-weight: 600;
|
|
66
|
+
color: #a0c4ff;
|
|
67
|
+
margin: 24px 0 10px;
|
|
68
|
+
}
|
|
69
|
+
table {
|
|
70
|
+
width: 100%;
|
|
71
|
+
border-collapse: collapse;
|
|
72
|
+
font-size: 13px;
|
|
73
|
+
}
|
|
74
|
+
th {
|
|
75
|
+
text-align: left;
|
|
76
|
+
color: #888;
|
|
77
|
+
font-weight: 600;
|
|
78
|
+
padding: 6px 12px 6px 0;
|
|
79
|
+
border-bottom: 1px solid #333;
|
|
80
|
+
}
|
|
81
|
+
td {
|
|
82
|
+
padding: 6px 12px 6px 0;
|
|
83
|
+
border-bottom: 1px solid #26263e;
|
|
84
|
+
vertical-align: top;
|
|
85
|
+
word-break: break-word;
|
|
86
|
+
}
|
|
87
|
+
td a {
|
|
88
|
+
color: #74c0fc;
|
|
89
|
+
}
|
|
90
|
+
.file {
|
|
91
|
+
color: #888;
|
|
92
|
+
}
|
|
93
|
+
.empty {
|
|
94
|
+
font-size: 13px;
|
|
95
|
+
color: #888;
|
|
96
|
+
}
|
|
97
|
+
.hint {
|
|
98
|
+
margin-top: 24px;
|
|
99
|
+
font-size: 12px;
|
|
100
|
+
color: #666;
|
|
101
|
+
}
|
|
102
|
+
.hint a {
|
|
103
|
+
color: #a0c4ff;
|
|
104
|
+
}
|
|
105
|
+
</style>
|
|
106
|
+
</head>
|
|
107
|
+
<body>
|
|
108
|
+
<div class="devtools">
|
|
109
|
+
<div class="header">
|
|
110
|
+
<span class="badge">pracht</span>
|
|
111
|
+
<span class="title">devtools — resolved app graph (dev only)</span>
|
|
112
|
+
</div>
|
|
113
|
+
<h2>Page routes</h2>
|
|
114
|
+
<table>
|
|
115
|
+
<thead><tr><th>Route</th><th>Render</th><th>Shell</th><th>Middleware</th><th>Source</th></tr></thead>
|
|
116
|
+
<tbody>
|
|
117
|
+
${routeRows}
|
|
118
|
+
</tbody>
|
|
119
|
+
</table>
|
|
120
|
+
${graph.api.length > 0 ? `<h2>API routes</h2>
|
|
121
|
+
<table>
|
|
122
|
+
<thead><tr><th>Path</th><th>Methods</th><th>Source</th></tr></thead>
|
|
123
|
+
<tbody>
|
|
124
|
+
${apiRows}
|
|
125
|
+
</tbody>
|
|
126
|
+
</table>` : `<h2>API routes</h2>
|
|
127
|
+
<p class="empty">No API routes found.</p>`}
|
|
128
|
+
<div class="hint">
|
|
129
|
+
Raw JSON at <a href="${DEVTOOLS_JSON_PATH}">${DEVTOOLS_JSON_PATH}</a> ·
|
|
130
|
+
same data as <code>pracht inspect --json</code> ·
|
|
131
|
+
per-request middleware/loader/render timings are on the <code>Server-Timing</code>
|
|
132
|
+
response header in the browser Network panel.
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
</body>
|
|
136
|
+
</html>`;
|
|
137
|
+
}
|
|
138
|
+
function routeLinkHtml(route) {
|
|
139
|
+
const label = escapeHtml(route.path);
|
|
140
|
+
if (!isLinkablePath(route.path)) return label;
|
|
141
|
+
return `<a href="${escapeHtml(route.path)}">${label}</a>`;
|
|
142
|
+
}
|
|
143
|
+
function apiLinkHtml(route) {
|
|
144
|
+
const label = escapeHtml(route.path);
|
|
145
|
+
if (!isLinkablePath(route.path) || !route.methods.includes("GET")) return label;
|
|
146
|
+
return `<a href="${escapeHtml(route.path)}">${label}</a>`;
|
|
147
|
+
}
|
|
148
|
+
/** Dynamic patterns (`:id`, `*`) are not navigable as-is — render them as text. */
|
|
149
|
+
function isLinkablePath(path) {
|
|
150
|
+
return !path.includes(":") && !path.includes("*");
|
|
151
|
+
}
|
|
152
|
+
function escapeHtml(str) {
|
|
153
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
154
|
+
}
|
|
155
|
+
//#endregion
|
|
156
|
+
export { DEVTOOLS_JSON_PATH, DEVTOOLS_PATH, buildAppGraph, buildDevtoolsHtml, detectApiMethods, serializeApiRoutes, serializeAppRoutes };
|
package/dist/error-overlay.d.mts
CHANGED
|
@@ -4,13 +4,50 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Returns a standalone HTML document with inline styles and scripts.
|
|
6
6
|
* Not a Preact component — must render even when Preact itself fails.
|
|
7
|
+
*
|
|
8
|
+
* Dev-only: the overlay is served exclusively by the vite-plugin dev SSR
|
|
9
|
+
* middleware, so it can rely on Vite's built-in `/__open-in-editor`
|
|
10
|
+
* endpoint (launch-editor middleware) to make stack frames clickable.
|
|
7
11
|
*/
|
|
8
12
|
interface ErrorOverlayOptions {
|
|
9
13
|
message: string;
|
|
10
14
|
stack?: string;
|
|
11
15
|
routeId?: string;
|
|
12
16
|
file?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Project root (Vite's `server.config.root`). Used to resolve
|
|
19
|
+
* dev-server URL paths such as `/src/routes/home.tsx` to filesystem
|
|
20
|
+
* paths for the open-in-editor links.
|
|
21
|
+
*/
|
|
22
|
+
root?: string;
|
|
23
|
+
}
|
|
24
|
+
interface StackFrame {
|
|
25
|
+
/** The original stack line, unmodified. */
|
|
26
|
+
raw: string;
|
|
27
|
+
/** The exact `file:line:column` substring inside `raw`, when present. */
|
|
28
|
+
locationText?: string;
|
|
29
|
+
/** Normalized filesystem path suitable for `/__open-in-editor`. */
|
|
30
|
+
file?: string;
|
|
31
|
+
line?: number;
|
|
32
|
+
column?: number;
|
|
33
|
+
/** False for node_modules, `node:` internals, and Vite-internal frames. */
|
|
34
|
+
isApp: boolean;
|
|
13
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Parse a V8-style stack trace into frames. Non-frame lines (the message
|
|
38
|
+
* line, empty lines) are preserved as non-app frames without a location.
|
|
39
|
+
*/
|
|
40
|
+
declare function parseStackFrames(stack: string, options?: {
|
|
41
|
+
root?: string;
|
|
42
|
+
}): StackFrame[];
|
|
43
|
+
/**
|
|
44
|
+
* Normalize a stack-frame path to a filesystem path that Vite's
|
|
45
|
+
* `/__open-in-editor` endpoint can open. Handles `file://` URLs,
|
|
46
|
+
* `http://` dev-server URLs, `/@fs/` prefixes, Vite query suffixes
|
|
47
|
+
* (`?t=123`, `?pracht-client`), and root-relative dev URLs like
|
|
48
|
+
* `/src/routes/home.tsx` (joined onto `root` when provided).
|
|
49
|
+
*/
|
|
50
|
+
declare function normalizeStackFile(rawPath: string, root?: string): string | undefined;
|
|
14
51
|
declare function buildErrorOverlayHtml(options: ErrorOverlayOptions): string;
|
|
15
52
|
//#endregion
|
|
16
|
-
export { ErrorOverlayOptions, buildErrorOverlayHtml };
|
|
53
|
+
export { ErrorOverlayOptions, StackFrame, buildErrorOverlayHtml, normalizeStackFile, parseStackFrames };
|
package/dist/error-overlay.mjs
CHANGED
|
@@ -1,9 +1,75 @@
|
|
|
1
1
|
//#region src/error-overlay.ts
|
|
2
|
+
const FRAME_PARENS = /^\s*at\s+(?:async\s+)?.*?\((.*)\)\s*$/;
|
|
3
|
+
const FRAME_BARE = /^\s*at\s+(?:async\s+)?(.*?)\s*$/;
|
|
4
|
+
const LOCATION = /^(.*?):(\d+):(\d+)$/;
|
|
5
|
+
const WINDOWS_DRIVE_PATH = /^\/?[A-Za-z]:[\\/]/;
|
|
6
|
+
/**
|
|
7
|
+
* Parse a V8-style stack trace into frames. Non-frame lines (the message
|
|
8
|
+
* line, empty lines) are preserved as non-app frames without a location.
|
|
9
|
+
*/
|
|
10
|
+
function parseStackFrames(stack, options = {}) {
|
|
11
|
+
return stack.split("\n").map((line) => parseStackFrameLine(line, options.root));
|
|
12
|
+
}
|
|
13
|
+
function parseStackFrameLine(raw, root) {
|
|
14
|
+
const locationText = FRAME_PARENS.exec(raw)?.[1] ?? FRAME_BARE.exec(raw)?.[1];
|
|
15
|
+
if (!locationText) return {
|
|
16
|
+
raw,
|
|
17
|
+
isApp: false
|
|
18
|
+
};
|
|
19
|
+
const location = LOCATION.exec(locationText);
|
|
20
|
+
if (!location) return {
|
|
21
|
+
raw,
|
|
22
|
+
locationText,
|
|
23
|
+
isApp: !isInternalStackPath(locationText)
|
|
24
|
+
};
|
|
25
|
+
const [, rawPath, line, column] = location;
|
|
26
|
+
if (isInternalStackPath(rawPath)) return {
|
|
27
|
+
raw,
|
|
28
|
+
locationText,
|
|
29
|
+
isApp: false
|
|
30
|
+
};
|
|
31
|
+
return {
|
|
32
|
+
raw,
|
|
33
|
+
locationText,
|
|
34
|
+
file: normalizeStackFile(rawPath, root),
|
|
35
|
+
line: Number(line),
|
|
36
|
+
column: Number(column),
|
|
37
|
+
isApp: true
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function isInternalStackPath(path) {
|
|
41
|
+
return path === "native" || path === "<anonymous>" || path.includes("(") || path.startsWith("node:") || path.startsWith("internal/") || path.startsWith("virtual:") || path.includes("\0") || path.includes("/node_modules/") || path.includes("\\node_modules\\") || path.includes("/@vite/");
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Normalize a stack-frame path to a filesystem path that Vite's
|
|
45
|
+
* `/__open-in-editor` endpoint can open. Handles `file://` URLs,
|
|
46
|
+
* `http://` dev-server URLs, `/@fs/` prefixes, Vite query suffixes
|
|
47
|
+
* (`?t=123`, `?pracht-client`), and root-relative dev URLs like
|
|
48
|
+
* `/src/routes/home.tsx` (joined onto `root` when provided).
|
|
49
|
+
*/
|
|
50
|
+
function normalizeStackFile(rawPath, root) {
|
|
51
|
+
let path = rawPath;
|
|
52
|
+
if (path.startsWith("http://") || path.startsWith("https://") || path.startsWith("file://")) try {
|
|
53
|
+
const url = new URL(path);
|
|
54
|
+
path = decodeURIComponent(url.pathname);
|
|
55
|
+
} catch {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
path = path.split("?")[0].split("#")[0];
|
|
59
|
+
if (path.startsWith("/@fs/")) path = path.slice(4);
|
|
60
|
+
if (WINDOWS_DRIVE_PATH.test(path) && path.startsWith("/")) path = path.slice(1);
|
|
61
|
+
if (!path) return void 0;
|
|
62
|
+
if (root && path.startsWith("/") && !WINDOWS_DRIVE_PATH.test(path)) {
|
|
63
|
+
const normalizedRoot = root.endsWith("/") ? root.slice(0, -1) : root;
|
|
64
|
+
if (path !== normalizedRoot && !path.startsWith(`${normalizedRoot}/`)) return `${normalizedRoot}${path}`;
|
|
65
|
+
}
|
|
66
|
+
return path;
|
|
67
|
+
}
|
|
2
68
|
function buildErrorOverlayHtml(options) {
|
|
3
|
-
const { message, stack, routeId, file } = options;
|
|
4
|
-
const stackHtml = stack ? `<pre class="stack">${
|
|
69
|
+
const { message, stack, routeId, file, root } = options;
|
|
70
|
+
const stackHtml = stack ? `<pre class="stack">${renderStackFrames(parseStackFrames(stack, { root }))}</pre>` : "";
|
|
5
71
|
const routeHtml = routeId ? `<div class="meta"><span class="label">Route</span> <span class="value">${escapeHtml(routeId)}</span></div>` : "";
|
|
6
|
-
const fileHtml = file ? `<div class="meta"><span class="label">File</span>
|
|
72
|
+
const fileHtml = file ? `<div class="meta"><span class="label">File</span> ${renderFileValue(file, root)}</div>` : "";
|
|
7
73
|
return `<!DOCTYPE html>
|
|
8
74
|
<html>
|
|
9
75
|
<head>
|
|
@@ -75,6 +141,20 @@ function buildErrorOverlayHtml(options) {
|
|
|
75
141
|
word-break: break-word;
|
|
76
142
|
color: #ccc;
|
|
77
143
|
}
|
|
144
|
+
.frame-internal {
|
|
145
|
+
opacity: 0.45;
|
|
146
|
+
}
|
|
147
|
+
.editor-link {
|
|
148
|
+
color: #a0c4ff;
|
|
149
|
+
text-decoration: underline;
|
|
150
|
+
text-decoration-style: dotted;
|
|
151
|
+
text-underline-offset: 3px;
|
|
152
|
+
cursor: pointer;
|
|
153
|
+
}
|
|
154
|
+
.editor-link:hover {
|
|
155
|
+
color: #d0e2ff;
|
|
156
|
+
text-decoration-style: solid;
|
|
157
|
+
}
|
|
78
158
|
.hint {
|
|
79
159
|
margin-top: 24px;
|
|
80
160
|
font-size: 12px;
|
|
@@ -92,8 +172,19 @@ function buildErrorOverlayHtml(options) {
|
|
|
92
172
|
${routeHtml}
|
|
93
173
|
${fileHtml}
|
|
94
174
|
${stackHtml}
|
|
95
|
-
<div class="hint">Fix the error and save — the page will reload automatically.</div>
|
|
175
|
+
<div class="hint">Click a stack frame to open it in your editor. Fix the error and save — the page will reload automatically.</div>
|
|
96
176
|
</div>
|
|
177
|
+
<script>
|
|
178
|
+
// Open clicked stack frames in the editor via Vite's built-in
|
|
179
|
+
// /__open-in-editor endpoint (dev server only).
|
|
180
|
+
document.addEventListener("click", function (event) {
|
|
181
|
+
var target = event.target;
|
|
182
|
+
var link = target && target.closest ? target.closest("[data-editor-file]") : null;
|
|
183
|
+
if (!link) return;
|
|
184
|
+
event.preventDefault();
|
|
185
|
+
fetch("/__open-in-editor?file=" + encodeURIComponent(link.getAttribute("data-editor-file")));
|
|
186
|
+
});
|
|
187
|
+
<\/script>
|
|
97
188
|
<script>
|
|
98
189
|
// Auto-reload when Vite triggers a full reload (e.g. file saved after fix)
|
|
99
190
|
if (import.meta.hot) {
|
|
@@ -105,8 +196,46 @@ function buildErrorOverlayHtml(options) {
|
|
|
105
196
|
</body>
|
|
106
197
|
</html>`;
|
|
107
198
|
}
|
|
199
|
+
function renderStackFrames(frames) {
|
|
200
|
+
return frames.map(renderStackFrame).join("\n");
|
|
201
|
+
}
|
|
202
|
+
function renderStackFrame(frame) {
|
|
203
|
+
if (!frame.isApp) return frame.locationText ? `<span class="frame-internal">${escapeHtml(frame.raw)}</span>` : escapeHtml(frame.raw);
|
|
204
|
+
if (!frame.file || !frame.locationText) return escapeHtml(frame.raw);
|
|
205
|
+
const locationIndex = frame.raw.indexOf(frame.locationText);
|
|
206
|
+
if (locationIndex === -1) return escapeHtml(frame.raw);
|
|
207
|
+
const prefix = frame.raw.slice(0, locationIndex);
|
|
208
|
+
const suffix = frame.raw.slice(locationIndex + frame.locationText.length);
|
|
209
|
+
const link = renderEditorLink(frame.file, frame.line, frame.column, frame.locationText);
|
|
210
|
+
return `${escapeHtml(prefix)}${link}${escapeHtml(suffix)}`;
|
|
211
|
+
}
|
|
212
|
+
function renderEditorLink(file, line, column, label) {
|
|
213
|
+
let target = file;
|
|
214
|
+
if (line !== void 0) {
|
|
215
|
+
target += `:${line}`;
|
|
216
|
+
if (column !== void 0) target += `:${column}`;
|
|
217
|
+
}
|
|
218
|
+
return `<a class="editor-link" href="#" data-editor-file="${escapeHtml(target)}">${escapeHtml(label)}</a>`;
|
|
219
|
+
}
|
|
220
|
+
function renderFileValue(file, root) {
|
|
221
|
+
const resolved = resolveEditorFilePath(file, root);
|
|
222
|
+
if (!resolved) return `<span class="value">${escapeHtml(file)}</span>`;
|
|
223
|
+
return `<a class="value editor-link" href="#" data-editor-file="${escapeHtml(resolved)}">${escapeHtml(file)}</a>`;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Resolve the `file` metadata option (typically a manifest-relative path
|
|
227
|
+
* such as `./routes/home.tsx`) to a filesystem path for open-in-editor.
|
|
228
|
+
*/
|
|
229
|
+
function resolveEditorFilePath(file, root) {
|
|
230
|
+
if (file.startsWith("./")) {
|
|
231
|
+
if (!root) return void 0;
|
|
232
|
+
return `${root.endsWith("/") ? root.slice(0, -1) : root}/src/${file.slice(2)}`;
|
|
233
|
+
}
|
|
234
|
+
if (file.startsWith("../")) return;
|
|
235
|
+
return normalizeStackFile(file, root);
|
|
236
|
+
}
|
|
108
237
|
function escapeHtml(str) {
|
|
109
238
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
110
239
|
}
|
|
111
240
|
//#endregion
|
|
112
|
-
export { buildErrorOverlayHtml };
|
|
241
|
+
export { buildErrorOverlayHtml, normalizeStackFile, parseStackFrames };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { $ as
|
|
2
|
-
import {
|
|
3
|
-
import { n as
|
|
4
|
-
import {
|
|
5
|
-
import { i as
|
|
6
|
-
import {
|
|
1
|
+
import { $ as RouteParamInput, A as MiddlewareNext, B as RenderMode, C as LoaderArgs, D as MiddlewareArgs, F as PrachtApp, G as RouteConfig, H as ResolvedPrachtApp, I as PrachtAppConfig, J as RouteId, K as RouteDataFor, L as PrachtHttpError, M as ModuleRef, N as ModuleRegistry, O as MiddlewareFn, P as NavigateOptions, Q as RouteModule, R as PrefetchStrategy, S as LinkPrefetchStrategy, T as LoaderFn, U as ResolvedRoute, V as ResolvedApiRoute, W as RouteComponentProps, X as RouteMatch, Y as RouteLoaderData, Z as RouteMeta, _ as HrefArgs, a as ApiRouteModule, at as RouteTarget, b as HrefRouteDefinition, c as DataModule, ct as SearchParamValue, d as GroupMeta, dt as ShellProps, et as RouteParams, f as HeadArgs, ft as TimeRevalidatePolicy, g as HeadersArgs, h as HeadScriptDescriptor, i as ApiRouteMatch, j as ModuleImporter, k as MiddlewareModule, l as ErrorBoundaryProps, lt as SearchParamsInput, m as HeadMetadata, n as ApiRouteArgs, nt as RouteRevalidate, o as BaseRouteArgs, ot as RouteTreeNode, p as HeadAttributes, q as RouteDefinition, r as ApiRouteHandler, rt as RouteSearchFor, s as BuildHrefOptions, st as SearchParamPrimitive, t as ApiConfig, tt as RouteParamsFor, u as GroupDefinition, ut as ShellModule, v as HrefFn, w as LoaderData, x as HttpMethod, y as HrefOptions, z as Register } from "./types-B-sfVjaH.mjs";
|
|
2
|
+
import { a as matchApiRoute, c as resolveApp, i as group, l as route, n as buildPathFromSegments, o as matchAppRoute, r as defineApp, s as resolveApiRoutes, t as buildHref, u as timeRevalidate } from "./app-BN3pjxtw.mjs";
|
|
3
|
+
import { _ as NavigationLocation, c as Link, d as useLocation, f as useNavigation, g as Navigation, h as useRouteData, l as LinkProps, m as useRevalidate, n as redirect, o as Form, p as useParams, r as RouteStateResult, s as FormProps, t as RedirectOptions, u as Location, v as createHref } from "./runtime-middleware-pYubngnL.mjs";
|
|
4
|
+
import { i as forwardRef, n as prefetch, r as useIsHydrated, t as PrefetchFn } from "./prefetch-api-Bf-P7XFo.mjs";
|
|
5
|
+
import { a as startApp, c as SerializedRouteError, i as readHydrationState, n as PrachtRuntimeProvider, o as PrachtRuntimeDiagnosticPhase, r as StartAppOptions, s as PrachtRuntimeDiagnostics, t as PrachtHydrationState } from "./runtime-context-BYBwVA0M.mjs";
|
|
6
|
+
import { i as useNavigate, n as NavigateFn, r as initClientRouter, t as InitClientRouterOptions } from "./router-B_Aj5TtY.mjs";
|
|
7
|
+
import { a as buildAppGraph, c as serializeAppRoutes, i as AppGraphRoute, n as AppGraphApiRoute, o as detectApiMethods, r as AppGraphModuleAccess, s as serializeApiRoutes, t as AppGraph } from "./app-graph-DBoDsHJ5.mjs";
|
|
8
|
+
import { a as prerenderApp, c as applyDefaultSecurityHeaders, i as PrerenderResult, l as PrachtPhaseTimings, n as PrerenderAppOptions, o as HandlePrachtRequestOptions, r as PrerenderAppResult, s as handlePrachtRequest, t as ISGManifestEntry, u as formatServerTimingHeader } from "./prerender-BV42B9jK.mjs";
|
|
7
9
|
import { Suspense, lazy } from "preact-suspense";
|
|
8
|
-
export { type ApiConfig, type ApiRouteArgs, type ApiRouteHandler, type ApiRouteMatch, type ApiRouteModule, type BaseRouteArgs, type BuildHrefOptions, type DataModule, type ErrorBoundaryProps, Form, type FormProps, type GroupDefinition, type GroupMeta, type HandlePrachtRequestOptions, type HeadArgs, type HeadAttributes, type HeadMetadata, type HeadScriptDescriptor, type HeadersArgs, type HrefArgs, type HrefFn, type HrefOptions, type HrefRouteDefinition, type HttpMethod, type ISGManifestEntry, type InitClientRouterOptions, Link, type LinkProps, type LoaderArgs, type LoaderData, type LoaderFn, type Location, type MiddlewareArgs, type MiddlewareFn, type MiddlewareModule, type MiddlewareNext, type ModuleImporter, type ModuleRef, type ModuleRegistry, type NavigateFn, type NavigateOptions, type PrachtApp, type PrachtAppConfig, PrachtHttpError, type PrachtHydrationState, type PrachtRuntimeDiagnosticPhase, type PrachtRuntimeDiagnostics, PrachtRuntimeProvider, type PrefetchStrategy, type PrerenderAppOptions, type PrerenderAppResult, type PrerenderResult, type RedirectOptions, type Register, type RenderMode, type ResolvedApiRoute, type ResolvedPrachtApp, type ResolvedRoute, type RouteComponentProps, type RouteConfig, type RouteDefinition, type RouteId, type RouteMatch, type RouteMeta, type RouteModule, type RouteParamInput, type RouteParams, type RouteParamsFor, type RouteRevalidate, type RouteSearchFor, type RouteStateResult, type RouteTarget, type RouteTreeNode, type SearchParamPrimitive, type SearchParamValue, type SearchParamsInput, type SerializedRouteError, type ShellModule, type ShellProps, type StartAppOptions, Suspense, type TimeRevalidatePolicy, applyDefaultSecurityHeaders, buildHref, buildPathFromSegments, createHref, defineApp, forwardRef, group, handlePrachtRequest, initClientRouter, lazy, matchApiRoute, matchAppRoute, prerenderApp, readHydrationState, redirect, resolveApiRoutes, resolveApp, route, startApp, timeRevalidate, useIsHydrated, useLocation, useNavigate, useParams, useRevalidate, useRouteData };
|
|
10
|
+
export { type ApiConfig, type ApiRouteArgs, type ApiRouteHandler, type ApiRouteMatch, type ApiRouteModule, type AppGraph, type AppGraphApiRoute, type AppGraphModuleAccess, type AppGraphRoute, type BaseRouteArgs, type BuildHrefOptions, type DataModule, type ErrorBoundaryProps, Form, type FormProps, type GroupDefinition, type GroupMeta, type HandlePrachtRequestOptions, type HeadArgs, type HeadAttributes, type HeadMetadata, type HeadScriptDescriptor, type HeadersArgs, type HrefArgs, type HrefFn, type HrefOptions, type HrefRouteDefinition, type HttpMethod, type ISGManifestEntry, type InitClientRouterOptions, Link, type LinkPrefetchStrategy, type LinkProps, type LoaderArgs, type LoaderData, type LoaderFn, type Location, type MiddlewareArgs, type MiddlewareFn, type MiddlewareModule, type MiddlewareNext, type ModuleImporter, type ModuleRef, type ModuleRegistry, type NavigateFn, type NavigateOptions, type Navigation, type NavigationLocation, type PrachtApp, type PrachtAppConfig, PrachtHttpError, type PrachtHydrationState, type PrachtPhaseTimings, type PrachtRuntimeDiagnosticPhase, type PrachtRuntimeDiagnostics, PrachtRuntimeProvider, type PrefetchFn, type PrefetchStrategy, type PrerenderAppOptions, type PrerenderAppResult, type PrerenderResult, type RedirectOptions, type Register, type RenderMode, type ResolvedApiRoute, type ResolvedPrachtApp, type ResolvedRoute, type RouteComponentProps, type RouteConfig, type RouteDataFor, type RouteDefinition, type RouteId, type RouteLoaderData, type RouteMatch, type RouteMeta, type RouteModule, type RouteParamInput, type RouteParams, type RouteParamsFor, type RouteRevalidate, type RouteSearchFor, type RouteStateResult, type RouteTarget, type RouteTreeNode, type SearchParamPrimitive, type SearchParamValue, type SearchParamsInput, type SerializedRouteError, type ShellModule, type ShellProps, type StartAppOptions, Suspense, type TimeRevalidatePolicy, applyDefaultSecurityHeaders, buildAppGraph, buildHref, buildPathFromSegments, createHref, defineApp, detectApiMethods, formatServerTimingHeader, forwardRef, group, handlePrachtRequest, initClientRouter, lazy, matchApiRoute, matchAppRoute, prefetch, prerenderApp, readHydrationState, redirect, resolveApiRoutes, resolveApp, route, serializeApiRoutes, serializeAppRoutes, startApp, timeRevalidate, useIsHydrated, useLocation, useNavigate, useNavigation, useParams, useRevalidate, useRouteData };
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { a as matchApiRoute, c as resolveApp, i as group, l as route, n as buildPathFromSegments, o as matchAppRoute, r as defineApp, s as resolveApiRoutes, t as buildHref, u as timeRevalidate } from "./app-
|
|
2
|
-
import {
|
|
3
|
-
import { t as
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
1
|
+
import { a as matchApiRoute, c as resolveApp, i as group, l as route, n as buildPathFromSegments, o as matchAppRoute, r as defineApp, s as resolveApiRoutes, t as buildHref, u as timeRevalidate } from "./app-CGRKjZ6Z.mjs";
|
|
2
|
+
import { C as createHref, a as useNavigation, c as useRouteData, d as redirect, i as useLocation, n as Form, o as useParams, r as Link, s as useRevalidate, t as PrachtHttpError, v as applyDefaultSecurityHeaders } from "./types-DEPXdbyp.mjs";
|
|
3
|
+
import { i as serializeAppRoutes, n as detectApiMethods, r as serializeApiRoutes, t as buildAppGraph } from "./app-graph-BlaCcGUZ.mjs";
|
|
4
|
+
import { t as forwardRef } from "./forwardRef-B0cSkHwH.mjs";
|
|
5
|
+
import { n as useNavigate, r as useIsHydrated, t as initClientRouter } from "./router-E5Wh-dOR.mjs";
|
|
6
|
+
import { l as readHydrationState, s as PrachtRuntimeProvider, u as startApp } from "./navigation-state-BjwXTeVz.mjs";
|
|
7
|
+
import { n as handlePrachtRequest, r as formatServerTimingHeader, t as prerenderApp } from "./prerender-C8BpANz3.mjs";
|
|
8
|
+
import { t as prefetch } from "./prefetch-api-ChSCTbEB.mjs";
|
|
7
9
|
import { Suspense, lazy } from "preact-suspense";
|
|
8
|
-
export { Form, Link, PrachtHttpError, PrachtRuntimeProvider, Suspense, applyDefaultSecurityHeaders, buildHref, buildPathFromSegments, createHref, defineApp, forwardRef, group, handlePrachtRequest, initClientRouter, lazy, matchApiRoute, matchAppRoute, prerenderApp, readHydrationState, redirect, resolveApiRoutes, resolveApp, route, startApp, timeRevalidate, useIsHydrated, useLocation, useNavigate, useParams, useRevalidate, useRouteData };
|
|
10
|
+
export { Form, Link, PrachtHttpError, PrachtRuntimeProvider, Suspense, applyDefaultSecurityHeaders, buildAppGraph, buildHref, buildPathFromSegments, createHref, defineApp, detectApiMethods, formatServerTimingHeader, forwardRef, group, handlePrachtRequest, initClientRouter, lazy, matchApiRoute, matchAppRoute, prefetch, prerenderApp, readHydrationState, redirect, resolveApiRoutes, resolveApp, route, serializeApiRoutes, serializeAppRoutes, startApp, timeRevalidate, useIsHydrated, useLocation, useNavigate, useNavigation, useParams, useRevalidate, useRouteData };
|
package/dist/manifest.d.mts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { B as RenderMode, F as PrachtApp, G as RouteConfig, I as PrachtAppConfig, M as ModuleRef, Z as RouteMeta, d as GroupMeta, ft as TimeRevalidatePolicy, nt as RouteRevalidate, ot as RouteTreeNode, q as RouteDefinition, u as GroupDefinition } from "./types-B-sfVjaH.mjs";
|
|
2
|
+
import { i as group, l as route, r as defineApp, u as timeRevalidate } from "./app-BN3pjxtw.mjs";
|
|
2
3
|
export { type GroupDefinition, type GroupMeta, type ModuleRef, type PrachtApp, type PrachtAppConfig, type RenderMode, type RouteConfig, type RouteDefinition, type RouteMeta, type RouteRevalidate, type RouteTreeNode, type TimeRevalidatePolicy, defineApp, group, route, timeRevalidate };
|
package/dist/manifest.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as group, l as route, r as defineApp, u as timeRevalidate } from "./app-
|
|
1
|
+
import { i as group, l as route, r as defineApp, u as timeRevalidate } from "./app-CGRKjZ6Z.mjs";
|
|
2
2
|
export { defineApp, group, route, timeRevalidate };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { f as EMPTY_ROUTE_PARAMS, p as HYDRATION_STATE_ELEMENT_ID } from "./prefetch-cache-BNLEp9H5.mjs";
|
|
2
2
|
import { createContext, h } from "preact";
|
|
3
3
|
import { useEffect, useMemo, useState } from "preact/hooks";
|
|
4
4
|
//#region src/runtime-errors.ts
|
|
@@ -161,4 +161,76 @@ function registerRuntimeRoutes(routes) {
|
|
|
161
161
|
globalThis.__PRACHT_ROUTE_DEFINITIONS__ = routes;
|
|
162
162
|
}
|
|
163
163
|
//#endregion
|
|
164
|
-
|
|
164
|
+
//#region src/navigation-state.ts
|
|
165
|
+
const IDLE_NAVIGATION = { state: "idle" };
|
|
166
|
+
let currentNavigation = IDLE_NAVIGATION;
|
|
167
|
+
let navigationToken = 0;
|
|
168
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
169
|
+
function getNavigation() {
|
|
170
|
+
return currentNavigation;
|
|
171
|
+
}
|
|
172
|
+
function subscribeToNavigation(listener) {
|
|
173
|
+
listeners.add(listener);
|
|
174
|
+
return () => {
|
|
175
|
+
listeners.delete(listener);
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function emit() {
|
|
179
|
+
const snapshot = Array.from(listeners);
|
|
180
|
+
for (const listener of snapshot) listener(currentNavigation);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Mark a navigation as in-flight. Returns a token that must be passed to
|
|
184
|
+
* `settleNavigation()` — settling is a no-op when a newer navigation or
|
|
185
|
+
* submission has started in the meantime, so superseded navigations never
|
|
186
|
+
* stomp the state of the one that replaced them.
|
|
187
|
+
*/
|
|
188
|
+
function beginLoadingNavigation(location) {
|
|
189
|
+
currentNavigation = {
|
|
190
|
+
state: "loading",
|
|
191
|
+
location
|
|
192
|
+
};
|
|
193
|
+
emit();
|
|
194
|
+
return ++navigationToken;
|
|
195
|
+
}
|
|
196
|
+
function beginSubmittingNavigation(location, formData) {
|
|
197
|
+
currentNavigation = {
|
|
198
|
+
state: "submitting",
|
|
199
|
+
location,
|
|
200
|
+
formData
|
|
201
|
+
};
|
|
202
|
+
emit();
|
|
203
|
+
return ++navigationToken;
|
|
204
|
+
}
|
|
205
|
+
function settleNavigation(token) {
|
|
206
|
+
if (token !== navigationToken) return;
|
|
207
|
+
if (currentNavigation.state === "idle") return;
|
|
208
|
+
currentNavigation = IDLE_NAVIGATION;
|
|
209
|
+
emit();
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Parse a navigation target (relative or absolute) into the location shape
|
|
213
|
+
* exposed through `useNavigation()`.
|
|
214
|
+
*/
|
|
215
|
+
function createNavigationLocation(url) {
|
|
216
|
+
const base = typeof window !== "undefined" ? window.location.href : "http://pracht.local";
|
|
217
|
+
let parsed;
|
|
218
|
+
try {
|
|
219
|
+
parsed = new URL(url, base);
|
|
220
|
+
} catch {
|
|
221
|
+
return {
|
|
222
|
+
hash: "",
|
|
223
|
+
href: url,
|
|
224
|
+
pathname: url,
|
|
225
|
+
search: ""
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
return {
|
|
229
|
+
hash: parsed.hash,
|
|
230
|
+
href: parsed.pathname + parsed.search + parsed.hash,
|
|
231
|
+
pathname: parsed.pathname,
|
|
232
|
+
search: parsed.search
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
//#endregion
|
|
236
|
+
export { settleNavigation as a, RouteDataContext as c, buildRuntimeDiagnostics as d, createSerializedRouteError as f, shouldExposeServerErrors as h, getNavigation as i, readHydrationState as l, normalizeRouteError as m, beginSubmittingNavigation as n, subscribeToNavigation as o, deserializeRouteError as p, createNavigationLocation as r, PrachtRuntimeProvider as s, beginLoadingNavigation as t, startApp as u };
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { o as matchAppRoute } from "./app-
|
|
2
|
-
import {
|
|
1
|
+
import { o as matchAppRoute } from "./app-CGRKjZ6Z.mjs";
|
|
2
|
+
import { m as PREFETCH_ATTRIBUTE, o as trimMapToSize } from "./prefetch-cache-BNLEp9H5.mjs";
|
|
3
|
+
import { n as prefetchRouteState } from "./prefetch-api-ChSCTbEB.mjs";
|
|
3
4
|
//#region src/prefetch.ts
|
|
4
5
|
const MAX_MATCH_CACHE_ENTRIES = 250;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
6
|
+
const LINK_PREFETCH_STRATEGIES = new Set([
|
|
7
|
+
"none",
|
|
8
|
+
"hover",
|
|
9
|
+
"intent",
|
|
10
|
+
"viewport",
|
|
11
|
+
"render"
|
|
12
|
+
]);
|
|
13
13
|
function setupPrefetching(app, warmModules) {
|
|
14
14
|
let hoverTimer = null;
|
|
15
|
-
const
|
|
15
|
+
const processedAnchors = /* @__PURE__ */ new WeakSet();
|
|
16
16
|
const matchCache = /* @__PURE__ */ new Map();
|
|
17
17
|
function getRoutePathname(url) {
|
|
18
18
|
try {
|
|
@@ -50,17 +50,29 @@ function setupPrefetching(app, warmModules) {
|
|
|
50
50
|
trimMapToSize(matchCache, MAX_MATCH_CACHE_ENTRIES);
|
|
51
51
|
return entry;
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Per-anchor `data-pracht-prefetch` (rendered by `<Link prefetch>`) wins
|
|
55
|
+
* over the route-level strategy; unmatched hrefs are never prefetched.
|
|
56
|
+
*/
|
|
57
|
+
function getAnchorStrategy(anchor, href) {
|
|
58
|
+
const entry = getMatchEntry(href);
|
|
59
|
+
if (!entry.match) return "none";
|
|
60
|
+
const override = anchor.getAttribute(PREFETCH_ATTRIBUTE);
|
|
61
|
+
if (override && LINK_PREFETCH_STRATEGIES.has(override)) return override;
|
|
62
|
+
return entry.strategy;
|
|
63
|
+
}
|
|
53
64
|
function prefetchHref(href) {
|
|
54
65
|
const match = getMatchEntry(href).match;
|
|
55
|
-
|
|
56
|
-
|
|
66
|
+
if (!match) return;
|
|
67
|
+
prefetchRouteState(href, match.route);
|
|
68
|
+
if (warmModules) warmModules(match);
|
|
57
69
|
}
|
|
58
70
|
document.addEventListener("mouseenter", (e) => {
|
|
59
71
|
const anchor = e.target.closest?.("a");
|
|
60
72
|
if (!anchor) return;
|
|
61
73
|
const href = getInternalHref(anchor);
|
|
62
74
|
if (!href) return;
|
|
63
|
-
const strategy =
|
|
75
|
+
const strategy = getAnchorStrategy(anchor, href);
|
|
64
76
|
if (strategy !== "hover" && strategy !== "intent") return;
|
|
65
77
|
if (hoverTimer) clearTimeout(hoverTimer);
|
|
66
78
|
hoverTimer = setTimeout(() => {
|
|
@@ -79,36 +91,41 @@ function setupPrefetching(app, warmModules) {
|
|
|
79
91
|
if (!anchor) return;
|
|
80
92
|
const href = getInternalHref(anchor);
|
|
81
93
|
if (!href) return;
|
|
82
|
-
const strategy =
|
|
94
|
+
const strategy = getAnchorStrategy(anchor, href);
|
|
83
95
|
if (strategy !== "hover" && strategy !== "intent") return;
|
|
84
96
|
prefetchHref(href);
|
|
85
97
|
}, true);
|
|
86
|
-
|
|
87
|
-
const observer = new IntersectionObserver((entries) => {
|
|
98
|
+
const observer = typeof IntersectionObserver === "undefined" ? null : new IntersectionObserver((entries) => {
|
|
88
99
|
for (const entry of entries) {
|
|
89
100
|
if (!entry.isIntersecting) continue;
|
|
90
101
|
const anchor = entry.target;
|
|
91
102
|
const href = getInternalHref(anchor);
|
|
92
103
|
if (!href) continue;
|
|
93
104
|
prefetchHref(href);
|
|
94
|
-
observer
|
|
105
|
+
observer?.unobserve(anchor);
|
|
95
106
|
}
|
|
96
107
|
}, { rootMargin: "200px" });
|
|
97
|
-
function
|
|
98
|
-
if (
|
|
108
|
+
function processAnchor(anchor) {
|
|
109
|
+
if (processedAnchors.has(anchor)) return;
|
|
99
110
|
const href = getInternalHref(anchor);
|
|
100
111
|
if (!href) return;
|
|
101
|
-
|
|
102
|
-
|
|
112
|
+
const strategy = getAnchorStrategy(anchor, href);
|
|
113
|
+
if (strategy === "render") {
|
|
114
|
+
processedAnchors.add(anchor);
|
|
115
|
+
prefetchHref(href);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (strategy !== "viewport" || !observer) return;
|
|
119
|
+
processedAnchors.add(anchor);
|
|
103
120
|
observer.observe(anchor);
|
|
104
121
|
}
|
|
105
|
-
function
|
|
106
|
-
if (root instanceof HTMLAnchorElement)
|
|
107
|
-
for (const anchor of root.querySelectorAll("a[href]"))
|
|
122
|
+
function processAnchors(root) {
|
|
123
|
+
if (root instanceof HTMLAnchorElement) processAnchor(root);
|
|
124
|
+
for (const anchor of root.querySelectorAll("a[href]")) processAnchor(anchor);
|
|
108
125
|
}
|
|
109
|
-
|
|
126
|
+
processAnchors(document.body);
|
|
110
127
|
new MutationObserver((records) => {
|
|
111
|
-
for (const record of records) for (const node of record.addedNodes) if (node instanceof HTMLElement || node instanceof DocumentFragment)
|
|
128
|
+
for (const record of records) for (const node of record.addedNodes) if (node instanceof HTMLElement || node instanceof DocumentFragment) processAnchors(node);
|
|
112
129
|
}).observe(document.body, {
|
|
113
130
|
childList: true,
|
|
114
131
|
subtree: true
|