@ebay/muse-lib-react 1.3.2 → 1.3.5
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/README.md +1 -1
- package/build/MUSE_README.md +621 -0
- package/build/dev/lib-manifest.json +78 -78
- package/build/dev/main.js +84 -84
- package/build/dist/lib-manifest.json +78 -78
- package/build/dist/main.js +1 -1
- package/build/test/lib-manifest.json +78 -78
- package/build/test/main.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# README
|
|
2
2
|
|
|
3
|
-
@ebay/muse-lib-react is a lib plugin
|
|
3
|
+
@ebay/muse-lib-react is a lib plugin with a combination of popular React tech stacks like:
|
|
4
4
|
|
|
5
5
|
- [React Router](https://reactrouter.com/en/main)
|
|
6
6
|
- [Redux](https://redux.js.org/)
|
|
@@ -0,0 +1,621 @@
|
|
|
1
|
+
# Plugin Integration Guide: @ebay/muse-lib-react
|
|
2
|
+
|
|
3
|
+
**Generated**: 2026-03-28
|
|
4
|
+
**Plugin Type**: lib
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1. Plugin Purpose & Overview
|
|
9
|
+
|
|
10
|
+
`@ebay/muse-lib-react` is the **foundational React library plugin** for MUSE applications. As a lib-type plugin with app entry capabilities, it serves three critical roles:
|
|
11
|
+
|
|
12
|
+
1. **React Application Bootstrap** - Creates the React root element, initializes the application, and renders the main component tree with all providers (Redux, React Router, React Query, Nice Modal)
|
|
13
|
+
|
|
14
|
+
2. **Shared Module Provider** - Exports commonly-used React libraries as shared modules to prevent duplicate dependencies across plugins:
|
|
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
|
|
20
|
+
|
|
21
|
+
3. **Extension Point Foundation** - Defines comprehensive extension points enabling other plugins to:
|
|
22
|
+
- Customize routing and homepage
|
|
23
|
+
- Extend Redux store with plugin-specific reducers
|
|
24
|
+
- Modify the provider stack
|
|
25
|
+
- Define main layout and root-level components
|
|
26
|
+
- Hook into application lifecycle events
|
|
27
|
+
|
|
28
|
+
### Key Features
|
|
29
|
+
|
|
30
|
+
- **Provider Stack Management**: Ordered provider chain (React Query → Redux → SubApp Context → Nice Modal → React Router) with extension points at every stage
|
|
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`)
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 2. Extension Points Exposed
|
|
39
|
+
|
|
40
|
+
This plugin exposes extension points that allow other plugins to customize and extend the React application. Extension points are organized into four categories:
|
|
41
|
+
|
|
42
|
+
### 2.1 Application Lifecycle
|
|
43
|
+
|
|
44
|
+
Extension points for hooking into application initialization:
|
|
45
|
+
|
|
46
|
+
#### `root.beforeRender`
|
|
47
|
+
- **Type**: `Function`
|
|
48
|
+
- **Purpose**: Execute logic before the React root element is created and rendered
|
|
49
|
+
- **Use Case**: Early initialization tasks like setting up global state, registering event listeners, or configuring third-party libraries
|
|
50
|
+
- **Invocation**: Called in `src/index.js:26` before `createRoot()`
|
|
51
|
+
- **Context**: Runs after all plugins are loaded but before any React rendering
|
|
52
|
+
|
|
53
|
+
#### `root.afterRender`
|
|
54
|
+
- **Type**: `Function`
|
|
55
|
+
- **Purpose**: Execute logic immediately after the React root element is rendered
|
|
56
|
+
- **Use Case**: Post-render initialization like focusing elements, starting analytics, or triggering initial data fetches
|
|
57
|
+
- **Invocation**: Called in `src/index.js:31` after `root.render(<Root />)`
|
|
58
|
+
- **Context**: Runs before `onReady`, React tree is mounted but may not be fully painted
|
|
59
|
+
|
|
60
|
+
#### `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
|
+
|
|
67
|
+
### 2.2 Routing & Navigation
|
|
68
|
+
|
|
69
|
+
Extension points for customizing application routes and routing behavior:
|
|
70
|
+
|
|
71
|
+
#### `route`
|
|
72
|
+
- **Type**: `MuseRoute | MuseRoute[]`
|
|
73
|
+
- **Purpose**: Register route definitions for plugin-specific pages
|
|
74
|
+
- **Use Case**: Add new pages/routes to the application
|
|
75
|
+
- **Collection**: Routes collected in `src/common/routeConfig.js:87` via `plugin.invoke('!route')`
|
|
76
|
+
- **Route Structure**:
|
|
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`
|
|
88
|
+
|
|
89
|
+
#### `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
|
+
|
|
97
|
+
### 2.3 Provider Stack Customization
|
|
98
|
+
|
|
99
|
+
Extension points for modifying the provider chain that wraps the application. Providers execute in order from outer to inner (lower order = outer wrapper).
|
|
100
|
+
|
|
101
|
+
#### `root.preProcessProviders`
|
|
102
|
+
- **Type**: `(context: { providers: ProviderType[] }) => void`
|
|
103
|
+
- **Purpose**: Modify or remove built-in providers before custom providers are collected
|
|
104
|
+
- **Use Case**: Disable default providers (e.g., remove Redux if using alternative state management)
|
|
105
|
+
- **Invocation Order**: 1st - called by `extendArray()` in `src/Root.js:181`
|
|
106
|
+
- **Context**: `{ providers }` array of default providers (React Query, Redux, SubApp Context, Nice Modal, React Router)
|
|
107
|
+
- **File Reference**: `src/utils.js:17`, `src/Root.js:144-181`
|
|
108
|
+
|
|
109
|
+
#### `root.getProviders`
|
|
110
|
+
- **Type**: `(context: { providers: ProviderType[] }) => ProviderType | ProviderType[] | void`
|
|
111
|
+
- **Purpose**: Contribute additional providers to wrap the application
|
|
112
|
+
- **Use Case**: Add custom providers (e.g., theme provider, i18n provider, custom context)
|
|
113
|
+
- **Invocation Order**: 2nd - called by `extendArray()` in `src/Root.js:181`
|
|
114
|
+
- **Return**: Single provider, array of providers, or nothing
|
|
115
|
+
- **Provider Structure**:
|
|
116
|
+
- `order`: Rendering order (lower = outer, default sort)
|
|
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`
|
|
122
|
+
|
|
123
|
+
#### `root.processProviders`
|
|
124
|
+
- **Type**: `(context: { providers: ProviderType[] }) => void`
|
|
125
|
+
- **Purpose**: Modify the collected providers array after all contributions
|
|
126
|
+
- **Use Case**: Reorder providers, modify provider props, or conditionally add/remove providers
|
|
127
|
+
- **Invocation Order**: 3rd - called by `extendArray()` in `src/Root.js:181`
|
|
128
|
+
- **Context**: `{ providers }` includes both built-in and contributed providers
|
|
129
|
+
- **File Reference**: `src/utils.js:20`, `src/Root.js:181`
|
|
130
|
+
|
|
131
|
+
#### `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
|
+
|
|
139
|
+
#### `root.renderChildren`
|
|
140
|
+
- **Type**: `(children: ReactNode) => ReactNode`
|
|
141
|
+
- **Purpose**: Wrap the root element with custom components
|
|
142
|
+
- **Use Case**: Alternative to `getProviders` for wrapping the app (less recommended)
|
|
143
|
+
- **Collection**: All contributions applied sequentially in `src/Root.js:105-110`
|
|
144
|
+
- **Note**: Prefer `getProviders` for adding providers; this is for non-provider wrappers
|
|
145
|
+
- **File Reference**: `src/Root.js:104-111`
|
|
146
|
+
|
|
147
|
+
### 2.4 Layout & UI
|
|
148
|
+
|
|
149
|
+
Extension points for customizing the application's visual structure:
|
|
150
|
+
|
|
151
|
+
#### `home.homepage`
|
|
152
|
+
- **Type**: `ComponentType<Object>`
|
|
153
|
+
- **Purpose**: Provide a custom homepage component for the root route `/`
|
|
154
|
+
- **Use Case**: Replace default "Welcome to Muse" page with application-specific homepage
|
|
155
|
+
- **Resolution**: Single contribution used; if multiple, checks `window.MUSE_GLOBAL.homepage` for preference
|
|
156
|
+
- **Collection**: Via `plugin.getPlugins('home.homepage')` in `src/common/routeConfig.js:96-111`
|
|
157
|
+
- **Default**: If not provided, uses built-in `Homepage` component (`src/features/home/Homepage.js`)
|
|
158
|
+
- **File Reference**: `src/common/routeConfig.js:94-116`
|
|
159
|
+
|
|
160
|
+
#### `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
|
+
|
|
169
|
+
#### `rootComponent`
|
|
170
|
+
- **Type**: `ComponentType<Object>`
|
|
171
|
+
- **Purpose**: Render initialization components at app root (should return `null`, no UI)
|
|
172
|
+
- **Use Case**: Global initialization logic that needs component lifecycle (e.g., modal managers, global listeners)
|
|
173
|
+
- **Collection**: All contributions rendered in `src/features/home/App.js:26-29`
|
|
174
|
+
- **Important**: Components should not render visible UI, only invisible initialization/context
|
|
175
|
+
- **File Reference**: `src/features/home/App.js:6-30`
|
|
176
|
+
|
|
177
|
+
### 2.5 State Management
|
|
178
|
+
|
|
179
|
+
Extension points for extending the Redux store:
|
|
180
|
+
|
|
181
|
+
#### `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
|
+
|
|
203
|
+
This plugin does not contribute to extension points from other plugins. As a foundational lib plugin, it defines the extension point architecture but does not extend external plugins.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## 4. Exported Functionality
|
|
208
|
+
|
|
209
|
+
As a lib-type plugin, `@ebay/muse-lib-react` exports shared modules and utilities for use by other plugins.
|
|
210
|
+
|
|
211
|
+
### 4.1 Shared Modules (Module Federation)
|
|
212
|
+
|
|
213
|
+
These modules are exported via the default export in `src/index.js:41` and made available to other plugins through MUSE's module sharing system (webpack/vite plugin externalization):
|
|
214
|
+
|
|
215
|
+
#### `Loadable`
|
|
216
|
+
- **Source**: `react-loadable` package
|
|
217
|
+
- **Purpose**: Code splitting and lazy loading of React components
|
|
218
|
+
- **Use Case**: Improve bundle size and load time by dynamically importing components
|
|
219
|
+
- **File Reference**: `src/index.js:7,41`
|
|
220
|
+
|
|
221
|
+
#### `_` (lodash)
|
|
222
|
+
- **Source**: `lodash` package
|
|
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`
|
|
226
|
+
|
|
227
|
+
#### `reactUse`
|
|
228
|
+
- **Source**: `react-use` package
|
|
229
|
+
- **Purpose**: Collection of essential React hooks
|
|
230
|
+
- **Use Case**: Access common hooks (useMount, useToggle, etc.) without separate package
|
|
231
|
+
- **File Reference**: `src/index.js:9,41`
|
|
232
|
+
|
|
233
|
+
#### `reactRouterDom`
|
|
234
|
+
- **Source**: `react-router-dom` v6
|
|
235
|
+
- **Purpose**: React Router v6 routing library
|
|
236
|
+
- **Use Case**: Navigation components (`Link`, `Navigate`) and hooks (`useNavigate`, `useParams`)
|
|
237
|
+
- **File Reference**: `src/index.js:11,41`
|
|
238
|
+
|
|
239
|
+
#### `reactQuery`
|
|
240
|
+
- **Source**: `@tanstack/react-query` v4
|
|
241
|
+
- **Purpose**: Server state management (data fetching, caching, synchronization)
|
|
242
|
+
- **Use Case**: Manage API calls and server state with hooks (`useQuery`, `useMutation`)
|
|
243
|
+
- **File Reference**: `src/index.js:12,41`
|
|
244
|
+
|
|
245
|
+
### 4.2 Utility Components
|
|
246
|
+
|
|
247
|
+
These components can be imported from the plugin's `build/` directory by other plugins:
|
|
248
|
+
|
|
249
|
+
#### `Nodes`
|
|
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`
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
## 5. Integration Examples
|
|
374
|
+
|
|
375
|
+
**CRITICAL**: Extension points are **nested object properties**, NOT string paths!
|
|
376
|
+
|
|
377
|
+
### Example 1: Adding a Custom Route
|
|
378
|
+
|
|
379
|
+
```javascript
|
|
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
|
+
});
|
|
394
|
+
|
|
395
|
+
// ❌ INCORRECT - DO NOT use string paths
|
|
396
|
+
plugin.register({
|
|
397
|
+
name: 'my-plugin',
|
|
398
|
+
'route': { ... } // Works, but 'route' doesn't need quotes
|
|
399
|
+
});
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### Example 2: Contributing Multiple Routes
|
|
403
|
+
|
|
404
|
+
```javascript
|
|
405
|
+
plugin.register({
|
|
406
|
+
name: 'my-plugin',
|
|
407
|
+
route: [
|
|
408
|
+
{ path: '/page1', component: Page1 },
|
|
409
|
+
{ path: '/page2', component: Page2 },
|
|
410
|
+
{ path: '/page3', component: Page3, parent: 'some-route-id' }
|
|
411
|
+
]
|
|
412
|
+
});
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### Example 3: Customizing Homepage
|
|
416
|
+
|
|
417
|
+
```javascript
|
|
418
|
+
import MyHomepage from './MyHomepage';
|
|
419
|
+
|
|
420
|
+
plugin.register({
|
|
421
|
+
name: 'my-plugin',
|
|
422
|
+
home: {
|
|
423
|
+
homepage: MyHomepage
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### Example 4: Adding Custom Layout
|
|
429
|
+
|
|
430
|
+
```javascript
|
|
431
|
+
import MyLayout from './MyLayout';
|
|
432
|
+
|
|
433
|
+
plugin.register({
|
|
434
|
+
name: 'my-plugin',
|
|
435
|
+
home: {
|
|
436
|
+
mainLayout: MyLayout // Should accept children prop
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Example 5: Adding Redux Reducer (Plugin-Level)
|
|
442
|
+
|
|
443
|
+
```javascript
|
|
444
|
+
import myReducer from './redux/reducer';
|
|
445
|
+
|
|
446
|
+
plugin.register({
|
|
447
|
+
name: '@ebay/my-plugin',
|
|
448
|
+
reducer: myReducer
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
// Redux state will be accessible at:
|
|
452
|
+
// state.pluginEbayMyPlugin
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### Example 6: Adding Redux Reducers (Root-Level)
|
|
456
|
+
|
|
457
|
+
```javascript
|
|
458
|
+
import userReducer from './redux/userReducer';
|
|
459
|
+
import configReducer from './redux/configReducer';
|
|
460
|
+
|
|
461
|
+
plugin.register({
|
|
462
|
+
name: 'my-plugin',
|
|
463
|
+
reducers: {
|
|
464
|
+
user: userReducer,
|
|
465
|
+
appConfig: <USER_NAME>
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
// Redux state accessible at:
|
|
470
|
+
// state.user
|
|
471
|
+
// state.appConfig
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### Example 7: Adding a Custom Provider
|
|
475
|
+
|
|
476
|
+
```javascript
|
|
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
|
|
493
|
+
|
|
494
|
+
```javascript
|
|
495
|
+
plugin.register({
|
|
496
|
+
name: 'my-plugin',
|
|
497
|
+
root: {
|
|
498
|
+
preProcessProviders: ({ providers }) => {
|
|
499
|
+
// Remove Redux provider
|
|
500
|
+
const reduxIndex = providers.findIndex(p => p.key === 'redux-provider');
|
|
501
|
+
if (reduxIndex >= 0) {
|
|
502
|
+
providers.splice(reduxIndex, 1);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
### Example 9: Lifecycle Hooks
|
|
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
|
+
```
|
|
530
|
+
|
|
531
|
+
### Example 10: Using Shared Modules
|
|
532
|
+
|
|
533
|
+
```javascript
|
|
534
|
+
// In your plugin code
|
|
535
|
+
const { exports } = plugin.getPlugin('@ebay/muse-lib-react');
|
|
536
|
+
const { _, reactRouterDom, reactQuery } = exports;
|
|
537
|
+
|
|
538
|
+
// Use lodash
|
|
539
|
+
const uniqueItems = _.uniq(items);
|
|
540
|
+
|
|
541
|
+
// Use React Router
|
|
542
|
+
const { useNavigate, Link } = reactRouterDom;
|
|
543
|
+
|
|
544
|
+
// Use React Query
|
|
545
|
+
const { useQuery, useMutation } = reactQuery;
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
### Example 11: Using Utility Components
|
|
549
|
+
|
|
550
|
+
```javascript
|
|
551
|
+
import { Nodes, useExtPoint } from '@ebay/muse-lib-react/build/features/common';
|
|
552
|
+
|
|
553
|
+
// Using Nodes component
|
|
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
|
+
}
|
|
567
|
+
|
|
568
|
+
// Using useExtPoint hook
|
|
569
|
+
function MyComponent() {
|
|
570
|
+
const { extNode, values } = useExtPoint('myPlugin.customPoint', { data: 123 });
|
|
571
|
+
|
|
572
|
+
return <div>{extNode}</div>;
|
|
573
|
+
}
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
### Example 12: Root Component for Initialization
|
|
577
|
+
|
|
578
|
+
```javascript
|
|
579
|
+
function MyInitComponent() {
|
|
580
|
+
useEffect(() => {
|
|
581
|
+
// Initialize something globally
|
|
582
|
+
window.myGlobalState = { ... };
|
|
583
|
+
}, []);
|
|
584
|
+
|
|
585
|
+
return null; // IMPORTANT: No UI rendering
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
plugin.register({
|
|
589
|
+
name: 'my-plugin',
|
|
590
|
+
rootComponent: MyInitComponent
|
|
591
|
+
});
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
### Example 13: Custom Router Props
|
|
595
|
+
|
|
596
|
+
```javascript
|
|
597
|
+
plugin.register({
|
|
598
|
+
name: 'my-plugin',
|
|
599
|
+
routerProps: {
|
|
600
|
+
future: {
|
|
601
|
+
v7_startTransition: true
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
});
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
---
|
|
608
|
+
|
|
609
|
+
## Integration Checklist
|
|
610
|
+
|
|
611
|
+
When integrating with `@ebay/muse-lib-react`:
|
|
612
|
+
|
|
613
|
+
- [ ] Ensure plugin type is compatible (normal or lib plugins can extend this)
|
|
614
|
+
- [ ] Use nested object properties for extension points, not string paths
|
|
615
|
+
- [ ] Only one plugin should provide `home.homepage` and `home.mainLayout`
|
|
616
|
+
- [ ] Provider `order` values: lower = outer wrapper (10, 20, 30...)
|
|
617
|
+
- [ ] Redux reducer keys via `reducer` are auto-generated; use `reducers` for custom keys
|
|
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
|