0
Fork 0
mirror of https://github.com/verdaccio/verdaccio.git synced 2024-12-30 22:34:10 -05: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

View file

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

View file

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

View file

@ -3,7 +3,6 @@ import fs from 'fs';
import { dirname, isAbsolute, join, resolve } from 'path'; import { dirname, isAbsolute, join, resolve } from 'path';
import { pluginUtils } from '@verdaccio/core'; import { pluginUtils } from '@verdaccio/core';
import { Config, Logger } from '@verdaccio/types';
import { PluginType, isES6, isValid, tryLoad } from './utils'; import { PluginType, isES6, isValid, tryLoad } from './utils';
@ -16,8 +15,6 @@ async function isDirectory(pathFolder: string) {
return stat.isDirectory(); return stat.isDirectory();
} }
export type Params = { config: Config; logger: Logger };
// type Plugins<T> = // type Plugins<T> =
// | pluginUtils.Auth<T> // | pluginUtils.Auth<T>
// | pluginUtils.Storage<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. * The `params` is an object that contains the global configuration and the logger.
* *
* @param {*} pluginConfigs the custom plugin section * @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 {*} 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 {*} 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 * @return {Array} list of plugins
*/ */
export async function asyncLoadPlugin<T extends pluginUtils.Plugin<T>>( export async function asyncLoadPlugin<T extends pluginUtils.Plugin<T>>(
pluginConfigs: any = {}, pluginConfigs: any = {},
params: Params, pluginOptions: pluginUtils.PluginOptions,
sanityCheck: (plugin: PluginType<T>) => boolean, sanityCheck: (plugin: PluginType<T>) => boolean,
prefix: string = 'verdaccio', prefix: string = 'verdaccio',
pluginCategory: string = '' pluginCategory: string = ''
): Promise<PluginType<T>[]> { ): Promise<PluginType<T>[]> {
const logger = params?.logger; const logger = pluginOptions?.logger;
const pluginsIds = Object.keys(pluginConfigs); const pluginsIds = Object.keys(pluginConfigs);
const { config } = params; const { config } = pluginOptions;
let plugins: PluginType<T>[] = []; let plugins: PluginType<T>[] = [];
for (let pluginId of pluginsIds) { for (let pluginId of pluginsIds) {
debug('plugin %s', pluginId); debug('looking for plugin %o', pluginId);
if (typeof config.plugins === 'string') { if (typeof config.plugins === 'string') {
let pluginsPath = config.plugins; let pluginsPath = config.plugins;
debug('plugin path %s', pluginsPath); debug('plugin path %s', pluginsPath);
@ -84,7 +82,7 @@ export async function asyncLoadPlugin<T extends pluginUtils.Plugin<T>>(
logger.error(a, b); logger.error(a, b);
}); });
if (plugin && isValid(plugin)) { if (plugin && isValid(plugin)) {
plugin = executePlugin(plugin, pluginConfigs[pluginId], params); plugin = executePlugin(plugin, pluginConfigs[pluginId], pluginOptions);
if (!sanityCheck(plugin)) { if (!sanityCheck(plugin)) {
logger.error( logger.error(
{ content: externalFilePlugin }, { content: externalFilePlugin },
@ -109,14 +107,14 @@ export async function asyncLoadPlugin<T extends pluginUtils.Plugin<T>>(
if (typeof pluginId === 'string') { if (typeof pluginId === 'string') {
const isScoped: boolean = pluginId.startsWith('@') && pluginId.includes('/'); 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}`; 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) => { let plugin = tryLoad<T>(pluginName, (a: any, b: any) => {
logger.error(a, b); logger.error(a, b);
}); });
if (plugin && isValid(plugin)) { if (plugin && isValid(plugin)) {
plugin = executePlugin(plugin, pluginConfigs[pluginId], params); plugin = executePlugin(plugin, pluginConfigs[pluginId], pluginOptions);
if (!sanityCheck(plugin)) { if (!sanityCheck(plugin)) {
logger.error({ content: pluginName }, "@{content} doesn't look like a valid plugin"); logger.error({ content: pluginName }, "@{content} doesn't look like a valid plugin");
continue; 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; return plugins;
} }
export function executePlugin<T>( export function executePlugin<T>(
plugin: PluginType<T>, plugin: PluginType<T>,
pluginConfig: unknown, pluginConfig: unknown,
params: Params pluginOptions: pluginUtils.PluginOptions
): PluginType<T> { ): PluginType<T> {
if (isES6(plugin)) { if (isES6(plugin)) {
debug('plugin is ES6'); debug('plugin is ES6');
// @ts-expect-error no relevant for the code // @ts-expect-error no relevant for the code
// eslint-disable-next-line new-cap // eslint-disable-next-line new-cap
return new plugin.default(pluginConfig, params) as Plugin; return new plugin.default(pluginConfig, pluginOptions) as Plugin;
} else { } else {
debug('plugin is commonJS'); debug('plugin is commonJS');
// @ts-expect-error improve this type // @ts-expect-error improve this type
return plugin(pluginConfig, params) as PluginType<T>; return plugin(pluginConfig, pluginOptions) as PluginType<T>;
} }
} }

View file

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

View file

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

View file

@ -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"
}

View file

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