@anansi/core 0.11.1 → 0.13.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 (73) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +18 -7
  3. package/dist/client.js +29 -12
  4. package/dist/client.js.map +1 -1
  5. package/dist/server.js +97 -79
  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 +2 -0
  11. package/lib/index.d.ts.map +1 -1
  12. package/lib/index.js +10 -2
  13. package/lib/index.server.d.ts +2 -0
  14. package/lib/index.server.d.ts.map +1 -1
  15. package/lib/index.server.js +10 -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/DocumentComponent.d.ts.map +1 -1
  22. package/lib/spouts/DocumentComponent.js +3 -2
  23. package/lib/spouts/app.d.ts +5 -0
  24. package/lib/spouts/app.d.ts.map +1 -0
  25. package/lib/spouts/app.js +12 -0
  26. package/lib/spouts/app.server.d.ts +6 -0
  27. package/lib/spouts/app.server.d.ts.map +1 -0
  28. package/lib/spouts/app.server.js +12 -0
  29. package/lib/spouts/document.d.ts +2 -2
  30. package/lib/spouts/document.d.ts.map +1 -1
  31. package/lib/spouts/document.js +3 -3
  32. package/lib/spouts/document.server.d.ts +2 -2
  33. package/lib/spouts/document.server.d.ts.map +1 -1
  34. package/lib/spouts/document.server.js +1 -1
  35. package/lib/spouts/json.d.ts +3 -1
  36. package/lib/spouts/json.d.ts.map +1 -1
  37. package/lib/spouts/json.js +6 -5
  38. package/lib/spouts/json.server.d.ts +2 -2
  39. package/lib/spouts/json.server.d.ts.map +1 -1
  40. package/lib/spouts/json.server.js +1 -1
  41. package/lib/spouts/prefetch.server.d.ts +1 -1
  42. package/lib/spouts/prefetch.server.d.ts.map +1 -1
  43. package/lib/spouts/prefetch.server.js +1 -1
  44. package/lib/spouts/restHooks.d.ts +3 -1
  45. package/lib/spouts/restHooks.d.ts.map +1 -1
  46. package/lib/spouts/restHooks.js +4 -4
  47. package/lib/spouts/restHooks.server.d.ts +44 -4
  48. package/lib/spouts/restHooks.server.d.ts.map +1 -1
  49. package/lib/spouts/restHooks.server.js +9 -4
  50. package/lib/spouts/router.d.ts +1 -1
  51. package/lib/spouts/router.d.ts.map +1 -1
  52. package/lib/spouts/router.js +3 -3
  53. package/lib/spouts/router.server.d.ts +5 -2
  54. package/lib/spouts/router.server.d.ts.map +1 -1
  55. package/lib/spouts/router.server.js +8 -4
  56. package/package.json +6 -6
  57. package/src/floodSpouts.tsx +2 -2
  58. package/src/index.server.ts +2 -0
  59. package/src/index.ts +2 -0
  60. package/src/laySpouts.tsx +23 -29
  61. package/src/scripts/startDevserver.ts +6 -2
  62. package/src/spouts/DocumentComponent.tsx +6 -1
  63. package/src/spouts/app.server.tsx +8 -0
  64. package/src/spouts/app.tsx +6 -0
  65. package/src/spouts/document.server.tsx +4 -4
  66. package/src/spouts/document.tsx +5 -5
  67. package/src/spouts/json.server.tsx +4 -8
  68. package/src/spouts/json.tsx +7 -12
  69. package/src/spouts/prefetch.server.tsx +8 -4
  70. package/src/spouts/restHooks.server.tsx +16 -7
  71. package/src/spouts/restHooks.tsx +6 -6
  72. package/src/spouts/router.server.tsx +17 -6
  73. package/src/spouts/router.tsx +5 -5
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
  }
