diff --git a/lib/datasource/api.ts b/lib/datasource/api.ts
index 381b5c80d45b305e80cba44816b586d9f929a2fc..7146f5565bc50b6201d230cc357a5ebff0f6fafb 100644
--- a/lib/datasource/api.ts
+++ b/lib/datasource/api.ts
@@ -25,7 +25,7 @@ import { HexDatasource } from './hex';
 import { JenkinsPluginsDatasource } from './jenkins-plugins';
 import { MavenDatasource } from './maven';
 import { NodeDatasource } from './node';
-import * as npm from './npm';
+import { NpmDatasource } from './npm';
 import { NugetDatasource } from './nuget';
 import { OrbDatasource } from './orb';
 import { PackagistDatasource } from './packagist';
@@ -70,7 +70,7 @@ api.set(HexDatasource.id, new HexDatasource());
 api.set(JenkinsPluginsDatasource.id, new JenkinsPluginsDatasource());
 api.set(MavenDatasource.id, new MavenDatasource());
 api.set(NodeDatasource.id, new NodeDatasource());
-api.set('npm', npm);
+api.set(NpmDatasource.id, new NpmDatasource());
 api.set(NugetDatasource.id, new NugetDatasource());
 api.set(OrbDatasource.id, new OrbDatasource());
 api.set(PackagistDatasource.id, new PackagistDatasource());
diff --git a/lib/datasource/metadata.spec.ts b/lib/datasource/metadata.spec.ts
index 019be661bdf3f6e9d5c19eb5675aa06db9c99908..50d5853d34ca9ca70ef53448d368344979340dd9 100644
--- a/lib/datasource/metadata.spec.ts
+++ b/lib/datasource/metadata.spec.ts
@@ -1,6 +1,6 @@
 import { MavenDatasource } from './maven';
 import { addMetaData, massageGithubUrl } from './metadata';
-import * as datasourceNpm from './npm';
+import { NpmDatasource } from './npm';
 import { PypiDatasource } from './pypi';
 import type { ReleaseResult } from './types';
 
@@ -105,7 +105,7 @@ describe('datasource/metadata', () => {
         },
       ],
     };
-    const datasource = datasourceNpm.id;
+    const datasource = NpmDatasource.id;
     const lookupName = 'dropzone';
 
     addMetaData(dep, datasource, lookupName);
@@ -124,7 +124,7 @@ describe('datasource/metadata', () => {
         },
       ],
     };
-    const datasource = datasourceNpm.id;
+    const datasource = NpmDatasource.id;
     const lookupName = 'dropzone';
 
     addMetaData(dep, datasource, lookupName);
@@ -143,7 +143,7 @@ describe('datasource/metadata', () => {
         },
       ],
     };
-    const datasource = datasourceNpm.id;
+    const datasource = NpmDatasource.id;
     const lookupName = 'dropzone';
 
     addMetaData(dep, datasource, lookupName);
@@ -162,7 +162,7 @@ describe('datasource/metadata', () => {
         },
       ],
     };
-    const datasource = datasourceNpm.id;
+    const datasource = NpmDatasource.id;
     const lookupName = 'dropzone';
 
     addMetaData(dep, datasource, lookupName);
diff --git a/lib/datasource/npm/get.spec.ts b/lib/datasource/npm/get.spec.ts
index 36df1ce3bcb75270700b97643d1b7f8cedd15c75..d4f9524b09873b585f37363a838bb9917e4f2f77 100644
--- a/lib/datasource/npm/get.spec.ts
+++ b/lib/datasource/npm/get.spec.ts
@@ -1,6 +1,7 @@
 import * as httpMock from '../../../test/http-mock';
 import { ExternalHostError } from '../../types/errors/external-host-error';
 import * as hostRules from '../../util/host-rules';
+import { Http } from '../../util/http';
 import { getDependency, resetMemCache } from './get';
 import { setNpmrc } from './npmrc';
 
@@ -10,6 +11,8 @@ function getPath(s = ''): string {
   return `${prePath}/@myco%2Ftest`;
 }
 
