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:
parent
9d9c086ba3
commit
5c50ec9a2c
18
.pnp.js
generated
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"],
|
||||
|
BIN
.yarn/cache/@verdaccio-scope-verdaccio-auth-foo-npm-0.0.2-e8d6fdf0d9-99f4727a67.zip
vendored
Normal file
BIN
.yarn/cache/@verdaccio-scope-verdaccio-auth-foo-npm-0.0.2-e8d6fdf0d9-99f4727a67.zip
vendored
Normal file
Binary file not shown.
@ -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
BIN
yarn.lock
Binary file not shown.
Loading…
Reference in New Issue
Block a user