@@ -30,7 +30,12 @@ export default function Document({
30
30
  const policy = {
31
31
  ...csPolicy,
32
32
  };
33
- if (nonce) {
33
+ if (
34
+ nonce &&
35
+ // nonces negate 'unsafe-inline' so do not add it if that directive exists
36
+ (!policy['script-src'] ||
37
+ !policy['script-src'].includes("'unsafe-inline'"))
38
+ ) {
34
39
  if (typeof policy['script-src'] === 'string') {
35
40
  policy['script-src'] = [policy['script-src'], `'nonce-${nonce}'`];
36
41
  } else {
@@ -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;
@@ -6,7 +6,7 @@ import type { ServerProps, ResolveProps } 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[];
@@ -19,10 +19,10 @@ export default function DocumentSpout(options: {
19
19
  charSet?: string;
20
20
  csPolicy?: Policy;
21
21
  }) {
22
- return function <T extends NeededProps>(
23
- next: (props: ServerProps) => Promise<T>,
22
+ return function <N extends NeededNext, I extends ServerProps>(
23
+ next: (props: I) => Promise<N>,
24
24
  ) {
25
- return async (props: ServerProps) => {
25
+ return async (props: I) => {
26
26
  const nextProps = await next(props);
27
27
 
28
28
  const publicPath = props.clientManifest.publicPath;
@@ -3,7 +3,7 @@ import type { Route } from '@anansi/router';
3
3
 
4
4
  import type { ResolveProps } from './types';
5
5
 
6
- type NeededProps = {
6
+ type NeededNext = {
7
7
  matchedRoutes: Route<any>[];
8
8
  title?: string;
9
9
  } & ResolveProps;
@@ -12,11 +12,11 @@ export default function documentSpout(options: {
12
12
  head?: React.ReactNode;
13
13
  title: string;
14
14
  }) {
15
- return function <T extends NeededProps>(
16
- next: (initData: Record<string, unknown>) => Promise<T>,
15
+ return function <N extends NeededNext, I extends Record<string, unknown>>(
16
+ next: (props: I) => Promise<N>,
17
17
  ) {
18
- return async (initData: Record<string, unknown>) => {
19
- const nextProps = await next(initData);
18
+ return async (props: I) => {
19
+ const nextProps = await next(props);
20
20
 
21
21
  return nextProps;
22
22
  };
@@ -1,12 +1,8 @@
1
1
  import React from 'react';
2
- import type { Route } from '@anansi/router';
3
- import { StatsChunkGroup } from 'webpack';
4
2
 
5
3
  import type { ServerProps, ResolveProps } from './types';
6
- import type { Policy } from './csp';
7
- import Document from './DocumentComponent';
8
4
 
9
- type NeededProps = {
5
+ type NeededNext = {
10
6
  initData?: Record<string, () => unknown>;
11
7
  scripts?: React.ReactNode[];
12
8
  } & ResolveProps;
@@ -14,10 +10,10 @@ type NeededProps = {
14
10
  export default function JSONSpout({
15
11
  id = 'anansi-json',
16
12
  }: { id?: string } = {}) {
17
- return function <T extends NeededProps>(
18
- next: (props: ServerProps) => Promise<T>,
13
+ return function <N extends NeededNext, I extends ServerProps>(
14
+ next: (props: I) => Promise<N>,
19
15
  ) {
20
- return async (props: ServerProps) => {
16
+ return async (props: I) => {
21
17
  const nextProps = await next(props);
22
18
 
23
19
  const scripts: React.ReactNode[] = nextProps.scripts ?? [];
@@ -1,25 +1,20 @@
1
- import React from 'react';
2
- import type { Route } from '@anansi/router';
3
-
4
1
  import type { ResolveProps } from './types';
5
2
 
6
- type NeededProps = ResolveProps;
3
+ type NeededNext = ResolveProps;
7
4
 
8
5
  export default function JSONSpout({
9
6
  id = 'anansi-json',
10
7
  }: { id?: string } = {}) {
11
- return function <T extends NeededProps>(
12
- next: (initData: Record<string, unknown>) => Promise<T>,
8
+ return function <N extends NeededNext, I extends Record<string, unknown>>(
9
+ next: (props: I & { initData: Record<string, unknown> }) => Promise<N>,
13
10
  ) {
14
- return async () => {
11
+ return async (props: I) => {
15
12
  const initData = getDatafromDOM(id);
16
- const nextProps = await next(initData);
17
-
18
- return nextProps;
13
+ return await next({ ...props, initData });
19
14
  };
20
15
  };
21
16
  }
22
17
  function getDatafromDOM(id: string): Record<string, unknown> {
23
- const element = document.querySelector(`#${id}`);
24
- return element?.innerHTML ? JSON.parse(element?.innerHTML) : undefined;
18
+ const element: HTMLScriptElement | null = document.querySelector(`#${id}`);
19
+ return element?.text ? JSON.parse(element?.text) : undefined;
25
20
  }
@@ -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,33 +1,42 @@
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
5
  import type { ResolveProps, ServerProps } from './types';
5
6
 
6
- type NeededProps = { initData?: Record<string, () => unknown> } & ResolveProps;
7
+ type NeededNext = { initData?: Record<string, () => unknown> } & ResolveProps;
7
8
 
8
9
  export default function restHooksSpout(
9
10
  options: {
10
11
  getManagers: () => Manager[];
11
12
  } = { getManagers: () => [new NetworkManager()] },
12
13
  ) {
13
- return function <T extends NeededProps>(
14
- next: (props: ServerProps) => Promise<T>,
14
+ return function <N extends NeededNext, I extends ServerProps>(
15
+ next: (
16
+ props: I & { controller: Controller; store: Store<State<unknown>> },
17
+ ) => Promise<N>,
15
18
  ) {
16
- return async (props: ServerProps) => {
19
+ return async (props: I) => {
17
20
  const [ServerCacheProvider, controller, store] = createPersistedStore(
18
21
  options.getManagers(),
19
22
  );
20
23
 
21
- const nextProps = await next(props);
24
+ const nextProps = await next({
25
+ ...props,
26
+ controller,
27
+ store,
28
+ });
22
29
 
23
30
  return {
24
31
  ...nextProps,
25
- controller,
26
32
  initData: {
27
33
  ...nextProps.initData,
28
34
  resthooks: () => store.getState(),
29
35
  },
30
36
  app: <ServerCacheProvider>{nextProps.app}</ServerCacheProvider>,
37
+ // TODO: figure out how to only inject in next and not have to also put here
38
+ controller,
39
+ store,
31
40
  };
32
41
  };
33
42
  };
@@ -7,20 +7,20 @@ import {
7
7
 
8
8
  import type { ResolveProps } from './types';
9
9
 
10
- type NeededProps = ResolveProps;
10
+ type NeededNext = ResolveProps;
11
11
 
12
12
  export default function restHooksSpout(
13
13
  options: {
14
14
  getManagers: () => Manager[];
15
15
  } = { getManagers: () => [new NetworkManager()] },
16
16
  ) {
17
- return function <T extends NeededProps>(
18
- next: (initData: Record<string, unknown>) => Promise<T>,
17
+ return function <N extends NeededNext, I extends Record<string, unknown>>(
18
+ next: (props: I) => Promise<N>,
19
19
  ) {
20
- return async (initData: Record<string, unknown>) => {
21
- const data = initData.resthooks as State<unknown>;
20
+ return async (props: I & { initData: Record<string, unknown> }) => {
21
+ const data = props.initData.resthooks as State<unknown>;
22
22
 
23
- const nextProps = await next(initData);
23
+ const nextProps = await next(props);
24
24
 
25
25
  return {
26
26
  ...nextProps,
@@ -4,7 +4,7 @@ import { createMemoryHistory } from 'history';
4
4
 
5
5
  import type { ResolveProps, ServerProps, CreateRouter } from './types';
6
6
 
7
- type NeededProps = ResolveProps;
7
+ type NeededNext = ResolveProps;
8
8
 
9
9
  export default function routerSpout<ResolveWith>(options: {
10
10
  resolveWith?: any;
@@ -24,24 +24,35 @@ export default function routerSpout<ResolveWith>(options: {
24
24
  );
25
25
  };
26
26
 
27
- return function <T extends NeededProps>(
28
- next: (props: ServerProps) => Promise<T>,
27
+ return function <N extends NeededNext, I extends ServerProps>(
28
+ next: (
29
+ props: I & {
30
+ matchedRoutes: Route<ResolveWith>[];
31
+ router: RouteController<Route<ResolveWith, any>>;
32
+ },
33
+ ) => Promise<N>,
29
34
  ) {
30
- return async (props: ServerProps) => {
35
+ return async (props: I) => {
31
36
  const url = props.req.url || '';
32
37
  const router = options.createRouter(
33
38
  createMemoryHistory({ initialEntries: [url] }),
34
39
  );
35
40
  const matchedRoutes: Route<ResolveWith>[] = router.getMatchedRoutes(url);
36
41
 
37
- const nextProps = await next(props);
42
+ const nextProps = await next({
43
+ ...props,
44
+ matchedRoutes,
45
+ router,
46
+ });
38
47
 
39
48
  const Router = createRouteComponent(router);
49
+
40
50
  return {
41
51
  ...nextProps,
52
+ app: <Router>{nextProps.app}</Router>,
53
+ // TODO: figure out how to only inject in next and not have to also put here
42
54
  matchedRoutes,
43
55
  router,
44
- app: <Router>{nextProps.app}</Router>,
45
56
  };
46
57
  };
47
58
  };
@@ -5,7 +5,7 @@ import type { Update } from 'history';
5
5
 
6
6
  import type { ResolveProps, CreateRouter } from './types';
7
7
 
8
- type NeededProps = ResolveProps;
8
+ type NeededNext = ResolveProps;
9
9
 
10
10
  export default function routerSpout<ResolveWith>(options: {
11
11
  resolveWith?: any;
@@ -30,15 +30,15 @@ export default function routerSpout<ResolveWith>(options: {
30
30
  );
31
31
  };
32
32
 
33
- return function <T extends NeededProps>(
34
- next: (initData: Record<string, unknown>) => Promise<T>,
33
+ return function <N extends NeededNext, I extends Record<string, unknown>>(
34
+ next: (initData: Record<string, unknown>) => Promise<N>,
35
35
  ) {
36
- return async (initData: Record<string, unknown>) => {
36
+ return async (props: I) => {
37
37
  const history = createBrowserHistory();
38
38
  const router = options.createRouter(history);
39
39
  const matchedRoutes = router.getMatchedRoutes(history.location.pathname);
40
40
 
41
- const nextProps = await next(initData);
41
+ const nextProps = await next(props);
42
42
 
43
43
  const Router = createRouteComponent(router);
44
44
  return {