@pushwoosh/single-spa 1.3.2 → 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.
- package/ErrorBoundary.d.ts +18 -0
- package/ErrorBoundary.js +48 -0
- package/Parcel.d.ts +1 -0
- package/Parcel.js +82 -7
- package/createApplicationFunction.d.ts +5 -1
- package/createApplicationFunction.js +8 -28
- package/createParcelFunction.d.ts +4 -1
- package/createParcelFunction.js +10 -1
- package/index.d.ts +3 -2
- package/index.js +1 -0
- package/inlineParcels.d.ts +2 -0
- package/inlineParcels.js +8 -0
- package/package.json +2 -1
|
@@ -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 {};
|
package/ErrorBoundary.js
ADDED
|
@@ -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
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
|
-
|
|
8
|
-
className,
|
|
9
|
+
const parcelKey = ({
|
|
9
10
|
module,
|
|
10
|
-
parcelFunctionName
|
|
11
|
-
|
|
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
|
-
},
|
|
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,6 +1,10 @@
|
|
|
1
1
|
import { type ComponentType } from 'react';
|
|
2
|
+
import type { AppProps } from 'single-spa';
|
|
2
3
|
import { type ReactAppOrParcel } from 'single-spa-react';
|
|
3
4
|
import { type SPAContextType } from '@pushwoosh/spa-context';
|
|
5
|
+
export type ApplicationFunctionResult<T> = ReactAppOrParcel<T> & {
|
|
6
|
+
Application: ComponentType<T & AppProps>;
|
|
7
|
+
};
|
|
4
8
|
export declare function createApplicationFunction<T extends {
|
|
5
9
|
spaContext: SPAContextType;
|
|
6
|
-
}>(Comp: ComponentType<T>):
|
|
10
|
+
}>(Comp: ComponentType<T>): ApplicationFunctionResult<T>;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
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
|
|
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,
|
|
@@ -56,9 +32,13 @@ export function createApplicationFunction(Comp) {
|
|
|
56
32
|
});
|
|
57
33
|
}
|
|
58
34
|
}
|
|
59
|
-
|
|
35
|
+
const lifecycles = singleSpaReact({
|
|
60
36
|
React,
|
|
61
37
|
ReactDOMClient,
|
|
62
38
|
rootComponent: AppWrapper
|
|
63
39
|
});
|
|
40
|
+
return {
|
|
41
|
+
...lifecycles,
|
|
42
|
+
Application: AppWrapper
|
|
43
|
+
};
|
|
64
44
|
}
|
|
@@ -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>):
|
|
12
|
+
}>(Comp: ComponentType<T>): ParcelFunction<T>;
|
|
10
13
|
export {};
|
package/createParcelFunction.js
CHANGED
|
@@ -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
|
-
|
|
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';
|
|
3
|
-
export { createApplicationFunction } from './createApplicationFunction';
|
|
2
|
+
export { createParcelFunction, type ParcelFunction } from './createParcelFunction';
|
|
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';
|
package/inlineParcels.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pushwoosh/single-spa",
|
|
3
|
-
"version": "1.
|
|
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",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"@pushwoosh/spa-context": "^3.4.1",
|
|
33
33
|
"@types/react": "^19.3.0",
|
|
34
34
|
"@types/react-dom": "^19.3.0",
|
|
35
|
+
"jsdom": "^26.1.0",
|
|
35
36
|
"react": "^19.3.0",
|
|
36
37
|
"react-dom": "^19.3.0",
|
|
37
38
|
"vitest": "^4.1.10"
|