react-chain-of-responsibility 0.2.0 → 0.2.1-main.6ac5632

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 CHANGED
@@ -1,12 +1,10 @@
1
1
  # `react-chain-of-responsibility`
2
2
 
3
- [Chain of responsibility design pattern](https://refactoring.guru/design-patterns/chain-of-responsibility) for React component customization.
3
+ [Chain of responsibility design pattern](https://refactoring.guru/design-patterns/chain-of-responsibility) for compositing and customizing React component.
4
4
 
5
5
  ## Background
6
6
 
7
- This package is designed for React component developers to enable component customization via composition using the [chain of responsibility design pattern](https://refactoring.guru/design-patterns/chain-of-responsibility).
8
-
9
- Additional entrypoint and hook are provided to use with [Fluent UI as `IRenderFunction`](https://github.com/microsoft/fluentui/blob/master/packages/utilities/src/IRenderFunction.ts).
7
+ This package is designed for React component developers to enable component customization via composition using the [chain of responsibility design pattern](https://refactoring.guru/design-patterns/chain-of-responsibility). This pattern is also used in [Express](https://expressjs.com/) and [Redux](https://redux.js.org/).
10
8
 
11
9
  By composing customizations, they can be decoupled and published separately. App developers could import these published customizations and orchestrate them to their needs. This pattern encourages [separation of concerns](https://en.wikipedia.org/wiki/Separation_of_concerns) and enables economy of customizability.
12
10
 
@@ -14,50 +12,243 @@ By composing customizations, they can be decoupled and published separately. App
14
12
 
15
13
  Click here for [our live demo](https://compulim.github.io/react-chain-of-responsibility/).
16
14
 
17
- ## How to use
15
+ ## How to use?
18
16
 
19
- ### Using `<Proxy>` component
17
+ 3 steps to adopt the chain of responsibility pattern.
20
18
 
21
- ```jsx
19
+ 1. [Create a chain](#create-a-chain)
20
+ 1. [Register handlers in the chain](#register-handlers-in-the-chain)
21
+ 1. [Make a render request](#make-a-render-request)
22
+
23
+ In this sample, we will use chain of responsibility pattern to create a file preview UI which can handle various file types.
24
+
25
+ ### Create a chain
26
+
27
+ A chain consists of multiple handlers (a.k.a. middleware) and each would handle rendering requests.
28
+
29
+ The request will be passed to the first handler and may traverse down the chain. The returning result will be a React component. If the chain decided not to render anything, it will return `undefined`.
30
+
31
+ ```tsx
22
32
  import { createChainOfResponsibility } from 'react-chain-of-responsibility';
23
33
 
24
- // Creates a <Provider> providing the chain of responsibility service.
25
- const { Provider, Proxy } = createChainOfResponsibility();
34
+ type Request = { contentType: string };
35
+ type Props = { url: string };
26
36
 
27
- // List of subcomponents.
28
- const Bold = ({ children }) => <strong>{children}</strong>;
29
- const Italic = ({ children }) => <i>{children}</i>;
30
- const Plain = ({ children }) => <>{children}</>;
37
+ const { asMiddleware, Provider, Proxy } = createChainOfResponsibility<Request, Props>();
38
+ ```
31
39
 
32
- // Constructs an array of middleware to handle the request and return corresponding subcomponents.
33
- const middleware = [
34
- () => next => request => (request === 'bold' ? Bold : next(request)),
35
- () => next => request => (request === 'italic' ? Italic : next(request)),
36
- () => () => () => Plain
37
- ];
40
+ In this sample, the `request` contains file type. And the `props` contains the URL of the file.
41
+
42
+ Tips: `request` is appearance, while `props` is for content.
43
+
44
+ ### Register handlers in the chain
45
+
46
+ Based on the rendering request, each middleware is called in turn and they will make decision:
47
+
48
+ - Will render
49
+ - Will render a component on its own
50
+ - Will render a component by compositing component from the next middleware in the chain
51
+ - Will not render
52
+ - Will not render anything at all
53
+ - Will not render, but let the next middleware in the chain to decide what to render
54
+
55
+ ```tsx
56
+ // Will handle request with content type `image/*`.
57
+ const Image = ({ middleware: { request, Next }, url }) =>
58
+ request.contentType.startsWith('image/') ? (
59
+ <img src={url} />
60
+ ) : (
61
+ <Next />
62
+ );
63
+
64
+ // Will handle request with content type `video/*`.
65
+ const Video = ({ middleware: { request, Next }, url }) =>
66
+ request.contentType.startsWith('video/') ? (
67
+ <video>
68
+ <source src={url} />
69
+ </video>
70
+ ) : (
71
+ <Next />
72
+ );
73
+
74
+ // Will handle everything.
75
+ const Binary = ({ url }) => <a href={url}>{url}</a>;
76
+
77
+ const middleware = [asMiddleware(Image), asMiddleware(Video), asMiddleware(Binary)];
78
+ ```
79
+
80
+ In this sample, 3 middleware are registered:
81
+
82
+ - `<Image>` will render `<img>` if content type is `'image/*'`, otherwise, will pass to next middleware (`<Video>`)
83
+ - `<Video>` will render `<video>` if content type is `'video/*'`, otherwise, will pass to next middleware (`<Binary>`)
84
+ - `<Binary>` is a catch-all and will render as a link
85
+
86
+ Notes: props passed to `<Next>` will override original props, however, `request` cannot be overridden.
38
87
 
88
+ ### Make a render request
89
+
90
+ Before calling any components or hooks, the `<Provider>` component must be set up with the chain.
91
+
92
+ When `<Proxy>` is being rendered, it will pass the `request` to the chain. The component returned from the chain will be rendered with `...props`. If no component is returned, it will be rendered as `undefined`.
93
+
94
+ ```tsx
39
95
  render(
40
96
  <Provider middleware={middleware}>
41
- <Proxy request="bold">This is bold.</Proxy>
42
- <Proxy request="italic">This is italic.</Proxy>
43
- <Proxy>This is plain.</Proxy>
97
+ <Proxy request={{ contentType: 'image/png' }} url="https://.../cat.png" />
98
+ <Proxy request={{ contentType: 'video/mp4' }} url="https://.../cat-jump.mp4" />
99
+ <Proxy request={{ contentType: 'application/octet-stream' }} url="https://.../cat.zip" />
44
100
  </Provider>
45
101
  );
46
102
  ```
47
103
 
48
- This sample will render:
104
+ The code above will render:
49
105
 
50
- > **This is bold.** _This is italic._ This is plain.
106
+ ```html
107
+ <img src="https://.../cat.png" />
108
+ <video>
109
+ <source src="https://.../cat-jump.mp4" />
110
+ </video>
111
+ <a href="https://.../cat.zip">https://.../cat.zip</a>
112
+ ```
51
113
 
52
- ```jsx
53
- <strong>This is bold.</strong>
54
- <i>This is italic.</i>
55
- <>This is plain.</>
114
+ For advanced scenario with precise rendering control, use the `useBuildComponentCallback` hook. This can be found in our live demo and [latter sections](#make-render-request-through-usebuildmiddlewarecallback).
115
+
116
+ ## How should I use?
117
+
118
+ Here are some recipes on leveraging the chain of responsibility pattern for UI composition.
119
+
120
+ ### Bring your own component
121
+
122
+ Customer of a component library can "bring your own" component by registering their component in the `<Provider>` component.
123
+
124
+ For example, in a date picker UI, using the chain of responsibility pattern enables app developer to bring their own "month picker" component.
125
+
126
+ ```diff
127
+ type MonthPickerProps = Readonly<{
128
+ onChange: (date: Date) => void;
129
+ value: Date
130
+ }>;
131
+
132
+ function MonthPicker(props: MonthPickerProps) {
133
+ return (
134
+ <div>
135
+ {props.value.toLocaleDateString(undefined, { month: 'long' })}
136
+ </div>
137
+ );
138
+ };
139
+
140
+ + const {
141
+ + asMiddleware: asMonthPickerMiddleware,
142
+ + Provider: MonthPickerProvider,
143
+ + Proxy: MonthPickerProxy
144
+ + } = createChainOfResponsibility<undefined, MonthPickerProps>();
145
+
146
+ type DatePickerProps = Readonly<{
147
+ + monthPickerComponent?: ComponentType<MonthPickerProps> | undefined;
148
+ onChange: (date: Date) => void;
149
+ value: Date;
150
+ }>;
151
+
152
+ + const monthPickerMiddleware = asMonthPickerMiddleware(MonthPicker);
153
+
154
+ function DatePicker(props: DatePickerProps) {
155
+ + const monthPickerMiddleware = useMemo(
156
+ + () =>
157
+ + props.monthPickerComponent
158
+ + ? [
159
+ + asMonthPickerMiddleware(props.monthPickerComponent),
160
+ + monthPickerMiddleware
161
+ + ]
162
+ + : [monthPickerMiddleware],
163
+ + [props.monthPickerComponent]
164
+ + );
165
+
166
+ return (
167
+ + <MonthPickerProvider middleware={monthPickerMiddleware}>
168
+ <div>
169
+ - <MonthPicker onChange={onChange} value={value} />
170
+ + <MonthPickerProxy onChange={onChange} value={value} />
171
+ <Calendar value={value} />
172
+ </div>
173
+ + </MonthPickerProvider>
174
+ );
175
+ }
176
+ ```
177
+
178
+ ### Customizing component
179
+
180
+ The "which component to render" decision in the middleware enables 4 key customization techniques:
181
+
182
+ - Add a new component
183
+ - Register a new `<Audio>` middleware component to handle content type of "audio/\*"
184
+ - Replace an existing component
185
+ - Register a new `<Image2>` middleware component to handle content type of "image/\*"
186
+ - Remove an existing component
187
+ - Return `undefined` when handling content type of "video/\*"
188
+ - Decorate an existing component
189
+ - Return a component which render `<div class="my-border"><Next /></div>`
190
+
191
+ ### Improve load time through code splitting and lazy loading
192
+
193
+ After a bundle is lazy-loaded, register the component in the middleware.
194
+
195
+ When the chain of the `<Provider>` is updated, the lazy-loaded component will be rendered immediately.
196
+
197
+ This recipe can also used to build multiple flavors of bundle and allow bundle to be composited to suit the apps need.
198
+
199
+ ## Advanced usage
200
+
201
+ ### Registering component using functional pattern
202
+
203
+ The `asMiddleware()` is a helper function to turn a React component into a component middleware for simpler registration. As it operates in render-time, there are disadvantages. For example, a VDOM node is always required.
204
+
205
+ If precise rendering control is a requirement, consider registering the component natively using functional programming.
206
+
207
+ The following code snippet shows the conversion from the `<Image>` middleware component in our previous sample, into a component registered via functional programming.
208
+
209
+ ```diff
210
+ // Simplify the `<Image>` component by removing `middleware` props.
211
+ - const Image = ({ middleware: { request, Next }, url }) =>
212
+ - request.contentType.startsWith('image/') ? <img src={url} /> : <Next />;
213
+ + const Image = ({ url }) => <img src={url} />;
214
+
215
+ // Registering the `<Image>` component functionally.
216
+ const middleware = [
217
+ - asMiddleware(Image);
218
+ + () => next => request =>
219
+ + request.contentType.startsWith('image/') ? Image : next(request)
220
+ ];
221
+ ```
222
+
223
+ Notes: for performance reason, the return value of the `next(request)` should be a stable value. In the example above, the middleware return `Image`, which is a constant and is stable.
224
+
225
+ ### Make render request through `useBuildMiddlewareCallback()`
226
+
227
+ Similar the `asMiddleware`, the `<Proxy>` component is a helper component for easier rendering. It shares similar disadvantages.
228
+
229
+ The following code snippet shows the conversion from the `<Proxy>` component into the `useBuildMiddlewareCallback()` hook.
230
+
231
+ ```diff
232
+ function App() {
233
+ // Make a render request (a.k.a. build a component.)
234
+ + const buildMiddleware = useBuildMiddlewareCallback();
235
+ + const FilePreview = buildMiddleware({ contextType: 'image/png' });
236
+
237
+ return (
238
+ <Provider middleware={middleware}>
239
+ {/* Simplify the element by removing `request` props and handling `FilePreview` if it is `undefined`. */}
240
+ - <Proxy request={{ contentType: 'image/png' }} url="https://.../cat.png" />
241
+ + {FilePreview && <FilePreview url="https://.../cat.png" />}
242
+ </Provider>
243
+ );
244
+ }
56
245
  ```
57
246
 
58
- ### Using with Fluent UI as `IRenderFunction`
247
+ ### Using as `IRenderFunction` in Fluent UI v8
248
+
249
+ > We are considering deprecating the `IRenderFunction` as Fluent UI no longer adopt this pattern.
59
250
 
60
- The chain of responsibility design pattern can be used in Fluent UI.
251
+ The chain of responsibility design pattern can be used in Fluent UI v8.
61
252
 
62
253
  After calling `createChainOfResponsibilityForFluentUI`, it will return `useBuildRenderFunction` hook. This hook, when called, will return a function to use as [`IRenderFunction`](https://github.com/microsoft/fluentui/blob/master/packages/utilities/src/IRenderFunction.ts) in Fluent UI components.
63
254
 
@@ -106,70 +297,19 @@ There are subtle differences between the standard version and the Fluent UI vers
106
297
  - They are optional too, as defined in [`IRenderFunction`](https://github.com/microsoft/fluentui/blob/master/packages/utilities/src/IRenderFunction.ts)
107
298
  - Automatic fallback to `defaultRender`
108
299
 
109
- ### Decorating UI components
110
-
111
- One core feature of chain of responsibility design pattern is allowing middleware to control the execution flow. In other words, middleware can decorate the result of their `next()` middleware. The code snippet below shows how the wrapping could be done.
112
-
113
- The bold middleware uses traditional approach to wrap the `next()` result, which is a component named `<Next>`. The italic middleware uses [`react-wrap-with`](https://npmjs.com/package/react-wrap-with/) to simplify the wrapping code.
114
-
115
- ```jsx
116
- const middleware = [
117
- () => next => request => {
118
- const Next = next(request);
119
-
120
- if (request?.has('bold')) {
121
- return props => <Bold>{Next && <Next {...props} />}</Bold>;
122
- }
123
-
124
- return Next;
125
- },
126
- () => next => request => wrapWith(request?.has('italic') && Italic)(next(request)),
127
- () => () => () => Plain
128
- ];
129
-
130
- const App = () => (
131
- <Provider middleware={middleware}>
132
- <Proxy request={new Set(['bold'])}>This is bold.</Proxy>
133
- <Proxy request={new Set(['italic'])}>This is italic.</Proxy>
134
- <Proxy request={new Set(['bold', 'italic'])}>This is bold and italic.</Proxy>
135
- <Proxy>This is plain.</Proxy>
136
- </Provider>
137
- );
138
- ```
139
-
140
- This sample will render:
141
-
142
- > **This is bold.** _This is italic._ **_This is bold and italic._** This is plain.
143
-
144
- ```jsx
145
- <Bold>This is bold.</Bold>
146
- <Italic>This is italic.</Italic>
147
- <Bold><Italic>This is bold and italic.</Italic></Bold>
148
- <Plain>This is plain.</Plain>
149
- ```
150
-
151
300
  ### Nesting `<Provider>`
152
301
 
153
302
  If the `<Provider>` from the same chain appears nested in the tree, the `<Proxy>` will render using the middleware from the closest `<Provider>` and fallback up the chain. The following code snippet will render "Second First".
154
303
 
155
304
  ```jsx
156
- const { Provider, Proxy } = createChainOfResponsibility();
157
-
158
- const firstMiddleware = () => next => request => {
159
- const NextComponent = next(request);
160
-
161
- return () => <Fragment>First {NextComponent && <NextComponent />}</Fragment>;
162
- };
305
+ const { asMiddleware, Provider, Proxy } = createChainOfResponsibility();
163
306
 
164
- const secondMiddleware = () => next => request => {
165
- const NextComponent = next(request);
166
-
167
- return () => <Fragment>Second {NextComponent && <NextComponent />}</Fragment>;
168
- };
307
+ const First = ({ middleware: { Next } }) => <>First <Next /></>;
308
+ const Second = ({ middleware: { Next } }) => <>Second <Next /></>;
169
309
 
170
310
  render(
171
- <Provider middleware={[firstMiddleware]}>
172
- <Provider middleware={[secondMiddleware]}>
311
+ <Provider middleware={[asMiddleware(First)]}>
312
+ <Provider middleware={[asMiddleware(Second)]}>
173
313
  <Proxy /> <!-- Renders "Second First" -->
174
314
  </Provider>
175
315
  </Provider>
@@ -182,29 +322,41 @@ render(
182
322
  function createChainOfResponsibility<Request = undefined, Props = { children?: never }, Init = undefined>(
183
323
  options?: Options
184
324
  ): {
325
+ asMiddleware(
326
+ middlewareComponent: ComponentType<MiddlewareComponentProps<Request, Props, Init>>
327
+ ): ComponentMiddleware<Request, Props, Init>;
185
328
  Provider: ComponentType<ProviderProps<Request, Props, Init>>;
186
329
  Proxy: ComponentType<ProxyProps<Request, Props>>;
187
330
  types: {
188
331
  init: Init;
189
332
  middleware: ComponentMiddleware<Request, Props, Init>;
333
+ middlewareComponentProps: MiddlewareComponentProps<Request, Props, Init>;
190
334
  props: Props;
191
335
  request: Request;
192
336
  };
193
- useBuildComponentCallback: () => UseBuildComponent<Request, Props>;
337
+ useBuildComponentCallback(): (
338
+ request: Request,
339
+ options?: {
340
+ fallbackComponent?: ComponentType<Props> | undefined
341
+ }
342
+ ) => ComponentType<Props> | undefined;
194
343
  };
195
344
  ```
196
345
 
197
346
  ### Return value
198
347
 
199
- | Name | Type | Description |
200
- | --------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
201
- | `Provider` | `React.ComponentType` | Entrypoint component, must wraps all usage of customizations |
202
- | `Proxy` | `React.ComponentType` | Proxy component, process the `request` from props and morph into the result component |
203
- | `types` | `{ init, middleware, props, request }` | TypeScript: shorthand types, all objects are `undefined` intentionally |
204
- | `useBuildComponentCallback` | `() => (request, options) => React.ComponentType \| false \| null \| undefined` | Callback hook which return a function to build the component for rendering the result |
348
+ | Name | Description |
349
+ | --------------------------- | ------------------------------------------------------------------------------------- |
350
+ | `asMiddleware` | A helper function to convert a React component into a middleware. |
351
+ | `Provider` | Entrypoint component, must wraps all usage of customizations |
352
+ | `Proxy` | Proxy component, process the `request` from props and morph into the result component |
353
+ | `types` | TypeScript: shorthand types, all objects are `undefined` intentionally |
354
+ | `useBuildComponentCallback` | Callback hook which return a function to build the component for rendering the result |
205
355
 
206
356
  ### Options
207
357
 
358
+ > `passModifiedRequest` is not supported by `asMiddleware`.
359
+
208
360
  ```ts
209
361
  type Options = {
210
362
  /** Allows a middleware to pass another request object to its next middleware. Default is false. */
@@ -212,12 +364,31 @@ type Options = {
212
364
  };
213
365
  ```
214
366
 
215
- If `passModifiedRequest` is default or `false`, middleware will not be allowed to pass another reference of `request` object to their `next()` middleware. Instead, the `request` object passed to `next()` will be ignored and the next middleware always receive the original `request` object. This behavior is similar to [ExpressJS](https://expressjs.com/) middleware.
367
+ If `passModifiedRequest` is default or `false`, middleware will not be allowed to pass another reference of `request` object to their `next()` middleware. Instead, the `request` object passed to `next()` will be ignored and the next middleware always receive the original `request` object. This behavior is similar to [Express](https://expressjs.com/) middleware.
216
368
 
217
369
  Setting to `true` will enable advanced scenarios and allow a middleware to influence their downstreamers.
218
370
 
219
371
  When the option is default or `false`, middleware could still modify the `request` object and influence their downstreamers. It is recommended to follow immutable pattern when handling the `request` object, or use deep [`Object.freeze()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) to guarantee immutability.
220
372
 
373
+ ### API of `asMiddleware`
374
+
375
+ ```ts
376
+ function asMiddleware(
377
+ middlewareComponent: ComponentType<MiddlewareComponentProps<Request, Props, Init>>
378
+ ): ComponentMiddleware<Request, Props, Init>;
379
+
380
+ type MiddlewareProps<Request, Props, Init> = Readonly<{
381
+ init: Init;
382
+ Next: ComponentType<Partial<Props>>;
383
+ request: Request;
384
+ }>;
385
+
386
+ type MiddlewareComponentProps<Request, Props, Init> = Props &
387
+ Readonly<{
388
+ middleware: MiddlewareProps<Request, Props, Init>
389
+ }>;
390
+ ```
391
+
221
392
  ### API of `useBuildComponentCallback`
222
393
 
223
394
  ```ts
@@ -259,9 +430,24 @@ When rendering the element, `getKey` is called to compute the `key` attribute. T
259
430
 
260
431
  ## Designs
261
432
 
262
- ### Why the type of request and props can be different?
433
+ ### What is the difference between request, and props?
434
+
435
+ - Request is for *appearance*, while props is for *content*
436
+ - Request is for *deciding which component to render*, while props is for *what to render*
263
437
 
264
- This approach may seem overkill at first.
438
+ For example:
439
+
440
+ - Button
441
+ - Request: button, link button, push button
442
+ - Props: icon, text
443
+ - File preview
444
+ - Request: image preview, video preview
445
+ - Props: file name, URL
446
+ - Input
447
+ - Request: text input, number input, password input
448
+ - Props: label, value, instruction
449
+
450
+ ### Why the type of request and props can be different?
265
451
 
266
452
  This is to support advanced scenarios where props are not ready until all rendering components are built.
267
453
 
@@ -302,26 +488,35 @@ This is for supporting multiple providers/proxies under a single app/tree.
302
488
 
303
489
  To reduce learning curve and likelihood of bugs, we disabled this feature until developers are more proficient with this package.
304
490
 
305
- If the `request` object passed to `next()` differs from the original `request` object, a reminder will be logged in the console.
491
+ With the default settings, if the `request` object passed to `next()` differs from the original `request` object, a reminder will be logged in the console.
492
+
493
+ ### Why `request` cannot be modified using `asMiddleware` even `passModifiedRequest` is enabled?
494
+
495
+ Request is for deciding which component to render. It is a build-time value.
496
+
497
+ For component registered using `asMiddleware()`, the `request` prop is a render-time value. A render-time value cannot be used to influence build phase.
498
+
499
+ To modify request, the middleware component must be converted to functional programming.
306
500
 
307
501
  ## Behaviors
308
502
 
309
503
  ### `<Proxy>` vs. `useBuildComponentCallback`
310
504
 
311
- Most of the time, use `<Proxy>`.
505
+ Most of the time, use `<Proxy>` unless precise rendering is needed.
312
506
 
313
507
  Behind the scene, `<Proxy>` call `useBuildComponentCallback()` to build the component it would morph into.
314
508
 
315
509
  You can use the following decision tree to know when to use `<Proxy>` vs. `useBuildComponentCallback`
316
510
 
317
- - If you want to know what component will render before actual render happen, use `useBuildComponentCallback()`
318
- - For example, using `useBuildComponentCallback()` allow you to know if the middleware will skip rendering the request
511
+ - If you need to know what kind of component will be rendered before actual render happen, use `useBuildComponentCallback()`
512
+ - For example, using `useBuildComponentCallback()` allow you to know if the middleware would skip rendering the request
319
513
  - If your component use `request` prop which is conflict with `<Proxy>`, use `useBuildComponentCallback()`
514
+ - Also consider using a wrapping component to rename `request` prop
320
515
  - Otherwise, use `<Proxy>`
321
516
 
322
517
  ### Calling `next()` multiple times
323
518
 
324
- It is possible to call `next()` multiple times to render multiple copies of UI. Middleware should be written as a stateless function.
519
+ It is possible to call `next()` multiple times. However, the return value should be stable, calling it multiple times without a different request should yield the same result.
325
520
 
326
521
  This is best used with options `passModifiedRequest` set to `true`. This combination allow a middleware to render the UI multiple times with some variations, such as rendering content and minimap at the same time.
327
522
 
@@ -335,11 +530,13 @@ This is because React does not allow asynchronous render. An exception will be t
335
530
 
336
531
  When writing middleware, keep them as stateless as possible and do not relies on data outside of the `request` object. The way it is writing should be similar to React function components.
337
532
 
338
- ### Good middleware returns false to skip rendering
533
+ When using functional programming to register the middleware, the return value should be stable.
534
+
535
+ ### Good middleware returns `false`/`null`/`undefined` to skip rendering
339
536
 
340
537
  If the middleware wants to skip rendering a request, return `false`/`null`/`undefined` directly. Do not return `() => false`, `<Fragment />`, or any other invisible components.
341
538
 
342
- This helps the component which send the request to the chain of responsibility to determine whether the request could be rendered or not.
539
+ This helps the hosting component to determine whether the request would be rendered or not.
343
540
 
344
541
  ### Typing a component which expect no props to be passed
345
542
 
@@ -349,27 +546,25 @@ In TypeScript, `{}` literally means any objects. Components of type `ComponentTy
349
546
 
350
547
  Although `Record<any, never>` means empty object, it is not extensible. Thus, [`Record<any, never> & { className: string }` means `Record<any, never>`](https://www.typescriptlang.org/play?#code/C4TwDgpgBACgTgezAZygXigJQgYwXAEwB4BDAOxABooyIA3COAPgG4AoUSKAZQFcAjeElQYhKKADIoAbyg4ANiWTIAciQC2EAFxRkwOAEsyAcygBfdmzxk9UMIhQ6+ghyJlzFytZp0BydSRGvubsQA).
351
548
 
352
- We believe the best way to type a component which does not allow any props, is `ComponentType<{ children?: undefined }>`.
353
-
354
549
  ## Inspirations
355
550
 
356
551
  This package is heavily inspired by [Redux](https://redux.js.org/) middleware, especially [`applyMiddleware()`](https://github.com/reduxjs/redux/blob/master/docs/api/applyMiddleware.md) and [`compose()`](https://github.com/reduxjs/redux/blob/master/docs/api/compose.md). We read [this article](https://medium.com/@jacobp100/you-arent-using-redux-middleware-enough-94ffe991e6) to understand the concept, followed by some more readings on functional programming topics.
357
552
 
358
553
  Over multiple years, the chain of responsibility design pattern is proven to be very flexible and extensible in [Bot Framework Web Chat](https://github.co/microsoft/BotFramework-WebChat/). Internal parts of Web Chat is written as middleware consumed by itself. Multiple bundles with various sizes can be offered by removing some middleware and treeshaking them off.
359
554
 
360
- Middleware and router in [ExpressJS](https://expressjs.com/) also inspired us to learn more about this pattern. Their fallback middleware always returns 404 is an innovative approach.
555
+ Middleware and router in [Express](https://expressjs.com/) also inspired us to learn more about this pattern. Their fallback middleware always returns 404 is an innovative approach.
361
556
 
362
- [Bing chat](https://bing.com/chat/) helped us understand and experiment with different naming.
557
+ [Microsoft Copilot](https://copilot.microsoft.com/) helped us understand and experiment with different naming.
363
558
 
364
- ### Differences from Redux and ExpressJS
559
+ ### Differences from Redux and Express
365
560
 
366
- The chain of responsibility design pattern implemented in [Redux](https://redux.js.org/) and [ExpressJS](https://expressjs.com/) prefers fire-and-forget execution (a.k.a. unidirectional): the result from the last middleware will not bubble up back to the first middleware. Instead, the caller may only collect the result from the last middleware. Sometimes, middleware may interrupt the execution and never return any results.
561
+ The chain of responsibility design pattern implemented in [Redux](https://redux.js.org/) and [Express](https://expressjs.com/) prefers fire-and-forget execution (a.k.a. unidirectional): the result from the last middleware will not bubble up back to the first middleware. Instead, the caller may only collect the result from the last middleware, or via asynchronous and intermediate `dispatch()` calls. Sometimes, middleware may interrupt the execution and never return any results.
367
562
 
368
- However, the chain of responsibility design pattern implemented in this package prefers call-and-return execution: the result from the last middleware will propagate back to the first middleware before returning to the caller. This gives every middleware a chance to manipulate the result from downstreamers before sending it back.
563
+ However, the chain of responsibility design pattern implemented in this package prefers synchronous call-and-return execution: the result from the last middleware will propagate back to the first middleware before returning to the caller. This gives every middleware a chance to manipulate the result from downstreamers before sending it back.
369
564
 
370
565
  ## Plain English
371
566
 
372
- This package implements the _chain of responsibility_ design pattern. Based on _request_, the chain of responsibility will be asked to _build a React component_. The middleware will _form a chain_ and request is _passed to the first one in the chain_. If the chain decided to render it, a component will be returned, otherwise, `false`/`null`/`undefined`.
567
+ This package implements the _chain of responsibility_ design pattern. Based on _request_, the chain of responsibility will be asked to _build a React component_. The middleware would _form a chain_ and request is _passed to the first one in the chain_. If the chain decided to render it, a component will be returned, otherwise, `false`/`null`/`undefined`.
373
568
 
374
569
  ## Contributions
375
570
 
@@ -1,5 +1,4 @@
1
1
  // src/createChainOfResponsibility.tsx
2
- import PropTypes from "prop-types";
3
2
  import React, {
4
3
  createContext,
5
4
  isValidElement,
@@ -65,7 +64,7 @@ function createChainOfResponsibility(options = {}) {
65
64
  }
66
65
  };
67
66
  const context = createContext(defaultUseBuildComponentCallback);
68
- const Provider = ({ children, init, middleware }) => {
67
+ function ChainOfResponsibilityProvider({ children, init, middleware }) {
69
68
  if (!Array.isArray(middleware) || middleware.some((middleware2) => typeof middleware2 !== "function")) {
70
69
  throw new Error("middleware prop must be an array of functions");
71
70
  }
@@ -116,32 +115,37 @@ function createChainOfResponsibility(options = {}) {
116
115
  [enhancer, useBuildComponentCallback2]
117
116
  );
118
117
  return /* @__PURE__ */ React.createElement(context.Provider, { value: contextValue }, children);
119
- };
120
- Provider.displayName = "ChainOfResponsibilityProvider";
121
- Provider.propTypes = {
122
- children: PropTypes.any,
123
- init: PropTypes.any,
124
- middleware: PropTypes.any
125
- };
118
+ }
126
119
  const useBuildComponentCallback = () => useContext(context).useBuildComponentCallback;
127
- const Proxy = memo(
128
- // False positive: "children" is not a prop.
129
- // eslint-disable-next-line react/prop-types
130
- ({ children, fallbackComponent, request, ...props }) => {
131
- const enhancer = useBuildComponentCallback();
132
- const Component = enhancer(request, { fallbackComponent });
133
- return Component ? /* @__PURE__ */ React.createElement(Component, { ...props }, children) : null;
134
- }
135
- );
136
- Proxy.displayName = "Proxy";
137
- Proxy.propTypes = {
138
- fallbackComponent: PropTypes.any,
139
- request: PropTypes.any
120
+ function Proxy({ children, fallbackComponent, request, ...props }) {
121
+ const enhancer = useBuildComponentCallback();
122
+ const Component = enhancer(request, { fallbackComponent });
123
+ return Component ? /* @__PURE__ */ React.createElement(Component, { ...props }, children) : null;
124
+ }
125
+ const asMiddleware = (MiddlewareComponent) => (init) => (next) => (request) => {
126
+ const RawNextComponent = next(request);
127
+ const MiddlewareOf = (props) => {
128
+ const middleware = useMemo(
129
+ () => Object.freeze({
130
+ init,
131
+ Next: memo(
132
+ RawNextComponent ? (overridingProps) => /* @__PURE__ */ React.createElement(RawNextComponent, { ...props, ...overridingProps }) : () => null
133
+ ),
134
+ request
135
+ }),
136
+ []
137
+ );
138
+ return /* @__PURE__ */ React.createElement(MiddlewareComponent, { ...props, middleware });
139
+ };
140
+ MiddlewareOf.displayName = `MiddlewareOf<${MiddlewareComponent.displayName || ""}>`;
141
+ return memo(MiddlewareOf);
140
142
  };
141
143
  return {
142
- Provider,
143
- Proxy,
144
+ asMiddleware,
145
+ Provider: memo(ChainOfResponsibilityProvider),
146
+ Proxy: memo(Proxy),
144
147
  types: {
148
+ middlewareComponentProps: void 0,
145
149
  init: void 0,
146
150
  middleware: void 0,
147
151
  props: void 0,
@@ -154,4 +158,4 @@ function createChainOfResponsibility(options = {}) {
154
158
  export {
155
159
  createChainOfResponsibility
156
160
  };
157
- //# sourceMappingURL=chunk-LQNXZPUH.mjs.map
161
+ //# sourceMappingURL=chunk-TT7FCTZA.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/createChainOfResponsibility.tsx","../src/isReactComponent.ts","../src/private/compose.ts","../src/private/applyMiddleware.ts"],"sourcesContent":["import React, {\n createContext,\n isValidElement,\n memo,\n useCallback,\n useContext,\n useMemo,\n type ComponentType,\n type PropsWithChildren\n} from 'react';\n\nimport isReactComponent from './isReactComponent.ts';\nimport applyMiddleware, { type Enhancer } from './private/applyMiddleware.ts';\nimport { type ComponentMiddleware } from './types.ts';\n\n// TODO: Simplify to ComponentType<Props> | undefined.\ntype ResultComponent<Props> = ComponentType<Props> | false | null | undefined;\n\ntype UseBuildComponentCallbackOptions<Props> = { fallbackComponent?: ResultComponent<Props> };\n\ntype UseBuildComponentCallback<Request, Props> = (\n request: Request,\n options?: UseBuildComponentCallbackOptions<Props>\n) => ResultComponent<Props>;\n\ntype ProviderContext<Request, Props> = {\n get enhancer(): Enhancer<[Request], ResultComponent<Props>> | undefined;\n useBuildComponentCallback: UseBuildComponentCallback<Request, Props>;\n};\n\ntype ProviderProps<Request, Props, Init> = PropsWithChildren<{\n middleware: readonly ComponentMiddleware<Request, Props, Init>[];\n}> &\n (Init extends never | void ? { init?: undefined } : Init extends undefined ? { init?: Init } : { init: Init });\n\ntype ProxyProps<Request, Props extends object> = PropsWithChildren<\n Request extends never | void\n ? Props & { fallbackComponent?: ComponentType<Props>; request?: undefined }\n : Request extends undefined\n ? Props & { fallbackComponent?: ComponentType<Props>; request?: Request }\n : Props & { fallbackComponent?: ComponentType<Props>; request: Request }\n>;\n\ntype Options = {\n /**\n * Allows a middleware to pass another request object when calling its next middleware. Default is false.\n *\n * However, middleware could modify the request object before calling its next middleware. It is recommended\n * to use Object.freeze() to prevent middleware from modifying the request object.\n */\n passModifiedRequest?: boolean;\n};\n\ntype MiddlewareProps<Request, Props, Init> = Readonly<{\n init: Init;\n Next: ComponentType<Partial<Props>>;\n request: Request;\n}>;\n\ntype MiddlewareComponentProps<Request, Props, Init> = Props &\n Readonly<{ middleware: MiddlewareProps<Request, Props, Init> }>;\n\nexport default function createChainOfResponsibility<\n Request = undefined,\n Props extends object = Readonly<{ children?: never }>,\n Init = undefined\n>(\n options: Options = {}\n): {\n asMiddleware: (\n middlewareComponent: ComponentType<MiddlewareComponentProps<Request, Props, Init>>\n ) => ComponentMiddleware<Request, Props, Init>;\n Provider: ComponentType<ProviderProps<Request, Props, Init>>;\n Proxy: ComponentType<ProxyProps<Request, Props>>;\n types: {\n init: Init;\n middleware: ComponentMiddleware<Request, Props, Init>;\n middlewareComponentProps: MiddlewareComponentProps<Request, Props, Init>;\n props: Props;\n request: Request;\n };\n useBuildComponentCallback: () => UseBuildComponentCallback<Request, Props>;\n} {\n const defaultUseBuildComponentCallback: ProviderContext<Request, Props> = {\n get enhancer() {\n return undefined;\n },\n useBuildComponentCallback(_, options) {\n if (options?.fallbackComponent) {\n return options.fallbackComponent;\n }\n\n throw new Error('This component/hook cannot be used outside of its corresponding <Provider>');\n }\n };\n\n const context = createContext<ProviderContext<Request, Props>>(defaultUseBuildComponentCallback);\n\n function ChainOfResponsibilityProvider({ children, init, middleware }: ProviderProps<Request, Props, Init>) {\n // TODO: Related to https://github.com/microsoft/TypeScript/issues/17002.\n // typescript@5.2.2 has a bug, Array.isArray() is a type predicate but only works with mutable array, not readonly array.\n // After removing \"as unknown\", `middleware` on the next line become `any[]`.\n if (!Array.isArray(middleware as unknown) || middleware.some(middleware => typeof middleware !== 'function')) {\n throw new Error('middleware prop must be an array of functions');\n }\n\n const patchedMiddleware: readonly ComponentMiddleware<Request, Props, Init>[] = Object.freeze(\n middleware\n ? middleware.map(fn => (init: Init) => {\n const enhancer = fn(init);\n\n return (next: UseBuildComponentCallback<Request, Props>) => (originalRequest: Request) => {\n // False positive: although we did not re-assign the variable from true, it was initialized as undefined.\n // eslint-disable-next-line prefer-const\n let hasReturned: boolean;\n\n const returnValue = enhancer(nextRequest => {\n if (hasReturned) {\n throw new Error('next() cannot be called after the function had returned synchronously');\n }\n\n !options.passModifiedRequest &&\n nextRequest !== originalRequest &&\n console.warn(\n 'react-chain-of-responsibility: \"options.passModifiedRequest\" must be set to true to pass a different request object to next().'\n );\n\n return next(options.passModifiedRequest ? nextRequest : originalRequest);\n })(originalRequest);\n\n hasReturned = true;\n\n if (isValidElement(returnValue)) {\n throw new Error('middleware must not return React element directly');\n } else if (\n returnValue !== false &&\n returnValue !== null &&\n typeof returnValue !== 'undefined' &&\n !isReactComponent(returnValue)\n ) {\n throw new Error(\n 'middleware must return false, null, undefined, function component, or class component'\n );\n }\n\n return returnValue;\n };\n })\n : []\n );\n\n const { enhancer: parentEnhancer } = useContext(context);\n\n const enhancer = useMemo(\n () =>\n // We are reversing because it is easier to read:\n // - With reverse, [a, b, c] will become a(b(c(fn)))\n // - Without reverse, [a, b, c] will become c(b(a(fn)))\n applyMiddleware<[Request], ResultComponent<Props>, [Init]>(\n ...[...patchedMiddleware, ...(parentEnhancer ? [() => parentEnhancer] : [])].reverse()\n )(init as Init),\n [init, middleware, parentEnhancer]\n );\n\n const useBuildComponentCallback = useCallback<UseBuildComponentCallback<Request, Props>>(\n (request, options = {}) => enhancer(() => options.fallbackComponent)(request),\n [enhancer]\n );\n\n const contextValue = useMemo<ProviderContext<Request, Props>>(\n () => ({ enhancer, useBuildComponentCallback }),\n [enhancer, useBuildComponentCallback]\n );\n\n return <context.Provider value={contextValue}>{children}</context.Provider>;\n }\n\n const useBuildComponentCallback = () => useContext(context).useBuildComponentCallback;\n\n function Proxy({ children, fallbackComponent, request, ...props }: ProxyProps<Request, Props>) {\n const enhancer = useBuildComponentCallback();\n const Component = enhancer(request as Request, { fallbackComponent });\n\n return Component ? <Component {...(props as Props)}>{children}</Component> : null;\n }\n\n const asMiddleware: (\n middlewareComponent: ComponentType<MiddlewareComponentProps<Request, Props, Init>>\n ) => ComponentMiddleware<Request, Props, Init> =\n (\n MiddlewareComponent: ComponentType<MiddlewareComponentProps<Request, Props, Init>>\n ): ComponentMiddleware<Request, Props, Init> =>\n init =>\n next =>\n request => {\n const RawNextComponent = next(request);\n\n // TODO: Can we pre-build this component during init?\n const MiddlewareOf = (props: Props) => {\n const middleware = useMemo(\n () =>\n Object.freeze({\n init,\n Next: memo<Partial<Props>>(\n RawNextComponent\n ? (overridingProps: Partial<Props>) => <RawNextComponent {...props} {...overridingProps} />\n : () => null\n ),\n request\n }),\n []\n );\n\n return <MiddlewareComponent {...props} middleware={middleware} />;\n };\n\n MiddlewareOf.displayName = `MiddlewareOf<${MiddlewareComponent.displayName || ''}>`;\n\n return memo<Props>(MiddlewareOf);\n };\n\n return {\n asMiddleware,\n Provider: memo<ProviderProps<Request, Props, Init>>(ChainOfResponsibilityProvider),\n Proxy: memo<ProxyProps<Request, Props>>(Proxy),\n types: {\n middlewareComponentProps: undefined as unknown as MiddlewareComponentProps<Request, Props, Init>,\n init: undefined as unknown as Init,\n middleware: undefined as unknown as ComponentMiddleware<Request, Props, Init>,\n props: undefined as unknown as Props,\n request: undefined as unknown as Request\n },\n useBuildComponentCallback\n };\n}\n","import {\n type ComponentClass,\n type ComponentType,\n type Consumer,\n type Fragment,\n type FunctionComponent,\n type Provider\n} from 'react';\n\nfunction isConsumer(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is Consumer<unknown> {\n return component?.$$typeof?.toString() === 'Symbol(react.context)';\n}\n\nfunction isProvider(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is Provider<unknown> {\n return component?.$$typeof?.toString() === 'Symbol(react.provider)';\n}\n\nfunction isFragment(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is typeof Fragment {\n return component?.toString() === 'Symbol(react.fragment)';\n}\n\nfunction isFunctionComponent(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is FunctionComponent {\n if (typeof component === 'function') {\n return true;\n }\n\n return isPureFunctionComponent(component);\n}\n\nfunction isPureFunctionComponent(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is FunctionComponent {\n return component?.$$typeof?.toString() === 'Symbol(react.memo)' && isFunctionComponent(component.type);\n}\n\nfunction isComponentClass(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is ComponentClass {\n return typeof component === 'object' && typeof component?.['render'] === 'function';\n}\n\n// There are no definitive ways to check if an object is a React component or not.\n// We are checking if the object has a render function (classic component).\n// Note: \"forwardRef()\" returns plain object, not class instance.\nexport default function isReactComponent(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is ComponentType {\n return (\n isFunctionComponent(component) ||\n isComponentClass(component) ||\n isFragment(component) ||\n isConsumer(component) ||\n isProvider(component)\n );\n}\n","// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Fn<P extends any[], R> = (...args: P) => R;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Enhancer<P extends any[], R> = (next: Fn<P, R>) => Fn<P, R>;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport default function compose<P extends any[], R>(...fns: Enhancer<P, R>[]): Enhancer<P, R> {\n return (fn: Fn<P, R>): Fn<P, R> => fns.reduce((chain, fn) => fn(chain), fn);\n}\n","import compose from './compose.ts';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Fn<P extends any[], R> = (...args: P) => R;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type Enhancer<P extends any[], R> = (next: Fn<P, R>) => Fn<P, R>;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type Middleware<P extends any[], R, S extends any[]> = (...init: S) => Enhancer<P, R>;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport default function applyMiddleware<P extends any[], R, S extends any[]>(\n ...arrayOfMiddleware: Middleware<P, R, S>[]\n) {\n return (...init: S) => {\n const chain = arrayOfMiddleware.map(middleware => middleware(...init));\n\n return compose(...chain);\n };\n}\n"],"mappings":"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","names":["fn","options","middleware","init","enhancer","useBuildComponentCallback"]}
@@ -26,7 +26,7 @@ type ProviderProps<Request, Props, Init> = PropsWithChildren<{
26
26
  } : {
27
27
  init: Init;
28
28
  });
29
- type ProxyProps<Request, Props> = PropsWithChildren<Request extends never | void ? Props & {
29
+ type ProxyProps<Request, Props extends object> = PropsWithChildren<Request extends never | void ? Props & {
30
30
  fallbackComponent?: ComponentType<Props>;
31
31
  request?: undefined;
32
32
  } : Request extends undefined ? Props & {
@@ -45,14 +45,24 @@ type Options = {
45
45
  */
46
46
  passModifiedRequest?: boolean;
47
47
  };
48
- declare function createChainOfResponsibility<Request = undefined, Props = {
48
+ type MiddlewareProps<Request, Props, Init> = Readonly<{
49
+ init: Init;
50
+ Next: ComponentType<Partial<Props>>;
51
+ request: Request;
52
+ }>;
53
+ type MiddlewareComponentProps<Request, Props, Init> = Props & Readonly<{
54
+ middleware: MiddlewareProps<Request, Props, Init>;
55
+ }>;
56
+ declare function createChainOfResponsibility<Request = undefined, Props extends object = Readonly<{
49
57
  children?: never;
50
- }, Init = undefined>(options?: Options): {
58
+ }>, Init = undefined>(options?: Options): {
59
+ asMiddleware: (middlewareComponent: ComponentType<MiddlewareComponentProps<Request, Props, Init>>) => ComponentMiddleware<Request, Props, Init>;
51
60
  Provider: ComponentType<ProviderProps<Request, Props, Init>>;
52
61
  Proxy: ComponentType<ProxyProps<Request, Props>>;
53
62
  types: {
54
63
  init: Init;
55
64
  middleware: ComponentMiddleware<Request, Props, Init>;
65
+ middlewareComponentProps: MiddlewareComponentProps<Request, Props, Init>;
56
66
  props: Props;
57
67
  request: Request;
58
68
  };
@@ -26,7 +26,7 @@ type ProviderProps<Request, Props, Init> = PropsWithChildren<{
26
26
  } : {
27
27
  init: Init;
28
28
  });
29
- type ProxyProps<Request, Props> = PropsWithChildren<Request extends never | void ? Props & {
29
+ type ProxyProps<Request, Props extends object> = PropsWithChildren<Request extends never | void ? Props & {
30
30
  fallbackComponent?: ComponentType<Props>;
31
31
  request?: undefined;
32
32
  } : Request extends undefined ? Props & {
@@ -45,14 +45,24 @@ type Options = {
45
45
  */
46
46
  passModifiedRequest?: boolean;
47
47
  };
48
- declare function createChainOfResponsibility<Request = undefined, Props = {
48
+ type MiddlewareProps<Request, Props, Init> = Readonly<{
49
+ init: Init;
50
+ Next: ComponentType<Partial<Props>>;
51
+ request: Request;
52
+ }>;
53
+ type MiddlewareComponentProps<Request, Props, Init> = Props & Readonly<{
54
+ middleware: MiddlewareProps<Request, Props, Init>;
55
+ }>;
56
+ declare function createChainOfResponsibility<Request = undefined, Props extends object = Readonly<{
49
57
  children?: never;
50
- }, Init = undefined>(options?: Options): {
58
+ }>, Init = undefined>(options?: Options): {
59
+ asMiddleware: (middlewareComponent: ComponentType<MiddlewareComponentProps<Request, Props, Init>>) => ComponentMiddleware<Request, Props, Init>;
51
60
  Provider: ComponentType<ProviderProps<Request, Props, Init>>;
52
61
  Proxy: ComponentType<ProxyProps<Request, Props>>;
53
62
  types: {
54
63
  init: Init;
55
64
  middleware: ComponentMiddleware<Request, Props, Init>;
65
+ middlewareComponentProps: MiddlewareComponentProps<Request, Props, Init>;
56
66
  props: Props;
57
67
  request: Request;
58
68
  };
@@ -38,7 +38,6 @@ module.exports = __toCommonJS(index_fluentUI_exports);
38
38
  var import_react2 = __toESM(require("react"));
39
39
 
40
40
  // src/createChainOfResponsibility.tsx
41
- var import_prop_types = __toESM(require("prop-types"));
42
41
  var import_react = __toESM(require("react"));
43
42
 
44
43
  // src/isReactComponent.ts
@@ -97,7 +96,7 @@ function createChainOfResponsibility(options = {}) {
97
96
  }
98
97
  };
99
98
  const context = (0, import_react.createContext)(defaultUseBuildComponentCallback);
100
- const Provider = ({ children, init, middleware }) => {
99
+ function ChainOfResponsibilityProvider({ children, init, middleware }) {
101
100
  if (!Array.isArray(middleware) || middleware.some((middleware2) => typeof middleware2 !== "function")) {
102
101
  throw new Error("middleware prop must be an array of functions");
103
102
  }
@@ -148,32 +147,37 @@ function createChainOfResponsibility(options = {}) {
148
147
  [enhancer, useBuildComponentCallback2]
149
148
  );
150
149
  return /* @__PURE__ */ import_react.default.createElement(context.Provider, { value: contextValue }, children);
151
- };
152
- Provider.displayName = "ChainOfResponsibilityProvider";
153
- Provider.propTypes = {
154
- children: import_prop_types.default.any,
155
- init: import_prop_types.default.any,
156
- middleware: import_prop_types.default.any
157
- };
150
+ }
158
151
  const useBuildComponentCallback = () => (0, import_react.useContext)(context).useBuildComponentCallback;
159
- const Proxy2 = (0, import_react.memo)(
160
- // False positive: "children" is not a prop.
161
- // eslint-disable-next-line react/prop-types
162
- ({ children, fallbackComponent, request, ...props }) => {
163
- const enhancer = useBuildComponentCallback();
164
- const Component = enhancer(request, { fallbackComponent });
165
- return Component ? /* @__PURE__ */ import_react.default.createElement(Component, { ...props }, children) : null;
166
- }
167
- );
168
- Proxy2.displayName = "Proxy";
169
- Proxy2.propTypes = {
170
- fallbackComponent: import_prop_types.default.any,
171
- request: import_prop_types.default.any
152
+ function Proxy2({ children, fallbackComponent, request, ...props }) {
153
+ const enhancer = useBuildComponentCallback();
154
+ const Component = enhancer(request, { fallbackComponent });
155
+ return Component ? /* @__PURE__ */ import_react.default.createElement(Component, { ...props }, children) : null;
156
+ }
157
+ const asMiddleware = (MiddlewareComponent) => (init) => (next) => (request) => {
158
+ const RawNextComponent = next(request);
159
+ const MiddlewareOf = (props) => {
160
+ const middleware = (0, import_react.useMemo)(
161
+ () => Object.freeze({
162
+ init,
163
+ Next: (0, import_react.memo)(
164
+ RawNextComponent ? (overridingProps) => /* @__PURE__ */ import_react.default.createElement(RawNextComponent, { ...props, ...overridingProps }) : () => null
165
+ ),
166
+ request
167
+ }),
168
+ []
169
+ );
170
+ return /* @__PURE__ */ import_react.default.createElement(MiddlewareComponent, { ...props, middleware });
171
+ };
172
+ MiddlewareOf.displayName = `MiddlewareOf<${MiddlewareComponent.displayName || ""}>`;
173
+ return (0, import_react.memo)(MiddlewareOf);
172
174
  };
173
175
  return {
174
- Provider,
175
- Proxy: Proxy2,
176
+ asMiddleware,
177
+ Provider: (0, import_react.memo)(ChainOfResponsibilityProvider),
178
+ Proxy: (0, import_react.memo)(Proxy2),
176
179
  types: {
180
+ middlewareComponentProps: void 0,
177
181
  init: void 0,
178
182
  middleware: void 0,
179
183
  props: void 0,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.fluentUI.ts","../src/createChainOfResponsibilityForFluentUI.tsx","../src/createChainOfResponsibility.tsx","../src/isReactComponent.ts","../src/private/compose.ts","../src/private/applyMiddleware.ts"],"sourcesContent":["import createChainOfResponsibilityForFluentUI from './createChainOfResponsibilityForFluentUI.tsx';\n\nexport { createChainOfResponsibilityForFluentUI };\n","import { type IRenderFunction } from '@fluentui/react';\nimport React, { useCallback, type Key } from 'react';\n\nimport createChainOfResponsibility from './createChainOfResponsibility.tsx';\n\ntype UseBuildRenderFunctionOptions<Props> = { getKey?: (props: Props | undefined) => Key };\n\ntype UseBuildRenderFunction<Props> = (options?: UseBuildRenderFunctionOptions<Props>) => IRenderFunction<Props>;\n\n// We are using the props as both \"Request\" and \"Props\".\n// This should eases migration from `onRender` to chain of responsibility.\n// Downside is, web developers could accidentally pass request as props and not honoring props modified by upstreamers.\nexport default function createChainOfResponsibilityForFluentUI<Props extends object, Init = undefined>(\n options?: Parameters<typeof createChainOfResponsibility>[0]\n): ReturnType<typeof createChainOfResponsibility<Props | undefined, Props, Init>> & {\n useBuildRenderFunction: UseBuildRenderFunction<Props>;\n} {\n const returnValue = createChainOfResponsibility<Props | undefined, Props, Init>(options);\n\n const { Proxy } = returnValue;\n\n const useBuildRenderFunction: UseBuildRenderFunction<Props> = (options = {}) => {\n const { getKey } = options;\n\n return useCallback<IRenderFunction<Props>>(\n (props, defaultRender) => (\n <Proxy {...(props as Props)} fallbackComponent={defaultRender} key={getKey?.(props)} request={props} />\n ),\n [getKey]\n );\n };\n\n return { ...returnValue, useBuildRenderFunction };\n}\n","import PropTypes from 'prop-types';\nimport React, {\n createContext,\n isValidElement,\n memo,\n useCallback,\n useContext,\n useMemo,\n type ComponentType,\n type PropsWithChildren\n} from 'react';\n\nimport isReactComponent from './isReactComponent.ts';\nimport applyMiddleware, { type Enhancer } from './private/applyMiddleware.ts';\nimport { type ComponentMiddleware } from './types.ts';\n\ntype ResultComponent<Props> = ComponentType<Props> | false | null | undefined;\n\ntype UseBuildComponentCallbackOptions<Props> = { fallbackComponent?: ResultComponent<Props> };\n\ntype UseBuildComponentCallback<Request, Props> = (\n request: Request,\n options?: UseBuildComponentCallbackOptions<Props>\n) => ResultComponent<Props>;\n\ntype ProviderContext<Request, Props> = {\n get enhancer(): Enhancer<[Request], ResultComponent<Props>> | undefined;\n useBuildComponentCallback: UseBuildComponentCallback<Request, Props>;\n};\n\ntype ProviderProps<Request, Props, Init> = PropsWithChildren<{\n middleware: readonly ComponentMiddleware<Request, Props, Init>[];\n}> &\n (Init extends never | void ? { init?: undefined } : Init extends undefined ? { init?: Init } : { init: Init });\n\ntype ProxyProps<Request, Props> = PropsWithChildren<\n Request extends never | void\n ? Props & { fallbackComponent?: ComponentType<Props>; request?: undefined }\n : Request extends undefined\n ? Props & { fallbackComponent?: ComponentType<Props>; request?: Request }\n : Props & { fallbackComponent?: ComponentType<Props>; request: Request }\n>;\n\ntype Options = {\n /**\n * Allows a middleware to pass another request object when calling its next middleware. Default is false.\n *\n * However, middleware could modify the request object before calling its next middleware. It is recommended\n * to use Object.freeze() to prevent middleware from modifying the request object.\n */\n passModifiedRequest?: boolean;\n};\n\nexport default function createChainOfResponsibility<\n Request = undefined,\n Props = { children?: never },\n Init = undefined\n>(\n options: Options = {}\n): {\n Provider: ComponentType<ProviderProps<Request, Props, Init>>;\n Proxy: ComponentType<ProxyProps<Request, Props>>;\n types: {\n init: Init;\n middleware: ComponentMiddleware<Request, Props, Init>;\n props: Props;\n request: Request;\n };\n useBuildComponentCallback: () => UseBuildComponentCallback<Request, Props>;\n} {\n const defaultUseBuildComponentCallback: ProviderContext<Request, Props> = {\n get enhancer() {\n return undefined;\n },\n useBuildComponentCallback(_, options) {\n if (options?.fallbackComponent) {\n return options.fallbackComponent;\n }\n\n throw new Error('This component/hook cannot be used outside of its corresponding <Provider>');\n }\n };\n\n const context = createContext<ProviderContext<Request, Props>>(defaultUseBuildComponentCallback);\n\n const Provider: ComponentType<ProviderProps<Request, Props, Init>> = ({ children, init, middleware }) => {\n // TODO: Related to https://github.com/microsoft/TypeScript/issues/17002.\n // typescript@5.2.2 has a bug, Array.isArray() is a type predicate but only works with mutable array, not readonly array.\n // After removing \"as unknown\", `middleware` on the next line become `any[]`.\n if (!Array.isArray(middleware as unknown) || middleware.some(middleware => typeof middleware !== 'function')) {\n throw new Error('middleware prop must be an array of functions');\n }\n\n const patchedMiddleware: readonly ComponentMiddleware<Request, Props, Init>[] = Object.freeze(\n middleware\n ? middleware.map(fn => (init: Init) => {\n const enhancer = fn(init);\n\n return (next: UseBuildComponentCallback<Request, Props>) => (originalRequest: Request) => {\n // False positive: although we did not re-assign the variable from true, it was initialized as undefined.\n // eslint-disable-next-line prefer-const\n let hasReturned: boolean;\n\n const returnValue = enhancer(nextRequest => {\n if (hasReturned) {\n throw new Error('next() cannot be called after the function had returned synchronously');\n }\n\n !options.passModifiedRequest &&\n nextRequest !== originalRequest &&\n console.warn(\n 'react-chain-of-responsibility: \"options.passModifiedRequest\" must be set to true to pass a different request object to next().'\n );\n\n return next(options.passModifiedRequest ? nextRequest : originalRequest);\n })(originalRequest);\n\n hasReturned = true;\n\n if (isValidElement(returnValue)) {\n throw new Error('middleware must not return React element directly');\n } else if (\n returnValue !== false &&\n returnValue !== null &&\n typeof returnValue !== 'undefined' &&\n !isReactComponent(returnValue)\n ) {\n throw new Error(\n 'middleware must return false, null, undefined, function component, or class component'\n );\n }\n\n return returnValue;\n };\n })\n : []\n );\n\n const { enhancer: parentEnhancer } = useContext(context);\n\n const enhancer = useMemo(\n () =>\n // We are reversing because it is easier to read:\n // - With reverse, [a, b, c] will become a(b(c(fn)))\n // - Without reverse, [a, b, c] will become c(b(a(fn)))\n applyMiddleware<[Request], ResultComponent<Props>, [Init]>(\n ...[...patchedMiddleware, ...(parentEnhancer ? [() => parentEnhancer] : [])].reverse()\n )(init as Init),\n [init, middleware, parentEnhancer]\n );\n\n const useBuildComponentCallback = useCallback<UseBuildComponentCallback<Request, Props>>(\n (request, options = {}) => enhancer(() => options.fallbackComponent)(request),\n [enhancer]\n );\n\n const contextValue = useMemo<ProviderContext<Request, Props>>(\n () => ({ enhancer, useBuildComponentCallback }),\n [enhancer, useBuildComponentCallback]\n );\n\n return <context.Provider value={contextValue}>{children}</context.Provider>;\n };\n\n Provider.displayName = 'ChainOfResponsibilityProvider';\n Provider.propTypes = {\n children: PropTypes.any,\n init: PropTypes.any,\n middleware: PropTypes.any\n };\n\n const useBuildComponentCallback = () => useContext(context).useBuildComponentCallback;\n\n const Proxy: ComponentType<ProxyProps<Request, Props>> = memo(\n // False positive: \"children\" is not a prop.\n // eslint-disable-next-line react/prop-types\n ({ children, fallbackComponent, request, ...props }) => {\n const enhancer = useBuildComponentCallback();\n const Component = enhancer(request as Request, { fallbackComponent });\n\n return Component ? <Component {...(props as Props)}>{children}</Component> : null;\n }\n );\n\n Proxy.displayName = 'Proxy';\n Proxy.propTypes = {\n fallbackComponent: PropTypes.any,\n request: PropTypes.any\n };\n\n return {\n Provider,\n Proxy,\n types: {\n init: undefined as unknown as Init,\n middleware: undefined as unknown as ComponentMiddleware<Request, Props, Init>,\n props: undefined as unknown as Props,\n request: undefined as unknown as Request\n },\n useBuildComponentCallback\n };\n}\n","import {\n type ComponentClass,\n type ComponentType,\n type Consumer,\n type Fragment,\n type FunctionComponent,\n type Provider\n} from 'react';\n\nfunction isConsumer(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is Consumer<unknown> {\n return component?.$$typeof?.toString() === 'Symbol(react.context)';\n}\n\nfunction isProvider(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is Provider<unknown> {\n return component?.$$typeof?.toString() === 'Symbol(react.provider)';\n}\n\nfunction isFragment(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is typeof Fragment {\n return component?.toString() === 'Symbol(react.fragment)';\n}\n\nfunction isFunctionComponent(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is FunctionComponent {\n if (typeof component === 'function') {\n return true;\n }\n\n return isPureFunctionComponent(component);\n}\n\nfunction isPureFunctionComponent(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is FunctionComponent {\n return component?.$$typeof?.toString() === 'Symbol(react.memo)' && isFunctionComponent(component.type);\n}\n\nfunction isComponentClass(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is ComponentClass {\n return typeof component === 'object' && typeof component?.['render'] === 'function';\n}\n\n// There are no definitive ways to check if an object is a React component or not.\n// We are checking if the object has a render function (classic component).\n// Note: \"forwardRef()\" returns plain object, not class instance.\nexport default function isReactComponent(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is ComponentType {\n return (\n isFunctionComponent(component) ||\n isComponentClass(component) ||\n isFragment(component) ||\n isConsumer(component) ||\n isProvider(component)\n );\n}\n","// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Fn<P extends any[], R> = (...args: P) => R;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Enhancer<P extends any[], R> = (next: Fn<P, R>) => Fn<P, R>;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport default function compose<P extends any[], R>(...fns: Enhancer<P, R>[]): Enhancer<P, R> {\n return (fn: Fn<P, R>): Fn<P, R> => fns.reduce((chain, fn) => fn(chain), fn);\n}\n","import compose from './compose.ts';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Fn<P extends any[], R> = (...args: P) => R;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type Enhancer<P extends any[], R> = (next: Fn<P, R>) => Fn<P, R>;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type Middleware<P extends any[], R, S extends any[]> = (...init: S) => Enhancer<P, R>;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport default function applyMiddleware<P extends any[], R, S extends any[]>(\n ...arrayOfMiddleware: Middleware<P, R, S>[]\n) {\n return (...init: S) => {\n const chain = arrayOfMiddleware.map(middleware => middleware(...init));\n\n return compose(...chain);\n };\n}\n"],"mappings":"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","names":["import_react","fn","options","middleware","init","enhancer","useBuildComponentCallback","React","PropTypes","Proxy","Proxy","options","React"]}
1
+ {"version":3,"sources":["../src/index.fluentUI.ts","../src/createChainOfResponsibilityForFluentUI.tsx","../src/createChainOfResponsibility.tsx","../src/isReactComponent.ts","../src/private/compose.ts","../src/private/applyMiddleware.ts"],"sourcesContent":["import createChainOfResponsibilityForFluentUI from './createChainOfResponsibilityForFluentUI.tsx';\n\nexport { createChainOfResponsibilityForFluentUI };\n","import { type IRenderFunction } from '@fluentui/react';\nimport React, { useCallback, type Key } from 'react';\n\nimport createChainOfResponsibility from './createChainOfResponsibility.tsx';\n\ntype UseBuildRenderFunctionOptions<Props> = { getKey?: (props: Props | undefined) => Key };\n\ntype UseBuildRenderFunction<Props> = (options?: UseBuildRenderFunctionOptions<Props>) => IRenderFunction<Props>;\n\n// We are using the props as both \"Request\" and \"Props\".\n// This should eases migration from `onRender` to chain of responsibility.\n// Downside is, web developers could accidentally pass request as props and not honoring props modified by upstreamers.\nexport default function createChainOfResponsibilityForFluentUI<Props extends object, Init = undefined>(\n options?: Parameters<typeof createChainOfResponsibility>[0]\n): ReturnType<typeof createChainOfResponsibility<Props | undefined, Props, Init>> & {\n useBuildRenderFunction: UseBuildRenderFunction<Props>;\n} {\n const returnValue = createChainOfResponsibility<Props | undefined, Props, Init>(options);\n\n const { Proxy } = returnValue;\n\n const useBuildRenderFunction: UseBuildRenderFunction<Props> = (options = {}) => {\n const { getKey } = options;\n\n return useCallback<IRenderFunction<Props>>(\n (props, defaultRender) => (\n <Proxy {...(props as Props)} fallbackComponent={defaultRender} key={getKey?.(props)} request={props} />\n ),\n [getKey]\n );\n };\n\n return { ...returnValue, useBuildRenderFunction };\n}\n","import React, {\n createContext,\n isValidElement,\n memo,\n useCallback,\n useContext,\n useMemo,\n type ComponentType,\n type PropsWithChildren\n} from 'react';\n\nimport isReactComponent from './isReactComponent.ts';\nimport applyMiddleware, { type Enhancer } from './private/applyMiddleware.ts';\nimport { type ComponentMiddleware } from './types.ts';\n\n// TODO: Simplify to ComponentType<Props> | undefined.\ntype ResultComponent<Props> = ComponentType<Props> | false | null | undefined;\n\ntype UseBuildComponentCallbackOptions<Props> = { fallbackComponent?: ResultComponent<Props> };\n\ntype UseBuildComponentCallback<Request, Props> = (\n request: Request,\n options?: UseBuildComponentCallbackOptions<Props>\n) => ResultComponent<Props>;\n\ntype ProviderContext<Request, Props> = {\n get enhancer(): Enhancer<[Request], ResultComponent<Props>> | undefined;\n useBuildComponentCallback: UseBuildComponentCallback<Request, Props>;\n};\n\ntype ProviderProps<Request, Props, Init> = PropsWithChildren<{\n middleware: readonly ComponentMiddleware<Request, Props, Init>[];\n}> &\n (Init extends never | void ? { init?: undefined } : Init extends undefined ? { init?: Init } : { init: Init });\n\ntype ProxyProps<Request, Props extends object> = PropsWithChildren<\n Request extends never | void\n ? Props & { fallbackComponent?: ComponentType<Props>; request?: undefined }\n : Request extends undefined\n ? Props & { fallbackComponent?: ComponentType<Props>; request?: Request }\n : Props & { fallbackComponent?: ComponentType<Props>; request: Request }\n>;\n\ntype Options = {\n /**\n * Allows a middleware to pass another request object when calling its next middleware. Default is false.\n *\n * However, middleware could modify the request object before calling its next middleware. It is recommended\n * to use Object.freeze() to prevent middleware from modifying the request object.\n */\n passModifiedRequest?: boolean;\n};\n\ntype MiddlewareProps<Request, Props, Init> = Readonly<{\n init: Init;\n Next: ComponentType<Partial<Props>>;\n request: Request;\n}>;\n\ntype MiddlewareComponentProps<Request, Props, Init> = Props &\n Readonly<{ middleware: MiddlewareProps<Request, Props, Init> }>;\n\nexport default function createChainOfResponsibility<\n Request = undefined,\n Props extends object = Readonly<{ children?: never }>,\n Init = undefined\n>(\n options: Options = {}\n): {\n asMiddleware: (\n middlewareComponent: ComponentType<MiddlewareComponentProps<Request, Props, Init>>\n ) => ComponentMiddleware<Request, Props, Init>;\n Provider: ComponentType<ProviderProps<Request, Props, Init>>;\n Proxy: ComponentType<ProxyProps<Request, Props>>;\n types: {\n init: Init;\n middleware: ComponentMiddleware<Request, Props, Init>;\n middlewareComponentProps: MiddlewareComponentProps<Request, Props, Init>;\n props: Props;\n request: Request;\n };\n useBuildComponentCallback: () => UseBuildComponentCallback<Request, Props>;\n} {\n const defaultUseBuildComponentCallback: ProviderContext<Request, Props> = {\n get enhancer() {\n return undefined;\n },\n useBuildComponentCallback(_, options) {\n if (options?.fallbackComponent) {\n return options.fallbackComponent;\n }\n\n throw new Error('This component/hook cannot be used outside of its corresponding <Provider>');\n }\n };\n\n const context = createContext<ProviderContext<Request, Props>>(defaultUseBuildComponentCallback);\n\n function ChainOfResponsibilityProvider({ children, init, middleware }: ProviderProps<Request, Props, Init>) {\n // TODO: Related to https://github.com/microsoft/TypeScript/issues/17002.\n // typescript@5.2.2 has a bug, Array.isArray() is a type predicate but only works with mutable array, not readonly array.\n // After removing \"as unknown\", `middleware` on the next line become `any[]`.\n if (!Array.isArray(middleware as unknown) || middleware.some(middleware => typeof middleware !== 'function')) {\n throw new Error('middleware prop must be an array of functions');\n }\n\n const patchedMiddleware: readonly ComponentMiddleware<Request, Props, Init>[] = Object.freeze(\n middleware\n ? middleware.map(fn => (init: Init) => {\n const enhancer = fn(init);\n\n return (next: UseBuildComponentCallback<Request, Props>) => (originalRequest: Request) => {\n // False positive: although we did not re-assign the variable from true, it was initialized as undefined.\n // eslint-disable-next-line prefer-const\n let hasReturned: boolean;\n\n const returnValue = enhancer(nextRequest => {\n if (hasReturned) {\n throw new Error('next() cannot be called after the function had returned synchronously');\n }\n\n !options.passModifiedRequest &&\n nextRequest !== originalRequest &&\n console.warn(\n 'react-chain-of-responsibility: \"options.passModifiedRequest\" must be set to true to pass a different request object to next().'\n );\n\n return next(options.passModifiedRequest ? nextRequest : originalRequest);\n })(originalRequest);\n\n hasReturned = true;\n\n if (isValidElement(returnValue)) {\n throw new Error('middleware must not return React element directly');\n } else if (\n returnValue !== false &&\n returnValue !== null &&\n typeof returnValue !== 'undefined' &&\n !isReactComponent(returnValue)\n ) {\n throw new Error(\n 'middleware must return false, null, undefined, function component, or class component'\n );\n }\n\n return returnValue;\n };\n })\n : []\n );\n\n const { enhancer: parentEnhancer } = useContext(context);\n\n const enhancer = useMemo(\n () =>\n // We are reversing because it is easier to read:\n // - With reverse, [a, b, c] will become a(b(c(fn)))\n // - Without reverse, [a, b, c] will become c(b(a(fn)))\n applyMiddleware<[Request], ResultComponent<Props>, [Init]>(\n ...[...patchedMiddleware, ...(parentEnhancer ? [() => parentEnhancer] : [])].reverse()\n )(init as Init),\n [init, middleware, parentEnhancer]\n );\n\n const useBuildComponentCallback = useCallback<UseBuildComponentCallback<Request, Props>>(\n (request, options = {}) => enhancer(() => options.fallbackComponent)(request),\n [enhancer]\n );\n\n const contextValue = useMemo<ProviderContext<Request, Props>>(\n () => ({ enhancer, useBuildComponentCallback }),\n [enhancer, useBuildComponentCallback]\n );\n\n return <context.Provider value={contextValue}>{children}</context.Provider>;\n }\n\n const useBuildComponentCallback = () => useContext(context).useBuildComponentCallback;\n\n function Proxy({ children, fallbackComponent, request, ...props }: ProxyProps<Request, Props>) {\n const enhancer = useBuildComponentCallback();\n const Component = enhancer(request as Request, { fallbackComponent });\n\n return Component ? <Component {...(props as Props)}>{children}</Component> : null;\n }\n\n const asMiddleware: (\n middlewareComponent: ComponentType<MiddlewareComponentProps<Request, Props, Init>>\n ) => ComponentMiddleware<Request, Props, Init> =\n (\n MiddlewareComponent: ComponentType<MiddlewareComponentProps<Request, Props, Init>>\n ): ComponentMiddleware<Request, Props, Init> =>\n init =>\n next =>\n request => {\n const RawNextComponent = next(request);\n\n // TODO: Can we pre-build this component during init?\n const MiddlewareOf = (props: Props) => {\n const middleware = useMemo(\n () =>\n Object.freeze({\n init,\n Next: memo<Partial<Props>>(\n RawNextComponent\n ? (overridingProps: Partial<Props>) => <RawNextComponent {...props} {...overridingProps} />\n : () => null\n ),\n request\n }),\n []\n );\n\n return <MiddlewareComponent {...props} middleware={middleware} />;\n };\n\n MiddlewareOf.displayName = `MiddlewareOf<${MiddlewareComponent.displayName || ''}>`;\n\n return memo<Props>(MiddlewareOf);\n };\n\n return {\n asMiddleware,\n Provider: memo<ProviderProps<Request, Props, Init>>(ChainOfResponsibilityProvider),\n Proxy: memo<ProxyProps<Request, Props>>(Proxy),\n types: {\n middlewareComponentProps: undefined as unknown as MiddlewareComponentProps<Request, Props, Init>,\n init: undefined as unknown as Init,\n middleware: undefined as unknown as ComponentMiddleware<Request, Props, Init>,\n props: undefined as unknown as Props,\n request: undefined as unknown as Request\n },\n useBuildComponentCallback\n };\n}\n","import {\n type ComponentClass,\n type ComponentType,\n type Consumer,\n type Fragment,\n type FunctionComponent,\n type Provider\n} from 'react';\n\nfunction isConsumer(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is Consumer<unknown> {\n return component?.$$typeof?.toString() === 'Symbol(react.context)';\n}\n\nfunction isProvider(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is Provider<unknown> {\n return component?.$$typeof?.toString() === 'Symbol(react.provider)';\n}\n\nfunction isFragment(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is typeof Fragment {\n return component?.toString() === 'Symbol(react.fragment)';\n}\n\nfunction isFunctionComponent(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is FunctionComponent {\n if (typeof component === 'function') {\n return true;\n }\n\n return isPureFunctionComponent(component);\n}\n\nfunction isPureFunctionComponent(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is FunctionComponent {\n return component?.$$typeof?.toString() === 'Symbol(react.memo)' && isFunctionComponent(component.type);\n}\n\nfunction isComponentClass(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is ComponentClass {\n return typeof component === 'object' && typeof component?.['render'] === 'function';\n}\n\n// There are no definitive ways to check if an object is a React component or not.\n// We are checking if the object has a render function (classic component).\n// Note: \"forwardRef()\" returns plain object, not class instance.\nexport default function isReactComponent(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is ComponentType {\n return (\n isFunctionComponent(component) ||\n isComponentClass(component) ||\n isFragment(component) ||\n isConsumer(component) ||\n isProvider(component)\n );\n}\n","// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Fn<P extends any[], R> = (...args: P) => R;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Enhancer<P extends any[], R> = (next: Fn<P, R>) => Fn<P, R>;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport default function compose<P extends any[], R>(...fns: Enhancer<P, R>[]): Enhancer<P, R> {\n return (fn: Fn<P, R>): Fn<P, R> => fns.reduce((chain, fn) => fn(chain), fn);\n}\n","import compose from './compose.ts';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Fn<P extends any[], R> = (...args: P) => R;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type Enhancer<P extends any[], R> = (next: Fn<P, R>) => Fn<P, R>;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type Middleware<P extends any[], R, S extends any[]> = (...init: S) => Enhancer<P, R>;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport default function applyMiddleware<P extends any[], R, S extends any[]>(\n ...arrayOfMiddleware: Middleware<P, R, S>[]\n) {\n return (...init: S) => {\n const chain = arrayOfMiddleware.map(middleware => middleware(...init));\n\n return compose(...chain);\n };\n}\n"],"mappings":"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","names":["import_react","fn","options","middleware","init","enhancer","useBuildComponentCallback","React","Proxy","Proxy","options","React"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createChainOfResponsibility
3
- } from "./chunk-LQNXZPUH.mjs";
3
+ } from "./chunk-TT7FCTZA.mjs";
4
4
 
5
5
  // src/createChainOfResponsibilityForFluentUI.tsx
6
6
  import React, { useCallback } from "react";
@@ -35,7 +35,6 @@ __export(src_exports, {
35
35
  module.exports = __toCommonJS(src_exports);
36
36
 
37
37
  // src/createChainOfResponsibility.tsx
38
- var import_prop_types = __toESM(require("prop-types"));
39
38
  var import_react = __toESM(require("react"));
40
39
 
41
40
  // src/isReactComponent.ts
@@ -94,7 +93,7 @@ function createChainOfResponsibility(options = {}) {
94
93
  }
95
94
  };
96
95
  const context = (0, import_react.createContext)(defaultUseBuildComponentCallback);
97
- const Provider = ({ children, init, middleware }) => {
96
+ function ChainOfResponsibilityProvider({ children, init, middleware }) {
98
97
  if (!Array.isArray(middleware) || middleware.some((middleware2) => typeof middleware2 !== "function")) {
99
98
  throw new Error("middleware prop must be an array of functions");
100
99
  }
@@ -145,32 +144,37 @@ function createChainOfResponsibility(options = {}) {
145
144
  [enhancer, useBuildComponentCallback2]
146
145
  );
147
146
  return /* @__PURE__ */ import_react.default.createElement(context.Provider, { value: contextValue }, children);
148
- };
149
- Provider.displayName = "ChainOfResponsibilityProvider";
150
- Provider.propTypes = {
151
- children: import_prop_types.default.any,
152
- init: import_prop_types.default.any,
153
- middleware: import_prop_types.default.any
154
- };
147
+ }
155
148
  const useBuildComponentCallback = () => (0, import_react.useContext)(context).useBuildComponentCallback;
156
- const Proxy2 = (0, import_react.memo)(
157
- // False positive: "children" is not a prop.
158
- // eslint-disable-next-line react/prop-types
159
- ({ children, fallbackComponent, request, ...props }) => {
160
- const enhancer = useBuildComponentCallback();
161
- const Component = enhancer(request, { fallbackComponent });
162
- return Component ? /* @__PURE__ */ import_react.default.createElement(Component, { ...props }, children) : null;
163
- }
164
- );
165
- Proxy2.displayName = "Proxy";
166
- Proxy2.propTypes = {
167
- fallbackComponent: import_prop_types.default.any,
168
- request: import_prop_types.default.any
149
+ function Proxy2({ children, fallbackComponent, request, ...props }) {
150
+ const enhancer = useBuildComponentCallback();
151
+ const Component = enhancer(request, { fallbackComponent });
152
+ return Component ? /* @__PURE__ */ import_react.default.createElement(Component, { ...props }, children) : null;
153
+ }
154
+ const asMiddleware = (MiddlewareComponent) => (init) => (next) => (request) => {
155
+ const RawNextComponent = next(request);
156
+ const MiddlewareOf = (props) => {
157
+ const middleware = (0, import_react.useMemo)(
158
+ () => Object.freeze({
159
+ init,
160
+ Next: (0, import_react.memo)(
161
+ RawNextComponent ? (overridingProps) => /* @__PURE__ */ import_react.default.createElement(RawNextComponent, { ...props, ...overridingProps }) : () => null
162
+ ),
163
+ request
164
+ }),
165
+ []
166
+ );
167
+ return /* @__PURE__ */ import_react.default.createElement(MiddlewareComponent, { ...props, middleware });
168
+ };
169
+ MiddlewareOf.displayName = `MiddlewareOf<${MiddlewareComponent.displayName || ""}>`;
170
+ return (0, import_react.memo)(MiddlewareOf);
169
171
  };
170
172
  return {
171
- Provider,
172
- Proxy: Proxy2,
173
+ asMiddleware,
174
+ Provider: (0, import_react.memo)(ChainOfResponsibilityProvider),
175
+ Proxy: (0, import_react.memo)(Proxy2),
173
176
  types: {
177
+ middlewareComponentProps: void 0,
174
178
  init: void 0,
175
179
  middleware: void 0,
176
180
  props: void 0,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/createChainOfResponsibility.tsx","../src/isReactComponent.ts","../src/private/compose.ts","../src/private/applyMiddleware.ts"],"sourcesContent":["import createChainOfResponsibility from './createChainOfResponsibility.tsx';\nimport { type ComponentMiddleware } from './types.ts';\n\nexport { createChainOfResponsibility };\n\nexport type { ComponentMiddleware };\n","import PropTypes from 'prop-types';\nimport React, {\n createContext,\n isValidElement,\n memo,\n useCallback,\n useContext,\n useMemo,\n type ComponentType,\n type PropsWithChildren\n} from 'react';\n\nimport isReactComponent from './isReactComponent.ts';\nimport applyMiddleware, { type Enhancer } from './private/applyMiddleware.ts';\nimport { type ComponentMiddleware } from './types.ts';\n\ntype ResultComponent<Props> = ComponentType<Props> | false | null | undefined;\n\ntype UseBuildComponentCallbackOptions<Props> = { fallbackComponent?: ResultComponent<Props> };\n\ntype UseBuildComponentCallback<Request, Props> = (\n request: Request,\n options?: UseBuildComponentCallbackOptions<Props>\n) => ResultComponent<Props>;\n\ntype ProviderContext<Request, Props> = {\n get enhancer(): Enhancer<[Request], ResultComponent<Props>> | undefined;\n useBuildComponentCallback: UseBuildComponentCallback<Request, Props>;\n};\n\ntype ProviderProps<Request, Props, Init> = PropsWithChildren<{\n middleware: readonly ComponentMiddleware<Request, Props, Init>[];\n}> &\n (Init extends never | void ? { init?: undefined } : Init extends undefined ? { init?: Init } : { init: Init });\n\ntype ProxyProps<Request, Props> = PropsWithChildren<\n Request extends never | void\n ? Props & { fallbackComponent?: ComponentType<Props>; request?: undefined }\n : Request extends undefined\n ? Props & { fallbackComponent?: ComponentType<Props>; request?: Request }\n : Props & { fallbackComponent?: ComponentType<Props>; request: Request }\n>;\n\ntype Options = {\n /**\n * Allows a middleware to pass another request object when calling its next middleware. Default is false.\n *\n * However, middleware could modify the request object before calling its next middleware. It is recommended\n * to use Object.freeze() to prevent middleware from modifying the request object.\n */\n passModifiedRequest?: boolean;\n};\n\nexport default function createChainOfResponsibility<\n Request = undefined,\n Props = { children?: never },\n Init = undefined\n>(\n options: Options = {}\n): {\n Provider: ComponentType<ProviderProps<Request, Props, Init>>;\n Proxy: ComponentType<ProxyProps<Request, Props>>;\n types: {\n init: Init;\n middleware: ComponentMiddleware<Request, Props, Init>;\n props: Props;\n request: Request;\n };\n useBuildComponentCallback: () => UseBuildComponentCallback<Request, Props>;\n} {\n const defaultUseBuildComponentCallback: ProviderContext<Request, Props> = {\n get enhancer() {\n return undefined;\n },\n useBuildComponentCallback(_, options) {\n if (options?.fallbackComponent) {\n return options.fallbackComponent;\n }\n\n throw new Error('This component/hook cannot be used outside of its corresponding <Provider>');\n }\n };\n\n const context = createContext<ProviderContext<Request, Props>>(defaultUseBuildComponentCallback);\n\n const Provider: ComponentType<ProviderProps<Request, Props, Init>> = ({ children, init, middleware }) => {\n // TODO: Related to https://github.com/microsoft/TypeScript/issues/17002.\n // typescript@5.2.2 has a bug, Array.isArray() is a type predicate but only works with mutable array, not readonly array.\n // After removing \"as unknown\", `middleware` on the next line become `any[]`.\n if (!Array.isArray(middleware as unknown) || middleware.some(middleware => typeof middleware !== 'function')) {\n throw new Error('middleware prop must be an array of functions');\n }\n\n const patchedMiddleware: readonly ComponentMiddleware<Request, Props, Init>[] = Object.freeze(\n middleware\n ? middleware.map(fn => (init: Init) => {\n const enhancer = fn(init);\n\n return (next: UseBuildComponentCallback<Request, Props>) => (originalRequest: Request) => {\n // False positive: although we did not re-assign the variable from true, it was initialized as undefined.\n // eslint-disable-next-line prefer-const\n let hasReturned: boolean;\n\n const returnValue = enhancer(nextRequest => {\n if (hasReturned) {\n throw new Error('next() cannot be called after the function had returned synchronously');\n }\n\n !options.passModifiedRequest &&\n nextRequest !== originalRequest &&\n console.warn(\n 'react-chain-of-responsibility: \"options.passModifiedRequest\" must be set to true to pass a different request object to next().'\n );\n\n return next(options.passModifiedRequest ? nextRequest : originalRequest);\n })(originalRequest);\n\n hasReturned = true;\n\n if (isValidElement(returnValue)) {\n throw new Error('middleware must not return React element directly');\n } else if (\n returnValue !== false &&\n returnValue !== null &&\n typeof returnValue !== 'undefined' &&\n !isReactComponent(returnValue)\n ) {\n throw new Error(\n 'middleware must return false, null, undefined, function component, or class component'\n );\n }\n\n return returnValue;\n };\n })\n : []\n );\n\n const { enhancer: parentEnhancer } = useContext(context);\n\n const enhancer = useMemo(\n () =>\n // We are reversing because it is easier to read:\n // - With reverse, [a, b, c] will become a(b(c(fn)))\n // - Without reverse, [a, b, c] will become c(b(a(fn)))\n applyMiddleware<[Request], ResultComponent<Props>, [Init]>(\n ...[...patchedMiddleware, ...(parentEnhancer ? [() => parentEnhancer] : [])].reverse()\n )(init as Init),\n [init, middleware, parentEnhancer]\n );\n\n const useBuildComponentCallback = useCallback<UseBuildComponentCallback<Request, Props>>(\n (request, options = {}) => enhancer(() => options.fallbackComponent)(request),\n [enhancer]\n );\n\n const contextValue = useMemo<ProviderContext<Request, Props>>(\n () => ({ enhancer, useBuildComponentCallback }),\n [enhancer, useBuildComponentCallback]\n );\n\n return <context.Provider value={contextValue}>{children}</context.Provider>;\n };\n\n Provider.displayName = 'ChainOfResponsibilityProvider';\n Provider.propTypes = {\n children: PropTypes.any,\n init: PropTypes.any,\n middleware: PropTypes.any\n };\n\n const useBuildComponentCallback = () => useContext(context).useBuildComponentCallback;\n\n const Proxy: ComponentType<ProxyProps<Request, Props>> = memo(\n // False positive: \"children\" is not a prop.\n // eslint-disable-next-line react/prop-types\n ({ children, fallbackComponent, request, ...props }) => {\n const enhancer = useBuildComponentCallback();\n const Component = enhancer(request as Request, { fallbackComponent });\n\n return Component ? <Component {...(props as Props)}>{children}</Component> : null;\n }\n );\n\n Proxy.displayName = 'Proxy';\n Proxy.propTypes = {\n fallbackComponent: PropTypes.any,\n request: PropTypes.any\n };\n\n return {\n Provider,\n Proxy,\n types: {\n init: undefined as unknown as Init,\n middleware: undefined as unknown as ComponentMiddleware<Request, Props, Init>,\n props: undefined as unknown as Props,\n request: undefined as unknown as Request\n },\n useBuildComponentCallback\n };\n}\n","import {\n type ComponentClass,\n type ComponentType,\n type Consumer,\n type Fragment,\n type FunctionComponent,\n type Provider\n} from 'react';\n\nfunction isConsumer(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is Consumer<unknown> {\n return component?.$$typeof?.toString() === 'Symbol(react.context)';\n}\n\nfunction isProvider(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is Provider<unknown> {\n return component?.$$typeof?.toString() === 'Symbol(react.provider)';\n}\n\nfunction isFragment(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is typeof Fragment {\n return component?.toString() === 'Symbol(react.fragment)';\n}\n\nfunction isFunctionComponent(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is FunctionComponent {\n if (typeof component === 'function') {\n return true;\n }\n\n return isPureFunctionComponent(component);\n}\n\nfunction isPureFunctionComponent(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is FunctionComponent {\n return component?.$$typeof?.toString() === 'Symbol(react.memo)' && isFunctionComponent(component.type);\n}\n\nfunction isComponentClass(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is ComponentClass {\n return typeof component === 'object' && typeof component?.['render'] === 'function';\n}\n\n// There are no definitive ways to check if an object is a React component or not.\n// We are checking if the object has a render function (classic component).\n// Note: \"forwardRef()\" returns plain object, not class instance.\nexport default function isReactComponent(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is ComponentType {\n return (\n isFunctionComponent(component) ||\n isComponentClass(component) ||\n isFragment(component) ||\n isConsumer(component) ||\n isProvider(component)\n );\n}\n","// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Fn<P extends any[], R> = (...args: P) => R;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Enhancer<P extends any[], R> = (next: Fn<P, R>) => Fn<P, R>;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport default function compose<P extends any[], R>(...fns: Enhancer<P, R>[]): Enhancer<P, R> {\n return (fn: Fn<P, R>): Fn<P, R> => fns.reduce((chain, fn) => fn(chain), fn);\n}\n","import compose from './compose.ts';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Fn<P extends any[], R> = (...args: P) => R;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type Enhancer<P extends any[], R> = (next: Fn<P, R>) => Fn<P, R>;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type Middleware<P extends any[], R, S extends any[]> = (...init: S) => Enhancer<P, R>;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport default function applyMiddleware<P extends any[], R, S extends any[]>(\n ...arrayOfMiddleware: Middleware<P, R, S>[]\n) {\n return (...init: S) => {\n const chain = arrayOfMiddleware.map(middleware => middleware(...init));\n\n return compose(...chain);\n };\n}\n"],"mappings":"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","names":["fn","options","middleware","init","enhancer","useBuildComponentCallback","React","PropTypes","Proxy"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/createChainOfResponsibility.tsx","../src/isReactComponent.ts","../src/private/compose.ts","../src/private/applyMiddleware.ts"],"sourcesContent":["export { default as createChainOfResponsibility } from './createChainOfResponsibility.tsx';\nexport { type ComponentMiddleware } from './types.ts';\n","import React, {\n createContext,\n isValidElement,\n memo,\n useCallback,\n useContext,\n useMemo,\n type ComponentType,\n type PropsWithChildren\n} from 'react';\n\nimport isReactComponent from './isReactComponent.ts';\nimport applyMiddleware, { type Enhancer } from './private/applyMiddleware.ts';\nimport { type ComponentMiddleware } from './types.ts';\n\n// TODO: Simplify to ComponentType<Props> | undefined.\ntype ResultComponent<Props> = ComponentType<Props> | false | null | undefined;\n\ntype UseBuildComponentCallbackOptions<Props> = { fallbackComponent?: ResultComponent<Props> };\n\ntype UseBuildComponentCallback<Request, Props> = (\n request: Request,\n options?: UseBuildComponentCallbackOptions<Props>\n) => ResultComponent<Props>;\n\ntype ProviderContext<Request, Props> = {\n get enhancer(): Enhancer<[Request], ResultComponent<Props>> | undefined;\n useBuildComponentCallback: UseBuildComponentCallback<Request, Props>;\n};\n\ntype ProviderProps<Request, Props, Init> = PropsWithChildren<{\n middleware: readonly ComponentMiddleware<Request, Props, Init>[];\n}> &\n (Init extends never | void ? { init?: undefined } : Init extends undefined ? { init?: Init } : { init: Init });\n\ntype ProxyProps<Request, Props extends object> = PropsWithChildren<\n Request extends never | void\n ? Props & { fallbackComponent?: ComponentType<Props>; request?: undefined }\n : Request extends undefined\n ? Props & { fallbackComponent?: ComponentType<Props>; request?: Request }\n : Props & { fallbackComponent?: ComponentType<Props>; request: Request }\n>;\n\ntype Options = {\n /**\n * Allows a middleware to pass another request object when calling its next middleware. Default is false.\n *\n * However, middleware could modify the request object before calling its next middleware. It is recommended\n * to use Object.freeze() to prevent middleware from modifying the request object.\n */\n passModifiedRequest?: boolean;\n};\n\ntype MiddlewareProps<Request, Props, Init> = Readonly<{\n init: Init;\n Next: ComponentType<Partial<Props>>;\n request: Request;\n}>;\n\ntype MiddlewareComponentProps<Request, Props, Init> = Props &\n Readonly<{ middleware: MiddlewareProps<Request, Props, Init> }>;\n\nexport default function createChainOfResponsibility<\n Request = undefined,\n Props extends object = Readonly<{ children?: never }>,\n Init = undefined\n>(\n options: Options = {}\n): {\n asMiddleware: (\n middlewareComponent: ComponentType<MiddlewareComponentProps<Request, Props, Init>>\n ) => ComponentMiddleware<Request, Props, Init>;\n Provider: ComponentType<ProviderProps<Request, Props, Init>>;\n Proxy: ComponentType<ProxyProps<Request, Props>>;\n types: {\n init: Init;\n middleware: ComponentMiddleware<Request, Props, Init>;\n middlewareComponentProps: MiddlewareComponentProps<Request, Props, Init>;\n props: Props;\n request: Request;\n };\n useBuildComponentCallback: () => UseBuildComponentCallback<Request, Props>;\n} {\n const defaultUseBuildComponentCallback: ProviderContext<Request, Props> = {\n get enhancer() {\n return undefined;\n },\n useBuildComponentCallback(_, options) {\n if (options?.fallbackComponent) {\n return options.fallbackComponent;\n }\n\n throw new Error('This component/hook cannot be used outside of its corresponding <Provider>');\n }\n };\n\n const context = createContext<ProviderContext<Request, Props>>(defaultUseBuildComponentCallback);\n\n function ChainOfResponsibilityProvider({ children, init, middleware }: ProviderProps<Request, Props, Init>) {\n // TODO: Related to https://github.com/microsoft/TypeScript/issues/17002.\n // typescript@5.2.2 has a bug, Array.isArray() is a type predicate but only works with mutable array, not readonly array.\n // After removing \"as unknown\", `middleware` on the next line become `any[]`.\n if (!Array.isArray(middleware as unknown) || middleware.some(middleware => typeof middleware !== 'function')) {\n throw new Error('middleware prop must be an array of functions');\n }\n\n const patchedMiddleware: readonly ComponentMiddleware<Request, Props, Init>[] = Object.freeze(\n middleware\n ? middleware.map(fn => (init: Init) => {\n const enhancer = fn(init);\n\n return (next: UseBuildComponentCallback<Request, Props>) => (originalRequest: Request) => {\n // False positive: although we did not re-assign the variable from true, it was initialized as undefined.\n // eslint-disable-next-line prefer-const\n let hasReturned: boolean;\n\n const returnValue = enhancer(nextRequest => {\n if (hasReturned) {\n throw new Error('next() cannot be called after the function had returned synchronously');\n }\n\n !options.passModifiedRequest &&\n nextRequest !== originalRequest &&\n console.warn(\n 'react-chain-of-responsibility: \"options.passModifiedRequest\" must be set to true to pass a different request object to next().'\n );\n\n return next(options.passModifiedRequest ? nextRequest : originalRequest);\n })(originalRequest);\n\n hasReturned = true;\n\n if (isValidElement(returnValue)) {\n throw new Error('middleware must not return React element directly');\n } else if (\n returnValue !== false &&\n returnValue !== null &&\n typeof returnValue !== 'undefined' &&\n !isReactComponent(returnValue)\n ) {\n throw new Error(\n 'middleware must return false, null, undefined, function component, or class component'\n );\n }\n\n return returnValue;\n };\n })\n : []\n );\n\n const { enhancer: parentEnhancer } = useContext(context);\n\n const enhancer = useMemo(\n () =>\n // We are reversing because it is easier to read:\n // - With reverse, [a, b, c] will become a(b(c(fn)))\n // - Without reverse, [a, b, c] will become c(b(a(fn)))\n applyMiddleware<[Request], ResultComponent<Props>, [Init]>(\n ...[...patchedMiddleware, ...(parentEnhancer ? [() => parentEnhancer] : [])].reverse()\n )(init as Init),\n [init, middleware, parentEnhancer]\n );\n\n const useBuildComponentCallback = useCallback<UseBuildComponentCallback<Request, Props>>(\n (request, options = {}) => enhancer(() => options.fallbackComponent)(request),\n [enhancer]\n );\n\n const contextValue = useMemo<ProviderContext<Request, Props>>(\n () => ({ enhancer, useBuildComponentCallback }),\n [enhancer, useBuildComponentCallback]\n );\n\n return <context.Provider value={contextValue}>{children}</context.Provider>;\n }\n\n const useBuildComponentCallback = () => useContext(context).useBuildComponentCallback;\n\n function Proxy({ children, fallbackComponent, request, ...props }: ProxyProps<Request, Props>) {\n const enhancer = useBuildComponentCallback();\n const Component = enhancer(request as Request, { fallbackComponent });\n\n return Component ? <Component {...(props as Props)}>{children}</Component> : null;\n }\n\n const asMiddleware: (\n middlewareComponent: ComponentType<MiddlewareComponentProps<Request, Props, Init>>\n ) => ComponentMiddleware<Request, Props, Init> =\n (\n MiddlewareComponent: ComponentType<MiddlewareComponentProps<Request, Props, Init>>\n ): ComponentMiddleware<Request, Props, Init> =>\n init =>\n next =>\n request => {\n const RawNextComponent = next(request);\n\n // TODO: Can we pre-build this component during init?\n const MiddlewareOf = (props: Props) => {\n const middleware = useMemo(\n () =>\n Object.freeze({\n init,\n Next: memo<Partial<Props>>(\n RawNextComponent\n ? (overridingProps: Partial<Props>) => <RawNextComponent {...props} {...overridingProps} />\n : () => null\n ),\n request\n }),\n []\n );\n\n return <MiddlewareComponent {...props} middleware={middleware} />;\n };\n\n MiddlewareOf.displayName = `MiddlewareOf<${MiddlewareComponent.displayName || ''}>`;\n\n return memo<Props>(MiddlewareOf);\n };\n\n return {\n asMiddleware,\n Provider: memo<ProviderProps<Request, Props, Init>>(ChainOfResponsibilityProvider),\n Proxy: memo<ProxyProps<Request, Props>>(Proxy),\n types: {\n middlewareComponentProps: undefined as unknown as MiddlewareComponentProps<Request, Props, Init>,\n init: undefined as unknown as Init,\n middleware: undefined as unknown as ComponentMiddleware<Request, Props, Init>,\n props: undefined as unknown as Props,\n request: undefined as unknown as Request\n },\n useBuildComponentCallback\n };\n}\n","import {\n type ComponentClass,\n type ComponentType,\n type Consumer,\n type Fragment,\n type FunctionComponent,\n type Provider\n} from 'react';\n\nfunction isConsumer(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is Consumer<unknown> {\n return component?.$$typeof?.toString() === 'Symbol(react.context)';\n}\n\nfunction isProvider(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is Provider<unknown> {\n return component?.$$typeof?.toString() === 'Symbol(react.provider)';\n}\n\nfunction isFragment(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is typeof Fragment {\n return component?.toString() === 'Symbol(react.fragment)';\n}\n\nfunction isFunctionComponent(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is FunctionComponent {\n if (typeof component === 'function') {\n return true;\n }\n\n return isPureFunctionComponent(component);\n}\n\nfunction isPureFunctionComponent(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is FunctionComponent {\n return component?.$$typeof?.toString() === 'Symbol(react.memo)' && isFunctionComponent(component.type);\n}\n\nfunction isComponentClass(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is ComponentClass {\n return typeof component === 'object' && typeof component?.['render'] === 'function';\n}\n\n// There are no definitive ways to check if an object is a React component or not.\n// We are checking if the object has a render function (classic component).\n// Note: \"forwardRef()\" returns plain object, not class instance.\nexport default function isReactComponent(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is ComponentType {\n return (\n isFunctionComponent(component) ||\n isComponentClass(component) ||\n isFragment(component) ||\n isConsumer(component) ||\n isProvider(component)\n );\n}\n","// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Fn<P extends any[], R> = (...args: P) => R;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Enhancer<P extends any[], R> = (next: Fn<P, R>) => Fn<P, R>;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport default function compose<P extends any[], R>(...fns: Enhancer<P, R>[]): Enhancer<P, R> {\n return (fn: Fn<P, R>): Fn<P, R> => fns.reduce((chain, fn) => fn(chain), fn);\n}\n","import compose from './compose.ts';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Fn<P extends any[], R> = (...args: P) => R;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type Enhancer<P extends any[], R> = (next: Fn<P, R>) => Fn<P, R>;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type Middleware<P extends any[], R, S extends any[]> = (...init: S) => Enhancer<P, R>;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport default function applyMiddleware<P extends any[], R, S extends any[]>(\n ...arrayOfMiddleware: Middleware<P, R, S>[]\n) {\n return (...init: S) => {\n const chain = arrayOfMiddleware.map(middleware => middleware(...init));\n\n return compose(...chain);\n };\n}\n"],"mappings":"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","names":["fn","options","middleware","init","enhancer","useBuildComponentCallback","React","Proxy"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createChainOfResponsibility
3
- } from "./chunk-LQNXZPUH.mjs";
3
+ } from "./chunk-TT7FCTZA.mjs";
4
4
  export {
5
5
  createChainOfResponsibility
6
6
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-chain-of-responsibility",
3
- "version": "0.2.0",
4
- "description": "Using chain of responsibility design pattern for React component customization.",
3
+ "version": "0.2.1-main.6ac5632",
4
+ "description": "Using chain of responsibility design pattern for compositing and customizing React component.",
5
5
  "files": [
6
6
  "./dist/"
7
7
  ],
@@ -87,36 +87,51 @@
87
87
  "react-test-renderer": "18.0.0"
88
88
  }
89
89
  },
90
+ "pinDependencies": {
91
+ "@types/react": [
92
+ "18"
93
+ ],
94
+ "react": [
95
+ "18"
96
+ ],
97
+ "react-dom": [
98
+ "18"
99
+ ],
100
+ "react-test-renderer": [
101
+ "18"
102
+ ]
103
+ },
90
104
  "devDependencies": {
91
- "@babel/preset-env": "^7.25.8",
92
- "@babel/preset-react": "^7.25.7",
93
- "@babel/preset-typescript": "^7.25.7",
94
- "@fluentui/react": "^8.121.4",
105
+ "@babel/preset-env": "^7.27.2",
106
+ "@babel/preset-react": "^7.27.1",
107
+ "@babel/preset-typescript": "^7.27.1",
108
+ "@fluentui/react": "^8.123.0",
109
+ "@testduet/given-when-then": "^0.1.0-main.5e524dc",
95
110
  "@testing-library/dom": "^10.4.0",
96
- "@testing-library/react": "^16.0.1",
97
- "@tsconfig/recommended": "^1.0.7",
111
+ "@testing-library/react": "^16.3.0",
112
+ "@tsconfig/recommended": "^1.0.10",
98
113
  "@tsconfig/strictest": "^2.0.5",
99
- "@types/jest": "^29.5.13",
100
- "@types/node": "^22.7.5",
101
- "@types/react": "^18.3.11",
102
- "esbuild": "^0.24.0",
114
+ "@types/jest": "^30.0.0",
115
+ "@types/node": "^24.0.3",
116
+ "@types/react": "^18.3.23",
117
+ "esbuild": "^0.25.5",
103
118
  "escape-string-regexp": "^5.0.0",
104
- "jest": "^29.7.0",
105
- "jest-environment-jsdom": "^29.7.0",
106
- "prettier": "^3.3.3",
107
- "publint": "^0.2.11",
119
+ "jest": "^30.0.2",
120
+ "jest-environment-jsdom": "^30.0.2",
121
+ "prettier": "^3.5.3",
122
+ "publint": "^0.3.12",
108
123
  "react": "^18.3.1",
109
124
  "react-dom": "^18.3.1",
110
125
  "react-test-renderer": "^18.3.1",
111
126
  "react-wrap-with": "^0.1.0",
112
- "tsup": "^8.3.0",
113
- "typescript": "^5.6.3"
127
+ "tsup": "^8.5.0",
128
+ "typescript": "^5.8.3"
114
129
  },
115
130
  "peerDependencies": {
116
131
  "react": ">=16.8.0"
117
132
  },
118
133
  "dependencies": {
119
134
  "prop-types": "^15.8.1",
120
- "react-chain-of-responsibility": "^0.2.0"
135
+ "react-chain-of-responsibility": "^0.2.1-main.6ac5632"
121
136
  }
122
137
  }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/createChainOfResponsibility.tsx","../src/isReactComponent.ts","../src/private/compose.ts","../src/private/applyMiddleware.ts"],"sourcesContent":["import PropTypes from 'prop-types';\nimport React, {\n createContext,\n isValidElement,\n memo,\n useCallback,\n useContext,\n useMemo,\n type ComponentType,\n type PropsWithChildren\n} from 'react';\n\nimport isReactComponent from './isReactComponent.ts';\nimport applyMiddleware, { type Enhancer } from './private/applyMiddleware.ts';\nimport { type ComponentMiddleware } from './types.ts';\n\ntype ResultComponent<Props> = ComponentType<Props> | false | null | undefined;\n\ntype UseBuildComponentCallbackOptions<Props> = { fallbackComponent?: ResultComponent<Props> };\n\ntype UseBuildComponentCallback<Request, Props> = (\n request: Request,\n options?: UseBuildComponentCallbackOptions<Props>\n) => ResultComponent<Props>;\n\ntype ProviderContext<Request, Props> = {\n get enhancer(): Enhancer<[Request], ResultComponent<Props>> | undefined;\n useBuildComponentCallback: UseBuildComponentCallback<Request, Props>;\n};\n\ntype ProviderProps<Request, Props, Init> = PropsWithChildren<{\n middleware: readonly ComponentMiddleware<Request, Props, Init>[];\n}> &\n (Init extends never | void ? { init?: undefined } : Init extends undefined ? { init?: Init } : { init: Init });\n\ntype ProxyProps<Request, Props> = PropsWithChildren<\n Request extends never | void\n ? Props & { fallbackComponent?: ComponentType<Props>; request?: undefined }\n : Request extends undefined\n ? Props & { fallbackComponent?: ComponentType<Props>; request?: Request }\n : Props & { fallbackComponent?: ComponentType<Props>; request: Request }\n>;\n\ntype Options = {\n /**\n * Allows a middleware to pass another request object when calling its next middleware. Default is false.\n *\n * However, middleware could modify the request object before calling its next middleware. It is recommended\n * to use Object.freeze() to prevent middleware from modifying the request object.\n */\n passModifiedRequest?: boolean;\n};\n\nexport default function createChainOfResponsibility<\n Request = undefined,\n Props = { children?: never },\n Init = undefined\n>(\n options: Options = {}\n): {\n Provider: ComponentType<ProviderProps<Request, Props, Init>>;\n Proxy: ComponentType<ProxyProps<Request, Props>>;\n types: {\n init: Init;\n middleware: ComponentMiddleware<Request, Props, Init>;\n props: Props;\n request: Request;\n };\n useBuildComponentCallback: () => UseBuildComponentCallback<Request, Props>;\n} {\n const defaultUseBuildComponentCallback: ProviderContext<Request, Props> = {\n get enhancer() {\n return undefined;\n },\n useBuildComponentCallback(_, options) {\n if (options?.fallbackComponent) {\n return options.fallbackComponent;\n }\n\n throw new Error('This component/hook cannot be used outside of its corresponding <Provider>');\n }\n };\n\n const context = createContext<ProviderContext<Request, Props>>(defaultUseBuildComponentCallback);\n\n const Provider: ComponentType<ProviderProps<Request, Props, Init>> = ({ children, init, middleware }) => {\n // TODO: Related to https://github.com/microsoft/TypeScript/issues/17002.\n // typescript@5.2.2 has a bug, Array.isArray() is a type predicate but only works with mutable array, not readonly array.\n // After removing \"as unknown\", `middleware` on the next line become `any[]`.\n if (!Array.isArray(middleware as unknown) || middleware.some(middleware => typeof middleware !== 'function')) {\n throw new Error('middleware prop must be an array of functions');\n }\n\n const patchedMiddleware: readonly ComponentMiddleware<Request, Props, Init>[] = Object.freeze(\n middleware\n ? middleware.map(fn => (init: Init) => {\n const enhancer = fn(init);\n\n return (next: UseBuildComponentCallback<Request, Props>) => (originalRequest: Request) => {\n // False positive: although we did not re-assign the variable from true, it was initialized as undefined.\n // eslint-disable-next-line prefer-const\n let hasReturned: boolean;\n\n const returnValue = enhancer(nextRequest => {\n if (hasReturned) {\n throw new Error('next() cannot be called after the function had returned synchronously');\n }\n\n !options.passModifiedRequest &&\n nextRequest !== originalRequest &&\n console.warn(\n 'react-chain-of-responsibility: \"options.passModifiedRequest\" must be set to true to pass a different request object to next().'\n );\n\n return next(options.passModifiedRequest ? nextRequest : originalRequest);\n })(originalRequest);\n\n hasReturned = true;\n\n if (isValidElement(returnValue)) {\n throw new Error('middleware must not return React element directly');\n } else if (\n returnValue !== false &&\n returnValue !== null &&\n typeof returnValue !== 'undefined' &&\n !isReactComponent(returnValue)\n ) {\n throw new Error(\n 'middleware must return false, null, undefined, function component, or class component'\n );\n }\n\n return returnValue;\n };\n })\n : []\n );\n\n const { enhancer: parentEnhancer } = useContext(context);\n\n const enhancer = useMemo(\n () =>\n // We are reversing because it is easier to read:\n // - With reverse, [a, b, c] will become a(b(c(fn)))\n // - Without reverse, [a, b, c] will become c(b(a(fn)))\n applyMiddleware<[Request], ResultComponent<Props>, [Init]>(\n ...[...patchedMiddleware, ...(parentEnhancer ? [() => parentEnhancer] : [])].reverse()\n )(init as Init),\n [init, middleware, parentEnhancer]\n );\n\n const useBuildComponentCallback = useCallback<UseBuildComponentCallback<Request, Props>>(\n (request, options = {}) => enhancer(() => options.fallbackComponent)(request),\n [enhancer]\n );\n\n const contextValue = useMemo<ProviderContext<Request, Props>>(\n () => ({ enhancer, useBuildComponentCallback }),\n [enhancer, useBuildComponentCallback]\n );\n\n return <context.Provider value={contextValue}>{children}</context.Provider>;\n };\n\n Provider.displayName = 'ChainOfResponsibilityProvider';\n Provider.propTypes = {\n children: PropTypes.any,\n init: PropTypes.any,\n middleware: PropTypes.any\n };\n\n const useBuildComponentCallback = () => useContext(context).useBuildComponentCallback;\n\n const Proxy: ComponentType<ProxyProps<Request, Props>> = memo(\n // False positive: \"children\" is not a prop.\n // eslint-disable-next-line react/prop-types\n ({ children, fallbackComponent, request, ...props }) => {\n const enhancer = useBuildComponentCallback();\n const Component = enhancer(request as Request, { fallbackComponent });\n\n return Component ? <Component {...(props as Props)}>{children}</Component> : null;\n }\n );\n\n Proxy.displayName = 'Proxy';\n Proxy.propTypes = {\n fallbackComponent: PropTypes.any,\n request: PropTypes.any\n };\n\n return {\n Provider,\n Proxy,\n types: {\n init: undefined as unknown as Init,\n middleware: undefined as unknown as ComponentMiddleware<Request, Props, Init>,\n props: undefined as unknown as Props,\n request: undefined as unknown as Request\n },\n useBuildComponentCallback\n };\n}\n","import {\n type ComponentClass,\n type ComponentType,\n type Consumer,\n type Fragment,\n type FunctionComponent,\n type Provider\n} from 'react';\n\nfunction isConsumer(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is Consumer<unknown> {\n return component?.$$typeof?.toString() === 'Symbol(react.context)';\n}\n\nfunction isProvider(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is Provider<unknown> {\n return component?.$$typeof?.toString() === 'Symbol(react.provider)';\n}\n\nfunction isFragment(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is typeof Fragment {\n return component?.toString() === 'Symbol(react.fragment)';\n}\n\nfunction isFunctionComponent(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is FunctionComponent {\n if (typeof component === 'function') {\n return true;\n }\n\n return isPureFunctionComponent(component);\n}\n\nfunction isPureFunctionComponent(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is FunctionComponent {\n return component?.$$typeof?.toString() === 'Symbol(react.memo)' && isFunctionComponent(component.type);\n}\n\nfunction isComponentClass(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is ComponentClass {\n return typeof component === 'object' && typeof component?.['render'] === 'function';\n}\n\n// There are no definitive ways to check if an object is a React component or not.\n// We are checking if the object has a render function (classic component).\n// Note: \"forwardRef()\" returns plain object, not class instance.\nexport default function isReactComponent(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n component: any\n): component is ComponentType {\n return (\n isFunctionComponent(component) ||\n isComponentClass(component) ||\n isFragment(component) ||\n isConsumer(component) ||\n isProvider(component)\n );\n}\n","// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Fn<P extends any[], R> = (...args: P) => R;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Enhancer<P extends any[], R> = (next: Fn<P, R>) => Fn<P, R>;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport default function compose<P extends any[], R>(...fns: Enhancer<P, R>[]): Enhancer<P, R> {\n return (fn: Fn<P, R>): Fn<P, R> => fns.reduce((chain, fn) => fn(chain), fn);\n}\n","import compose from './compose.ts';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Fn<P extends any[], R> = (...args: P) => R;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type Enhancer<P extends any[], R> = (next: Fn<P, R>) => Fn<P, R>;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type Middleware<P extends any[], R, S extends any[]> = (...init: S) => Enhancer<P, R>;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport default function applyMiddleware<P extends any[], R, S extends any[]>(\n ...arrayOfMiddleware: Middleware<P, R, S>[]\n) {\n return (...init: S) => {\n const chain = arrayOfMiddleware.map(middleware => middleware(...init));\n\n return compose(...chain);\n };\n}\n"],"mappings":"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","names":["fn","options","middleware","init","enhancer","useBuildComponentCallback"]}