piral-core 0.15.0-alpha.3549 → 0.15.0-alpha.3592

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 (75) hide show
  1. package/esm/actions/app.d.ts +3 -1
  2. package/esm/actions/app.js +18 -0
  3. package/esm/actions/app.js.map +1 -1
  4. package/esm/components/ErrorBoundary.d.ts +12 -33
  5. package/esm/components/ErrorBoundary.js +12 -14
  6. package/esm/components/ErrorBoundary.js.map +1 -1
  7. package/esm/createInstance.js +5 -1
  8. package/esm/createInstance.js.map +1 -1
  9. package/{debug-piral.d.ts → esm/debugger.d.ts} +1 -1
  10. package/esm/debugger.js +52 -0
  11. package/esm/debugger.js.map +1 -0
  12. package/{debug-pilet.d.ts → esm/emulator.d.ts} +1 -1
  13. package/esm/emulator.js +8 -0
  14. package/esm/emulator.js.map +1 -0
  15. package/esm/helpers.js +2 -2
  16. package/esm/helpers.js.map +1 -1
  17. package/esm/modules/core.js +8 -6
  18. package/esm/modules/core.js.map +1 -1
  19. package/esm/modules/dependencies.d.ts +2 -2
  20. package/esm/modules/dependencies.js.map +1 -1
  21. package/esm/state/withApi.d.ts +1 -1
  22. package/esm/state/withApi.js +31 -33
  23. package/esm/state/withApi.js.map +1 -1
  24. package/esm/types/api.d.ts +12 -8
  25. package/esm/types/state.d.ts +13 -3
  26. package/esm/types/utils.d.ts +1 -1
  27. package/esm/utils/index.d.ts +1 -0
  28. package/esm/utils/index.js +1 -0
  29. package/esm/utils/index.js.map +1 -1
  30. package/lib/actions/app.d.ts +3 -1
  31. package/lib/actions/app.js +21 -1
  32. package/lib/actions/app.js.map +1 -1
  33. package/lib/components/ErrorBoundary.d.ts +12 -33
  34. package/lib/components/ErrorBoundary.js +12 -14
  35. package/lib/components/ErrorBoundary.js.map +1 -1
  36. package/lib/createInstance.js +5 -1
  37. package/lib/createInstance.js.map +1 -1
  38. package/lib/debugger.d.ts +4 -0
  39. package/lib/debugger.js +56 -0
  40. package/lib/debugger.js.map +1 -0
  41. package/lib/emulator.d.ts +3 -0
  42. package/lib/emulator.js +12 -0
  43. package/lib/emulator.js.map +1 -0
  44. package/lib/helpers.js +2 -2
  45. package/lib/helpers.js.map +1 -1
  46. package/lib/modules/core.js +8 -6
  47. package/lib/modules/core.js.map +1 -1
  48. package/lib/modules/dependencies.d.ts +2 -2
  49. package/lib/modules/dependencies.js.map +1 -1
  50. package/lib/state/withApi.d.ts +1 -1
  51. package/lib/state/withApi.js +30 -32
  52. package/lib/state/withApi.js.map +1 -1
  53. package/lib/types/api.d.ts +12 -8
  54. package/lib/types/state.d.ts +13 -3
  55. package/lib/types/utils.d.ts +1 -1
  56. package/lib/utils/index.d.ts +1 -0
  57. package/lib/utils/index.js +4 -0
  58. package/lib/utils/index.js.map +1 -1
  59. package/package.json +9 -11
  60. package/src/actions/app.ts +25 -0
  61. package/src/components/ErrorBoundary.tsx +19 -51
  62. package/src/createInstance.tsx +5 -1
  63. package/src/debugger.ts +82 -0
  64. package/src/emulator.ts +10 -0
  65. package/src/helpers.tsx +2 -2
  66. package/src/modules/core.ts +10 -8
  67. package/src/modules/dependencies.ts +3 -2
  68. package/src/state/withApi.test.tsx +20 -4
  69. package/src/state/withApi.tsx +51 -42
  70. package/src/types/api.ts +23 -9
  71. package/src/types/state.ts +13 -3
  72. package/src/types/utils.ts +1 -1
  73. package/src/utils/index.ts +2 -0
  74. package/debug-pilet.js +0 -13
  75. package/debug-piral.js +0 -59
