Skip to content

Commit

Permalink
Target as a function (#1052)
Browse files Browse the repository at this point in the history
Co-authored-by: Raine Revere <[email protected]>
  • Loading branch information
Primajin and raineorshine authored Feb 28, 2022
1 parent 60e18c2 commit d335923
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 15 deletions.
20 changes: 19 additions & 1 deletion src/cli-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,24 @@ by each project's maintainers. Default.`])
other version numbers that are higher. Includes prereleases.`])
table.push(['patch', `Upgrade to the highest patch version without bumping the minor or major versions.`])

return `Set the target version that is upgraded to (default: "latest").\n\n${table.toString()}`
return `Set the target version that is upgraded to (default: "latest").
${table.toString()}
You can also specify a custom function in your .ncurc.js file, or when importing npm-check-updates:
${chalk.gray(`/** Custom target.
@param dependencyName The name of the dependency.
@param parsedVersion A parsed Semver object from semver-utils.
(See https://git.coolaj86.com/coolaj86/semver-utils.js#semverutils-parse-semverstring)
@returns One of the valid target values (specified in the table above).
*/`)}
${chalk.cyan('target')}: (dependencyName, [{ semver, version, operator, major, minor, patch, release, build }]) ${chalk.cyan('=>')} {
${chalk.red('if')} (major ${chalk.red('===')} ${chalk.blue('0')}) ${chalk.red('return')} ${chalk.yellow('\'minor\'')}
${chalk.red('return')} ${chalk.yellow('\'latest\'')}
}
`
}

// store CLI options separately from bin file so that they can be used to build type definitions
Expand Down Expand Up @@ -307,6 +324,7 @@ As a comparison: without using the --peer option, ncu will suggest the latest ve
arg: 'value',
description: 'Target version to upgrade to: latest, newest, greatest, minor, patch. Run "ncu --help --target" for details.` (default: "latest")',
help: getHelpTargetTable(),
type: 'string | TargetFunction',
},
{
long: 'timeout',
Expand Down
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

0 comments on commit d335923

Please sign in to comment.