1+ import { cloneDeep } from 'lodash-es' ;
12import { Plugin , PluginConfigItem } from './plugin' ;
23
4+ /**
5+ * Resolves the plugin config.
6+ *
7+ * @param plugins - An array of plugin configuration items which can be
8+ * instances of `Plugin` or functions that return a `Plugin`.
9+ * @param data - The data to be passed to plugin functions if they are not
10+ * instances of `Plugin`.
11+ * @returns An array of processed `Plugin` instances after applying hooks.
12+ */
313export const resolvePlugins = ( plugins : PluginConfigItem [ ] , data : any ) => {
4- return plugins
14+ const p = plugins
515 . flatMap ( ( pl ) => {
616 if ( pl instanceof Plugin ) {
717 return pl ;
@@ -11,4 +21,84 @@ export const resolvePlugins = (plugins: PluginConfigItem[], data: any) => {
1121 return ;
1222 } )
1323 . filter ( ( p ) => p instanceof Plugin ) ;
24+
25+ return applyHooks ( p ) ;
26+ } ;
27+
28+ /**
29+ * Applies hooks from the provided plugins to their respective targets.
30+ *
31+ * This function iterates over each plugin and applies hooks such as
32+ * `onAfterInit` and `onAfterEditSchema` to the corresponding target plugins.
33+ * The hooks are executed in the context of the source plugin.
34+ *
35+ * @param plugins - List of plugins. All the source and target plugins must be
36+ * on the list.
37+ *
38+ *
39+ * @remarks
40+ * - The `onAfterInit` hook is executed after the target plugin's `init`
41+ * function.
42+ * - The `onAfterEditSchema` hook is composed with the target plugin's
43+ * `editSchema` function.
44+ *
45+ * @example
46+ * ```typescript
47+ * class MyPlugin extends Plugin {
48+ * name = 'MyPlugin';
49+ *
50+ * [Plugin.HOOKS]: [
51+ * {
52+ * name: 'pluginA', // Target plugin
53+ * onAfterInit: async (target, data) => { }, // Executes after pluginA's init function.
54+ * onAfterEditSchema: (target, formData, origEditSchema) => { } // Composes with pluginA's editSchema function and returns a new one.
55+ * },
56+ * {
57+ * name: 'pluginB', // Target plugin
58+ * onAfterInit: async (target, data) => { }, // Executes after pluginB's init function.
59+ * }
60+ * ];
61+ * }
62+ *
63+ * applyHooks(plugins);
64+ * ```
65+ */
66+ export const applyHooks = ( plugins : Plugin [ ] ) => {
67+ const pluginsCopy = cloneDeep ( plugins ) ;
68+
69+ for ( const plSource of pluginsCopy ) {
70+ for ( const hook of plSource [ Plugin . HOOKS ] ) {
71+ // Target where to apply the hook
72+ const plTarget = plugins . find ( ( p ) => p . name === hook . name ) ;
73+ if ( ! plTarget ) {
74+ continue ;
75+ }
76+
77+ // The onAfterInit hook is made by executing one function after another.
78+ if ( hook . onAfterInit ) {
79+ const fn = hook . onAfterInit ;
80+ const origInit = plTarget . init ;
81+ plTarget . init = async ( data : any ) => {
82+ await origInit . call ( plTarget , data ) ;
83+ await fn . call ( plSource , plTarget , data ) ;
84+ } ;
85+ }
86+
87+ // The onAfterEditSchema hook is made by composing functions.
88+ if ( hook . onAfterEditSchema ) {
89+ const fn = hook . onAfterEditSchema ;
90+ const origEditSchema = plTarget . editSchema ;
91+ plTarget . editSchema = ( formData ?: any ) => {
92+ return fn . call (
93+ plSource ,
94+ plTarget ,
95+ formData ,
96+ origEditSchema . call ( plTarget , formData )
97+ ) ;
98+ } ;
99+ }
100+ }
101+ }
102+
103+ return pluginsCopy ;
14104} ;
0 commit comments