@quilted/quilt 0.5.136 → 0.5.138
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 +20 -0
- package/build/cjs/index.cjs +34 -0
- package/build/cjs/server/ServerContext.cjs +3 -3
- package/build/cjs/server/index.cjs +28 -14
- package/build/cjs/server/preload.cjs +37 -0
- package/build/cjs/server/request-router.cjs +73 -57
- package/build/cjs/static/StaticContext.cjs +3 -3
- package/build/cjs/static/index.cjs +30 -44
- package/build/cjs/static/render.cjs +9 -6
- package/build/esm/index.mjs +2 -0
- package/build/esm/server/ServerContext.mjs +3 -3
- package/build/esm/server/index.mjs +3 -2
- package/build/esm/server/preload.mjs +35 -0
- package/build/esm/server/request-router.mjs +55 -39
- package/build/esm/static/StaticContext.mjs +3 -3
- package/build/esm/static/index.mjs +25 -39
- package/build/esm/static/render.mjs +7 -4
- package/build/esnext/index.esnext +2 -0
- package/build/esnext/server/ServerContext.esnext +3 -3
- package/build/esnext/server/index.esnext +3 -2
- package/build/esnext/server/preload.esnext +35 -0
- package/build/esnext/server/request-router.esnext +55 -39
- package/build/esnext/static/StaticContext.esnext +3 -3
- package/build/esnext/static/index.esnext +25 -39
- package/build/esnext/static/render.esnext +7 -4
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/typescript/assets.d.ts +7 -0
- package/build/typescript/assets.d.ts.map +1 -0
- package/build/typescript/index.d.ts +3 -0
- package/build/typescript/index.d.ts.map +1 -1
- package/build/typescript/magic/assets.d.ts +3 -0
- package/build/typescript/magic/assets.d.ts.map +1 -0
- package/build/typescript/server/ServerContext.d.ts +5 -5
- package/build/typescript/server/ServerContext.d.ts.map +1 -1
- package/build/typescript/server/index.d.ts +4 -3
- package/build/typescript/server/index.d.ts.map +1 -1
- package/build/typescript/server/preload.d.ts +8 -0
- package/build/typescript/server/preload.d.ts.map +1 -0
- package/build/typescript/server/request-router.d.ts +23 -14
- package/build/typescript/server/request-router.d.ts.map +1 -1
- package/build/typescript/static/StaticContext.d.ts +5 -5
- package/build/typescript/static/StaticContext.d.ts.map +1 -1
- package/build/typescript/static/index.d.ts +3 -5
- package/build/typescript/static/index.d.ts.map +1 -1
- package/build/typescript/static/render.d.ts +6 -4
- package/build/typescript/static/render.d.ts.map +1 -1
- package/package.json +5 -3
- package/source/assets.ts +7 -0
- package/source/index.ts +19 -0
- package/source/magic/assets.ts +5 -0
- package/source/server/ServerContext.tsx +7 -10
- package/source/server/index.ts +19 -13
- package/source/server/preload.ts +69 -0
- package/source/server/request-router.tsx +129 -73
- package/source/static/StaticContext.tsx +7 -10
- package/source/static/index.tsx +30 -41
- package/source/static/render.tsx +9 -12
- package/tsconfig.json +2 -0
- package/source/magic/asset-manifest.ts +0 -5
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import {Fragment, type ReactElement
|
|
1
|
+
import {Fragment, type ReactElement} from 'react';
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
styleAssetAttributes,
|
|
5
5
|
styleAssetPreloadAttributes,
|
|
6
6
|
scriptAssetAttributes,
|
|
7
7
|
scriptAssetPreloadAttributes,
|
|
8
|
-
type
|
|
9
|
-
type
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
type AssetsCacheKey,
|
|
9
|
+
type BrowserAssets,
|
|
10
|
+
type BrowserAssetsEntry,
|
|
11
|
+
} from '@quilted/assets';
|
|
12
|
+
import {AssetsManager} from '@quilted/react-assets/server';
|
|
12
13
|
import {HttpManager} from '@quilted/react-http/server';
|
|
13
14
|
import {
|
|
14
15
|
renderHtmlToString,
|
|
@@ -30,39 +31,52 @@ import type {
|
|
|
30
31
|
|
|
31
32
|
import {ServerContext} from './ServerContext';
|
|
32
33
|
|
|
33
|
-
export interface ServerRenderOptions<
|
|
34
|
+
export interface ServerRenderOptions<
|
|
35
|
+
Context = RequestContext,
|
|
36
|
+
CacheKey = AssetsCacheKey,
|
|
37
|
+
> {
|
|
34
38
|
stream?: 'headers' | false;
|
|
35
|
-
assets?:
|
|
36
|
-
extract?: ExtractOptions
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
assets?: BrowserAssets<CacheKey>;
|
|
40
|
+
extract?: Omit<ExtractOptions, 'context'> & {
|
|
41
|
+
readonly context?:
|
|
42
|
+
| ServerRenderRequestContext
|
|
43
|
+
| ((
|
|
44
|
+
request: EnhancedRequest,
|
|
45
|
+
context: Context,
|
|
46
|
+
) => ServerRenderRequestContext);
|
|
47
|
+
};
|
|
41
48
|
renderHtml?(
|
|
42
49
|
content: string | undefined,
|
|
43
|
-
request: Request,
|
|
44
50
|
details: Pick<ServerRenderAppDetails, 'http' | 'html'> & {
|
|
45
|
-
readonly
|
|
46
|
-
readonly
|
|
51
|
+
readonly request: EnhancedRequest;
|
|
52
|
+
readonly context: Context;
|
|
53
|
+
readonly assets?: BrowserAssetsEntry;
|
|
54
|
+
readonly preloadAssets?: BrowserAssetsEntry;
|
|
47
55
|
},
|
|
48
56
|
): ReactElement<any> | Promise<ReactElement<any>>;
|
|
49
57
|
}
|
|
50
58
|
|
|
51
|
-
export interface ServerRenderAppDetails
|
|
59
|
+
export interface ServerRenderAppDetails<
|
|
60
|
+
_Context = RequestContext,
|
|
61
|
+
CacheKey = AssetsCacheKey,
|
|
62
|
+
> {
|
|
52
63
|
readonly http: HttpManager;
|
|
53
64
|
readonly html: HtmlManager;
|
|
65
|
+
readonly assets: AssetsManager<CacheKey>;
|
|
54
66
|
readonly rendered?: string;
|
|
55
|
-
readonly asyncAssets: AsyncAssetManager;
|
|
56
67
|
}
|
|
57
68
|
|
|
58
|
-
export function createServerRender<
|
|
69
|
+
export function createServerRender<
|
|
70
|
+
Context = RequestContext,
|
|
71
|
+
CacheKey = AssetsCacheKey,
|
|
72
|
+
>(
|
|
59
73
|
getApp:
|
|
60
74
|
| ReactElement<any>
|
|
61
75
|
| ((
|
|
62
76
|
request: EnhancedRequest,
|
|
63
77
|
context: Context,
|
|
64
78
|
) => ReactElement<any> | Promise<ReactElement<any>>),
|
|
65
|
-
{
|
|
79
|
+
{stream, ...options}: ServerRenderOptions<Context, CacheKey> = {},
|
|
66
80
|
): RequestHandler<Context> {
|
|
67
81
|
return async (request, requestContext) => {
|
|
68
82
|
const accepts = request.headers.get('Accept');
|
|
@@ -77,36 +91,46 @@ export function createServerRender<Context = RequestContext>(
|
|
|
77
91
|
typeof getApp === 'function'
|
|
78
92
|
? () => getApp(request, requestContext)
|
|
79
93
|
: getApp,
|
|
80
|
-
request,
|
|
81
94
|
{
|
|
82
95
|
...options,
|
|
96
|
+
request,
|
|
97
|
+
context: requestContext,
|
|
83
98
|
extract: {
|
|
84
99
|
...options.extract,
|
|
85
100
|
context:
|
|
86
|
-
options.extract
|
|
87
|
-
|
|
88
|
-
|
|
101
|
+
typeof options.extract?.context === 'function'
|
|
102
|
+
? options.extract.context(request, requestContext)
|
|
103
|
+
: options.extract?.context,
|
|
89
104
|
},
|
|
90
105
|
},
|
|
91
106
|
);
|
|
92
107
|
};
|
|
93
108
|
}
|
|
94
109
|
|
|
95
|
-
export async function renderAppToResponse
|
|
110
|
+
export async function renderAppToResponse<
|
|
111
|
+
Context = RequestContext,
|
|
112
|
+
CacheKey = AssetsCacheKey,
|
|
113
|
+
>(
|
|
96
114
|
getApp:
|
|
97
115
|
| ReactElement<any>
|
|
98
116
|
| (() => ReactElement<any> | Promise<ReactElement<any>>),
|
|
99
|
-
request: Request,
|
|
100
117
|
{
|
|
118
|
+
request,
|
|
119
|
+
context,
|
|
101
120
|
assets,
|
|
102
121
|
extract,
|
|
103
122
|
renderHtml,
|
|
104
|
-
}: Pick<
|
|
123
|
+
}: Pick<
|
|
124
|
+
ServerRenderOptions<Context, CacheKey>,
|
|
125
|
+
'assets' | 'renderHtml' | 'extract'
|
|
126
|
+
> & {readonly request: EnhancedRequest; readonly context: Context},
|
|
105
127
|
) {
|
|
106
128
|
const app = typeof getApp === 'function' ? await getApp() : getApp;
|
|
129
|
+
const cacheKey = (await assets?.cacheKey?.(request)) as CacheKey;
|
|
107
130
|
|
|
108
131
|
const renderDetails = await serverRenderDetailsForApp(app, {
|
|
109
132
|
extract,
|
|
133
|
+
cacheKey,
|
|
110
134
|
url: request.url,
|
|
111
135
|
headers: request.headers,
|
|
112
136
|
});
|
|
@@ -120,10 +144,15 @@ export async function renderAppToResponse(
|
|
|
120
144
|
});
|
|
121
145
|
}
|
|
122
146
|
|
|
123
|
-
const content = await renderAppDetailsToHtmlString
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
147
|
+
const content = await renderAppDetailsToHtmlString<Context, CacheKey>(
|
|
148
|
+
renderDetails,
|
|
149
|
+
{
|
|
150
|
+
request,
|
|
151
|
+
context,
|
|
152
|
+
assets,
|
|
153
|
+
renderHtml,
|
|
154
|
+
},
|
|
155
|
+
);
|
|
127
156
|
|
|
128
157
|
return html(content, {
|
|
129
158
|
headers,
|
|
@@ -131,22 +160,29 @@ export async function renderAppToResponse(
|
|
|
131
160
|
});
|
|
132
161
|
}
|
|
133
162
|
|
|
134
|
-
export async function renderAppToStreamedResponse
|
|
163
|
+
export async function renderAppToStreamedResponse<
|
|
164
|
+
Context = RequestContext,
|
|
165
|
+
CacheKey = AssetsCacheKey,
|
|
166
|
+
>(
|
|
135
167
|
getApp:
|
|
136
168
|
| ReactElement<any>
|
|
137
169
|
| (() => ReactElement<any> | Promise<ReactElement<any>>),
|
|
138
|
-
request: Request,
|
|
139
170
|
{
|
|
171
|
+
request,
|
|
172
|
+
context,
|
|
140
173
|
assets,
|
|
141
174
|
extract,
|
|
142
175
|
renderHtml,
|
|
143
|
-
}: Pick<
|
|
176
|
+
}: Pick<
|
|
177
|
+
ServerRenderOptions<Context, CacheKey>,
|
|
178
|
+
'assets' | 'renderHtml' | 'extract'
|
|
179
|
+
> & {readonly request: EnhancedRequest; readonly context: Context},
|
|
144
180
|
) {
|
|
145
181
|
const headers = new Headers();
|
|
146
182
|
const stream = new TransformStream();
|
|
147
183
|
|
|
148
|
-
const
|
|
149
|
-
const guaranteedAssets = await assets?.
|
|
184
|
+
const cacheKey = (await assets?.cacheKey?.(request)) as CacheKey;
|
|
185
|
+
const guaranteedAssets = await assets?.entry({cacheKey});
|
|
150
186
|
|
|
151
187
|
if (guaranteedAssets) {
|
|
152
188
|
for (const style of guaranteedAssets.styles) {
|
|
@@ -173,14 +209,20 @@ export async function renderAppToStreamedResponse(
|
|
|
173
209
|
|
|
174
210
|
const renderDetails = await serverRenderDetailsForApp(app, {
|
|
175
211
|
extract,
|
|
212
|
+
cacheKey,
|
|
176
213
|
url: request.url,
|
|
177
214
|
headers: request.headers,
|
|
178
215
|
});
|
|
179
216
|
|
|
180
|
-
const content = await renderAppDetailsToHtmlString
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
217
|
+
const content = await renderAppDetailsToHtmlString<Context, CacheKey>(
|
|
218
|
+
renderDetails,
|
|
219
|
+
{
|
|
220
|
+
request,
|
|
221
|
+
context,
|
|
222
|
+
assets,
|
|
223
|
+
renderHtml,
|
|
224
|
+
},
|
|
225
|
+
);
|
|
184
226
|
|
|
185
227
|
const encoder = new TextEncoder();
|
|
186
228
|
const writer = stream.writable.getWriter();
|
|
@@ -189,34 +231,34 @@ export async function renderAppToStreamedResponse(
|
|
|
189
231
|
}
|
|
190
232
|
}
|
|
191
233
|
|
|
192
|
-
async function serverRenderDetailsForApp
|
|
234
|
+
async function serverRenderDetailsForApp<
|
|
235
|
+
Context = RequestContext,
|
|
236
|
+
CacheKey = AssetsCacheKey,
|
|
237
|
+
>(
|
|
193
238
|
app: ReactElement<any>,
|
|
194
239
|
{
|
|
195
240
|
url,
|
|
196
241
|
headers,
|
|
242
|
+
cacheKey,
|
|
197
243
|
extract: extractOptions,
|
|
198
244
|
}: Pick<ServerRenderOptions, 'extract'> & {
|
|
199
245
|
url?: string | URL;
|
|
246
|
+
cacheKey?: CacheKey;
|
|
200
247
|
headers?: NonNullable<
|
|
201
248
|
ConstructorParameters<typeof HttpManager>[0]
|
|
202
249
|
>['headers'];
|
|
203
250
|
} = {},
|
|
204
|
-
): Promise<ServerRenderAppDetails
|
|
251
|
+
): Promise<ServerRenderAppDetails<Context, CacheKey>> {
|
|
205
252
|
const html = new HtmlManager();
|
|
206
|
-
const asyncAssets = new AsyncAssetManager();
|
|
207
253
|
const http = new HttpManager({headers});
|
|
254
|
+
const assets = new AssetsManager<CacheKey>({cacheKey});
|
|
208
255
|
|
|
209
256
|
const {decorate, ...rest} = extractOptions ?? {};
|
|
210
257
|
|
|
211
258
|
const rendered = await extract(app, {
|
|
212
259
|
decorate(app) {
|
|
213
260
|
return (
|
|
214
|
-
<ServerContext
|
|
215
|
-
asyncAssets={asyncAssets}
|
|
216
|
-
http={http}
|
|
217
|
-
html={html}
|
|
218
|
-
url={url}
|
|
219
|
-
>
|
|
261
|
+
<ServerContext http={http} html={html} url={url} assets={assets}>
|
|
220
262
|
{decorate?.(app) ?? app}
|
|
221
263
|
</ServerContext>
|
|
222
264
|
);
|
|
@@ -224,32 +266,42 @@ async function serverRenderDetailsForApp(
|
|
|
224
266
|
...rest,
|
|
225
267
|
});
|
|
226
268
|
|
|
227
|
-
return {rendered, http, html,
|
|
269
|
+
return {rendered, http, html, assets};
|
|
228
270
|
}
|
|
229
271
|
|
|
230
|
-
async function renderAppDetailsToHtmlString
|
|
231
|
-
|
|
232
|
-
|
|
272
|
+
async function renderAppDetailsToHtmlString<
|
|
273
|
+
Context = RequestContext,
|
|
274
|
+
CacheKey = AssetsCacheKey,
|
|
275
|
+
>(
|
|
276
|
+
details: ServerRenderAppDetails<Context, CacheKey>,
|
|
233
277
|
{
|
|
278
|
+
request,
|
|
279
|
+
context,
|
|
234
280
|
assets,
|
|
235
281
|
renderHtml = defaultRenderHtml,
|
|
236
|
-
}: Pick<ServerRenderOptions, 'assets' | 'renderHtml'>
|
|
282
|
+
}: Pick<ServerRenderOptions<Context, CacheKey>, 'assets' | 'renderHtml'> & {
|
|
283
|
+
readonly request: EnhancedRequest;
|
|
284
|
+
readonly context: Context;
|
|
285
|
+
readonly cacheKey?: Partial<CacheKey>;
|
|
286
|
+
},
|
|
237
287
|
) {
|
|
238
|
-
const {html: htmlManager,
|
|
288
|
+
const {http, rendered, html: htmlManager, assets: assetsManager} = details;
|
|
239
289
|
|
|
240
|
-
const
|
|
241
|
-
const
|
|
290
|
+
const cacheKey = assetsManager.cacheKey as CacheKey;
|
|
291
|
+
const usedModules = assetsManager.usedModules({timing: 'load'});
|
|
242
292
|
|
|
243
293
|
const [entryAssets, preloadAssets] = assets
|
|
244
294
|
? await Promise.all([
|
|
245
|
-
assets.
|
|
246
|
-
assets.
|
|
247
|
-
|
|
295
|
+
assets.entry({modules: usedModules, cacheKey}),
|
|
296
|
+
assets.modules(assetsManager.usedModules({timing: 'preload'}), {
|
|
297
|
+
cacheKey,
|
|
248
298
|
}),
|
|
249
299
|
])
|
|
250
300
|
: [];
|
|
251
301
|
|
|
252
|
-
const htmlElement = await renderHtml(rendered,
|
|
302
|
+
const htmlElement = await renderHtml(rendered, {
|
|
303
|
+
request,
|
|
304
|
+
context,
|
|
253
305
|
html: htmlManager,
|
|
254
306
|
http,
|
|
255
307
|
assets: entryAssets,
|
|
@@ -260,7 +312,7 @@ async function renderAppDetailsToHtmlString(
|
|
|
260
312
|
}
|
|
261
313
|
|
|
262
314
|
const defaultRenderHtml: NonNullable<ServerRenderOptions<any>['renderHtml']> =
|
|
263
|
-
function defaultRenderHtml(content, request,
|
|
315
|
+
function defaultRenderHtml(content, {request, html, assets, preloadAssets}) {
|
|
264
316
|
const baseUrl = new URL(request.url);
|
|
265
317
|
|
|
266
318
|
return (
|
|
@@ -269,14 +321,14 @@ const defaultRenderHtml: NonNullable<ServerRenderOptions<any>['renderHtml']> =
|
|
|
269
321
|
headEndContent={
|
|
270
322
|
<>
|
|
271
323
|
{assets &&
|
|
272
|
-
|
|
324
|
+
assets.styles.map((style) => {
|
|
273
325
|
const attributes = styleAssetAttributes(style, {baseUrl});
|
|
274
|
-
return <link key={style.source} {...attributes} />;
|
|
326
|
+
return <link key={style.source} {...(attributes as any)} />;
|
|
275
327
|
})}
|
|
276
328
|
|
|
277
329
|
{assets &&
|
|
278
|
-
|
|
279
|
-
const isModule = script.attributes
|
|
330
|
+
assets.scripts.map((script) => {
|
|
331
|
+
const isModule = script.attributes?.type === 'module';
|
|
280
332
|
|
|
281
333
|
const attributes = scriptAssetAttributes(script, {
|
|
282
334
|
baseUrl,
|
|
@@ -285,31 +337,35 @@ const defaultRenderHtml: NonNullable<ServerRenderOptions<any>['renderHtml']> =
|
|
|
285
337
|
if (isModule) {
|
|
286
338
|
return (
|
|
287
339
|
<Fragment key={script.source}>
|
|
288
|
-
<link
|
|
289
|
-
|
|
340
|
+
<link
|
|
341
|
+
{...(scriptAssetPreloadAttributes(script) as any)}
|
|
342
|
+
/>
|
|
343
|
+
<script {...(attributes as any)} async />
|
|
290
344
|
</Fragment>
|
|
291
345
|
);
|
|
292
346
|
}
|
|
293
347
|
|
|
294
|
-
return
|
|
348
|
+
return (
|
|
349
|
+
<script key={script.source} {...(attributes as any)} defer />
|
|
350
|
+
);
|
|
295
351
|
})}
|
|
296
352
|
|
|
297
353
|
{preloadAssets &&
|
|
298
|
-
|
|
354
|
+
preloadAssets.styles.map((style) => {
|
|
299
355
|
const attributes = styleAssetPreloadAttributes(style, {
|
|
300
356
|
baseUrl,
|
|
301
357
|
});
|
|
302
358
|
|
|
303
|
-
return <link key={style.source} {...attributes} />;
|
|
359
|
+
return <link key={style.source} {...(attributes as any)} />;
|
|
304
360
|
})}
|
|
305
361
|
|
|
306
362
|
{preloadAssets &&
|
|
307
|
-
|
|
363
|
+
preloadAssets.scripts.map((script) => {
|
|
308
364
|
const attributes = scriptAssetPreloadAttributes(script, {
|
|
309
365
|
baseUrl,
|
|
310
366
|
});
|
|
311
367
|
|
|
312
|
-
return <link key={script.source} {...attributes} />;
|
|
368
|
+
return <link key={script.source} {...(attributes as any)} />;
|
|
313
369
|
})}
|
|
314
370
|
</>
|
|
315
371
|
}
|
|
@@ -319,7 +375,7 @@ const defaultRenderHtml: NonNullable<ServerRenderOptions<any>['renderHtml']> =
|
|
|
319
375
|
);
|
|
320
376
|
};
|
|
321
377
|
|
|
322
|
-
function preloadHeader(attributes:
|
|
378
|
+
function preloadHeader(attributes: Partial<HTMLLinkElement>) {
|
|
323
379
|
const {
|
|
324
380
|
as,
|
|
325
381
|
rel = 'preload',
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import type {PropsWithChildren} from 'react';
|
|
2
2
|
|
|
3
|
+
import {AssetsContext, type AssetsManager} from '@quilted/react-assets/server';
|
|
3
4
|
import {InitialUrlContext} from '@quilted/react-router';
|
|
4
|
-
import {HtmlContext} from '@quilted/react-html/server';
|
|
5
|
-
import type
|
|
6
|
-
import {AsyncAssetContext} from '@quilted/react-async/server';
|
|
7
|
-
import type {AsyncAssetManager} from '@quilted/react-async/server';
|
|
8
|
-
import {HttpServerContext} from '@quilted/react-http/server';
|
|
9
|
-
import type {HttpManager} from '@quilted/react-http/server';
|
|
5
|
+
import {HtmlContext, type HtmlManager} from '@quilted/react-html/server';
|
|
6
|
+
import {HttpServerContext, type HttpManager} from '@quilted/react-http/server';
|
|
10
7
|
|
|
11
8
|
import {maybeWrapContext} from '../utilities/react';
|
|
12
9
|
|
|
@@ -14,21 +11,21 @@ interface Props {
|
|
|
14
11
|
url?: string | URL;
|
|
15
12
|
html?: HtmlManager;
|
|
16
13
|
http?: HttpManager;
|
|
17
|
-
|
|
14
|
+
assets?: AssetsManager;
|
|
18
15
|
}
|
|
19
16
|
|
|
20
17
|
export function StaticContext({
|
|
21
18
|
url,
|
|
22
19
|
html,
|
|
23
20
|
http,
|
|
24
|
-
|
|
21
|
+
assets,
|
|
25
22
|
children,
|
|
26
23
|
}: PropsWithChildren<Props>) {
|
|
27
24
|
const normalizedUrl = typeof url === 'string' ? new URL(url) : url;
|
|
28
25
|
|
|
29
26
|
return maybeWrapContext(
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
AssetsContext,
|
|
28
|
+
assets,
|
|
32
29
|
maybeWrapContext(
|
|
33
30
|
HttpServerContext,
|
|
34
31
|
http,
|
package/source/static/index.tsx
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import {Fragment, type ComponentType} from 'react';
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
styleAssetAttributes,
|
|
5
5
|
styleAssetPreloadAttributes,
|
|
6
6
|
scriptAssetAttributes,
|
|
7
7
|
scriptAssetPreloadAttributes,
|
|
8
|
-
type
|
|
9
|
-
} from '@quilted/
|
|
8
|
+
type BrowserAssets,
|
|
9
|
+
} from '@quilted/assets';
|
|
10
10
|
import {renderHtmlToString, Html} from '@quilted/react-html/server';
|
|
11
11
|
import type {RouteDefinition} from '@quilted/react-router';
|
|
12
12
|
import {
|
|
@@ -32,7 +32,7 @@ interface RenderableRoute {
|
|
|
32
32
|
|
|
33
33
|
export interface Options {
|
|
34
34
|
routes: string[];
|
|
35
|
-
assets:
|
|
35
|
+
assets: BrowserAssets<any>;
|
|
36
36
|
crawl?: boolean;
|
|
37
37
|
baseUrl?: string;
|
|
38
38
|
prettify?: boolean;
|
|
@@ -212,13 +212,16 @@ export async function renderStatic(
|
|
|
212
212
|
forceFallback: fallback ? url.pathname : undefined,
|
|
213
213
|
});
|
|
214
214
|
|
|
215
|
+
const initialCacheKey = await assets.cacheKey?.(new Request(url));
|
|
216
|
+
|
|
215
217
|
const {
|
|
216
218
|
http,
|
|
217
219
|
html: htmlManager,
|
|
220
|
+
assets: assetsManager,
|
|
218
221
|
markup,
|
|
219
|
-
asyncAssets,
|
|
220
222
|
} = await renderApp(<App />, {
|
|
221
223
|
url,
|
|
224
|
+
cacheKey: initialCacheKey,
|
|
222
225
|
decorate(app) {
|
|
223
226
|
return (
|
|
224
227
|
<StaticRendererContext.Provider value={routeRecorder}>
|
|
@@ -228,71 +231,57 @@ export async function renderStatic(
|
|
|
228
231
|
},
|
|
229
232
|
});
|
|
230
233
|
|
|
231
|
-
const
|
|
234
|
+
const cacheKey = assetsManager.cacheKey;
|
|
232
235
|
|
|
233
|
-
const [
|
|
234
|
-
assets.
|
|
235
|
-
|
|
236
|
-
|
|
236
|
+
const [mainAssets, preloadAssets] = await Promise.all([
|
|
237
|
+
assets.entry({
|
|
238
|
+
modules: assetsManager.usedModules({timing: 'load'}),
|
|
239
|
+
cacheKey,
|
|
240
|
+
}),
|
|
241
|
+
assets.modules(assetsManager.usedModules({timing: 'preload'}), {
|
|
242
|
+
cacheKey,
|
|
237
243
|
}),
|
|
238
|
-
assets.assets({async: usedAssets, context: {modules: false}}),
|
|
239
244
|
]);
|
|
240
245
|
|
|
241
|
-
// We don’t want to load styles from both bundles, so we only use module styles,
|
|
242
|
-
// since modules are intended to be the default and CSS (usually) doesn’t
|
|
243
|
-
// have features that meaningfully break older user agents.
|
|
244
|
-
const styles =
|
|
245
|
-
moduleAssets.styles.length > 0
|
|
246
|
-
? moduleAssets.styles
|
|
247
|
-
: nomoduleAssets.styles;
|
|
248
|
-
|
|
249
246
|
const minifiedHtml = renderHtmlToString(
|
|
250
247
|
<Html
|
|
251
248
|
manager={htmlManager}
|
|
252
249
|
headEndContent={
|
|
253
250
|
<>
|
|
254
|
-
{[...styles].map((style) => {
|
|
251
|
+
{[...mainAssets.styles].map((style) => {
|
|
255
252
|
const attributes = styleAssetAttributes(style, {baseUrl: url});
|
|
256
|
-
return <link key={style.source} {...attributes} />;
|
|
257
|
-
})}
|
|
258
|
-
|
|
259
|
-
{[...moduleAssets.scripts].map((script) => {
|
|
260
|
-
const attributes = scriptAssetAttributes(script, {
|
|
261
|
-
baseUrl: url,
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
return <script key={script.source} {...attributes} />;
|
|
253
|
+
return <link key={style.source} {...(attributes as any)} />;
|
|
265
254
|
})}
|
|
266
255
|
|
|
267
|
-
{[...
|
|
256
|
+
{[...mainAssets.scripts].map((script) => {
|
|
268
257
|
const attributes = scriptAssetAttributes(script, {
|
|
269
258
|
baseUrl: url,
|
|
270
259
|
});
|
|
271
260
|
|
|
272
|
-
return (
|
|
273
|
-
<script
|
|
274
|
-
|
|
275
|
-
{...attributes}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
/>
|
|
261
|
+
return attributes.type === 'module' ? (
|
|
262
|
+
<Fragment key={script.source}>
|
|
263
|
+
<link {...(scriptAssetPreloadAttributes(script) as any)} />
|
|
264
|
+
<script {...(attributes as any)} />
|
|
265
|
+
</Fragment>
|
|
266
|
+
) : (
|
|
267
|
+
<script key={script.source} {...(attributes as any)} />
|
|
279
268
|
);
|
|
280
269
|
})}
|
|
281
270
|
|
|
282
|
-
{[...
|
|
271
|
+
{[...preloadAssets.styles].map((style) => {
|
|
283
272
|
const attributes = styleAssetPreloadAttributes(style, {
|
|
284
273
|
baseUrl: url,
|
|
285
274
|
});
|
|
286
275
|
|
|
287
|
-
return <link key={style.source} {...attributes} />;
|
|
276
|
+
return <link key={style.source} {...(attributes as any)} />;
|
|
288
277
|
})}
|
|
289
278
|
|
|
290
|
-
{[...
|
|
279
|
+
{[...preloadAssets.scripts].map((script) => {
|
|
291
280
|
const attributes = scriptAssetPreloadAttributes(script, {
|
|
292
281
|
baseUrl: url,
|
|
293
282
|
});
|
|
294
283
|
|
|
295
|
-
return <link key={script.source} {...attributes} />;
|
|
284
|
+
return <link key={script.source} {...(attributes as any)} />;
|
|
296
285
|
})}
|
|
297
286
|
</>
|
|
298
287
|
}
|
package/source/static/render.tsx
CHANGED
|
@@ -4,34 +4,31 @@ import {extract} from '@quilted/react-server-render/server';
|
|
|
4
4
|
import type {Options as ExtractOptions} from '@quilted/react-server-render/server';
|
|
5
5
|
import {HtmlManager} from '@quilted/react-html/server';
|
|
6
6
|
import {HttpManager} from '@quilted/react-http/server';
|
|
7
|
-
import {
|
|
7
|
+
import {AssetsManager} from '@quilted/react-assets/server';
|
|
8
|
+
import type {AssetsCacheKey} from '@quilted/assets';
|
|
8
9
|
|
|
9
10
|
import {StaticContext} from './StaticContext';
|
|
10
11
|
|
|
11
|
-
interface Options extends ExtractOptions {
|
|
12
|
+
interface Options<CacheKey = AssetsCacheKey> extends ExtractOptions {
|
|
12
13
|
url?: string | URL;
|
|
14
|
+
cacheKey?: CacheKey;
|
|
13
15
|
headers?: NonNullable<
|
|
14
16
|
ConstructorParameters<typeof HttpManager>[0]
|
|
15
17
|
>['headers'];
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
export async function renderApp(
|
|
20
|
+
export async function renderApp<CacheKey = AssetsCacheKey>(
|
|
19
21
|
app: ReactElement<any>,
|
|
20
|
-
{decorate, url, headers, ...rest}: Options = {},
|
|
22
|
+
{decorate, url, headers, cacheKey, ...rest}: Options<CacheKey> = {},
|
|
21
23
|
) {
|
|
22
24
|
const html = new HtmlManager();
|
|
23
|
-
const asyncAssets = new AsyncAssetManager();
|
|
24
25
|
const http = new HttpManager({headers});
|
|
26
|
+
const assets = new AssetsManager<CacheKey>({cacheKey});
|
|
25
27
|
|
|
26
28
|
const markup = await extract(app, {
|
|
27
29
|
decorate(app) {
|
|
28
30
|
return (
|
|
29
|
-
<StaticContext
|
|
30
|
-
asyncAssets={asyncAssets}
|
|
31
|
-
html={html}
|
|
32
|
-
http={http}
|
|
33
|
-
url={url}
|
|
34
|
-
>
|
|
31
|
+
<StaticContext html={html} http={http} url={url} assets={assets}>
|
|
35
32
|
{decorate?.(app) ?? app}
|
|
36
33
|
</StaticContext>
|
|
37
34
|
);
|
|
@@ -39,5 +36,5 @@ export async function renderApp(
|
|
|
39
36
|
...rest,
|
|
40
37
|
});
|
|
41
38
|
|
|
42
|
-
return {markup, http, html,
|
|
39
|
+
return {markup, http, html, assets};
|
|
43
40
|
}
|
package/tsconfig.json
CHANGED
|
@@ -7,11 +7,13 @@
|
|
|
7
7
|
"include": ["source"],
|
|
8
8
|
"exclude": ["quilt.project.ts", "**/*.test.ts", "**/*.test.tsx"],
|
|
9
9
|
"references": [
|
|
10
|
+
{"path": "../assets"},
|
|
10
11
|
{"path": "../async"},
|
|
11
12
|
{"path": "../events"},
|
|
12
13
|
{"path": "../graphql"},
|
|
13
14
|
{"path": "../polyfills"},
|
|
14
15
|
{"path": "../react"},
|
|
16
|
+
{"path": "../react-assets"},
|
|
15
17
|
{"path": "../react-async"},
|
|
16
18
|
{"path": "../react-dom"},
|
|
17
19
|
{"path": "../react-graphql"},
|