Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Target as a function #1052

Merged
merged 10 commits into from
Feb 28, 2022
22 changes: 12 additions & 10 deletions src/lib/queryVersions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import cint from 'cint'
import chalk from 'chalk'
import pMap from 'p-map'
import ProgressBar from 'progress'
import { parseRange } from 'semver-utils'
import { supportedVersionTargets } from '../constants'
import getPackageManager from './getPackageManager'
import packageManagers from '../package-managers'
Expand All @@ -28,14 +29,6 @@ async function queryVersions(packageMap: Index<VersionSpec>, options: Options =
bar.render()
}

// set the getPackageVersion function from options.target
// TODO: Remove "as GetVersion" and fix types
const getPackageVersion = packageManager[target as keyof typeof packageManager] as GetVersion
if (!getPackageVersion) {
const packageManagerSupportedVersionTargets = supportedVersionTargets.filter(t => t in packageManager)
return Promise.reject(new Error(`Unsupported target "${target}" for ${options.packageManager || 'npm'}. Supported version targets are: ${packageManagerSupportedVersionTargets.join(', ')}`))
}

/**
* Ignore 404 errors from getPackageVersion by having them return `null`
* instead of rejecting.
Expand All @@ -47,6 +40,7 @@ async function queryVersions(packageMap: Index<VersionSpec>, options: Options =

const npmAlias = parseNpmAlias(packageMap[dep])
const [name, version] = npmAlias || [dep, packageMap[dep]]
const targetResult = typeof target === 'string' ? target : target(name, parseRange(version))

let versionNew: Version | null = null

Expand All @@ -55,11 +49,11 @@ async function queryVersions(packageMap: Index<VersionSpec>, options: Options =

// override packageManager and getPackageVersion just for this dependency
const packageManager = packageManagers.gitTags
const getPackageVersion = packageManager[target as keyof typeof packageManager] as GetVersion
const getPackageVersion = packageManager[targetResult as keyof typeof packageManager] as GetVersion

if (!getPackageVersion) {
const packageManagerSupportedVersionTargets = supportedVersionTargets.filter(t => t in packageManager)
return Promise.reject(new Error(`Unsupported target "${target}" for github urls. Supported version targets are: ${packageManagerSupportedVersionTargets.join(', ')}`))
return Promise.reject(new Error(`Unsupported target "${targetResult}" for github urls. Supported version targets are: ${packageManagerSupportedVersionTargets.join(', ')}`))
}
versionNew = await getPackageVersion(name, version, {
...options,
Expand All @@ -68,6 +62,14 @@ async function queryVersions(packageMap: Index<VersionSpec>, options: Options =
})
}
else {
// set the getPackageVersion function from options.target
// TODO: Remove "as GetVersion" and fix types
const getPackageVersion = packageManager[targetResult as keyof typeof packageManager] as GetVersion
if (!getPackageVersion) {
const packageManagerSupportedVersionTargets = supportedVersionTargets.filter(t => t in packageManager)
return Promise.reject(new Error(`Unsupported target "${targetResult}" for ${options.packageManager || 'npm'}. Supported version targets are: ${packageManagerSupportedVersionTargets.join(', ')}`))
}

try {
versionNew = await getPackageVersion(name, version, {
...options,
Expand Down
9 changes: 6 additions & 3 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@ export type Version = string
export type VersionSpec = string
export type VersionLevel = 'major' | 'minor' | 'patch'

type FilterFunction = (packageName: string, version: SemVer[]) => boolean
export type FilterFunction = (packageName: string, versionRange: SemVer[]) => boolean
export type FilterRejectPattern = string | string[] | RegExp | RegExp[] | FilterFunction

export type TargetFunction = (packageName: string, versionRange: SemVer[]) => string
export type Target = string | TargetFunction

export interface Packument {
name: string,
deprecated?: boolean,
Expand Down Expand Up @@ -287,9 +290,9 @@ export interface RunOptions {
silent?: boolean,

/**
* Target version to upgrade to: latest, newest, greatest, minor, patch. Run "ncu --help --target" for details.` (default: "latest")
* Target function that returns version to upgrade to: latest, newest, greatest, minor, patch. Run "ncu --help --target" for details.` (default: "latest")
*/
target?: string,
target?: Target,

/**
* Global timeout in milliseconds. (default: no global timeout and 30 seconds per npm-registry-fetch).
Expand Down
52 changes: 51 additions & 1 deletion test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import chai from 'chai'
import chaiAsPromised from 'chai-as-promised'
import chaiString from 'chai-string'
import * as ncu from '../src/'
import { FilterFunction, TargetFunction } from '../src/types'

chai.should()
chai.use(chaiAsPromised)
Expand Down Expand Up @@ -370,11 +371,60 @@ describe('run', function () {
;(pkgData as any).chalk.should.equal('2.4.2')
})

it('skip non-semver versions with --target', async () => {
it('skip non-semver versions with --target patch', async () => {
const pkgData = await ncu.run({ target: 'patch', packageData: '{ "dependencies": { "test": "github:a/b" } }' })
pkgData!.should.not.have.property('test')
})

it('custom target function to mimic semver', async () => {
// eslint-disable-next-line jsdoc/require-jsdoc
const target:TargetFunction = (name, [{ operator }]) => operator === '^' ? 'minor' : operator === '~' ? 'patch' : 'latest'
const pkgData = await ncu.run({
target,
packageData: JSON.stringify({
dependencies: {
'eslint-plugin-jsdoc': '~36.1.0',
jsonlines: '0.1.0',
juggernaut: '1.0.0',
mocha: '^8.3.2',
}
})
})
pkgData!.should.have.property('eslint-plugin-jsdoc')
;(pkgData as any)['eslint-plugin-jsdoc'].should.equal('~36.1.1')
pkgData!.should.have.property('jsonlines')
;(pkgData as any).jsonlines.should.equal('0.1.1')
pkgData!.should.have.property('juggernaut')
;(pkgData as any).juggernaut.should.equal('2.1.1')
pkgData!.should.have.property('mocha')
;(pkgData as any).mocha.should.equal('^8.4.0')
})

it('custom target and filter function to mimic semver', async () => {
// eslint-disable-next-line jsdoc/require-jsdoc
const target:TargetFunction = (name, [{ operator }]) => operator === '^' ? 'minor' : operator === '~' ? 'patch' : 'latest'
// eslint-disable-next-line jsdoc/require-jsdoc
const filter:FilterFunction = (_, [{ major, operator }]) => !(major === '0' || major === undefined || operator === undefined)
const pkgData = await ncu.run({
filter,
target,
packageData: JSON.stringify({
dependencies: {
'eslint-plugin-jsdoc': '~36.1.0',
jsonlines: '0.1.0',
juggernaut: '1.0.0',
mocha: '^8.3.2',
}
})
})
pkgData!.should.have.property('eslint-plugin-jsdoc')
;(pkgData as any)['eslint-plugin-jsdoc'].should.equal('~36.1.1')
pkgData!.should.not.have.property('jsonlines')
pkgData!.should.not.have.property('juggernaut')
pkgData!.should.have.property('mocha')
;(pkgData as any).mocha.should.equal('^8.4.0')
})

}) // end 'target'

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