+const http = new Http('npm');
+
 describe('datasource/npm/get', () => {
   beforeEach(() => {
     jest.clearAllMocks();
@@ -40,7 +43,7 @@ describe('datasource/npm/get', () => {
         .reply(200, { name: '@myco/test' });
 
       setNpmrc(npmrc);
-      await getDependency('@myco/test');
+      await getDependency(http, '@myco/test');
 
       const trace = httpMock.getTrace();
       expect(trace[0].headers.authorization).toBe('Bearer XXX');
@@ -72,7 +75,7 @@ describe('datasource/npm/get', () => {
         .get(getPath(npmrc))
         .reply(200, { name: '@myco/test' });
       setNpmrc(npmrc);
-      await getDependency('@myco/test');
+      await getDependency(http, '@myco/test');
 
       const trace = httpMock.getTrace();
       expect(trace[0].headers.authorization).toBe('Basic dGVzdDp0ZXN0');
@@ -96,7 +99,7 @@ describe('datasource/npm/get', () => {
         .get(getPath(npmrc))
         .reply(200, { name: '@myco/test' });
       setNpmrc(npmrc);
-      await getDependency('@myco/test');
+      await getDependency(http, '@myco/test');
 
       const trace = httpMock.getTrace();
       expect(trace[0].headers.authorization).toBeUndefined();
@@ -122,7 +125,7 @@ describe('datasource/npm/get', () => {
       .get(getPath(npmrc))
       .reply(200, { name: '@myco/test' });
     setNpmrc(npmrc);
-    await getDependency('@myco/test');
+    await getDependency(http, '@myco/test');
     expect(httpMock.getTrace()).toMatchSnapshot();
   });
 
@@ -143,7 +146,7 @@ describe('datasource/npm/get', () => {
       .get('/renovate')
       .reply(200, { name: 'renovate' });
     setNpmrc(npmrc);
-    await getDependency('renovate');
+    await getDependency(http, 'renovate');
     expect(httpMock.getTrace()).toMatchSnapshot();
   });
 
@@ -165,7 +168,7 @@ describe('datasource/npm/get', () => {
       .get('/renovate')
       .reply(200, { name: 'renovate' });
     setNpmrc(npmrc);
-    await getDependency('renovate');
+    await getDependency(http, 'renovate');
     expect(httpMock.getTrace()).toMatchSnapshot();
   });
 
@@ -178,7 +181,7 @@ describe('datasource/npm/get', () => {
       .scope('https://test.org')
       .get('/none')
       .reply(200, { name: '@myco/test' });
-    expect(await getDependency('none')).toBeNull();
+    expect(await getDependency(http, 'none')).toBeNull();
 
     httpMock
       .scope('https://test.org')
@@ -189,7 +192,7 @@ describe('datasource/npm/get', () => {
         versions: { '1.0.0': {} },
         'dist-tags': { latest: '1.0.0' },
       });
-    expect(await getDependency('@myco/test')).toBeDefined();
+    expect(await getDependency(http, '@myco/test')).toBeDefined();
 
     httpMock
       .scope('https://test.org')
@@ -199,26 +202,26 @@ describe('datasource/npm/get', () => {
         versions: { '1.0.0': {} },
         'dist-tags': { latest: '1.0.0' },
       });
-    expect(await getDependency('@myco/test2')).toBeDefined();
+    expect(await getDependency(http, '@myco/test2')).toBeDefined();
 
     httpMock.scope('https://test.org').get('/error-401').reply(401);
-    expect(await getDependency('error-401')).toBeNull();
+    expect(await getDependency(http, 'error-401')).toBeNull();
 
     httpMock.scope('https://test.org').get('/error-402').reply(402);
-    expect(await getDependency('error-402')).toBeNull();
+    expect(await getDependency(http, 'error-402')).toBeNull();
 
     httpMock.scope('https://test.org').get('/error-404').reply(404);
-    expect(await getDependency('error-404')).toBeNull();
+    expect(await getDependency(http, 'error-404')).toBeNull();
 
     httpMock.scope('https://test.org').get('/error4').reply(200, null);
-    expect(await getDependency('error4')).toBeNull();
+    expect(await getDependency(http, 'error4')).toBeNull();
 
     setNpmrc();
     httpMock
       .scope('https://registry.npmjs.org')
       .get('/npm-parse-error')
       .reply(200, 'not-a-json');
-    await expect(getDependency('npm-parse-error')).rejects.toThrow(
+    await expect(getDependency(http, 'npm-parse-error')).rejects.toThrow(
       ExternalHostError
     );
 
@@ -226,7 +229,7 @@ describe('datasource/npm/get', () => {
       .scope('https://registry.npmjs.org')
       .get('/npm-error-402')
       .reply(402);
-    expect(await getDependency('npm-error-402')).toBeNull();
+    expect(await getDependency(http, 'npm-error-402')).toBeNull();
 
     expect(httpMock.getTrace()).toMatchSnapshot();
   });
@@ -247,7 +250,7 @@ describe('datasource/npm/get', () => {
         'dist-tags': { latest: '1.0.0' },
       });
 
-    const dep = await getDependency('@neutrinojs/react');
+    const dep = await getDependency(http, '@neutrinojs/react');
 
     expect(dep.sourceUrl).toBe('https://github.com/neutrinojs/neutrino');
     expect(dep.sourceDirectory).toBe('packages/react');
@@ -298,7 +301,7 @@ describe('datasource/npm/get', () => {
         'dist-tags': { latest: '2.0.0' },
       });
 
-    const dep = await getDependency('vue');
+    const dep = await getDependency(http, 'vue');
 
     expect(dep.sourceUrl).toBe('https://github.com/vuejs/vue.git');
     expect(dep.releases[0].sourceUrl).toBeUndefined();
@@ -324,7 +327,7 @@ describe('datasource/npm/get', () => {
         'dist-tags': { latest: '1.0.0' },
       });
 
-    const dep = await getDependency('@neutrinojs/react');
+    const dep = await getDependency(http, '@neutrinojs/react');
 
     expect(dep.sourceUrl).toBe('https://github.com/neutrinojs/neutrino');
     expect(dep.sourceDirectory).toBe('packages/foo');
@@ -362,7 +365,7 @@ describe('datasource/npm/get', () => {
         'dist-tags': { latest: '1.0.0' },
       });
 
-    const dep = await getDependency('@neutrinojs/react');
+    const dep = await getDependency(http, '@neutrinojs/react');
 
     expect(dep.sourceUrl).toBe(
       'https://bitbucket.org/neutrinojs/neutrino/tree/master/packages/react'
diff --git a/lib/datasource/npm/get.ts b/lib/datasource/npm/get.ts
index d9de4aedc91b03b71a6549a48bab532e30452a07..8d0cfea3a75cb80e5d4b20bd358bf8db01dbf5f4 100644
--- a/lib/datasource/npm/get.ts
+++ b/lib/datasource/npm/get.ts
@@ -3,13 +3,11 @@ import is from '@sindresorhus/is';
 import { logger } from '../../logger';
 import { ExternalHostError } from '../../types/errors/external-host-error';
 import * as packageCache from '../../util/cache/package';
-import { Http, HttpOptions } from '../../util/http';
+import type { Http, HttpOptions } from '../../util/http';
 import { id } from './common';
 import { resolvePackage } from './npmrc';
 import type { NpmDependency, NpmRelease, NpmResponse } from './types';
 
-const http = new Http(id);
-
 let memcache: Record<string, string> = {};
 
 export function resetMemCache(): void {
@@ -53,6 +51,7 @@ function getPackageSource(repository: any): PackageSource {
 }
 
 export async function getDependency(
+  http: Http,
   packageName: string
 ): Promise<NpmDependency | null> {
   logger.trace(`npm.getDependency(${packageName})`);
diff --git a/lib/datasource/npm/index.spec.ts b/lib/datasource/npm/index.spec.ts
index ed891df75ad0b6dd2f739d799c8da2e45014b68a..f373ac6f51575b0e46fad72aadf257aa77a99584 100644
--- a/lib/datasource/npm/index.spec.ts
+++ b/lib/datasource/npm/index.spec.ts
@@ -5,7 +5,9 @@ import * as httpMock from '../../../test/http-mock';
 import { GlobalConfig } from '../../config/global';
 import { EXTERNAL_HOST_ERROR } from '../../constants/error-messages';
 import * as hostRules from '../../util/host-rules';
-import { id as datasource, getNpmrc, resetCache, setNpmrc } from '.';
+import { NpmDatasource, getNpmrc, resetCache, setNpmrc } from '.';
+
+const datasource = NpmDatasource.id;
 
 jest.mock('registry-auth-token');
 jest.mock('delay');
diff --git a/lib/datasource/npm/index.ts b/lib/datasource/npm/index.ts
index 1f3bfc82ecab6f54256785f2ddc741073a5c82a3..a3c19643d27dba3dc09bd2c29361345b10b346c8 100644
--- a/lib/datasource/npm/index.ts
+++ b/lib/datasource/npm/index.ts
@@ -1,8 +1,39 @@
+import is from '@sindresorhus/is';
 import * as npmVersioning from '../../versioning/npm';
+import { Datasource } from '../datasource';
+import type { GetReleasesConfig, ReleaseResult } from '../types';
+import { id } from './common';
+import { getDependency } from './get';
+import { setNpmrc } from './npmrc';
 
 export { resetMemCache, resetCache } from './get';
-export { getReleases } from './releases';
 export { getNpmrc, setNpmrc } from './npmrc';
-export { id } from './common';
-export const defaultVersioning = npmVersioning.id;
+
 export const customRegistrySupport = false;
+
+export class NpmDatasource extends Datasource {
+  static readonly id = id;
+
+  override readonly customRegistrySupport = false;
+
+  override readonly defaultVersioning = npmVersioning.id;
+
+  constructor() {
+    super(NpmDatasource.id);
+  }
+
+  async getReleases({
+    lookupName,
+    npmrc,
+  }: GetReleasesConfig): Promise<ReleaseResult | null> {
+    if (is.string(npmrc)) {
+      setNpmrc(npmrc);
+    }
+    const res = await getDependency(this.http, lookupName);
+    if (res) {
+      res.tags = res['dist-tags'];
+      delete res['dist-tags'];
+    }
+    return res;
+  }
+}
diff --git a/lib/datasource/npm/releases.ts b/lib/datasource/npm/releases.ts
deleted file mode 100644
index ef724d708f103097b13bdfeaa57da83d09293e39..0000000000000000000000000000000000000000
--- a/lib/datasource/npm/releases.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import is from '@sindresorhus/is';
-import type { GetReleasesConfig, ReleaseResult } from '../types';
-import { getDependency } from './get';
-import { setNpmrc } from './npmrc';
-
-export async function getReleases({
-  lookupName,
-  npmrc,
-}: GetReleasesConfig): Promise<ReleaseResult | null> {
-  if (is.string(npmrc)) {
-    setNpmrc(npmrc);
-  }
-  const res = await getDependency(lookupName);
-  if (res) {
-    res.tags = res['dist-tags'];
-    delete res['dist-tags'];
-  }
-  return res;
-}
diff --git a/lib/manager/meteor/extract.ts b/lib/manager/meteor/extract.ts
index d5dde17014c566f9c6a5da98a824a24c95ed535c..2c3a0179080106b0716e40bdd5a7426bba703831 100644
--- a/lib/manager/meteor/extract.ts
+++ b/lib/manager/meteor/extract.ts
@@ -1,4 +1,4 @@
-import { id as npmId } from '../../datasource/npm';
+import { NpmDatasource } from '../../datasource/npm';
 import { logger } from '../../logger';
 import { regEx } from '../../util/regex';
 import type { PackageDependency, PackageFile } from '../types';
@@ -25,7 +25,7 @@ export function extractPackageFile(content: string): PackageFile | null {
         return {
           depName,
           currentValue,
-          datasource: npmId,
+          datasource: NpmDatasource.id,
         };
       })
       .filter((dep) => dep.depName && dep.currentValue);
diff --git a/lib/manager/meteor/index.ts b/lib/manager/meteor/index.ts
index 63a1000f81414fd1f0e8916ae7c49d8edacd2410..dfa1089d6667a98225202aeabd04019611c6316a 100644
--- a/lib/manager/meteor/index.ts
+++ b/lib/manager/meteor/index.ts
@@ -1,5 +1,5 @@
 import { ProgrammingLanguage } from '../../constants';
-import { id as npmId } from '../../datasource/npm';
+import { NpmDatasource } from '../../datasource/npm';
 
 export { extractPackageFile } from './extract';
 
@@ -9,4 +9,4 @@ export const defaultConfig = {
   fileMatch: ['(^|/)package.js$'],
 };
 
-export const supportedDatasources = [npmId];
+export const supportedDatasources = [NpmDatasource.id];
diff --git a/lib/manager/npm/extract/index.ts b/lib/manager/npm/extract/index.ts
index 4b57e051fc91941dc34948347046d8668ac1437e..ccf7b20ab81b5860ef6289c0aeaec12a5cdc763d 100644
--- a/lib/manager/npm/extract/index.ts
+++ b/lib/manager/npm/extract/index.ts
@@ -3,7 +3,7 @@ import validateNpmPackageName from 'validate-npm-package-name';
 import { GlobalConfig } from '../../../config/global';
 import { CONFIG_VALIDATION } from '../../../constants/error-messages';
 import { GithubTagsDatasource } from '../../../datasource/github-tags';
-import { id as npmId } from '../../../datasource/npm';
+import { NpmDatasource } from '../../../datasource/npm';
 import { logger } from '../../../logger';
 import { getSiblingFileName, readLocalFile } from '../../../util/fs';
 import { newlineRegex, regEx } from '../../../util/regex';
@@ -193,7 +193,7 @@ export async function extractPackageFile(
         dep.versioning = nodeVersioning.id;
         constraints.node = dep.currentValue;
       } else if (depName === 'yarn') {
-        dep.datasource = npmId;
+        dep.datasource = NpmDatasource.id;
         dep.commitMessageTopic = 'Yarn';
         constraints.yarn = dep.currentValue;
         if (
@@ -203,11 +203,11 @@ export async function extractPackageFile(
           dep.lookupName = '@yarnpkg/cli';
         }
       } else if (depName === 'npm') {
-        dep.datasource = npmId;
+        dep.datasource = NpmDatasource.id;
         dep.commitMessageTopic = 'npm';
         constraints.npm = dep.currentValue;
       } else if (depName === 'pnpm') {
-        dep.datasource = npmId;
+        dep.datasource = NpmDatasource.id;
         dep.commitMessageTopic = 'pnpm';
         constraints.pnpm = dep.currentValue;
       } else if (depName === 'vscode') {
@@ -230,10 +230,10 @@ export async function extractPackageFile(
         dep.lookupName = 'nodejs/node';
         dep.versioning = nodeVersioning.id;
       } else if (depName === 'yarn') {
-        dep.datasource = npmId;
+        dep.datasource = NpmDatasource.id;
         dep.commitMessageTopic = 'Yarn';
       } else if (depName === 'npm') {
-        dep.datasource = npmId;
+        dep.datasource = NpmDatasource.id;
       } else {
         dep.skipReason = 'unknown-volta';
       }
@@ -263,7 +263,7 @@ export async function extractPackageFile(
       return dep;
     }
     if (isValid(dep.currentValue)) {
-      dep.datasource = npmId;
+      dep.datasource = NpmDatasource.id;
       if (dep.currentValue === '*') {
         dep.skipReason = 'any-version';
       }
diff --git a/lib/manager/npm/index.ts b/lib/manager/npm/index.ts
index 653ee81edb490878a102b8098d2985d1422eb591..0f5c452963eb9480a4e503a37ab64022be5c5598 100644
--- a/lib/manager/npm/index.ts
+++ b/lib/manager/npm/index.ts
@@ -1,6 +1,6 @@
 import { ProgrammingLanguage } from '../../constants';
 import { GithubTagsDatasource } from '../../datasource/github-tags';
-import { id as npmId } from '../../datasource/npm';
+import { NpmDatasource } from '../../datasource/npm';
 import * as npmVersioning from '../../versioning/npm';
 
 export { detectGlobalConfig } from './detect';
@@ -31,4 +31,4 @@ export const defaultConfig = {
   },
 };
 
-export const supportedDatasources = [GithubTagsDatasource.id, npmId];
+export const supportedDatasources = [GithubTagsDatasource.id, NpmDatasource.id];
diff --git a/lib/manager/npm/post-update/index.ts b/lib/manager/npm/post-update/index.ts
index 9f19c6e144f9092ab7be70d1502daf8aa1ac66d0..6207c66889aaa5ac7a7236d1fcd13e95f44c9212 100644
--- a/lib/manager/npm/post-update/index.ts
+++ b/lib/manager/npm/post-update/index.ts
@@ -5,7 +5,7 @@ import { dump, load } from 'js-yaml';
 import upath from 'upath';
 import { GlobalConfig } from '../../../config/global';
 import { SYSTEM_INSUFFICIENT_DISK_SPACE } from '../../../constants/error-messages';
-import { id as npmId } from '../../../datasource/npm';
+import { NpmDatasource } from '../../../datasource/npm';
 import { logger } from '../../../logger';
 import { ExternalHostError } from '../../../types/errors/external-host-error';
 import { getChildProcessEnv } from '../../../util/exec/env';
@@ -563,7 +563,7 @@ export async function getAdditionalFiles(
             const err = new Error(
               'lock file failed for the dependency being updated - skipping branch creation'
             );
-            throw new ExternalHostError(err, npmId);
+            throw new ExternalHostError(err, NpmDatasource.id);
           }
         }
       }
@@ -650,7 +650,7 @@ export async function getAdditionalFiles(
               new Error(
                 'lock file failed for the dependency being updated - skipping branch creation'
               ),
-              npmId
+              NpmDatasource.id
             );
           }
           /* eslint-enable no-useless-escape */
@@ -727,7 +727,7 @@ export async function getAdditionalFiles(
               Error(
                 'lock file failed for the dependency being updated - skipping branch creation'
               ),
-              npmId
+              NpmDatasource.id
             );
           }
         }
@@ -810,7 +810,7 @@ export async function getAdditionalFiles(
             Error(
               'lock file failed for the dependency being updated - skipping branch creation'
             ),
-            npmId
+            NpmDatasource.id
           );
         }
         /* eslint-enable no-useless-escape */
@@ -827,7 +827,7 @@ export async function getAdditionalFiles(
             Error(
               'lock file failed for the dependency being updated - skipping branch creation'
             ),
-            npmId
+            NpmDatasource.id
           );
         }
       }
diff --git a/lib/manager/npm/post-update/yarn.ts b/lib/manager/npm/post-update/yarn.ts
index 24aa84c5b23c1f33a2127195310ec814a1562d76..5420e46b27963c4b2fe1280a26ba442d70c9b6ef 100644
--- a/lib/manager/npm/post-update/yarn.ts
+++ b/lib/manager/npm/post-update/yarn.ts
@@ -7,7 +7,7 @@ import {
   SYSTEM_INSUFFICIENT_DISK_SPACE,
   TEMPORARY_ERROR,
 } from '../../../constants/error-messages';
-import { id as npmId } from '../../../datasource/npm';
+import { NpmDatasource } from '../../../datasource/npm';
 import { logger } from '../../../logger';
 import { ExternalHostError } from '../../../types/errors/external-host-error';
 import { exec } from '../../../util/exec';
@@ -249,7 +249,7 @@ export async function generateLockFile(
         err.stderr.includes('getaddrinfo ENOTFOUND registry.yarnpkg.com') ||
         err.stderr.includes('getaddrinfo ENOTFOUND registry.npmjs.org')
       ) {
-        throw new ExternalHostError(err, npmId);
+        throw new ExternalHostError(err, NpmDatasource.id);
       }
     }
     return { error: true, stderr: err.stderr, stdout: err.stdout };
diff --git a/lib/workers/repository/init/vulnerability.ts b/lib/workers/repository/init/vulnerability.ts
index acdeb49b7e18bcd1ca3177836d71ef515438c21c..970db34363a6f46af5ed40a49289ad4825af84cf 100644
--- a/lib/workers/repository/init/vulnerability.ts
+++ b/lib/workers/repository/init/vulnerability.ts
@@ -1,7 +1,7 @@
 import type { PackageRule, RenovateConfig } from '../../../config/types';
 import { NO_VULNERABILITY_ALERTS } from '../../../constants/error-messages';
 import { MavenDatasource } from '../../../datasource/maven';
-import { id as npmId } from '../../../datasource/npm';
+import { NpmDatasource } from '../../../datasource/npm';
 import { NugetDatasource } from '../../../datasource/nuget';
 import { PypiDatasource } from '../../../datasource/pypi';
 import { RubyGemsDatasource } from '../../../datasource/rubygems';
@@ -88,7 +88,7 @@ export async function detectVulnerabilityAlerts(
       }
       const datasourceMapping: Record<string, string> = {
         MAVEN: MavenDatasource.id,
-        NPM: npmId,
+        NPM: NpmDatasource.id,
         NUGET: NugetDatasource.id,
         PIP: PypiDatasource.id,
         RUBYGEMS: RubyGemsDatasource.id,
diff --git a/lib/workers/repository/process/lookup/index.spec.ts b/lib/workers/repository/process/lookup/index.spec.ts
index cf9a06d6ee29eac199985ec3f9a1df1daa2f752c..eff2420f50329ce0eac2811f6e1f2daf385abbe8 100644
--- a/lib/workers/repository/process/lookup/index.spec.ts
+++ b/lib/workers/repository/process/lookup/index.spec.ts
@@ -11,7 +11,7 @@ import { GitRefsDatasource } from '../../../../datasource/git-refs';
 import { GitDatasource } from '../../../../datasource/git-refs/base';
 import { GithubReleasesDatasource } from '../../../../datasource/github-releases';
 import { GithubTagsDatasource } from '../../../../datasource/github-tags';
-import { id as datasourceNpmId } from '../../../../datasource/npm';
+import { NpmDatasource } from '../../../../datasource/npm';
 import { PackagistDatasource } from '../../../../datasource/packagist';
 import { PypiDatasource } from '../../../../datasource/pypi';
 import { id as dockerVersioningId } from '../../../../versioning/docker';
@@ -63,7 +63,7 @@ describe('workers/repository/process/lookup/index', () => {
     it('returns rollback for pinned version', async () => {
       config.currentValue = '0.9.99';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       config.rollbackPrs = true;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
@@ -74,7 +74,7 @@ describe('workers/repository/process/lookup/index', () => {
     it('returns rollback for ranged version', async () => {
       config.currentValue = '^0.9.99';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       config.rollbackPrs = true;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
@@ -85,7 +85,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '^0.4.0';
       config.rangeStrategy = 'pin';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '0.4.4', updateType: 'pin' },
@@ -97,7 +97,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '^0.4.0';
       config.rangeStrategy = 'update-lockfile';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       config.separateMinorPatch = true;
       config.lockedVersion = '0.4.0';
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
@@ -112,7 +112,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '0.4.0';
       config.rangeStrategy = 'pin';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       const res = await lookup.lookupUpdates(config);
       expect(res.updates).toMatchSnapshot();
@@ -124,7 +124,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'pin';
       config.depName = 'q';
       config.separateMinorPatch = true;
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       const res = await lookup.lookupUpdates(config);
       expect(res.updates).toMatchSnapshot();
@@ -136,7 +136,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'pin';
       config.separateMajorMinor = false;
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       const res = await lookup.lookupUpdates(config);
       expect(res.updates).toMatchSnapshot();
@@ -147,7 +147,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '^0.4.0';
       config.rangeStrategy = 'pin';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '0.4.4', updateType: 'pin' },
@@ -159,7 +159,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '0.4.0';
       config.allowedVersions = '<1';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toHaveLength(1);
     });
@@ -167,7 +167,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '0.4.0';
       config.allowedVersions = '/^0/';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toHaveLength(1);
     });
@@ -175,7 +175,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '0.4.0';
       config.allowedVersions = '!/^1/';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toHaveLength(1);
     });
@@ -184,7 +184,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.allowedVersions = '<1';
       config.depName = 'q';
       config.versioning = dockerVersioningId; // this doesn't make sense but works for this test
-      config.datasource = datasourceNpmId; // this doesn't make sense but works for this test
+      config.datasource = NpmDatasource.id; // this doesn't make sense but works for this test
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toHaveLength(1);
     });
@@ -193,7 +193,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.allowedVersions = '==0.9.4';
       config.depName = 'q';
       config.versioning = poetryVersioningId; // this doesn't make sense but works for this test
-      config.datasource = datasourceNpmId; // this doesn't make sense but works for this test
+      config.datasource = NpmDatasource.id; // this doesn't make sense but works for this test
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toHaveLength(1);
     });
@@ -201,7 +201,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '0.4.0';
       config.allowedVersions = 'less than 1';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       await expect(lookup.lookupUpdates(config)).rejects.toThrow(
         Error(CONFIG_VALIDATION)
@@ -211,7 +211,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '0.9.0';
       config.rangeStrategy = 'pin';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       const res = await lookup.lookupUpdates(config);
       expect(res.updates).toMatchSnapshot();
@@ -229,7 +229,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '0.9.0';
       config.rangeStrategy = 'pin';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       const res = await lookup.lookupUpdates(config);
       expect(res.updates).toMatchSnapshot();
@@ -240,7 +240,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '0.9.0';
       config.rangeStrategy = 'pin';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '0.9.7', updateType: 'patch' },
@@ -252,7 +252,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '0.8.0';
       config.rangeStrategy = 'pin';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       const res = await lookup.lookupUpdates(config);
       expect(res.updates).toHaveLength(3);
@@ -263,7 +263,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '^0.4.0';
       config.rangeStrategy = 'pin';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '0.4.4', updateType: 'pin' },
@@ -275,7 +275,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '1.0.0';
       config.rangeStrategy = 'pin';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '1.4.1', updateType: 'minor' },
@@ -285,7 +285,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '1.0.0';
       config.isVulnerabilityAlert = true;
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       const res = (await lookup.lookupUpdates(config)).updates;
       expect(res).toMatchSnapshot();
@@ -295,7 +295,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '~0.4.0';
       config.rangeStrategy = 'pin';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '0.4.4', updateType: 'pin' },
@@ -307,7 +307,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '~0.9.0';
       config.rangeStrategy = 'pin';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '0.9.7', updateType: 'pin' },
@@ -318,7 +318,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '~1.0.0';
       config.rangeStrategy = 'pin';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '1.0.1', updateType: 'pin' },
