diff --git a/lib/manager/npm/extract/index.js b/lib/manager/npm/extract/index.js index 282c2a3d597782ad7dcaaaf9171175fc04a8788e..aab145b4d0d1f90f9315d6c5b1cb04d81ff2b063 100644 --- a/lib/manager/npm/extract/index.js +++ b/lib/manager/npm/extract/index.js @@ -3,6 +3,7 @@ const upath = require('upath'); const { getLockedVersions } = require('./locked-versions'); const { detectMonorepos } = require('./monorepo'); const { mightBeABrowserLibrary } = require('./type'); +const semver = require('../../../versioning/semver'); module.exports = { extractDependencies, @@ -103,11 +104,32 @@ async function extractDependencies(content, packageFile, config) { if (packageJson[depType]) { try { for (const [depName, version] of Object.entries(packageJson[depType])) { - deps.push({ - depName, + const dep = { depType, - currentVersion: version.trim().replace(/^=/, ''), - }); + depName, + }; + // TODO: do we need to remove the equals? + dep.currentVersion = version.trim().replace(/^=/, ''); + if (dep.currentVersion.startsWith('file:')) { + dep.skipReason = 'file'; + } else if (depType === 'engines' && depName === 'node') { + dep.purl = 'pkg:github/nodejs/node'; + } else if (depType === 'engines' && depName === 'yarn') { + dep.purl = 'pkg:github/yarnpkg/yarn'; + dep.commitMessageTopic = 'Yarn'; + } else if (semver.isValid(dep.currentVersion)) { + dep.purl = `pkg:npm/${depName.replace('@', '%40')}`; + if (dep.currentVersion === '*') { + dep.skipReason = 'any-version'; + } + } else { + dep.skipReason = 'unknown-version'; + } + if (depName === 'node') { + // This is a special case for Node.js to group it together with other managers + dep.commitMessageTopic = 'Node.js'; + } + deps.push(dep); } } catch (err) /* istanbul ignore next */ { logger.info( diff --git a/test/manager/npm/extract/__snapshots__/index.spec.js.snap b/test/manager/npm/extract/__snapshots__/index.spec.js.snap index df1a385500548f5108d0586a1d74ed04387b72fa..b933fbf398268c922f8c320c661f2679b7214f3e 100644 --- a/test/manager/npm/extract/__snapshots__/index.spec.js.snap +++ b/test/manager/npm/extract/__snapshots__/index.spec.js.snap @@ -1,5 +1,69 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`manager/npm/extract .extractDependencies() extracts engines 1`] = ` +Object { + "deps": Array [ + Object { + "currentVersion": "1.6.0", + "depName": "angular", + "depType": "dependencies", + "purl": "pkg:npm/angular", + }, + Object { + "currentVersion": "1.6.0", + "depName": "@angular/cli", + "depType": "devDependencies", + "purl": "pkg:npm/%40angular/cli", + }, + Object { + "currentVersion": "*", + "depName": "foo", + "depType": "devDependencies", + "purl": "pkg:npm/foo", + "skipReason": "any-version", + }, + Object { + "currentVersion": "file:../foo/bar", + "depName": "bar", + "depType": "devDependencies", + "skipReason": "file", + }, + Object { + "currentVersion": "latest", + "depName": "other", + "depType": "devDependencies", + "skipReason": "unknown-version", + }, + Object { + "commitMessageTopic": "Node.js", + "currentVersion": ">= 8.9.2", + "depName": "node", + "depType": "engines", + "purl": "pkg:github/nodejs/node", + }, + Object { + "commitMessageTopic": "Yarn", + "currentVersion": "1.7.0", + "depName": "yarn", + "depType": "engines", + "purl": "pkg:github/yarnpkg/yarn", + }, + ], + "lernaClient": undefined, + "lernaDir": undefined, + "lernaPackages": undefined, + "npmLock": undefined, + "npmrc": undefined, + "packageJsonName": undefined, + "packageJsonType": "app", + "packageJsonVersion": undefined, + "pnpmShrinkwrap": undefined, + "yarnLock": undefined, + "yarnWorkspacesPackages": undefined, + "yarnrc": undefined, +} +`; + exports[`manager/npm/extract .extractDependencies() finds a lock file 1`] = ` Object { "deps": Array [ @@ -7,51 +71,61 @@ Object { "currentVersion": "6.5.0", "depName": "autoprefixer", "depType": "dependencies", + "purl": "pkg:npm/autoprefixer", }, Object { "currentVersion": "~1.6.0", "depName": "bower", "depType": "dependencies", + "purl": "pkg:npm/bower", }, Object { "currentVersion": "13.1.0", "depName": "browserify", "depType": "dependencies", + "purl": "pkg:npm/browserify", }, Object { "currentVersion": "0.9.2", "depName": "browserify-css", "depType": "dependencies", + "purl": "pkg:npm/browserify-css", }, Object { "currentVersion": "0.22.0", "depName": "cheerio", "depType": "dependencies", + "purl": "pkg:npm/cheerio", }, Object { "currentVersion": "1.21.0", "depName": "config", "depType": "dependencies", + "purl": "pkg:npm/config", }, Object { "currentVersion": "^1.5.8", "depName": "angular", "depType": "devDependencies", + "purl": "pkg:npm/angular", }, Object { "currentVersion": "1.5.8", "depName": "angular-touch", "depType": "devDependencies", + "purl": "pkg:npm/angular-touch", }, Object { "currentVersion": "1.5.8", "depName": "angular-sanitize", "depType": "devDependencies", + "purl": "pkg:npm/angular-sanitize", }, Object { "currentVersion": "4.0.0-beta.1", "depName": "@angular/core", "depType": "devDependencies", + "purl": "pkg:npm/%40angular/core", }, ], "lernaClient": undefined, @@ -96,51 +170,61 @@ Object { "currentVersion": "6.5.0", "depName": "autoprefixer", "depType": "dependencies", + "purl": "pkg:npm/autoprefixer", }, Object { "currentVersion": "~1.6.0", "depName": "bower", "depType": "dependencies", + "purl": "pkg:npm/bower", }, Object { "currentVersion": "13.1.0", "depName": "browserify", "depType": "dependencies", + "purl": "pkg:npm/browserify", }, Object { "currentVersion": "0.9.2", "depName": "browserify-css", "depType": "dependencies", + "purl": "pkg:npm/browserify-css", }, Object { "currentVersion": "0.22.0", "depName": "cheerio", "depType": "dependencies", + "purl": "pkg:npm/cheerio", }, Object { "currentVersion": "1.21.0", "depName": "config", "depType": "dependencies", + "purl": "pkg:npm/config", }, Object { "currentVersion": "^1.5.8", "depName": "angular", "depType": "devDependencies", + "purl": "pkg:npm/angular", }, Object { "currentVersion": "1.5.8", "depName": "angular-touch", "depType": "devDependencies", + "purl": "pkg:npm/angular-touch", }, Object { "currentVersion": "1.5.8", "depName": "angular-sanitize", "depType": "devDependencies", + "purl": "pkg:npm/angular-sanitize", }, Object { "currentVersion": "4.0.0-beta.1", "depName": "@angular/core", "depType": "devDependencies", + "purl": "pkg:npm/%40angular/core", }, ], "lernaClient": "npm", @@ -165,51 +249,61 @@ Object { "currentVersion": "6.5.0", "depName": "autoprefixer", "depType": "dependencies", + "purl": "pkg:npm/autoprefixer", }, Object { "currentVersion": "~1.6.0", "depName": "bower", "depType": "dependencies", + "purl": "pkg:npm/bower", }, Object { "currentVersion": "13.1.0", "depName": "browserify", "depType": "dependencies", + "purl": "pkg:npm/browserify", }, Object { "currentVersion": "0.9.2", "depName": "browserify-css", "depType": "dependencies", + "purl": "pkg:npm/browserify-css", }, Object { "currentVersion": "0.22.0", "depName": "cheerio", "depType": "dependencies", + "purl": "pkg:npm/cheerio", }, Object { "currentVersion": "1.21.0", "depName": "config", "depType": "dependencies", + "purl": "pkg:npm/config", }, Object { "currentVersion": "^1.5.8", "depName": "angular", "depType": "devDependencies", + "purl": "pkg:npm/angular", }, Object { "currentVersion": "1.5.8", "depName": "angular-touch", "depType": "devDependencies", + "purl": "pkg:npm/angular-touch", }, Object { "currentVersion": "1.5.8", "depName": "angular-sanitize", "depType": "devDependencies", + "purl": "pkg:npm/angular-sanitize", }, Object { "currentVersion": "4.0.0-beta.1", "depName": "@angular/core", "depType": "devDependencies", + "purl": "pkg:npm/%40angular/core", }, ], "lernaClient": undefined, diff --git a/test/manager/npm/extract/index.spec.js b/test/manager/npm/extract/index.spec.js index ef64b4de592ccd5fbfd88f438fa209e52fea1f15..f86f496b24cfe2965a414ad5f247b36011fb9cf9 100644 --- a/test/manager/npm/extract/index.spec.js +++ b/test/manager/npm/extract/index.spec.js @@ -119,6 +119,30 @@ describe('manager/npm/extract', () => { ); expect(res).toMatchSnapshot(); }); + it('extracts engines', async () => { + const pJson = { + dependencies: { + angular: '1.6.0', + }, + devDependencies: { + '@angular/cli': '1.6.0', + foo: '*', + bar: 'file:../foo/bar', + other: 'latest', + }, + engines: { + node: '>= 8.9.2', + yarn: '1.7.0', + }, + }; + const pJsonStr = JSON.stringify(pJson); + const res = await npmExtract.extractDependencies( + pJsonStr, + 'package.json', + {} + ); + expect(res).toMatchSnapshot(); + }); }); describe('.postExtract()', () => { it('runs', async () => {