@quilted/quilt 0.5.130 → 0.5.131
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/CHANGELOG.md +6 -0
- package/build/cjs/server/index.cjs +17 -2
- package/build/cjs/server/request-router.cjs +188 -38
- package/build/cjs/static/index.cjs +47 -29
- package/build/esm/server/index.mjs +2 -3
- package/build/esm/server/request-router.mjs +188 -39
- package/build/esm/static/index.mjs +48 -30
- package/build/esnext/server/index.esnext +2 -3
- package/build/esnext/server/request-router.esnext +188 -39
- package/build/esnext/static/index.esnext +48 -30
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/typescript/server/index.d.ts +3 -4
- package/build/typescript/server/index.d.ts.map +1 -1
- package/build/typescript/server/request-router.d.ts +21 -12
- package/build/typescript/server/request-router.d.ts.map +1 -1
- package/build/typescript/static/index.d.ts +1 -1
- package/build/typescript/static/index.d.ts.map +1 -1
- package/package.json +3 -3
- package/source/server/index.ts +14 -5
- package/source/server/request-router.tsx +251 -64
- package/source/static/index.tsx +62 -28
- package/build/cjs/server/render.cjs +0 -41
- package/build/esm/server/render.mjs +0 -39
- package/build/esnext/server/render.esnext +0 -39
- package/source/server/render.tsx +0 -50
|
@@ -1,34 +1,39 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fragment as Fragment$1 } from 'react';
|
|
2
|
+
import { styleAssetPreloadAttributes, scriptAssetPreloadAttributes, styleAssetAttributes, scriptAssetAttributes } from '@quilted/async/server';
|
|
3
|
+
import { AsyncAssetManager } from '@quilted/react-async/server';
|
|
4
|
+
import { HttpManager } from '@quilted/react-http/server';
|
|
5
|
+
import { HtmlManager, renderHtmlToString, Html } from '@quilted/react-html/server';
|
|
6
|
+
import { extract } from '@quilted/react-server-render/server';
|
|
2
7
|
import { redirect, html } from '@quilted/request-router';
|
|
3
|
-
import {
|
|
4
|
-
import { jsx } from 'react/jsx-runtime';
|
|
8
|
+
import { ServerContext } from './ServerContext.mjs';
|
|
9
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
5
10
|
|
|
6
11
|
function createServerRender(render, {
|
|
7
12
|
context,
|
|
13
|
+
stream,
|
|
8
14
|
...options
|
|
9
15
|
} = {}) {
|
|
10
16
|
return async (request, requestContext) => {
|
|
11
17
|
const accepts = request.headers.get('Accept');
|
|
12
18
|
if (accepts != null && !accepts.includes('text/html')) return;
|
|
13
19
|
const app = await render(request, requestContext);
|
|
14
|
-
|
|
20
|
+
const renderResponse = stream ? renderAppToStreamedResponse : renderAppToResponse;
|
|
21
|
+
return renderResponse(app, request, {
|
|
15
22
|
...options,
|
|
16
|
-
|
|
23
|
+
extract: {
|
|
24
|
+
...options.extract,
|
|
25
|
+
context: options.extract ?? context?.(request, requestContext) ?? requestContext
|
|
26
|
+
}
|
|
17
27
|
});
|
|
18
28
|
};
|
|
19
29
|
}
|
|
20
30
|
async function renderAppToResponse(app, request, {
|
|
21
31
|
assets,
|
|
22
|
-
|
|
23
|
-
|
|
32
|
+
extract,
|
|
33
|
+
renderHtml
|
|
24
34
|
} = {}) {
|
|
25
|
-
const {
|
|
26
|
-
|
|
27
|
-
http,
|
|
28
|
-
rendered,
|
|
29
|
-
asyncAssets
|
|
30
|
-
} = await renderApp(app, {
|
|
31
|
-
...options,
|
|
35
|
+
const renderDetails = await serverRenderDetailsForApp(app, {
|
|
36
|
+
extract,
|
|
32
37
|
url: request.url,
|
|
33
38
|
headers: request.headers
|
|
34
39
|
});
|
|
@@ -36,56 +41,200 @@ async function renderAppToResponse(app, request, {
|
|
|
36
41
|
headers,
|
|
37
42
|
statusCode = 200,
|
|
38
43
|
redirectUrl
|
|
39
|
-
} = http.state;
|
|
44
|
+
} = renderDetails.http.state;
|
|
40
45
|
if (redirectUrl) {
|
|
41
46
|
return redirect(redirectUrl, {
|
|
42
47
|
status: statusCode,
|
|
43
48
|
headers
|
|
44
49
|
});
|
|
45
50
|
}
|
|
51
|
+
const content = await renderAppDetailsToHtmlString(renderDetails, request, {
|
|
52
|
+
assets,
|
|
53
|
+
renderHtml
|
|
54
|
+
});
|
|
55
|
+
return html(content, {
|
|
56
|
+
headers,
|
|
57
|
+
status: statusCode
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
async function renderAppToStreamedResponse(app, request, {
|
|
61
|
+
assets,
|
|
62
|
+
extract,
|
|
63
|
+
renderHtml
|
|
64
|
+
} = {}) {
|
|
65
|
+
const headers = new Headers();
|
|
66
|
+
const stream = new TransformStream();
|
|
67
|
+
const assetContext = {
|
|
68
|
+
userAgent: request.headers.get('User-Agent')
|
|
69
|
+
};
|
|
70
|
+
const guaranteedAssets = await assets?.assets({
|
|
71
|
+
context: assetContext
|
|
72
|
+
});
|
|
73
|
+
if (guaranteedAssets) {
|
|
74
|
+
for (const style of guaranteedAssets.styles) {
|
|
75
|
+
headers.append('Link', preloadHeader(styleAssetPreloadAttributes(style)));
|
|
76
|
+
}
|
|
77
|
+
for (const script of guaranteedAssets.scripts) {
|
|
78
|
+
headers.append('Link', preloadHeader(scriptAssetPreloadAttributes(script)));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
renderResponseToStream();
|
|
82
|
+
return html(stream.readable, {
|
|
83
|
+
headers,
|
|
84
|
+
status: 200
|
|
85
|
+
});
|
|
86
|
+
async function renderResponseToStream() {
|
|
87
|
+
const renderDetails = await serverRenderDetailsForApp(app, {
|
|
88
|
+
extract,
|
|
89
|
+
url: request.url,
|
|
90
|
+
headers: request.headers
|
|
91
|
+
});
|
|
92
|
+
const content = await renderAppDetailsToHtmlString(renderDetails, request, {
|
|
93
|
+
assets,
|
|
94
|
+
renderHtml
|
|
95
|
+
});
|
|
96
|
+
const writer = stream.writable.getWriter();
|
|
97
|
+
await writer.write(content);
|
|
98
|
+
await writer.close();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async function serverRenderDetailsForApp(app, {
|
|
102
|
+
url,
|
|
103
|
+
headers,
|
|
104
|
+
extract: extractOptions
|
|
105
|
+
} = {}) {
|
|
106
|
+
const html = new HtmlManager();
|
|
107
|
+
const asyncAssets = new AsyncAssetManager();
|
|
108
|
+
const http = new HttpManager({
|
|
109
|
+
headers
|
|
110
|
+
});
|
|
111
|
+
const {
|
|
112
|
+
decorate,
|
|
113
|
+
...rest
|
|
114
|
+
} = extractOptions ?? {};
|
|
115
|
+
const rendered = await extract(app, {
|
|
116
|
+
decorate(app) {
|
|
117
|
+
return /*#__PURE__*/jsx(ServerContext, {
|
|
118
|
+
asyncAssets: asyncAssets,
|
|
119
|
+
http: http,
|
|
120
|
+
html: html,
|
|
121
|
+
url: url,
|
|
122
|
+
children: decorate?.(app) ?? app
|
|
123
|
+
});
|
|
124
|
+
},
|
|
125
|
+
...rest
|
|
126
|
+
});
|
|
127
|
+
return {
|
|
128
|
+
rendered,
|
|
129
|
+
http,
|
|
130
|
+
html,
|
|
131
|
+
asyncAssets
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
async function renderAppDetailsToHtmlString(details, request, {
|
|
135
|
+
assets,
|
|
136
|
+
renderHtml = defaultRenderHtml
|
|
137
|
+
} = {}) {
|
|
138
|
+
const {
|
|
139
|
+
html: htmlManager,
|
|
140
|
+
http,
|
|
141
|
+
rendered,
|
|
142
|
+
asyncAssets
|
|
143
|
+
} = details;
|
|
46
144
|
const usedAssets = asyncAssets.used({
|
|
47
145
|
timing: 'load'
|
|
48
146
|
});
|
|
49
|
-
const
|
|
147
|
+
const assetContext = {
|
|
50
148
|
userAgent: request.headers.get('User-Agent')
|
|
51
149
|
};
|
|
52
|
-
const [
|
|
150
|
+
const [entryAssets, preloadAssets] = assets ? await Promise.all([assets.assets({
|
|
53
151
|
async: usedAssets,
|
|
54
|
-
|
|
55
|
-
}), assets.scripts({
|
|
56
|
-
async: usedAssets,
|
|
57
|
-
options: assetOptions
|
|
152
|
+
context: assetContext
|
|
58
153
|
}), assets.asyncAssets(asyncAssets.used({
|
|
59
154
|
timing: 'preload'
|
|
60
155
|
}), {
|
|
61
|
-
|
|
62
|
-
})]) : [
|
|
156
|
+
context: assetContext
|
|
157
|
+
})]) : [];
|
|
63
158
|
const htmlElement = await renderHtml(rendered, request, {
|
|
64
159
|
html: htmlManager,
|
|
65
160
|
http,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
preload
|
|
69
|
-
});
|
|
70
|
-
return html(renderHtmlToString(htmlElement), {
|
|
71
|
-
headers,
|
|
72
|
-
status: statusCode
|
|
161
|
+
assets: entryAssets,
|
|
162
|
+
preloadAssets
|
|
73
163
|
});
|
|
164
|
+
return renderHtmlToString(htmlElement);
|
|
74
165
|
}
|
|
75
|
-
function defaultRenderHtml(content, request, {
|
|
166
|
+
const defaultRenderHtml = function defaultRenderHtml(content, request, {
|
|
76
167
|
html,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
preload
|
|
168
|
+
assets,
|
|
169
|
+
preloadAssets
|
|
80
170
|
}) {
|
|
171
|
+
const baseUrl = new URL(request.url);
|
|
81
172
|
return /*#__PURE__*/jsx(Html, {
|
|
82
|
-
url: new URL(request.url),
|
|
83
173
|
manager: html,
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
174
|
+
headEndContent: /*#__PURE__*/jsxs(Fragment, {
|
|
175
|
+
children: [assets && [...assets.styles].map(style => {
|
|
176
|
+
const attributes = styleAssetAttributes(style, {
|
|
177
|
+
baseUrl
|
|
178
|
+
});
|
|
179
|
+
return /*#__PURE__*/jsx("link", {
|
|
180
|
+
...attributes
|
|
181
|
+
}, style.source);
|
|
182
|
+
}), assets && [...assets.scripts].map(script => {
|
|
183
|
+
const isModule = script.attributes.type === 'module';
|
|
184
|
+
const attributes = scriptAssetAttributes(script, {
|
|
185
|
+
baseUrl
|
|
186
|
+
});
|
|
187
|
+
if (isModule) {
|
|
188
|
+
return /*#__PURE__*/jsxs(Fragment$1, {
|
|
189
|
+
children: [/*#__PURE__*/jsx("link", {
|
|
190
|
+
...scriptAssetPreloadAttributes(script)
|
|
191
|
+
}), /*#__PURE__*/jsx("script", {
|
|
192
|
+
...attributes,
|
|
193
|
+
async: true
|
|
194
|
+
})]
|
|
195
|
+
}, script.source);
|
|
196
|
+
}
|
|
197
|
+
return /*#__PURE__*/jsx("script", {
|
|
198
|
+
...attributes,
|
|
199
|
+
defer: true
|
|
200
|
+
}, script.source);
|
|
201
|
+
}), preloadAssets && [...preloadAssets.styles].map(style => {
|
|
202
|
+
const attributes = styleAssetPreloadAttributes(style, {
|
|
203
|
+
baseUrl
|
|
204
|
+
});
|
|
205
|
+
return /*#__PURE__*/jsx("link", {
|
|
206
|
+
...attributes
|
|
207
|
+
}, style.source);
|
|
208
|
+
}), preloadAssets && [...preloadAssets.scripts].map(script => {
|
|
209
|
+
const attributes = scriptAssetPreloadAttributes(script, {
|
|
210
|
+
baseUrl
|
|
211
|
+
});
|
|
212
|
+
return /*#__PURE__*/jsx("link", {
|
|
213
|
+
...attributes
|
|
214
|
+
}, script.source);
|
|
215
|
+
})]
|
|
216
|
+
}),
|
|
87
217
|
children: content
|
|
88
218
|
});
|
|
219
|
+
};
|
|
220
|
+
function preloadHeader(attributes) {
|
|
221
|
+
const {
|
|
222
|
+
as,
|
|
223
|
+
rel = 'preload',
|
|
224
|
+
href,
|
|
225
|
+
crossOrigin,
|
|
226
|
+
crossorigin
|
|
227
|
+
} = attributes;
|
|
228
|
+
|
|
229
|
+
// Support both property and attribute versions of the casing
|
|
230
|
+
const finalCrossOrigin = crossOrigin ?? crossorigin;
|
|
231
|
+
let header = `<${href}>; rel="${rel}"; as="${as}"`;
|
|
232
|
+
if (finalCrossOrigin === '' || finalCrossOrigin === true) {
|
|
233
|
+
header += `; crossorigin`;
|
|
234
|
+
} else if (typeof finalCrossOrigin === 'string') {
|
|
235
|
+
header += `; crossorigin="${finalCrossOrigin}"`;
|
|
236
|
+
}
|
|
237
|
+
return header;
|
|
89
238
|
}
|
|
90
239
|
|
|
91
|
-
export { createServerRender, renderAppToResponse };
|
|
240
|
+
export { createServerRender, renderAppToResponse, renderAppToStreamedResponse };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { styleAssetAttributes, scriptAssetAttributes, styleAssetPreloadAttributes, scriptAssetPreloadAttributes } from '@quilted/async/server';
|
|
1
2
|
import { renderHtmlToString, Html } from '@quilted/react-html/server';
|
|
2
3
|
import { StaticRenderer, StaticRendererContext } from '@quilted/react-router/static';
|
|
3
4
|
import { renderApp } from './render.mjs';
|
|
4
|
-
import { jsx } from 'react/jsx-runtime';
|
|
5
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
5
6
|
|
|
6
7
|
const BASE_URL = 'http://localhost:3000';
|
|
7
8
|
async function renderStatic(App, {
|
|
@@ -173,30 +174,20 @@ async function renderStatic(App, {
|
|
|
173
174
|
const usedAssets = asyncAssets.used({
|
|
174
175
|
timing: 'load'
|
|
175
176
|
});
|
|
176
|
-
const [
|
|
177
|
+
const [moduleAssets, modulePreload, nomoduleAssets] = await Promise.all([assets.assets({
|
|
177
178
|
async: usedAssets,
|
|
178
|
-
|
|
179
|
-
modules: true
|
|
180
|
-
}
|
|
181
|
-
}), assets.scripts({
|
|
182
|
-
async: usedAssets,
|
|
183
|
-
options: {
|
|
179
|
+
context: {
|
|
184
180
|
modules: true
|
|
185
181
|
}
|
|
186
182
|
}), assets.asyncAssets(asyncAssets.used({
|
|
187
183
|
timing: 'preload'
|
|
188
184
|
}), {
|
|
189
|
-
|
|
185
|
+
context: {
|
|
190
186
|
modules: true
|
|
191
187
|
}
|
|
192
|
-
}), assets.
|
|
193
|
-
async: usedAssets,
|
|
194
|
-
options: {
|
|
195
|
-
modules: false
|
|
196
|
-
}
|
|
197
|
-
}), assets.scripts({
|
|
188
|
+
}), assets.assets({
|
|
198
189
|
async: usedAssets,
|
|
199
|
-
|
|
190
|
+
context: {
|
|
200
191
|
modules: false
|
|
201
192
|
}
|
|
202
193
|
})]);
|
|
@@ -204,22 +195,49 @@ async function renderStatic(App, {
|
|
|
204
195
|
// We don’t want to load styles from both bundles, so we only use module styles,
|
|
205
196
|
// since modules are intended to be the default and CSS (usually) doesn’t
|
|
206
197
|
// have features that meaningfully break older user agents.
|
|
207
|
-
const styles =
|
|
208
|
-
|
|
209
|
-
// If there are nomodule scripts, we can’t really do preloading, because we can’t
|
|
210
|
-
// prevent the nomodule scripts from being preloaded in module browsers. If there
|
|
211
|
-
// are only module scripts, we can preload those.
|
|
212
|
-
const preload = nomoduleScripts.length > 0 ? [] : modulePreload;
|
|
213
|
-
const scripts = [...moduleScripts, ...nomoduleScripts.map(script => ({
|
|
214
|
-
...script,
|
|
215
|
-
nomodule: true
|
|
216
|
-
}))];
|
|
198
|
+
const styles = moduleAssets.styles.length > 0 ? moduleAssets.styles : nomoduleAssets.styles;
|
|
217
199
|
const minifiedHtml = renderHtmlToString( /*#__PURE__*/jsx(Html, {
|
|
218
|
-
url: url,
|
|
219
200
|
manager: htmlManager,
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
201
|
+
headEndContent: /*#__PURE__*/jsxs(Fragment, {
|
|
202
|
+
children: [[...styles].map(style => {
|
|
203
|
+
const attributes = styleAssetAttributes(style, {
|
|
204
|
+
baseUrl: url
|
|
205
|
+
});
|
|
206
|
+
return /*#__PURE__*/jsx("link", {
|
|
207
|
+
...attributes
|
|
208
|
+
}, style.source);
|
|
209
|
+
}), [...moduleAssets.scripts].map(script => {
|
|
210
|
+
const attributes = scriptAssetAttributes(script, {
|
|
211
|
+
baseUrl: url
|
|
212
|
+
});
|
|
213
|
+
return /*#__PURE__*/jsx("script", {
|
|
214
|
+
...attributes
|
|
215
|
+
}, script.source);
|
|
216
|
+
}), [...nomoduleAssets.scripts].map(script => {
|
|
217
|
+
const attributes = scriptAssetAttributes(script, {
|
|
218
|
+
baseUrl: url
|
|
219
|
+
});
|
|
220
|
+
return /*#__PURE__*/jsx("script", {
|
|
221
|
+
...attributes,
|
|
222
|
+
// @ts-expect-error Rendering to HTML, so using the lowercase name
|
|
223
|
+
nomodule: moduleAssets.scripts.length > 0 ? true : undefined
|
|
224
|
+
}, script.source);
|
|
225
|
+
}), [...modulePreload.styles].map(style => {
|
|
226
|
+
const attributes = styleAssetPreloadAttributes(style, {
|
|
227
|
+
baseUrl: url
|
|
228
|
+
});
|
|
229
|
+
return /*#__PURE__*/jsx("link", {
|
|
230
|
+
...attributes
|
|
231
|
+
}, style.source);
|
|
232
|
+
}), [...modulePreload.scripts].map(script => {
|
|
233
|
+
const attributes = scriptAssetPreloadAttributes(script, {
|
|
234
|
+
baseUrl: url
|
|
235
|
+
});
|
|
236
|
+
return /*#__PURE__*/jsx("link", {
|
|
237
|
+
...attributes
|
|
238
|
+
}, script.source);
|
|
239
|
+
})]
|
|
240
|
+
}),
|
|
223
241
|
children: markup
|
|
224
242
|
}));
|
|
225
243
|
const html = prettify ? await prettifyHtml(minifiedHtml) : minifiedHtml;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
export { SERVER_ACTION_ID as HTML_SERVER_ACTION_ID, Html, HtmlContext, HtmlManager, Serialize, renderHtmlToString } from '@quilted/react-html/server';
|
|
2
2
|
export { ServerAction, ServerRenderManager, ServerRenderManagerContext, extract, useServerAction } from '@quilted/react-server-render/server';
|
|
3
|
-
export { createAssetManifest } from '@quilted/async/server';
|
|
3
|
+
export { createAssetManifest, scriptAssetAttributes, scriptAssetPreloadAttributes, styleAssetAttributes, styleAssetPreloadAttributes } from '@quilted/async/server';
|
|
4
4
|
export { SERVER_ACTION_ID as ASYNC_ASSETS_SERVER_ACTION_ID, AsyncAssetContext, AsyncAssetManager } from '@quilted/react-async/server';
|
|
5
5
|
export { createRequestRouterLocalization } from '@quilted/react-localize/request-router';
|
|
6
6
|
export { EnhancedRequest, EnhancedResponse, Request, Response, createHeaders, createRequestRouter } from '@quilted/request-router';
|
|
7
7
|
export { parseAcceptLanguageHeader } from '@quilted/react-localize';
|
|
8
|
-
export { renderApp } from './render.esnext';
|
|
9
8
|
export { ServerContext } from './ServerContext.esnext';
|
|
10
|
-
export { createServerRender, renderAppToResponse } from './request-router.esnext';
|
|
9
|
+
export { createServerRender, renderAppToResponse, renderAppToStreamedResponse } from './request-router.esnext';
|
|
@@ -1,34 +1,39 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fragment as Fragment$1 } from 'react';
|
|
2
|
+
import { styleAssetPreloadAttributes, scriptAssetPreloadAttributes, styleAssetAttributes, scriptAssetAttributes } from '@quilted/async/server';
|
|
3
|
+
import { AsyncAssetManager } from '@quilted/react-async/server';
|
|
4
|
+
import { HttpManager } from '@quilted/react-http/server';
|
|
5
|
+
import { HtmlManager, renderHtmlToString, Html } from '@quilted/react-html/server';
|
|
6
|
+
import { extract } from '@quilted/react-server-render/server';
|
|
2
7
|
import { redirect, html } from '@quilted/request-router';
|
|
3
|
-
import {
|
|
4
|
-
import { jsx } from 'react/jsx-runtime';
|
|
8
|
+
import { ServerContext } from './ServerContext.esnext';
|
|
9
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
5
10
|
|
|
6
11
|
function createServerRender(render, {
|
|
7
12
|
context,
|
|
13
|
+
stream,
|
|
8
14
|
...options
|
|
9
15
|
} = {}) {
|
|
10
16
|
return async (request, requestContext) => {
|
|
11
17
|
const accepts = request.headers.get('Accept');
|
|
12
18
|
if (accepts != null && !accepts.includes('text/html')) return;
|
|
13
19
|
const app = await render(request, requestContext);
|
|
14
|
-
|
|
20
|
+
const renderResponse = stream ? renderAppToStreamedResponse : renderAppToResponse;
|
|
21
|
+
return renderResponse(app, request, {
|
|
15
22
|
...options,
|
|
16
|
-
|
|
23
|
+
extract: {
|
|
24
|
+
...options.extract,
|
|
25
|
+
context: options.extract ?? context?.(request, requestContext) ?? requestContext
|
|
26
|
+
}
|
|
17
27
|
});
|
|
18
28
|
};
|
|
19
29
|
}
|
|
20
30
|
async function renderAppToResponse(app, request, {
|
|
21
31
|
assets,
|
|
22
|
-
|
|
23
|
-
|
|
32
|
+
extract,
|
|
33
|
+
renderHtml
|
|
24
34
|
} = {}) {
|
|
25
|
-
const {
|
|
26
|
-
|
|
27
|
-
http,
|
|
28
|
-
rendered,
|
|
29
|
-
asyncAssets
|
|
30
|
-
} = await renderApp(app, {
|
|
31
|
-
...options,
|
|
35
|
+
const renderDetails = await serverRenderDetailsForApp(app, {
|
|
36
|
+
extract,
|
|
32
37
|
url: request.url,
|
|
33
38
|
headers: request.headers
|
|
34
39
|
});
|
|
@@ -36,56 +41,200 @@ async function renderAppToResponse(app, request, {
|
|
|
36
41
|
headers,
|
|
37
42
|
statusCode = 200,
|
|
38
43
|
redirectUrl
|
|
39
|
-
} = http.state;
|
|
44
|
+
} = renderDetails.http.state;
|
|
40
45
|
if (redirectUrl) {
|
|
41
46
|
return redirect(redirectUrl, {
|
|
42
47
|
status: statusCode,
|
|
43
48
|
headers
|
|
44
49
|
});
|
|
45
50
|
}
|
|
51
|
+
const content = await renderAppDetailsToHtmlString(renderDetails, request, {
|
|
52
|
+
assets,
|
|
53
|
+
renderHtml
|
|
54
|
+
});
|
|
55
|
+
return html(content, {
|
|
56
|
+
headers,
|
|
57
|
+
status: statusCode
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
async function renderAppToStreamedResponse(app, request, {
|
|
61
|
+
assets,
|
|
62
|
+
extract,
|
|
63
|
+
renderHtml
|
|
64
|
+
} = {}) {
|
|
65
|
+
const headers = new Headers();
|
|
66
|
+
const stream = new TransformStream();
|
|
67
|
+
const assetContext = {
|
|
68
|
+
userAgent: request.headers.get('User-Agent')
|
|
69
|
+
};
|
|
70
|
+
const guaranteedAssets = await assets?.assets({
|
|
71
|
+
context: assetContext
|
|
72
|
+
});
|
|
73
|
+
if (guaranteedAssets) {
|
|
74
|
+
for (const style of guaranteedAssets.styles) {
|
|
75
|
+
headers.append('Link', preloadHeader(styleAssetPreloadAttributes(style)));
|
|
76
|
+
}
|
|
77
|
+
for (const script of guaranteedAssets.scripts) {
|
|
78
|
+
headers.append('Link', preloadHeader(scriptAssetPreloadAttributes(script)));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
renderResponseToStream();
|
|
82
|
+
return html(stream.readable, {
|
|
83
|
+
headers,
|
|
84
|
+
status: 200
|
|
85
|
+
});
|
|
86
|
+
async function renderResponseToStream() {
|
|
87
|
+
const renderDetails = await serverRenderDetailsForApp(app, {
|
|
88
|
+
extract,
|
|
89
|
+
url: request.url,
|
|
90
|
+
headers: request.headers
|
|
91
|
+
});
|
|
92
|
+
const content = await renderAppDetailsToHtmlString(renderDetails, request, {
|
|
93
|
+
assets,
|
|
94
|
+
renderHtml
|
|
95
|
+
});
|
|
96
|
+
const writer = stream.writable.getWriter();
|
|
97
|
+
await writer.write(content);
|
|
98
|
+
await writer.close();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async function serverRenderDetailsForApp(app, {
|
|
102
|
+
url,
|
|
103
|
+
headers,
|
|
104
|
+
extract: extractOptions
|
|
105
|
+
} = {}) {
|
|
106
|
+
const html = new HtmlManager();
|
|
107
|
+
const asyncAssets = new AsyncAssetManager();
|
|
108
|
+
const http = new HttpManager({
|
|
109
|
+
headers
|
|
110
|
+
});
|
|
111
|
+
const {
|
|
112
|
+
decorate,
|
|
113
|
+
...rest
|
|
114
|
+
} = extractOptions ?? {};
|
|
115
|
+
const rendered = await extract(app, {
|
|
116
|
+
decorate(app) {
|
|
117
|
+
return /*#__PURE__*/jsx(ServerContext, {
|
|
118
|
+
asyncAssets: asyncAssets,
|
|
119
|
+
http: http,
|
|
120
|
+
html: html,
|
|
121
|
+
url: url,
|
|
122
|
+
children: decorate?.(app) ?? app
|
|
123
|
+
});
|
|
124
|
+
},
|
|
125
|
+
...rest
|
|
126
|
+
});
|
|
127
|
+
return {
|
|
128
|
+
rendered,
|
|
129
|
+
http,
|
|
130
|
+
html,
|
|
131
|
+
asyncAssets
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
async function renderAppDetailsToHtmlString(details, request, {
|
|
135
|
+
assets,
|
|
136
|
+
renderHtml = defaultRenderHtml
|
|
137
|
+
} = {}) {
|
|
138
|
+
const {
|
|
139
|
+
html: htmlManager,
|
|
140
|
+
http,
|
|
141
|
+
rendered,
|
|
142
|
+
asyncAssets
|
|
143
|
+
} = details;
|
|
46
144
|
const usedAssets = asyncAssets.used({
|
|
47
145
|
timing: 'load'
|
|
48
146
|
});
|
|
49
|
-
const
|
|
147
|
+
const assetContext = {
|
|
50
148
|
userAgent: request.headers.get('User-Agent')
|
|
51
149
|
};
|
|
52
|
-
const [
|
|
150
|
+
const [entryAssets, preloadAssets] = assets ? await Promise.all([assets.assets({
|
|
53
151
|
async: usedAssets,
|
|
54
|
-
|
|
55
|
-
}), assets.scripts({
|
|
56
|
-
async: usedAssets,
|
|
57
|
-
options: assetOptions
|
|
152
|
+
context: assetContext
|
|
58
153
|
}), assets.asyncAssets(asyncAssets.used({
|
|
59
154
|
timing: 'preload'
|
|
60
155
|
}), {
|
|
61
|
-
|
|
62
|
-
})]) : [
|
|
156
|
+
context: assetContext
|
|
157
|
+
})]) : [];
|
|
63
158
|
const htmlElement = await renderHtml(rendered, request, {
|
|
64
159
|
html: htmlManager,
|
|
65
160
|
http,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
preload
|
|
69
|
-
});
|
|
70
|
-
return html(renderHtmlToString(htmlElement), {
|
|
71
|
-
headers,
|
|
72
|
-
status: statusCode
|
|
161
|
+
assets: entryAssets,
|
|
162
|
+
preloadAssets
|
|
73
163
|
});
|
|
164
|
+
return renderHtmlToString(htmlElement);
|
|
74
165
|
}
|
|
75
|
-
function defaultRenderHtml(content, request, {
|
|
166
|
+
const defaultRenderHtml = function defaultRenderHtml(content, request, {
|
|
76
167
|
html,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
preload
|
|
168
|
+
assets,
|
|
169
|
+
preloadAssets
|
|
80
170
|
}) {
|
|
171
|
+
const baseUrl = new URL(request.url);
|
|
81
172
|
return /*#__PURE__*/jsx(Html, {
|
|
82
|
-
url: new URL(request.url),
|
|
83
173
|
manager: html,
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
174
|
+
headEndContent: /*#__PURE__*/jsxs(Fragment, {
|
|
175
|
+
children: [assets && [...assets.styles].map(style => {
|
|
176
|
+
const attributes = styleAssetAttributes(style, {
|
|
177
|
+
baseUrl
|
|
178
|
+
});
|
|
179
|
+
return /*#__PURE__*/jsx("link", {
|
|
180
|
+
...attributes
|
|
181
|
+
}, style.source);
|
|
182
|
+
}), assets && [...assets.scripts].map(script => {
|
|
183
|
+
const isModule = script.attributes.type === 'module';
|
|
184
|
+
const attributes = scriptAssetAttributes(script, {
|
|
185
|
+
baseUrl
|
|
186
|
+
});
|
|
187
|
+
if (isModule) {
|
|
188
|
+
return /*#__PURE__*/jsxs(Fragment$1, {
|
|
189
|
+
children: [/*#__PURE__*/jsx("link", {
|
|
190
|
+
...scriptAssetPreloadAttributes(script)
|
|
191
|
+
}), /*#__PURE__*/jsx("script", {
|
|
192
|
+
...attributes,
|
|
193
|
+
async: true
|
|
194
|
+
})]
|
|
195
|
+
}, script.source);
|
|
196
|
+
}
|
|
197
|
+
return /*#__PURE__*/jsx("script", {
|
|
198
|
+
...attributes,
|
|
199
|
+
defer: true
|
|
200
|
+
}, script.source);
|
|
201
|
+
}), preloadAssets && [...preloadAssets.styles].map(style => {
|
|
202
|
+
const attributes = styleAssetPreloadAttributes(style, {
|
|
203
|
+
baseUrl
|
|
204
|
+
});
|
|
205
|
+
return /*#__PURE__*/jsx("link", {
|
|
206
|
+
...attributes
|
|
207
|
+
}, style.source);
|
|
208
|
+
}), preloadAssets && [...preloadAssets.scripts].map(script => {
|
|
209
|
+
const attributes = scriptAssetPreloadAttributes(script, {
|
|
210
|
+
baseUrl
|
|
211
|
+
});
|
|
212
|
+
return /*#__PURE__*/jsx("link", {
|
|
213
|
+
...attributes
|
|
214
|
+
}, script.source);
|
|
215
|
+
})]
|
|
216
|
+
}),
|
|
87
217
|
children: content
|
|
88
218
|
});
|
|
219
|
+
};
|
|
220
|
+
function preloadHeader(attributes) {
|
|
221
|
+
const {
|
|
222
|
+
as,
|
|
223
|
+
rel = 'preload',
|
|
224
|
+
href,
|
|
225
|
+
crossOrigin,
|
|
226
|
+
crossorigin
|
|
227
|
+
} = attributes;
|
|
228
|
+
|
|
229
|
+
// Support both property and attribute versions of the casing
|
|
230
|
+
const finalCrossOrigin = crossOrigin ?? crossorigin;
|
|
231
|
+
let header = `<${href}>; rel="${rel}"; as="${as}"`;
|
|
232
|
+
if (finalCrossOrigin === '' || finalCrossOrigin === true) {
|
|
233
|
+
header += `; crossorigin`;
|
|
234
|
+
} else if (typeof finalCrossOrigin === 'string') {
|
|
235
|
+
header += `; crossorigin="${finalCrossOrigin}"`;
|
|
236
|
+
}
|
|
237
|
+
return header;
|
|
89
238
|
}
|
|
90
239
|
|
|
91
|
-
export { createServerRender, renderAppToResponse };
|
|
240
|
+
export { createServerRender, renderAppToResponse, renderAppToStreamedResponse };
|