Skip to content

Commit a7b799c

Browse files
committed
fix: add proper jsdoc, remove extraneous logic from plugin + tests
- add test cases for getChain + executeChain to validate the processor execution - remove extraneous function to create a loader and just use the constructor - address review comments
1 parent f9667d6 commit a7b799c

File tree

9 files changed

+498
-129
lines changed

9 files changed

+498
-129
lines changed

package-lock.json

Lines changed: 140 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
"mocha": "^10.2.0",
9797
"nyc": "^17.0.0",
9898
"prettier": "^3.0.0",
99+
"sinon": "^19.0.2",
99100
"vite": "^4.4.2"
100101
},
101102
"optionalDependencies": {

src/plugin.js

Lines changed: 58 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -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
*/
1010
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
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
*/
3134
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-
*/
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-
231222
module.exports = {
232-
createLoader,
233223
PluginLoader,
234224
PushActionPlugin,
235225
PullActionPlugin,
236226
isCompatiblePlugin,
237-
}
227+
}

0 commit comments

Comments
 (0)