react-chain-of-responsibility 0.4.0-main.4f9e7bf → 0.4.0-main.5f5cc2f
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.
|
@@ -64,6 +64,9 @@ var componentHandlerResultSchema = (0, import_valibot.custom)(
|
|
|
64
64
|
(value) => (0, import_valibot.safeParse)((0, import_valibot.object)({ render: (0, import_valibot.function_)() }), value).success && !!value && typeof value === "object" && DO_NOT_CREATE_THIS_OBJECT_YOURSELF in value,
|
|
65
65
|
"react-chain-of-responsibility: middleware must return value constructed by reactComponent()"
|
|
66
66
|
);
|
|
67
|
+
function createComponentHandlerResult(render) {
|
|
68
|
+
return Object.freeze({ [DO_NOT_CREATE_THIS_OBJECT_YOURSELF]: void 0, render });
|
|
69
|
+
}
|
|
67
70
|
function createChainOfResponsibility(options = {}) {
|
|
68
71
|
options = Object.freeze({ ...options });
|
|
69
72
|
const BuildContext = (0, import_react.createContext)(
|
|
@@ -80,17 +83,14 @@ function createChainOfResponsibility(options = {}) {
|
|
|
80
83
|
})
|
|
81
84
|
);
|
|
82
85
|
function reactComponent(component, bindProps) {
|
|
83
|
-
return
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
)
|
|
93
|
-
});
|
|
86
|
+
return createComponentHandlerResult((overridingProps) => /* @__PURE__ */ import_react.default.createElement(
|
|
87
|
+
ComponentWithProps,
|
|
88
|
+
{
|
|
89
|
+
bindProps,
|
|
90
|
+
component,
|
|
91
|
+
overridingProps
|
|
92
|
+
}
|
|
93
|
+
));
|
|
94
94
|
}
|
|
95
95
|
const ComponentWithProps = (0, import_react.memo)(function ComponentWithProps2({
|
|
96
96
|
bindProps,
|
|
@@ -107,7 +107,6 @@ function createChainOfResponsibility(options = {}) {
|
|
|
107
107
|
);
|
|
108
108
|
return /* @__PURE__ */ import_react.default.createElement(Component, { ...props, ...typeof bindProps === "function" ? bindProps(props) : bindProps });
|
|
109
109
|
});
|
|
110
|
-
const RENDER_CALLBACK_SYMBOL = `REACT_CHAIN_OF_RESPONSIBILITY:DO_NOT_USE_THIS_RENDER_CALLBACK`;
|
|
111
110
|
const useBuildRenderCallback = () => {
|
|
112
111
|
const { enhancer } = (0, import_react.useContext)(BuildContext);
|
|
113
112
|
return (0, import_react.useCallback)(
|
|
@@ -123,41 +122,21 @@ function createChainOfResponsibility(options = {}) {
|
|
|
123
122
|
);
|
|
124
123
|
return;
|
|
125
124
|
}
|
|
126
|
-
return
|
|
127
|
-
// Convert fallback component as renderer.
|
|
128
|
-
[DO_NOT_CREATE_THIS_OBJECT_YOURSELF]: void 0,
|
|
129
|
-
render: () => (
|
|
130
|
-
// Currently, there are no ways to set `bindProps` to `fallbackComponent`.
|
|
131
|
-
// `fallbackComponent` do not need `overridingProps` because it is the last one in the chain, it would not have the next() function.
|
|
132
|
-
/* @__PURE__ */ import_react.default.createElement(ComponentWithProps, { component: fallbackComponent })
|
|
133
|
-
)
|
|
134
|
-
});
|
|
125
|
+
return reactComponent(fallbackComponent);
|
|
135
126
|
})(request)
|
|
136
127
|
);
|
|
137
|
-
return result && ((
|
|
128
|
+
return result && ((originalProps) => (
|
|
138
129
|
// This is render function, we cannot call any hooks here.
|
|
139
|
-
/* @__PURE__ */ import_react.default.createElement(
|
|
140
|
-
RenderCallbackAsComponent,
|
|
141
|
-
{
|
|
142
|
-
...props,
|
|
143
|
-
...{
|
|
144
|
-
// TODO: Verify if `result.render` is stable or not, and check performance
|
|
145
|
-
[RENDER_CALLBACK_SYMBOL]: result.render
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
)
|
|
130
|
+
/* @__PURE__ */ import_react.default.createElement(BuildRenderCallback, { originalProps, render: result.render })
|
|
149
131
|
));
|
|
150
132
|
},
|
|
151
133
|
[enhancer]
|
|
152
134
|
);
|
|
153
135
|
};
|
|
154
|
-
|
|
155
|
-
[
|
|
156
|
-
...props
|
|
157
|
-
}) {
|
|
158
|
-
const context = (0, import_react.useMemo)(() => Object.freeze({ originalProps: props }), [props]);
|
|
136
|
+
function BuildRenderCallback({ originalProps, render }) {
|
|
137
|
+
const context = (0, import_react.useMemo)(() => Object.freeze({ originalProps }), [originalProps]);
|
|
159
138
|
return /* @__PURE__ */ import_react.default.createElement(RenderContext.Provider, { value: context }, render());
|
|
160
|
-
}
|
|
139
|
+
}
|
|
161
140
|
function ChainOfResponsibilityProvider({ children, init, middleware }) {
|
|
162
141
|
if (!Array.isArray(middleware) || middleware.some((middleware2) => typeof middleware2 !== "function")) {
|
|
163
142
|
throw new Error('react-chain-of-responsibility: "middleware" prop must be an array of functions');
|
|
@@ -208,7 +187,7 @@ function createChainOfResponsibility(options = {}) {
|
|
|
208
187
|
const MemoizedChainOfResponsibilityProvider = (0, import_react.memo)(ChainOfResponsibilityProvider);
|
|
209
188
|
return Object.freeze({
|
|
210
189
|
Provider: MemoizedChainOfResponsibilityProvider,
|
|
211
|
-
Proxy:
|
|
190
|
+
Proxy: ChainOfResponsibilityProxy,
|
|
212
191
|
reactComponent,
|
|
213
192
|
useBuildRenderCallback
|
|
214
193
|
// TODO: Consider adding back `asMiddleware`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.preview.ts","../src/preview/createChainOfResponsibilityAsRenderCallback.tsx","../src/preview/private/arePropsEqual.ts"],"sourcesContent":["export {\n default as createChainOfResponsibility,\n type ChainOfResponsibility,\n type ComponentEnhancer,\n type ComponentHandler,\n type ComponentHandlerResult,\n type ComponentMiddleware,\n type ComponentRenderer,\n type CreateChainOfResponsibilityOptions,\n type InferenceHelper,\n type InferInit,\n type InferMiddleware,\n type InferProps,\n type InferProviderProps,\n type InferProxyProps,\n type InferRequest,\n type ProviderProps,\n type ProxyProps,\n type ReactComponentHandlerResult,\n type UseBuildRenderCallback,\n type UseBuildRenderCallbackOptions\n} from './preview/createChainOfResponsibilityAsRenderCallback.tsx';\n","import { applyMiddleware } from 'handler-chain';\nimport React, {\n createContext,\n Fragment,\n memo,\n useCallback,\n useContext,\n useMemo,\n type ComponentType,\n type PropsWithChildren,\n type ReactNode\n} from 'react';\nimport { custom, function_, object, parse, safeParse } from 'valibot';\n\nimport arePropsEqual from './private/arePropsEqual.ts';\n\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.\ndeclare global {\n interface ArrayConstructor {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n isArray(arg: any): arg is readonly any[];\n }\n}\n\ntype BaseProps = object;\n\ntype CreateChainOfResponsibilityOptions = {\n /**\n * Allows one component to pass different set of props to its downstream component. Default is false.\n *\n * It is recommended to keep this settings as default to prevent newly added component from unexpectedly changing behavior of downstream components.\n */\n readonly allowOverrideProps?: boolean | undefined;\n\n /**\n * Allows a middleware to pass another request object when calling its next middleware. Default is false.\n *\n * It is recommended to keep this settings as default ot prevent newly added middleware from unexpectedly changing behavior of downstream middleware.\n *\n * To prevent upstream middleware from modifying the request, the request object should be set to be immutable through `Object.freeze`.\n */\n readonly passModifiedRequest?: boolean | undefined;\n};\n\ntype ChainOfResponsibility<Request, Props extends BaseProps, Init> = {\n readonly Provider: ComponentType<ProviderProps<Request, Props, Init>> & InferenceHelper<Request, Props, Init>;\n readonly Proxy: ComponentType<ProxyProps<Request, Props>>;\n readonly reactComponent: ReactComponentHandlerResult<Props>;\n readonly useBuildRenderCallback: () => UseBuildRenderCallback<Request, Props>;\n};\n\n// TODO: Maybe this one should be local.\n// Verify that reactComponent() from an instance of CoR should throw error when used in another instance of CoR.\nconst DO_NOT_CREATE_THIS_OBJECT_YOURSELF = Symbol();\n\ntype ComponentRenderer<Props> = (props: Props) => ReactNode;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst componentHandlerResultSchema = custom<ComponentHandlerResult<any>>(\n value =>\n safeParse(object({ render: function_() }), value).success &&\n !!value &&\n typeof value === 'object' &&\n DO_NOT_CREATE_THIS_OBJECT_YOURSELF in value,\n 'react-chain-of-responsibility: middleware must return value constructed by reactComponent()'\n);\n\ninterface ComponentHandlerResult<Props extends BaseProps> {\n readonly [DO_NOT_CREATE_THIS_OBJECT_YOURSELF]: undefined;\n readonly render: (overridingProps?: Partial<Props> | undefined) => ReactNode;\n}\n\ntype ComponentHandler<Request, Props extends BaseProps> = (\n request: Request\n) => ComponentHandlerResult<Props> | undefined;\n\ntype ComponentEnhancer<Request, Props extends BaseProps> = (\n next: ComponentHandler<Request, Props>\n) => ComponentHandler<Request, Props>;\n\ntype ComponentMiddleware<Request, Props extends BaseProps, Init = undefined> = (\n init: Init\n) => ComponentEnhancer<Request, Props>;\n\ntype ReactComponentHandlerResult<Props extends object> = <P extends Props>(\n component: ComponentType<P>,\n bindProps?:\n | (Partial<Props> & Omit<P, keyof Props>)\n | ((props: Props) => Partial<Props> & Omit<P, keyof Props>)\n | undefined\n) => ComponentHandlerResult<Props>;\n\ntype UseBuildRenderCallbackOptions<Props> = {\n readonly fallbackComponent?: ComponentType<Props> | undefined;\n};\n\ninterface UseBuildRenderCallback<Request, Props extends BaseProps> {\n (request: Request, options?: undefined | UseBuildRenderCallbackOptions<Props>): ComponentRenderer<Props> | undefined;\n}\n\ntype BuildContextType<Request, Props extends BaseProps> = {\n readonly enhancer: ComponentEnhancer<Request, Props>;\n};\n\ntype RenderContextType<Props> = {\n readonly originalProps: Props;\n};\n\ntype ProviderProps<Request, Props extends BaseProps, Init> = PropsWithChildren<{\n readonly 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 BaseProps> = Props & {\n readonly fallbackComponent?: ComponentType<Props> | undefined;\n readonly request: Request;\n};\n\ntype InferenceHelper<Request, Props extends BaseProps, Init> = {\n readonly '~types': {\n readonly init: Init;\n readonly middleware: ComponentMiddleware<Request, Props, Init>;\n readonly props: Props;\n readonly proxyProps: ProxyProps<Request, Props>;\n readonly providerProps: ProviderProps<Request, Props, Init>;\n readonly request: Request;\n };\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InferInit<T extends InferenceHelper<any, any, any>> = T['~types']['init'];\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InferMiddleware<T extends InferenceHelper<any, any, any>> = T['~types']['middleware'];\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InferProps<T extends InferenceHelper<any, any, any>> = T['~types']['props'];\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InferProxyProps<T extends InferenceHelper<any, any, any>> = T['~types']['proxyProps'];\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InferProviderProps<T extends InferenceHelper<any, any, any>> = T['~types']['providerProps'];\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InferRequest<T extends InferenceHelper<any, any, any>> = T['~types']['request'];\n\nfunction createChainOfResponsibility<\n Request = void,\n Props extends BaseProps = { readonly children?: never },\n Init = void\n>(options: CreateChainOfResponsibilityOptions = {}): ChainOfResponsibility<Request, Props, Init> {\n // Freeze options to prevent accidental change.\n options = Object.freeze({ ...options });\n\n const BuildContext = createContext<BuildContextType<Request, Props>>(\n Object.freeze({ enhancer: next => request => next(request) })\n );\n\n const RenderContext = createContext<RenderContextType<Props>>(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n new Proxy({} as any, {\n get() {\n // The following is assertion, there is no way to hit this line.\n /* istanbul ignore next */\n throw new Error(\n 'react-chain-of-responsibility: this hook cannot be used outside of <Proxy> and useBuildRenderCallback()'\n );\n }\n })\n );\n\n function reactComponent<P extends Props>(\n component: ComponentType<P>,\n bindProps?:\n | (Partial<Props> & Omit<P, keyof Props>)\n | ((props: Props) => Partial<Props> & Omit<P, keyof Props>)\n | undefined\n ): ComponentHandlerResult<Props> {\n return Object.freeze({\n [DO_NOT_CREATE_THIS_OBJECT_YOURSELF]: undefined,\n render: (overridingProps?: Partial<Props> | undefined) => (\n <ComponentWithProps\n bindProps={bindProps}\n component={component as ComponentType<Props>}\n overridingProps={overridingProps}\n />\n )\n });\n }\n\n const ComponentWithProps = memo(function ComponentWithProps({\n bindProps,\n component: Component,\n overridingProps\n }: {\n readonly bindProps?: Partial<Props> | ((props: Props) => Partial<Props>) | undefined;\n readonly component: ComponentType<Props>;\n readonly overridingProps?: Partial<Props> | undefined;\n }) {\n const { allowOverrideProps } = options;\n const { originalProps: renderCallbackProps } = useContext(RenderContext);\n\n if (overridingProps && !arePropsEqual(overridingProps, renderCallbackProps) && !allowOverrideProps) {\n console.warn('react-chain-of-responsibility: \"allowOverrideProps\" must be set to true to override props');\n }\n\n const props = Object.freeze(\n allowOverrideProps ? { ...renderCallbackProps, ...overridingProps } : { ...renderCallbackProps }\n );\n\n return <Component {...props} {...(typeof bindProps === 'function' ? bindProps(props) : bindProps)} />;\n });\n\n const RENDER_CALLBACK_SYMBOL = `REACT_CHAIN_OF_RESPONSIBILITY:DO_NOT_USE_THIS_RENDER_CALLBACK`;\n\n const useBuildRenderCallback: () => UseBuildRenderCallback<Request, Props> = () => {\n const { enhancer } = useContext(BuildContext);\n\n return useCallback(\n (request, buildOptions = {}) => {\n const result =\n // Put the \"fallbackComponent\" as the last one in the chain.\n enhancer(() => {\n const { fallbackComponent } = buildOptions;\n\n if (!fallbackComponent) {\n console.warn(\n 'react-chain-of-responsibility: the request has fall through all middleware, set \"fallbackComponent\" as a catchall',\n request\n );\n\n // For clarity, we are returning `undefined` instead of `() => undefined`.\n return;\n }\n\n return Object.freeze({\n // Convert fallback component as renderer.\n [DO_NOT_CREATE_THIS_OBJECT_YOURSELF]: undefined,\n render: () => (\n // Currently, there are no ways to set `bindProps` to `fallbackComponent`.\n // `fallbackComponent` do not need `overridingProps` because it is the last one in the chain, it would not have the next() function.\n <ComponentWithProps component={fallbackComponent} />\n )\n });\n })(request);\n\n return (\n result &&\n ((props: Props) => (\n // This is render function, we cannot call any hooks here.\n <RenderCallbackAsComponent\n {...props} // Spreading the props to leverage React.memo()\n {...{\n // TODO: Verify if `result.render` is stable or not, and check performance\n [RENDER_CALLBACK_SYMBOL]: result.render\n }}\n />\n ))\n );\n },\n [enhancer]\n );\n };\n\n type RenderCallbackAsComponentProps = Props & {\n // First render function does not need overrideProps.\n // Override props is for upstreamer to override props before passing to downsteamers.\n readonly [RENDER_CALLBACK_SYMBOL]: () => ReactNode;\n };\n\n const RenderCallbackAsComponent = memo(function RenderFunction({\n [RENDER_CALLBACK_SYMBOL]: render,\n ...props\n }: RenderCallbackAsComponentProps) {\n const context = useMemo<RenderContextType<Props>>(() => Object.freeze({ originalProps: props as Props }), [props]);\n\n return <RenderContext.Provider value={context}>{render()}</RenderContext.Provider>;\n });\n\n function ChainOfResponsibilityProvider({ children, init, middleware }: ProviderProps<Request, Props, Init>) {\n if (!Array.isArray(middleware) || middleware.some(middleware => typeof middleware !== 'function')) {\n throw new Error('react-chain-of-responsibility: \"middleware\" prop must be an array of functions');\n }\n\n // Remap the middleware, so all inputs/outputs are validated.\n const fortifiedMiddleware = useMemo(\n () =>\n Object.freeze(\n middleware.map<ComponentMiddleware<Request, Props, Init>>(fn => (init: Init) => {\n const enhancer = fn(init);\n\n return next => originalRequest => {\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(\n 'react-chain-of-responsibility: next() cannot be called after the function had returned synchronously'\n );\n }\n\n // We do not allow passing void/undefined to next() because it would be confusing whether to keep the original request or pass an undefined.\n !options.passModifiedRequest &&\n !Object.is(nextRequest, originalRequest) &&\n console.warn(\n 'react-chain-of-responsibility: next() must be called with the original request, otherwise, set \"options.passModifiedRequest\" to true to pass a different request object downstream'\n );\n\n return next(options.passModifiedRequest ? nextRequest : originalRequest);\n })(originalRequest);\n\n hasReturned = true;\n\n // Make sure the return value is built using our helper function for forward-compatibility reason.\n return returnValue && parse(componentHandlerResultSchema, returnValue);\n };\n })\n ),\n [middleware]\n );\n\n const { enhancer: parentEnhancer } = useContext(BuildContext);\n\n const enhancer = useMemo<ComponentEnhancer<Request, Props>>(\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<ComponentHandlerResult<Props> | undefined, Request, Init>(\n ...[...fortifiedMiddleware, ...[() => parentEnhancer]]\n )(init as Init),\n [init, fortifiedMiddleware, parentEnhancer]\n );\n\n const contextValue = useMemo<BuildContextType<Request, Props>>(() => Object.freeze({ enhancer }), [enhancer]);\n\n return <BuildContext.Provider value={contextValue}>{children}</BuildContext.Provider>;\n }\n\n function ChainOfResponsibilityProxy({ fallbackComponent, request, ...props }: ProxyProps<Request, Props>) {\n const result = useBuildRenderCallback()(request, { fallbackComponent })?.(props as Props);\n\n return result ? <Fragment>{result}</Fragment> : null;\n }\n\n const MemoizedChainOfResponsibilityProvider =\n memo<ProviderProps<Request, Props, Init>>(ChainOfResponsibilityProvider);\n\n return Object.freeze({\n Provider: MemoizedChainOfResponsibilityProvider as typeof MemoizedChainOfResponsibilityProvider &\n InferenceHelper<Request, Props, Init>,\n Proxy: memo<ProxyProps<Request, Props>>(ChainOfResponsibilityProxy),\n reactComponent,\n useBuildRenderCallback\n\n // TODO: Consider adding back `asMiddleware`.\n });\n}\n\nexport default createChainOfResponsibility;\nexport {\n type ChainOfResponsibility,\n type ComponentEnhancer,\n type ComponentHandler,\n type ComponentHandlerResult,\n type ComponentMiddleware,\n type ComponentRenderer,\n type CreateChainOfResponsibilityOptions,\n type InferenceHelper,\n type InferInit,\n type InferMiddleware,\n type InferProps,\n type InferProviderProps,\n type InferProxyProps,\n type InferRequest,\n type ProviderProps,\n type ProxyProps,\n type ReactComponentHandlerResult,\n type UseBuildRenderCallback,\n type UseBuildRenderCallbackOptions\n};\n","// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport default function arePropsEqual<T extends Record<string, any>>(x: T, y: T): boolean {\n if (Object.is(x, y)) {\n return true;\n }\n\n const mapOfX = new Map(Object.entries(x));\n const mapOfY = new Map(Object.entries(y));\n\n if (mapOfX.size !== mapOfY.size) {\n return false;\n }\n\n const keys = new Set([...mapOfX.keys(), ...mapOfY.keys()]);\n\n for (const key of keys) {\n if (!Object.is(mapOfX.get(key), mapOfY.get(key))) {\n return false;\n }\n }\n\n return true;\n}\n"],"mappings":"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","names":["React","ComponentWithProps","middleware","init","enhancer"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.preview.ts","../src/preview/createChainOfResponsibilityAsRenderCallback.tsx","../src/preview/private/arePropsEqual.ts"],"sourcesContent":["export {\n default as createChainOfResponsibility,\n type ChainOfResponsibility,\n type ComponentEnhancer,\n type ComponentHandler,\n type ComponentHandlerResult,\n type ComponentMiddleware,\n type ComponentRenderer,\n type CreateChainOfResponsibilityOptions,\n type InferenceHelper,\n type InferInit,\n type InferMiddleware,\n type InferProps,\n type InferProviderProps,\n type InferProxyProps,\n type InferRequest,\n type ProviderProps,\n type ProxyProps,\n type ReactComponentHandlerResult,\n type UseBuildRenderCallback,\n type UseBuildRenderCallbackOptions\n} from './preview/createChainOfResponsibilityAsRenderCallback.tsx';\n","import { applyMiddleware } from 'handler-chain';\nimport React, {\n createContext,\n Fragment,\n memo,\n useCallback,\n useContext,\n useMemo,\n type ComponentType,\n type PropsWithChildren,\n type ReactNode\n} from 'react';\nimport { custom, function_, object, parse, safeParse } from 'valibot';\n\nimport arePropsEqual from './private/arePropsEqual.ts';\n\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.\ndeclare global {\n interface ArrayConstructor {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n isArray(arg: any): arg is readonly any[];\n }\n}\n\ntype BaseProps = object;\n\ntype CreateChainOfResponsibilityOptions = {\n /**\n * Allows one component to pass different set of props to its downstream component. Default is false.\n *\n * It is recommended to keep this settings as default to prevent newly added component from unexpectedly changing behavior of downstream components.\n */\n readonly allowOverrideProps?: boolean | undefined;\n\n /**\n * Allows a middleware to pass another request object when calling its next middleware. Default is false.\n *\n * It is recommended to keep this settings as default ot prevent newly added middleware from unexpectedly changing behavior of downstream middleware.\n *\n * To prevent upstream middleware from modifying the request, the request object should be set to be immutable through `Object.freeze`.\n */\n readonly passModifiedRequest?: boolean | undefined;\n};\n\ntype ChainOfResponsibility<Request, Props extends BaseProps, Init> = {\n readonly Provider: ComponentType<ProviderProps<Request, Props, Init>> & InferenceHelper<Request, Props, Init>;\n readonly Proxy: ComponentType<ProxyProps<Request, Props>>;\n readonly reactComponent: ReactComponentHandlerResult<Props>;\n readonly useBuildRenderCallback: () => UseBuildRenderCallback<Request, Props>;\n};\n\n// TODO: Maybe this one should be local.\n// Verify that reactComponent() from an instance of CoR should throw error when used in another instance of CoR.\nconst DO_NOT_CREATE_THIS_OBJECT_YOURSELF = Symbol();\n\ntype ComponentRenderer<Props> = (props: Props) => ReactNode;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst componentHandlerResultSchema = custom<ComponentHandlerResult<any>>(\n value =>\n safeParse(object({ render: function_() }), value).success &&\n !!value &&\n typeof value === 'object' &&\n DO_NOT_CREATE_THIS_OBJECT_YOURSELF in value,\n 'react-chain-of-responsibility: middleware must return value constructed by reactComponent()'\n);\n\ninterface ComponentHandlerResult<Props extends BaseProps> {\n readonly [DO_NOT_CREATE_THIS_OBJECT_YOURSELF]: undefined;\n readonly render: (overridingProps?: Partial<Props> | undefined) => ReactNode;\n}\n\ntype ComponentHandler<Request, Props extends BaseProps> = (\n request: Request\n) => ComponentHandlerResult<Props> | undefined;\n\ntype ComponentEnhancer<Request, Props extends BaseProps> = (\n next: ComponentHandler<Request, Props>\n) => ComponentHandler<Request, Props>;\n\ntype ComponentMiddleware<Request, Props extends BaseProps, Init = undefined> = (\n init: Init\n) => ComponentEnhancer<Request, Props>;\n\ntype ReactComponentHandlerResult<Props extends object> = <P extends Props>(\n component: ComponentType<P>,\n bindProps?:\n | (Partial<Props> & Omit<P, keyof Props>)\n | ((props: Props) => Partial<Props> & Omit<P, keyof Props>)\n | undefined\n) => ComponentHandlerResult<Props>;\n\ntype UseBuildRenderCallbackOptions<Props> = {\n readonly fallbackComponent?: ComponentType<Props> | undefined;\n};\n\ninterface UseBuildRenderCallback<Request, Props extends BaseProps> {\n (request: Request, options?: undefined | UseBuildRenderCallbackOptions<Props>): ComponentRenderer<Props> | undefined;\n}\n\ntype BuildContextType<Request, Props extends BaseProps> = {\n readonly enhancer: ComponentEnhancer<Request, Props>;\n};\n\ntype RenderContextType<Props> = {\n readonly originalProps: Props;\n};\n\ntype ProviderProps<Request, Props extends BaseProps, Init> = PropsWithChildren<{\n readonly 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 BaseProps> = Props & {\n readonly fallbackComponent?: ComponentType<Props> | undefined;\n readonly request: Request;\n};\n\ntype InferenceHelper<Request, Props extends BaseProps, Init> = {\n readonly '~types': {\n readonly init: Init;\n readonly middleware: ComponentMiddleware<Request, Props, Init>;\n readonly props: Props;\n readonly proxyProps: ProxyProps<Request, Props>;\n readonly providerProps: ProviderProps<Request, Props, Init>;\n readonly request: Request;\n };\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InferInit<T extends InferenceHelper<any, any, any>> = T['~types']['init'];\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InferMiddleware<T extends InferenceHelper<any, any, any>> = T['~types']['middleware'];\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InferProps<T extends InferenceHelper<any, any, any>> = T['~types']['props'];\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InferProxyProps<T extends InferenceHelper<any, any, any>> = T['~types']['proxyProps'];\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InferProviderProps<T extends InferenceHelper<any, any, any>> = T['~types']['providerProps'];\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InferRequest<T extends InferenceHelper<any, any, any>> = T['~types']['request'];\n\nfunction createComponentHandlerResult<Props extends BaseProps>(\n render: (overridingProps?: Partial<Props> | undefined) => ReactNode\n): ComponentHandlerResult<Props> {\n return Object.freeze({ [DO_NOT_CREATE_THIS_OBJECT_YOURSELF]: undefined, render });\n}\n\nfunction createChainOfResponsibility<\n Request = void,\n Props extends BaseProps = { readonly children?: never },\n Init = void\n>(options: CreateChainOfResponsibilityOptions = {}): ChainOfResponsibility<Request, Props, Init> {\n // Freeze options to prevent accidental change.\n options = Object.freeze({ ...options });\n\n const BuildContext = createContext<BuildContextType<Request, Props>>(\n Object.freeze({ enhancer: next => request => next(request) })\n );\n\n const RenderContext = createContext<RenderContextType<Props>>(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n new Proxy({} as any, {\n get() {\n // The following is assertion, there is no way to hit this line.\n /* istanbul ignore next */\n throw new Error(\n 'react-chain-of-responsibility: this hook cannot be used outside of <Proxy> and useBuildRenderCallback()'\n );\n }\n })\n );\n\n function reactComponent<P extends Props>(\n component: ComponentType<P>,\n // For `bindProps` of type function, do not do side-effect in it, it may not be always called in all scenarios.\n bindProps?:\n | (Partial<Props> & Omit<P, keyof Props>)\n | ((props: Props) => Partial<Props> & Omit<P, keyof Props>)\n | undefined\n ): ComponentHandlerResult<Props> {\n return createComponentHandlerResult((overridingProps?: Partial<Props> | undefined) => (\n <ComponentWithProps\n bindProps={bindProps}\n component={component as ComponentType<Props>}\n overridingProps={overridingProps}\n />\n ));\n }\n\n const ComponentWithProps = memo(function ComponentWithProps({\n bindProps,\n component: Component,\n overridingProps\n }: {\n readonly bindProps?: Partial<Props> | ((props: Props) => Partial<Props>) | undefined;\n readonly component: ComponentType<Props>;\n readonly overridingProps?: Partial<Props> | undefined;\n }) {\n const { allowOverrideProps } = options;\n const { originalProps: renderCallbackProps } = useContext(RenderContext);\n\n if (overridingProps && !arePropsEqual(overridingProps, renderCallbackProps) && !allowOverrideProps) {\n console.warn('react-chain-of-responsibility: \"allowOverrideProps\" must be set to true to override props');\n }\n\n const props = Object.freeze(\n allowOverrideProps ? { ...renderCallbackProps, ...overridingProps } : { ...renderCallbackProps }\n );\n\n return <Component {...props} {...(typeof bindProps === 'function' ? bindProps(props) : bindProps)} />;\n });\n\n const useBuildRenderCallback: () => UseBuildRenderCallback<Request, Props> = () => {\n const { enhancer } = useContext(BuildContext);\n\n return useCallback(\n (request, buildOptions = {}) => {\n const result =\n // Put the \"fallbackComponent\" as the last one in the chain.\n enhancer(() => {\n const { fallbackComponent } = buildOptions;\n\n if (!fallbackComponent) {\n console.warn(\n 'react-chain-of-responsibility: the request has fall through all middleware, set \"fallbackComponent\" as a catchall',\n request\n );\n\n // For clarity, we are returning `undefined` instead of `() => undefined`.\n return;\n }\n\n // `fallbackComponent` do not need `overridingProps` because it is the last one in the chain, it would not have the next() function.\n return reactComponent(fallbackComponent);\n })(request);\n\n return (\n result &&\n ((originalProps: Props) => (\n // This is render function, we cannot call any hooks here.\n <BuildRenderCallback originalProps={originalProps} render={result.render} />\n ))\n );\n },\n [enhancer]\n );\n };\n\n type BuildRenderCallbackProps = {\n readonly originalProps: Props;\n readonly render: () => ReactNode;\n };\n\n // Do not memoize <BuildRenderCallback>.\n // `bindProps` may have side effect and we want to be re-rendered to capture the side-effect.\n // To prevent wasted render, web devs should memoize it themselves.\n function BuildRenderCallback({ originalProps, render }: BuildRenderCallbackProps) {\n const context = useMemo<RenderContextType<Props>>(() => Object.freeze({ originalProps }), [originalProps]);\n\n return <RenderContext.Provider value={context}>{render()}</RenderContext.Provider>;\n }\n\n function ChainOfResponsibilityProvider({ children, init, middleware }: ProviderProps<Request, Props, Init>) {\n if (!Array.isArray(middleware) || middleware.some(middleware => typeof middleware !== 'function')) {\n throw new Error('react-chain-of-responsibility: \"middleware\" prop must be an array of functions');\n }\n\n // Remap the middleware, so all inputs/outputs are validated.\n const fortifiedMiddleware = useMemo(\n () =>\n Object.freeze(\n middleware.map<ComponentMiddleware<Request, Props, Init>>(fn => (init: Init) => {\n const enhancer = fn(init);\n\n return next => originalRequest => {\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(\n 'react-chain-of-responsibility: next() cannot be called after the function had returned synchronously'\n );\n }\n\n // We do not allow passing void/undefined to next() because it would be confusing whether to keep the original request or pass an undefined.\n !options.passModifiedRequest &&\n !Object.is(nextRequest, originalRequest) &&\n console.warn(\n 'react-chain-of-responsibility: next() must be called with the original request, otherwise, set \"options.passModifiedRequest\" to true to pass a different request object downstream'\n );\n\n return next(options.passModifiedRequest ? nextRequest : originalRequest);\n })(originalRequest);\n\n hasReturned = true;\n\n // Make sure the return value is built using our helper function for forward-compatibility reason.\n return returnValue && parse(componentHandlerResultSchema, returnValue);\n };\n })\n ),\n [middleware]\n );\n\n const { enhancer: parentEnhancer } = useContext(BuildContext);\n\n const enhancer = useMemo<ComponentEnhancer<Request, Props>>(\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<ComponentHandlerResult<Props> | undefined, Request, Init>(\n ...[...fortifiedMiddleware, ...[() => parentEnhancer]]\n )(init as Init),\n [init, fortifiedMiddleware, parentEnhancer]\n );\n\n const contextValue = useMemo<BuildContextType<Request, Props>>(() => Object.freeze({ enhancer }), [enhancer]);\n\n return <BuildContext.Provider value={contextValue}>{children}</BuildContext.Provider>;\n }\n\n function ChainOfResponsibilityProxy({ fallbackComponent, request, ...props }: ProxyProps<Request, Props>) {\n const result = useBuildRenderCallback()(request, { fallbackComponent })?.(props as Props);\n\n return result ? <Fragment>{result}</Fragment> : null;\n }\n\n const MemoizedChainOfResponsibilityProvider =\n memo<ProviderProps<Request, Props, Init>>(ChainOfResponsibilityProvider);\n\n return Object.freeze({\n Provider: MemoizedChainOfResponsibilityProvider as typeof MemoizedChainOfResponsibilityProvider &\n InferenceHelper<Request, Props, Init>,\n Proxy: ChainOfResponsibilityProxy,\n reactComponent,\n useBuildRenderCallback\n\n // TODO: Consider adding back `asMiddleware`.\n });\n}\n\nexport default createChainOfResponsibility;\nexport {\n type ChainOfResponsibility,\n type ComponentEnhancer,\n type ComponentHandler,\n type ComponentHandlerResult,\n type ComponentMiddleware,\n type ComponentRenderer,\n type CreateChainOfResponsibilityOptions,\n type InferenceHelper,\n type InferInit,\n type InferMiddleware,\n type InferProps,\n type InferProviderProps,\n type InferProxyProps,\n type InferRequest,\n type ProviderProps,\n type ProxyProps,\n type ReactComponentHandlerResult,\n type UseBuildRenderCallback,\n type UseBuildRenderCallbackOptions\n};\n","// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport default function arePropsEqual<T extends Record<string, any>>(x: T, y: T): boolean {\n if (Object.is(x, y)) {\n return true;\n }\n\n const mapOfX = new Map(Object.entries(x));\n const mapOfY = new Map(Object.entries(y));\n\n if (mapOfX.size !== mapOfY.size) {\n return false;\n }\n\n const keys = new Set([...mapOfX.keys(), ...mapOfY.keys()]);\n\n for (const key of keys) {\n if (!Object.is(mapOfX.get(key), mapOfY.get(key))) {\n return false;\n }\n }\n\n return true;\n}\n"],"mappings":"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","names":["React","ComponentWithProps","middleware","init","enhancer"]}
|
|
@@ -35,6 +35,9 @@ var componentHandlerResultSchema = custom(
|
|
|
35
35
|
(value) => safeParse(object({ render: function_() }), value).success && !!value && typeof value === "object" && DO_NOT_CREATE_THIS_OBJECT_YOURSELF in value,
|
|
36
36
|
"react-chain-of-responsibility: middleware must return value constructed by reactComponent()"
|
|
37
37
|
);
|
|
38
|
+
function createComponentHandlerResult(render) {
|
|
39
|
+
return Object.freeze({ [DO_NOT_CREATE_THIS_OBJECT_YOURSELF]: void 0, render });
|
|
40
|
+
}
|
|
38
41
|
function createChainOfResponsibility(options = {}) {
|
|
39
42
|
options = Object.freeze({ ...options });
|
|
40
43
|
const BuildContext = createContext(
|
|
@@ -51,17 +54,14 @@ function createChainOfResponsibility(options = {}) {
|
|
|
51
54
|
})
|
|
52
55
|
);
|
|
53
56
|
function reactComponent(component, bindProps) {
|
|
54
|
-
return
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
)
|
|
64
|
-
});
|
|
57
|
+
return createComponentHandlerResult((overridingProps) => /* @__PURE__ */ React.createElement(
|
|
58
|
+
ComponentWithProps,
|
|
59
|
+
{
|
|
60
|
+
bindProps,
|
|
61
|
+
component,
|
|
62
|
+
overridingProps
|
|
63
|
+
}
|
|
64
|
+
));
|
|
65
65
|
}
|
|
66
66
|
const ComponentWithProps = memo(function ComponentWithProps2({
|
|
67
67
|
bindProps,
|
|
@@ -78,7 +78,6 @@ function createChainOfResponsibility(options = {}) {
|
|
|
78
78
|
);
|
|
79
79
|
return /* @__PURE__ */ React.createElement(Component, { ...props, ...typeof bindProps === "function" ? bindProps(props) : bindProps });
|
|
80
80
|
});
|
|
81
|
-
const RENDER_CALLBACK_SYMBOL = `REACT_CHAIN_OF_RESPONSIBILITY:DO_NOT_USE_THIS_RENDER_CALLBACK`;
|
|
82
81
|
const useBuildRenderCallback = () => {
|
|
83
82
|
const { enhancer } = useContext(BuildContext);
|
|
84
83
|
return useCallback(
|
|
@@ -94,41 +93,21 @@ function createChainOfResponsibility(options = {}) {
|
|
|
94
93
|
);
|
|
95
94
|
return;
|
|
96
95
|
}
|
|
97
|
-
return
|
|
98
|
-
// Convert fallback component as renderer.
|
|
99
|
-
[DO_NOT_CREATE_THIS_OBJECT_YOURSELF]: void 0,
|
|
100
|
-
render: () => (
|
|
101
|
-
// Currently, there are no ways to set `bindProps` to `fallbackComponent`.
|
|
102
|
-
// `fallbackComponent` do not need `overridingProps` because it is the last one in the chain, it would not have the next() function.
|
|
103
|
-
/* @__PURE__ */ React.createElement(ComponentWithProps, { component: fallbackComponent })
|
|
104
|
-
)
|
|
105
|
-
});
|
|
96
|
+
return reactComponent(fallbackComponent);
|
|
106
97
|
})(request)
|
|
107
98
|
);
|
|
108
|
-
return result && ((
|
|
99
|
+
return result && ((originalProps) => (
|
|
109
100
|
// This is render function, we cannot call any hooks here.
|
|
110
|
-
/* @__PURE__ */ React.createElement(
|
|
111
|
-
RenderCallbackAsComponent,
|
|
112
|
-
{
|
|
113
|
-
...props,
|
|
114
|
-
...{
|
|
115
|
-
// TODO: Verify if `result.render` is stable or not, and check performance
|
|
116
|
-
[RENDER_CALLBACK_SYMBOL]: result.render
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
)
|
|
101
|
+
/* @__PURE__ */ React.createElement(BuildRenderCallback, { originalProps, render: result.render })
|
|
120
102
|
));
|
|
121
103
|
},
|
|
122
104
|
[enhancer]
|
|
123
105
|
);
|
|
124
106
|
};
|
|
125
|
-
|
|
126
|
-
[
|
|
127
|
-
...props
|
|
128
|
-
}) {
|
|
129
|
-
const context = useMemo(() => Object.freeze({ originalProps: props }), [props]);
|
|
107
|
+
function BuildRenderCallback({ originalProps, render }) {
|
|
108
|
+
const context = useMemo(() => Object.freeze({ originalProps }), [originalProps]);
|
|
130
109
|
return /* @__PURE__ */ React.createElement(RenderContext.Provider, { value: context }, render());
|
|
131
|
-
}
|
|
110
|
+
}
|
|
132
111
|
function ChainOfResponsibilityProvider({ children, init, middleware }) {
|
|
133
112
|
if (!Array.isArray(middleware) || middleware.some((middleware2) => typeof middleware2 !== "function")) {
|
|
134
113
|
throw new Error('react-chain-of-responsibility: "middleware" prop must be an array of functions');
|
|
@@ -179,7 +158,7 @@ function createChainOfResponsibility(options = {}) {
|
|
|
179
158
|
const MemoizedChainOfResponsibilityProvider = memo(ChainOfResponsibilityProvider);
|
|
180
159
|
return Object.freeze({
|
|
181
160
|
Provider: MemoizedChainOfResponsibilityProvider,
|
|
182
|
-
Proxy:
|
|
161
|
+
Proxy: ChainOfResponsibilityProxy,
|
|
183
162
|
reactComponent,
|
|
184
163
|
useBuildRenderCallback
|
|
185
164
|
// TODO: Consider adding back `asMiddleware`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/preview/createChainOfResponsibilityAsRenderCallback.tsx","../src/preview/private/arePropsEqual.ts"],"sourcesContent":["import { applyMiddleware } from 'handler-chain';\nimport React, {\n createContext,\n Fragment,\n memo,\n useCallback,\n useContext,\n useMemo,\n type ComponentType,\n type PropsWithChildren,\n type ReactNode\n} from 'react';\nimport { custom, function_, object, parse, safeParse } from 'valibot';\n\nimport arePropsEqual from './private/arePropsEqual.ts';\n\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.\ndeclare global {\n interface ArrayConstructor {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n isArray(arg: any): arg is readonly any[];\n }\n}\n\ntype BaseProps = object;\n\ntype CreateChainOfResponsibilityOptions = {\n /**\n * Allows one component to pass different set of props to its downstream component. Default is false.\n *\n * It is recommended to keep this settings as default to prevent newly added component from unexpectedly changing behavior of downstream components.\n */\n readonly allowOverrideProps?: boolean | undefined;\n\n /**\n * Allows a middleware to pass another request object when calling its next middleware. Default is false.\n *\n * It is recommended to keep this settings as default ot prevent newly added middleware from unexpectedly changing behavior of downstream middleware.\n *\n * To prevent upstream middleware from modifying the request, the request object should be set to be immutable through `Object.freeze`.\n */\n readonly passModifiedRequest?: boolean | undefined;\n};\n\ntype ChainOfResponsibility<Request, Props extends BaseProps, Init> = {\n readonly Provider: ComponentType<ProviderProps<Request, Props, Init>> & InferenceHelper<Request, Props, Init>;\n readonly Proxy: ComponentType<ProxyProps<Request, Props>>;\n readonly reactComponent: ReactComponentHandlerResult<Props>;\n readonly useBuildRenderCallback: () => UseBuildRenderCallback<Request, Props>;\n};\n\n// TODO: Maybe this one should be local.\n// Verify that reactComponent() from an instance of CoR should throw error when used in another instance of CoR.\nconst DO_NOT_CREATE_THIS_OBJECT_YOURSELF = Symbol();\n\ntype ComponentRenderer<Props> = (props: Props) => ReactNode;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst componentHandlerResultSchema = custom<ComponentHandlerResult<any>>(\n value =>\n safeParse(object({ render: function_() }), value).success &&\n !!value &&\n typeof value === 'object' &&\n DO_NOT_CREATE_THIS_OBJECT_YOURSELF in value,\n 'react-chain-of-responsibility: middleware must return value constructed by reactComponent()'\n);\n\ninterface ComponentHandlerResult<Props extends BaseProps> {\n readonly [DO_NOT_CREATE_THIS_OBJECT_YOURSELF]: undefined;\n readonly render: (overridingProps?: Partial<Props> | undefined) => ReactNode;\n}\n\ntype ComponentHandler<Request, Props extends BaseProps> = (\n request: Request\n) => ComponentHandlerResult<Props> | undefined;\n\ntype ComponentEnhancer<Request, Props extends BaseProps> = (\n next: ComponentHandler<Request, Props>\n) => ComponentHandler<Request, Props>;\n\ntype ComponentMiddleware<Request, Props extends BaseProps, Init = undefined> = (\n init: Init\n) => ComponentEnhancer<Request, Props>;\n\ntype ReactComponentHandlerResult<Props extends object> = <P extends Props>(\n component: ComponentType<P>,\n bindProps?:\n | (Partial<Props> & Omit<P, keyof Props>)\n | ((props: Props) => Partial<Props> & Omit<P, keyof Props>)\n | undefined\n) => ComponentHandlerResult<Props>;\n\ntype UseBuildRenderCallbackOptions<Props> = {\n readonly fallbackComponent?: ComponentType<Props> | undefined;\n};\n\ninterface UseBuildRenderCallback<Request, Props extends BaseProps> {\n (request: Request, options?: undefined | UseBuildRenderCallbackOptions<Props>): ComponentRenderer<Props> | undefined;\n}\n\ntype BuildContextType<Request, Props extends BaseProps> = {\n readonly enhancer: ComponentEnhancer<Request, Props>;\n};\n\ntype RenderContextType<Props> = {\n readonly originalProps: Props;\n};\n\ntype ProviderProps<Request, Props extends BaseProps, Init> = PropsWithChildren<{\n readonly 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 BaseProps> = Props & {\n readonly fallbackComponent?: ComponentType<Props> | undefined;\n readonly request: Request;\n};\n\ntype InferenceHelper<Request, Props extends BaseProps, Init> = {\n readonly '~types': {\n readonly init: Init;\n readonly middleware: ComponentMiddleware<Request, Props, Init>;\n readonly props: Props;\n readonly proxyProps: ProxyProps<Request, Props>;\n readonly providerProps: ProviderProps<Request, Props, Init>;\n readonly request: Request;\n };\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InferInit<T extends InferenceHelper<any, any, any>> = T['~types']['init'];\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InferMiddleware<T extends InferenceHelper<any, any, any>> = T['~types']['middleware'];\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InferProps<T extends InferenceHelper<any, any, any>> = T['~types']['props'];\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InferProxyProps<T extends InferenceHelper<any, any, any>> = T['~types']['proxyProps'];\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InferProviderProps<T extends InferenceHelper<any, any, any>> = T['~types']['providerProps'];\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InferRequest<T extends InferenceHelper<any, any, any>> = T['~types']['request'];\n\nfunction createChainOfResponsibility<\n Request = void,\n Props extends BaseProps = { readonly children?: never },\n Init = void\n>(options: CreateChainOfResponsibilityOptions = {}): ChainOfResponsibility<Request, Props, Init> {\n // Freeze options to prevent accidental change.\n options = Object.freeze({ ...options });\n\n const BuildContext = createContext<BuildContextType<Request, Props>>(\n Object.freeze({ enhancer: next => request => next(request) })\n );\n\n const RenderContext = createContext<RenderContextType<Props>>(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n new Proxy({} as any, {\n get() {\n // The following is assertion, there is no way to hit this line.\n /* istanbul ignore next */\n throw new Error(\n 'react-chain-of-responsibility: this hook cannot be used outside of <Proxy> and useBuildRenderCallback()'\n );\n }\n })\n );\n\n function reactComponent<P extends Props>(\n component: ComponentType<P>,\n bindProps?:\n | (Partial<Props> & Omit<P, keyof Props>)\n | ((props: Props) => Partial<Props> & Omit<P, keyof Props>)\n | undefined\n ): ComponentHandlerResult<Props> {\n return Object.freeze({\n [DO_NOT_CREATE_THIS_OBJECT_YOURSELF]: undefined,\n render: (overridingProps?: Partial<Props> | undefined) => (\n <ComponentWithProps\n bindProps={bindProps}\n component={component as ComponentType<Props>}\n overridingProps={overridingProps}\n />\n )\n });\n }\n\n const ComponentWithProps = memo(function ComponentWithProps({\n bindProps,\n component: Component,\n overridingProps\n }: {\n readonly bindProps?: Partial<Props> | ((props: Props) => Partial<Props>) | undefined;\n readonly component: ComponentType<Props>;\n readonly overridingProps?: Partial<Props> | undefined;\n }) {\n const { allowOverrideProps } = options;\n const { originalProps: renderCallbackProps } = useContext(RenderContext);\n\n if (overridingProps && !arePropsEqual(overridingProps, renderCallbackProps) && !allowOverrideProps) {\n console.warn('react-chain-of-responsibility: \"allowOverrideProps\" must be set to true to override props');\n }\n\n const props = Object.freeze(\n allowOverrideProps ? { ...renderCallbackProps, ...overridingProps } : { ...renderCallbackProps }\n );\n\n return <Component {...props} {...(typeof bindProps === 'function' ? bindProps(props) : bindProps)} />;\n });\n\n const RENDER_CALLBACK_SYMBOL = `REACT_CHAIN_OF_RESPONSIBILITY:DO_NOT_USE_THIS_RENDER_CALLBACK`;\n\n const useBuildRenderCallback: () => UseBuildRenderCallback<Request, Props> = () => {\n const { enhancer } = useContext(BuildContext);\n\n return useCallback(\n (request, buildOptions = {}) => {\n const result =\n // Put the \"fallbackComponent\" as the last one in the chain.\n enhancer(() => {\n const { fallbackComponent } = buildOptions;\n\n if (!fallbackComponent) {\n console.warn(\n 'react-chain-of-responsibility: the request has fall through all middleware, set \"fallbackComponent\" as a catchall',\n request\n );\n\n // For clarity, we are returning `undefined` instead of `() => undefined`.\n return;\n }\n\n return Object.freeze({\n // Convert fallback component as renderer.\n [DO_NOT_CREATE_THIS_OBJECT_YOURSELF]: undefined,\n render: () => (\n // Currently, there are no ways to set `bindProps` to `fallbackComponent`.\n // `fallbackComponent` do not need `overridingProps` because it is the last one in the chain, it would not have the next() function.\n <ComponentWithProps component={fallbackComponent} />\n )\n });\n })(request);\n\n return (\n result &&\n ((props: Props) => (\n // This is render function, we cannot call any hooks here.\n <RenderCallbackAsComponent\n {...props} // Spreading the props to leverage React.memo()\n {...{\n // TODO: Verify if `result.render` is stable or not, and check performance\n [RENDER_CALLBACK_SYMBOL]: result.render\n }}\n />\n ))\n );\n },\n [enhancer]\n );\n };\n\n type RenderCallbackAsComponentProps = Props & {\n // First render function does not need overrideProps.\n // Override props is for upstreamer to override props before passing to downsteamers.\n readonly [RENDER_CALLBACK_SYMBOL]: () => ReactNode;\n };\n\n const RenderCallbackAsComponent = memo(function RenderFunction({\n [RENDER_CALLBACK_SYMBOL]: render,\n ...props\n }: RenderCallbackAsComponentProps) {\n const context = useMemo<RenderContextType<Props>>(() => Object.freeze({ originalProps: props as Props }), [props]);\n\n return <RenderContext.Provider value={context}>{render()}</RenderContext.Provider>;\n });\n\n function ChainOfResponsibilityProvider({ children, init, middleware }: ProviderProps<Request, Props, Init>) {\n if (!Array.isArray(middleware) || middleware.some(middleware => typeof middleware !== 'function')) {\n throw new Error('react-chain-of-responsibility: \"middleware\" prop must be an array of functions');\n }\n\n // Remap the middleware, so all inputs/outputs are validated.\n const fortifiedMiddleware = useMemo(\n () =>\n Object.freeze(\n middleware.map<ComponentMiddleware<Request, Props, Init>>(fn => (init: Init) => {\n const enhancer = fn(init);\n\n return next => originalRequest => {\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(\n 'react-chain-of-responsibility: next() cannot be called after the function had returned synchronously'\n );\n }\n\n // We do not allow passing void/undefined to next() because it would be confusing whether to keep the original request or pass an undefined.\n !options.passModifiedRequest &&\n !Object.is(nextRequest, originalRequest) &&\n console.warn(\n 'react-chain-of-responsibility: next() must be called with the original request, otherwise, set \"options.passModifiedRequest\" to true to pass a different request object downstream'\n );\n\n return next(options.passModifiedRequest ? nextRequest : originalRequest);\n })(originalRequest);\n\n hasReturned = true;\n\n // Make sure the return value is built using our helper function for forward-compatibility reason.\n return returnValue && parse(componentHandlerResultSchema, returnValue);\n };\n })\n ),\n [middleware]\n );\n\n const { enhancer: parentEnhancer } = useContext(BuildContext);\n\n const enhancer = useMemo<ComponentEnhancer<Request, Props>>(\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<ComponentHandlerResult<Props> | undefined, Request, Init>(\n ...[...fortifiedMiddleware, ...[() => parentEnhancer]]\n )(init as Init),\n [init, fortifiedMiddleware, parentEnhancer]\n );\n\n const contextValue = useMemo<BuildContextType<Request, Props>>(() => Object.freeze({ enhancer }), [enhancer]);\n\n return <BuildContext.Provider value={contextValue}>{children}</BuildContext.Provider>;\n }\n\n function ChainOfResponsibilityProxy({ fallbackComponent, request, ...props }: ProxyProps<Request, Props>) {\n const result = useBuildRenderCallback()(request, { fallbackComponent })?.(props as Props);\n\n return result ? <Fragment>{result}</Fragment> : null;\n }\n\n const MemoizedChainOfResponsibilityProvider =\n memo<ProviderProps<Request, Props, Init>>(ChainOfResponsibilityProvider);\n\n return Object.freeze({\n Provider: MemoizedChainOfResponsibilityProvider as typeof MemoizedChainOfResponsibilityProvider &\n InferenceHelper<Request, Props, Init>,\n Proxy: memo<ProxyProps<Request, Props>>(ChainOfResponsibilityProxy),\n reactComponent,\n useBuildRenderCallback\n\n // TODO: Consider adding back `asMiddleware`.\n });\n}\n\nexport default createChainOfResponsibility;\nexport {\n type ChainOfResponsibility,\n type ComponentEnhancer,\n type ComponentHandler,\n type ComponentHandlerResult,\n type ComponentMiddleware,\n type ComponentRenderer,\n type CreateChainOfResponsibilityOptions,\n type InferenceHelper,\n type InferInit,\n type InferMiddleware,\n type InferProps,\n type InferProviderProps,\n type InferProxyProps,\n type InferRequest,\n type ProviderProps,\n type ProxyProps,\n type ReactComponentHandlerResult,\n type UseBuildRenderCallback,\n type UseBuildRenderCallbackOptions\n};\n","// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport default function arePropsEqual<T extends Record<string, any>>(x: T, y: T): boolean {\n if (Object.is(x, y)) {\n return true;\n }\n\n const mapOfX = new Map(Object.entries(x));\n const mapOfY = new Map(Object.entries(y));\n\n if (mapOfX.size !== mapOfY.size) {\n return false;\n }\n\n const keys = new Set([...mapOfX.keys(), ...mapOfY.keys()]);\n\n for (const key of keys) {\n if (!Object.is(mapOfX.get(key), mapOfY.get(key))) {\n return false;\n }\n }\n\n return true;\n}\n"],"mappings":"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","names":["ComponentWithProps","middleware","init","enhancer"]}
|
|
1
|
+
{"version":3,"sources":["../src/preview/createChainOfResponsibilityAsRenderCallback.tsx","../src/preview/private/arePropsEqual.ts"],"sourcesContent":["import { applyMiddleware } from 'handler-chain';\nimport React, {\n createContext,\n Fragment,\n memo,\n useCallback,\n useContext,\n useMemo,\n type ComponentType,\n type PropsWithChildren,\n type ReactNode\n} from 'react';\nimport { custom, function_, object, parse, safeParse } from 'valibot';\n\nimport arePropsEqual from './private/arePropsEqual.ts';\n\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.\ndeclare global {\n interface ArrayConstructor {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n isArray(arg: any): arg is readonly any[];\n }\n}\n\ntype BaseProps = object;\n\ntype CreateChainOfResponsibilityOptions = {\n /**\n * Allows one component to pass different set of props to its downstream component. Default is false.\n *\n * It is recommended to keep this settings as default to prevent newly added component from unexpectedly changing behavior of downstream components.\n */\n readonly allowOverrideProps?: boolean | undefined;\n\n /**\n * Allows a middleware to pass another request object when calling its next middleware. Default is false.\n *\n * It is recommended to keep this settings as default ot prevent newly added middleware from unexpectedly changing behavior of downstream middleware.\n *\n * To prevent upstream middleware from modifying the request, the request object should be set to be immutable through `Object.freeze`.\n */\n readonly passModifiedRequest?: boolean | undefined;\n};\n\ntype ChainOfResponsibility<Request, Props extends BaseProps, Init> = {\n readonly Provider: ComponentType<ProviderProps<Request, Props, Init>> & InferenceHelper<Request, Props, Init>;\n readonly Proxy: ComponentType<ProxyProps<Request, Props>>;\n readonly reactComponent: ReactComponentHandlerResult<Props>;\n readonly useBuildRenderCallback: () => UseBuildRenderCallback<Request, Props>;\n};\n\n// TODO: Maybe this one should be local.\n// Verify that reactComponent() from an instance of CoR should throw error when used in another instance of CoR.\nconst DO_NOT_CREATE_THIS_OBJECT_YOURSELF = Symbol();\n\ntype ComponentRenderer<Props> = (props: Props) => ReactNode;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst componentHandlerResultSchema = custom<ComponentHandlerResult<any>>(\n value =>\n safeParse(object({ render: function_() }), value).success &&\n !!value &&\n typeof value === 'object' &&\n DO_NOT_CREATE_THIS_OBJECT_YOURSELF in value,\n 'react-chain-of-responsibility: middleware must return value constructed by reactComponent()'\n);\n\ninterface ComponentHandlerResult<Props extends BaseProps> {\n readonly [DO_NOT_CREATE_THIS_OBJECT_YOURSELF]: undefined;\n readonly render: (overridingProps?: Partial<Props> | undefined) => ReactNode;\n}\n\ntype ComponentHandler<Request, Props extends BaseProps> = (\n request: Request\n) => ComponentHandlerResult<Props> | undefined;\n\ntype ComponentEnhancer<Request, Props extends BaseProps> = (\n next: ComponentHandler<Request, Props>\n) => ComponentHandler<Request, Props>;\n\ntype ComponentMiddleware<Request, Props extends BaseProps, Init = undefined> = (\n init: Init\n) => ComponentEnhancer<Request, Props>;\n\ntype ReactComponentHandlerResult<Props extends object> = <P extends Props>(\n component: ComponentType<P>,\n bindProps?:\n | (Partial<Props> & Omit<P, keyof Props>)\n | ((props: Props) => Partial<Props> & Omit<P, keyof Props>)\n | undefined\n) => ComponentHandlerResult<Props>;\n\ntype UseBuildRenderCallbackOptions<Props> = {\n readonly fallbackComponent?: ComponentType<Props> | undefined;\n};\n\ninterface UseBuildRenderCallback<Request, Props extends BaseProps> {\n (request: Request, options?: undefined | UseBuildRenderCallbackOptions<Props>): ComponentRenderer<Props> | undefined;\n}\n\ntype BuildContextType<Request, Props extends BaseProps> = {\n readonly enhancer: ComponentEnhancer<Request, Props>;\n};\n\ntype RenderContextType<Props> = {\n readonly originalProps: Props;\n};\n\ntype ProviderProps<Request, Props extends BaseProps, Init> = PropsWithChildren<{\n readonly 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 BaseProps> = Props & {\n readonly fallbackComponent?: ComponentType<Props> | undefined;\n readonly request: Request;\n};\n\ntype InferenceHelper<Request, Props extends BaseProps, Init> = {\n readonly '~types': {\n readonly init: Init;\n readonly middleware: ComponentMiddleware<Request, Props, Init>;\n readonly props: Props;\n readonly proxyProps: ProxyProps<Request, Props>;\n readonly providerProps: ProviderProps<Request, Props, Init>;\n readonly request: Request;\n };\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InferInit<T extends InferenceHelper<any, any, any>> = T['~types']['init'];\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InferMiddleware<T extends InferenceHelper<any, any, any>> = T['~types']['middleware'];\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InferProps<T extends InferenceHelper<any, any, any>> = T['~types']['props'];\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InferProxyProps<T extends InferenceHelper<any, any, any>> = T['~types']['proxyProps'];\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InferProviderProps<T extends InferenceHelper<any, any, any>> = T['~types']['providerProps'];\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InferRequest<T extends InferenceHelper<any, any, any>> = T['~types']['request'];\n\nfunction createComponentHandlerResult<Props extends BaseProps>(\n render: (overridingProps?: Partial<Props> | undefined) => ReactNode\n): ComponentHandlerResult<Props> {\n return Object.freeze({ [DO_NOT_CREATE_THIS_OBJECT_YOURSELF]: undefined, render });\n}\n\nfunction createChainOfResponsibility<\n Request = void,\n Props extends BaseProps = { readonly children?: never },\n Init = void\n>(options: CreateChainOfResponsibilityOptions = {}): ChainOfResponsibility<Request, Props, Init> {\n // Freeze options to prevent accidental change.\n options = Object.freeze({ ...options });\n\n const BuildContext = createContext<BuildContextType<Request, Props>>(\n Object.freeze({ enhancer: next => request => next(request) })\n );\n\n const RenderContext = createContext<RenderContextType<Props>>(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n new Proxy({} as any, {\n get() {\n // The following is assertion, there is no way to hit this line.\n /* istanbul ignore next */\n throw new Error(\n 'react-chain-of-responsibility: this hook cannot be used outside of <Proxy> and useBuildRenderCallback()'\n );\n }\n })\n );\n\n function reactComponent<P extends Props>(\n component: ComponentType<P>,\n // For `bindProps` of type function, do not do side-effect in it, it may not be always called in all scenarios.\n bindProps?:\n | (Partial<Props> & Omit<P, keyof Props>)\n | ((props: Props) => Partial<Props> & Omit<P, keyof Props>)\n | undefined\n ): ComponentHandlerResult<Props> {\n return createComponentHandlerResult((overridingProps?: Partial<Props> | undefined) => (\n <ComponentWithProps\n bindProps={bindProps}\n component={component as ComponentType<Props>}\n overridingProps={overridingProps}\n />\n ));\n }\n\n const ComponentWithProps = memo(function ComponentWithProps({\n bindProps,\n component: Component,\n overridingProps\n }: {\n readonly bindProps?: Partial<Props> | ((props: Props) => Partial<Props>) | undefined;\n readonly component: ComponentType<Props>;\n readonly overridingProps?: Partial<Props> | undefined;\n }) {\n const { allowOverrideProps } = options;\n const { originalProps: renderCallbackProps } = useContext(RenderContext);\n\n if (overridingProps && !arePropsEqual(overridingProps, renderCallbackProps) && !allowOverrideProps) {\n console.warn('react-chain-of-responsibility: \"allowOverrideProps\" must be set to true to override props');\n }\n\n const props = Object.freeze(\n allowOverrideProps ? { ...renderCallbackProps, ...overridingProps } : { ...renderCallbackProps }\n );\n\n return <Component {...props} {...(typeof bindProps === 'function' ? bindProps(props) : bindProps)} />;\n });\n\n const useBuildRenderCallback: () => UseBuildRenderCallback<Request, Props> = () => {\n const { enhancer } = useContext(BuildContext);\n\n return useCallback(\n (request, buildOptions = {}) => {\n const result =\n // Put the \"fallbackComponent\" as the last one in the chain.\n enhancer(() => {\n const { fallbackComponent } = buildOptions;\n\n if (!fallbackComponent) {\n console.warn(\n 'react-chain-of-responsibility: the request has fall through all middleware, set \"fallbackComponent\" as a catchall',\n request\n );\n\n // For clarity, we are returning `undefined` instead of `() => undefined`.\n return;\n }\n\n // `fallbackComponent` do not need `overridingProps` because it is the last one in the chain, it would not have the next() function.\n return reactComponent(fallbackComponent);\n })(request);\n\n return (\n result &&\n ((originalProps: Props) => (\n // This is render function, we cannot call any hooks here.\n <BuildRenderCallback originalProps={originalProps} render={result.render} />\n ))\n );\n },\n [enhancer]\n );\n };\n\n type BuildRenderCallbackProps = {\n readonly originalProps: Props;\n readonly render: () => ReactNode;\n };\n\n // Do not memoize <BuildRenderCallback>.\n // `bindProps` may have side effect and we want to be re-rendered to capture the side-effect.\n // To prevent wasted render, web devs should memoize it themselves.\n function BuildRenderCallback({ originalProps, render }: BuildRenderCallbackProps) {\n const context = useMemo<RenderContextType<Props>>(() => Object.freeze({ originalProps }), [originalProps]);\n\n return <RenderContext.Provider value={context}>{render()}</RenderContext.Provider>;\n }\n\n function ChainOfResponsibilityProvider({ children, init, middleware }: ProviderProps<Request, Props, Init>) {\n if (!Array.isArray(middleware) || middleware.some(middleware => typeof middleware !== 'function')) {\n throw new Error('react-chain-of-responsibility: \"middleware\" prop must be an array of functions');\n }\n\n // Remap the middleware, so all inputs/outputs are validated.\n const fortifiedMiddleware = useMemo(\n () =>\n Object.freeze(\n middleware.map<ComponentMiddleware<Request, Props, Init>>(fn => (init: Init) => {\n const enhancer = fn(init);\n\n return next => originalRequest => {\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(\n 'react-chain-of-responsibility: next() cannot be called after the function had returned synchronously'\n );\n }\n\n // We do not allow passing void/undefined to next() because it would be confusing whether to keep the original request or pass an undefined.\n !options.passModifiedRequest &&\n !Object.is(nextRequest, originalRequest) &&\n console.warn(\n 'react-chain-of-responsibility: next() must be called with the original request, otherwise, set \"options.passModifiedRequest\" to true to pass a different request object downstream'\n );\n\n return next(options.passModifiedRequest ? nextRequest : originalRequest);\n })(originalRequest);\n\n hasReturned = true;\n\n // Make sure the return value is built using our helper function for forward-compatibility reason.\n return returnValue && parse(componentHandlerResultSchema, returnValue);\n };\n })\n ),\n [middleware]\n );\n\n const { enhancer: parentEnhancer } = useContext(BuildContext);\n\n const enhancer = useMemo<ComponentEnhancer<Request, Props>>(\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<ComponentHandlerResult<Props> | undefined, Request, Init>(\n ...[...fortifiedMiddleware, ...[() => parentEnhancer]]\n )(init as Init),\n [init, fortifiedMiddleware, parentEnhancer]\n );\n\n const contextValue = useMemo<BuildContextType<Request, Props>>(() => Object.freeze({ enhancer }), [enhancer]);\n\n return <BuildContext.Provider value={contextValue}>{children}</BuildContext.Provider>;\n }\n\n function ChainOfResponsibilityProxy({ fallbackComponent, request, ...props }: ProxyProps<Request, Props>) {\n const result = useBuildRenderCallback()(request, { fallbackComponent })?.(props as Props);\n\n return result ? <Fragment>{result}</Fragment> : null;\n }\n\n const MemoizedChainOfResponsibilityProvider =\n memo<ProviderProps<Request, Props, Init>>(ChainOfResponsibilityProvider);\n\n return Object.freeze({\n Provider: MemoizedChainOfResponsibilityProvider as typeof MemoizedChainOfResponsibilityProvider &\n InferenceHelper<Request, Props, Init>,\n Proxy: ChainOfResponsibilityProxy,\n reactComponent,\n useBuildRenderCallback\n\n // TODO: Consider adding back `asMiddleware`.\n });\n}\n\nexport default createChainOfResponsibility;\nexport {\n type ChainOfResponsibility,\n type ComponentEnhancer,\n type ComponentHandler,\n type ComponentHandlerResult,\n type ComponentMiddleware,\n type ComponentRenderer,\n type CreateChainOfResponsibilityOptions,\n type InferenceHelper,\n type InferInit,\n type InferMiddleware,\n type InferProps,\n type InferProviderProps,\n type InferProxyProps,\n type InferRequest,\n type ProviderProps,\n type ProxyProps,\n type ReactComponentHandlerResult,\n type UseBuildRenderCallback,\n type UseBuildRenderCallbackOptions\n};\n","// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport default function arePropsEqual<T extends Record<string, any>>(x: T, y: T): boolean {\n if (Object.is(x, y)) {\n return true;\n }\n\n const mapOfX = new Map(Object.entries(x));\n const mapOfY = new Map(Object.entries(y));\n\n if (mapOfX.size !== mapOfY.size) {\n return false;\n }\n\n const keys = new Set([...mapOfX.keys(), ...mapOfY.keys()]);\n\n for (const key of keys) {\n if (!Object.is(mapOfX.get(key), mapOfY.get(key))) {\n return false;\n }\n }\n\n return true;\n}\n"],"mappings":"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","names":["ComponentWithProps","middleware","init","enhancer"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-chain-of-responsibility",
|
|
3
|
-
"version": "0.4.0-main.
|
|
3
|
+
"version": "0.4.0-main.5f5cc2f",
|
|
4
4
|
"description": "Using chain of responsibility design pattern for compositing and customizing React component.",
|
|
5
5
|
"files": [
|
|
6
6
|
"./dist/"
|
|
@@ -74,9 +74,10 @@
|
|
|
74
74
|
"@testing-library/react": "^12",
|
|
75
75
|
"@types/react": "^16",
|
|
76
76
|
"@types/react-dom": "^16",
|
|
77
|
-
"react": "16
|
|
78
|
-
"react
|
|
79
|
-
"react-
|
|
77
|
+
"@types/react-test-renderer": "^16",
|
|
78
|
+
"react": "16.8.6",
|
|
79
|
+
"react-dom": "16.8.6",
|
|
80
|
+
"react-test-renderer": "16.8.6"
|
|
80
81
|
}
|
|
81
82
|
},
|
|
82
83
|
"switch:react-17": {
|
|
@@ -84,6 +85,7 @@
|
|
|
84
85
|
"@testing-library/react": "^12",
|
|
85
86
|
"@types/react": "^17",
|
|
86
87
|
"@types/react-dom": "^17",
|
|
88
|
+
"@types/react-test-renderer": "^17",
|
|
87
89
|
"react": "17.0.0",
|
|
88
90
|
"react-dom": "17.0.0",
|
|
89
91
|
"react-test-renderer": "17.0.0"
|
|
@@ -91,8 +93,10 @@
|
|
|
91
93
|
},
|
|
92
94
|
"switch:react-18": {
|
|
93
95
|
"devDependencies": {
|
|
96
|
+
"@testing-library/react": "^16",
|
|
94
97
|
"@types/react": "^18",
|
|
95
98
|
"@types/react-dom": "^18",
|
|
99
|
+
"@types/react-test-renderer": "^18",
|
|
96
100
|
"react": "18.0.0",
|
|
97
101
|
"react-dom": "18.0.0",
|
|
98
102
|
"react-test-renderer": "18.0.0"
|
|
@@ -108,6 +112,12 @@
|
|
|
108
112
|
"@types/react": [
|
|
109
113
|
"18"
|
|
110
114
|
],
|
|
115
|
+
"@types/react-dom": [
|
|
116
|
+
"18"
|
|
117
|
+
],
|
|
118
|
+
"@types/react-test-renderer": [
|
|
119
|
+
"18"
|
|
120
|
+
],
|
|
111
121
|
"escape-string-regexp": [
|
|
112
122
|
"4"
|
|
113
123
|
],
|
|
@@ -135,6 +145,8 @@
|
|
|
135
145
|
"@types/jest": "^30.0.0",
|
|
136
146
|
"@types/node": "^24.0.15",
|
|
137
147
|
"@types/react": "^18.3.23",
|
|
148
|
+
"@types/react-dom": "^18.3.7",
|
|
149
|
+
"@types/react-test-renderer": "^18.3.1",
|
|
138
150
|
"esbuild": "^0.25.8",
|
|
139
151
|
"escape-string-regexp": "^4.0.0",
|
|
140
152
|
"jest": "^30.0.4",
|
|
@@ -150,11 +162,11 @@
|
|
|
150
162
|
"typescript": "^5.8.3"
|
|
151
163
|
},
|
|
152
164
|
"peerDependencies": {
|
|
153
|
-
"react": ">=16.8.
|
|
165
|
+
"react": ">=16.8.6"
|
|
154
166
|
},
|
|
155
167
|
"dependencies": {
|
|
156
168
|
"handler-chain": "^0.1.0",
|
|
157
|
-
"react-chain-of-responsibility": "^0.4.0-main.
|
|
169
|
+
"react-chain-of-responsibility": "^0.4.0-main.5f5cc2f",
|
|
158
170
|
"valibot": "^1.1.0"
|
|
159
171
|
}
|
|
160
172
|
}
|