@@ -8,6 +8,9 @@ const lpModule = import('load-plugin');
88 * @return {boolean } - True if the object or any of its prototypes has the 'isGitProxyPlugin' property set to true, false otherwise.
99 */
1010function isCompatiblePlugin ( obj , propertyName = 'isGitProxyPlugin' ) {
11+ // loop through the prototype chain to check if the object is a ProxyPlugin
12+ // valid plugin objects will have the appropriate property set to true
13+ // if the prototype chain is exhausted, return false
1114 while ( obj != null ) {
1215 if ( Object . prototype . hasOwnProperty . call ( obj , propertyName ) &&
1316 obj . isGitProxyPlugin &&
@@ -21,46 +24,45 @@ function isCompatiblePlugin(obj, propertyName = 'isGitProxyPlugin') {
2124
2225/**
2326 * @typedef PluginTypeResult
24- * @property {ProxyPlugin [] } pushPlugins - List of push plugins
25- * @property {ProxyPlugin [] } pullPlugins - List of pull plugins
27+ * @property {PushActionPlugin [] } pushAction - List of push action plugins
28+ * @property {PullActionPlugin [] } pullAction - List of pull action plugins
2629 */
2730
2831/**
2932 * Registers and loads plugins used by git-proxy
3033 */
3134class PluginLoader {
32- /**
33- * @property {Promise } load - A Promise that begins loading plugins from a list of modules. Callers must run `await loader.load` to load plugins.
34- */
35- load ;
36- /**
37- * This property is not used in production code. It is exposed for testing purposes.
38- * @property {Promise } ready - A Promise that resolves when all plugins have been loaded.
39- */
40- ready ;
41- /**
42- * Initialize PluginLoader with candidates modules (node_modules or relative
43- * file paths).
44- * @param {Array.<string> } targets List of Node module package names or files to load.
45- */
4635 constructor ( targets ) {
36+ /**
37+ * List of Node module specifiers to load as plugins. It can be a relative path, an
38+ * absolute path, or a module name (which can include scoped packages like '@bar/baz').
39+ * @type {string[] }
40+ * @public
41+ */
4742 this . targets = targets ;
4843 /**
49- * @type {ProxyPlugin[] } List of loaded ProxyPlugins
44+ * List of loaded PushActionPlugin objects.
45+ * @type {PushActionPlugin[] }
5046 * @public
5147 */
5248 this . pushPlugins = [ ] ;
49+ /**
50+ * List of loaded PullActionPlugin objects.
51+ * @type {PullActionPlugin[] }
52+ * @public
53+ */
5354 this . pullPlugins = [ ] ;
5455 if ( this . targets . length === 0 ) {
5556 console . log ( 'No plugins configured' ) ; // TODO: log.debug()
56- this . ready = Promise . resolve ( ) ;
57- this . load = ( ) => Promise . resolve ( ) ; // Ensure this.load is always defined
58- return ;
5957 }
60- this . load = this . _loadPlugins ( ) ;
6158 }
6259
63- async _loadPlugins ( ) {
60+ /**
61+ * Load all plugins specified in the `targets` property. This method must complete before a PluginLoader instance
62+ * can be used to retrieve plugins.
63+ * @return {Promise<void> } A Promise that resolves when all plugins have been loaded.
64+ */
65+ async load ( ) {
6466 try {
6567 const modulePromises = this . targets . map ( target =>
6668 this . _loadPluginModule ( target ) . catch ( error => {
@@ -84,30 +86,31 @@ class PluginLoader {
8486 ) ;
8587
8688 const settledPluginTypeResults = await Promise . allSettled ( pluginTypeResultPromises ) ;
89+ /**
90+ * @type {PluginTypeResult[] } List of resolved PluginTypeResult objects
91+ */
8792 const pluginTypeResults = settledPluginTypeResults
8893 . filter ( result => result . status === 'fulfilled' && result . value !== null )
8994 . map ( result => result . value ) ;
9095
9196 for ( const result of pluginTypeResults ) {
92- this . pushPlugins . push ( ...result . pushPlugins )
93- this . pullPlugins . push ( ...result . pullPlugins )
97+ this . pushPlugins . push ( ...result . pushAction )
98+ this . pullPlugins . push ( ...result . pullAction )
9499 }
95100
96101 const combinedPlugins = [ ...this . pushPlugins , ...this . pullPlugins ] ;
97102 combinedPlugins . forEach ( plugin => {
98103 console . log ( `Loaded plugin: ${ plugin . constructor . name } ` ) ;
99104 } ) ;
100-
101- this . ready = Promise . resolve ( ) ;
102105 } catch ( error ) {
103106 console . error ( `Error loading plugins: ${ error } ` ) ;
104- this . ready = Promise . reject ( error ) ;
105107 }
106108 }
109+
107110 /**
108- * Load a plugin module from either a file path or a Node module .
109- * @param {string } target
110- * @return {Module }
111+ * Resolve & load a Node module from either a given specifier ( file path, import specifier or package name) using load-plugin .
112+ * @param {string } target The module specifier to load
113+ * @return {Promise< Module> } A resolved & loaded Module
111114 */
112115 async _loadPluginModule ( target ) {
113116 const lp = await lpModule ;
@@ -116,40 +119,39 @@ class PluginLoader {
116119 }
117120
118121 /**
119- * Set a list of ProxyPlugin objects to this.plugins
120- * from the keys exported by the passed in module.
121- * @param {object } pluginModule
122- * @return {PluginTypeResult } - An object containing the loaded plugins classified by their type.
122+ * Checks for known compatible plugin objects in a Module and returns them classified by their type.
123+ * @param {Module } pluginModule The module to extract plugins from
124+ * @return {Promise<PluginTypeResult> } An object containing the loaded plugins classified by their type.
123125 */
124126 async _getPluginObjects ( pluginModule ) {
125127 const plugins = {
126- pushPlugins : [ ] ,
127- pullPlugins : [ ] ,
128+ pushAction : [ ] ,
129+ pullAction : [ ] ,
128130 } ;
129- // handles the case where the `module.exports = new ProxyPlugin()` or `exports default new ProxyPlugin()`
130- if ( isCompatiblePlugin ( pluginModule ) ) {
131- if ( isCompatiblePlugin ( pluginModule , 'isGitProxyPushActionPlugin' ) ) {
132- console . log ( 'found push plugin' , pluginModule . constructor . name ) ;
133- plugins . pushPlugins . push ( pluginModule ) ;
134- } else if ( isCompatiblePlugin ( pluginModule , 'isGitProxyPullActionPlugin' ) ) {
135- console . log ( 'found pull plugin' , pluginModule . constructor . name ) ;
136- plugins . pullPlugins . push ( pluginModule ) ;
131+
132+ function handlePlugin ( potentialModule ) {
133+ if ( isCompatiblePlugin ( potentialModule , 'isGitProxyPushActionPlugin' ) ) {
134+ console . log ( 'found push plugin' , potentialModule . constructor . name ) ;
135+ plugins . pushAction . push ( potentialModule ) ;
136+ } else if ( isCompatiblePlugin ( potentialModule , 'isGitProxyPullActionPlugin' ) ) {
137+ console . log ( 'found pull plugin' , potentialModule . constructor . name ) ;
138+ plugins . pullAction . push ( potentialModule ) ;
137139 } else {
138- console . error ( `Error: Object ${ pluginModule . constructor . name } does not seem to be a compatible plugin type` ) ;
140+ console . error ( `Error: Object ${ potentialModule . constructor . name } does not seem to be a compatible plugin type` ) ;
139141 }
142+ }
143+
144+ // handles the default export case
145+ // `module.exports = new ProxyPlugin()` in CJS or `exports default new ProxyPlugin()` in ESM
146+ // the "module" is a single object that could be a plugin
147+ if ( isCompatiblePlugin ( pluginModule ) ) {
148+ handlePlugin ( pluginModule )
140149 } else {
141- // iterate over the module.exports keys if multiple arbitrary objects are exported
150+ // handle the typical case of a module which exports multiple objects
151+ // module.exports = { x, y } (CJS) or multiple `export ...` statements (ESM)
142152 for ( const key of Object . keys ( pluginModule ) ) {
143153 if ( isCompatiblePlugin ( pluginModule [ key ] ) ) {
144- if ( isCompatiblePlugin ( pluginModule [ key ] , 'isGitProxyPushActionPlugin' ) ) {
145- console . log ( 'found push plugin' , pluginModule [ key ] . constructor . name ) ;
146- plugins . pushPlugins . push ( pluginModule [ key ] ) ;
147- } else if ( isCompatiblePlugin ( pluginModule [ key ] , 'isGitProxyPullActionPlugin' ) ) {
148- console . log ( 'found pull plugin' , pluginModule [ key ] . constructor . name ) ;
149- plugins . pullPlugins . push ( pluginModule [ key ] ) ;
150- } else {
151- console . error ( `Error: Object ${ pluginModule . constructor . name } does not seem to be a compatible plugin type` ) ;
152- }
154+ handlePlugin ( pluginModule [ key ] ) ;
153155 }
154156 }
155157 }
@@ -217,21 +219,9 @@ class PullActionPlugin extends ProxyPlugin {
217219 }
218220}
219221
220- /**
221- *
222- * @param {Array<string> } targets A list of loadable targets for plugin modules.
223- * @return {PluginLoader }
224- */
225- const createLoader = async ( targets ) => {
226- const loadTargets = targets ;
227- const loader = new PluginLoader ( loadTargets ) ;
228- return loader ;
229- } ;
230-
231222module . exports = {
232- createLoader,
233223 PluginLoader,
234224 PushActionPlugin,
235225 PullActionPlugin,
236226 isCompatiblePlugin,
237- }
227+ }
0 commit comments