mirror of
https://github.com/pnpm/pnpm.git
synced 2026-05-07 15:38:24 -04:00
@@ -37,6 +37,7 @@
|
||||
"@pnpm/package-is-installable": "workspace:2.2.1",
|
||||
"@pnpm/package-store": "workspace:4.0.16",
|
||||
"@pnpm/read-importer-manifest": "workspace:1.0.8",
|
||||
"@pnpm/semver-diff": "1.0.0",
|
||||
"@pnpm/server": "workspace:3.0.6",
|
||||
"@pnpm/store-controller-types": "workspace:3.0.3",
|
||||
"@pnpm/store-path": "2.1.0",
|
||||
|
||||
@@ -7,14 +7,18 @@ import {
|
||||
import outdated, {
|
||||
forPackages as outdatedForPackages, OutdatedPackage,
|
||||
} from '@pnpm/outdated'
|
||||
import semverDiff, { SEMVER_CHANGE } from '@pnpm/semver-diff'
|
||||
import storePath from '@pnpm/store-path'
|
||||
import { PackageJson, Registries } from '@pnpm/types'
|
||||
import chalk from 'chalk'
|
||||
import R = require('ramda')
|
||||
import stripColor = require('strip-color')
|
||||
import table = require('text-table')
|
||||
import createLatestVersionGetter from '../createLatestVersionGetter'
|
||||
import { readImporterManifestOnly } from '../readImporterManifest'
|
||||
|
||||
export type OutdatedWithVersionDiff = OutdatedPackage & { change: SEMVER_CHANGE | null, diff?: [string[], string[]] }
|
||||
|
||||
export default async function (
|
||||
args: string[],
|
||||
opts: {
|
||||
@@ -58,27 +62,93 @@ export default async function (
|
||||
const columnNames = [
|
||||
'Package',
|
||||
'Current',
|
||||
'Wanted',
|
||||
'Latest',
|
||||
...(opts.global ? [] : ['Belongs To']),
|
||||
].map((txt) => chalk.underline(txt))
|
||||
let columnFns: Array<(outdatedPkg: OutdatedPackage) => string> = [
|
||||
({ packageName }) => chalk.yellow(packageName),
|
||||
({ current }) => current || 'missing',
|
||||
({ wanted }) => chalk.green(wanted),
|
||||
({ latest }) => latest && chalk.magenta(latest) || '',
|
||||
let columnFns: Array<(outdatedPkg: OutdatedWithVersionDiff) => string> = [
|
||||
renderPackageName,
|
||||
renderCurrent,
|
||||
renderLatest,
|
||||
]
|
||||
if (!opts.global) {
|
||||
columnFns.push(({ belongsTo }) => belongsTo)
|
||||
return table([
|
||||
columnNames,
|
||||
...R.sortWith(
|
||||
[
|
||||
sortBySemverChange,
|
||||
(o1, o2) => o1.packageName.localeCompare(o2.packageName),
|
||||
],
|
||||
outdatedPackages.map(toOutdatedWithVersionDiff)
|
||||
)
|
||||
.map((outdatedPkg) => columnFns.map((fn) => fn(outdatedPkg))),
|
||||
], {
|
||||
stringLength: (s: string) => stripColor(s).length,
|
||||
})
|
||||
}
|
||||
|
||||
export function toOutdatedWithVersionDiff<T> (outdated: T & OutdatedPackage): T & OutdatedWithVersionDiff {
|
||||
if (outdated.latest) {
|
||||
return {
|
||||
...outdated,
|
||||
...semverDiff(outdated.wanted, outdated.latest),
|
||||
}
|
||||
}
|
||||
return {
|
||||
...outdated,
|
||||
change: 'unknown',
|
||||
}
|
||||
}
|
||||
|
||||
export function renderPackageName ({ belongsTo, packageName }: OutdatedWithVersionDiff) {
|
||||
switch (belongsTo) {
|
||||
case 'devDependencies': return `${packageName} ${chalk.dim('(dev)')}`
|
||||
case 'optionalDependencies': return `${packageName} ${chalk.dim('(optional)')}`
|
||||
default: return packageName
|
||||
}
|
||||
}
|
||||
|
||||
export function renderCurrent ({ current, wanted }: OutdatedWithVersionDiff) {
|
||||
let output = current || 'missing'
|
||||
if (current === wanted) return output
|
||||
return `${output} (wanted ${wanted})`
|
||||
}
|
||||
|
||||
const DIFF_COLORS = {
|
||||
feature: chalk.yellowBright.bold,
|
||||
fix: chalk.greenBright.bold,
|
||||
}
|
||||
|
||||
export function renderLatest ({ latest, change, diff }: OutdatedWithVersionDiff) {
|
||||
if (!latest) return ''
|
||||
if (change === null || !diff) return latest
|
||||
|
||||
const highlight = DIFF_COLORS[change] || chalk.redBright.bold
|
||||
const same = joinVersionTuples(diff[0], 0)
|
||||
const other = highlight(joinVersionTuples(diff[1], diff[0].length))
|
||||
if (!same) return other
|
||||
return diff[0].length === 3 ? `${same}-${other}` : `${same}.${other}`
|
||||
}
|
||||
|
||||
function joinVersionTuples (versionTuples: string[], startIndex: number) {
|
||||
const neededForSemver = 3 - startIndex
|
||||
if (versionTuples.length <= neededForSemver) return versionTuples.join('.')
|
||||
return `${
|
||||
versionTuples.slice(0, neededForSemver).join('.')
|
||||
}-${
|
||||
versionTuples.slice(neededForSemver).join('.')
|
||||
}`
|
||||
}
|
||||
|
||||
export function sortBySemverChange (outdated1: OutdatedWithVersionDiff, outdated2: OutdatedWithVersionDiff) {
|
||||
return pkgPriority(outdated1) - pkgPriority(outdated2)
|
||||
}
|
||||
|
||||
function pkgPriority (pkg: OutdatedWithVersionDiff) {
|
||||
switch (pkg.change) {
|
||||
case null: return 0
|
||||
case 'fix': return 1
|
||||
case 'feature': return 2
|
||||
case 'breaking': return 3
|
||||
default: return 4
|
||||
}
|
||||
console.log(
|
||||
table([
|
||||
columnNames,
|
||||
...outdatedPackages.map((outdatedPkg) => columnFns.map((fn) => fn(outdatedPkg))),
|
||||
], {
|
||||
stringLength: (s: string) => stripColor(s).length,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
export async function outdatedDependenciesOfWorkspacePackages (
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
import { getLockfileImporterId } from '@pnpm/lockfile-file'
|
||||
import { OutdatedPackage } from '@pnpm/outdated'
|
||||
import { DependenciesField, PackageJson, Registries } from '@pnpm/types'
|
||||
import chalk from 'chalk'
|
||||
import R = require('ramda')
|
||||
import stripColor = require('strip-color')
|
||||
import table = require('text-table')
|
||||
import { outdatedDependenciesOfWorkspacePackages } from '../outdated'
|
||||
import {
|
||||
outdatedDependenciesOfWorkspacePackages,
|
||||
renderCurrent,
|
||||
renderLatest,
|
||||
renderPackageName,
|
||||
sortBySemverChange,
|
||||
toOutdatedWithVersionDiff,
|
||||
} from '../outdated'
|
||||
|
||||
const DEP_PRIORITY: Record<DependenciesField, number> = {
|
||||
dependencies: 1,
|
||||
@@ -12,6 +20,15 @@ const DEP_PRIORITY: Record<DependenciesField, number> = {
|
||||
optionalDependencies: 0,
|
||||
}
|
||||
|
||||
type OutdatedInWorkspace = OutdatedPackage & {
|
||||
belongsTo: DependenciesField,
|
||||
current?: string,
|
||||
dependentPkgs: Array<{ location: string, manifest: PackageJson }>,
|
||||
latest?: string,
|
||||
packageName: string,
|
||||
wanted: string,
|
||||
}
|
||||
|
||||
export default async (
|
||||
pkgs: Array<{path: string, manifest: PackageJson}>,
|
||||
args: string[],
|
||||
@@ -42,14 +59,7 @@ export default async (
|
||||
userAgent: string,
|
||||
},
|
||||
) => {
|
||||
const outdatedByNameAndType = {} as Record<string, {
|
||||
belongsTo: DependenciesField,
|
||||
current?: string,
|
||||
dependentPkgs: Array<{ location: string, manifest: PackageJson }>,
|
||||
latest?: string,
|
||||
packageName: string,
|
||||
wanted: string,
|
||||
}>
|
||||
const outdatedByNameAndType = {} as Record<string, OutdatedInWorkspace>
|
||||
if (opts.lockfileDirectory) {
|
||||
const outdatedPackagesByProject = await outdatedDependenciesOfWorkspacePackages(pkgs, args, opts)
|
||||
for (let { prefix, outdatedPackages, manifest } of outdatedPackagesByProject) {
|
||||
@@ -76,23 +86,24 @@ export default async (
|
||||
}))
|
||||
}
|
||||
|
||||
const columnNames = ['Package', 'Current', 'Wanted', 'Latest', 'Belongs To', 'Dependents'].map((txt) => chalk.underline(txt))
|
||||
const columnNames = ['Package', 'Current', 'Latest', 'Dependents'].map((txt) => chalk.underline(txt))
|
||||
console.log(
|
||||
table([
|
||||
columnNames,
|
||||
...R.sortWith(
|
||||
[
|
||||
sortBySemverChange,
|
||||
(o1, o2) => o1.packageName.localeCompare(o2.packageName),
|
||||
(o1, o2) => DEP_PRIORITY[o1.belongsTo] - DEP_PRIORITY[o2.belongsTo],
|
||||
],
|
||||
(Object.values(outdatedByNameAndType)),
|
||||
(
|
||||
Object.values(outdatedByNameAndType).map(toOutdatedWithVersionDiff)
|
||||
),
|
||||
)
|
||||
.map((outdatedPkg) => [
|
||||
chalk.yellow(outdatedPkg.packageName),
|
||||
outdatedPkg.current || 'missing',
|
||||
chalk.green(outdatedPkg.wanted),
|
||||
chalk.magenta(outdatedPkg.latest || ''),
|
||||
outdatedPkg.belongsTo,
|
||||
renderPackageName(outdatedPkg),
|
||||
renderCurrent(outdatedPkg),
|
||||
renderLatest(outdatedPkg),
|
||||
outdatedPkg.dependentPkgs
|
||||
.map(({ manifest, location }) => manifest.name || location)
|
||||
.sort()
|
||||
|
||||
@@ -280,9 +280,17 @@ export default async function run (argv: string[]) {
|
||||
const result = pnpmCmds[cmd](cliArgs, opts, cliConf.argv.remain[0])
|
||||
if (result instanceof Promise) {
|
||||
result
|
||||
.then(resolve)
|
||||
.then((output) => {
|
||||
if (typeof output === 'string') {
|
||||
console.log(output)
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
.catch(reject)
|
||||
} else {
|
||||
if (typeof result === 'string') {
|
||||
console.log(result)
|
||||
}
|
||||
resolve()
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { WANTED_LOCKFILE } from '@pnpm/constants'
|
||||
import prepare, { tempDir } from '@pnpm/prepare'
|
||||
import chalk from 'chalk'
|
||||
import { stripIndents } from 'common-tags'
|
||||
import makeDir = require('make-dir')
|
||||
import fs = require('mz/fs')
|
||||
@@ -7,6 +8,7 @@ import normalizeNewline = require('normalize-newline')
|
||||
import path = require('path')
|
||||
import tape = require('tape')
|
||||
import promisifyTape from 'tape-promise'
|
||||
import outdated from '../src/cmd/outdated'
|
||||
import { execPnpm, execPnpmSync } from './utils'
|
||||
|
||||
const hasOutdatedDepsFixture = path.join(__dirname, 'packages', 'has-outdated-deps')
|
||||
@@ -18,15 +20,30 @@ const testOnly = promisifyTape(tape.only)
|
||||
test('pnpm outdated', async (t: tape.Test) => {
|
||||
process.chdir(hasOutdatedDepsFixture)
|
||||
|
||||
const result = execPnpmSync('outdated')
|
||||
|
||||
t.equal(result.status, 0)
|
||||
|
||||
t.equal(normalizeNewline(result.stdout.toString()), stripIndents`
|
||||
Package Current Wanted Latest Belongs To
|
||||
is-negative 1.0.0 1.1.0 2.1.0 dependencies
|
||||
is-positive 1.0.0 3.1.0 3.1.0 dependencies
|
||||
` + '\n')
|
||||
t.equal(
|
||||
await outdated([], {
|
||||
alwaysAuth: false,
|
||||
fetchRetries: 2,
|
||||
fetchRetryFactor: 1,
|
||||
fetchRetryMaxtimeout: 60000,
|
||||
fetchRetryMintimeout: 10000,
|
||||
global: false,
|
||||
independentLeaves: false,
|
||||
networkConcurrency: 16,
|
||||
offline: false,
|
||||
prefix: process.cwd(),
|
||||
rawNpmConfig: { registry: 'https://localhost:4873' },
|
||||
registries: { default: 'https://localhost:4873' },
|
||||
strictSsl: false,
|
||||
tag: 'latest',
|
||||
userAgent: '',
|
||||
}, 'outdated'),
|
||||
stripIndents`
|
||||
${chalk.underline('Package')} ${chalk.underline('Current')} ${chalk.underline('Latest')}
|
||||
is-positive 1.0.0 (wanted 3.1.0) 3.1.0
|
||||
is-negative 1.0.0 (wanted 1.1.0) ${chalk.redBright.bold('2.1.0')}
|
||||
`,
|
||||
)
|
||||
})
|
||||
|
||||
test('pnpm outdated: only current lockfile is available', async (t: tape.Test) => {
|
||||
@@ -41,9 +58,9 @@ test('pnpm outdated: only current lockfile is available', async (t: tape.Test) =
|
||||
t.equal(result.status, 0)
|
||||
|
||||
t.equal(normalizeNewline(result.stdout.toString()), stripIndents`
|
||||
Package Current Wanted Latest Belongs To
|
||||
is-negative 1.0.0 1.0.0 2.1.0 dependencies
|
||||
is-positive 1.0.0 1.0.0 3.1.0 dependencies
|
||||
Package Current Latest
|
||||
is-negative 1.0.0 2.1.0
|
||||
is-positive 1.0.0 3.1.0
|
||||
` + '\n')
|
||||
})
|
||||
|
||||
@@ -58,9 +75,9 @@ test('pnpm outdated: only wanted lockfile is available', async (t: tape.Test) =>
|
||||
t.equal(result.status, 0)
|
||||
|
||||
t.equal(normalizeNewline(result.stdout.toString()), stripIndents`
|
||||
Package Current Wanted Latest Belongs To
|
||||
is-negative missing 1.1.0 2.1.0 dependencies
|
||||
is-positive missing 3.1.0 3.1.0 dependencies
|
||||
Package Current Latest
|
||||
is-positive missing (wanted 3.1.0) 3.1.0
|
||||
is-negative missing (wanted 1.1.0) 2.1.0
|
||||
` + '\n')
|
||||
})
|
||||
|
||||
@@ -82,9 +99,9 @@ test('pnpm outdated with external lockfile', async (t: tape.Test) => {
|
||||
t.equal(result.status, 0)
|
||||
|
||||
t.equal(normalizeNewline(result.stdout.toString()), stripIndents`
|
||||
Package Current Wanted Latest Belongs To
|
||||
is-negative 1.0.0 1.1.0 2.1.0 dependencies
|
||||
is-positive 1.0.0 3.1.0 3.1.0 dependencies
|
||||
Package Current Latest
|
||||
is-positive 1.0.0 (wanted 3.1.0) 3.1.0
|
||||
is-negative 1.0.0 (wanted 1.1.0) 2.1.0
|
||||
` + '\n')
|
||||
})
|
||||
|
||||
@@ -102,9 +119,9 @@ test('pnpm outdated on global packages', async (t: tape.Test) => {
|
||||
t.equal(result.status, 0)
|
||||
|
||||
t.equal(normalizeNewline(result.stdout.toString()), stripIndents`
|
||||
Package Current Wanted Latest
|
||||
is-negative 1.0.0 1.0.0 2.1.0
|
||||
is-positive 1.0.0 1.0.0 3.1.0
|
||||
Package Current Latest
|
||||
is-negative 1.0.0 2.1.0
|
||||
is-positive 1.0.0 3.1.0
|
||||
` + '\n')
|
||||
})
|
||||
|
||||
|
||||
@@ -51,10 +51,10 @@ test('pnpm recursive outdated', async (t: tape.Test) => {
|
||||
t.equal(result.status, 0)
|
||||
|
||||
t.equal(normalizeNewline(result.stdout.toString()), stripIndents`
|
||||
Package Current Wanted Latest Belongs To Dependents
|
||||
is-negative 1.0.0 1.0.0 2.1.0 dependencies project-2
|
||||
is-negative 1.0.0 1.0.0 2.1.0 devDependencies project-3
|
||||
is-positive 1.0.0 1.0.0 3.1.0 dependencies project-1, project-3
|
||||
Package Current Latest Dependents
|
||||
is-negative 1.0.0 2.1.0 project-2
|
||||
is-negative (dev) 1.0.0 2.1.0 project-3
|
||||
is-positive 1.0.0 3.1.0 project-1, project-3
|
||||
` + '\n')
|
||||
}
|
||||
|
||||
@@ -64,8 +64,8 @@ test('pnpm recursive outdated', async (t: tape.Test) => {
|
||||
t.equal(result.status, 0)
|
||||
|
||||
t.equal(normalizeNewline(result.stdout.toString()), stripIndents`
|
||||
Package Current Wanted Latest Belongs To Dependents
|
||||
is-positive 1.0.0 1.0.0 3.1.0 dependencies project-1, project-3
|
||||
Package Current Latest Dependents
|
||||
is-positive 1.0.0 3.1.0 project-1, project-3
|
||||
` + '\n')
|
||||
}
|
||||
})
|
||||
@@ -111,10 +111,10 @@ test('pnpm recursive outdated in workspace with shared lockfile', async (t: tape
|
||||
t.equal(result.status, 0)
|
||||
|
||||
t.equal(normalizeNewline(result.stdout.toString()), stripIndents`
|
||||
Package Current Wanted Latest Belongs To Dependents
|
||||
is-negative 1.0.0 1.0.0 2.1.0 dependencies project-2
|
||||
is-negative 1.0.0 1.0.0 2.1.0 devDependencies project-3
|
||||
is-positive 1.0.0 1.0.0 3.1.0 dependencies project-1, project-3
|
||||
Package Current Latest Dependents
|
||||
is-negative 1.0.0 2.1.0 project-2
|
||||
is-negative (dev) 1.0.0 2.1.0 project-3
|
||||
is-positive 1.0.0 3.1.0 project-1, project-3
|
||||
` + '\n')
|
||||
}
|
||||
|
||||
@@ -124,8 +124,8 @@ test('pnpm recursive outdated in workspace with shared lockfile', async (t: tape
|
||||
t.equal(result.status, 0)
|
||||
|
||||
t.equal(normalizeNewline(result.stdout.toString()), stripIndents`
|
||||
Package Current Wanted Latest Belongs To Dependents
|
||||
is-positive 1.0.0 1.0.0 3.1.0 dependencies project-1, project-3
|
||||
Package Current Latest Dependents
|
||||
is-positive 1.0.0 3.1.0 project-1, project-3
|
||||
` + '\n')
|
||||
}
|
||||
})
|
||||
|
||||
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@@ -1036,6 +1036,7 @@ importers:
|
||||
'@pnpm/package-is-installable': 'link:../package-is-installable'
|
||||
'@pnpm/package-store': 'link:../package-store'
|
||||
'@pnpm/read-importer-manifest': 'link:../read-importer-manifest'
|
||||
'@pnpm/semver-diff': 1.0.0
|
||||
'@pnpm/server': 'link:../server'
|
||||
'@pnpm/store-controller-types': 'link:../store-controller-types'
|
||||
'@pnpm/store-path': 2.1.0
|
||||
@@ -1148,6 +1149,7 @@ importers:
|
||||
'@pnpm/read-importer-manifest': 'workspace:1.0.8'
|
||||
'@pnpm/read-package-json': 'link:../read-package-json'
|
||||
'@pnpm/registry-mock': 1.4.0
|
||||
'@pnpm/semver-diff': 1.0.0
|
||||
'@pnpm/server': 'workspace:3.0.6'
|
||||
'@pnpm/store-controller-types': 'workspace:3.0.3'
|
||||
'@pnpm/store-path': 2.1.0
|
||||
@@ -2225,6 +2227,12 @@ packages:
|
||||
hasBin: true
|
||||
resolution:
|
||||
integrity: sha512-2GzsC4937QJcdWr9uWcBIaHF7CIeIeCVJhwfab31B73O9+Pc7baikKjMMUWDPgnMJG7kTRlRSadVYN6KDjiLaw==
|
||||
/@pnpm/semver-diff/1.0.0:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=8.15'
|
||||
resolution:
|
||||
integrity: sha512-xxjp9zkTS8cfRNEhfoTNIG9tGfiXn42anXDc6pLLepJlBMsDH4RVIF/ygOO2EKz7/9E2PF1zCQUTwjFpjFwL2g==
|
||||
/@pnpm/store-path/2.1.0:
|
||||
dependencies:
|
||||
can-link: 1.0.2
|
||||
|
||||
Reference in New Issue
Block a user