react-chain-of-responsibility 0.4.3-main.202512151007.a6ddded → 0.4.3-main.202512312342.8e5988c
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 +0 -79
- package/dist/react-chain-of-responsibility.d.mts +66 -3
- package/dist/react-chain-of-responsibility.d.ts +66 -3
- package/dist/react-chain-of-responsibility.js +17 -12
- package/dist/react-chain-of-responsibility.js.map +1 -1
- package/dist/react-chain-of-responsibility.mjs +143 -3
- package/dist/react-chain-of-responsibility.mjs.map +1 -1
- package/dist/react-chain-of-responsibility.preview.js +14 -13
- package/dist/react-chain-of-responsibility.preview.js.map +1 -1
- package/dist/react-chain-of-responsibility.preview.mjs +2 -8
- package/dist/react-chain-of-responsibility.preview.mjs.map +1 -1
- package/package.json +21 -36
- package/dist/chunk-U4UF7NZV.mjs +0 -150
- package/dist/chunk-U4UF7NZV.mjs.map +0 -1
- package/dist/index-Dm_KqEiI.d.mts +0 -66
- package/dist/index-Dm_KqEiI.d.ts +0 -66
- package/dist/react-chain-of-responsibility.fluentUI.d.mts +0 -17
- package/dist/react-chain-of-responsibility.fluentUI.d.ts +0 -17
- package/dist/react-chain-of-responsibility.fluentUI.js +0 -203
- package/dist/react-chain-of-responsibility.fluentUI.js.map +0 -1
- package/dist/react-chain-of-responsibility.fluentUI.mjs +0 -26
- package/dist/react-chain-of-responsibility.fluentUI.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -245,59 +245,6 @@ The following code snippet shows the conversion from the `<Proxy>` component int
|
|
|
245
245
|
}
|
|
246
246
|
```
|
|
247
247
|
|
|
248
|
-
### Using as `IRenderFunction` in Fluent UI v8
|
|
249
|
-
|
|
250
|
-
> We are considering deprecating the `IRenderFunction` as Fluent UI no longer adopt this pattern.
|
|
251
|
-
|
|
252
|
-
The chain of responsibility design pattern can be used in Fluent UI v8.
|
|
253
|
-
|
|
254
|
-
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.
|
|
255
|
-
|
|
256
|
-
#### Sample code
|
|
257
|
-
|
|
258
|
-
```jsx
|
|
259
|
-
import { createChainOfResponsibilityForFluentUI } from 'react-chain-of-responsibility/fluentUI';
|
|
260
|
-
|
|
261
|
-
// Creates a <Provider> providing the chain of responsibility service.
|
|
262
|
-
const { Provider, Proxy } = createChainOfResponsibilityForFluentUI();
|
|
263
|
-
|
|
264
|
-
// List of subcomponents.
|
|
265
|
-
const Banana = () => <>🍌</>;
|
|
266
|
-
const Orange = () => <>🍊</>;
|
|
267
|
-
|
|
268
|
-
// Constructs an array of middleware to handle the request and return corresponding subcomponents.
|
|
269
|
-
const middleware = [
|
|
270
|
-
() => next => props => (props?.iconProps?.iconName === 'Banana' ? Banana : next(props)),
|
|
271
|
-
() => next => props => (props?.iconProps?.iconName === 'Orange' ? Orange : next(props))
|
|
272
|
-
// Fallback to `defaultRender` of `IRenderFunction` is automatically injected.
|
|
273
|
-
];
|
|
274
|
-
|
|
275
|
-
const Inner = () => {
|
|
276
|
-
const renderIconFunction = useBuildRenderFunction();
|
|
277
|
-
|
|
278
|
-
return (
|
|
279
|
-
<Fragment>
|
|
280
|
-
<DefaultButton iconProps={{ iconName: 'Banana' }} onRenderIcon={renderIconFunction} />
|
|
281
|
-
<DefaultButton iconProps={{ iconName: 'Orange' }} onRenderIcon={renderIconFunction} />
|
|
282
|
-
<DefaultButton iconProps={{ iconName: 'OpenInNewTab' }} onRenderIcon={renderIconFunction} />
|
|
283
|
-
</Fragment>
|
|
284
|
-
);
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
render(
|
|
288
|
-
<Provider middleware={middleware}>
|
|
289
|
-
<Inner />
|
|
290
|
-
</Provider>
|
|
291
|
-
);
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
There are subtle differences between the standard version and the Fluent UI version:
|
|
295
|
-
|
|
296
|
-
- Entrypoint is `createChainOfResponsibilityForFluentUI()` and imported from 'react-chain-of-responsibility/fluentUI'
|
|
297
|
-
- Request and props are always of same type
|
|
298
|
-
- They are optional too, as defined in [`IRenderFunction`](https://github.com/microsoft/fluentui/blob/master/packages/utilities/src/IRenderFunction.ts)
|
|
299
|
-
- Automatic fallback to `defaultRender`
|
|
300
|
-
|
|
301
248
|
### Nesting `<Provider>`
|
|
302
249
|
|
|
303
250
|
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".
|
|
@@ -407,32 +354,6 @@ For simplicity, instead of returning a React component or `false`/`null`/`undefi
|
|
|
407
354
|
|
|
408
355
|
The `fallbackComponent` is a component which all unhandled requests will sink into, including calls outside of `<Provider>`.
|
|
409
356
|
|
|
410
|
-
### API for Fluent UI
|
|
411
|
-
|
|
412
|
-
```ts
|
|
413
|
-
export default function createChainOfResponsibilityForFluentUI<Props extends {}, Init = undefined>(
|
|
414
|
-
options?: Options
|
|
415
|
-
): ReturnType<typeof createChainOfResponsibility<Props | undefined, Props, Init>> & {
|
|
416
|
-
useBuildRenderFunction: useBuildRenderFunction<Props>;
|
|
417
|
-
};
|
|
418
|
-
```
|
|
419
|
-
|
|
420
|
-
#### Return value
|
|
421
|
-
|
|
422
|
-
| Name | Type | Description |
|
|
423
|
-
| ------------------------ | ---------------------------------------- | ---------------------------------------------------------------- |
|
|
424
|
-
| `useBuildRenderFunction` | `({ getKey }) => IRenderFunction<Props>` | Callback hook to build the `IRenderFunction` to use in Fluent UI |
|
|
425
|
-
|
|
426
|
-
#### API of `useBuildRenderFunction`
|
|
427
|
-
|
|
428
|
-
```ts
|
|
429
|
-
type UseBuildRenderFunctionOptions<Props> = { getKey?: (props: Props | undefined) => Key };
|
|
430
|
-
|
|
431
|
-
type UseBuildRenderFunction<Props> = (options?: UseBuildRenderFunctionOptions<Props>) => IRenderFunction<Props>;
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
When rendering the element, `getKey` is called to compute the `key` attribute. This is required for some `onRenderXXX` props. These props are usually used to render more than one elements, such as [`DetailsList.onRenderField`](https://developer.microsoft.com/en-us/fluentui#/controls/web/detailslist#implementation), which renders every field (a.k.a. cell) in the [`<DetailsList>`](https://developer.microsoft.com/en-us/fluentui#/controls/web/detailslist).
|
|
435
|
-
|
|
436
357
|
### `withBuildProps` higher-order helper function
|
|
437
358
|
|
|
438
359
|
`withBuildProps` is a higher-order function that extends the chain-of-responsibility with props transformation capability. It modified the `<Proxy>` and `useBuildComponentCallback` hook so that request can be hoisted and accessible in props.
|
|
@@ -1,3 +1,66 @@
|
|
|
1
|
-
|
|
2
|
-
import '
|
|
3
|
-
|
|
1
|
+
import { ComponentType, PropsWithChildren } from 'react';
|
|
2
|
+
import { Middleware } from 'handler-chain';
|
|
3
|
+
|
|
4
|
+
type ComponentResult<Props = {
|
|
5
|
+
children?: never;
|
|
6
|
+
}> = ComponentType<Props> | false | null | undefined;
|
|
7
|
+
type ComponentMiddleware<Request, Props = {
|
|
8
|
+
children?: never;
|
|
9
|
+
}, Init = undefined> = Middleware<ComponentResult<Props>, Request, Init>;
|
|
10
|
+
|
|
11
|
+
type ResultComponent<Props> = ComponentType<Props> | false | null | undefined;
|
|
12
|
+
type UseBuildComponentCallbackOptions<Props> = {
|
|
13
|
+
fallbackComponent?: ResultComponent<Props> | undefined;
|
|
14
|
+
};
|
|
15
|
+
interface UseBuildComponentCallback<Request, Props> {
|
|
16
|
+
(request: Request, options?: undefined | UseBuildComponentCallbackOptions<Props>): ComponentType<Props> | undefined;
|
|
17
|
+
}
|
|
18
|
+
type ProviderProps<Request, Props, Init> = PropsWithChildren<{
|
|
19
|
+
middleware: readonly ComponentMiddleware<Request, Props, Init>[];
|
|
20
|
+
}> & (Init extends never | void ? {
|
|
21
|
+
readonly init?: undefined;
|
|
22
|
+
} : Init extends undefined | void ? {
|
|
23
|
+
readonly init?: Init;
|
|
24
|
+
} : {
|
|
25
|
+
readonly init: Init;
|
|
26
|
+
});
|
|
27
|
+
type ProxyProps<Request, Props extends object> = Props & {
|
|
28
|
+
readonly fallbackComponent?: ComponentType<Props> | undefined;
|
|
29
|
+
readonly request: Request;
|
|
30
|
+
};
|
|
31
|
+
type CreateChainOfResponsibilityOptions = {
|
|
32
|
+
/**
|
|
33
|
+
* Allows a middleware to pass another request object when calling its next middleware. Default is false.
|
|
34
|
+
*
|
|
35
|
+
* However, middleware could modify the request object before calling its next middleware. It is recommended
|
|
36
|
+
* to use Object.freeze() to prevent middleware from modifying the request object.
|
|
37
|
+
*/
|
|
38
|
+
readonly passModifiedRequest?: boolean | undefined;
|
|
39
|
+
};
|
|
40
|
+
type AsMiddlewareProps<Request, Props, Init> = {
|
|
41
|
+
readonly init: Init;
|
|
42
|
+
readonly Next: ComponentType<Partial<Props>>;
|
|
43
|
+
readonly request: Request;
|
|
44
|
+
};
|
|
45
|
+
type AsMiddlewareComponentProps<Request, Props, Init> = Props & {
|
|
46
|
+
readonly middleware: AsMiddlewareProps<Request, Props, Init>;
|
|
47
|
+
};
|
|
48
|
+
type ChainOfResponsibility<Request, Props extends object, Init> = {
|
|
49
|
+
readonly asMiddleware: (middlewareComponent: ComponentType<AsMiddlewareComponentProps<Request, Props, Init>>) => ComponentMiddleware<Request, Props, Init>;
|
|
50
|
+
readonly Provider: ComponentType<ProviderProps<Request, Props, Init>>;
|
|
51
|
+
readonly Proxy: ComponentType<ProxyProps<Request, Props>>;
|
|
52
|
+
readonly types: {
|
|
53
|
+
readonly init: Init;
|
|
54
|
+
readonly middleware: ComponentMiddleware<Request, Props, Init>;
|
|
55
|
+
readonly middlewareComponentProps: AsMiddlewareComponentProps<Request, Props, Init>;
|
|
56
|
+
readonly props: Props;
|
|
57
|
+
readonly proxyProps: ProxyProps<Request, Props>;
|
|
58
|
+
readonly request: Request;
|
|
59
|
+
};
|
|
60
|
+
readonly useBuildComponentCallback: () => UseBuildComponentCallback<Request, Props>;
|
|
61
|
+
};
|
|
62
|
+
declare function createChainOfResponsibility<Request = void, Props extends object = {
|
|
63
|
+
readonly children?: never;
|
|
64
|
+
}, Init = void>(options?: CreateChainOfResponsibilityOptions): ChainOfResponsibility<Request, Props, Init>;
|
|
65
|
+
|
|
66
|
+
export { type ComponentMiddleware, createChainOfResponsibility };
|
|
@@ -1,3 +1,66 @@
|
|
|
1
|
-
|
|
2
|
-
import '
|
|
3
|
-
|
|
1
|
+
import { ComponentType, PropsWithChildren } from 'react';
|
|
2
|
+
import { Middleware } from 'handler-chain';
|
|
3
|
+
|
|
4
|
+
type ComponentResult<Props = {
|
|
5
|
+
children?: never;
|
|
6
|
+
}> = ComponentType<Props> | false | null | undefined;
|
|
7
|
+
type ComponentMiddleware<Request, Props = {
|
|
8
|
+
children?: never;
|
|
9
|
+
}, Init = undefined> = Middleware<ComponentResult<Props>, Request, Init>;
|
|
10
|
+
|
|
11
|
+
type ResultComponent<Props> = ComponentType<Props> | false | null | undefined;
|
|
12
|
+
type UseBuildComponentCallbackOptions<Props> = {
|
|
13
|
+
fallbackComponent?: ResultComponent<Props> | undefined;
|
|
14
|
+
};
|
|
15
|
+
interface UseBuildComponentCallback<Request, Props> {
|
|
16
|
+
(request: Request, options?: undefined | UseBuildComponentCallbackOptions<Props>): ComponentType<Props> | undefined;
|
|
17
|
+
}
|
|
18
|
+
type ProviderProps<Request, Props, Init> = PropsWithChildren<{
|
|
19
|
+
middleware: readonly ComponentMiddleware<Request, Props, Init>[];
|
|
20
|
+
}> & (Init extends never | void ? {
|
|
21
|
+
readonly init?: undefined;
|
|
22
|
+
} : Init extends undefined | void ? {
|
|
23
|
+
readonly init?: Init;
|
|
24
|
+
} : {
|
|
25
|
+
readonly init: Init;
|
|
26
|
+
});
|
|
27
|
+
type ProxyProps<Request, Props extends object> = Props & {
|
|
28
|
+
readonly fallbackComponent?: ComponentType<Props> | undefined;
|
|
29
|
+
readonly request: Request;
|
|
30
|
+
};
|
|
31
|
+
type CreateChainOfResponsibilityOptions = {
|
|
32
|
+
/**
|
|
33
|
+
* Allows a middleware to pass another request object when calling its next middleware. Default is false.
|
|
34
|
+
*
|
|
35
|
+
* However, middleware could modify the request object before calling its next middleware. It is recommended
|
|
36
|
+
* to use Object.freeze() to prevent middleware from modifying the request object.
|
|
37
|
+
*/
|
|
38
|
+
readonly passModifiedRequest?: boolean | undefined;
|
|
39
|
+
};
|
|
40
|
+
type AsMiddlewareProps<Request, Props, Init> = {
|
|
41
|
+
readonly init: Init;
|
|
42
|
+
readonly Next: ComponentType<Partial<Props>>;
|
|
43
|
+
readonly request: Request;
|
|
44
|
+
};
|
|
45
|
+
type AsMiddlewareComponentProps<Request, Props, Init> = Props & {
|
|
46
|
+
readonly middleware: AsMiddlewareProps<Request, Props, Init>;
|
|
47
|
+
};
|
|
48
|
+
type ChainOfResponsibility<Request, Props extends object, Init> = {
|
|
49
|
+
readonly asMiddleware: (middlewareComponent: ComponentType<AsMiddlewareComponentProps<Request, Props, Init>>) => ComponentMiddleware<Request, Props, Init>;
|
|
50
|
+
readonly Provider: ComponentType<ProviderProps<Request, Props, Init>>;
|
|
51
|
+
readonly Proxy: ComponentType<ProxyProps<Request, Props>>;
|
|
52
|
+
readonly types: {
|
|
53
|
+
readonly init: Init;
|
|
54
|
+
readonly middleware: ComponentMiddleware<Request, Props, Init>;
|
|
55
|
+
readonly middlewareComponentProps: AsMiddlewareComponentProps<Request, Props, Init>;
|
|
56
|
+
readonly props: Props;
|
|
57
|
+
readonly proxyProps: ProxyProps<Request, Props>;
|
|
58
|
+
readonly request: Request;
|
|
59
|
+
};
|
|
60
|
+
readonly useBuildComponentCallback: () => UseBuildComponentCallback<Request, Props>;
|
|
61
|
+
};
|
|
62
|
+
declare function createChainOfResponsibility<Request = void, Props extends object = {
|
|
63
|
+
readonly children?: never;
|
|
64
|
+
}, Init = void>(options?: CreateChainOfResponsibilityOptions): ChainOfResponsibility<Request, Props, Init>;
|
|
65
|
+
|
|
66
|
+
export { type ComponentMiddleware, createChainOfResponsibility };
|
|
@@ -70,6 +70,7 @@ function isReactComponent(component) {
|
|
|
70
70
|
var isReactComponent_default = isReactComponent;
|
|
71
71
|
|
|
72
72
|
// src/createChainOfResponsibility.tsx
|
|
73
|
+
var { createContext, isValidElement, memo, useCallback, useContext, useMemo } = import_react.default;
|
|
73
74
|
function createChainOfResponsibility(options = {}) {
|
|
74
75
|
const defaultUseBuildComponentCallback = {
|
|
75
76
|
get enhancer() {
|
|
@@ -82,7 +83,7 @@ function createChainOfResponsibility(options = {}) {
|
|
|
82
83
|
return options2.fallbackComponent;
|
|
83
84
|
}
|
|
84
85
|
};
|
|
85
|
-
const context =
|
|
86
|
+
const context = createContext(defaultUseBuildComponentCallback);
|
|
86
87
|
function ChainOfResponsibilityProvider({ children, init, middleware }) {
|
|
87
88
|
if (!Array.isArray(middleware) || middleware.some((middleware2) => typeof middleware2 !== "function")) {
|
|
88
89
|
throw new Error("middleware prop must be an array of functions");
|
|
@@ -102,7 +103,7 @@ function createChainOfResponsibility(options = {}) {
|
|
|
102
103
|
return next(options.passModifiedRequest ? nextRequest : originalRequest);
|
|
103
104
|
})(originalRequest);
|
|
104
105
|
hasReturned = true;
|
|
105
|
-
if (
|
|
106
|
+
if (isValidElement(returnValue)) {
|
|
106
107
|
throw new Error("middleware must not return React element directly");
|
|
107
108
|
} else if (returnValue !== false && returnValue !== null && typeof returnValue !== "undefined" && !isReactComponent_default(returnValue)) {
|
|
108
109
|
throw new Error(
|
|
@@ -113,8 +114,8 @@ function createChainOfResponsibility(options = {}) {
|
|
|
113
114
|
};
|
|
114
115
|
}) : []
|
|
115
116
|
);
|
|
116
|
-
const { enhancer: parentEnhancer } =
|
|
117
|
-
const enhancer =
|
|
117
|
+
const { enhancer: parentEnhancer } = useContext(context);
|
|
118
|
+
const enhancer = useMemo(
|
|
118
119
|
() => (
|
|
119
120
|
// We are reversing because it is easier to read:
|
|
120
121
|
// - With reverse, [a, b, c] will become a(b(c(fn)))
|
|
@@ -123,19 +124,21 @@ function createChainOfResponsibility(options = {}) {
|
|
|
123
124
|
...[...patchedMiddleware, ...parentEnhancer ? [() => parentEnhancer] : []]
|
|
124
125
|
)(init)
|
|
125
126
|
),
|
|
127
|
+
// TODO: "middleware" should be "patchedMiddleware", however, "patchedMiddleware" is not cached properly.
|
|
128
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
126
129
|
[init, middleware, parentEnhancer]
|
|
127
130
|
);
|
|
128
|
-
const useBuildComponentCallback2 =
|
|
131
|
+
const useBuildComponentCallback2 = useCallback(
|
|
129
132
|
(request, options2 = {}) => enhancer(() => options2.fallbackComponent)(request) || void 0,
|
|
130
133
|
[enhancer]
|
|
131
134
|
);
|
|
132
|
-
const contextValue =
|
|
135
|
+
const contextValue = useMemo(
|
|
133
136
|
() => ({ enhancer, useBuildComponentCallback: useBuildComponentCallback2 }),
|
|
134
137
|
[enhancer, useBuildComponentCallback2]
|
|
135
138
|
);
|
|
136
139
|
return /* @__PURE__ */ import_react.default.createElement(context.Provider, { value: contextValue }, children);
|
|
137
140
|
}
|
|
138
|
-
const useBuildComponentCallback = () =>
|
|
141
|
+
const useBuildComponentCallback = () => useContext(context).useBuildComponentCallback;
|
|
139
142
|
function Proxy2({ fallbackComponent, request, ...props }) {
|
|
140
143
|
const enhancer = useBuildComponentCallback();
|
|
141
144
|
const Component = enhancer(request, { fallbackComponent });
|
|
@@ -144,25 +147,27 @@ function createChainOfResponsibility(options = {}) {
|
|
|
144
147
|
const asMiddleware = (MiddlewareComponent) => (init) => (next) => (request) => {
|
|
145
148
|
const RawNextComponent = next(request);
|
|
146
149
|
const MiddlewareOf = (props) => {
|
|
147
|
-
const middleware =
|
|
150
|
+
const middleware = useMemo(
|
|
148
151
|
() => Object.freeze({
|
|
149
152
|
init,
|
|
150
|
-
Next:
|
|
153
|
+
Next: memo(
|
|
151
154
|
RawNextComponent ? (overridingProps) => /* @__PURE__ */ import_react.default.createElement(RawNextComponent, { ...props, ...overridingProps }) : () => null
|
|
152
155
|
),
|
|
153
156
|
request
|
|
154
157
|
}),
|
|
158
|
+
// TODO: We should check "props" changes.
|
|
159
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
155
160
|
[]
|
|
156
161
|
);
|
|
157
162
|
return /* @__PURE__ */ import_react.default.createElement(MiddlewareComponent, { ...props, middleware });
|
|
158
163
|
};
|
|
159
164
|
MiddlewareOf.displayName = `MiddlewareOf<${MiddlewareComponent.displayName || ""}>`;
|
|
160
|
-
return
|
|
165
|
+
return memo(MiddlewareOf);
|
|
161
166
|
};
|
|
162
167
|
return Object.freeze({
|
|
163
168
|
asMiddleware,
|
|
164
|
-
Provider:
|
|
165
|
-
Proxy:
|
|
169
|
+
Provider: memo(ChainOfResponsibilityProvider),
|
|
170
|
+
Proxy: memo(Proxy2),
|
|
166
171
|
types: Object.freeze({
|
|
167
172
|
middlewareComponentProps: void 0,
|
|
168
173
|
init: void 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/createChainOfResponsibility.tsx","../src/isReactComponent.ts"],"sourcesContent":["export { default as createChainOfResponsibility } from './createChainOfResponsibility.tsx';\nexport { type ComponentMiddleware } from './types.ts';\n","import { applyMiddleware } from 'handler-chain';\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 { type ComponentEnhancer, 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> = {\n fallbackComponent?: ResultComponent<Props> | undefined;\n};\n\ninterface UseBuildComponentCallback<Request, Props> {\n (request: Request, options?: undefined | UseBuildComponentCallbackOptions<Props>): ComponentType<Props> | undefined;\n}\n\ntype ProviderContext<Request, Props> = {\n get enhancer(): ComponentEnhancer<Request, 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\n ? { readonly init?: undefined }\n : Init extends undefined | void\n ? { readonly init?: Init }\n : { readonly init: Init });\n\ntype ProxyProps<Request, Props extends object> = Props & {\n readonly fallbackComponent?: ComponentType<Props> | undefined;\n readonly request: Request;\n};\n\ntype CreateChainOfResponsibilityOptions = {\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 readonly passModifiedRequest?: boolean | undefined;\n};\n\ntype AsMiddlewareProps<Request, Props, Init> = {\n readonly init: Init;\n readonly Next: ComponentType<Partial<Props>>;\n readonly request: Request;\n};\n\ntype AsMiddlewareComponentProps<Request, Props, Init> = Props & {\n readonly middleware: AsMiddlewareProps<Request, Props, Init>;\n};\n\ntype ChainOfResponsibility<Request, Props extends object, Init> = {\n readonly asMiddleware: (\n middlewareComponent: ComponentType<AsMiddlewareComponentProps<Request, Props, Init>>\n ) => ComponentMiddleware<Request, Props, Init>;\n readonly Provider: ComponentType<ProviderProps<Request, Props, Init>>;\n readonly Proxy: ComponentType<ProxyProps<Request, Props>>;\n readonly types: {\n readonly init: Init;\n readonly middleware: ComponentMiddleware<Request, Props, Init>;\n readonly middlewareComponentProps: AsMiddlewareComponentProps<Request, Props, Init>;\n readonly props: Props;\n readonly proxyProps: ProxyProps<Request, Props>;\n readonly request: Request;\n };\n readonly useBuildComponentCallback: () => UseBuildComponentCallback<Request, Props>;\n};\n\nfunction createChainOfResponsibility<Request = void, Props extends object = { readonly children?: never }, Init = void>(\n options: CreateChainOfResponsibilityOptions = {}\n): ChainOfResponsibility<Request, Props, Init> {\n const defaultUseBuildComponentCallback: ProviderContext<Request, Props> = {\n get enhancer() {\n return undefined;\n },\n useBuildComponentCallback(_request, options): ComponentType<Props> {\n if (!options?.fallbackComponent) {\n throw new Error('This component/hook cannot be used outside of its corresponding <Provider>');\n }\n\n return options.fallbackComponent;\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: (request: Request) => ComponentType<Props> | false | null | undefined) =>\n (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<ResultComponent<Props>, Request, Init>(\n ...[...patchedMiddleware, ...(parentEnhancer ? [() => parentEnhancer] : [])]\n )(init as Init),\n [init, middleware, parentEnhancer]\n );\n\n const useBuildComponentCallback = useCallback<UseBuildComponentCallback<Request, Props>>(\n (request, options = {}) => enhancer(() => options.fallbackComponent)(request) || undefined,\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({ fallbackComponent, request, ...props }: ProxyProps<Request, Props>) {\n const enhancer = useBuildComponentCallback();\n\n const Component = enhancer(request as Request, { fallbackComponent });\n\n return Component ? <Component {...(props as Props)} /> : null;\n }\n\n const asMiddleware: (\n middlewareComponent: ComponentType<AsMiddlewareComponentProps<Request, Props, Init>>\n ) => ComponentMiddleware<Request, Props, Init> =\n (\n MiddlewareComponent: ComponentType<AsMiddlewareComponentProps<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 Object.freeze({\n asMiddleware,\n Provider: memo<ProviderProps<Request, Props, Init>>(ChainOfResponsibilityProvider),\n Proxy: memo<ProxyProps<Request, Props>>(Proxy),\n types: Object.freeze({\n middlewareComponentProps: undefined as unknown as AsMiddlewareComponentProps<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 proxyProps: undefined as unknown as ProxyProps<Request, Props>,\n request: undefined as unknown as Request\n }),\n useBuildComponentCallback\n });\n}\n\nexport default createChainOfResponsibility;\nexport {\n type ChainOfResponsibility,\n type CreateChainOfResponsibilityOptions,\n type ProxyProps,\n type UseBuildComponentCallback\n};\n","import {\n type ComponentClass,\n type ComponentType,\n type Consumer,\n type Fragment,\n type FunctionComponent,\n type Provider\n} from 'react';\nimport { custom } from 'valibot';\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.\nfunction 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\nconst reactComponent = () =>\n custom<ComponentType<unknown>>(value => isReactComponent(value), 'not a valid React component');\n\nexport default isReactComponent;\nexport { reactComponent };\n"],"mappings":"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","names":["options","middleware","init","enhancer","useBuildComponentCallback","React","Proxy"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/createChainOfResponsibility.tsx","../src/isReactComponent.ts"],"sourcesContent":["export { default as createChainOfResponsibility } from './createChainOfResponsibility.tsx';\nexport { type ComponentMiddleware } from './types.ts';\n","import { applyMiddleware } from 'handler-chain';\nimport React, { type ComponentType, type PropsWithChildren } from 'react';\n\nimport isReactComponent from './isReactComponent.ts';\nimport { type ComponentEnhancer, type ComponentMiddleware } from './types.ts';\n\nconst { createContext, isValidElement, memo, useCallback, useContext, useMemo } = React;\n\n// TODO: Simplify to ComponentType<Props> | undefined.\ntype ResultComponent<Props> = ComponentType<Props> | false | null | undefined;\n\ntype UseBuildComponentCallbackOptions<Props> = {\n fallbackComponent?: ResultComponent<Props> | undefined;\n};\n\ninterface UseBuildComponentCallback<Request, Props> {\n (request: Request, options?: undefined | UseBuildComponentCallbackOptions<Props>): ComponentType<Props> | undefined;\n}\n\ntype ProviderContext<Request, Props> = {\n get enhancer(): ComponentEnhancer<Request, 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\n ? { readonly init?: undefined }\n : Init extends undefined | void\n ? { readonly init?: Init }\n : { readonly init: Init });\n\ntype ProxyProps<Request, Props extends object> = Props & {\n readonly fallbackComponent?: ComponentType<Props> | undefined;\n readonly request: Request;\n};\n\ntype CreateChainOfResponsibilityOptions = {\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 readonly passModifiedRequest?: boolean | undefined;\n};\n\ntype AsMiddlewareProps<Request, Props, Init> = {\n readonly init: Init;\n readonly Next: ComponentType<Partial<Props>>;\n readonly request: Request;\n};\n\ntype AsMiddlewareComponentProps<Request, Props, Init> = Props & {\n readonly middleware: AsMiddlewareProps<Request, Props, Init>;\n};\n\ntype ChainOfResponsibility<Request, Props extends object, Init> = {\n readonly asMiddleware: (\n middlewareComponent: ComponentType<AsMiddlewareComponentProps<Request, Props, Init>>\n ) => ComponentMiddleware<Request, Props, Init>;\n readonly Provider: ComponentType<ProviderProps<Request, Props, Init>>;\n readonly Proxy: ComponentType<ProxyProps<Request, Props>>;\n readonly types: {\n readonly init: Init;\n readonly middleware: ComponentMiddleware<Request, Props, Init>;\n readonly middlewareComponentProps: AsMiddlewareComponentProps<Request, Props, Init>;\n readonly props: Props;\n readonly proxyProps: ProxyProps<Request, Props>;\n readonly request: Request;\n };\n readonly useBuildComponentCallback: () => UseBuildComponentCallback<Request, Props>;\n};\n\nfunction createChainOfResponsibility<Request = void, Props extends object = { readonly children?: never }, Init = void>(\n options: CreateChainOfResponsibilityOptions = {}\n): ChainOfResponsibility<Request, Props, Init> {\n const defaultUseBuildComponentCallback: ProviderContext<Request, Props> = {\n get enhancer() {\n return undefined;\n },\n useBuildComponentCallback(_request, options): ComponentType<Props> {\n if (!options?.fallbackComponent) {\n throw new Error('This component/hook cannot be used outside of its corresponding <Provider>');\n }\n\n return options.fallbackComponent;\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: (request: Request) => ComponentType<Props> | false | null | undefined) =>\n (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<ResultComponent<Props>, Request, Init>(\n ...[...patchedMiddleware, ...(parentEnhancer ? [() => parentEnhancer] : [])]\n )(init as Init),\n // TODO: \"middleware\" should be \"patchedMiddleware\", however, \"patchedMiddleware\" is not cached properly.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [init, middleware, parentEnhancer]\n );\n\n const useBuildComponentCallback = useCallback<UseBuildComponentCallback<Request, Props>>(\n (request, options = {}) => enhancer(() => options.fallbackComponent)(request) || undefined,\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({ fallbackComponent, request, ...props }: ProxyProps<Request, Props>) {\n const enhancer = useBuildComponentCallback();\n\n const Component = enhancer(request as Request, { fallbackComponent });\n\n return Component ? <Component {...(props as Props)} /> : null;\n }\n\n const asMiddleware: (\n middlewareComponent: ComponentType<AsMiddlewareComponentProps<Request, Props, Init>>\n ) => ComponentMiddleware<Request, Props, Init> =\n (\n MiddlewareComponent: ComponentType<AsMiddlewareComponentProps<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 // TODO: We should check \"props\" changes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\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 Object.freeze({\n asMiddleware,\n Provider: memo<ProviderProps<Request, Props, Init>>(ChainOfResponsibilityProvider),\n Proxy: memo<ProxyProps<Request, Props>>(Proxy),\n types: Object.freeze({\n middlewareComponentProps: undefined as unknown as AsMiddlewareComponentProps<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 proxyProps: undefined as unknown as ProxyProps<Request, Props>,\n request: undefined as unknown as Request\n }),\n useBuildComponentCallback\n });\n}\n\nexport default createChainOfResponsibility;\nexport {\n type ChainOfResponsibility,\n type CreateChainOfResponsibilityOptions,\n type ProxyProps,\n type UseBuildComponentCallback\n};\n","import {\n type ComponentClass,\n type ComponentType,\n type Consumer,\n type Fragment,\n type FunctionComponent,\n type Provider\n} from 'react';\nimport { custom } from 'valibot';\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.\nfunction 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\nconst reactComponent = () =>\n custom<ComponentType<unknown>>(value => isReactComponent(value), 'not a valid React component');\n\nexport default isReactComponent;\nexport { reactComponent };\n"],"mappings":"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","names":["React","options","middleware","init","enhancer","useBuildComponentCallback","Proxy"]}
|
|
@@ -1,6 +1,146 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
// src/createChainOfResponsibility.tsx
|
|
2
|
+
import { applyMiddleware } from "handler-chain";
|
|
3
|
+
import React from "react";
|
|
4
|
+
|
|
5
|
+
// src/isReactComponent.ts
|
|
6
|
+
import { custom } from "valibot";
|
|
7
|
+
function isConsumer(component) {
|
|
8
|
+
return component?.$$typeof?.toString() === "Symbol(react.context)";
|
|
9
|
+
}
|
|
10
|
+
function isProvider(component) {
|
|
11
|
+
return component?.$$typeof?.toString() === "Symbol(react.provider)";
|
|
12
|
+
}
|
|
13
|
+
function isFragment(component) {
|
|
14
|
+
return component?.toString() === "Symbol(react.fragment)";
|
|
15
|
+
}
|
|
16
|
+
function isFunctionComponent(component) {
|
|
17
|
+
if (typeof component === "function") {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
return isPureFunctionComponent(component);
|
|
21
|
+
}
|
|
22
|
+
function isPureFunctionComponent(component) {
|
|
23
|
+
return component?.$$typeof?.toString() === "Symbol(react.memo)" && isFunctionComponent(component.type);
|
|
24
|
+
}
|
|
25
|
+
function isComponentClass(component) {
|
|
26
|
+
return typeof component === "object" && typeof component?.["render"] === "function";
|
|
27
|
+
}
|
|
28
|
+
function isReactComponent(component) {
|
|
29
|
+
return isFunctionComponent(component) || isComponentClass(component) || isFragment(component) || isConsumer(component) || isProvider(component);
|
|
30
|
+
}
|
|
31
|
+
var isReactComponent_default = isReactComponent;
|
|
32
|
+
|
|
33
|
+
// src/createChainOfResponsibility.tsx
|
|
34
|
+
var { createContext, isValidElement, memo, useCallback, useContext, useMemo } = React;
|
|
35
|
+
function createChainOfResponsibility(options = {}) {
|
|
36
|
+
const defaultUseBuildComponentCallback = {
|
|
37
|
+
get enhancer() {
|
|
38
|
+
return void 0;
|
|
39
|
+
},
|
|
40
|
+
useBuildComponentCallback(_request, options2) {
|
|
41
|
+
if (!options2?.fallbackComponent) {
|
|
42
|
+
throw new Error("This component/hook cannot be used outside of its corresponding <Provider>");
|
|
43
|
+
}
|
|
44
|
+
return options2.fallbackComponent;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const context = createContext(defaultUseBuildComponentCallback);
|
|
48
|
+
function ChainOfResponsibilityProvider({ children, init, middleware }) {
|
|
49
|
+
if (!Array.isArray(middleware) || middleware.some((middleware2) => typeof middleware2 !== "function")) {
|
|
50
|
+
throw new Error("middleware prop must be an array of functions");
|
|
51
|
+
}
|
|
52
|
+
const patchedMiddleware = Object.freeze(
|
|
53
|
+
middleware ? middleware.map((fn) => (init2) => {
|
|
54
|
+
const enhancer2 = fn(init2);
|
|
55
|
+
return (next) => (originalRequest) => {
|
|
56
|
+
let hasReturned;
|
|
57
|
+
const returnValue = enhancer2((nextRequest) => {
|
|
58
|
+
if (hasReturned) {
|
|
59
|
+
throw new Error("next() cannot be called after the function had returned synchronously");
|
|
60
|
+
}
|
|
61
|
+
!options.passModifiedRequest && nextRequest !== originalRequest && console.warn(
|
|
62
|
+
'react-chain-of-responsibility: "options.passModifiedRequest" must be set to true to pass a different request object to next().'
|
|
63
|
+
);
|
|
64
|
+
return next(options.passModifiedRequest ? nextRequest : originalRequest);
|
|
65
|
+
})(originalRequest);
|
|
66
|
+
hasReturned = true;
|
|
67
|
+
if (isValidElement(returnValue)) {
|
|
68
|
+
throw new Error("middleware must not return React element directly");
|
|
69
|
+
} else if (returnValue !== false && returnValue !== null && typeof returnValue !== "undefined" && !isReactComponent_default(returnValue)) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
"middleware must return false, null, undefined, function component, or class component"
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
return returnValue;
|
|
75
|
+
};
|
|
76
|
+
}) : []
|
|
77
|
+
);
|
|
78
|
+
const { enhancer: parentEnhancer } = useContext(context);
|
|
79
|
+
const enhancer = useMemo(
|
|
80
|
+
() => (
|
|
81
|
+
// We are reversing because it is easier to read:
|
|
82
|
+
// - With reverse, [a, b, c] will become a(b(c(fn)))
|
|
83
|
+
// - Without reverse, [a, b, c] will become c(b(a(fn)))
|
|
84
|
+
applyMiddleware(
|
|
85
|
+
...[...patchedMiddleware, ...parentEnhancer ? [() => parentEnhancer] : []]
|
|
86
|
+
)(init)
|
|
87
|
+
),
|
|
88
|
+
// TODO: "middleware" should be "patchedMiddleware", however, "patchedMiddleware" is not cached properly.
|
|
89
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
90
|
+
[init, middleware, parentEnhancer]
|
|
91
|
+
);
|
|
92
|
+
const useBuildComponentCallback2 = useCallback(
|
|
93
|
+
(request, options2 = {}) => enhancer(() => options2.fallbackComponent)(request) || void 0,
|
|
94
|
+
[enhancer]
|
|
95
|
+
);
|
|
96
|
+
const contextValue = useMemo(
|
|
97
|
+
() => ({ enhancer, useBuildComponentCallback: useBuildComponentCallback2 }),
|
|
98
|
+
[enhancer, useBuildComponentCallback2]
|
|
99
|
+
);
|
|
100
|
+
return /* @__PURE__ */ React.createElement(context.Provider, { value: contextValue }, children);
|
|
101
|
+
}
|
|
102
|
+
const useBuildComponentCallback = () => useContext(context).useBuildComponentCallback;
|
|
103
|
+
function Proxy({ fallbackComponent, request, ...props }) {
|
|
104
|
+
const enhancer = useBuildComponentCallback();
|
|
105
|
+
const Component = enhancer(request, { fallbackComponent });
|
|
106
|
+
return Component ? /* @__PURE__ */ React.createElement(Component, { ...props }) : null;
|
|
107
|
+
}
|
|
108
|
+
const asMiddleware = (MiddlewareComponent) => (init) => (next) => (request) => {
|
|
109
|
+
const RawNextComponent = next(request);
|
|
110
|
+
const MiddlewareOf = (props) => {
|
|
111
|
+
const middleware = useMemo(
|
|
112
|
+
() => Object.freeze({
|
|
113
|
+
init,
|
|
114
|
+
Next: memo(
|
|
115
|
+
RawNextComponent ? (overridingProps) => /* @__PURE__ */ React.createElement(RawNextComponent, { ...props, ...overridingProps }) : () => null
|
|
116
|
+
),
|
|
117
|
+
request
|
|
118
|
+
}),
|
|
119
|
+
// TODO: We should check "props" changes.
|
|
120
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
121
|
+
[]
|
|
122
|
+
);
|
|
123
|
+
return /* @__PURE__ */ React.createElement(MiddlewareComponent, { ...props, middleware });
|
|
124
|
+
};
|
|
125
|
+
MiddlewareOf.displayName = `MiddlewareOf<${MiddlewareComponent.displayName || ""}>`;
|
|
126
|
+
return memo(MiddlewareOf);
|
|
127
|
+
};
|
|
128
|
+
return Object.freeze({
|
|
129
|
+
asMiddleware,
|
|
130
|
+
Provider: memo(ChainOfResponsibilityProvider),
|
|
131
|
+
Proxy: memo(Proxy),
|
|
132
|
+
types: Object.freeze({
|
|
133
|
+
middlewareComponentProps: void 0,
|
|
134
|
+
init: void 0,
|
|
135
|
+
middleware: void 0,
|
|
136
|
+
props: void 0,
|
|
137
|
+
proxyProps: void 0,
|
|
138
|
+
request: void 0
|
|
139
|
+
}),
|
|
140
|
+
useBuildComponentCallback
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
var createChainOfResponsibility_default = createChainOfResponsibility;
|
|
4
144
|
export {
|
|
5
145
|
createChainOfResponsibility_default as createChainOfResponsibility
|
|
6
146
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/createChainOfResponsibility.tsx","../src/isReactComponent.ts"],"sourcesContent":["import { applyMiddleware } from 'handler-chain';\nimport React, { type ComponentType, type PropsWithChildren } from 'react';\n\nimport isReactComponent from './isReactComponent.ts';\nimport { type ComponentEnhancer, type ComponentMiddleware } from './types.ts';\n\nconst { createContext, isValidElement, memo, useCallback, useContext, useMemo } = React;\n\n// TODO: Simplify to ComponentType<Props> | undefined.\ntype ResultComponent<Props> = ComponentType<Props> | false | null | undefined;\n\ntype UseBuildComponentCallbackOptions<Props> = {\n fallbackComponent?: ResultComponent<Props> | undefined;\n};\n\ninterface UseBuildComponentCallback<Request, Props> {\n (request: Request, options?: undefined | UseBuildComponentCallbackOptions<Props>): ComponentType<Props> | undefined;\n}\n\ntype ProviderContext<Request, Props> = {\n get enhancer(): ComponentEnhancer<Request, 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\n ? { readonly init?: undefined }\n : Init extends undefined | void\n ? { readonly init?: Init }\n : { readonly init: Init });\n\ntype ProxyProps<Request, Props extends object> = Props & {\n readonly fallbackComponent?: ComponentType<Props> | undefined;\n readonly request: Request;\n};\n\ntype CreateChainOfResponsibilityOptions = {\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 readonly passModifiedRequest?: boolean | undefined;\n};\n\ntype AsMiddlewareProps<Request, Props, Init> = {\n readonly init: Init;\n readonly Next: ComponentType<Partial<Props>>;\n readonly request: Request;\n};\n\ntype AsMiddlewareComponentProps<Request, Props, Init> = Props & {\n readonly middleware: AsMiddlewareProps<Request, Props, Init>;\n};\n\ntype ChainOfResponsibility<Request, Props extends object, Init> = {\n readonly asMiddleware: (\n middlewareComponent: ComponentType<AsMiddlewareComponentProps<Request, Props, Init>>\n ) => ComponentMiddleware<Request, Props, Init>;\n readonly Provider: ComponentType<ProviderProps<Request, Props, Init>>;\n readonly Proxy: ComponentType<ProxyProps<Request, Props>>;\n readonly types: {\n readonly init: Init;\n readonly middleware: ComponentMiddleware<Request, Props, Init>;\n readonly middlewareComponentProps: AsMiddlewareComponentProps<Request, Props, Init>;\n readonly props: Props;\n readonly proxyProps: ProxyProps<Request, Props>;\n readonly request: Request;\n };\n readonly useBuildComponentCallback: () => UseBuildComponentCallback<Request, Props>;\n};\n\nfunction createChainOfResponsibility<Request = void, Props extends object = { readonly children?: never }, Init = void>(\n options: CreateChainOfResponsibilityOptions = {}\n): ChainOfResponsibility<Request, Props, Init> {\n const defaultUseBuildComponentCallback: ProviderContext<Request, Props> = {\n get enhancer() {\n return undefined;\n },\n useBuildComponentCallback(_request, options): ComponentType<Props> {\n if (!options?.fallbackComponent) {\n throw new Error('This component/hook cannot be used outside of its corresponding <Provider>');\n }\n\n return options.fallbackComponent;\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: (request: Request) => ComponentType<Props> | false | null | undefined) =>\n (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<ResultComponent<Props>, Request, Init>(\n ...[...patchedMiddleware, ...(parentEnhancer ? [() => parentEnhancer] : [])]\n )(init as Init),\n // TODO: \"middleware\" should be \"patchedMiddleware\", however, \"patchedMiddleware\" is not cached properly.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [init, middleware, parentEnhancer]\n );\n\n const useBuildComponentCallback = useCallback<UseBuildComponentCallback<Request, Props>>(\n (request, options = {}) => enhancer(() => options.fallbackComponent)(request) || undefined,\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({ fallbackComponent, request, ...props }: ProxyProps<Request, Props>) {\n const enhancer = useBuildComponentCallback();\n\n const Component = enhancer(request as Request, { fallbackComponent });\n\n return Component ? <Component {...(props as Props)} /> : null;\n }\n\n const asMiddleware: (\n middlewareComponent: ComponentType<AsMiddlewareComponentProps<Request, Props, Init>>\n ) => ComponentMiddleware<Request, Props, Init> =\n (\n MiddlewareComponent: ComponentType<AsMiddlewareComponentProps<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 // TODO: We should check \"props\" changes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\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 Object.freeze({\n asMiddleware,\n Provider: memo<ProviderProps<Request, Props, Init>>(ChainOfResponsibilityProvider),\n Proxy: memo<ProxyProps<Request, Props>>(Proxy),\n types: Object.freeze({\n middlewareComponentProps: undefined as unknown as AsMiddlewareComponentProps<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 proxyProps: undefined as unknown as ProxyProps<Request, Props>,\n request: undefined as unknown as Request\n }),\n useBuildComponentCallback\n });\n}\n\nexport default createChainOfResponsibility;\nexport {\n type ChainOfResponsibility,\n type CreateChainOfResponsibilityOptions,\n type ProxyProps,\n type UseBuildComponentCallback\n};\n","import {\n type ComponentClass,\n type ComponentType,\n type Consumer,\n type Fragment,\n type FunctionComponent,\n type Provider\n} from 'react';\nimport { custom } from 'valibot';\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.\nfunction 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\nconst reactComponent = () =>\n custom<ComponentType<unknown>>(value => isReactComponent(value), 'not a valid React component');\n\nexport default isReactComponent;\nexport { reactComponent };\n"],"mappings":"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","names":["options","middleware","init","enhancer","useBuildComponentCallback"]}
|