@anansi/core 0.11.2 → 0.14.0

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.
Files changed (74) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +18 -7
  3. package/dist/client.js +63 -47
  4. package/dist/client.js.map +1 -1
  5. package/dist/server.js +201 -190
  6. package/dist/server.js.map +1 -1
  7. package/lib/floodSpouts.d.ts +1 -1
  8. package/lib/floodSpouts.d.ts.map +1 -1
  9. package/lib/floodSpouts.js +2 -2
  10. package/lib/index.d.ts +3 -0
  11. package/lib/index.d.ts.map +1 -1
  12. package/lib/index.js +11 -2
  13. package/lib/index.server.d.ts +3 -0
  14. package/lib/index.server.d.ts.map +1 -1
  15. package/lib/index.server.js +11 -2
  16. package/lib/laySpouts.d.ts +2 -1
  17. package/lib/laySpouts.d.ts.map +1 -1
  18. package/lib/laySpouts.js +45 -54
  19. package/lib/scripts/startDevserver.d.ts.map +1 -1
  20. package/lib/scripts/startDevserver.js +8 -3
  21. package/lib/spouts/app.d.ts +5 -0
  22. package/lib/spouts/app.d.ts.map +1 -0
  23. package/lib/spouts/app.js +12 -0
  24. package/lib/spouts/app.server.d.ts +6 -0
  25. package/lib/spouts/app.server.d.ts.map +1 -0
  26. package/lib/spouts/app.server.js +12 -0
  27. package/lib/spouts/document.d.ts +2 -8
  28. package/lib/spouts/document.d.ts.map +1 -1
  29. package/lib/spouts/document.js +4 -6
  30. package/lib/spouts/document.server.d.ts +4 -6
  31. package/lib/spouts/document.server.d.ts.map +1 -1
  32. package/lib/spouts/document.server.js +40 -42
  33. package/lib/spouts/json.d.ts +4 -2
  34. package/lib/spouts/json.d.ts.map +1 -1
  35. package/lib/spouts/json.js +7 -6
  36. package/lib/spouts/json.server.d.ts +4 -6
  37. package/lib/spouts/json.server.d.ts.map +1 -1
  38. package/lib/spouts/json.server.js +32 -34
  39. package/lib/spouts/prefetch.server.d.ts +1 -1
  40. package/lib/spouts/prefetch.server.d.ts.map +1 -1
  41. package/lib/spouts/prefetch.server.js +1 -1
  42. package/lib/spouts/restHooks.d.ts +3 -3
  43. package/lib/spouts/restHooks.d.ts.map +1 -1
  44. package/lib/spouts/restHooks.js +9 -11
  45. package/lib/spouts/restHooks.server.d.ts +8 -45
  46. package/lib/spouts/restHooks.server.d.ts.map +1 -1
  47. package/lib/spouts/restHooks.server.js +15 -12
  48. package/lib/spouts/router.d.ts +3 -4
  49. package/lib/spouts/router.d.ts.map +1 -1
  50. package/lib/spouts/router.js +14 -13
  51. package/lib/spouts/router.server.d.ts +3 -4
  52. package/lib/spouts/router.server.d.ts.map +1 -1
  53. package/lib/spouts/router.server.js +17 -15
  54. package/lib/spouts/types.d.ts +2 -0
  55. package/lib/spouts/types.d.ts.map +1 -1
  56. package/lib/spouts/types.js +1 -1
  57. package/package.json +5 -5
  58. package/src/floodSpouts.tsx +2 -2
  59. package/src/index.server.ts +3 -0
  60. package/src/index.ts +3 -0
  61. package/src/laySpouts.tsx +23 -29
  62. package/src/scripts/startDevserver.ts +6 -2
  63. package/src/spouts/app.server.tsx +8 -0
  64. package/src/spouts/app.tsx +6 -0
  65. package/src/spouts/document.server.tsx +64 -68
  66. package/src/spouts/document.tsx +5 -14
  67. package/src/spouts/json.server.tsx +41 -45
  68. package/src/spouts/json.tsx +8 -16
  69. package/src/spouts/prefetch.server.tsx +8 -4
  70. package/src/spouts/restHooks.server.tsx +27 -22
  71. package/src/spouts/restHooks.tsx +12 -18
  72. package/src/spouts/router.server.tsx +27 -21
  73. package/src/spouts/router.tsx +19 -19
  74. package/src/spouts/types.ts +18 -0
