@pushwoosh/single-spa 1.4.0 → 1.5.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.
@@ -0,0 +1,18 @@
1
+ import { Component, type ErrorInfo, type ReactNode } from 'react';
2
+ export declare const ErrorFallback: () => import("react").JSX.Element;
3
+ type ParcelErrorBoundaryProps = {
4
+ module: string;
5
+ onError?: (error: Error) => void;
6
+ children: ReactNode;
7
+ };
8
+ export declare class ParcelErrorBoundary extends Component<ParcelErrorBoundaryProps, {
9
+ hasError: boolean;
10
+ }> {
11
+ constructor(props: ParcelErrorBoundaryProps);
12
+ static getDerivedStateFromError(): {
13
+ hasError: boolean;
14
+ };
15
+ componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
16
+ render(): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react").JSX.Element | null | undefined;
17
+ }
18
+ export {};
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Component } from 'react';
3
+ export const ErrorFallback = () => _jsxs("div", {
4
+ style: {
5
+ display: 'flex',
6
+ flexDirection: 'column',
7
+ alignItems: 'center',
8
+ justifyContent: 'center',
9
+ maxWidth: 520,
10
+ margin: '64px auto',
11
+ padding: 24,
12
+ textAlign: 'center'
13
+ },
14
+ children: [_jsx("h2", {
15
+ style: {
16
+ margin: '0 0 8px',
17
+ fontSize: 20,
18
+ fontWeight: 500
19
+ },
20
+ children: "Something went wrong"
21
+ }), _jsx("p", {
22
+ style: {
23
+ margin: 0,
24
+ color: '#7C8595'
25
+ },
26
+ children: "Please refresh the page or try again later."
27
+ })]
28
+ });
29
+ export class ParcelErrorBoundary extends Component {
30
+ constructor(props) {
31
+ super(props);
32
+ this.state = {
33
+ hasError: false
34
+ };
35
+ }
36
+ static getDerivedStateFromError() {
37
+ return {
38
+ hasError: true
39
+ };
40
+ }
41
+ componentDidCatch(error, errorInfo) {
42
+ console.error(`Parcel ${this.props.module} caught an error`, error, errorInfo);
43
+ this.props.onError?.(error);
44
+ }
45
+ render() {
46
+ return this.state.hasError ? _jsx(ErrorFallback, {}) : this.props.children;
47
+ }
48
+ }
package/Parcel.d.ts CHANGED
@@ -3,6 +3,7 @@ type ParcelProps = {
3
3
  className?: string;
4
4
  module: string;
5
5
  parcelFunctionName?: string;
6
+ inline?: boolean;
6
7
  [key: string]: any;
7
8
  };
8
9
  export declare const Parcel: FC<ParcelProps>;
package/Parcel.js CHANGED
@@ -1,15 +1,22 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useMemo } from 'react';
2
+ import { useEffect, useMemo, useState } from 'react';
3
3
  import { mountRootParcel } from 'single-spa';
4
4
  import SingleSpaParcel from 'single-spa-react/parcel';
5
5
  import { useSPAContext } from '@pushwoosh/spa-context';
6
+ import { ParcelErrorBoundary } from './ErrorBoundary';
7
+ import { isInlineParcelsEnabled } from './inlineParcels';
6
8
  import { loadModule } from './loadModule';
