@@ -8,6 +8,9 @@ const lpModule = import('load-plugin');
8
8
* @return {boolean } - True if the object or any of its prototypes has the 'isGitProxyPlugin' property set to true, false otherwise.
9
9
*/
10
10
function 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
11
14
while ( obj != null ) {
12
15
if ( Object . prototype . hasOwnProperty . call ( obj , propertyName ) &&
13
16
obj . isGitProxyPlugin &&
@@ -21,46 +24,45 @@ function isCompatiblePlugin(obj, propertyName = 'isGitProxyPlugin') {
21
24
22
25
/**
23
26
* @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
26
29
*/
27
30
28
31
/**
29
32
* Registers and loads plugins used by git-proxy
30
33
*/
31
34
class 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
- */
46
35
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
+ */
47
42
this . targets = targets ;
48
43
/**
49
- * @type {ProxyPlugin[] } List of loaded ProxyPlugins
44
+ * List of loaded PushActionPlugin objects.
45
+ * @type {PushActionPlugin[] }
50
46
* @public
51
47
*/
52
48
this . pushPlugins = [ ] ;
49
+ /**
50
+ * List of loaded PullActionPlugin objects.
51
+ * @type {PullActionPlugin[] }
52
+ * @public
53
+ */
53
54
this . pullPlugins = [ ] ;
54
55
if ( this . targets . length === 0 ) {
55
56
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 ;
59
57
}
60
- this . load = this . _loadPlugins ( ) ;
61
58
}
62
59
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 ( ) {
64
66
try {
65
67
const modulePromises = this . targets . map ( target =>
66
68
this . _loadPluginModule ( target ) . catch ( error => {
@@ -84,30 +86,31 @@ class PluginLoader {
84
86
) ;
85
87
86
88
const settledPluginTypeResults = await Promise . allSettled ( pluginTypeResultPromises ) ;
89
+ /**
90
+ * @type {PluginTypeResult[] } List of resolved PluginTypeResult objects
91
+ */
87
92
const pluginTypeResults = settledPluginTypeResults
88
93
. filter ( result => result . status === 'fulfilled' && result . value !== null )
89
94
. map ( result => result . value ) ;
90
95
91
96
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 )
94
99
}
95
100
96
101
const combinedPlugins = [ ...this . pushPlugins , ...this . pullPlugins ] ;
97
102
combinedPlugins . forEach ( plugin => {
98
103
console . log ( `Loaded plugin: ${ plugin . constructor . name } ` ) ;
99
104
} ) ;
100
-
101
- this . ready = Promise . resolve ( ) ;
102
105
} catch ( error ) {
103
106
console . error ( `Error loading plugins: ${ error } ` ) ;
104
- this . ready = Promise . reject ( error ) ;
105
107
}
106
108
}
109
+
107
110
/**
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
111
114
*/
112
115
async _loadPluginModule ( target ) {
113
116
const lp = await lpModule ;
@@ -116,40 +119,39 @@ class PluginLoader {
116
119
}
117
120
118
121
/**
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.
123
125
*/
124
126
async _getPluginObjects ( pluginModule ) {
125
127
const plugins = {
126
- pushPlugins : [ ] ,
127
- pullPlugins : [ ] ,
128
+ pushAction : [ ] ,
129
+ pullAction : [ ] ,
128
130
} ;
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 ) ;
137
139
} 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` ) ;
139
141
}
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 )
140
149
} 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)
142
152
for ( const key of Object . keys ( pluginModule ) ) {
143
153
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 ] ) ;
153
155
}
154
156
}
155
157
}
@@ -217,21 +219,9 @@ class PullActionPlugin extends ProxyPlugin {
217
219
}
218
220
}
219
221
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
-
231
222
module . exports = {
232
- createLoader,
233
223
PluginLoader,
234
224
PushActionPlugin,
235
225
PullActionPlugin,
236
226
isCompatiblePlugin,
237
- }
227
+ }
0 commit comments