Skip to content

Commit

Permalink
Merge eff27e6 into 57cf3dd
Browse files Browse the repository at this point in the history
  • Loading branch information
zkochan committed Mar 19, 2020
2 parents 57cf3dd + eff27e6 commit 1d91e03
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 11 deletions.
11 changes: 7 additions & 4 deletions packages/default-reporter/src/reportError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,14 @@ export default function reportError (logObj: Log) {
if (!err.code?.startsWith?.('ERR_PNPM_')) {
return formatGenericError(err.message ?? logObj['message'], err.stack)
}
const errorSummary = formatErrorSummary(err.message)
if (!logObj['message']['pkgsStack']?.length) {
return errorSummary
let errorOutput = formatErrorSummary(err.message)
if (logObj['message']['pkgsStack']?.length) {
errorOutput += `${EOL}${formatPkgsStack(logObj['message']['pkgsStack'])}`
}
return `${errorSummary}${EOL}${formatPkgsStack(logObj['message']['pkgsStack'])}`
if (logObj['message']['hint']) {
errorOutput += `${EOL}${logObj['message']['hint']}`
}
return errorOutput
}
}
return formatErrorSummary(logObj['message'])
Expand Down
1 change: 1 addition & 0 deletions packages/error/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export default class PnpmError extends Error {
public readonly code: string
public readonly hint?: string
public pkgsStack?: Array<{ id: string, name: string, version: string }>
constructor (code: string, message: string) {
super(message)
Expand Down
2 changes: 2 additions & 0 deletions packages/plugin-commands-installation/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ import * as unlink from './unlink'
import * as update from './update'

export { add, install, link, prune, remove, unlink, update }

export { RemoveMissingDepsError } from './remove'
52 changes: 48 additions & 4 deletions packages/plugin-commands-installation/src/remove.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import PnpmError from '@pnpm/error'
import findWorkspacePackages, { arrayOfWorkspacePackagesToMap } from '@pnpm/find-workspace-packages'
import { requireHooks } from '@pnpm/pnpmfile'
import { createOrConnectStoreController, CreateStoreControllerOptions } from '@pnpm/store-connection-manager'
import { DependenciesField } from '@pnpm/types'
import { filterDependenciesByType, getAllDependenciesFromPackage } from '@pnpm/utils'
import { oneLine } from 'common-tags'
import R = require('ramda')
import renderHelp = require('render-help')
Expand All @@ -19,6 +21,24 @@ import {
} from 'supi'
import recursive from './recursive'

export class RemoveMissingDepsError extends PnpmError {
public hint: string
constructor (
opts: {
availableDependencies: string[],
nonMatchedDependencies: string[],
removingFrom?: DependenciesField,
},
) {
let message = 'Cannot remove '
message += `${opts.nonMatchedDependencies.map(dep => `'${dep}'`).join(', ')}: `
message += `no such ${opts.nonMatchedDependencies.length > 1 ? 'dependencies' : 'dependency'} `
message += `found${opts.removingFrom ? ` in '${opts.removingFrom}'` : ''}`
super('PKG_TO_REMOVE_NOT_FOUND', message)
this.hint = `Available dependencies: ${opts.availableDependencies.join(', ')}`
}
}

export function rcOptionsTypes () {
return R.pick([
'global-dir',
Expand Down Expand Up @@ -137,19 +157,43 @@ export async function handler (
removeOpts['workspacePackages'] = opts.workspaceDir
? arrayOfWorkspacePackagesToMap(await findWorkspacePackages(opts.workspaceDir, opts))
: undefined
const currentManifest = await readProjectManifest(opts.dir, opts)
const targetDependenciesField = getSaveType(opts)
const {
manifest: currentManifest,
writeProjectManifest,
} = await readProjectManifest(opts.dir, opts)
const targetDeps = Object.keys(
targetDependenciesField === undefined
? getAllDependenciesFromPackage(currentManifest)
: filterDependenciesByType(currentManifest, {
dependencies: targetDependenciesField === 'dependencies',
devDependencies: targetDependenciesField === 'devDependencies',
optionalDependencies: targetDependenciesField === 'optionalDependencies',
}),
)
if (targetDeps.length === 0) {
throw new PnpmError('REMOVE_FROM_EMPTY_PROJECT', 'There are no dependencies to remove from')
}
const nonMatched = R.without(targetDeps, params)
if (nonMatched.length !== 0) {
throw new RemoveMissingDepsError({
availableDependencies: targetDeps,
nonMatchedDependencies: nonMatched,
removingFrom: targetDependenciesField,
})
}
const [mutationResult] = await mutateModules(
[
{
binsDir: opts.bin,
dependencyNames: params,
manifest: currentManifest.manifest,
manifest: currentManifest,
mutation: 'uninstallSome',
rootDir: opts.dir,
targetDependenciesField: getSaveType(opts),
targetDependenciesField,
},
],
removeOpts,
)
await currentManifest.writeProjectManifest(mutationResult.manifest)
await writeProjectManifest(mutationResult.manifest)
}
102 changes: 101 additions & 1 deletion packages/plugin-commands-installation/test/remove/remove.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import PnpmError from '@pnpm/error'
import { remove } from '@pnpm/plugin-commands-installation'
import { remove, RemoveMissingDepsError } from '@pnpm/plugin-commands-installation'
import prepare, { preparePackages } from '@pnpm/prepare'
import { oneLine } from 'common-tags'
import test = require('tape')
import { DEFAULT_OPTS } from '../utils'

Expand All @@ -20,3 +21,102 @@ test('remove should fail if no dependency is specified for removal', async (t) =
t.equal(err.message, 'At least one dependency name should be specified for removal')
t.end()
})

test('remove should fail if the project has no dependencies at all', async (t) => {
prepare(t)

let err!: PnpmError
try {
await remove.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
}, ['express'])
} catch (_err) {
err = _err
}
t.equal(err.code, 'ERR_PNPM_REMOVE_FROM_EMPTY_PROJECT')
t.equal(err.message, 'There are no dependencies to remove from')
t.end()
})

test('remove should fail if the project does not have one of the removed dependencies', async (t) => {
prepare(t, {
dependencies: {
'prod-dep-1': '1.0.0',
'prod-dep-2': '1.0.0',
},
devDependencies: {
'dev-dep-1': '1.0.0',
'dev-dep-2': '1.0.0',
},
optionalDependencies: {
'optional-dep-1': '1.0.0',
'optional-dep-2': '1.0.0',
},
})

{
let err!: RemoveMissingDepsError
try {
await remove.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
saveProd: true,
}, ['prod-dep-1', 'dev-dep-1', 'optional-dep-1'])
} catch (_err) {
err = _err
}
t.equal(err.code, 'ERR_PNPM_PKG_TO_REMOVE_NOT_FOUND')
t.equal(err.message, oneLine`Cannot remove 'dev-dep-1', 'optional-dep-1':
no such dependencies found in 'dependencies'`)
t.equal(err.hint, 'Available dependencies: prod-dep-1, prod-dep-2')
}
{
let err!: RemoveMissingDepsError
try {
await remove.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
saveDev: true,
}, ['prod-dep-1', 'dev-dep-1', 'optional-dep-1'])
} catch (_err) {
err = _err
}
t.equal(err.code, 'ERR_PNPM_PKG_TO_REMOVE_NOT_FOUND')
t.equal(err.message, oneLine`Cannot remove 'prod-dep-1', 'optional-dep-1':
no such dependencies found in 'devDependencies'`)
t.equal(err.hint, 'Available dependencies: dev-dep-1, dev-dep-2')
}
{
let err!: RemoveMissingDepsError
try {
await remove.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
saveOptional: true,
}, ['prod-dep-1', 'dev-dep-1', 'optional-dep-1'])
} catch (_err) {
err = _err
}
t.equal(err.code, 'ERR_PNPM_PKG_TO_REMOVE_NOT_FOUND')
t.equal(err.message, oneLine`Cannot remove 'prod-dep-1', 'dev-dep-1':
no such dependencies found in 'optionalDependencies'`)
t.equal(err.hint, 'Available dependencies: optional-dep-1, optional-dep-2')
}
{
let err!: RemoveMissingDepsError
try {
await remove.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
}, ['express', 'prod-dep-1', 'dev-dep-1', 'optional-dep-1'])
} catch (_err) {
err = _err
}
t.equal(err.code, 'ERR_PNPM_PKG_TO_REMOVE_NOT_FOUND')
t.equal(err.message, "Cannot remove 'express': no such dependency found")
t.equal(err.hint, oneLine`Available dependencies: dev-dep-1, dev-dep-2,
prod-dep-1, prod-dep-2, optional-dep-1, optional-dep-2`)
}
t.end()
})
8 changes: 6 additions & 2 deletions packages/pnpm/test/install/misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,15 +196,19 @@ test('lockfile compatibility', async (t: tape.Test) => {
})

test('support installing and uninstalling from the same store simultaneously', async (t: tape.Test) => {
const project = prepare(t)
const project = prepare(t, {
dependencies: {
rimraf: '2.5.1',
},
})

await Promise.all([
execPnpm(['install', 'pkg-that-installs-slowly']),
(async () => {
await delay(500) // to be sure that lock was created

await project.storeHasNot('pkg-that-installs-slowly')
await execPnpm(['uninstall', 'rimraf@2.5.1'])
await execPnpm(['uninstall', 'rimraf'])

await project.hasNot('rimraf')
})(),
Expand Down

0 comments on commit 1d91e03

Please sign in to comment.