@quilted/quilt 0.5.129 → 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 +12 -0
- package/build/cjs/html.cjs +0 -4
- 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/html.mjs +1 -1
- 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/html.esnext +1 -1
- 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/html.d.ts +1 -1
- package/build/typescript/html.d.ts.map +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/html.ts +0 -1
- 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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../source/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,SAAS,EACT,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,gBAAgB,IAAI,qBAAqB,GAC1C,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,YAAY,EACZ,eAAe,EACf,mBAAmB,EACnB,0BAA0B,EAC1B,OAAO,GACR,MAAM,qCAAqC,CAAC;AAC7C,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../source/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,SAAS,EACT,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,gBAAgB,IAAI,qBAAqB,GAC1C,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,YAAY,EACZ,eAAe,EACf,mBAAmB,EACnB,0BAA0B,EAC1B,OAAO,GACR,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,2BAA2B,EAC3B,qBAAqB,EACrB,4BAA4B,GAC7B,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,KAAK,EACL,kBAAkB,EAClB,oBAAoB,EACpB,0BAA0B,EAC1B,aAAa,EACb,UAAU,EACV,WAAW,GACZ,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,IAAI,6BAA6B,GAClD,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,gBAAgB,EAChB,mBAAmB,EACnB,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,qCAAqC,CAAC;AAC7C,YAAY,EAAC,SAAS,EAAC,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAC,+BAA+B,EAAC,MAAM,wCAAwC,CAAC;AACvF,YAAY,EACV,yBAAyB,EACzB,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,wCAAwC,CAAC;AAChD,OAAO,EACL,OAAO,EACP,QAAQ,EACR,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,aAAa,GACd,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,OAAO,EACP,aAAa,EACb,aAAa,EACb,cAAc,EACd,cAAc,EACd,0BAA0B,EAC1B,gBAAgB,EAChB,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAC,yBAAyB,EAAC,MAAM,yBAAyB,CAAC;AAElE,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,2BAA2B,GAC5B,MAAM,kBAAkB,CAAC"}
|
|
@@ -1,18 +1,27 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import type
|
|
1
|
+
import { type ReactElement } from 'react';
|
|
2
|
+
import { type AssetsEntry, type AssetManifest } from '@quilted/async/server';
|
|
3
|
+
import { AsyncAssetManager } from '@quilted/react-async/server';
|
|
4
|
+
import { HttpManager } from '@quilted/react-http/server';
|
|
5
|
+
import { HtmlManager } from '@quilted/react-html/server';
|
|
3
6
|
import type { Options as ExtractOptions, ServerRenderRequestContext } from '@quilted/react-server-render/server';
|
|
4
|
-
import type {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
+
import type { EnhancedRequest, RequestHandler, RequestContext } from '@quilted/request-router';
|
|
8
|
+
export interface ServerRenderOptions<Context = RequestContext> {
|
|
9
|
+
stream?: 'headers' | false;
|
|
7
10
|
assets?: AssetManifest<unknown>;
|
|
8
|
-
|
|
11
|
+
extract?: ExtractOptions;
|
|
9
12
|
context?(request: EnhancedRequest, context: Context): ServerRenderRequestContext;
|
|
10
|
-
renderHtml?(content: string | undefined, request: Request, details:
|
|
11
|
-
readonly
|
|
12
|
-
readonly
|
|
13
|
-
readonly preload: readonly Asset[];
|
|
13
|
+
renderHtml?(content: string | undefined, request: Request, details: Pick<ServerRenderAppDetails, 'http' | 'html'> & {
|
|
14
|
+
readonly assets?: AssetsEntry;
|
|
15
|
+
readonly preloadAssets?: AssetsEntry;
|
|
14
16
|
}): ReactElement<any> | Promise<ReactElement<any>>;
|
|
15
17
|
}
|
|
16
|
-
export
|
|
17
|
-
|
|
18
|
+
export interface ServerRenderAppDetails {
|
|
19
|
+
readonly http: HttpManager;
|
|
20
|
+
readonly html: HtmlManager;
|
|
21
|
+
readonly rendered?: string;
|
|
22
|
+
readonly asyncAssets: AsyncAssetManager;
|
|
23
|
+
}
|
|
24
|
+
export declare function createServerRender<Context = RequestContext>(render: (request: EnhancedRequest, context: Context) => ReactElement<any> | Promise<ReactElement<any>>, { context, stream, ...options }?: ServerRenderOptions<Context>): RequestHandler<Context>;
|
|
25
|
+
export declare function renderAppToResponse(app: ReactElement<any>, request: Request, { assets, extract, renderHtml, }?: Pick<ServerRenderOptions, 'assets' | 'renderHtml' | 'extract'>): Promise<import("@quilted/request-router").EnhancedResponse>;
|
|
26
|
+
export declare function renderAppToStreamedResponse(app: ReactElement<any>, request: Request, { assets, extract, renderHtml, }?: Pick<ServerRenderOptions, 'assets' | 'renderHtml' | 'extract'>): Promise<import("@quilted/request-router").EnhancedResponse>;
|
|
18
27
|
//# sourceMappingURL=request-router.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-router.d.ts","sourceRoot":"","sources":["../../../source/server/request-router.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"request-router.d.ts","sourceRoot":"","sources":["../../../source/server/request-router.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,YAAY,EAA0B,MAAM,OAAO,CAAC;AAE3E,OAAO,EAKL,KAAK,WAAW,EAChB,KAAK,aAAa,EACnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAC,iBAAiB,EAAC,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAC,WAAW,EAAC,MAAM,4BAA4B,CAAC;AACvD,OAAO,EAEL,WAAW,EAEZ,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EACV,OAAO,IAAI,cAAc,EACzB,0BAA0B,EAC3B,MAAM,qCAAqC,CAAC;AAI7C,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACd,cAAc,EACf,MAAM,yBAAyB,CAAC;AAIjC,MAAM,WAAW,mBAAmB,CAAC,OAAO,GAAG,cAAc;IAC3D,MAAM,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC;IAC3B,MAAM,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,OAAO,CAAC,CACN,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,OAAO,GACf,0BAA0B,CAAC;IAC9B,UAAU,CAAC,CACT,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,IAAI,CAAC,sBAAsB,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG;QACvD,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;QAC9B,QAAQ,CAAC,aAAa,CAAC,EAAE,WAAW,CAAC;KACtC,GACA,YAAY,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;CACnD;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,iBAAiB,CAAC;CACzC;AAED,wBAAgB,kBAAkB,CAAC,OAAO,GAAG,cAAc,EACzD,MAAM,EAAE,CACN,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,OAAO,KACb,YAAY,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,EACnD,EAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,EAAC,GAAE,mBAAmB,CAAC,OAAO,CAAM,GAC/D,cAAc,CAAC,OAAO,CAAC,CAuBzB;AAED,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,YAAY,CAAC,GAAG,CAAC,EACtB,OAAO,EAAE,OAAO,EAChB,EACE,MAAM,EACN,OAAO,EACP,UAAU,GACX,GAAE,IAAI,CAAC,mBAAmB,EAAE,QAAQ,GAAG,YAAY,GAAG,SAAS,CAAM,+DA0BvE;AAED,wBAAsB,2BAA2B,CAC/C,GAAG,EAAE,YAAY,CAAC,GAAG,CAAC,EACtB,OAAO,EAAE,OAAO,EAChB,EACE,MAAM,EACN,OAAO,EACP,UAAU,GACX,GAAE,IAAI,CAAC,mBAAmB,EAAE,QAAQ,GAAG,YAAY,GAAG,SAAS,CAAM,+DA4CvE"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ComponentType } from 'react';
|
|
2
|
-
import type
|
|
2
|
+
import { type AssetManifest } from '@quilted/async/server';
|
|
3
3
|
import type { HttpState } from '@quilted/react-http/server';
|
|
4
4
|
export interface RenderDetails {
|
|
5
5
|
readonly route: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../source/static/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,OAAO,CAAC;AAEzC,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../source/static/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,OAAO,CAAC;AAEzC,OAAO,EAKL,KAAK,aAAa,EACnB,MAAM,uBAAuB,CAAC;AAO/B,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,4BAA4B,CAAC;AAI1D,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;CAC1B;AAOD,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,aAAa,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAC,CAAC,CAAC;IAC1C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxD;AAID,wBAAsB,YAAY,CAChC,GAAG,EAAE,aAAa,CAAC,GAAG,CAAC,EACvB,EACE,MAAM,EACN,MAAM,EAAE,cAAc,EACtB,QAAQ,EACR,KAAY,EACZ,OAAkB,EAClB,QAAe,GAChB,EAAE,OAAO,iBAmQX"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quilted/quilt",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.131",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/lemonmade/quilt.git",
|
|
@@ -214,12 +214,12 @@
|
|
|
214
214
|
"./build/esm/polyfills/*.mjs"
|
|
215
215
|
],
|
|
216
216
|
"dependencies": {
|
|
217
|
-
"@quilted/async": "^0.3.
|
|
217
|
+
"@quilted/async": "^0.3.34",
|
|
218
218
|
"@quilted/events": "^0.1.0",
|
|
219
219
|
"@quilted/graphql": "^0.4.34",
|
|
220
220
|
"@quilted/polyfills": "^0.2.12",
|
|
221
221
|
"@quilted/react": "^18.2.0",
|
|
222
|
-
"@quilted/react-async": "^0.3.
|
|
222
|
+
"@quilted/react-async": "^0.3.43",
|
|
223
223
|
"@quilted/react-dom": "^18.2.0",
|
|
224
224
|
"@quilted/react-graphql": "^0.4.30",
|
|
225
225
|
"@quilted/react-html": "^0.3.18",
|
package/source/html.ts
CHANGED
package/source/server/index.ts
CHANGED
|
@@ -13,15 +13,21 @@ export {
|
|
|
13
13
|
ServerRenderManagerContext,
|
|
14
14
|
extract,
|
|
15
15
|
} from '@quilted/react-server-render/server';
|
|
16
|
-
export {
|
|
16
|
+
export {
|
|
17
|
+
createAssetManifest,
|
|
18
|
+
styleAssetAttributes,
|
|
19
|
+
styleAssetPreloadAttributes,
|
|
20
|
+
scriptAssetAttributes,
|
|
21
|
+
scriptAssetPreloadAttributes,
|
|
22
|
+
} from '@quilted/async/server';
|
|
17
23
|
export type {
|
|
18
24
|
Asset,
|
|
19
|
-
AssetSelector,
|
|
20
25
|
AsyncAssetSelector,
|
|
26
|
+
AssetSelectorOptions,
|
|
21
27
|
CreateAssetManifestOptions,
|
|
22
28
|
AssetManifest,
|
|
23
29
|
AssetBuild,
|
|
24
|
-
|
|
30
|
+
AssetsEntry,
|
|
25
31
|
} from '@quilted/async/server';
|
|
26
32
|
export {
|
|
27
33
|
AsyncAssetContext,
|
|
@@ -66,6 +72,9 @@ export type {
|
|
|
66
72
|
} from '@quilted/request-router';
|
|
67
73
|
export {parseAcceptLanguageHeader} from '@quilted/react-localize';
|
|
68
74
|
|
|
69
|
-
export {renderApp} from './render';
|
|
70
75
|
export {ServerContext} from './ServerContext';
|
|
71
|
-
export {
|
|
76
|
+
export {
|
|
77
|
+
createServerRender,
|
|
78
|
+
renderAppToResponse,
|
|
79
|
+
renderAppToStreamedResponse,
|
|
80
|
+
} from './request-router';
|
|
@@ -1,26 +1,39 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import {Fragment, type ReactElement, type LinkHTMLAttributes} from 'react';
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
styleAssetAttributes,
|
|
5
|
+
styleAssetPreloadAttributes,
|
|
6
|
+
scriptAssetAttributes,
|
|
7
|
+
scriptAssetPreloadAttributes,
|
|
8
|
+
type AssetsEntry,
|
|
9
|
+
type AssetManifest,
|
|
10
|
+
} from '@quilted/async/server';
|
|
11
|
+
import {AsyncAssetManager} from '@quilted/react-async/server';
|
|
12
|
+
import {HttpManager} from '@quilted/react-http/server';
|
|
13
|
+
import {
|
|
14
|
+
renderHtmlToString,
|
|
15
|
+
HtmlManager,
|
|
16
|
+
Html,
|
|
17
|
+
} from '@quilted/react-html/server';
|
|
5
18
|
import type {
|
|
6
19
|
Options as ExtractOptions,
|
|
7
20
|
ServerRenderRequestContext,
|
|
8
21
|
} from '@quilted/react-server-render/server';
|
|
22
|
+
import {extract} from '@quilted/react-server-render/server';
|
|
9
23
|
|
|
10
24
|
import {html, redirect} from '@quilted/request-router';
|
|
11
25
|
import type {
|
|
12
|
-
RequestRouter,
|
|
13
26
|
EnhancedRequest,
|
|
14
27
|
RequestHandler,
|
|
15
28
|
RequestContext,
|
|
16
29
|
} from '@quilted/request-router';
|
|
17
30
|
|
|
18
|
-
import {
|
|
31
|
+
import {ServerContext} from './ServerContext';
|
|
19
32
|
|
|
20
|
-
export interface
|
|
21
|
-
|
|
33
|
+
export interface ServerRenderOptions<Context = RequestContext> {
|
|
34
|
+
stream?: 'headers' | false;
|
|
22
35
|
assets?: AssetManifest<unknown>;
|
|
23
|
-
|
|
36
|
+
extract?: ExtractOptions;
|
|
24
37
|
context?(
|
|
25
38
|
request: EnhancedRequest,
|
|
26
39
|
context: Context,
|
|
@@ -28,20 +41,26 @@ export interface Options<Context = RequestContext>
|
|
|
28
41
|
renderHtml?(
|
|
29
42
|
content: string | undefined,
|
|
30
43
|
request: Request,
|
|
31
|
-
details:
|
|
32
|
-
readonly
|
|
33
|
-
readonly
|
|
34
|
-
readonly preload: readonly Asset[];
|
|
44
|
+
details: Pick<ServerRenderAppDetails, 'http' | 'html'> & {
|
|
45
|
+
readonly assets?: AssetsEntry;
|
|
46
|
+
readonly preloadAssets?: AssetsEntry;
|
|
35
47
|
},
|
|
36
48
|
): ReactElement<any> | Promise<ReactElement<any>>;
|
|
37
49
|
}
|
|
38
50
|
|
|
51
|
+
export interface ServerRenderAppDetails {
|
|
52
|
+
readonly http: HttpManager;
|
|
53
|
+
readonly html: HtmlManager;
|
|
54
|
+
readonly rendered?: string;
|
|
55
|
+
readonly asyncAssets: AsyncAssetManager;
|
|
56
|
+
}
|
|
57
|
+
|
|
39
58
|
export function createServerRender<Context = RequestContext>(
|
|
40
59
|
render: (
|
|
41
60
|
request: EnhancedRequest,
|
|
42
61
|
context: Context,
|
|
43
62
|
) => ReactElement<any> | Promise<ReactElement<any>>,
|
|
44
|
-
{context, ...options}:
|
|
63
|
+
{context, stream, ...options}: ServerRenderOptions<Context> = {},
|
|
45
64
|
): RequestHandler<Context> {
|
|
46
65
|
return async (request, requestContext) => {
|
|
47
66
|
const accepts = request.headers.get('Accept');
|
|
@@ -50,9 +69,19 @@ export function createServerRender<Context = RequestContext>(
|
|
|
50
69
|
|
|
51
70
|
const app = await render(request, requestContext);
|
|
52
71
|
|
|
53
|
-
|
|
72
|
+
const renderResponse = stream
|
|
73
|
+
? renderAppToStreamedResponse
|
|
74
|
+
: renderAppToResponse;
|
|
75
|
+
|
|
76
|
+
return renderResponse(app, request, {
|
|
54
77
|
...options,
|
|
55
|
-
|
|
78
|
+
extract: {
|
|
79
|
+
...options.extract,
|
|
80
|
+
context:
|
|
81
|
+
options.extract ??
|
|
82
|
+
context?.(request, requestContext) ??
|
|
83
|
+
(requestContext as any),
|
|
84
|
+
},
|
|
56
85
|
});
|
|
57
86
|
};
|
|
58
87
|
}
|
|
@@ -62,22 +91,17 @@ export async function renderAppToResponse(
|
|
|
62
91
|
request: Request,
|
|
63
92
|
{
|
|
64
93
|
assets,
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}:
|
|
94
|
+
extract,
|
|
95
|
+
renderHtml,
|
|
96
|
+
}: Pick<ServerRenderOptions, 'assets' | 'renderHtml' | 'extract'> = {},
|
|
68
97
|
) {
|
|
69
|
-
const {
|
|
70
|
-
|
|
71
|
-
http,
|
|
72
|
-
rendered,
|
|
73
|
-
asyncAssets,
|
|
74
|
-
} = await renderApp(app, {
|
|
75
|
-
...options,
|
|
98
|
+
const renderDetails = await serverRenderDetailsForApp(app, {
|
|
99
|
+
extract,
|
|
76
100
|
url: request.url,
|
|
77
101
|
headers: request.headers,
|
|
78
102
|
});
|
|
79
103
|
|
|
80
|
-
const {headers, statusCode = 200, redirectUrl} = http.state;
|
|
104
|
+
const {headers, statusCode = 200, redirectUrl} = renderDetails.http.state;
|
|
81
105
|
|
|
82
106
|
if (redirectUrl) {
|
|
83
107
|
return redirect(redirectUrl, {
|
|
@@ -86,56 +110,219 @@ export async function renderAppToResponse(
|
|
|
86
110
|
});
|
|
87
111
|
}
|
|
88
112
|
|
|
113
|
+
const content = await renderAppDetailsToHtmlString(renderDetails, request, {
|
|
114
|
+
assets,
|
|
115
|
+
renderHtml,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
return html(content, {
|
|
119
|
+
headers,
|
|
120
|
+
status: statusCode,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export async function renderAppToStreamedResponse(
|
|
125
|
+
app: ReactElement<any>,
|
|
126
|
+
request: Request,
|
|
127
|
+
{
|
|
128
|
+
assets,
|
|
129
|
+
extract,
|
|
130
|
+
renderHtml,
|
|
131
|
+
}: Pick<ServerRenderOptions, 'assets' | 'renderHtml' | 'extract'> = {},
|
|
132
|
+
) {
|
|
133
|
+
const headers = new Headers();
|
|
134
|
+
const stream = new TransformStream();
|
|
135
|
+
|
|
136
|
+
const assetContext = {userAgent: request.headers.get('User-Agent')};
|
|
137
|
+
const guaranteedAssets = await assets?.assets({context: assetContext});
|
|
138
|
+
|
|
139
|
+
if (guaranteedAssets) {
|
|
140
|
+
for (const style of guaranteedAssets.styles) {
|
|
141
|
+
headers.append('Link', preloadHeader(styleAssetPreloadAttributes(style)));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
for (const script of guaranteedAssets.scripts) {
|
|
145
|
+
headers.append(
|
|
146
|
+
'Link',
|
|
147
|
+
preloadHeader(scriptAssetPreloadAttributes(script)),
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
renderResponseToStream();
|
|
153
|
+
|
|
154
|
+
return html(stream.readable, {
|
|
155
|
+
headers,
|
|
156
|
+
status: 200,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
async function renderResponseToStream() {
|
|
160
|
+
const renderDetails = await serverRenderDetailsForApp(app, {
|
|
161
|
+
extract,
|
|
162
|
+
url: request.url,
|
|
163
|
+
headers: request.headers,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const content = await renderAppDetailsToHtmlString(renderDetails, request, {
|
|
167
|
+
assets,
|
|
168
|
+
renderHtml,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const writer = stream.writable.getWriter();
|
|
172
|
+
await writer.write(content);
|
|
173
|
+
await writer.close();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function serverRenderDetailsForApp(
|
|
178
|
+
app: ReactElement<any>,
|
|
179
|
+
{
|
|
180
|
+
url,
|
|
181
|
+
headers,
|
|
182
|
+
extract: extractOptions,
|
|
183
|
+
}: Pick<ServerRenderOptions, 'extract'> & {
|
|
184
|
+
url?: string | URL;
|
|
185
|
+
headers?: NonNullable<
|
|
186
|
+
ConstructorParameters<typeof HttpManager>[0]
|
|
187
|
+
>['headers'];
|
|
188
|
+
} = {},
|
|
189
|
+
): Promise<ServerRenderAppDetails> {
|
|
190
|
+
const html = new HtmlManager();
|
|
191
|
+
const asyncAssets = new AsyncAssetManager();
|
|
192
|
+
const http = new HttpManager({headers});
|
|
193
|
+
|
|
194
|
+
const {decorate, ...rest} = extractOptions ?? {};
|
|
195
|
+
|
|
196
|
+
const rendered = await extract(app, {
|
|
197
|
+
decorate(app) {
|
|
198
|
+
return (
|
|
199
|
+
<ServerContext
|
|
200
|
+
asyncAssets={asyncAssets}
|
|
201
|
+
http={http}
|
|
202
|
+
html={html}
|
|
203
|
+
url={url}
|
|
204
|
+
>
|
|
205
|
+
{decorate?.(app) ?? app}
|
|
206
|
+
</ServerContext>
|
|
207
|
+
);
|
|
208
|
+
},
|
|
209
|
+
...rest,
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
return {rendered, http, html, asyncAssets};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function renderAppDetailsToHtmlString(
|
|
216
|
+
details: ServerRenderAppDetails,
|
|
217
|
+
request: Request,
|
|
218
|
+
{
|
|
219
|
+
assets,
|
|
220
|
+
renderHtml = defaultRenderHtml,
|
|
221
|
+
}: Pick<ServerRenderOptions, 'assets' | 'renderHtml'> = {},
|
|
222
|
+
) {
|
|
223
|
+
const {html: htmlManager, http, rendered, asyncAssets} = details;
|
|
224
|
+
|
|
89
225
|
const usedAssets = asyncAssets.used({timing: 'load'});
|
|
90
|
-
const
|
|
226
|
+
const assetContext = {userAgent: request.headers.get('User-Agent')};
|
|
91
227
|
|
|
92
|
-
const [
|
|
228
|
+
const [entryAssets, preloadAssets] = assets
|
|
93
229
|
? await Promise.all([
|
|
94
|
-
assets.
|
|
95
|
-
assets.scripts({async: usedAssets, options: assetOptions}),
|
|
230
|
+
assets.assets({async: usedAssets, context: assetContext}),
|
|
96
231
|
assets.asyncAssets(asyncAssets.used({timing: 'preload'}), {
|
|
97
|
-
|
|
232
|
+
context: assetContext,
|
|
98
233
|
}),
|
|
99
234
|
])
|
|
100
|
-
: [
|
|
235
|
+
: [];
|
|
101
236
|
|
|
102
237
|
const htmlElement = await renderHtml(rendered, request, {
|
|
103
238
|
html: htmlManager,
|
|
104
239
|
http,
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
preload,
|
|
240
|
+
assets: entryAssets,
|
|
241
|
+
preloadAssets,
|
|
108
242
|
});
|
|
109
243
|
|
|
110
|
-
return
|
|
111
|
-
headers,
|
|
112
|
-
status: statusCode,
|
|
113
|
-
});
|
|
244
|
+
return renderHtmlToString(htmlElement);
|
|
114
245
|
}
|
|
115
246
|
|
|
116
|
-
|
|
117
|
-
content
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
247
|
+
const defaultRenderHtml: NonNullable<ServerRenderOptions<any>['renderHtml']> =
|
|
248
|
+
function defaultRenderHtml(content, request, {html, assets, preloadAssets}) {
|
|
249
|
+
const baseUrl = new URL(request.url);
|
|
250
|
+
|
|
251
|
+
return (
|
|
252
|
+
<Html
|
|
253
|
+
manager={html}
|
|
254
|
+
headEndContent={
|
|
255
|
+
<>
|
|
256
|
+
{assets &&
|
|
257
|
+
[...assets.styles].map((style) => {
|
|
258
|
+
const attributes = styleAssetAttributes(style, {baseUrl});
|
|
259
|
+
return <link key={style.source} {...attributes} />;
|
|
260
|
+
})}
|
|
261
|
+
|
|
262
|
+
{assets &&
|
|
263
|
+
[...assets.scripts].map((script) => {
|
|
264
|
+
const isModule = script.attributes.type === 'module';
|
|
265
|
+
|
|
266
|
+
const attributes = scriptAssetAttributes(script, {
|
|
267
|
+
baseUrl,
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
if (isModule) {
|
|
271
|
+
return (
|
|
272
|
+
<Fragment key={script.source}>
|
|
273
|
+
<link {...scriptAssetPreloadAttributes(script)} />
|
|
274
|
+
<script {...attributes} async />
|
|
275
|
+
</Fragment>
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return <script key={script.source} {...attributes} defer />;
|
|
280
|
+
})}
|
|
281
|
+
|
|
282
|
+
{preloadAssets &&
|
|
283
|
+
[...preloadAssets.styles].map((style) => {
|
|
284
|
+
const attributes = styleAssetPreloadAttributes(style, {
|
|
285
|
+
baseUrl,
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
return <link key={style.source} {...attributes} />;
|
|
289
|
+
})}
|
|
290
|
+
|
|
291
|
+
{preloadAssets &&
|
|
292
|
+
[...preloadAssets.scripts].map((script) => {
|
|
293
|
+
const attributes = scriptAssetPreloadAttributes(script, {
|
|
294
|
+
baseUrl,
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
return <link key={script.source} {...attributes} />;
|
|
298
|
+
})}
|
|
299
|
+
</>
|
|
300
|
+
}
|
|
301
|
+
>
|
|
302
|
+
{content}
|
|
303
|
+
</Html>
|
|
304
|
+
);
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
function preloadHeader(attributes: LinkHTMLAttributes<HTMLLinkElement>) {
|
|
308
|
+
const {
|
|
309
|
+
as,
|
|
310
|
+
rel = 'preload',
|
|
311
|
+
href,
|
|
312
|
+
crossOrigin,
|
|
313
|
+
crossorigin,
|
|
314
|
+
} = attributes as any;
|
|
315
|
+
|
|
316
|
+
// Support both property and attribute versions of the casing
|
|
317
|
+
const finalCrossOrigin = crossOrigin ?? crossorigin;
|
|
318
|
+
|
|
319
|
+
let header = `<${href}>; rel="${rel}"; as="${as}"`;
|
|
320
|
+
|
|
321
|
+
if (finalCrossOrigin === '' || finalCrossOrigin === true) {
|
|
322
|
+
header += `; crossorigin`;
|
|
323
|
+
} else if (typeof finalCrossOrigin === 'string') {
|
|
324
|
+
header += `; crossorigin="${finalCrossOrigin}"`;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return header;
|
|
141
328
|
}
|
package/source/static/index.tsx
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type {ComponentType} from 'react';
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import {
|
|
4
|
+
styleAssetAttributes,
|
|
5
|
+
styleAssetPreloadAttributes,
|
|
6
|
+
scriptAssetAttributes,
|
|
7
|
+
scriptAssetPreloadAttributes,
|
|
8
|
+
type AssetManifest,
|
|
9
|
+
} from '@quilted/async/server';
|
|
4
10
|
import {renderHtmlToString, Html} from '@quilted/react-html/server';
|
|
5
11
|
import type {RouteDefinition} from '@quilted/react-router';
|
|
6
12
|
import {
|
|
@@ -224,44 +230,72 @@ export async function renderStatic(
|
|
|
224
230
|
|
|
225
231
|
const usedAssets = asyncAssets.used({timing: 'load'});
|
|
226
232
|
|
|
227
|
-
const [
|
|
228
|
-
|
|
229
|
-
moduleScripts,
|
|
230
|
-
modulePreload,
|
|
231
|
-
nomoduleStyles,
|
|
232
|
-
nomoduleScripts,
|
|
233
|
-
] = await Promise.all([
|
|
234
|
-
assets.styles({async: usedAssets, options: {modules: true}}),
|
|
235
|
-
assets.scripts({async: usedAssets, options: {modules: true}}),
|
|
233
|
+
const [moduleAssets, modulePreload, nomoduleAssets] = await Promise.all([
|
|
234
|
+
assets.assets({async: usedAssets, context: {modules: true}}),
|
|
236
235
|
assets.asyncAssets(asyncAssets.used({timing: 'preload'}), {
|
|
237
|
-
|
|
236
|
+
context: {modules: true},
|
|
238
237
|
}),
|
|
239
|
-
assets.
|
|
240
|
-
assets.scripts({async: usedAssets, options: {modules: false}}),
|
|
238
|
+
assets.assets({async: usedAssets, context: {modules: false}}),
|
|
241
239
|
]);
|
|
242
240
|
|
|
243
241
|
// We don’t want to load styles from both bundles, so we only use module styles,
|
|
244
242
|
// since modules are intended to be the default and CSS (usually) doesn’t
|
|
245
243
|
// have features that meaningfully break older user agents.
|
|
246
|
-
const styles =
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
// are only module scripts, we can preload those.
|
|
251
|
-
const preload = nomoduleScripts.length > 0 ? [] : modulePreload;
|
|
252
|
-
|
|
253
|
-
const scripts = [
|
|
254
|
-
...moduleScripts,
|
|
255
|
-
...nomoduleScripts.map((script) => ({...script, nomodule: true})),
|
|
256
|
-
];
|
|
244
|
+
const styles =
|
|
245
|
+
moduleAssets.styles.length > 0
|
|
246
|
+
? moduleAssets.styles
|
|
247
|
+
: nomoduleAssets.styles;
|
|
257
248
|
|
|
258
249
|
const minifiedHtml = renderHtmlToString(
|
|
259
250
|
<Html
|
|
260
|
-
url={url}
|
|
261
251
|
manager={htmlManager}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
252
|
+
headEndContent={
|
|
253
|
+
<>
|
|
254
|
+
{[...styles].map((style) => {
|
|
255
|
+
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} />;
|
|
265
|
+
})}
|
|
266
|
+
|
|
267
|
+
{[...nomoduleAssets.scripts].map((script) => {
|
|
268
|
+
const attributes = scriptAssetAttributes(script, {
|
|
269
|
+
baseUrl: url,
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
return (
|
|
273
|
+
<script
|
|
274
|
+
key={script.source}
|
|
275
|
+
{...attributes}
|
|
276
|
+
// @ts-expect-error Rendering to HTML, so using the lowercase name
|
|
277
|
+
nomodule={moduleAssets.scripts.length > 0 ? true : undefined}
|
|
278
|
+
/>
|
|
279
|
+
);
|
|
280
|
+
})}
|
|
281
|
+
|
|
282
|
+
{[...modulePreload.styles].map((style) => {
|
|
283
|
+
const attributes = styleAssetPreloadAttributes(style, {
|
|
284
|
+
baseUrl: url,
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
return <link key={style.source} {...attributes} />;
|
|
288
|
+
})}
|
|
289
|
+
|
|
290
|
+
{[...modulePreload.scripts].map((script) => {
|
|
291
|
+
const attributes = scriptAssetPreloadAttributes(script, {
|
|
292
|
+
baseUrl: url,
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
return <link key={script.source} {...attributes} />;
|
|
296
|
+
})}
|
|
297
|
+
</>
|
|
298
|
+
}
|
|
265
299
|
>
|
|
266
300
|
{markup}
|
|
267
301
|
</Html>,
|