@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.
@@ -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,EAAC,mBAAmB,EAAC,MAAM,uBAAuB,CAAC;AAC1D,YAAY,EACV,KAAK,EACL,aAAa,EACb,kBAAkB,EAClB,0BAA0B,EAC1B,aAAa,EACb,UAAU,EACV,eAAe,GAChB,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,SAAS,EAAC,MAAM,UAAU,CAAC;AACnC,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAC,kBAAkB,EAAE,mBAAmB,EAAC,MAAM,kBAAkB,CAAC"}
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 { ReactElement } from 'react';
2
- import type { Asset, AssetManifest } from '@quilted/async/server';
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 { RequestRouter, EnhancedRequest, RequestHandler, RequestContext } from '@quilted/request-router';
5
- import { type RenderResult as RenderAppResult } from './render';
6
- export interface Options<Context = RequestContext> extends Omit<ExtractOptions, 'context'> {
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
- router?: RequestRouter<Context>;
11
+ extract?: ExtractOptions;
9
12
  context?(request: EnhancedRequest, context: Context): ServerRenderRequestContext;
10
- renderHtml?(content: string | undefined, request: Request, details: Omit<RenderAppResult, 'asyncAssets' | 'markup'> & {
11
- readonly styles: readonly Asset[];
12
- readonly scripts: readonly Asset[];
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 declare function createServerRender<Context = RequestContext>(render: (request: EnhancedRequest, context: Context) => ReactElement<any> | Promise<ReactElement<any>>, { context, ...options }?: Omit<Options<Context>, 'router'>): RequestHandler<Context>;
17
- export declare function renderAppToResponse(app: ReactElement<any>, request: Request, { assets, renderHtml, ...options }?: Omit<Options, 'handler' | 'renderProps'>): Promise<import("@quilted/request-router").EnhancedResponse>;
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,EAAC,YAAY,EAAC,MAAM,OAAO,CAAC;AAExC,OAAO,KAAK,EAAC,KAAK,EAAE,aAAa,EAAC,MAAM,uBAAuB,CAAC;AAEhE,OAAO,KAAK,EACV,OAAO,IAAI,cAAc,EACzB,0BAA0B,EAC3B,MAAM,qCAAqC,CAAC;AAG7C,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EACf,cAAc,EACd,cAAc,EACf,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAY,KAAK,YAAY,IAAI,eAAe,EAAC,MAAM,UAAU,CAAC;AAEzE,MAAM,WAAW,OAAO,CAAC,OAAO,GAAG,cAAc,CAC/C,SAAQ,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC;IACvC,MAAM,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IAChC,MAAM,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IAChC,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,eAAe,EAAE,aAAa,GAAG,QAAQ,CAAC,GAAG;QACzD,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;QAClC,QAAQ,CAAC,OAAO,EAAE,SAAS,KAAK,EAAE,CAAC;QACnC,QAAQ,CAAC,OAAO,EAAE,SAAS,KAAK,EAAE,CAAC;KACpC,GACA,YAAY,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;CACnD;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,GAAG,OAAO,EAAC,GAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAM,GAC3D,cAAc,CAAC,OAAO,CAAC,CAazB;AAED,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,YAAY,CAAC,GAAG,CAAC,EACtB,OAAO,EAAE,OAAO,EAChB,EACE,MAAM,EACN,UAA8B,EAC9B,GAAG,OAAO,EACX,GAAE,IAAI,CAAC,OAAO,EAAE,SAAS,GAAG,aAAa,CAAM,+DA+CjD"}
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 { AssetManifest } from '@quilted/async/server';
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,EAAC,aAAa,EAAC,MAAM,uBAAuB,CAAC;AAOzD,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,iBAuOX"}
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.129",
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.12",
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.22",
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
@@ -2,7 +2,6 @@ export {
2
2
  Alternate,
3
3
  BodyAttributes,
4
4
  HtmlAttributes,
5
- Hydrator,
6
5
  Link,
7
6
  Meta,
8
7
  SearchRobots,
@@ -13,15 +13,21 @@ export {
13
13
  ServerRenderManagerContext,
14
14
  extract,
15
15
  } from '@quilted/react-server-render/server';
16
- export {createAssetManifest} from '@quilted/async/server';
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
- AssetBuildEntry,
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 {createServerRender, renderAppToResponse} from './request-router';
76
+ export {
77
+ createServerRender,
78
+ renderAppToResponse,
79
+ renderAppToStreamedResponse,
80
+ } from './request-router';
@@ -1,26 +1,39 @@
1
- import type {ReactElement} from 'react';
1
+ import {Fragment, type ReactElement, type LinkHTMLAttributes} from 'react';
2
2
 
3
- import type {Asset, AssetManifest} from '@quilted/async/server';
4
- import {renderHtmlToString, Html} from '@quilted/react-html/server';
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 {renderApp, type RenderResult as RenderAppResult} from './render';
31
+ import {ServerContext} from './ServerContext';
19
32
 
20
- export interface Options<Context = RequestContext>
21
- extends Omit<ExtractOptions, 'context'> {
33
+ export interface ServerRenderOptions<Context = RequestContext> {
34
+ stream?: 'headers' | false;
22
35
  assets?: AssetManifest<unknown>;
23
- router?: RequestRouter<Context>;
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: Omit<RenderAppResult, 'asyncAssets' | 'markup'> & {
32
- readonly styles: readonly Asset[];
33
- readonly scripts: readonly Asset[];
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}: Omit<Options<Context>, 'router'> = {},
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
- return renderAppToResponse(app, request, {
72
+ const renderResponse = stream
73
+ ? renderAppToStreamedResponse
74
+ : renderAppToResponse;
75
+
76
+ return renderResponse(app, request, {
54
77
  ...options,
55
- context: context?.(request, requestContext) ?? (requestContext as any),
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
- renderHtml = defaultRenderHtml,
66
- ...options
67
- }: Omit<Options, 'handler' | 'renderProps'> = {},
94
+ extract,
95
+ renderHtml,
96
+ }: Pick<ServerRenderOptions, 'assets' | 'renderHtml' | 'extract'> = {},
68
97
  ) {
69
- const {
70
- html: htmlManager,
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 assetOptions = {userAgent: request.headers.get('User-Agent')};
226
+ const assetContext = {userAgent: request.headers.get('User-Agent')};
91
227
 
92
- const [styles, scripts, preload] = assets
228
+ const [entryAssets, preloadAssets] = assets
93
229
  ? await Promise.all([
94
- assets.styles({async: usedAssets, options: assetOptions}),
95
- assets.scripts({async: usedAssets, options: assetOptions}),
230
+ assets.assets({async: usedAssets, context: assetContext}),
96
231
  assets.asyncAssets(asyncAssets.used({timing: 'preload'}), {
97
- options: assetOptions,
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
- styles,
106
- scripts,
107
- preload,
240
+ assets: entryAssets,
241
+ preloadAssets,
108
242
  });
109
243
 
110
- return html(renderHtmlToString(htmlElement), {
111
- headers,
112
- status: statusCode,
113
- });
244
+ return renderHtmlToString(htmlElement);
114
245
  }
115
246
 
116
- function defaultRenderHtml(
117
- content: string | undefined,
118
- request: Request,
119
- {
120
- html,
121
- styles,
122
- scripts,
123
- preload,
124
- }: Omit<RenderAppResult, 'asyncAssets' | 'markup'> & {
125
- readonly styles: readonly Asset[];
126
- readonly scripts: readonly Asset[];
127
- readonly preload: readonly Asset[];
128
- },
129
- ) {
130
- return (
131
- <Html
132
- url={new URL(request.url)}
133
- manager={html}
134
- styles={styles}
135
- scripts={scripts}
136
- preloadAssets={preload}
137
- >
138
- {content}
139
- </Html>
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
  }
@@ -1,6 +1,12 @@
1
1
  import type {ComponentType} from 'react';
2
2
 
3
- import type {AssetManifest} from '@quilted/async/server';
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
- moduleStyles,
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
- options: {modules: true},
236
+ context: {modules: true},
238
237
  }),
239
- assets.styles({async: usedAssets, options: {modules: false}}),
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 = moduleStyles.length > 0 ? moduleStyles : nomoduleStyles;
247
-
248
- // If there are nomodule scripts, we can’t really do preloading, because we can’t
249
- // prevent the nomodule scripts from being preloaded in module browsers. If there
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
- styles={styles}
263
- scripts={scripts}
264
- preloadAssets={preload}
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>,