@@ -6,7 +6,7 @@ import type { Dict, Without } from './common';
6
6
  import type { LayoutType } from './layout';
7
7
  import type { SharedDataItem, DataStoreTarget } from './data';
8
8
  import type { PiralCustomActions, PiralCustomState, PiralCustomRegistryState, PiralCustomComponentsState } from './custom';
9
- import type { PiletMetadata, EventEmitter, Pilet, BaseComponentProps, PageComponentProps, ExtensionComponentProps, PiletsBag, PiralPageMeta } from './api';
9
+ import type { EventEmitter, Pilet, BaseComponentProps, PageComponentProps, ExtensionComponentProps, PiletsBag, PiralPageMeta, PiletEntry } from './api';
10
10
  import type { ComponentConverters, LoadingIndicatorProps, ErrorInfoProps, RouterProps, LayoutProps, Errors } from './components';
11
11
  export interface StateDispatcher<TState> {
12
12
  (state: TState): Partial<TState>;
@@ -130,7 +130,7 @@ export interface GlobalState extends PiralCustomState {
130
130
  /**
131
131
  * Gets the loaded modules.
132
132
  */
133
- modules: Array<PiletMetadata>;
133
+ modules: Array<Pilet>;
134
134
  /**
135
135
  * The foreign component portals to render.
136
136
  */
@@ -172,10 +172,20 @@ export interface PiralActions extends PiralCustomActions {
172
172
  */
173
173
  initialize(loading: boolean, error: Error | undefined, modules: Array<Pilet>): void;
174
174
  /**
175
- * Injects a pilet at runtime - removes the pilet from registry first if available.
175
+ * Injects an evaluated pilet at runtime - removes the pilet from registry first if available.
176
176
  * @param pilet The pilet to be injected.
177
177
  */
178
178
  injectPilet(pilet: Pilet): void;
179
+ /**
180
+ * Adds a pilet at runtime by loading it, evaluating it, and then injecting it.
181
+ * @param pilet The pilet to be added.
182
+ */
183
+ addPilet(pilet: PiletEntry): void;
184
+ /**
185
+ * Removes a pilet by unloading it and deleting all component registrations.
186
+ * @param name The name of the pilet to remove.
187
+ */
188
+ removePilet(name: string): void;
179
189
  /**
180
190
  * Defines a single action for Piral.
181
191
  * @param actionName The name of the action to define.
@@ -55,7 +55,7 @@ export interface PiralStoreDataEvent<TValue = any> {
55
55
  */
56
56
  expires: number;
57
57
  }
58
- declare module 'piral-base/lib/types' {
58
+ declare module 'piral-base/lib/types/api' {
59
59
  interface PiralEventMap extends PiralCustomEventMap {
60
60
  'store-data': PiralStoreDataEvent;
61
61
  }
@@ -1,3 +1,4 @@
1
+ export { isfunc, requireModule } from 'piral-base';
1
2
  export * from './compare';
2
3
  export * from './data';
3
4
  export * from './extension';
@@ -1,6 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.requireModule = exports.isfunc = void 0;
3
4
  const tslib_1 = require("tslib");
5
+ var piral_base_1 = require("piral-base");
6
+ Object.defineProperty(exports, "isfunc", { enumerable: true, get: function () { return piral_base_1.isfunc; } });
7
+ Object.defineProperty(exports, "requireModule", { enumerable: true, get: function () { return piral_base_1.requireModule; } });
4
8
  (0, tslib_1.__exportStar)(require("./compare"), exports);
5
9
  (0, tslib_1.__exportStar)(require("./data"), exports);
6
10
  (0, tslib_1.__exportStar)(require("./extension"), exports);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":";;;AAAA,yDAA0B;AAC1B,sDAAuB;AACvB,2DAA4B;AAC5B,yDAA0B;AAC1B,sDAAuB;AACvB,yDAA0B;AAC1B,uDAAwB;AACxB,uDAAwB;AACxB,uDAAwB;AACxB,yDAA0B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":";;;;AAAA,yCAAmD;AAA1C,oGAAA,MAAM,OAAA;AAAE,2GAAA,aAAa,OAAA;AAE9B,yDAA0B;AAC1B,sDAAuB;AACvB,2DAA4B;AAC5B,yDAA0B;AAC1B,sDAAuB;AACvB,yDAA0B;AAC1B,uDAAwB;AACxB,uDAAwB;AACxB,uDAAwB;AACxB,yDAA0B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "piral-core",
3
- "version": "0.15.0-alpha.3549",
3
+ "version": "0.15.0-alpha.3592",
4
4
  "description": "The core library for creating a Piral instance.",
5
5
  "keywords": [
6
6
  "portal",
@@ -22,10 +22,6 @@
22
22
  "esm",
23
23
  "lib",
24
24
  "src",
25
- "debug-pilet.d.ts",
26
- "debug-piral.d.ts",
27
- "debug-pilet.js",
28
- "debug-piral.js",
29
25
  "dependencies.codegen",
30
26
  "dependencies.codegen.native.js"
31
27
  ],
@@ -41,8 +37,7 @@
41
37
  "url": "https://github.com/smapiot/piral/issues"
42
38
  },
43
39
  "scripts": {
44
- "build": "yarn build:commonjs && yarn build:esnext && yarn build:convert",
45
- "build:convert": "tsc debug-pilet.ts debug-piral.ts --skipLibCheck --declaration",
40
+ "build": "yarn build:commonjs && yarn build:esnext",
46
41
  "build:commonjs": "tsc --project tsconfig.json --outDir lib --module commonjs",
47
42
  "build:esnext": "tsc --project tsconfig.json --outDir esm --module esnext",
48
43
  "typedoc": "typedoc --json ../../../docs/types/piral-core.json src --exclude \"src/**/*.test.*\"",
@@ -50,8 +45,8 @@
50
45
  },
51
46
  "dependencies": {
52
47
  "@dbeining/react-atom": "^4.0.0",
53
- "piral-base": "0.15.0-alpha.3549",
54
- "piral-debug-utils": "0.15.0-alpha.3549"
48
+ "piral-base": "0.15.0-alpha.3592",
49
+ "piral-debug-utils": "0.15.0-alpha.3592"
55
50
  },
56
51
  "peerDependencies": {
57
52
  "react": ">=16.8.0",
@@ -62,10 +57,13 @@
62
57
  "devDependencies": {
63
58
  "@types/history": "^4.7.8",
64
59
  "@types/react": "^17.0.0",
60
+ "@types/react-dom": "^17.0.0",
65
61
  "@types/react-router": "^5.1.8",
66
62
  "@types/react-router-dom": "^5.1.6",
67
63
  "react": "^17.0.1",
68
- "react-dom": "^17.0.1"
64
+ "react-dom": "^17.0.1",
65
+ "react-router": "^5.2.0",
66
+ "react-router-dom": "^5.2.0"
69
67
  },
70
68
  "sharedDependencies": [
71
69
  "react",
@@ -78,5 +76,5 @@
78
76
  "@libre/atom",
79
77
  "@dbeining/react-atom"
80
78
  ],
81
- "gitHead": "2edf7bbcee6a7e931348198d14139c7f958877e7"
79
+ "gitHead": "5537ccd39d275afdc53845ab8cb4868a8dbc8903"
82
80
  }
@@ -1,5 +1,6 @@
1
1
  import { ComponentType } from 'react';
2
2
  import { RouteComponentProps } from 'react-router';
3
+ import { runPilet } from 'piral-base';
3
4
  import { withKey, replaceOrAddItem, removeNested, withProvider, withRoute } from '../utils';
4
5
  import {
5
6
  LayoutType,
@@ -9,6 +10,7 @@ import {
9
10
  RegistryState,
10
11
  GlobalStateContext,
11
12
  Pilet,
13
+ PiletEntry,
12
14
  } from '../types';
13
15
 
14
16
  export function changeLayout(ctx: GlobalStateContext, current: LayoutType) {
@@ -30,6 +32,29 @@ export function initialize(ctx: GlobalStateContext, loading: boolean, error: Err
30
32
  }));
31
33
  }
32
34
 
35
+ export function addPilet(ctx: GlobalStateContext, meta: PiletEntry) {
36
+ ctx.options.loadPilet(meta).then((pilet) => {
37
+ try {
38
+ ctx.injectPilet(pilet);
39
+ runPilet(ctx.options.createApi, pilet, ctx.options.hooks);
40
+ } catch (error) {
41
+ console.error(error);
42
+ }
43
+ });
44
+ }
45
+
46
+ export function removePilet(ctx: GlobalStateContext, name: string) {
47
+ ctx.dispatch((state) => ({
48
+ ...state,
49
+ modules: state.modules.filter((m) => m.name !== name),
50
+ registry: removeNested<RegistryState, BaseRegistration>(state.registry, (m) => m.pilet === name),
51
+ }));
52
+
53
+ ctx.emit('unload-pilet', {
54
+ name,
55
+ });
56
+ }
57
+
33
58
  export function injectPilet(ctx: GlobalStateContext, pilet: Pilet) {
34
59
  ctx.dispatch((state) => ({
35
60
  ...state,
@@ -1,42 +1,18 @@
1
1
  import * as React from 'react';
2
- import { isfunc } from 'piral-base';
2
+ import { PiralError, PiralLoadingIndicator } from './components';
3
+ import { Errors, PiletApi } from '../types';
3
4
 
4
- /**
5
- * The options to be used for determining the boundary's behavior.
6
- */
7
- export interface ErrorBoundaryOptions<T> {
8
- /**
9
- * Callback to be used in case of an emitted error.
10
- * @param error The error caught by the boundary.
11
- */
12
- onError(error: Error): void;
5
+ export interface ErrorBoundaryProps {
13
6
  /**
14
- * Callback to be used when an error should be rendered.
15
- * @param error The error caught by the boundary.
16
- * @param props The props used by the boundary.
7
+ * The type of error to indicate when caught.
17
8
  */
18
- renderError?(error: Error, props: T): React.ReactNode;
19
- /**
20
- * Callback to used when no error should be rendered.
21
- * @param children The standard children to render.
22
- * @param props The props used by the boundary.
23
- */
24
- renderChild(children: React.ReactNode, props: T): React.ReactNode;
25
- }
26
-
27
- /**
28
- * The props of the ErrorBoundary component.
29
- */
30
- export interface ErrorBoundaryProps<T> extends ErrorBoundaryOptions<T> {
9
+ errorType: keyof Errors;
31
10
  /**
32
- * The render props for the specific ErrorBoundary.
11
+ * The associated pilet api for the metadata.
33
12
  */
34
- renderProps: T;
13
+ piral: PiletApi;
35
14
  }
36
15
 
37
- /**
38
- * The state of the ErrorBoundary component.
39
- */
40
16
  export interface ErrorBoundaryState {
41
17
  /**
42
18
  * The current error (if any) caught by the boundary.
@@ -45,22 +21,17 @@ export interface ErrorBoundaryState {
45
21
  }
46
22
 
47
23
  /**
48
- * The React component for catching errors and displaying error information.
24
+ * The component for catching errors and displaying error information.
49
25
  */
50
- export class ErrorBoundary<T> extends React.Component<ErrorBoundaryProps<T>, ErrorBoundaryState> {
51
- constructor(props: ErrorBoundaryProps<T>) {
52
- super(props);
53
- this.state = {
54
- error: undefined,
55
- };
56
- }
26
+ export class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
27
+ state = {
28
+ error: undefined,
29
+ };
57
30
 
58
31
  componentDidCatch(error: Error) {
59
- const { onError } = this.props;
60
-
61
- if (isfunc(onError)) {
62
- onError(error);
63
- }
32
+ const { piral, errorType } = this.props;
33
+ const pilet = piral.meta.name;
34
+ console.error(`[${pilet}] Exception in component of type "${errorType}".`, error);
64
35
 
65
36
  this.setState({
66
37
  error,
@@ -68,17 +39,14 @@ export class ErrorBoundary<T> extends React.Component<ErrorBoundaryProps<T>, Err
68
39
  }
69
40
 
70
41
  render() {
71
- const { children, renderError, renderChild, renderProps } = this.props;
42
+ const { children, piral, errorType, ...renderProps } = this.props;
72
43
  const { error } = this.state;
44
+ const rest: any = renderProps;
73
45
 
74
46
  if (error) {
75
- if (isfunc(renderError)) {
76
- return renderError(error, renderProps);
77
- }
78
-
79
- return <div style={{ whiteSpace: 'pre-wrap' }}>{error && error.message}</div>;
47
+ return <PiralError type={errorType} error={error} {...rest} />;
80
48
  }
81
49
 
82
- return isfunc(renderChild) ? renderChild(children, renderProps) : children;
50
+ return <React.Suspense fallback={<PiralLoadingIndicator />}>{children}</React.Suspense>;
83
51
  }
84
52
  }
@@ -51,7 +51,11 @@ export function createInstance(config: PiralInstanceOptions = {}): PiralInstance
51
51
  const root = createApi({
52
52
  name: 'root',
53
53
  version: process.env.BUILD_PCKG_VERSION || '1.0.0',
54
- spec: '',
54
+ spec: 'v0',
55
+ basePath: '',
56
+ link: '',
57
+ config: {},
58
+ dependencies: {},
55
59
  });
56
60
  const options = createPiletOptions({
57
61
  context,
@@ -0,0 +1,82 @@
1
+ import { addChangeHandler } from '@dbeining/react-atom';
2
+ import { LoadPiletsOptions } from 'piral-base';
3
+ import { installPiralDebug, DebuggerExtensionOptions } from 'piral-debug-utils';
4
+ import { GlobalStateContext } from './types';
5
+
6
+ export function integrate(
7
+ context: GlobalStateContext,
8
+ options: LoadPiletsOptions,
9
+ debug: DebuggerExtensionOptions = {},
10
+ ) {
11
+ installPiralDebug({
12
+ ...debug,
13
+ addPilet: context.addPilet,
14
+ removePilet: context.removePilet,
15
+ updatePilet(pilet) {
16
+ if (!pilet.disabled) {
17
+ const { createApi } = options;
18
+ const newApi = createApi(pilet);
19
+
20
+ try {
21
+ context.injectPilet(pilet);
22
+ pilet.setup(newApi);
23
+ } catch (error) {
24
+ console.error(error);
25
+ }
26
+ } else {
27
+ context.injectPilet(pilet);
28
+ }
29
+ },
30
+ fireEvent: context.emit,
31
+ getDependencies() {
32
+ return Object.keys(options.dependencies);
33
+ },
34
+ getExtensions() {
35
+ return context.readState((s) => Object.keys(s.registry.extensions));
36
+ },
37
+ getRoutes() {
38
+ const registeredRoutes = context.readState((state) => Object.keys(state.registry.pages));
39
+ const componentRoutes = context.readState((state) => Object.keys(state.routes));
40
+ return [...componentRoutes, ...registeredRoutes];
41
+ },
42
+ getGlobalState() {
43
+ return context.readState((s) => s);
44
+ },
45
+ getPilets() {
46
+ return context.readState((s) => s.modules);
47
+ },
48
+ integrate(dbg) {
49
+ context.dispatch((s) => ({
50
+ ...s,
51
+ components: {
52
+ ...s.components,
53
+ ...dbg.components,
54
+ },
55
+ routes: {
56
+ ...s.routes,
57
+ ...dbg.routes,
58
+ },
59
+ registry: {
60
+ ...s.registry,
61
+ wrappers: {
62
+ ...s.registry.wrappers,
63
+ ...dbg.wrappers,
64
+ },
65
+ },
66
+ }));
67
+
68
+ addChangeHandler(context.state, 'debugging', ({ previous, current }) => {
69
+ const pilets = current.modules !== previous.modules;
70
+ const pages = current.registry.pages !== previous.registry.pages || current.routes !== previous.routes;
71
+ const extensions = current.registry.extensions !== previous.registry.extensions;
72
+ const state = current !== previous;
73
+ dbg.onChange(previous, current, {
74
+ pilets,
75
+ pages,
76
+ extensions,
77
+ state,
78
+ });
79
+ });
80
+ },
81
+ });
82
+ }
@@ -0,0 +1,10 @@
1
+ import { LoadPiletsOptions } from 'piral-base';
2
+ import { withEmulatorPilets } from 'piral-debug-utils';
3
+ import { GlobalStateContext } from './types';
4
+
5
+ export function integrate(context: GlobalStateContext, options: LoadPiletsOptions) {
6
+ options.fetchPilets = withEmulatorPilets(options.fetchPilets, {
7
+ addPilet: context.addPilet,
8
+ removePilet: context.removePilet,
9
+ });
10
+ }
package/src/helpers.tsx CHANGED
@@ -52,13 +52,13 @@ export function createPiletOptions({
52
52
 
53
53
  // if we build the debug version of piral (debug and emulator build)
54
54
  if (process.env.DEBUG_PIRAL) {
55
- const { integrate } = require('../debug-piral');
55
+ const { integrate } = require('./debugger');
56
56
  integrate(context, options, debug);
57
57
  }
58
58
 
59
59
  // if we build the emulator version of piral (shipped to pilets)
60
60
  if (process.env.DEBUG_PILET) {
61
- const { integrate } = require('../debug-pilet');
61
+ const { integrate } = require('./emulator');
62
62
  integrate(context, options);
63
63
  }
64
64
 
@@ -17,28 +17,30 @@ export function createCoreApi(context: GlobalStateContext): PiletApiExtender<Pil
17
17
  const expiration = getDataExpiration(expires);
18
18
  return context.tryWriteDataItem(name, value, pilet, target, expiration);
19
19
  },
20
- registerPage(route, arg, meta) {
20
+ registerPage(route, arg, meta = {}) {
21
+ const component = withApi(context, arg, api, 'page', undefined, { meta });
21
22
  context.registerPage(route, {
22
23
  pilet,
23
24
  meta,
24
- component: withApi(context, arg, api, 'page'),
25
+ component,
25
26
  });
26
27
  return () => api.unregisterPage(route);
27
28
  },
28
29
  unregisterPage(route) {
29
30
  context.unregisterPage(route);
30
31
  },
31
- registerExtension(name, arg, defaults) {
32
- context.registerExtension(name as string, {
32
+ registerExtension(name, reference, defaults) {
33
+ const component = withApi(context, reference, api, 'extension');
34
+ context.registerExtension(name, {
33
35
  pilet,
34
- component: withApi(context, arg, api, 'extension'),
35
- reference: arg,
36
+ component,
37
+ reference,
36
38
  defaults,
37
39
  });
38
- return () => api.unregisterExtension(name, arg);
40
+ return () => api.unregisterExtension(name, reference);
39
41
  },
40
42
  unregisterExtension(name, arg) {
41
- context.unregisterExtension(name as string, arg);
43
+ context.unregisterExtension(name, arg);
42
44
  },
43
45
  renderHtmlExtension(element, props) {
44
46
  const [dispose] = renderElement(context, element, props);
@@ -1,5 +1,6 @@
1
- import { PiletMetadata, AvailableDependencies, isfunc } from 'piral-base';
1
+ import { isfunc } from 'piral-base';
2
2
  import { __assign } from 'tslib';
3
+ import type { AvailableDependencies, PiletEntries } from '../types';
3
4
 
4
5
  /**
5
6
  * The global dependencies, which represent the dependencies
@@ -49,6 +50,6 @@ return fetch('https://feed.piral.cloud/api/v1/pilet/sample')
49
50
  .then(res => res.items);
50
51
  ```
51
52
  */
52
- export function defaultModuleRequester(): Promise<Array<PiletMetadata>> {
53
+ export function defaultModuleRequester(): Promise<PiletEntries> {
53
54
  return Promise.resolve([]);
54
55
  }
@@ -58,7 +58,11 @@ StubComponent.displayName = 'StubComponent';
58
58
 
59
59
  describe('withApi Module', () => {
60
60
  it('wraps a component and forwards the API as piral', () => {
61
- const api: any = {};
61
+ const api: any = {
62
+ meta: {
63
+ name: 'foo',
64
+ },
65
+ };
62
66
  const { context } = createMockContainer();
63
67
  const Component = withApi(context, StubComponent, api, 'feed' as any);
64
68
  const node = mount(<Component />);
@@ -67,7 +71,11 @@ describe('withApi Module', () => {
67
71
 
68
72
  it('is protected against a component crash', () => {
69
73
  console.error = jest.fn();
70
- const api: any = {};
74
+ const api: any = {
75
+ meta: {
76
+ name: 'foo',
77
+ },
78
+ };
71
79
  const { context } = createMockContainer();
72
80
  const Component = withApi(context, StubComponent, api, 'feed' as any);
73
81
  const node = mount(<Component shouldCrash />);
@@ -88,7 +96,11 @@ describe('withApi Module', () => {
88
96
  });
89
97
 
90
98
  it('Wraps component of type object', () => {
91
- const api: any = {};
99
+ const api: any = {
100
+ meta: {
101
+ name: 'foo',
102
+ },
103
+ };
92
104
  const { context } = createMockContainer();
93
105
  context.converters = {
94
106
  html: (component) => {
@@ -107,7 +119,11 @@ describe('withApi Module', () => {
107
119
  });
108
120
 
109
121
  it('Wraps component which is object == null.', () => {
110
- const api: any = {};
122
+ const api: any = {
123
+ meta: {
124
+ name: 'foo',
125
+ },
126
+ };
111
127
  const { context } = createMockContainer();
112
128
  context.converters = {
113
129
  html: (component) => {
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { isfunc } from 'piral-base';
3
3
  import { __RouterContext } from 'react-router';
4
- import { PiralError, PiralLoadingIndicator, ErrorBoundary, ErrorBoundaryOptions, PortalRenderer } from '../components';
4
+ import { ErrorBoundary, PortalRenderer } from '../components';
5
5
  import { useGlobalStateContext } from '../hooks';
6
6
  import { defaultRender, convertComponent, none } from '../utils';
7
7
  import {
@@ -20,6 +20,10 @@ let portalIdBase = 123456;
20
20
 
21
21
  const DefaultWrapper: React.FC = (props) => defaultRender(props.children);
22
22
 
23
+ interface CapturedProps {
24
+ piral: PiletApi;
25
+ }
26
+
23
27
  interface ForeignComponentContainerProps<T> {
24
28
  $portalId: string;
25
29
  $component: ForeignComponent<T>;
@@ -87,40 +91,34 @@ class ForeignComponentContainer<T> extends React.Component<ForeignComponentConta
87
91
 
88
92
  function wrapReactComponent<T>(
89
93
  Component: React.ComponentType<T & BaseComponentProps>,
90
- stasisOptions: ErrorBoundaryOptions<T>,
91
- piral: PiletApi,
92
- Wrapper: React.ComponentType<any>,
94
+ captured: CapturedProps,
95
+ Wrapper: React.FC<T>,
93
96
  ): React.ComponentType<T> {
94
97
  return (props: T) => (
95
- <Wrapper {...props} piral={piral}>
96
- <ErrorBoundary {...stasisOptions} renderProps={props}>
97
- <Component {...props} piral={piral} />
98
- </ErrorBoundary>
98
+ <Wrapper {...props}>
99
+ <Component {...props} {...captured} />
99
100
  </Wrapper>
100
101
  );
101
102
  }
102
103
 
103
104
  function wrapForeignComponent<T>(
104
105
  component: ForeignComponent<T & BaseComponentProps>,
105
- stasisOptions: ErrorBoundaryOptions<T>,
106
- piral: PiletApi,
107
- Wrapper: React.ComponentType<any>,
106
+ captured: CapturedProps,
107
+ Wrapper: React.FC<T>,
108
108
  ) {
109
109
  return React.memo((props: T) => {
110
110
  const { state, readState, destroyPortal } = useGlobalStateContext();
111
111
  const router = React.useContext(__RouterContext);
112
112
  const id = React.useMemo(() => (portalIdBase++).toString(26), none);
113
113
  const context = React.useMemo(() => ({ router, state, readState }), [router, state]);
114
- const innerProps = React.useMemo(() => ({ ...props, piral }), [props]);
114
+ const innerProps = React.useMemo(() => ({ ...props, ...captured }), [props]);
115
115
 
116
116
  React.useEffect(() => () => destroyPortal(id), none);
117
117
 
118
118
  return (
119
- <Wrapper {...innerProps}>
120
- <ErrorBoundary {...stasisOptions} renderProps={props}>
121
- <PortalRenderer id={id} />
122
- <ForeignComponentContainer innerProps={innerProps} $portalId={id} $component={component} $context={context} />
123
- </ErrorBoundary>
119
+ <Wrapper {...props}>
120
+ <PortalRenderer id={id} />
121
+ <ForeignComponentContainer innerProps={innerProps} $portalId={id} $component={component} $context={context} />
124
122
  </Wrapper>
125
123
  );
126
124
  });
@@ -133,26 +131,30 @@ function isNotExotic(component: any): component is object {
133
131
  function wrapComponent<T>(
134
132
  converters: ComponentConverters<T & BaseComponentProps>,
135
133
  component: AnyComponent<T & BaseComponentProps>,
136
- piral: PiletApi,
137
- Wrapper: React.ComponentType<any>,
138
- stasisOptions: ErrorBoundaryOptions<T>,
134
+ captured: CapturedProps,
135
+ Wrapper: React.FC<T>,
139
136
  ) {
140
- if (!component) {
141
- console.error('The given value is not a valid component.');
142
- // tslint:disable-next-line:no-null-keyword
143
- component = () => null;
144
- }
145
-
146
137
  if (typeof component === 'object' && isNotExotic(component)) {
147
138
  const result = convertComponent(converters[component.type], component);
148
- return wrapForeignComponent<T>(result, stasisOptions, piral, Wrapper);
139
+ return wrapForeignComponent<T>(result, captured, Wrapper);
149
140
  }
150
141
 
151
- return wrapReactComponent<T>(component, stasisOptions, piral, Wrapper);
142
+ return wrapReactComponent<T>(component, captured, Wrapper);
152
143
  }
153
144
 
154
145
  function getWrapper(wrappers: Record<string, React.ComponentType<any>>, wrapperType: string) {
155
- return wrappers[wrapperType] || wrappers['*'] || DefaultWrapper;
146
+ const WrapAll = wrappers['*'];
147
+ const WrapType = wrappers[wrapperType];
148
+
149
+ if (WrapAll && WrapType) {
150
+ return (props) => (
151
+ <WrapAll {...props}>
152
+ <WrapType {...props} />
153
+ </WrapAll>
154
+ );
155
+ }
156
+
157
+ return WrapType || WrapAll || DefaultWrapper;
156
158
  }
157
159
 
158
160
  export function withApi<TProps>(
@@ -161,19 +163,26 @@ export function withApi<TProps>(
161
163
  piral: PiletApi,
162
164
  errorType: keyof Errors,
163
165
  wrapperType: string = errorType,
166
+ captured = {},
164
167
  ) {
168
+ const outerProps = { ...captured, piral };
165
169
  const converters = context.converters;
166
- const Wrapper = context.readState((m) => getWrapper(m.registry.wrappers, wrapperType));
167
-
168
- return wrapComponent<TProps>(converters, component, piral, Wrapper, {
169
- onError(error) {
170
- console.error(piral, error);
171
- },
172
- renderChild(child) {
173
- return <React.Suspense fallback={<PiralLoadingIndicator />}>{child}</React.Suspense>;
174
- },
175
- renderError(error, props: any) {
176
- return <PiralError type={errorType} error={error} {...props} />;
177
- },
178
- });
170
+ const OuterWrapper = context.readState((m) => getWrapper(m.registry.wrappers, wrapperType));
171
+
172
+ const Wrapper: React.FC<TProps> = (props) => (
173
+ <OuterWrapper {...outerProps} {...props}>
174
+ <ErrorBoundary {...outerProps} {...props} errorType={errorType}>
175
+ {props.children}
176
+ </ErrorBoundary>
177
+ </OuterWrapper>
178
+ );
179
+
180
+ if (!component) {
181
+ const pilet = piral.meta.name;
182
+ console.error(`[${pilet}] The given value is not a valid component.`);
183
+ // tslint:disable-next-line:no-null-keyword
184
+ component = () => null;
185
+ }
186
+
187
+ return wrapComponent<TProps>(converters, component, outerProps, Wrapper);
179
188
  }