@@ -330,7 +330,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.lockedVersion = '1.2.1';
       config.rangeStrategy = 'update-lockfile';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       const res = await lookup.lookupUpdates(config);
       expect(res.updates).toMatchSnapshot();
@@ -342,7 +342,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.lockedVersion = '1.2.1';
       config.rangeStrategy = 'in-range-only';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       const res = await lookup.lookupUpdates(config);
       expect(res.updates).toMatchSnapshot();
@@ -354,7 +354,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.lockedVersion = '1.2.0';
       config.rangeStrategy = 'in-range-only';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       const res = await lookup.lookupUpdates(config);
       expect(res.updates).toBeEmptyArray();
@@ -364,7 +364,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.lockedVersion = '1.2.1';
       config.rangeStrategy = 'update-lockfile';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       const res = await lookup.lookupUpdates(config);
       expect(res.updates).toMatchInlineSnapshot(`
@@ -389,7 +389,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '~1.3.0';
       config.rangeStrategy = 'widen';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '~1.3.0 || ~1.4.0', updateType: 'minor' },
@@ -399,7 +399,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '~1.2.0 || ~1.3.0';
       config.rangeStrategy = 'replace';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '~1.4.0', updateType: 'minor' },
@@ -409,7 +409,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '^2.0.0';
       config.rangeStrategy = 'widen';
       config.depName = 'webpack';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock
         .scope('https://registry.npmjs.org')
         .get('/webpack')
@@ -422,7 +422,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '^1.0.0 || ^2.0.0';
       config.rangeStrategy = 'replace';
       config.depName = 'webpack';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock
         .scope('https://registry.npmjs.org')
         .get('/webpack')
@@ -435,7 +435,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '^1.0.0';
       config.rangeStrategy = 'pin';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '1.4.1', updateType: 'pin' },
@@ -446,7 +446,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.lockedVersion = '1.0.0';
       config.rangeStrategy = 'pin';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '1.0.0', updateType: 'pin' },
@@ -457,7 +457,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'replace';
       config.currentValue = '^1.0.0';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toHaveLength(0);
     });
@@ -466,7 +466,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '^1.0.0';
       config.lockedVersion = '1.1.0';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toHaveLength(0);
     });
@@ -474,7 +474,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'pin';
       config.currentValue = '~1.3.0';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '1.3.0', updateType: 'pin' },
@@ -485,7 +485,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '1.3.x';
       config.rangeStrategy = 'pin';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '1.3.0', updateType: 'pin' },
@@ -496,7 +496,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'replace';
       config.currentValue = '~1.3.0';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '~1.4.0', updateType: 'minor' },
@@ -506,7 +506,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'replace';
       config.currentValue = '0.x';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '1.x', updateType: 'major' },
@@ -516,7 +516,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'replace';
       config.currentValue = '1.3.x';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '1.4.x', updateType: 'minor' },
@@ -526,7 +526,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'widen';
       config.currentValue = '1.2.x - 1.3.x';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '1.2.x - 1.4.x', updateType: 'minor' },
@@ -536,7 +536,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'replace';
       config.currentValue = '0';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '1', updateType: 'major' },
@@ -546,7 +546,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'replace';
       config.currentValue = '1.3';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '1.4', updateType: 'minor' },
@@ -556,7 +556,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'replace';
       config.currentValue = '~0.7.0';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '~0.9.0', updateType: 'minor' },
@@ -567,7 +567,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'replace';
       config.currentValue = '^0.7.0';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '^0.9.0', updateType: 'minor' },
@@ -578,7 +578,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'widen';
       config.currentValue = '^0.7.0 || ^0.8.0';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       const res = await lookup.lookupUpdates(config);
       expect(res.updates).toHaveLength(2);
@@ -591,7 +591,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'widen';
       config.currentValue = '^1.0.0 || ^2.0.0';
       config.depName = 'webpack';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock
         .scope('https://registry.npmjs.org')
         .get('/webpack')
@@ -607,7 +607,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'widen';
       config.currentValue = '1.x - 2.x';
       config.depName = 'webpack';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock
         .scope('https://registry.npmjs.org')
         .get('/webpack')
@@ -620,7 +620,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'widen';
       config.currentValue = '1.x || 2.x';
       config.depName = 'webpack';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock
         .scope('https://registry.npmjs.org')
         .get('/webpack')
@@ -633,7 +633,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'widen';
       config.currentValue = '1 || 2';
       config.depName = 'webpack';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock
         .scope('https://registry.npmjs.org')
         .get('/webpack')
@@ -646,7 +646,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'widen';
       config.currentValue = '~1.2.0 || ~1.3.0';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '~1.2.0 || ~1.3.0 || ~1.4.0', updateType: 'minor' },
@@ -656,7 +656,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'replace';
       config.currentValue = '>= 0.7.0';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toHaveLength(0);
     });
@@ -664,7 +664,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'replace';
       config.currentValue = '<= 0.7.2';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '<= 0.9.7', updateType: 'minor' },
@@ -675,7 +675,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'replace';
       config.currentValue = '< 0.7.2';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '< 0.9.8', updateType: 'minor' },
@@ -686,7 +686,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'replace';
       config.currentValue = '< 1';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '< 2', updateType: 'major' },
@@ -696,7 +696,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'replace';
       config.currentValue = '<= 1.3';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '<= 1.4', updateType: 'minor' },
@@ -706,7 +706,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'replace';
       config.currentValue = '=1.3.1';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '=1.4.1', updateType: 'minor' },
@@ -717,7 +717,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.respectLatest = false;
       config.currentValue = '<= 1';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '<= 2', updateType: 'major' },
@@ -727,7 +727,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'replace';
       config.currentValue = '<= 1.0.0';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       const res = await lookup.lookupUpdates(config);
       expect(res.updates).toMatchSnapshot();
@@ -737,7 +737,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'replace';
       config.currentValue = '< 1.0.0';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       const res = await lookup.lookupUpdates(config);
       expect(res.updates).toMatchSnapshot();
@@ -747,7 +747,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'widen';
       config.currentValue = '>= 0.5.0 < 1.0.0';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       const res = await lookup.lookupUpdates(config);
       expect(res.updates).toMatchSnapshot();
@@ -757,7 +757,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'widen';
       config.currentValue = '>= 0.5.0 <0.8';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       const res = await lookup.lookupUpdates(config);
       expect(res.updates).toMatchSnapshot();
@@ -768,7 +768,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'widen';
       config.currentValue = '>= 0.5.0 <= 0.8.0';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       const res = await lookup.lookupUpdates(config);
       expect(res.updates).toMatchSnapshot();
@@ -779,7 +779,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'widen';
       config.currentValue = '<= 0.8.0 >= 0.5.0';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       const res = await lookup.lookupUpdates(config);
       expect(res.updates).toMatchSnapshot([]);
@@ -788,7 +788,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.respectLatest = false;
       config.currentValue = '1.4.1';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '2.0.3', updateType: 'major' },
@@ -797,7 +797,7 @@ describe('workers/repository/process/lookup/index', () => {
     it('should ignore unstable versions if the current version is stable', async () => {
       config.currentValue = '2.5.16';
       config.depName = 'vue';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock
         .scope('https://registry.npmjs.org')
         .get('/vue')
@@ -874,7 +874,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.ignoreUnstable = false;
       config.respectLatest = false;
       config.depName = 'vue';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock
         .scope('https://registry.npmjs.org')
         .get('/vue')
@@ -887,7 +887,7 @@ describe('workers/repository/process/lookup/index', () => {
     it('should allow unstable versions if the current version is unstable', async () => {
       config.currentValue = '3.1.0-dev.20180731';
       config.depName = 'typescript';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock
         .scope('https://registry.npmjs.org')
         .get('/typescript')
@@ -900,7 +900,7 @@ describe('workers/repository/process/lookup/index', () => {
     it('should not jump unstable versions', async () => {
       config.currentValue = '3.0.1-insiders.20180726';
       config.depName = 'typescript';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock
         .scope('https://registry.npmjs.org')
         .get('/typescript')
@@ -915,7 +915,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '0.0.34';
       config.updatePinnedDependencies = true;
       config.depName = '@types/helmet';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock
         .scope('https://registry.npmjs.org')
         .get('/@types%2Fhelmet')
@@ -930,7 +930,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '0.0.34';
       config.updatePinnedDependencies = false;
       config.depName = '@types/helmet';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock
         .scope('https://registry.npmjs.org')
         .get('/@types%2Fhelmet')
@@ -942,7 +942,7 @@ describe('workers/repository/process/lookup/index', () => {
     it('should follow dist-tag even if newer version exists', async () => {
       config.currentValue = '3.0.1-insiders.20180713';
       config.depName = 'typescript';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       config.followTag = 'insiders';
       httpMock
         .scope('https://registry.npmjs.org')
@@ -956,7 +956,7 @@ describe('workers/repository/process/lookup/index', () => {
     it('should roll back to dist-tag if current version is higher', async () => {
       config.currentValue = '3.1.0-dev.20180813';
       config.depName = 'typescript';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       config.followTag = 'insiders';
       config.rollbackPrs = true;
       httpMock
@@ -971,7 +971,7 @@ describe('workers/repository/process/lookup/index', () => {
     it('should jump unstable versions if followTag', async () => {
       config.currentValue = '3.0.0-insiders.20180706';
       config.depName = 'typescript';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       config.followTag = 'insiders';
       httpMock
         .scope('https://registry.npmjs.org')
@@ -985,7 +985,7 @@ describe('workers/repository/process/lookup/index', () => {
     it('should update nothing if current version is dist-tag', async () => {
       config.currentValue = '3.0.1-insiders.20180726';
       config.depName = 'typescript';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       config.followTag = 'insiders';
       httpMock
         .scope('https://registry.npmjs.org')
@@ -997,7 +997,7 @@ describe('workers/repository/process/lookup/index', () => {
     it('should warn if no version matches dist-tag', async () => {
       config.currentValue = '3.0.1-dev.20180726';
       config.depName = 'typescript';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       config.followTag = 'foo';
       httpMock
         .scope('https://registry.npmjs.org')
@@ -1015,7 +1015,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'replace';
       config.currentValue = '~0.0.34';
       config.depName = '@types/helmet';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock
         .scope('https://registry.npmjs.org')
         .get('/@types%2Fhelmet')
@@ -1026,7 +1026,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'replace';
       config.currentValue = '^0.0.34';
       config.depName = '@types/helmet';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock
         .scope('https://registry.npmjs.org')
         .get('/@types%2Fhelmet')
@@ -1038,7 +1038,7 @@ describe('workers/repository/process/lookup/index', () => {
     it('should downgrade from missing versions', async () => {
       config.currentValue = '1.16.1';
       config.depName = 'coffeelint';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       config.rollbackPrs = true;
       httpMock
         .scope('https://registry.npmjs.org')
@@ -1051,7 +1051,7 @@ describe('workers/repository/process/lookup/index', () => {
     it('should upgrade to only one major', async () => {
       config.currentValue = '1.0.0';
       config.depName = 'webpack';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock
         .scope('https://registry.npmjs.org')
         .get('/webpack')
@@ -1063,7 +1063,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '1.0.0';
       config.separateMultipleMajor = true;
       config.depName = 'webpack';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock
         .scope('https://registry.npmjs.org')
         .get('/webpack')
@@ -1075,7 +1075,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '^4.4.0-canary.3';
       config.rangeStrategy = 'replace';
       config.depName = 'next';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock
         .scope('https://registry.npmjs.org')
         .get('/next')
@@ -1087,7 +1087,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'bump';
       config.currentValue = '^1.0.0';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '^1.4.1', updateType: 'minor' },
@@ -1098,7 +1098,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '~1.0.0';
       config.depName = 'q';
       config.separateMinorPatch = true;
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '~1.0.1', updateType: 'patch' },
@@ -1110,7 +1110,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '~1.0.0';
       config.depName = 'q';
       config.separateMinorPatch = true;
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '~1.0.1', updateType: 'patch' },
@@ -1121,7 +1121,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'bump';
       config.currentValue = '>=1.0.0';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '>=1.4.1', updateType: 'minor' },
@@ -1131,7 +1131,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'bump';
       config.currentValue = '>=0.9.0';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       config.separateMajorMinor = false;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
@@ -1142,7 +1142,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'bump';
       config.currentValue = '>1.0.0';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([]);
     });
@@ -1150,7 +1150,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'bump';
       config.currentValue = '1.x';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([]);
     });
@@ -1158,13 +1158,13 @@ describe('workers/repository/process/lookup/index', () => {
       config.rangeStrategy = 'bump';
       config.currentValue = '^0.9.0 || ^1.0.0';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([]);
     });
     it('replaces non-range in-range updates', async () => {
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       config.packageFile = 'package.json';
       config.rangeStrategy = 'bump';
       config.currentValue = '1.0.0';
@@ -1221,7 +1221,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.currentValue = '~=0.9';
       config.depName = 'q';
       // TODO: we are using npm as source to test pep440 (#9721)
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       const res = await lookup.lookupUpdates(config);
       expect(res.updates).toMatchSnapshot([
@@ -1233,7 +1233,7 @@ describe('workers/repository/process/lookup/index', () => {
     it('returns complex object', async () => {
       config.currentValue = '1.3.0';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       const res = await lookup.lookupUpdates(config);
       expect(res).toMatchSnapshot();
@@ -1242,7 +1242,7 @@ describe('workers/repository/process/lookup/index', () => {
     it('ignores deprecated', async () => {
       config.currentValue = '1.3.0';
       config.depName = 'q2';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       const returnJson = JSON.parse(JSON.stringify(qJson));
       returnJson.name = 'q2';
       returnJson.versions['1.4.1'].deprecated = 'true';
@@ -1257,7 +1257,7 @@ describe('workers/repository/process/lookup/index', () => {
     it('is deprecated', async () => {
       config.currentValue = '1.3.0';
       config.depName = 'q3';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       const returnJson = {
         ...JSON.parse(JSON.stringify(qJson)),
         name: 'q3',
@@ -1580,7 +1580,7 @@ describe('workers/repository/process/lookup/index', () => {
     it('handles sourceUrl packageRules with version restrictions', async () => {
       config.currentValue = '0.9.99';
       config.depName = 'q';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       config.packageRules = [
         {
           matchSourceUrlPrefixes: ['https://github.com/kriskowal/q'],
@@ -1601,7 +1601,7 @@ describe('workers/repository/process/lookup/index', () => {
       // This config is normally set when packageRules are applied
       config.replacementName = 'r';
       config.replacementVersion = '2.0.0';
-      config.datasource = datasourceNpmId;
+      config.datasource = NpmDatasource.id;
       httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
       const res = await lookup.lookupUpdates(config);
       expect(res).toMatchSnapshot();
diff --git a/lib/workers/repository/updates/generate.spec.ts b/lib/workers/repository/updates/generate.spec.ts
index c621fdf70909f2e4f0698d269e8e9aa3fcd5af36..e0bed221d578642867132bdeae1990179b69ea60 100644
--- a/lib/workers/repository/updates/generate.spec.ts
+++ b/lib/workers/repository/updates/generate.spec.ts
@@ -1,6 +1,6 @@
 import { defaultConfig, partial } from '../../../../test/util';
 import type { UpdateType } from '../../../config/types';
-import * as datasourceNpm from '../../../datasource/npm';
+import { NpmDatasource } from '../../../datasource/npm';
 import type { BranchUpgradeConfig } from '../../types';
 import { generateBranchConfig } from './generate';
 
@@ -502,7 +502,7 @@ describe('workers/repository/updates/generate', () => {
       const branch: BranchUpgradeConfig[] = [
         {
           commitBodyTable: true,
-          datasource: datasourceNpm.id,
+          datasource: NpmDatasource.id,
           depName: '@types/some-dep',
           groupName: null,
           branchName: 'some-branch',
@@ -515,7 +515,7 @@ describe('workers/repository/updates/generate', () => {
         },
         {
           commitBodyTable: true,
-          datasource: datasourceNpm.id,
+          datasource: NpmDatasource.id,
           depName: 'some-dep',
           groupName: null,
           branchName: 'some-branch',
@@ -525,7 +525,7 @@ describe('workers/repository/updates/generate', () => {
         },
         {
           commitBodyTable: true,
-          datasource: datasourceNpm.id,
+          datasource: NpmDatasource.id,
           depName: 'some-dep',
           groupName: null,
           branchName: 'some-branch',
@@ -570,7 +570,7 @@ describe('workers/repository/updates/generate', () => {
         },
         {
           commitBodyTable: true,
-          datasource: datasourceNpm.id,
+          datasource: NpmDatasource.id,
           depName: 'some-dep',
           groupName: null,
           branchName: 'some-branch',