diff --git a/.pnp.js b/.pnp.js index e108acb50..358444e45 100755 --- a/.pnp.js +++ b/.pnp.js @@ -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"], diff --git a/.yarn/cache/@verdaccio-scope-verdaccio-auth-foo-npm-0.0.2-e8d6fdf0d9-99f4727a67.zip b/.yarn/cache/@verdaccio-scope-verdaccio-auth-foo-npm-0.0.2-e8d6fdf0d9-99f4727a67.zip new file mode 100644 index 000000000..86c0a5529 Binary files /dev/null and b/.yarn/cache/@verdaccio-scope-verdaccio-auth-foo-npm-0.0.2-e8d6fdf0d9-99f4727a67.zip differ diff --git a/package.json b/package.json index 270790269..e96b72c80 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/lib/plugin-loader.ts b/src/lib/plugin-loader.ts index 4065a96d5..fdc4ae15a 100644 --- a/src/lib/plugin-loader.ts +++ b/src/lib/plugin-loader.ts @@ -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>(config: Config, pluginConfigs: any = {}, params: any, sanityCheck: any, prefix: string = 'verdaccio'): any[] { return Object.keys(pluginConfigs).map((pluginId: string): IPlugin => { 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>(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>(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>(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>(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>(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; }); } diff --git a/test/unit/modules/plugin/plugin_loader.spec.ts b/test/unit/modules/plugin/plugin_loader.spec.ts index 3b189166a..39efb6d2a 100644 --- a/test/unit/modules/plugin/plugin_loader.spec.ts +++ b/test/unit/modules/plugin/plugin_loader.spec.ts @@ -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 diff --git a/yarn.lock b/yarn.lock index 24da54576..a339a746d 100644 Binary files a/yarn.lock and b/yarn.lock differ