@alignable/bifrost 0.0.11 → 0.0.13
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/dist/index.d.ts +6 -22
- package/dist/index.d.ts.map +1 -1
- package/dist/lib/PageShell.d.ts.map +1 -1
- package/dist/lib/getElementAttributes.d.ts +2 -0
- package/dist/lib/getElementAttributes.d.ts.map +1 -0
- package/dist/lib/getElementAttributes.js +7 -0
- package/dist/lib/turbolinks/adapter.d.ts +17 -0
- package/dist/lib/turbolinks/adapter.d.ts.map +1 -0
- package/dist/lib/turbolinks/adapter.js +1 -0
- package/dist/lib/turbolinks/browser_adapter.d.ts +28 -0
- package/dist/lib/turbolinks/browser_adapter.d.ts.map +1 -0
- package/dist/lib/turbolinks/browser_adapter.js +68 -0
- package/dist/lib/turbolinks/controller.d.ts +75 -0
- package/dist/lib/turbolinks/controller.d.ts.map +1 -0
- package/dist/lib/turbolinks/controller.js +219 -0
- package/dist/lib/turbolinks/head_details.d.ts +24 -0
- package/dist/lib/turbolinks/head_details.d.ts.map +1 -0
- package/dist/lib/turbolinks/head_details.js +97 -0
- package/dist/lib/turbolinks/index.d.ts +14 -0
- package/dist/lib/turbolinks/index.d.ts.map +1 -0
- package/dist/lib/turbolinks/index.js +55 -0
- package/dist/lib/turbolinks/location.d.ts +23 -0
- package/dist/lib/turbolinks/location.d.ts.map +1 -0
- package/dist/lib/turbolinks/location.js +75 -0
- package/dist/lib/turbolinks/lruCache.d.ts.map +1 -0
- package/dist/lib/turbolinks/mergeHead.d.ts +2 -0
- package/dist/lib/turbolinks/mergeHead.d.ts.map +1 -0
- package/dist/lib/{mergeHead.js → turbolinks/mergeHead.js} +27 -33
- package/dist/lib/turbolinks/progress_bar.d.ts +24 -0
- package/dist/lib/turbolinks/progress_bar.d.ts.map +1 -0
- package/dist/lib/turbolinks/progress_bar.js +99 -0
- package/dist/lib/turbolinks/types.d.ts +7 -0
- package/dist/lib/turbolinks/types.d.ts.map +1 -0
- package/dist/lib/turbolinks/types.js +3 -0
- package/dist/lib/turbolinks/util.d.ts +18 -0
- package/dist/lib/turbolinks/util.d.ts.map +1 -0
- package/dist/lib/turbolinks/util.js +125 -0
- package/dist/lib/turbolinks/visit.d.ts +55 -0
- package/dist/lib/turbolinks/visit.d.ts.map +1 -0
- package/dist/lib/turbolinks/visit.js +163 -0
- package/dist/proxy/pages/onRenderClient.d.ts.map +1 -1
- package/dist/proxy/pages/onRenderClient.js +40 -29
- package/dist/proxy/pages/onRenderHtml.d.ts.map +1 -1
- package/dist/proxy/pages/onRenderHtml.js +19 -5
- package/dist/proxy/pages/restorationVisit/+config.d.ts +2 -0
- package/dist/proxy/pages/restorationVisit/+config.d.ts.map +1 -1
- package/dist/proxy/pages/restorationVisit/+config.js +3 -0
- package/dist/proxy/pages/restorationVisit/onRenderClient.d.ts +3 -0
- package/dist/proxy/pages/restorationVisit/onRenderClient.d.ts.map +1 -0
- package/dist/proxy/pages/restorationVisit/onRenderClient.js +41 -0
- package/dist/renderer/getConfigOrPageContext.d.ts +8 -0
- package/dist/renderer/getConfigOrPageContext.d.ts.map +1 -0
- package/dist/renderer/getConfigOrPageContext.js +6 -0
- package/dist/renderer/onBeforeRoute.d.ts +1 -6
- package/dist/renderer/onBeforeRoute.d.ts.map +1 -1
- package/dist/renderer/onBeforeRoute.js +19 -11
- package/dist/renderer/onRenderClient.d.ts.map +1 -1
- package/dist/renderer/onRenderClient.js +31 -24
- package/dist/renderer/onRenderHtml.d.ts.map +1 -1
- package/dist/renderer/onRenderHtml.js +15 -18
- package/dist/renderer/utils/buildHead.d.ts +4 -0
- package/dist/renderer/utils/buildHead.d.ts.map +1 -0
- package/dist/renderer/utils/buildHead.js +10 -0
- package/dist/types/internal.d.ts +26 -23
- package/dist/types/internal.d.ts.map +1 -1
- package/package.json +13 -5
- package/dist/lib/dispatchTurbolinks.d.ts +0 -28
- package/dist/lib/dispatchTurbolinks.d.ts.map +0 -1
- package/dist/lib/dispatchTurbolinks.js +0 -5
- package/dist/lib/domUtils.d.ts +0 -3
- package/dist/lib/domUtils.d.ts.map +0 -1
- package/dist/lib/domUtils.js +0 -34
- package/dist/lib/linkInterceptor.d.ts +0 -2
- package/dist/lib/linkInterceptor.d.ts.map +0 -1
- package/dist/lib/linkInterceptor.js +0 -74
- package/dist/lib/lruCache.d.ts.map +0 -1
- package/dist/lib/mergeHead.d.ts +0 -2
- package/dist/lib/mergeHead.d.ts.map +0 -1
- package/dist/lib/navigateAnywhere.d.ts +0 -3
- package/dist/lib/navigateAnywhere.d.ts.map +0 -1
- package/dist/lib/navigateAnywhere.js +0 -15
- package/dist/lib/snapshots.d.ts +0 -9
- package/dist/lib/snapshots.d.ts.map +0 -1
- package/dist/lib/snapshots.js +0 -50
- package/dist/lib/turbolinks.d.ts +0 -5
- package/dist/lib/turbolinks.d.ts.map +0 -1
- package/dist/lib/turbolinks.js +0 -14
- package/dist/renderer/getDocumentProps.d.ts +0 -3
- package/dist/renderer/getDocumentProps.d.ts.map +0 -1
- package/dist/renderer/getDocumentProps.js +0 -3
- package/dist/renderer/utils/formatMetaObject.d.ts +0 -4
- package/dist/renderer/utils/formatMetaObject.d.ts.map +0 -1
- package/dist/renderer/utils/formatMetaObject.js +0 -3
- /package/dist/lib/{lruCache.d.ts → turbolinks/lruCache.d.ts} +0 -0
- /package/dist/lib/{lruCache.js → turbolinks/lruCache.js} +0 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { navigate } from "vite-plugin-ssr/client/router";
|
|
2
|
+
import { uuid } from "./util";
|
|
3
|
+
export var TimingMetric;
|
|
4
|
+
(function (TimingMetric) {
|
|
5
|
+
TimingMetric["visitStart"] = "visitStart";
|
|
6
|
+
TimingMetric["requestStart"] = "requestStart";
|
|
7
|
+
TimingMetric["requestEnd"] = "requestEnd";
|
|
8
|
+
TimingMetric["visitEnd"] = "visitEnd";
|
|
9
|
+
})(TimingMetric || (TimingMetric = {}));
|
|
10
|
+
export var VisitState;
|
|
11
|
+
(function (VisitState) {
|
|
12
|
+
VisitState["initialized"] = "initialized";
|
|
13
|
+
VisitState["started"] = "started";
|
|
14
|
+
VisitState["canceled"] = "canceled";
|
|
15
|
+
VisitState["failed"] = "failed";
|
|
16
|
+
VisitState["completed"] = "completed";
|
|
17
|
+
})(VisitState || (VisitState = {}));
|
|
18
|
+
export class Visit {
|
|
19
|
+
constructor(controller, location, action, restorationIdentifier = uuid()) {
|
|
20
|
+
this.identifier = uuid();
|
|
21
|
+
this.timingMetrics = {};
|
|
22
|
+
this.progress = 0;
|
|
23
|
+
this.snapshotCached = false;
|
|
24
|
+
this.state = VisitState.initialized;
|
|
25
|
+
this.requestInFlight = false;
|
|
26
|
+
this.controller = controller;
|
|
27
|
+
this.location = location;
|
|
28
|
+
this.action = action;
|
|
29
|
+
this.adapter = controller.adapter;
|
|
30
|
+
this.restorationIdentifier = restorationIdentifier;
|
|
31
|
+
}
|
|
32
|
+
start() {
|
|
33
|
+
if (this.state == VisitState.initialized) {
|
|
34
|
+
this.recordTimingMetric(TimingMetric.visitStart);
|
|
35
|
+
this.state = VisitState.started;
|
|
36
|
+
this.adapter.visitStarted(this);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
cancel() {
|
|
40
|
+
if (this.state == VisitState.started) {
|
|
41
|
+
this.requestInFlight = false;
|
|
42
|
+
this.cancelRender();
|
|
43
|
+
this.state = VisitState.canceled;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
complete() {
|
|
47
|
+
if (this.state == VisitState.started) {
|
|
48
|
+
this.recordTimingMetric(TimingMetric.visitEnd);
|
|
49
|
+
this.state = VisitState.completed;
|
|
50
|
+
this.adapter.visitCompleted(this);
|
|
51
|
+
this.controller.visitCompleted(this);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
fail() {
|
|
55
|
+
if (this.state == VisitState.started) {
|
|
56
|
+
this.state = VisitState.failed;
|
|
57
|
+
this.adapter.visitFailed(this);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
changeHistory() {
|
|
61
|
+
// no-op since issueRequest calls navigate which handles all of this already
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
issueRequest() {
|
|
65
|
+
if (!this.requestInFlight) {
|
|
66
|
+
if (this.shouldIssueRequest()) {
|
|
67
|
+
const url = new URL(this.location.toString(), this.location.getOrigin());
|
|
68
|
+
navigate(url.pathname + url.hash + url.search, {
|
|
69
|
+
overwriteLastHistoryEntry: this.action === "replace",
|
|
70
|
+
}).catch(console.error);
|
|
71
|
+
this.progress = 0;
|
|
72
|
+
this.requestInFlight = true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
getCachedSnapshot() {
|
|
77
|
+
const snapshot = this.controller.getCachedSnapshotForLocation(this.location);
|
|
78
|
+
if (snapshot) {
|
|
79
|
+
if (this.action == "restore") {
|
|
80
|
+
return snapshot;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
hasCachedSnapshot() {
|
|
85
|
+
return this.getCachedSnapshot() != null;
|
|
86
|
+
}
|
|
87
|
+
loadCachedSnapshot() {
|
|
88
|
+
// no-op since issueRequest calls navigate which handles all of this already
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
loadResponse() {
|
|
92
|
+
this.render(async () => {
|
|
93
|
+
if (!this.renderFn)
|
|
94
|
+
throw new Error("Render details not set before rendering");
|
|
95
|
+
this.cacheSnapshot();
|
|
96
|
+
await this.renderFn();
|
|
97
|
+
this.complete();
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
// HTTP request delegate
|
|
101
|
+
/*
|
|
102
|
+
requestStarted() {
|
|
103
|
+
this.recordTimingMetric(TimingMetric.requestStart);
|
|
104
|
+
this.adapter.visitRequestStarted(this);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
requestProgressed(progress: number) {
|
|
108
|
+
this.progress = progress;
|
|
109
|
+
if (this.adapter.visitRequestProgressed) {
|
|
110
|
+
this.adapter.visitRequestProgressed(this);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
requestCompletedWithResponse(
|
|
115
|
+
response: string,
|
|
116
|
+
redirectedToLocation?: Location
|
|
117
|
+
) {
|
|
118
|
+
this.response = response;
|
|
119
|
+
this.redirectedToLocation = redirectedToLocation;
|
|
120
|
+
this.adapter.visitRequestCompleted(this);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
requestFailedWithStatusCode(statusCode: number, response?: string) {
|
|
124
|
+
this.response = response;
|
|
125
|
+
this.adapter.visitRequestFailedWithStatusCode(this, statusCode);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
requestFinished() {
|
|
129
|
+
this.recordTimingMetric(TimingMetric.requestEnd);
|
|
130
|
+
this.adapter.visitRequestFinished(this);
|
|
131
|
+
}
|
|
132
|
+
*/
|
|
133
|
+
// Instrumentation
|
|
134
|
+
recordTimingMetric(metric) {
|
|
135
|
+
this.timingMetrics[metric] = new Date().getTime();
|
|
136
|
+
}
|
|
137
|
+
getTimingMetrics() {
|
|
138
|
+
return { ...this.timingMetrics };
|
|
139
|
+
}
|
|
140
|
+
// Private
|
|
141
|
+
shouldIssueRequest() {
|
|
142
|
+
return this.action == "restore" ? !this.hasCachedSnapshot() : true;
|
|
143
|
+
}
|
|
144
|
+
cacheSnapshot() {
|
|
145
|
+
if (!this.snapshotCached) {
|
|
146
|
+
this.controller.cacheSnapshot();
|
|
147
|
+
this.snapshotCached = true;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
render(callback) {
|
|
151
|
+
this.cancelRender();
|
|
152
|
+
this.frame = requestAnimationFrame(() => {
|
|
153
|
+
delete this.frame;
|
|
154
|
+
callback.call(this);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
cancelRender() {
|
|
158
|
+
if (this.frame) {
|
|
159
|
+
cancelAnimationFrame(this.frame);
|
|
160
|
+
delete this.frame;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onRenderClient.d.ts","sourceRoot":"","sources":["../../../proxy/pages/onRenderClient.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"onRenderClient.d.ts","sourceRoot":"","sources":["../../../proxy/pages/onRenderClient.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AASjE,wBAA8B,cAAc,CAC1C,WAAW,EAAE,sBAAsB,iBA4DpC"}
|
|
@@ -1,45 +1,56 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { renderReact } from "../../lib/renderReact.js";
|
|
3
3
|
import { PageShell } from "../../lib/PageShell.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
import { navigateAnywhere } from "../../lib/navigateAnywhere.js";
|
|
9
|
-
import { setupTurbolinks } from "../../lib/turbolinks.js";
|
|
10
|
-
setupTurbolinks();
|
|
4
|
+
import { Turbolinks } from "../../lib/turbolinks/index.js";
|
|
5
|
+
import { copyElementAttributes, } from "../../lib/turbolinks/util.js";
|
|
6
|
+
import { getElementAttributes } from "../../lib/getElementAttributes.js";
|
|
7
|
+
Turbolinks.start();
|
|
11
8
|
export default async function onRenderClient(pageContext) {
|
|
12
|
-
if (
|
|
9
|
+
if (pageContext.redirectTo) {
|
|
10
|
+
Turbolinks.visit(pageContext.redirectTo);
|
|
13
11
|
return;
|
|
14
|
-
|
|
12
|
+
}
|
|
15
13
|
const { layoutProps, layout } = pageContext;
|
|
16
|
-
const
|
|
14
|
+
const { layoutMap } = pageContext.config;
|
|
15
|
+
if (!layoutMap) {
|
|
16
|
+
throw new Error("layoutMap needs to be defined in config");
|
|
17
|
+
}
|
|
18
|
+
const Layout = layoutMap[layout];
|
|
19
|
+
function render(body) {
|
|
20
|
+
renderReact(React.createElement(PageShell, { key: pageContext.urlOriginal, pageContext: pageContext },
|
|
21
|
+
React.createElement(Layout, { ...layoutProps },
|
|
22
|
+
React.createElement("div", { id: "proxied-body", dangerouslySetInnerHTML: { __html: body } }))), pageContext.isHydration);
|
|
23
|
+
}
|
|
24
|
+
let bodyEl;
|
|
17
25
|
if (pageContext.isHydration) {
|
|
18
26
|
// During hydration of initial ssr, body is in dom, not page props (to avoid double-send)
|
|
19
|
-
|
|
27
|
+
bodyEl = document.getElementById("proxied-body");
|
|
28
|
+
render(bodyEl.innerHTML);
|
|
20
29
|
}
|
|
21
30
|
else {
|
|
22
31
|
const { proxySendClient: proxy } = pageContext;
|
|
23
32
|
if (!proxy) {
|
|
24
|
-
console.error("proxy/+onRenderClient did not receive proxySendClient
|
|
33
|
+
console.error("proxy/+onRenderClient did not receive proxySendClient");
|
|
25
34
|
return;
|
|
26
35
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
const parsed = document.createElement("html");
|
|
37
|
+
parsed.innerHTML = proxy;
|
|
38
|
+
bodyEl = parsed.querySelector("body");
|
|
39
|
+
const headEl = parsed.querySelector("head");
|
|
40
|
+
Turbolinks._vpsOnRenderClient(headEl, true, () => {
|
|
41
|
+
// merge body attributes
|
|
42
|
+
document.body
|
|
43
|
+
.getAttributeNames()
|
|
44
|
+
.forEach((n) => document.body.removeAttribute(n));
|
|
45
|
+
copyElementAttributes(document.body, bodyEl);
|
|
46
|
+
// render body with react
|
|
47
|
+
render(bodyEl.innerHTML);
|
|
48
|
+
});
|
|
37
49
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
React.createElement("div", { id: "proxied-body", dangerouslySetInnerHTML: { __html: body } }))), pageContext.isHydration);
|
|
50
|
+
// cache page context will save it and return it to us during restoration visits
|
|
51
|
+
Turbolinks._vpsCachePageContext({
|
|
52
|
+
layoutProps,
|
|
53
|
+
layout,
|
|
54
|
+
bodyAttrs: getElementAttributes(bodyEl),
|
|
55
|
+
});
|
|
45
56
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onRenderHtml.d.ts","sourceRoot":"","sources":["../../../proxy/pages/onRenderHtml.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"onRenderHtml.d.ts","sourceRoot":"","sources":["../../../proxy/pages/onRenderHtml.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAKjE,wBAA8B,YAAY,CACxC,WAAW,EAAE,sBAAsB;;;;;;GAkEpC"}
|
|
@@ -2,25 +2,39 @@ import React from "react";
|
|
|
2
2
|
import ReactDOMServer from "react-dom/server";
|
|
3
3
|
import { dangerouslySkipEscape, escapeInject } from "vite-plugin-ssr/server";
|
|
4
4
|
import { PageShell } from "../../lib/PageShell.js";
|
|
5
|
+
import jsdom from "jsdom";
|
|
6
|
+
import { getElementAttributes } from "../../lib/getElementAttributes.js";
|
|
5
7
|
export default async function onRenderHtml(pageContext) {
|
|
6
8
|
if (pageContext.proxy) {
|
|
7
|
-
const { proxy
|
|
8
|
-
const
|
|
9
|
+
const { proxy, layoutProps, layout } = pageContext;
|
|
10
|
+
const dom = new jsdom.JSDOM(proxy);
|
|
11
|
+
const doc = dom.window.document;
|
|
12
|
+
const bodyEl = doc.querySelector("body");
|
|
13
|
+
const head = doc.querySelector("head");
|
|
14
|
+
if (!bodyEl || !head) {
|
|
15
|
+
throw new Error("Proxy failed");
|
|
16
|
+
}
|
|
17
|
+
const { layoutMap } = pageContext.config;
|
|
18
|
+
if (!layoutMap) {
|
|
19
|
+
throw new Error("layoutMap needs to be defined in config");
|
|
20
|
+
}
|
|
21
|
+
const Layout = layoutMap[layout];
|
|
9
22
|
if (!Layout)
|
|
10
23
|
throw new Error(`${layout} layout not found`);
|
|
11
24
|
const pageHtml = ReactDOMServer.renderToString(React.createElement(PageShell, { pageContext: pageContext },
|
|
12
25
|
React.createElement(Layout, { ...layoutProps },
|
|
13
|
-
React.createElement("div", { id: "proxied-body", dangerouslySetInnerHTML: { __html:
|
|
26
|
+
React.createElement("div", { id: "proxied-body", dangerouslySetInnerHTML: { __html: bodyEl.innerHTML } }))));
|
|
14
27
|
const documentHtml = escapeInject `
|
|
15
28
|
<!DOCTYPE html>
|
|
16
29
|
<html>
|
|
17
30
|
<head>
|
|
18
|
-
${dangerouslySkipEscape(head)}
|
|
31
|
+
${dangerouslySkipEscape(head.innerHTML)}
|
|
19
32
|
${
|
|
20
33
|
// We need to fire turbolinks:load exactly on DCL, so it must be a blocking head script to catch DCL event.
|
|
21
34
|
// Vite loads scripts with type="module" so the rest of our code will show up too late.
|
|
22
35
|
// TODO: figure out how to bundle this better. at least read from a .js file
|
|
23
36
|
dangerouslySkipEscape(`<script>
|
|
37
|
+
window.Turbolinks = {controller:{restorationIdentifier: ''}};
|
|
24
38
|
addEventListener("DOMContentLoaded", () => {
|
|
25
39
|
const event = new Event("turbolinks:load", { bubbles: true, cancelable: true });
|
|
26
40
|
event.data = {url: window.location.href};
|
|
@@ -28,7 +42,7 @@ export default async function onRenderHtml(pageContext) {
|
|
|
28
42
|
})
|
|
29
43
|
</script>`)}
|
|
30
44
|
</head>
|
|
31
|
-
<body ${dangerouslySkipEscape(Object.entries(
|
|
45
|
+
<body ${dangerouslySkipEscape(Object.entries(getElementAttributes(bodyEl))
|
|
32
46
|
.map(([name, value]) => `${name}="${value}"`)
|
|
33
47
|
.join(" "))}>
|
|
34
48
|
<div id="page-view">${dangerouslySkipEscape(pageHtml)}</div>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"+config.d.ts","sourceRoot":"","sources":["../../../../proxy/pages/restorationVisit/+config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"+config.d.ts","sourceRoot":"","sources":["../../../../proxy/pages/restorationVisit/+config.ts"],"names":[],"mappings":";;;;;;;;;;;AAEA,wBAcgC"}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
route: "import:@alignable/bifrost/proxy/pages/restorationVisit/route",
|
|
3
3
|
Page: "import:@alignable/bifrost/proxy/pages/Page",
|
|
4
|
+
onRenderClient: "import:@alignable/bifrost/proxy/pages/restorationVisit/onRenderClient",
|
|
5
|
+
// See onBeforeRoute for how head and body are inserted from Turbolinks snapshot
|
|
6
|
+
passToClient: ["headEl", "bodyEl", "layout", "layoutProps"],
|
|
4
7
|
meta: {
|
|
5
8
|
onBeforeRender: {
|
|
6
9
|
// We tell vite-plugin-ssr to load and execute onBeforeRender()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onRenderClient.d.ts","sourceRoot":"","sources":["../../../../proxy/pages/restorationVisit/onRenderClient.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,gCAAgC,EAAE,MAAM,4BAA4B,CAAC;AAO9E,wBAA8B,cAAc,CAC1C,WAAW,EAAE,gCAAgC,iBA2C9C"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { PageShell } from "../../../lib/PageShell.js";
|
|
3
|
+
import { renderReact } from "../../../lib/renderReact.js";
|
|
4
|
+
import { Turbolinks } from "../../../lib/turbolinks/index.js";
|
|
5
|
+
import { getElementAttributes } from "../../../lib/getElementAttributes.js";
|
|
6
|
+
import { copyElementAttributes } from "../../../lib/turbolinks/util.js";
|
|
7
|
+
export default async function onRenderClient(pageContext) {
|
|
8
|
+
if (pageContext.isHydration) {
|
|
9
|
+
throw new Error("restoration visit should never happen on initial render");
|
|
10
|
+
}
|
|
11
|
+
const { layoutProps, layout, bodyEl, headEl } = pageContext;
|
|
12
|
+
const { layoutMap } = pageContext.config;
|
|
13
|
+
if (!layoutMap) {
|
|
14
|
+
throw new Error("layoutMap needs to be defined in config");
|
|
15
|
+
}
|
|
16
|
+
const Layout = layoutMap[layout];
|
|
17
|
+
function render(body) {
|
|
18
|
+
renderReact(React.createElement(PageShell, { key: pageContext.urlOriginal, pageContext: pageContext },
|
|
19
|
+
React.createElement(Layout, { ...layoutProps },
|
|
20
|
+
React.createElement("div", { id: "proxied-body", dangerouslySetInnerHTML: { __html: body } }))), pageContext.isHydration);
|
|
21
|
+
}
|
|
22
|
+
const proxyBodyEl = bodyEl.querySelector("#proxied-body");
|
|
23
|
+
if (!proxyBodyEl) {
|
|
24
|
+
throw new Error("proxied body not found in cached snapshot");
|
|
25
|
+
}
|
|
26
|
+
Turbolinks._vpsOnRenderClient(headEl, true, () => {
|
|
27
|
+
// merge body attributes
|
|
28
|
+
document.body
|
|
29
|
+
.getAttributeNames()
|
|
30
|
+
.forEach((n) => document.body.removeAttribute(n));
|
|
31
|
+
copyElementAttributes(document.body, bodyEl);
|
|
32
|
+
// render body with react
|
|
33
|
+
render(proxyBodyEl.innerHTML);
|
|
34
|
+
});
|
|
35
|
+
// cache page context will save it and return it to us during restoration visits
|
|
36
|
+
Turbolinks._vpsCachePageContext({
|
|
37
|
+
layoutProps,
|
|
38
|
+
layout,
|
|
39
|
+
bodyAttrs: getElementAttributes(bodyEl),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { PageContextNoProxy } from "../types/internal.js";
|
|
2
|
+
type ConfigOrContext = PageContextNoProxy | PageContextNoProxy["config"];
|
|
3
|
+
/**
|
|
4
|
+
* Get page configs that are definable in config and in runtime via onBeforeRoute. documentProps, for example.
|
|
5
|
+
*/
|
|
6
|
+
export declare function getPageContextOrConfig<T extends keyof ConfigOrContext>(pageContext: PageContextNoProxy, prop: T): ConfigOrContext[T];
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=getConfigOrPageContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getConfigOrPageContext.d.ts","sourceRoot":"","sources":["../../renderer/getConfigOrPageContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,KAAK,eAAe,GAAG,kBAAkB,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;AAEzE;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,MAAM,eAAe,EACpE,WAAW,EAAE,kBAAkB,EAC/B,IAAI,EAAE,CAAC,GACN,eAAe,CAAC,CAAC,CAAC,CAEpB"}
|
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
export default function onBeforeRoute(_pageContext: any): {
|
|
2
|
-
pageContext:
|
|
3
|
-
_pageId: string;
|
|
4
|
-
layoutProps: Record<string, unknown>;
|
|
5
|
-
proxySendClient?: import("../types/internal.js").Proxy | undefined;
|
|
6
|
-
layout: string;
|
|
7
|
-
};
|
|
2
|
+
pageContext: any;
|
|
8
3
|
} | undefined;
|
|
9
4
|
//# sourceMappingURL=onBeforeRoute.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onBeforeRoute.d.ts","sourceRoot":"","sources":["../../renderer/onBeforeRoute.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,YAAY,EAAE,GAAG
|
|
1
|
+
{"version":3,"file":"onBeforeRoute.d.ts","sourceRoot":"","sources":["../../renderer/onBeforeRoute.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,YAAY,EAAE,GAAG;;cA2BtD"}
|
|
@@ -1,15 +1,23 @@
|
|
|
1
|
-
import
|
|
1
|
+
// do NOT import turbolinks in this file. It is used on server side.
|
|
2
2
|
export default function onBeforeRoute(_pageContext) {
|
|
3
|
-
|
|
4
|
-
if (!!snapshot?.proxySendClient) {
|
|
5
|
-
return {
|
|
6
|
-
pageContext: {
|
|
7
|
-
...snapshot,
|
|
8
|
-
_pageId: "/proxy/pages/restorationVisit",
|
|
9
|
-
},
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
else {
|
|
3
|
+
if (typeof window === "undefined")
|
|
13
4
|
return undefined;
|
|
5
|
+
const Turbolinks = window.Turbolinks;
|
|
6
|
+
const currentVisit = Turbolinks.controller.currentVisit;
|
|
7
|
+
if (!currentVisit || currentVisit.state === "completed") {
|
|
8
|
+
// old/nonexistent currentVisit means VPS is doing history navigation. Ideally we might turn off VPS' onpopstate listener.
|
|
9
|
+
const snapshot = Turbolinks.controller.getCachedSnapshotForLocation(window.location.href);
|
|
10
|
+
Turbolinks.controller.historyPoppedToLocationWithRestorationIdentifier(window.location.href, "");
|
|
11
|
+
if (!!snapshot) {
|
|
12
|
+
return {
|
|
13
|
+
pageContext: {
|
|
14
|
+
...snapshot.pageContext,
|
|
15
|
+
bodyEl: snapshot.bodyEl,
|
|
16
|
+
headEl: snapshot.headEl,
|
|
17
|
+
_pageId: "/proxy/pages/restorationVisit",
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
14
21
|
}
|
|
22
|
+
return undefined;
|
|
15
23
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onRenderClient.d.ts","sourceRoot":"","sources":["../../renderer/onRenderClient.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"onRenderClient.d.ts","sourceRoot":"","sources":["../../renderer/onRenderClient.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAahE,wBAA8B,cAAc,CAC1C,WAAW,EAAE,wBAAwB,iBA6CtC"}
|
|
@@ -1,36 +1,43 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { renderReact } from "../lib/renderReact.js";
|
|
3
3
|
import { PageShell } from "../lib/PageShell.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
setupTurbolinks();
|
|
4
|
+
import { Turbolinks } from "../lib/turbolinks/index.js";
|
|
5
|
+
import { documentPropsToReact } from "./utils/buildHead.js";
|
|
6
|
+
import { getPageContextOrConfig } from "./getConfigOrPageContext.js";
|
|
7
|
+
import { createRoot } from "react-dom/client";
|
|
8
|
+
Turbolinks.start();
|
|
9
|
+
const PassThruLayout = ({ children, }) => React.createElement(React.Fragment, null, children);
|
|
11
10
|
export default async function onRenderClient(pageContext) {
|
|
12
|
-
if (
|
|
11
|
+
if (pageContext.redirectTo) {
|
|
12
|
+
Turbolinks.visit(pageContext.redirectTo);
|
|
13
13
|
return;
|
|
14
|
+
}
|
|
14
15
|
const { Page, pageProps } = pageContext;
|
|
15
|
-
const { Layout
|
|
16
|
+
const { Layout = PassThruLayout } = pageContext.config;
|
|
17
|
+
const layoutProps = getPageContextOrConfig(pageContext, "layoutProps") || {};
|
|
16
18
|
if (!Page)
|
|
17
19
|
throw new Error("Client-side render() hook expects Page to be exported");
|
|
18
|
-
if (!Layout)
|
|
19
|
-
throw new Error("Client-side render() hook expects Layout to be exported");
|
|
20
|
-
document.removeEventListener("click", turbolinksClickListener);
|
|
21
|
-
cacheProxiedBody();
|
|
22
|
-
const { title = "", description = "", viewport } = getDocumentProps(pageContext);
|
|
23
|
-
document.title = title;
|
|
24
|
-
document.head
|
|
25
|
-
.querySelector("meta[name='description']")
|
|
26
|
-
?.setAttribute("content", description);
|
|
27
|
-
if (viewport) {
|
|
28
|
-
document.head
|
|
29
|
-
.querySelector("meta[name='viewport']")
|
|
30
|
-
?.setAttribute("content", formatMetaObject(viewport));
|
|
31
|
-
}
|
|
32
20
|
const page = (React.createElement(PageShell, { pageContext: pageContext },
|
|
33
21
|
React.createElement(Layout, { ...layoutProps },
|
|
34
22
|
React.createElement(Page, { ...pageProps }))));
|
|
35
|
-
|
|
23
|
+
if (pageContext.isHydration) {
|
|
24
|
+
// During hydration of initial ssr, body is in dom, not page props (to avoid double-send)
|
|
25
|
+
renderReact(page, pageContext.isHydration);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
const head = document.createElement("head");
|
|
29
|
+
createRoot(head).render(documentPropsToReact(getPageContextOrConfig(pageContext, "documentProps") || {}));
|
|
30
|
+
pageContext.config.scripts?.forEach((s) => {
|
|
31
|
+
head.insertAdjacentHTML("beforeend", s);
|
|
32
|
+
});
|
|
33
|
+
requestAnimationFrame(() => {
|
|
34
|
+
Turbolinks._vpsOnRenderClient(head, false, () => {
|
|
35
|
+
// clear anything on body
|
|
36
|
+
document.body
|
|
37
|
+
.getAttributeNames()
|
|
38
|
+
.forEach((n) => document.body.removeAttribute(n));
|
|
39
|
+
renderReact(page, pageContext.isHydration);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
36
43
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onRenderHtml.d.ts","sourceRoot":"","sources":["../../renderer/onRenderHtml.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAIhE,wBAA8B,YAAY,CACxC,WAAW,EAAE,wBAAwB;;;
|
|
1
|
+
{"version":3,"file":"onRenderHtml.d.ts","sourceRoot":"","sources":["../../renderer/onRenderHtml.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAIhE,wBAA8B,YAAY,CACxC,WAAW,EAAE,wBAAwB;;;GAoDtC"}
|
|
@@ -2,11 +2,12 @@ import ReactDOMServer from "react-dom/server";
|
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { PageShell } from "../lib/PageShell.js";
|
|
4
4
|
import { escapeInject, dangerouslySkipEscape } from "vite-plugin-ssr/server";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { documentPropsToReact } from "./utils/buildHead.js";
|
|
6
|
+
import { getPageContextOrConfig } from "./getConfigOrPageContext.js";
|
|
7
7
|
export default async function onRenderHtml(pageContext) {
|
|
8
8
|
const { Page, pageProps } = pageContext;
|
|
9
|
-
const { Layout
|
|
9
|
+
const { Layout } = pageContext.config;
|
|
10
|
+
const layoutProps = getPageContextOrConfig(pageContext, "layoutProps") || {};
|
|
10
11
|
if (!Page)
|
|
11
12
|
throw new Error("Server-side render() hook expects Page to be exported");
|
|
12
13
|
if (!Layout)
|
|
@@ -14,24 +15,20 @@ export default async function onRenderHtml(pageContext) {
|
|
|
14
15
|
const pageHtml = ReactDOMServer.renderToString(React.createElement(PageShell, { pageContext: pageContext },
|
|
15
16
|
React.createElement(Layout, { ...layoutProps },
|
|
16
17
|
React.createElement(Page, { ...pageProps }))));
|
|
17
|
-
const
|
|
18
|
-
const googleAnalyticsTag = dangerouslySkipEscape(googleAnalytics);
|
|
19
|
-
const osanoTag = dangerouslySkipEscape(osano);
|
|
20
|
-
// // See https://vite-plugin-ssr.com/head
|
|
21
|
-
const { title = "", description = "", viewport } = getDocumentProps(pageContext);
|
|
22
|
-
if (!title) {
|
|
23
|
-
console.warn(`No title set for ${pageContext.urlOriginal}!`);
|
|
24
|
-
}
|
|
25
|
-
const viewportTag = !viewport ? "" : escapeInject `<meta content="${formatMetaObject(viewport)}" name="viewport">`;
|
|
18
|
+
const headHtml = ReactDOMServer.renderToString(documentPropsToReact(getPageContextOrConfig(pageContext, "documentProps") || {}));
|
|
26
19
|
const documentHtml = escapeInject `<!DOCTYPE html>
|
|
27
20
|
<html lang="en">
|
|
28
21
|
<head>
|
|
29
|
-
${
|
|
30
|
-
${
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
22
|
+
${dangerouslySkipEscape(headHtml)}
|
|
23
|
+
${dangerouslySkipEscape(Object.values(pageContext.config.scripts || {}).join(""))}
|
|
24
|
+
${dangerouslySkipEscape(`<script>
|
|
25
|
+
window.Turbolinks = {controller:{restorationIdentifier: ''}};
|
|
26
|
+
addEventListener("DOMContentLoaded", () => {
|
|
27
|
+
const event = new Event("turbolinks:load", { bubbles: true, cancelable: true });
|
|
28
|
+
event.data = {url: window.location.href};
|
|
29
|
+
document.dispatchEvent(event);
|
|
30
|
+
})
|
|
31
|
+
</script>`)}
|
|
35
32
|
</head>
|
|
36
33
|
<body>
|
|
37
34
|
<div id="page-view">${dangerouslySkipEscape(pageHtml)}</div>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildHead.d.ts","sourceRoot":"","sources":["../../../renderer/utils/buildHead.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,wBAAgB,oBAAoB,CAAC,EACnC,KAAU,EACV,WAAgB,EAChB,QAAa,GACd,EAAE,aAAa,GAAG,KAAK,CAAC,YAAY,CAcpC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export function documentPropsToReact({ title = "", description = "", viewport = {}, }) {
|
|
3
|
+
return (React.createElement(React.Fragment, null,
|
|
4
|
+
React.createElement("title", null, title),
|
|
5
|
+
React.createElement("meta", { name: "title", property: "og:title", content: title }),
|
|
6
|
+
React.createElement("meta", { name: "description", content: description }),
|
|
7
|
+
React.createElement("meta", { name: "viewport", content: Object.entries(viewport)
|
|
8
|
+
.map((e) => e.join("="))
|
|
9
|
+
.join(", ") })));
|
|
10
|
+
}
|