Skip to content

Commit

Permalink
Add support for npm aliases. Fixes #633.
Browse files Browse the repository at this point in the history
  • Loading branch information
raineorshine committed Aug 10, 2020
1 parent 97a1165 commit 0f6f35c
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 9 deletions.
23 changes: 23 additions & 0 deletions lib/version-util.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,27 @@ function isPre(version) {
return getPrecision(version) === 'release'
}

/**
* Constructs an npm alias from the name and version of the actual package.
*
* @param name Name of the actual package.
* @param version Version of the actual package.
* @returns "npm:package@x.y.z"
* @example createNpmAlias('chalk', '2.0.0') -> 'npm:chalk@2.0.0'
*/
const createNpmAlias = (name, version) => `npm:${name}@${version}`

/**
* Parses an npm alias into a [name, version] 2-tuple.
*
* @returns [name, version] or null if the input is not an npm alias
* @example 'npm:chalk@1.0.0' -> ['chalk', '1.0.0']
*/
const parseNpmAlias = alias => {
const match = alias && alias.match(/^npm:(.*)@(.*)/)
return match && match.slice(1)
}

module.exports = {
compareVersions,
numParts,
Expand All @@ -243,6 +264,8 @@ module.exports = {
isWildPart,
colorizeDiff,
findGreatestByLevel,
createNpmAlias,
parseNpmAlias,
VERSION_BASE_PARTS,
VERSION_ADDED_PARTS,
VERSION_PARTS,
Expand Down
41 changes: 32 additions & 9 deletions lib/versionmanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ function and(f, g) {
}

/**
* Upgrade an existing dependency declaration to satisfy the latest version
* Upgrade an existing dependency declaration to satisfy the latest version. Handles npm aliases.
*
* @param declaration Current version declaration (e.g. "1.2.x")
* @param latestVersion Latest version (e.g "1.3.2")
Expand All @@ -48,6 +48,14 @@ function and(f, g) {
function upgradeDependencyDeclaration(declaration, latestVersion, options = {}) {
options.wildcard = options.wildcard || DEFAULT_WILDCARD

// parse npm alias
const npmAliasCurrent = versionUtil.parseNpmAlias(declaration)
const npmAliasLatest = versionUtil.parseNpmAlias(latestVersion)
if (npmAliasCurrent && npmAliasLatest) {
declaration = npmAliasCurrent[1]
latestVersion = npmAliasLatest[1]
}

// parse the latestVersion
// return original declaration if latestSemver is invalid
const [latestSemver] = semverutils.parseRange(latestVersion)
Expand Down Expand Up @@ -107,13 +115,16 @@ function upgradeDependencyDeclaration(declaration, latestVersion, options = {})

// convert versions with </<= or mixed operators into the preferred wildcard
// only do so if the new version does not already contain a wildcard
return !hasWildCard && (isLessThan || isMixed) ?
const versionWithWildcard = !hasWildCard && (isLessThan || isMixed) ?
versionUtil.addWildCard(version, options.wildcard) :
operator + version

// convert back to npm alias
return npmAliasCurrent ? versionUtil.createNpmAlias(npmAliasCurrent[0], versionWithWildcard) : versionWithWildcard
}

/**
* Upgrade a dependencies collection based on latest available versions
* Upgrade a dependencies collection based on latest available versions. Supports npm aliases.
*
* @param currentDependencies current dependencies collection object
* @param latestVersions latest available versions collection object
Expand Down Expand Up @@ -161,14 +172,22 @@ function upgradeDependencies(currentDependencies, latestVersions, options = {})
const isSatisfied = semver.satisfies

/**
* Satisfy the latest, and it is not beyond the latest).
* Satisfy the latest, and it is not beyond the latest). Handles npm aliases.
*
* @param current
* @param latest
* @returns
*/
function isUpgradeable(current, latest) {

// parse npm alias
const npmAliasCurrent = versionUtil.parseNpmAlias(current)
const npmAliasLatest = versionUtil.parseNpmAlias(latest)
if (npmAliasCurrent && npmAliasLatest) {
current = npmAliasCurrent[1]
latest = npmAliasLatest[1]
}

// do not upgrade non-npm version declarations (such as git tags)
// do not upgrade versionUtil.wildcards
if (!semver.validRange(current) || versionUtil.isWildCard(current)) {
Expand Down Expand Up @@ -394,7 +413,7 @@ function getInstalledPackages(options = {}) {
/**
* Get the latest or greatest versions from the NPM repository based on the version target.
*
* @param packageMap An object whose keys are package name and values are current versions
* @param packageMap An object whose keys are package name and values are current versions. May include npm aliases, i.e. { "package": "npm:other-package@1.0.0" }
* @param [options={}] Options. Default: { versionTarget: 'latest' }. You may also specify { versionTarget: 'greatest' }
* @returns Promised {packageName: version} collection
*/
Expand Down Expand Up @@ -449,10 +468,14 @@ function queryVersions(packageMap, options = {}) {
* @returns
*/
function getPackageVersionProtected(dep) {
return getPackageVersion(dep, packageMap[dep], {

const npmAlias = versionUtil.parseNpmAlias(packageMap[dep])
const [name, version] = npmAlias || [dep, packageMap[dep]]

return getPackageVersion(name, version, {
...options,
// upgrade prereleases to newer prereleases by default
pre: options.pre != null ? options.pre : versionUtil.isPre(packageMap[dep])
pre: options.pre != null ? options.pre : versionUtil.isPre(version)
}).catch(err => {
const errorMessage = err ? (err.message || err).toString() : ''
if (errorMessage.match(/E404|ENOTFOUND|404 Not Found/i)) {
Expand All @@ -467,11 +490,11 @@ function queryVersions(packageMap, options = {}) {

throw err
}
}).then(result => {
}).then(versionNew => {
if (bar) {
bar.tick()
}
return result
return npmAlias ? versionUtil.createNpmAlias(name, versionNew) : versionNew
})
}

Expand Down
15 changes: 15 additions & 0 deletions test/test-ncu.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ describe('npm-check-updates', function () {
}

describe('run', () => {

it('should return promised jsonUpgraded', () => {
return ncu.run({
packageData: fs.readFileSync(`${__dirname}/ncu/package.json`, 'utf-8')
Expand Down Expand Up @@ -381,6 +382,20 @@ describe('npm-check-updates', function () {
})
})

it('should upgrade npm alias', () => {
const upgraded = ncu.run({
packageData: '{ "dependencies": { "request": "npm:postman-request@^2.88.1-postman.16" } }',
jsonUpgraded: true
})

return Promise.all([
upgraded.should.eventually.have.property('request'),
upgraded.then(data => {
return data.should.eql({ request: 'npm:postman-request@^2.88.1-postman.24' })
})
])
})

})

describe('cli', () => {
Expand Down
20 changes: 20 additions & 0 deletions test/test-version-util.js
Original file line number Diff line number Diff line change
Expand Up @@ -300,4 +300,24 @@ describe('version-util', () => {

})

describe('createNpmAlias', () => {

it('should create an npm alias from a name and version', () => {
versionUtil.createNpmAlias('chalk', '1.0.0').should.equal('npm:chalk@1.0.0')
})

})

describe('parseNpmAlias', () => {

it('should parse an npm alias into [name, version]', () => {
versionUtil.parseNpmAlias('npm:chalk@1.0.0').should.eql(['chalk', '1.0.0'])
})

it('should return null if given a non-alias', () => {
should.equal(versionUtil.parseNpmAlias('1.0.0'), null)
})

})

})
35 changes: 35 additions & 0 deletions test/test-versionmanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ describe('versionmanager', () => {
vm.upgradeDependencyDeclaration('^1.0.0', '1.0.1', { removeRange: true }).should.equal('1.0.1')
vm.upgradeDependencyDeclaration('2.2.*', '3.1.1', { removeRange: true }).should.equal('3.1.1')
})

it('npm alias', () => {
vm.upgradeDependencyDeclaration('npm:chalk@^1.0.0', 'npm:chalk@2.0.0').should.equal('npm:chalk@^2.0.0')
})

})

describe('upgradePackageData', () => {
Expand Down Expand Up @@ -163,6 +168,23 @@ describe('versionmanager', () => {
}
})
})

it('should upgrade npm aliases', async () => {

const oldDependencies = { request: 'postman-request@^2.88.1-postman.16' }
const newDependencies = { request: 'postman-request@^2.88.1-postman.24' }
const newVersions = { request: 'postman-request@2.88.1-postman.24' }
const oldPkgData = JSON.stringify({ dependencies: oldDependencies })

const { newPkgData } = await vm.upgradePackageData(oldPkgData, oldDependencies, newDependencies, newVersions)

JSON.parse(newPkgData)
.should.eql({
dependencies: {
request: 'postman-request@^2.88.1-postman.24'
}
})
})
})

describe('getCurrentDependencies', () => {
Expand Down Expand Up @@ -453,6 +475,15 @@ describe('versionmanager', () => {
return a.should.be.rejected
})

it('npm aliases should upgrade the installed package', () => {
return vm.queryVersions({
request: 'npm:postman-request@2.88.1-postman.16'
}, { loglevel: 'silent' })
.should.eventually.deep.equal({
request: 'npm:postman-request@2.88.1-postman.24'
})
})

})

describe('isUpgradeable', () => {
Expand Down Expand Up @@ -482,6 +513,10 @@ describe('versionmanager', () => {
vm.isUpgradeable('<7', '7.2.0').should.equal(true)
})

it('should upgrade npm aliases', () => {
vm.isUpgradeable('npm:chalk@1.0.0', 'npm:chalk@2.0.0').should.equal(true)
})

})

describe('getPreferredWildcard', () => {
Expand Down

0 comments on commit 0f6f35c

Please sign in to comment.