1
0
mirror of https://github.com/verdaccio/verdaccio.git synced 2024-12-24 21:15:51 +01:00

chore(loader): fix types for plugin options (#4970)

* chore(loader): fix types for plugin options

* debug
This commit is contained in:
Marc Bernard 2024-11-30 03:01:23 -05:00 committed by GitHub
parent 6e344d0f48
commit ba71932523
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 58 additions and 41 deletions

@ -0,0 +1,6 @@
---
'@verdaccio/loaders': patch
'@verdaccio/config': patch
---
chore(loader): fix types for plugin options

@ -52,6 +52,7 @@ class Config implements AppConfig {
public packages: PackageList;
public users: any;
public auth: AuthConf;
public store: any;
public server_id: string;
public configPath: string;
/**

@ -3,7 +3,6 @@ import fs from 'fs';
import { dirname, isAbsolute, join, resolve } from 'path';
import { pluginUtils } from '@verdaccio/core';
import { Config, Logger } from '@verdaccio/types';
import { PluginType, isES6, isValid, tryLoad } from './utils';
@ -16,8 +15,6 @@ async function isDirectory(pathFolder: string) {
return stat.isDirectory();
}
export type Params = { config: Config; logger: Logger };
// type Plugins<T> =
// | pluginUtils.Auth<T>
// | pluginUtils.Storage<T>
@ -40,24 +37,25 @@ export type Params = { config: Config; logger: Logger };
* The `params` is an object that contains the global configuration and the logger.
*
* @param {*} pluginConfigs the custom plugin section
* @param {*} params a set of params to initialize the plugin
* @param {*} pluginOptions a set of options to initialize the plugin
* @param {*} sanityCheck callback that check the shape that should fulfill the plugin
* @param {*} prefix by default is verdaccio but can be override with config.server.pluginPrefix
* @param {*} pluginCategory the category of the plugin, eg: auth, storage, middleware
* @return {Array} list of plugins
*/
export async function asyncLoadPlugin<T extends pluginUtils.Plugin<T>>(
pluginConfigs: any = {},
params: Params,
pluginOptions: pluginUtils.PluginOptions,
sanityCheck: (plugin: PluginType<T>) => boolean,
prefix: string = 'verdaccio',
pluginCategory: string = ''
): Promise<PluginType<T>[]> {
const logger = params?.logger;
const logger = pluginOptions?.logger;
const pluginsIds = Object.keys(pluginConfigs);
const { config } = params;
const { config } = pluginOptions;
let plugins: PluginType<T>[] = [];
for (let pluginId of pluginsIds) {
debug('plugin %s', pluginId);
debug('looking for plugin %o', pluginId);
if (typeof config.plugins === 'string') {
let pluginsPath = config.plugins;
debug('plugin path %s', pluginsPath);
@ -84,7 +82,7 @@ export async function asyncLoadPlugin<T extends pluginUtils.Plugin<T>>(
logger.error(a, b);
});
if (plugin && isValid(plugin)) {
plugin = executePlugin(plugin, pluginConfigs[pluginId], params);
plugin = executePlugin(plugin, pluginConfigs[pluginId], pluginOptions);
if (!sanityCheck(plugin)) {
logger.error(
{ content: externalFilePlugin },
@ -109,14 +107,14 @@ export async function asyncLoadPlugin<T extends pluginUtils.Plugin<T>>(
if (typeof pluginId === 'string') {
const isScoped: boolean = pluginId.startsWith('@') && pluginId.includes('/');
debug('is scoped plugin %s', isScoped);
debug('is scoped plugin: %s', isScoped);
const pluginName = isScoped ? pluginId : `${prefix}-${pluginId}`;
debug('plugin pkg name %s', pluginName);
debug('plugin package name %s', pluginName);
let plugin = tryLoad<T>(pluginName, (a: any, b: any) => {
logger.error(a, b);
});
if (plugin && isValid(plugin)) {
plugin = executePlugin(plugin, pluginConfigs[pluginId], params);
plugin = executePlugin(plugin, pluginConfigs[pluginId], pluginOptions);
if (!sanityCheck(plugin)) {
logger.error({ content: pluginName }, "@{content} doesn't look like a valid plugin");
continue;
@ -136,23 +134,23 @@ export async function asyncLoadPlugin<T extends pluginUtils.Plugin<T>>(
}
}
}
debug('plugin found %s', plugins.length);
debug('%s plugins found: %s', pluginCategory, plugins.length);
return plugins;
}
export function executePlugin<T>(
plugin: PluginType<T>,
pluginConfig: unknown,
params: Params
pluginOptions: pluginUtils.PluginOptions
): PluginType<T> {
if (isES6(plugin)) {
debug('plugin is ES6');
// @ts-expect-error no relevant for the code
// eslint-disable-next-line new-cap
return new plugin.default(pluginConfig, params) as Plugin;
return new plugin.default(pluginConfig, pluginOptions) as Plugin;
} else {
debug('plugin is commonJS');
// @ts-expect-error improve this type
return plugin(pluginConfig, params) as PluginType<T>;
return plugin(pluginConfig, pluginOptions) as PluginType<T>;
}
}

@ -0,0 +1,3 @@
store:
plugin-store:
enabled: true

@ -0,0 +1,7 @@
function ValidVerdaccioPlugin() {
return {
getPackageStorage: function () {},
};
}
module.exports = ValidVerdaccioPlugin;

@ -0,0 +1,11 @@
{
"name": "verdaccio-plugin",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}

@ -15,6 +15,9 @@ function getConfig(file: string) {
const authSanitize = function (plugin) {
return plugin.authenticate || plugin.allow_access || plugin.allow_publish;
};
const storeSanitize = function (plugin) {
return typeof plugin.getPackageStorage !== 'undefined';
};
const pluginsPartialsFolder = path.join(__dirname, './partials/test-plugin-storage');
@ -31,13 +34,17 @@ describe('plugin loader', () => {
expect(plugins).toHaveLength(1);
});
test('testing storage valid plugin loader', async () => {
const config = getConfig('valid-plugin-store.yaml');
config.plugins = pluginsPartialsFolder;
const plugins = await asyncLoadPlugin(config.store, { config, logger }, storeSanitize);
expect(plugins).toHaveLength(1);
});
test('should handle does not exist plugin folder', async () => {
const config = getConfig('plugins-folder-fake.yaml');
const plugins = await asyncLoadPlugin(
config.auth,
{ logger: logger, config: config },
authSanitize
);
const plugins = await asyncLoadPlugin(config.auth, { config, logger }, authSanitize);
expect(plugins).toHaveLength(0);
});
@ -59,11 +66,7 @@ describe('plugin loader', () => {
const config = getConfig('plugins-folder-fake.yaml');
// force file instead a folder.
config.plugins = path.join(__dirname, 'just-a-file.js');
const plugins = await asyncLoadPlugin(
config.auth,
{ logger: logger, config: config },
authSanitize
);
const plugins = await asyncLoadPlugin(config.auth, { config, logger }, authSanitize);
expect(plugins).toHaveLength(0);
});
@ -72,11 +75,7 @@ describe('plugin loader', () => {
test('should resolve plugin based on relative path', async () => {
const config = getConfig('relative-plugins.yaml');
// force file instead a folder.
const plugins = await asyncLoadPlugin(
config.auth,
{ logger: logger, config: config },
authSanitize
);
const plugins = await asyncLoadPlugin(config.auth, { config, logger }, authSanitize);
expect(plugins).toHaveLength(1);
});
@ -88,11 +87,7 @@ describe('plugin loader', () => {
// @ts-expect-error
config.config_path = undefined;
// force file instead a folder.
const plugins = await asyncLoadPlugin(
config.auth,
{ logger: logger, config: config },
authSanitize
);
const plugins = await asyncLoadPlugin(config.auth, { config, logger }, authSanitize);
expect(plugins).toHaveLength(0);
});
@ -103,11 +98,7 @@ describe('plugin loader', () => {
// @ts-expect-error
config.configPath = undefined;
// force file instead a folder.
const plugins = await asyncLoadPlugin(
config.auth,
{ logger: logger, config: config },
authSanitize
);
const plugins = await asyncLoadPlugin(config.auth, { config, logger }, authSanitize);
expect(plugins).toHaveLength(0);
});