Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: raineorshine/npm-check-updates
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v16.3.20
Choose a base ref
...
head repository: raineorshine/npm-check-updates
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v16.3.21
Choose a head ref
  • 2 commits
  • 7 files changed
  • 1 contributor

Commits on Nov 11, 2022

  1. Copy the full SHA
    f13fd29 View commit details
  2. 16.3.21

    raineorshine committed Nov 11, 2022
    Copy the full SHA
    25f9b68 View commit details
Showing with 79 additions and 10 deletions.
  1. +2 −2 package-lock.json
  2. +1 −1 package.json
  3. +15 −4 src/index.ts
  4. +3 −1 src/lib/getCurrentDependencies.ts
  5. +5 −2 src/lib/keyValueBy.ts
  6. +3 −0 src/types/Options.ts
  7. +50 −0 test/workspaces.test.ts
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "npm-check-updates",
"version": "16.3.20",
"version": "16.3.21",
"author": "Tomas Junnonen <tomas1@gmail.com>",
"license": "Apache-2.0",
"contributors": [
19 changes: 15 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -205,6 +205,10 @@ export async function run(
async function runUpgrades(): Promise<Index<string> | PackageFile | void> {
const defaultPackageFilename = getPackageFileName(options)

// Workspace package names
// These will be used to filter out local workspace packages so they are not fetched from the registry.
let workspacePackages: string[] = []

// Find the package file with globby.
// When in workspaces mode, only include the root project package file when --root is used.
let pkgs =
@@ -248,20 +252,26 @@ export async function run(
.replace(/\\/g, '/'),
)

const workspacePackages = [
// e.g. [packages/a/package.json, ...]
const workspacePackageFiles = [
...globby.sync(workspacePackageGlob, {
ignore: ['**/node_modules/**'],
}),
]

// Extract the package names from the full package paths.
// These will be used to filter out local workspace packages so they are not fetched from the registry.
// e.g. [a, b, c, ...]
workspacePackages = workspacePackageFiles.map(file => file.split('/').slice(-2)[0])

// add workspace packages
pkgs = [
...pkgs,
...(options.workspaces
? // --workspaces
workspacePackages
workspacePackageFiles
: // --workspace
workspacePackages.filter(pkgFile =>
workspacePackageFiles.filter(pkgFile =>
options.workspace?.some(workspace =>
workspaces?.some(
workspacePattern =>
@@ -291,10 +301,11 @@ export async function run(
// Merge config options.
rcConfig = mergeOptions(options, rcConfig)
}
const pkgOptions = {
const pkgOptions: Options = {
...options,
...rcConfig,
packageFile,
workspacePackages,
}
const [pkgData, pkgFile] = await findPackage(pkgOptions)
return {
4 changes: 3 additions & 1 deletion src/lib/getCurrentDependencies.ts
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import { PackageFile } from '../types/PackageFile'
import { VersionSpec } from '../types/VersionSpec'
import filterAndReject from './filterAndReject'
import filterObject from './filterObject'
import { keyValueBy } from './keyValueBy'

/** Returns true if spec1 is greater than spec2, ignoring invalid version ranges. */
const isGreaterThanSafe = (spec1: VersionSpec, spec2: VersionSpec) =>
@@ -45,8 +46,9 @@ function getCurrentDependencies(pkgData: PackageFile = {}, options: Options = {}
}, {} as Index<VersionSpec>)

// filter & reject dependencies and versions
const workspacePackageMap = keyValueBy(options.workspacePackages || [])
const filteredDependencies = filterObject(
allDependencies,
filterObject(allDependencies, name => !workspacePackageMap[name]),
filterAndReject(
options.filter || null,
options.reject || null,
7 changes: 5 additions & 2 deletions src/lib/keyValueBy.ts
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ type KeyValueGenerator<K, V, R> = (key: K, value: V, accum: Index<R>) => Index<R
type ArrayKeyValueGenerator<T, R> = KeyValueGenerator<T, number, R>
type ObjectKeyValueGenerator<T, R> = KeyValueGenerator<string, T, R>

export function keyValueBy<T>(arr: T[]): Index<true>
export function keyValueBy<T, R>(arr: T[], keyValue: KeyValueGenerator<T, number, R>, initialValue?: Index<R>): Index<R>
export function keyValueBy<T, R>(
obj: Index<T>,
@@ -12,12 +13,14 @@ export function keyValueBy<T, R>(
): Index<R>

/** Generates an object from an array or object. Simpler than reduce or _.transform. The KeyValueGenerator passes (key, value) if the input is an object, and (value, i) if it is an array. The return object from each iteration is merged into the accumulated object. Return null to skip an item. */
export function keyValueBy<T, R>(
export function keyValueBy<T, R = true>(
input: T[] | Index<T>,
keyValue: ArrayKeyValueGenerator<T, R> | ObjectKeyValueGenerator<T, R>,
// if no keyValue is given, sets all values to true
keyValue?: ArrayKeyValueGenerator<T, R> | ObjectKeyValueGenerator<T, R>,
accum: Index<R> = {},
): Index<R> {
const isArray = Array.isArray(input)
keyValue = keyValue || ((key: T) => ({ [key as unknown as string]: true as unknown as R }))
// considerably faster than Array.prototype.reduce
Object.entries(input || {}).forEach(([key, value], i) => {
const o = isArray
3 changes: 3 additions & 0 deletions src/types/Options.ts
Original file line number Diff line number Diff line change
@@ -14,4 +14,7 @@ export type Options = RunOptions & {
packageData?: string
peerDependencies?: Index<any>
rcConfigPath?: string
// A list of local workspace packages by name.
// This is used to ignore local workspace packages when fetching new versions.
workspacePackages?: string[]
}
50 changes: 50 additions & 0 deletions test/workspaces.test.ts
Original file line number Diff line number Diff line change
@@ -58,6 +58,38 @@ const setup = async (workspaces: string[] | { packages: string[] } = ['packages/
return tempDir
}

/** Sets up a workspace with a dependency to a symlinked workspace package. */
const setupSymlinkedPackages = async (workspaces: string[] | { packages: string[] } = ['packages/**']) => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'npm-check-updates-'))
await fs.mkdtemp(path.join(os.tmpdir(), 'npm-check-updates-'))

const pkgDataRoot = JSON.stringify({ workspaces })

const pkgDataA = JSON.stringify({
dependencies: {
b: '0.4.2',
'ncu-test-v2': '1.0.0',
},
})

const pkgDataB = JSON.stringify({
dependencies: {
'ncu-test-v2': '1.1.0',
},
})

// write root package file
await fs.writeFile(path.join(tempDir, 'package.json'), pkgDataRoot, 'utf-8')

// write workspace package files
await fs.mkdir(path.join(tempDir, 'packages/a'), { recursive: true })
await fs.writeFile(path.join(tempDir, 'packages/a/package.json'), pkgDataA, 'utf-8')
await fs.mkdir(path.join(tempDir, 'packages/b'), { recursive: true })
await fs.writeFile(path.join(tempDir, 'packages/b/package.json'), pkgDataB, 'utf-8')

return tempDir
}

describe('--workspaces', function () {
this.timeout(60000)

@@ -147,6 +179,24 @@ describe('--workspaces', function () {
await fs.rm(tempDir, { recursive: true, force: true })
}
})

// https://github.com/raineorshine/npm-check-updates/issues/1217
it('ignore local workspace packages', async () => {
const tempDir = await setupSymlinkedPackages()
try {
const upgrades = await spawn('node', [bin, '--jsonUpgraded', '--workspaces'], { cwd: tempDir }).then(JSON.parse)
upgrades.should.deep.equal({
'packages/a/package.json': {
'ncu-test-v2': '2.0.0',
},
'packages/b/package.json': {
'ncu-test-v2': '2.0.0',
},
})
} finally {
await fs.rm(tempDir, { recursive: true, force: true })
}
})
})

describe('--workspace', function () {