@ebay/muse-lib-react 2.0.0 → 2.0.1
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/MUSE_README.md +252 -503
- package/build/MUSE_README.md +252 -503
- package/build/dev/info.json +4 -4
- package/build/dev/lib-manifest.json +831 -827
- package/build/dev/main.js +115 -92
- package/build/dev/main.js.map +1 -1
- package/build/dist/info.json +4 -4
- package/build/dist/lib-manifest.json +716 -712
- package/build/dist/main.js +54 -31
- package/build/dist/main.js.map +1 -1
- package/build/test/info.json +4 -4
- package/build/test/lib-manifest.json +999 -995
- package/build/test/main.js +54 -31
- package/build/test/main.js.map +1 -1
- package/package.json +3 -3
- package/src/common/configStore.js +9 -8
package/build/MUSE_README.md
CHANGED
|
@@ -1,621 +1,370 @@
|
|
|
1
1
|
# Plugin Integration Guide: @ebay/muse-lib-react
|
|
2
2
|
|
|
3
|
-
**Generated**: 2026-
|
|
3
|
+
**Generated**: 2026-05-21
|
|
4
4
|
**Plugin Type**: lib
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
## 1. Plugin Purpose & Overview
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
### What This Plugin Does
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
`@ebay/muse-lib-react` is the foundational React lib plugin for the MUSE micro-frontends framework. It bootstraps the React application (creates the React DOM root and renders the app), assembles the provider stack (Redux, React Query, React Router, NiceModal, SubApp context), builds the route tree from contributions across all plugins, and combines Redux reducers from all loaded plugins into a single store.
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
- `react-loadable` - Code splitting and lazy loading
|
|
16
|
-
- `lodash` - Utility functions
|
|
17
|
-
- `react-use` - Collection of React hooks
|
|
18
|
-
- `react-router-dom` v6 - Client-side routing
|
|
19
|
-
- `@tanstack/react-query` v4 - Server state management
|
|
14
|
+
### Key Features
|
|
20
15
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
- **App bootstrapping** — Creates and renders the React root into `#muse-react-root` on demand, invokes lifecycle hooks before and after render.
|
|
17
|
+
- **Provider stack** — Builds the ordered React provider chain (React Query → Redux → SubApp Context → NiceModal → Router) and makes it extensible via extension points.
|
|
18
|
+
- **Route assembly** — Collects route definitions from all loaded plugins, normalizes absolute/parent routes, resolves the homepage component, and renders with React Router v7.
|
|
19
|
+
- **Redux store** — Combines built-in reducers with plugin-contributed reducers (both plugin-scoped and root-level).
|
|
20
|
+
- **Shared module exports** — Bundles React ecosystem libraries (React Router, React Query, lodash, react-use, react-loadable) so all normal plugins can use them without duplicating them in their own bundles.
|
|
21
|
+
- **Utility primitives** — Exports `extendArray`, `useExtPoint`, and `Nodes` to help other plugins build their own extensible arrays and UI node lists.
|
|
27
22
|
|
|
28
|
-
###
|
|
23
|
+
### Plugin Type: lib
|
|
29
24
|
|
|
30
|
-
|
|
31
|
-
- **Flexible Routing**: Supports Browser, Hash, and Memory routers with React Router v6, backwards-compatible with v3-style route configuration
|
|
32
|
-
- **Redux Integration**: Pre-configured Redux store with Thunk middleware, Redux Logger (dev), and DevTools support
|
|
33
|
-
- **Sub-App Support**: Built-in iframe-based sub-application system for embedding external MUSE apps
|
|
34
|
-
- **Lifecycle Hooks**: Extension points for initialization (`beforeRender`, `afterRender`, `onReady`)
|
|
25
|
+
As a `lib` plugin with `muse.isAppEntry: true`, this plugin loads before all normal plugins and serves two roles: it **provides shared runtime modules** (React, Redux, React Router, etc.) to every normal plugin via the MUSE build system's module federation, and it **acts as the application entry point** that renders the React tree. Normal plugins must never bundle these libraries themselves; they receive them from this plugin at runtime.
|
|
35
26
|
|
|
36
27
|
---
|
|
37
28
|
|
|
38
29
|
## 2. Extension Points Exposed
|
|
39
30
|
|
|
40
|
-
This plugin exposes extension points that
|
|
31
|
+
This plugin exposes the following extension points that other plugins can implement to extend its functionality.
|
|
32
|
+
|
|
33
|
+
### Summary
|
|
41
34
|
|
|
42
|
-
|
|
35
|
+
- **Total Extension Points**: 15
|
|
36
|
+
- **Categories**: Root Lifecycle, Provider Stack, Routing, Redux, App Shell, Utility
|
|
43
37
|
|
|
44
|
-
Extension
|
|
38
|
+
### Extension Point List
|
|
45
39
|
|
|
46
40
|
#### `root.beforeRender`
|
|
47
|
-
|
|
48
|
-
- **Purpose**: Execute logic before the React root
|
|
49
|
-
- **
|
|
50
|
-
- **
|
|
51
|
-
- **
|
|
41
|
+
|
|
42
|
+
- **Purpose**: Execute initialization logic before the React root is created and rendered. Use this for side effects that must complete before any React component mounts.
|
|
43
|
+
- **When Invoked**: Synchronously before `createRoot().render()` is called.
|
|
44
|
+
- **Context Parameters**: none
|
|
45
|
+
- **Expected Return**: ignored
|
|
46
|
+
- **Use Case Example**: Injecting global CSS variables, initializing an analytics library, or pre-fetching critical configuration.
|
|
47
|
+
- **File Reference**: `src/index.jsx:26`
|
|
52
48
|
|
|
53
49
|
#### `root.afterRender`
|
|
54
|
-
|
|
55
|
-
- **Purpose**: Execute logic immediately after
|
|
56
|
-
- **
|
|
57
|
-
- **
|
|
58
|
-
- **
|
|
50
|
+
|
|
51
|
+
- **Purpose**: Execute logic immediately after `root.render()` is called (before React has painted).
|
|
52
|
+
- **When Invoked**: Synchronously after `root.render(<Root />)`.
|
|
53
|
+
- **Context Parameters**: none
|
|
54
|
+
- **Expected Return**: ignored
|
|
55
|
+
- **Use Case Example**: Starting a performance measurement timer or notifying a loading screen manager.
|
|
56
|
+
- **File Reference**: `src/index.jsx:31`
|
|
59
57
|
|
|
60
58
|
#### `onReady`
|
|
61
|
-
- **Type**: `Function`
|
|
62
|
-
- **Purpose**: Execute logic after the application is fully mounted to the DOM
|
|
63
|
-
- **Use Case**: Final initialization tasks that require the full component tree to be ready
|
|
64
|
-
- **Invocation**: Called in `src/index.js:33` as the last step of `renderApp()`
|
|
65
|
-
- **Context**: Complete application ready, all components mounted
|
|
66
59
|
|
|
67
|
-
|
|
60
|
+
- **Purpose**: Execute logic after the application is fully mounted to the DOM.
|
|
61
|
+
- **When Invoked**: After `root.afterRender`, still synchronous on the same tick.
|
|
62
|
+
- **Context Parameters**: none
|
|
63
|
+
- **Expected Return**: ignored
|
|
64
|
+
- **Use Case Example**: Hiding a static HTML splash screen, signalling to an E2E test harness that the app is ready.
|
|
65
|
+
- **File Reference**: `src/index.jsx:33`
|
|
68
66
|
|
|
69
|
-
|
|
67
|
+
#### `root.renderChildren`
|
|
70
68
|
|
|
71
|
-
|
|
72
|
-
- **
|
|
73
|
-
- **
|
|
74
|
-
- **
|
|
75
|
-
- **
|
|
76
|
-
- **
|
|
77
|
-
- `path`: URL path (string or array of strings)
|
|
78
|
-
- `component`: React component to render
|
|
79
|
-
- `childRoutes`: Nested routes
|
|
80
|
-
- `parent`: ID of parent route (moves route to parent's `childRoutes`)
|
|
81
|
-
- `isIndex`: Mark as index route (duplicated to path='')
|
|
82
|
-
- `id`: Unique identifier for route (enables `parent` references)
|
|
83
|
-
- **Special Behavior**:
|
|
84
|
-
- Routes with `path` starting with `/` are moved to top level
|
|
85
|
-
- Routes with `parent` property are relocated to parent's `childRoutes`
|
|
86
|
-
- Array paths are expanded to multiple route objects
|
|
87
|
-
- **File Reference**: `src/common/routeConfig.js:32-132`
|
|
69
|
+
- **Purpose**: Wrap the rendered route tree in additional React components. Each implementer receives the current children and must return the (potentially wrapped) children.
|
|
70
|
+
- **When Invoked**: During `Root` render, before the provider chain is built.
|
|
71
|
+
- **Context Parameters**: `children` — `React.ReactNode` — the current route element tree
|
|
72
|
+
- **Expected Return**: `React.ReactNode` — the (wrapped or unchanged) children
|
|
73
|
+
- **Use Case Example**: Wrapping the entire app in an error boundary, a theme context, or a drag-and-drop context.
|
|
74
|
+
- **File Reference**: `src/Root.jsx:104`
|
|
88
75
|
|
|
89
76
|
#### `routerProps`
|
|
90
|
-
- **Type**: `Record<string, any>`
|
|
91
|
-
- **Purpose**: Merge additional props into the React Router `RouterProvider` component
|
|
92
|
-
- **Use Case**: Customize router behavior (e.g., future flags, error handlers)
|
|
93
|
-
- **Collection**: First contribution used via `plugin.invoke('!routerProps')[0]` in `src/Root.js:140`
|
|
94
|
-
- **Note**: Props are merged with defaults (`basename`, `router`, `navigator`)
|
|
95
|
-
- **File Reference**: `src/Root.js:140-177`
|
|
96
77
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
78
|
+
- **Purpose**: Merge additional props directly into the `RouterProvider` component.
|
|
79
|
+
- **When Invoked**: During `Root` render, when the Router is configured.
|
|
80
|
+
- **Context Parameters**: none
|
|
81
|
+
- **Expected Return**: `Record<string, any>` — props merged into `RouterProvider`. Only the first contribution is used.
|
|
82
|
+
- **Use Case Example**: Providing a custom `future` flag object for React Router v7 feature flags.
|
|
83
|
+
- **File Reference**: `src/Root.jsx:140`
|
|
100
84
|
|
|
101
85
|
#### `root.preProcessProviders`
|
|
102
|
-
|
|
103
|
-
- **Purpose**:
|
|
104
|
-
- **
|
|
105
|
-
- **
|
|
106
|
-
- **
|
|
107
|
-
- **
|
|
86
|
+
|
|
87
|
+
- **Purpose**: Inspect or mutate the initial providers array before additional providers from `root.getProviders` are added. Use this to remove or reorder built-in providers.
|
|
88
|
+
- **When Invoked**: During `Root` render, via `extendArray` before provider collection.
|
|
89
|
+
- **Context Parameters**: `{ providers: ProviderType[] }` — the current mutable array of built-in providers
|
|
90
|
+
- **Expected Return**: `string | boolean` — return value is used by `jsPlugin.sort` ordering
|
|
91
|
+
- **Use Case Example**: Removing the built-in Redux `Provider` to replace it with a custom store setup.
|
|
92
|
+
- **File Reference**: `src/Root.jsx:181`, `src/utils.js:17`
|
|
108
93
|
|
|
109
94
|
#### `root.getProviders`
|
|
110
|
-
|
|
111
|
-
- **Purpose**: Contribute additional providers to
|
|
112
|
-
- **
|
|
113
|
-
- **
|
|
114
|
-
- **Return**:
|
|
115
|
-
- **
|
|
116
|
-
|
|
117
|
-
- `key`: Unique identifier
|
|
118
|
-
- `provider`: React component (must accept `children` prop)
|
|
119
|
-
- `props`: Props object for provider
|
|
120
|
-
- `renderProvider`: Custom render function `(children) => ReactNode` (alternative to `provider`)
|
|
121
|
-
- **File Reference**: `src/utils.js:18`, `src/Root.js:144-192`
|
|
95
|
+
|
|
96
|
+
- **Purpose**: Contribute one or more additional React context providers to be inserted into the provider stack at a specific order position.
|
|
97
|
+
- **When Invoked**: During `Root` render, via `extendArray` during provider collection.
|
|
98
|
+
- **Context Parameters**: `{ providers: ProviderType[] }` — the current providers array (after pre-processing)
|
|
99
|
+
- **Expected Return**: `ProviderType | ProviderType[] | void` — provider descriptor(s) to add
|
|
100
|
+
- **Use Case Example**: Adding an Apollo Client provider at `order: 25` (between Redux and SubApp context).
|
|
101
|
+
- **File Reference**: `src/Root.jsx:181`, `src/utils.js:18`
|
|
122
102
|
|
|
123
103
|
#### `root.processProviders`
|
|
124
|
-
|
|
125
|
-
- **Purpose**:
|
|
126
|
-
- **
|
|
127
|
-
- **
|
|
128
|
-
- **
|
|
129
|
-
- **
|
|
104
|
+
|
|
105
|
+
- **Purpose**: Post-process the combined providers array after all `getProviders` contributions have been collected.
|
|
106
|
+
- **When Invoked**: During `Root` render, via `extendArray` after collection.
|
|
107
|
+
- **Context Parameters**: `{ providers: ProviderType[] }` — the full combined providers array
|
|
108
|
+
- **Expected Return**: `string | void` — used for sort ordering
|
|
109
|
+
- **Use Case Example**: Validating that required providers are present and throwing a descriptive error if not.
|
|
110
|
+
- **File Reference**: `src/Root.jsx:181`, `src/utils.js:20`
|
|
130
111
|
|
|
131
112
|
#### `root.postProcessProviders`
|
|
132
|
-
- **Type**: `(context: { providers: ProviderType[] }) => void`
|
|
133
|
-
- **Purpose**: Final opportunity to modify providers before rendering
|
|
134
|
-
- **Use Case**: Last-minute adjustments or validations
|
|
135
|
-
- **Invocation Order**: 4th (final) - called by `extendArray()` in `src/Root.js:181`
|
|
136
|
-
- **Note**: After this, providers are sorted by `order` and rendered
|
|
137
|
-
- **File Reference**: `src/utils.js:21`, `src/Root.js:181`
|
|
138
113
|
|
|
139
|
-
|
|
140
|
-
- **
|
|
141
|
-
- **
|
|
142
|
-
- **
|
|
143
|
-
- **
|
|
144
|
-
- **
|
|
145
|
-
- **File Reference**: `src/Root.js:104-111`
|
|
114
|
+
- **Purpose**: Final hook to inspect or modify the providers array after all processing is complete.
|
|
115
|
+
- **When Invoked**: During `Root` render, via `extendArray` as the last step.
|
|
116
|
+
- **Context Parameters**: `{ providers: ProviderType[] }` — the finalized providers array
|
|
117
|
+
- **Expected Return**: `void`
|
|
118
|
+
- **Use Case Example**: Logging the final provider stack in development for debugging.
|
|
119
|
+
- **File Reference**: `src/Root.jsx:181`, `src/utils.js:21`
|
|
146
120
|
|
|
147
|
-
|
|
121
|
+
#### `route`
|
|
148
122
|
|
|
149
|
-
|
|
123
|
+
- **Purpose**: Register one or more route definitions into the application's route tree.
|
|
124
|
+
- **When Invoked**: On every render of the `Root` component, when `routeConfig()` rebuilds the route tree.
|
|
125
|
+
- **Context Parameters**: none
|
|
126
|
+
- **Expected Return**: `MuseRoute | MuseRoute[]` — one or more route objects. See `MuseRoute` in `src/muse.d.ts:89`.
|
|
127
|
+
- **Use Case Example**: Adding `/settings`, `/reports`, or any feature page routes.
|
|
128
|
+
- **File Reference**: `src/common/routeConfig.jsx:87`
|
|
150
129
|
|
|
151
130
|
#### `home.homepage`
|
|
152
|
-
|
|
153
|
-
- **Purpose**:
|
|
154
|
-
- **
|
|
155
|
-
- **
|
|
156
|
-
- **
|
|
157
|
-
- **
|
|
158
|
-
- **File Reference**: `src/common/routeConfig.
|
|
131
|
+
|
|
132
|
+
- **Purpose**: Replace the default homepage component rendered at the root path `/`.
|
|
133
|
+
- **When Invoked**: During route assembly in `routeConfig()`.
|
|
134
|
+
- **Context Parameters**: none
|
|
135
|
+
- **Expected Return**: `ComponentType` — the React component to render at `/`. Only one plugin should contribute this.
|
|
136
|
+
- **Use Case Example**: A dashboard plugin providing a `DashboardHomepage` component.
|
|
137
|
+
- **File Reference**: `src/common/routeConfig.jsx:96`
|
|
159
138
|
|
|
160
139
|
#### `home.mainLayout`
|
|
161
|
-
- **Type**: `ComponentType<{ children: ReactNode }>`
|
|
162
|
-
- **Purpose**: Provide a main layout component that wraps all routes
|
|
163
|
-
- **Use Case**: Define application shell (header, sidebar, footer) that persists across pages
|
|
164
|
-
- **Collection**: Via `plugin.invoke('!home.mainLayout')` in `src/features/home/App.js:10`
|
|
165
|
-
- **Constraint**: Only one layout allowed; multiple contributions will show error message
|
|
166
|
-
- **Note**: When using custom layout, undeploy `@ebay/muse-layout-antd` if present
|
|
167
|
-
- **File Reference**: `src/features/home/App.js:9-22`
|
|
168
140
|
|
|
169
|
-
|
|
170
|
-
- **
|
|
171
|
-
- **
|
|
172
|
-
- **
|
|
173
|
-
- **
|
|
174
|
-
- **
|
|
175
|
-
- **File Reference**: `src/features/home/App.js:6-30`
|
|
141
|
+
- **Purpose**: Provide the main layout shell component that wraps all page content. Only one plugin should contribute this — multiple contributions render an error message.
|
|
142
|
+
- **When Invoked**: On every render of the `App` shell component.
|
|
143
|
+
- **Context Parameters**: none
|
|
144
|
+
- **Expected Return**: `ComponentType` — a layout component that accepts `children`. Only the first contribution is used.
|
|
145
|
+
- **Use Case Example**: `muse-layout-antd` provides a sidebar/header layout via this extension point.
|
|
146
|
+
- **File Reference**: `src/features/home/App.jsx:10`
|
|
176
147
|
|
|
177
|
-
|
|
148
|
+
#### `rootComponent`
|
|
178
149
|
|
|
179
|
-
|
|
150
|
+
- **Purpose**: Mount an invisible initialization React component into the app root. The component should return `null` — it exists only for side effects (subscriptions, global listeners, etc.).
|
|
151
|
+
- **When Invoked**: On every render of the `App` shell component.
|
|
152
|
+
- **Context Parameters**: none
|
|
153
|
+
- **Expected Return**: `ComponentType` — a component that renders `null`. All contributing plugins' components are rendered simultaneously.
|
|
154
|
+
- **Use Case Example**: A plugin that listens for global WebSocket messages and dispatches Redux actions.
|
|
155
|
+
- **File Reference**: `src/features/home/App.jsx:7`
|
|
180
156
|
|
|
181
157
|
#### `reducer`
|
|
182
|
-
- **Type**: `Reducer<any, AnyAction>`
|
|
183
|
-
- **Purpose**: Contribute a plugin-level reducer to the Redux store
|
|
184
|
-
- **Use Case**: Add plugin-specific state management to Redux
|
|
185
|
-
- **Storage Key**: Auto-generated as `camelCase('plugin-' + pluginName)`
|
|
186
|
-
- **Example**: Plugin `@ebay/my-plugin` → state key `pluginEbayMyPlugin`
|
|
187
|
-
- **Collection**: Via `plugin.getPlugins('reducer')` in `src/common/rootReducer.js:24-31`
|
|
188
|
-
- **File Reference**: `src/common/rootReducer.js:23-31`
|
|
189
|
-
|
|
190
|
-
#### `reducers`
|
|
191
|
-
- **Type**: `Record<string, Reducer<any, AnyAction>>`
|
|
192
|
-
- **Purpose**: Contribute root-level reducers with custom state keys
|
|
193
|
-
- **Use Case**: Add multiple reducers or control exact state key names
|
|
194
|
-
- **Example**: `{ myData: myDataReducer, config: configReducer }` → `state.myData`, `state.config`
|
|
195
|
-
- **Collection**: Via `plugin.getPlugins('reducers')` in `src/common/rootReducer.js:33-37`
|
|
196
|
-
- **Note**: Provides more control than `reducer` extension point
|
|
197
|
-
- **File Reference**: `src/common/rootReducer.js:33-39`
|
|
198
|
-
|
|
199
|
-
---
|
|
200
|
-
|
|
201
|
-
## 3. Extension Points Contributed
|
|
202
158
|
|
|
203
|
-
|
|
159
|
+
- **Purpose**: Contribute a plugin-scoped Redux reducer. The reducer is automatically mounted under the key `plugin-<pluginName>` (camelCased) in the store.
|
|
160
|
+
- **When Invoked**: When the Redux store is created (once at startup).
|
|
161
|
+
- **Context Parameters**: none
|
|
162
|
+
- **Expected Return**: `Reducer<any, AnyAction>` — a Redux reducer function
|
|
163
|
+
- **Use Case Example**: A plugin contributing its own Redux slice under `pluginMyPlugin` in the store.
|
|
164
|
+
- **File Reference**: `src/common/rootReducer.js:24`
|
|
204
165
|
|
|
205
|
-
|
|
166
|
+
#### `reducers`
|
|
206
167
|
|
|
207
|
-
|
|
168
|
+
- **Purpose**: Contribute one or more root-level Redux reducers with custom store branch keys (unlike `reducer` which auto-generates the key).
|
|
169
|
+
- **When Invoked**: When the Redux store is created (once at startup).
|
|
170
|
+
- **Context Parameters**: none
|
|
171
|
+
- **Expected Return**: `Record<string, Reducer<any, AnyAction>>` — a map of store key → reducer
|
|
172
|
+
- **Use Case Example**: A plugin that needs its state at `store.auth` or `store.featureFlags` instead of an auto-generated key.
|
|
173
|
+
- **File Reference**: `src/common/rootReducer.js:33`
|
|
208
174
|
|
|
209
|
-
|
|
175
|
+
### Usage Example
|
|
210
176
|
|
|
211
|
-
|
|
177
|
+
```javascript
|
|
178
|
+
// Extension points use nested object properties — NOT string paths
|
|
179
|
+
plugin.register({
|
|
180
|
+
name: 'my-plugin',
|
|
212
181
|
|
|
213
|
-
|
|
182
|
+
// Lifecycle hooks
|
|
183
|
+
onReady: () => {
|
|
184
|
+
console.log('App is ready');
|
|
185
|
+
},
|
|
214
186
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
187
|
+
// Route contribution
|
|
188
|
+
route: [
|
|
189
|
+
{ path: 'settings', component: SettingsPage },
|
|
190
|
+
{ path: 'reports', component: ReportsPage },
|
|
191
|
+
],
|
|
220
192
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
- **Purpose**: Utility functions for arrays, objects, strings, etc.
|
|
224
|
-
- **Use Case**: Common data manipulation without importing lodash separately
|
|
225
|
-
- **File Reference**: `src/index.js:8,41`
|
|
193
|
+
// Redux reducer
|
|
194
|
+
reducer: myPluginReducer,
|
|
226
195
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
-
|
|
196
|
+
// Provider injection
|
|
197
|
+
root: {
|
|
198
|
+
getProviders: ({ providers }) => ({
|
|
199
|
+
order: 25,
|
|
200
|
+
key: 'my-context',
|
|
201
|
+
provider: MyContextProvider,
|
|
202
|
+
props: { value: myContextValue },
|
|
203
|
+
}),
|
|
204
|
+
beforeRender: () => {
|
|
205
|
+
initAnalytics();
|
|
206
|
+
},
|
|
207
|
+
},
|
|
232
208
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
209
|
+
// Layout/homepage
|
|
210
|
+
home: {
|
|
211
|
+
homepage: MyHomepageComponent,
|
|
212
|
+
mainLayout: MyLayoutComponent, // WARNING: only one plugin should provide this
|
|
213
|
+
},
|
|
238
214
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
- **File Reference**: `src/index.js:12,41`
|
|
215
|
+
// Invisible init component
|
|
216
|
+
rootComponent: MyGlobalListenerComponent,
|
|
217
|
+
});
|
|
218
|
+
```
|
|
244
219
|
|
|
245
|
-
|
|
220
|
+
---
|
|
246
221
|
|
|
247
|
-
|
|
222
|
+
## 3. Extension Points Contributed
|
|
248
223
|
|
|
249
|
-
|
|
250
|
-
- **Source**: `src/features/common/Nodes.js`
|
|
251
|
-
- **Type**: `ComponentType<{ items: any[], extName: string, extBase: string, extArgs: any }>`
|
|
252
|
-
- **Purpose**: Render a list of components with extension point integration
|
|
253
|
-
- **Use Case**: Create extendable UI component lists (e.g., toolbar buttons, menu items)
|
|
254
|
-
- **Behavior**: Uses `extendArray()` to allow plugins to contribute additional nodes
|
|
255
|
-
- **Exported**: `src/features/common/index.js:3`
|
|
256
|
-
- **File Reference**: `src/features/common/Nodes.js:1-21`
|
|
257
|
-
|
|
258
|
-
#### `useExtPoint`
|
|
259
|
-
- **Source**: `src/features/common/useExtPoint.js`
|
|
260
|
-
- **Type**: `(extPointName: string, extArgs?: any) => { extNode: ReactNode, values: any[] }`
|
|
261
|
-
- **Purpose**: React hook for consuming extension points within components
|
|
262
|
-
- **Use Case**: Render extension point contributions as React elements and collect callback values
|
|
263
|
-
- **Returns**:
|
|
264
|
-
- `extNode`: React fragment containing all extension point components
|
|
265
|
-
- `values`: Array of values passed to callbacks
|
|
266
|
-
- **Exported**: `src/features/common/index.js:4`
|
|
267
|
-
- **File Reference**: `src/features/common/useExtPoint.js:1-29`
|
|
268
|
-
|
|
269
|
-
#### `ErrorBoundary`
|
|
270
|
-
- **Source**: `src/features/common/ErrorBoundary.js`
|
|
271
|
-
- **Type**: `ComponentType<{ children: ReactNode }>`
|
|
272
|
-
- **Purpose**: React error boundary component
|
|
273
|
-
- **Use Case**: Catch and handle React errors gracefully
|
|
274
|
-
- **Exported**: `src/features/common/index.js:2`
|
|
275
|
-
|
|
276
|
-
#### `PageNotFound`
|
|
277
|
-
- **Source**: `src/features/common/PageNotFound.js`
|
|
278
|
-
- **Type**: `ComponentType<Object>`
|
|
279
|
-
- **Purpose**: Default 404 page component
|
|
280
|
-
- **Use Case**: Fallback for unmatched routes
|
|
281
|
-
- **Exported**: `src/features/common/index.js:1`
|
|
282
|
-
|
|
283
|
-
### 4.3 Sub-App Utilities
|
|
284
|
-
|
|
285
|
-
Components and utilities for the sub-application feature:
|
|
286
|
-
|
|
287
|
-
#### `SubAppContainer`
|
|
288
|
-
- **Source**: `src/features/sub-app/SubAppContainer.js`
|
|
289
|
-
- **Type**: `ComponentType<{ subApp: SubAppConfig, subApps: SubAppConfig[] }>`
|
|
290
|
-
- **Purpose**: Renders an external MUSE app in an iframe
|
|
291
|
-
- **Use Case**: Embed other MUSE applications within the main app
|
|
292
|
-
- **Exported**: `src/features/sub-app/index.js:1`
|
|
293
|
-
|
|
294
|
-
#### `FixedSubAppContainer`
|
|
295
|
-
- **Source**: `src/features/sub-app/FixedSubAppContainer.js`
|
|
296
|
-
- **Type**: `ComponentType<Object>`
|
|
297
|
-
- **Purpose**: Fixed-position sub-app container
|
|
298
|
-
- **Use Case**: Persistent sub-app that doesn't follow routing
|
|
299
|
-
- **Exported**: `src/features/sub-app/index.js:2`
|
|
300
|
-
|
|
301
|
-
#### `SubAppContext`
|
|
302
|
-
- **Source**: `src/features/sub-app/SubAppContext.js`
|
|
303
|
-
- **Type**: `React.Context<any>`
|
|
304
|
-
- **Purpose**: React context for parent-child sub-app communication
|
|
305
|
-
- **Use Case**: Share data between parent app and embedded sub-apps
|
|
306
|
-
- **Exported**: `src/features/sub-app/index.js:4`
|
|
307
|
-
|
|
308
|
-
#### `LoadingSkeleton`
|
|
309
|
-
- **Source**: `src/features/sub-app/LoadingSkeleton.js`
|
|
310
|
-
- **Type**: `ComponentType<Object>`
|
|
311
|
-
- **Purpose**: Loading placeholder for sub-apps
|
|
312
|
-
- **Use Case**: Display loading state while sub-app loads
|
|
313
|
-
- **Exported**: `src/features/sub-app/index.js:3`
|
|
314
|
-
|
|
315
|
-
#### `C2SProxyFailed`
|
|
316
|
-
- **Source**: `src/features/sub-app/C2SProxyFailed.js`
|
|
317
|
-
- **Type**: `ComponentType<Object>`
|
|
318
|
-
- **Purpose**: Error component for sub-app proxy failures
|
|
319
|
-
- **Use Case**: Display when C2S proxy configuration fails
|
|
320
|
-
- **Exported**: `src/features/sub-app/index.js:5`
|
|
321
|
-
|
|
322
|
-
#### `useSetSubAppState`
|
|
323
|
-
- **Source**: `src/features/sub-app/redux/setSubAppState.js`
|
|
324
|
-
- **Type**: `() => (state: any) => void`
|
|
325
|
-
- **Purpose**: Hook for setting sub-app state from Redux
|
|
326
|
-
- **Use Case**: Update sub-app context from parent app
|
|
327
|
-
- **Exported**: `src/features/sub-app/redux/hooks.js:1`
|
|
328
|
-
|
|
329
|
-
### 4.4 Common Utilities
|
|
330
|
-
|
|
331
|
-
#### `extendArray`
|
|
332
|
-
- **Source**: `src/utils.js:15-24`
|
|
333
|
-
- **Type**: `(arr: any[], extName: string, extBase: string, ...args: any[]) => any[]`
|
|
334
|
-
- **Purpose**: Makes an array extensible via js-plugin extension points
|
|
335
|
-
- **Use Case**: Internal utility for provider/route extension, can be used by other plugins for custom extension points
|
|
336
|
-
- **Behavior**: Invokes `preProcess`, `get`, `process`, `postProcess` extension points, flattens contributions, sorts by order
|
|
337
|
-
- **Example**:
|
|
338
|
-
```javascript
|
|
339
|
-
const items = [];
|
|
340
|
-
extendArray(items, 'items', 'myPlugin.customExt', { context: 'data' });
|
|
341
|
-
// Invokes: myPlugin.customExt.preProcessItems
|
|
342
|
-
// myPlugin.customExt.getItems
|
|
343
|
-
// myPlugin.customExt.processItems
|
|
344
|
-
// myPlugin.customExt.postProcessItems
|
|
345
|
-
```
|
|
346
|
-
- **Exported**: `src/utils.js:26`
|
|
347
|
-
- **File Reference**: `src/utils.js:15-24`
|
|
348
|
-
|
|
349
|
-
### 4.5 Redux Store Access
|
|
350
|
-
|
|
351
|
-
#### `store`
|
|
352
|
-
- **Source**: `src/common/store.js`
|
|
353
|
-
- **Type**: Redux Store wrapper
|
|
354
|
-
- **Purpose**: Access to the global Redux store
|
|
355
|
-
- **Use Case**: Dispatch actions or access state outside React components
|
|
356
|
-
- **Methods**:
|
|
357
|
-
- `getStore()`: Returns the Redux store instance
|
|
358
|
-
- `getState()`: Returns current state
|
|
359
|
-
- `dispatch(action)`: Dispatches an action
|
|
360
|
-
- `subscribe(listener)`: Subscribes to store changes
|
|
361
|
-
- `replaceReducer(reducer)`: Hot-swaps reducers
|
|
362
|
-
- **File Reference**: `src/common/store.js:1-21`
|
|
363
|
-
|
|
364
|
-
#### `history`
|
|
365
|
-
- **Source**: `src/common/history.js`
|
|
366
|
-
- **Type**: History object (from `history` package)
|
|
367
|
-
- **Purpose**: Programmatic navigation outside React components
|
|
368
|
-
- **Use Case**: Navigate to routes from Redux actions or non-React code
|
|
369
|
-
- **File Reference**: `src/common/history.js`
|
|
224
|
+
This plugin does not extend other plugins via extension points. It is the foundational bootstrap plugin — it only **exposes** extension points for others to implement.
|
|
370
225
|
|
|
371
226
|
---
|
|
372
227
|
|
|
373
|
-
##
|
|
228
|
+
## 4. Exported Functionality
|
|
374
229
|
|
|
375
|
-
|
|
230
|
+
This plugin exports the following functionality for use by other plugins.
|
|
376
231
|
|
|
377
|
-
|
|
232
|
+
**Access via**: Shared modules are available automatically in normal plugins via the MUSE build system. Direct exports are accessible via `plugin.getPlugin('@ebay/muse-lib-react').exports` or direct source imports.
|
|
378
233
|
|
|
379
|
-
|
|
380
|
-
// ✅ CORRECT - nested object properties
|
|
381
|
-
plugin.register({
|
|
382
|
-
name: 'my-plugin',
|
|
383
|
-
route: {
|
|
384
|
-
path: '/my-page',
|
|
385
|
-
component: MyPageComponent,
|
|
386
|
-
childRoutes: [
|
|
387
|
-
{
|
|
388
|
-
path: 'details/:id',
|
|
389
|
-
component: DetailsComponent
|
|
390
|
-
}
|
|
391
|
-
]
|
|
392
|
-
}
|
|
393
|
-
});
|
|
234
|
+
### Shared Modules (Module Federation)
|
|
394
235
|
|
|
395
|
-
|
|
396
|
-
plugin.register({
|
|
397
|
-
name: 'my-plugin',
|
|
398
|
-
'route': { ... } // Works, but 'route' doesn't need quotes
|
|
399
|
-
});
|
|
400
|
-
```
|
|
236
|
+
As a `lib` plugin, `@ebay/muse-lib-react` makes the following libraries available as shared singletons to all normal plugins at runtime. Normal plugins must **not** bundle these themselves — they receive them from this plugin via the MUSE vite/webpack build plugin.
|
|
401
237
|
|
|
402
|
-
|
|
238
|
+
The shared module set is determined by the transitive imports of `src/index.jsx`. The following are explicitly re-exported:
|
|
403
239
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
]
|
|
412
|
-
});
|
|
413
|
-
```
|
|
240
|
+
| Shared Module | Package | Version | Export Key |
|
|
241
|
+
|---|---|---|---|
|
|
242
|
+
| `Loadable` | `react-loadable` | 5.5.0 | `Loadable` |
|
|
243
|
+
| `_` (lodash) | `lodash` | 4.18.1 | `_` |
|
|
244
|
+
| `reactUse` | `react-use` | 17.6.0 | `reactUse` |
|
|
245
|
+
| `reactRouterDom` | `react-router-dom` | 7.15.0 | `reactRouterDom` |
|
|
246
|
+
| `reactQuery` | `@tanstack/react-query` | 5.100.9 | `reactQuery` |
|
|
414
247
|
|
|
415
|
-
|
|
248
|
+
Additionally, all transitive dependencies (including `react`, `react-dom`, `redux`, `react-redux`, `@ebay/nice-modal-react`, `history`) are shared automatically by the MUSE build system.
|
|
416
249
|
|
|
417
|
-
|
|
418
|
-
import MyHomepage from './MyHomepage';
|
|
250
|
+
**File Reference**: `src/index.jsx:41`
|
|
419
251
|
|
|
420
|
-
|
|
421
|
-
name: 'my-plugin',
|
|
422
|
-
home: {
|
|
423
|
-
homepage: MyHomepage
|
|
424
|
-
}
|
|
425
|
-
});
|
|
426
|
-
```
|
|
252
|
+
### Utilities
|
|
427
253
|
|
|
428
|
-
|
|
254
|
+
#### `extendArray(arr, extName, extBase, ...args)`
|
|
429
255
|
|
|
430
|
-
|
|
431
|
-
import MyLayout from './MyLayout';
|
|
256
|
+
Makes any array extensible by js-plugin extension points. Invokes four lifecycle hooks in order — `preProcess<Name>`, `get<Name>`, `process<Name>`, `postProcess<Name>` — then sorts the array by `order`.
|
|
432
257
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
mainLayout: MyLayout // Should accept children prop
|
|
437
|
-
}
|
|
438
|
-
});
|
|
439
|
-
```
|
|
258
|
+
- **Parameters**: `arr` (mutable array), `extName` (string, capitalized to form hook names), `extBase` (string prefix for hook names), `...args` (passed through to each hook invocation)
|
|
259
|
+
- **Returns**: the mutated `arr`
|
|
260
|
+
- **File Reference**: `src/utils.js:15`
|
|
440
261
|
|
|
441
|
-
###
|
|
262
|
+
### React Hooks
|
|
442
263
|
|
|
443
|
-
|
|
444
|
-
import myReducer from './redux/reducer';
|
|
264
|
+
#### `useExtPoint(extPointName, extArgs)`
|
|
445
265
|
|
|
446
|
-
|
|
447
|
-
name: '@ebay/my-plugin',
|
|
448
|
-
reducer: myReducer
|
|
449
|
-
});
|
|
266
|
+
A React hook that invokes any extension point and renders all contributed components into a JSX fragment. Returns `{ extNode, values }` where `extNode` is the rendered fragment of all contributions and `values` is state updated when a contribution calls its `callback` prop.
|
|
450
267
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
268
|
+
- **Parameters**: `extPointName` (string), `extArgs` (object passed as props to each contributed component)
|
|
269
|
+
- **Returns**: `{ extNode: ReactNode, values: any[] }`
|
|
270
|
+
- **File Reference**: `src/features/common/useExtPoint.jsx:7`
|
|
454
271
|
|
|
455
|
-
###
|
|
272
|
+
### React Components
|
|
456
273
|
|
|
457
|
-
|
|
458
|
-
import userReducer from './redux/userReducer';
|
|
459
|
-
import configReducer from './redux/configReducer';
|
|
274
|
+
#### `Nodes`
|
|
460
275
|
|
|
461
|
-
|
|
462
|
-
name: 'my-plugin',
|
|
463
|
-
reducers: {
|
|
464
|
-
user: userReducer,
|
|
465
|
-
appConfig: <USER_NAME>
|
|
466
|
-
}
|
|
467
|
-
});
|
|
276
|
+
Renders an extensible list of nodes where each node can be a render function, raw content, or component. Internally uses `extendArray` to allow other plugins to inject items.
|
|
468
277
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
// state.appConfig
|
|
472
|
-
```
|
|
278
|
+
- **Props**: `items` (array), `extName` (string), `extBase` (string), `extArgs` (object)
|
|
279
|
+
- **File Reference**: `src/features/common/Nodes.jsx:8`
|
|
473
280
|
|
|
474
|
-
###
|
|
281
|
+
### Using Shared Modules
|
|
475
282
|
|
|
476
|
-
|
|
477
|
-
import { ThemeProvider } from 'my-theme-library';
|
|
478
|
-
|
|
479
|
-
plugin.register({
|
|
480
|
-
name: 'my-plugin',
|
|
481
|
-
root: {
|
|
482
|
-
getProviders: () => ({
|
|
483
|
-
order: 15, // Between React Query (10) and Redux (20)
|
|
484
|
-
key: 'theme-provider',
|
|
485
|
-
provider: ThemeProvider,
|
|
486
|
-
props: { theme: myTheme }
|
|
487
|
-
})
|
|
488
|
-
}
|
|
489
|
-
});
|
|
490
|
-
```
|
|
491
|
-
|
|
492
|
-
### Example 8: Modifying Existing Providers
|
|
283
|
+
In normal plugins built with `@ebay/muse-vite-plugin` or `@ebay/muse-webpack-plugin`, these modules are automatically externalized and resolved from `@ebay/muse-lib-react` at runtime:
|
|
493
284
|
|
|
494
285
|
```javascript
|
|
495
|
-
plugin
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
const reduxIndex = providers.findIndex(p => p.key === 'redux-provider');
|
|
501
|
-
if (reduxIndex >= 0) {
|
|
502
|
-
providers.splice(reduxIndex, 1);
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
});
|
|
286
|
+
// In a normal plugin — these are resolved from muse-lib-react at runtime,
|
|
287
|
+
// NOT bundled into the plugin's own bundle.
|
|
288
|
+
import { useNavigate, Link } from 'react-router-dom';
|
|
289
|
+
import { useQuery } from '@tanstack/react-query';
|
|
290
|
+
import _ from 'lodash';
|
|
507
291
|
```
|
|
508
292
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
```javascript
|
|
512
|
-
plugin.register({
|
|
513
|
-
name: 'my-plugin',
|
|
514
|
-
root: {
|
|
515
|
-
beforeRender: () => {
|
|
516
|
-
console.log('App is about to render');
|
|
517
|
-
// Initialize analytics, set up listeners, etc.
|
|
518
|
-
},
|
|
519
|
-
afterRender: () => {
|
|
520
|
-
console.log('App has rendered');
|
|
521
|
-
// Post-render tasks
|
|
522
|
-
}
|
|
523
|
-
},
|
|
524
|
-
onReady: () => {
|
|
525
|
-
console.log('App is fully ready');
|
|
526
|
-
// Final initialization
|
|
527
|
-
}
|
|
528
|
-
});
|
|
529
|
-
```
|
|
293
|
+
**Note**: Exports create tight coupling. Prefer extension points for loose coupling when possible.
|
|
530
294
|
|
|
531
|
-
|
|
295
|
+
---
|
|
532
296
|
|
|
533
|
-
|
|
534
|
-
// In your plugin code
|
|
535
|
-
const { exports } = plugin.getPlugin('@ebay/muse-lib-react');
|
|
536
|
-
const { _, reactRouterDom, reactQuery } = exports;
|
|
297
|
+
## 5. Consumed Exports (Runtime Dependencies)
|
|
537
298
|
|
|
538
|
-
|
|
539
|
-
const uniqueItems = _.uniq(items);
|
|
299
|
+
This plugin does not consume exports from other plugins. All inter-plugin collaboration is done through extension points (loose coupling).
|
|
540
300
|
|
|
541
|
-
|
|
542
|
-
const { useNavigate, Link } = reactRouterDom;
|
|
301
|
+
---
|
|
543
302
|
|
|
544
|
-
|
|
545
|
-
const { useQuery, useMutation } = reactQuery;
|
|
546
|
-
```
|
|
303
|
+
## 6. Integration Examples
|
|
547
304
|
|
|
548
|
-
###
|
|
305
|
+
### Extending This Plugin
|
|
549
306
|
|
|
550
307
|
```javascript
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
function MyToolbar() {
|
|
555
|
-
const items = [
|
|
556
|
-
{ key: 'btn1', component: Button1, props: { label: 'Click' } }
|
|
557
|
-
];
|
|
558
|
-
|
|
559
|
-
return (
|
|
560
|
-
<Nodes
|
|
561
|
-
items={items}
|
|
562
|
-
extName="buttons"
|
|
563
|
-
extBase="myPlugin.toolbar"
|
|
564
|
-
/>
|
|
565
|
-
);
|
|
566
|
-
}
|
|
308
|
+
// Extension points use nested object properties — NOT string paths
|
|
309
|
+
plugin.register({
|
|
310
|
+
name: 'my-plugin',
|
|
567
311
|
|
|
568
|
-
//
|
|
569
|
-
|
|
570
|
-
|
|
312
|
+
// Add application routes
|
|
313
|
+
route: [
|
|
314
|
+
{ path: 'my-feature', component: MyFeaturePage },
|
|
315
|
+
],
|
|
571
316
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
```
|
|
317
|
+
// Redux integration
|
|
318
|
+
reducer: myFeatureReducer,
|
|
575
319
|
|
|
576
|
-
|
|
320
|
+
// Root lifecycle
|
|
321
|
+
onReady: () => {
|
|
322
|
+
console.log('App mounted');
|
|
323
|
+
},
|
|
577
324
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
325
|
+
// Provider injection
|
|
326
|
+
root: {
|
|
327
|
+
getProviders: ({ providers }) => ({
|
|
328
|
+
order: 25,
|
|
329
|
+
key: 'my-context',
|
|
330
|
+
provider: MyContextProvider,
|
|
331
|
+
props: { store: myStore },
|
|
332
|
+
}),
|
|
333
|
+
},
|
|
584
334
|
|
|
585
|
-
|
|
586
|
-
|
|
335
|
+
// Layout/homepage
|
|
336
|
+
home: {
|
|
337
|
+
homepage: MyHomepageComponent,
|
|
338
|
+
mainLayout: MyLayoutComponent, // WARNING: only one plugin should provide this
|
|
339
|
+
},
|
|
587
340
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
rootComponent: MyInitComponent
|
|
341
|
+
// Invisible init component
|
|
342
|
+
rootComponent: MyGlobalListenerComponent,
|
|
591
343
|
});
|
|
592
344
|
```
|
|
593
345
|
|
|
594
|
-
###
|
|
346
|
+
### Building Your Own Extensible Array
|
|
595
347
|
|
|
596
348
|
```javascript
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
349
|
+
import { extendArray } from '@ebay/muse-lib-react/src/utils';
|
|
350
|
+
|
|
351
|
+
// In your component:
|
|
352
|
+
const menuItems = [...defaultItems];
|
|
353
|
+
extendArray(menuItems, 'menuItems', 'myPlugin', { context: someContext });
|
|
354
|
+
// Now other plugins can implement:
|
|
355
|
+
// myPlugin.preProcessMenuItems
|
|
356
|
+
// myPlugin.getMenuItems (return additional items)
|
|
357
|
+
// myPlugin.processMenuItems
|
|
358
|
+
// myPlugin.postProcessMenuItems
|
|
605
359
|
```
|
|
606
360
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
## Integration Checklist
|
|
361
|
+
### Using the useExtPoint Hook
|
|
610
362
|
|
|
611
|
-
|
|
363
|
+
```javascript
|
|
364
|
+
import useExtPoint from '@ebay/muse-lib-react/src/features/common/useExtPoint';
|
|
612
365
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
- [ ] `rootComponent` must return `null` (no UI)
|
|
619
|
-
- [ ] Routes with absolute paths (`/`) are moved to top level
|
|
620
|
-
- [ ] Shared modules accessed via `plugin.getPlugin('@ebay/muse-lib-react').exports`
|
|
621
|
-
- [ ] Sub-app configuration goes in `window.MUSE_GLOBAL.plugins` under this plugin's `subApps` array
|
|
366
|
+
function MyToolbar() {
|
|
367
|
+
const { extNode } = useExtPoint('myPlugin.toolbarItems', { context: 'main' });
|
|
368
|
+
return <div className="toolbar">{extNode}</div>;
|
|
369
|
+
}
|
|
370
|
+
```
|