package/src/index.ts CHANGED
@@ -3,3 +3,6 @@ export { default as documentSpout } from './spouts/document';
3
3
  export { default as restHooksSpout } from './spouts/restHooks';
4
4
  export { default as routerSpout } from './spouts/router';
5
5
  export { default as JSONSpout } from './spouts/json';
6
+ export { default as appSpout } from './spouts/app';
7
+ export { ServerProps } from './spouts/types';
8
+ export { ClientSpout as Spout } from './spouts/types';
package/src/laySpouts.tsx CHANGED
@@ -8,32 +8,19 @@ export default function laySpouts(
8
8
  spouts: (props: ServerProps) => Promise<{
9
9
  app: JSX.Element;
10
10
  }>,
11
- { timeoutMS = 200 }: { timeoutMS?: number } = {},
11
+ {
12
+ timeoutMS = 200,
13
+ onError,
14
+ }: { timeoutMS?: number; onError?: (error: unknown) => void } = {},
12
15
  ) {
13
16
  const render: Render = async (clientManifest, req, res) => {
14
17
  const nonce = crypto.randomBytes(16).toString('base64');
15
18
 
16
- const { app } = await spouts({ clientManifest, req, res, nonce });
17
- let didError = false;
18
- const { pipe, abort } = reactRender(
19
- app,
20
- /*
21
- This is not documented, so included the types here for reference:
22
- type Options = {|
23
- identifierPrefix?: string,
24
- namespaceURI?: string,
25
- nonce?: string,
26
- bootstrapScriptContent?: string,
27
- bootstrapScripts?: Array<string>,
28
- bootstrapModules?: Array<string>,
29
- progressiveChunkSize?: number,
30
- onShellReady?: () => void,
31
- onShellError?: () => void,
32
- onAllReady?: () => void,
33
- onError?: (error: mixed) => void,
34
- |};
35
- */
36
- {
19
+ try {
20
+ const { app } = await spouts({ clientManifest, req, res, nonce });
21
+
22
+ let didError = false;
23
+ const { pipe, abort } = reactRender(app, {
37
24
  nonce,
38
25
  //bootstrapScripts: assets.filter(asset => asset.endsWith('.js')),
39
26
  onShellReady() {
@@ -48,17 +35,24 @@ type Options = {|
48
35
  res.statusCode = 500;
49
36
  pipe(res);
50
37
  },
51
- onError(x: any) {
38
+ onError(e: any) {
52
39
  didError = true;
53
- console.error(x);
40
+ console.error(e);
54
41
  res.statusCode = 500;
55
42
  //pipe(res); Removing this avoids, "React currently only supports piping to one writable stream."
43
+ if (onError) onError(e);
56
44
  },
57
- },
58
- );
59
- // Abandon and switch to client rendering if enough time passes.
60
- // Try lowering this to see the client recover.
61
- setTimeout(abort, timeoutMS);
45
+ });
46
+ // Abandon and switch to client rendering if enough time passes.
47
+ // Try lowering this to see the client recover.
48
+ setTimeout(
49
+ () => (abort as any)(`Timeout of ${timeoutMS}ms exceeded`),
50
+ timeoutMS,
51
+ );
52
+ } catch (e: unknown) {
53
+ if (onError) onError(e);
54
+ throw e;
55
+ }
62
56
  };
63
57
  return render;
64
58
  }
@@ -134,8 +134,12 @@ export default function startDevServer(
134
134
  ) {
135
135
  log.error('Errors for client build: ' + clientStats.compilation.errors);
136
136
  log.error('Errors for server build: ' + serverStats.compilation.errors);
137
- // TODO: handle more gracefully
138
- process.exit(-1);
137
+ // first time, rather than re-render
138
+ if (Array.isArray(initRender)) {
139
+ process.exit(-1);
140
+ }
141
+ log.error('Above compiler errors blocking reload');
142
+ return;
139
143
  } else {
140
144
  log.info('Launching SSR');
141
145
  }
@@ -0,0 +1,8 @@
1
+ import { ServerProps } from './types';
2
+
3
+ const appSpout =
4
+ (app: JSX.Element) =>
5
+ <P extends ServerProps>(props: P) =>
6
+ Promise.resolve({ ...props, app });
7
+
8
+ export default appSpout;
@@ -0,0 +1,6 @@
1
+ const appSpout =
2
+ (app: JSX.Element) =>
3
+ <P extends Record<string, unknown>>(props: P) =>
4
+ Promise.resolve({ ...props, app });
5
+
6
+ export default appSpout;
@@ -2,15 +2,15 @@ import React from 'react';
2
2
  import type { Route } from '@anansi/router';
3
3
  import { StatsChunkGroup } from 'webpack';
4
4
 
5
- import type { ServerProps, ResolveProps } from './types';
5
+ import type { ServerSpout } from './types';
6
6
  import type { Policy } from './csp';
7
7
  import Document from './DocumentComponent';
8
8
 
9
- type NeededProps = {
9
+ type NeededNext = {
10
10
  matchedRoutes: Route<any>[];
11
11
  title?: string;
12
12
  scripts?: React.ReactNode[];
13
- } & ResolveProps;
13
+ };
14
14
 
15
15
  export default function DocumentSpout(options: {
16
16
  head?: React.ReactNode;
@@ -18,77 +18,73 @@ export default function DocumentSpout(options: {
18
18
  rootId?: string;
19
19
  charSet?: string;
20
20
  csPolicy?: Policy;
21
- }) {
22
- return function <T extends NeededProps>(
23
- next: (props: ServerProps) => Promise<T>,
24
- ) {
25
- return async (props: ServerProps) => {
26
- const nextProps = await next(props);
21
+ }): ServerSpout<Record<string, unknown>, Record<string, unknown>, NeededNext> {
22
+ return next => async props => {
23
+ const nextProps = await next(props);
27
24
 
28
- const publicPath = props.clientManifest.publicPath;
25
+ const publicPath = props.clientManifest.publicPath;
29
26
 
30
- if (
31
- Object.keys(props.clientManifest?.entrypoints ?? {}).length < 1 ||
32
- publicPath === undefined
33
- )
34
- throw new Error('Manifest missing entries needed');
27
+ if (
28
+ Object.keys(props.clientManifest?.entrypoints ?? {}).length < 1 ||
29
+ publicPath === undefined
30
+ )
31
+ throw new Error('Manifest missing entries needed');
35
32
 
36
- // TODO: consider using this package for build stats in future:
37
- // https://github.com/facebook/react/tree/main/packages/react-server-dom-webpack
38
- const assetMap = (assets: { name: string; size?: number }[]) =>
39
- assets.map(({ name }) => `${publicPath}${name}`);
33
+ // TODO: consider using this package for build stats in future:
34
+ // https://github.com/facebook/react/tree/main/packages/react-server-dom-webpack
35
+ const assetMap = (assets: { name: string; size?: number }[]) =>
36
+ assets.map(({ name }) => `${publicPath}${name}`);
40
37
 
41
- const assetList: string[] = [];
42
- Object.values(props.clientManifest?.entrypoints ?? {}).forEach(
43
- entrypoint => {
44
- assetList.push(...assetMap(entrypoint.assets ?? []));
45
- },
46
- );
47
- new Set(
48
- assetMap(
49
- Object.values(props.clientManifest.namedChunkGroups ?? {})
50
- .filter(({ name }) =>
51
- nextProps.matchedRoutes.some(route => name?.includes(route.name)),
52
- )
53
- .flatMap(chunk => [
54
- ...(chunk.assets ?? []),
55
- // any chunk preloads
56
- ...childrenAssets(chunk),
57
- ]),
58
- ),
59
- ).forEach(asset => assetList.push(asset));
38
+ const assetList: string[] = [];
39
+ Object.values(props.clientManifest?.entrypoints ?? {}).forEach(
40
+ entrypoint => {
41
+ assetList.push(...assetMap(entrypoint.assets ?? []));
42
+ },
43
+ );
44
+ new Set(
45
+ assetMap(
46
+ Object.values(props.clientManifest.namedChunkGroups ?? {})
47
+ .filter(({ name }) =>
48
+ nextProps.matchedRoutes.some(route => name?.includes(route.name)),
49
+ )
50
+ .flatMap(chunk => [
51
+ ...(chunk.assets ?? []),
52
+ // any chunk preloads
53
+ ...childrenAssets(chunk),
54
+ ]),
55
+ ),
56
+ ).forEach(asset => assetList.push(asset));
60
57
 
61
- // find additional assets to preload based on matched route
62
- const assets: {
63
- href: string;
64
- as?: string | undefined;
65
- rel?: string | undefined;
66
- }[] = assetList
67
- .filter(asset => !asset.endsWith('.hot-update.js'))
68
- .map(asset =>
69
- asset.endsWith('.css')
70
- ? { href: asset, rel: 'stylesheet' }
71
- : asset.endsWith('.js')
72
- ? { href: asset, as: 'script' }
73
- : { href: asset },
74
- );
58
+ // find additional assets to preload based on matched route
59
+ const assets: {
60
+ href: string;
61
+ as?: string | undefined;
62
+ rel?: string | undefined;
63
+ }[] = assetList
64
+ .filter(asset => !asset.endsWith('.hot-update.js'))
65
+ .map(asset =>
66
+ asset.endsWith('.css')
67
+ ? { href: asset, rel: 'stylesheet' }
68
+ : asset.endsWith('.js')
69
+ ? { href: asset, as: 'script' }
70
+ : { href: asset },
71
+ );
75
72
 
76
- return {
77
- ...nextProps,
78
- app: (
79
- <Document
80
- {...options}
81
- title={nextProps.title ?? options.title}
82
- assets={assets}
83
- rootId={options.rootId}
84
- nonce={props.nonce}
85
- csPolicy={options.csPolicy}
86
- scripts={nextProps.scripts}
87
- >
88
- {nextProps.app}
89
- </Document>
90
- ),
91
- };
73
+ return {
74
+ ...nextProps,
75
+ app: (
76
+ <Document
77
+ {...options}
78
+ title={nextProps.title ?? options.title}
79
+ assets={assets}
80
+ rootId={options.rootId}
81
+ nonce={props.nonce}
82
+ csPolicy={options.csPolicy}
83
+ scripts={nextProps.scripts}
84
+ >
85
+ {nextProps.app}
86
+ </Document>
87
+ ),
92
88
  };
93
89
  };
94
90
  }
@@ -1,24 +1,15 @@
1
1
  import React from 'react';
2
2
  import type { Route } from '@anansi/router';
3
3
 
4
- import type { ResolveProps } from './types';
5
-
6
- type NeededProps = {
7
- matchedRoutes: Route<any>[];
8
- title?: string;
9
- } & ResolveProps;
4
+ import type { ClientSpout } from './types';
10
5
 
11
6
  export default function documentSpout(options: {
12
7
  head?: React.ReactNode;
13
8
  title: string;
14
- }) {
15
- return function <T extends NeededProps>(
16
- next: (initData: Record<string, unknown>) => Promise<T>,
17
- ) {
18
- return async (initData: Record<string, unknown>) => {
19
- const nextProps = await next(initData);
9
+ }): ClientSpout {
10
+ return next => async props => {
11
+ const nextProps = await next(props);
20
12
 
21
- return nextProps;
22
- };
13
+ return nextProps;
23
14
  };
24
15
  }
@@ -1,27 +1,24 @@
1
1
  import React from 'react';
2
- import type { Route } from '@anansi/router';
3
- import { StatsChunkGroup } from 'webpack';
4
2
 
5
- import type { ServerProps, ResolveProps } from './types';
6
- import type { Policy } from './csp';
7
- import Document from './DocumentComponent';
3
+ import type { ServerSpout } from './types';
8
4
 
9
- type NeededProps = {
5
+ type NeededNext = {
10
6
  initData?: Record<string, () => unknown>;
11
7
  scripts?: React.ReactNode[];
12
- } & ResolveProps;
8
+ };
13
9
 
14
10
  export default function JSONSpout({
15
11
  id = 'anansi-json',
16
- }: { id?: string } = {}) {
17
- return function <T extends NeededProps>(
18
- next: (props: ServerProps) => Promise<T>,
19
- ) {
20
- return async (props: ServerProps) => {
21
- const nextProps = await next(props);
12
+ }: { id?: string } = {}): ServerSpout<
13
+ Record<string, unknown>,
14
+ Record<string, unknown>,
15
+ NeededNext
16
+ > {
17
+ return next => async props => {
18
+ const nextProps = await next(props);
22
19
 
23
- const scripts: React.ReactNode[] = nextProps.scripts ?? [];
24
- /*
20
+ const scripts: React.ReactNode[] = nextProps.scripts ?? [];
21
+ /*
25
22
  Object.entries(nextProps.initData ?? {}).forEach(([key, data]) => {
26
23
  try {
27
24
  const encoded = JSON.stringify(data);
@@ -41,37 +38,36 @@ export default function JSONSpout({
41
38
  console.error(e);
42
39
  }
43
40
  });*/
44
- const Script = () => {
45
- try {
46
- const data: any = {};
47
- Object.entries(nextProps.initData ?? {}).forEach(([key, getData]) => {
48
- data[key] = getData();
49
- });
50
- const encoded = JSON.stringify(data);
51
- return (
52
- <script
53
- key={id}
54
- id={id}
55
- type="application/json"
56
- dangerouslySetInnerHTML={{
57
- __html: encoded,
58
- }}
59
- nonce={props.nonce}
60
- />
61
- );
62
- } catch (e) {
63
- // TODO: Use unified logging
64
- console.error('Error serializing json');
65
- console.error(e);
66
- return null;
67
- }
68
- };
69
- scripts.push(<Script />);
41
+ const Script = () => {
42
+ try {
43
+ const data: any = {};
44
+ Object.entries(nextProps.initData ?? {}).forEach(([key, getData]) => {
45
+ data[key] = getData();
46
+ });
47
+ const encoded = JSON.stringify(data);
48
+ return (
49
+ <script
50
+ key={id}
51
+ id={id}
52
+ type="application/json"
53
+ dangerouslySetInnerHTML={{
54
+ __html: encoded,
55
+ }}
56
+ nonce={props.nonce}
57
+ />
58
+ );
59
+ } catch (e) {
60
+ // TODO: Use unified logging
61
+ console.error('Error serializing json');
62
+ console.error(e);
63
+ return null;
64
+ }
65
+ };
66
+ scripts.push(<Script />);
70
67
 
71
- return {
72
- ...nextProps,
73
- scripts,
74
- };
68
+ return {
69
+ ...nextProps,
70
+ scripts,
75
71
  };
76
72
  };
77
73
  }
@@ -1,22 +1,14 @@
1
- import React from 'react';
2
- import type { Route } from '@anansi/router';
3
-
4
- import type { ResolveProps } from './types';
5
-
6
- type NeededProps = ResolveProps;
1
+ import type { ClientSpout } from './types';
7
2
 
8
3
  export default function JSONSpout({
9
4
  id = 'anansi-json',
10
- }: { id?: string } = {}) {
11
- return function <T extends NeededProps>(
12
- next: (initData: Record<string, unknown>) => Promise<T>,
13
- ) {
14
- return async () => {
15
- const initData = getDatafromDOM(id);
16
- const nextProps = await next(initData);
17
-
18
- return nextProps;
19
- };
5
+ }: { id?: string } = {}): ClientSpout<
6
+ Record<string, unknown>,
7
+ { initData: Record<string, unknown> }
8
+ > {
9
+ return next => async props => {
10
+ const initData = getDatafromDOM(id);
11
+ return { ...(await next({ ...props, initData })), initData };
20
12
  };
21
13
  }
22
14
  function getDatafromDOM(id: string): Record<string, unknown> {
@@ -7,14 +7,18 @@ type NeededProps<RouteWith> = {
7
7
  } & ResolveProps;
8
8
 
9
9
  export default function prefetchSpout<F extends string>(field: F) {
10
- return function <RouteWith, T extends NeededProps<RouteWith>>(
11
- next: (props: ServerProps) => Promise<
10
+ return function <
11
+ RouteWith,
12
+ N extends NeededProps<RouteWith>,
13
+ I extends ServerProps,
14
+ >(
15
+ next: (props: I) => Promise<
12
16
  {
13
17
  [K in F]: RouteWith;
14
- } & T
18
+ } & N
15
19
  >,
16
20
  ) {
17
- return async (props: ServerProps) => {
21
+ return async (props: I) => {
18
22
  const nextProps = await next(props);
19
23
 
20
24
  try {
@@ -1,34 +1,39 @@
1
- import { Manager, NetworkManager } from '@rest-hooks/core';
1
+ import { Controller, Manager, NetworkManager, State } from '@rest-hooks/core';
2
+ import type { Store } from 'redux';
2
3
 
3
4
  import { createPersistedStore } from './rhHelp';
4
- import type { ResolveProps, ServerProps } from './types';
5
-
6
- type NeededProps = { initData?: Record<string, () => unknown> } & ResolveProps;
5
+ import type { ServerSpout } from './types';
7
6
 
8
7
  export default function restHooksSpout(
9
8
  options: {
10
9
  getManagers: () => Manager[];
11
10
  } = { getManagers: () => [new NetworkManager()] },
12
- ) {
13
- return function <T extends NeededProps>(
14
- next: (props: ServerProps) => Promise<T>,
15
- ) {
16
- return async (props: ServerProps) => {
17
- const [ServerCacheProvider, controller, store] = createPersistedStore(
18
- options.getManagers(),
19
- );
11
+ ): ServerSpout<
12
+ Record<string, unknown>,
13
+ { controller: Controller; store: Store<State<unknown>> },
14
+ { initData?: Record<string, () => unknown> }
15
+ > {
16
+ return next => async props => {
17
+ const [ServerCacheProvider, controller, store] = createPersistedStore(
18
+ options.getManagers(),
19
+ );
20
20
 
21
- const nextProps = await next(props);
21
+ const nextProps = await next({
22
+ ...props,
23
+ controller,
24
+ store,
25
+ });
22
26
 
23
- return {
24
- ...nextProps,
25
- controller,
26
- initData: {
27
- ...nextProps.initData,
28
- resthooks: () => store.getState(),
29
- },
30
- app: <ServerCacheProvider>{nextProps.app}</ServerCacheProvider>,
31
- };
27
+ return {
28
+ ...nextProps,
29
+ initData: {
30
+ ...nextProps.initData,
31
+ resthooks: () => store.getState(),
32
+ },
33
+ app: <ServerCacheProvider>{nextProps.app}</ServerCacheProvider>,
34
+ // TODO: figure out how to only inject in next and not have to also put here
35
+ controller,
36
+ store,
32
37
  };
33
38
  };
34
39
  }
@@ -5,31 +5,25 @@ import {
5
5
  State,
6
6
  } from '@rest-hooks/core';
7
7
 
8
- import type { ResolveProps } from './types';
9
-
10
- type NeededProps = ResolveProps;
8
+ import type { ClientSpout } from './types';
11
9
 
12
10
  export default function restHooksSpout(
13
11
  options: {
14
12
  getManagers: () => Manager[];
15
13
  } = { getManagers: () => [new NetworkManager()] },
16
- ) {
17
- return function <T extends NeededProps>(
18
- next: (initData: Record<string, unknown>) => Promise<T>,
19
- ) {
20
- return async (initData: Record<string, unknown>) => {
21
- const data = initData.resthooks as State<unknown>;
14
+ ): ClientSpout<{ initData: Record<string, unknown> }> {
15
+ return next => async props => {
16
+ const data = props.initData.resthooks as State<unknown>;
22
17
 
23
- const nextProps = await next(initData);
18
+ const nextProps = await next(props);
24
19
 
25
- return {
26
- ...nextProps,
27
- app: (
28
- <CacheProvider initialState={data} managers={options.getManagers()}>
29
- {nextProps.app}
30
- </CacheProvider>
31
- ),
32
- };
20
+ return {
21
+ ...nextProps,
22
+ app: (
23
+ <CacheProvider initialState={data} managers={options.getManagers()}>
24
+ {nextProps.app}
25
+ </CacheProvider>
26
+ ),
33
27
  };
34
28
  };
35
29
  }
@@ -2,15 +2,19 @@ import { Route, RouteProvider, RouteController } from '@anansi/router';
2
2
  import React from 'react';
3
3
  import { createMemoryHistory } from 'history';
4
4
 
5
- import type { ResolveProps, ServerProps, CreateRouter } from './types';
6
-
7
- type NeededProps = ResolveProps;
5
+ import type { CreateRouter, ServerSpout } from './types';
8
6
 
9
7
  export default function routerSpout<ResolveWith>(options: {
10
8
  resolveWith?: any;
11
9
  useResolveWith: () => ResolveWith;
12
10
  createRouter: CreateRouter<ResolveWith>;
13
- }) {
11
+ }): ServerSpout<
12
+ Record<string, unknown>,
13
+ {
14
+ matchedRoutes: Route<ResolveWith>[];
15
+ router: RouteController<Route<ResolveWith, any>>;
16
+ }
17
+ > {
14
18
  const createRouteComponent = (
15
19
  router: RouteController<Route<ResolveWith, any>>,
16
20
  ) =>
@@ -24,25 +28,27 @@ export default function routerSpout<ResolveWith>(options: {
24
28
  );
25
29
  };
26
30
 
27
- return function <T extends NeededProps>(
28
- next: (props: ServerProps) => Promise<T>,
29
- ) {
30
- return async (props: ServerProps) => {
31
- const url = props.req.url || '';
32
- const router = options.createRouter(
33
- createMemoryHistory({ initialEntries: [url] }),
34
- );
35
- const matchedRoutes: Route<ResolveWith>[] = router.getMatchedRoutes(url);
31
+ return next => async props => {
32
+ const url = props.req.url || '';
33
+ const router = options.createRouter(
34
+ createMemoryHistory({ initialEntries: [url] }),
35
+ );
36
+ const matchedRoutes: Route<ResolveWith>[] = router.getMatchedRoutes(url);
37
+
38
+ const nextProps = await next({
39
+ ...props,
40
+ matchedRoutes,
41
+ router,
42
+ });
36
43
 
37
- const nextProps = await next(props);
44
+ const Router = createRouteComponent(router);
38
45
 
39
- const Router = createRouteComponent(router);
40
- return {
41
- ...nextProps,
42
- matchedRoutes,
43
- router,
44
- app: <Router>{nextProps.app}</Router>,
45
- };
46
+ return {
47
+ ...nextProps,
48
+ app: <Router>{nextProps.app}</Router>,
49
+ // TODO: figure out how to only inject in next and not have to also put here
50
+ matchedRoutes,
51
+ router,
46
52
  };
47
53
  };
48
54
  }