7
- export const Parcel = ({
8
- className,
9
+ const parcelKey = ({
9
10
  module,
10
- parcelFunctionName = 'parcel',
11
- ...transferProps
12
- }) => {
11
+ parcelFunctionName
12
+ }) => `${module}-${parcelFunctionName}`;
13
+ const MountedParcel = props => {
14
+ const {
15
+ className,
16
+ module,
17
+ parcelFunctionName,
18
+ ...transferProps
19
+ } = props;
13
20
  const spaContext = useSPAContext();
14
21
  const parcelConfig = useMemo(() => {
15
22
  let parcel;
@@ -40,5 +47,73 @@ export const Parcel = ({
40
47
  config: parcelConfig,
41
48
  wrapClassName: className,
42
49
  mountParcel: mountRootParcel
43
- }, `${module}-${parcelFunctionName}`);
50
+ }, parcelKey(props));
51
+ };
52
+ const InlineParcel = props => {
53
+ const {
54
+ className,
55
+ module,
56
+ parcelFunctionName,
57
+ wrapWith: Wrapper = 'div',
58
+ wrapStyle,
59
+ handleError,
60
+ parcelDidMount,
61
+ ...transferProps
62
+ } = props;
63
+ const spaContext = useSPAContext();
64
+ const key = parcelKey(props);
65
+ const [loaded, setLoaded] = useState(null);
66
+ const Component = loaded?.key === key ? loaded.Component : undefined;
67
+ useEffect(() => {
68
+ let cancelled = false;
69
+ loadModule(module).then(imported => {
70
+ if (!cancelled) setLoaded({
71
+ key,
72
+ Component: imported[parcelFunctionName]?.Component
73
+ });
74
+ }, () => {
75
+ if (!cancelled) setLoaded({
76
+ key
77
+ });
78
+ });
79
+ return () => {
80
+ cancelled = true;
81
+ };
82
+ }, [key, module, parcelFunctionName]);
83
+ useEffect(() => {
84
+ if (Component) parcelDidMount?.();
85
+ }, [Component]); // eslint-disable-line react-hooks/exhaustive-deps
86
+ if (loaded?.key === key && !Component) {
87
+ return _jsx(MountedParcel, {
88
+ ...props
89
+ });
90
+ }
91
+ return _jsx(Wrapper, {
92
+ style: wrapStyle,
93
+ className: className,
94
+ children: Component && _jsx(ParcelErrorBoundary, {
95
+ module: module,
96
+ onError: handleError,
97
+ children: _jsx(Component, {
98
+ ...transferProps,
99
+ spaContext: spaContext
100
+ })
101
+ })
102
+ }, key);
103
+ };
104
+ export const Parcel = ({
105
+ inline,
106
+ parcelFunctionName = 'parcel',
107
+ ...rest
108
+ }) => {
109
+ const [inlineByDefault] = useState(isInlineParcelsEnabled);
110
+ const props = {
111
+ ...rest,
112
+ parcelFunctionName
113
+ };
114
+ return (inline ?? inlineByDefault) && !props.appendTo ? _jsx(InlineParcel, {
115
+ ...props
116
+ }) : _jsx(MountedParcel, {
117
+ ...props
118
+ });
44
119
  };
@@ -1,8 +1,9 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import React, { Component } from 'react';
3
3
  import ReactDOMClient from 'react-dom/client';
4
4
  import singleSpaReact from 'single-spa-react';
5
5
  import { SPAContextProvider } from '@pushwoosh/spa-context';
6
+ import { ErrorFallback } from './ErrorBoundary';
6
7
  export function createApplicationFunction(Comp) {
7
8
  class AppWrapper extends Component {
8
9
  constructor(props) {
@@ -21,32 +22,7 @@ export function createApplicationFunction(Comp) {
21
22
  }
22
23
  render() {
23
24
  if (this.state.hasError) {
24
- return _jsxs("div", {
25
- style: {
26
- display: 'flex',
27
- flexDirection: 'column',
28
- alignItems: 'center',
29
- justifyContent: 'center',
30
- maxWidth: 520,
31
- margin: '64px auto',
32
- padding: 24,
33
- textAlign: 'center'
34
- },
35
- children: [_jsx("h2", {
36
- style: {
37
- margin: '0 0 8px',
38
- fontSize: 20,
39
- fontWeight: 500
40
- },
41
- children: "Something went wrong"
42
- }), _jsx("p", {
43
- style: {
44
- margin: 0,
45
- color: '#7C8595'
46
- },
47
- children: "Please refresh the page or try again later."
48
- })]
49
- });
25
+ return _jsx(ErrorFallback, {});
50
26
  }
51
27
  return _jsx(SPAContextProvider, {
52
28
  value: this.props.spaContext,
@@ -4,7 +4,10 @@ type ParcelResult<T> = {
4
4
  render: (props: T) => void;
5
5
  unmount: () => void;
6
6
  };
7
+ export type ParcelFunction<T> = ((container: HTMLElement) => ParcelResult<T>) & {
8
+ Component: ComponentType<T>;
9
+ };
7
10
  export declare function createParcelFunction<T extends {
8
11
  spaContext: SPAContextType;
9
- }>(Comp: ComponentType<T>): (container: HTMLElement) => ParcelResult<T>;
12
+ }>(Comp: ComponentType<T>): ParcelFunction<T>;
10
13
  export {};
@@ -2,7 +2,13 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { createRoot } from 'react-dom/client';
3
3
  import { SPAContextProvider } from '@pushwoosh/spa-context';
4
4
  export function createParcelFunction(Comp) {
5
- return container => {
5
+ const Component = props => _jsx(SPAContextProvider, {
6
+ value: props.spaContext,
7
+ children: _jsx(Comp, {
8
+ ...props
9
+ })
10
+ });
11
+ const parcel = container => {
6
12
  const root = createRoot(container);
7
13
  return {
8
14
  render(props) {
@@ -18,4 +24,7 @@ export function createParcelFunction(Comp) {
18
24
  }
19
25
  };
20
26
  };
27
+ return Object.assign(parcel, {
28
+ Component
29
+ });
21
30
  }
package/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './Parcel';
2
- export { createParcelFunction } from './createParcelFunction';
2
+ export { createParcelFunction, type ParcelFunction } from './createParcelFunction';
3
3
  export { type ApplicationFunctionResult, createApplicationFunction } from './createApplicationFunction';
4
+ export { INLINE_PARCELS_STORAGE_KEY } from './inlineParcels';
4
5
  export { loadModule } from './loadModule';
package/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './Parcel';
2
2
  export { createParcelFunction } from './createParcelFunction';
3
3
  export { createApplicationFunction } from './createApplicationFunction';
4
+ export { INLINE_PARCELS_STORAGE_KEY } from './inlineParcels';
4
5
  export { loadModule } from './loadModule';
@@ -0,0 +1,2 @@
1
+ export declare const INLINE_PARCELS_STORAGE_KEY = "single-spa:inline-parcels";
2
+ export declare function isInlineParcelsEnabled(): boolean;
@@ -0,0 +1,8 @@
1
+ export const INLINE_PARCELS_STORAGE_KEY = 'single-spa:inline-parcels';
2
+ export function isInlineParcelsEnabled() {
3
+ try {
4
+ return globalThis.localStorage?.getItem(INLINE_PARCELS_STORAGE_KEY) === 'true';
5
+ } catch {
6
+ return false;
7
+ }
8
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushwoosh/single-spa",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "single-spa lifecycle helpers for Pushwoosh micro-frontends: application and parcel factories wrapped in SPAContextProvider",
5
5
  "repository": {
6
6
  "type": "git",