1
0
mirror of https://github.com/verdaccio/verdaccio.git synced 2025-02-21 07:29:37 +01:00

feat: add scope support loading plugins (#3227)

* feat: add scope support loading plugins

* format

* Update src/lib/plugin-loader.ts

Co-authored-by: Michael Prentice <splaktar@gmail.com>

* Update src/lib/plugin-loader.ts

Co-authored-by: Michael Prentice <splaktar@gmail.com>

* chore: add tests

* chore: add comment

* format

* chore: update dep

* chore: add better name

Co-authored-by: Michael Prentice <splaktar@gmail.com>
This commit is contained in:
Juan Picado 2022-06-14 07:47:17 +02:00 committed by GitHub
parent 9d9c086ba3
commit 5c50ec9a2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 83 additions and 9 deletions

18
.pnp.js generated

@ -79,6 +79,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@types/semver", "npm:7.3.9"],
["@typescript-eslint/eslint-plugin", "virtual:7f7b3df50ee4b7b1719ad19fad11505dc2788f3227a7e5cc9ca19f71d8cb309c9d33b532ea2b2b60ab65abf6cc12153df4643c5e6e17d01ea0ae0492723bb4b4#npm:4.33.0"],
["@typescript-eslint/parser", "virtual:7f7b3df50ee4b7b1719ad19fad11505dc2788f3227a7e5cc9ca19f71d8cb309c9d33b532ea2b2b60ab65abf6cc12153df4643c5e6e17d01ea0ae0492723bb4b4#npm:4.33.0"],
["@verdaccio-scope/verdaccio-auth-foo", "npm:0.0.2"],
["@verdaccio/commons-api", "npm:10.2.0"],
["@verdaccio/eslint-config", "virtual:7f7b3df50ee4b7b1719ad19fad11505dc2788f3227a7e5cc9ca19f71d8cb309c9d33b532ea2b2b60ab65abf6cc12153df4643c5e6e17d01ea0ae0492723bb4b4#npm:10.0.0"],
["@verdaccio/local-storage", "npm:10.3.0"],
@ -5723,6 +5724,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD",
}]
]],
["@verdaccio-scope/verdaccio-auth-foo", [
["npm:0.0.2", {
"packageLocation": "./.yarn/cache/@verdaccio-scope-verdaccio-auth-foo-npm-0.0.2-e8d6fdf0d9-99f4727a67.zip/node_modules/@verdaccio-scope/verdaccio-auth-foo/",
"packageDependencies": [
["@verdaccio-scope/verdaccio-auth-foo", "npm:0.0.2"],
["@verdaccio/commons-api", "npm:10.2.0"]
],
"linkType": "HARD",
}]
]],
["@verdaccio/commons-api", [
["npm:10.2.0", {
"packageLocation": "./.yarn/cache/@verdaccio-commons-api-npm-10.2.0-d36a19383f-d93c93220a.zip/node_modules/@verdaccio/commons-api/",
@ -8036,14 +8047,14 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
]],
["core-js", [
["npm:2.6.9", {
"packageLocation": "./.yarn/cache/core-js-npm-2.6.9-f821bf686c-00c30207eb.zip/node_modules/core-js/",
"packageLocation": "./.yarn/unplugged/core-js-npm-2.6.9-f821bf686c/node_modules/core-js/",
"packageDependencies": [
["core-js", "npm:2.6.9"]
],
"linkType": "HARD",
}],
["npm:3.22.4", {
"packageLocation": "./.yarn/cache/core-js-npm-3.22.4-4469b89edf-1305b2b9c1.zip/node_modules/core-js/",
"packageLocation": "./.yarn/unplugged/core-js-npm-3.22.4-4469b89edf/node_modules/core-js/",
"packageDependencies": [
["core-js", "npm:3.22.4"]
],
@ -15293,7 +15304,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
]],
["puppeteer", [
["npm:5.5.0", {
"packageLocation": "./.yarn/cache/puppeteer-npm-5.5.0-bba75ba998-08ba8a7da5.zip/node_modules/puppeteer/",
"packageLocation": "./.yarn/unplugged/puppeteer-npm-5.5.0-bba75ba998/node_modules/puppeteer/",
"packageDependencies": [
["puppeteer", "npm:5.5.0"],
["debug", "virtual:fada3bd8ad326a7c196d0c24aae1d5410b84126805d4b297cac3abf549e077c61a437968e49905247d38e2ca430b4cee29c78b779ec928550ea7a1cdf2adc3c1#npm:4.1.1"],
@ -18111,6 +18122,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@types/semver", "npm:7.3.9"],
["@typescript-eslint/eslint-plugin", "virtual:7f7b3df50ee4b7b1719ad19fad11505dc2788f3227a7e5cc9ca19f71d8cb309c9d33b532ea2b2b60ab65abf6cc12153df4643c5e6e17d01ea0ae0492723bb4b4#npm:4.33.0"],
["@typescript-eslint/parser", "virtual:7f7b3df50ee4b7b1719ad19fad11505dc2788f3227a7e5cc9ca19f71d8cb309c9d33b532ea2b2b60ab65abf6cc12153df4643c5e6e17d01ea0ae0492723bb4b4#npm:4.33.0"],
["@verdaccio-scope/verdaccio-auth-foo", "npm:0.0.2"],
["@verdaccio/commons-api", "npm:10.2.0"],
["@verdaccio/eslint-config", "virtual:7f7b3df50ee4b7b1719ad19fad11505dc2788f3227a7e5cc9ca19f71d8cb309c9d33b532ea2b2b60ab65abf6cc12153df4643c5e6e17d01ea0ae0492723bb4b4#npm:10.0.0"],
["@verdaccio/local-storage", "npm:10.3.0"],

@ -104,6 +104,7 @@
"@types/semver": "7.3.9",
"@typescript-eslint/eslint-plugin": "4.33.0",
"@typescript-eslint/parser": "4.33.0",
"@verdaccio-scope/verdaccio-auth-foo": "0.0.2",
"@verdaccio/eslint-config": "^10.0.0",
"@verdaccio/types": "10.5.1",
"all-contributors-cli": "6.20.0",

@ -1,3 +1,4 @@
import buildDebug from 'debug';
import _ from 'lodash';
import Path from 'path';
@ -6,6 +7,8 @@ import { Config, IPlugin } from '@verdaccio/types';
import { MODULE_NOT_FOUND } from './constants';
import { logger } from './logger';
const debug = buildDebug('verdaccio:plugin:loader');
/**
* Requires a module.
* @param {*} path the module's path
@ -13,11 +16,14 @@ import { logger } from './logger';
*/
function tryLoad(path: string): any {
try {
debug('loading plugin %s', path);
return require(path);
} catch (err) {
if (err.code === MODULE_NOT_FOUND) {
debug('plugin %s not found', path);
return null;
}
logger.error({ err: err.msg }, 'error loading plugin @{err}');
throw err;
}
}
@ -39,6 +45,7 @@ function isES6(plugin): boolean {
/**
* Load a plugin following the rules
* - First try to load from the internal directory plugins (which will disappear soon or later).
* - If the package is scoped eg: @scope/foo, try to load as a package
* - A second attempt from the external plugin directory
* - A third attempt from node_modules, in case to have multiple match as for instance verdaccio-ldap
* and sinopia-ldap. All verdaccio prefix will have preferences.
@ -51,6 +58,11 @@ function isES6(plugin): boolean {
export default function loadPlugin<T extends IPlugin<T>>(config: Config, pluginConfigs: any = {}, params: any, sanityCheck: any, prefix: string = 'verdaccio'): any[] {
return Object.keys(pluginConfigs).map((pluginId: string): IPlugin<T> => {
let plugin;
const isScoped: boolean = pluginId.startsWith('@') && pluginId.includes('/');
debug('isScoped %s', isScoped);
if (isScoped) {
plugin = tryLoad(pluginId);
}
const localPlugin = Path.resolve(__dirname + '/../plugins', pluginId);
// try local plugins first
@ -68,6 +80,9 @@ export default function loadPlugin<T extends IPlugin<T>>(config: Config, pluginC
// compatibility for old sinopia plugins
if (!plugin) {
plugin = tryLoad(Path.resolve(pluginDir, `sinopia-${pluginId}`));
if (plugin) {
logger.warn({ name: pluginId }, `plugin names that start with sinopia-* will be removed in the future, please rename package to verdaccio-*`);
}
}
}
}
@ -79,6 +94,9 @@ export default function loadPlugin<T extends IPlugin<T>>(config: Config, pluginC
if (!plugin) {
plugin = tryLoad(`sinopia-${pluginId}`);
}
if (plugin) {
debug('plugin %s is an npm package', pluginId);
}
}
if (plugin === null) {
@ -91,9 +109,17 @@ export default function loadPlugin<T extends IPlugin<T>>(config: Config, pluginC
}
if (plugin === null) {
logger.error({ content: pluginId, prefix }, 'plugin not found. try npm install @{prefix}-@{content}');
throw Error(`
${prefix}-${pluginId} plugin not found. try "npm install ${prefix}-${pluginId}"`);
if (isScoped) {
logger.error({ content: pluginId }, 'plugin not found. try npm install @{content}');
} else {
logger.error({ content: pluginId, prefix }, 'plugin not found. try npm install @{prefix}-@{content}');
}
const msg = isScoped
? `
${pluginId} plugin not found. try "npm install ${pluginId}"`
: `
${prefix}-${pluginId} plugin not found. try "npm install ${prefix}-${pluginId}"`;
throw Error(msg);
}
if (!isValid(plugin)) {
@ -103,7 +129,13 @@ export default function loadPlugin<T extends IPlugin<T>>(config: Config, pluginC
/* eslint new-cap:off */
try {
plugin = isES6(plugin) ? new plugin.default(mergeConfig(config, pluginConfigs[pluginId]), params) : plugin(pluginConfigs[pluginId], params);
if (isES6(plugin)) {
debug('plugin is ES6');
plugin = new plugin.default(mergeConfig(config, pluginConfigs[pluginId]), params);
} else {
debug('plugin is commonJS');
plugin = plugin(pluginConfigs[pluginId], params);
}
} catch (error) {
plugin = null;
logger.error({ error, pluginId }, 'error loading a plugin @{pluginId}: @{error}');
@ -111,11 +143,19 @@ export default function loadPlugin<T extends IPlugin<T>>(config: Config, pluginC
/* eslint new-cap:off */
if (plugin === null || !sanityCheck(plugin)) {
logger.error({ content: pluginId, prefix }, "@{prefix}-@{content} doesn't look like a valid plugin");
if (isScoped) {
logger.error({ content: pluginId }, "@{content} doesn't look like a valid plugin");
} else {
logger.error({ content: pluginId, prefix }, "@{prefix}-@{content} doesn't look like a valid plugin");
}
throw Error(`sanity check has failed, "${pluginId}" is not a valid plugin`);
}
logger.warn({ content: pluginId, prefix }, 'Plugin successfully loaded: @{prefix}-@{content}');
if (isScoped) {
logger.info({ content: pluginId }, 'plugin successfully loaded: @{content}');
} else {
logger.info({ content: pluginId, prefix }, 'plugin successfully loaded: @{prefix}-@{content}');
}
return plugin;
});
}

@ -28,6 +28,27 @@ describe('plugin loader', () => {
expect(plugins).toHaveLength(1);
});
test('fails on load scoped auth missing package', () => {
const _config = buildConf('@scope/package');
try {
// @ts-ignore
loadPlugin(_config, { '@scope/package': {} }, {}, undefined);
} catch (e) {
expect(e.message).toMatch(`@scope/package plugin not found. try \"npm install @scope/package\"`);
}
});
// This package is locally installed, just a dummy scoped auth plugin
// TODO: move this package to the public registry
test('should load @verdaccio-scope/verdaccio-auth-foo scoped package', () => {
const _config = buildConf('@verdaccio-scope/verdaccio-auth-foo');
// @ts-ignore
const plugins = loadPlugin(_config, { '@verdaccio-scope/verdaccio-auth-foo': {} }, {}, function (plugin) {
return plugin.authenticate || plugin.allow_access || plugin.allow_publish;
});
expect(plugins).toHaveLength(1);
});
test('testing storage valid plugin loader', () => {
const _config = buildConf('verdaccio-es6-plugin');
// @ts-ignore

BIN
yarn.lock

Binary file not shown.