diff --git a/lib/resolve/npm.js b/lib/resolve/npm.js index 45558816af..29b3fe9640 100644 --- a/lib/resolve/npm.js +++ b/lib/resolve/npm.js @@ -3,6 +3,7 @@ var url = require('url') var enc = global.encodeURIComponent var got = require('../got') var registryUrl = require('registry-url') +var semver = require('semver') /** * Resolves a package in the NPM registry. Done as part of `install()`. @@ -22,29 +23,65 @@ module.exports = function resolveNpm (pkg, log) { // { raw: 'rimraf@2', scope: null, name: 'rimraf', rawSpec: '2' || '' } return Promise.resolve() .then(_ => toUri(pkg)) - .then(url => { - return got.get(url).then(res => { - if (log) log('resolving') - return res.promise - }) - }) + .then(url => got.get(url).then(res => { + if (log) log('resolving') + return res.promise + })) .then(res => JSON.parse(res.body)) - .then(res => { - return { - name: res.name, - fullname: '' + res.name.replace('/', '!') + '@' + res.version, - version: res.version, // used for displaying - dist: res.dist - } - }) - .catch(errify) + .catch(err => err.statusCode === 404 + ? getAllVersionsAndMatchOnClient(pkg) + : errify(err, pkg) + ) + .then(res => ({ + name: res.name, + fullname: '' + res.name.replace('/', '!') + '@' + res.version, + version: res.version, // used for displaying + dist: res.dist + })) +} - function errify (err) { - if (err.statusCode === 404) { - throw new Error("Module '" + pkg.raw + "' not found") - } - throw err +function errify (err, pkg) { + if (err.statusCode === 404) { + throw new Error("Module '" + pkg.raw + "' not found") } + throw err +} + +function getAllVersionsAndMatchOnClient (pkg) { + return Promise.resolve() + .then(_ => url.resolve(registryUrl(pkg.scope), pkg.name)) + .then(url => got.get(url).then(res => res.promise)) + .then(res => JSON.parse(res.body)) + .then(res => pickVersionFromRegistryDocument(res, pkg)) + .catch(err => errify(err, pkg)) +} + +function pickVersionFromRegistryDocument (pkg, dep) { + var versions = Object.keys(pkg.versions) + if (dep.tag === 'tag' && dep.spec === 'latest') { + var sortedVersions = versions.sort(semver.rcompare) + return pkg(sortedVersions[0]) + } + + if (dep.type === 'tag' && dep.spec !== 'latest') { + var tagVersion = pkg['dist-tags'][dep.spec] + if (pkg.versions[tagVersion]) { + return pkg.versions[tagVersion] + } + } else { + var spec = scopeWorkarounds(dep) + var maxSatisfyingVersion = semver.maxSatisfying(versions, spec) + if (maxSatisfyingVersion) { + return pkg.versions[maxSatisfyingVersion] + } + } + + var message = versions.length + ? 'Versions in registry:\n' + versions.join(', ') + '\n' + : 'No valid version found.' + var er = new Error('No compatible version found: ' + + dep.raw + '\n' + message) + throw er } /**