hunk.code_window:     const oldPkgs = R.toPairs(mergeDependencies(oldImporterShr))
       const newPkgs = R.toPairs(mergeDependencies(opts.newShrinkwrap.importers[importer.id]))
       const depsToRemove = new Set([
  
hunk.labels: keep keep add keep
hunk.after_edit:     const allCurrentPackages = new Set(
         (opts.pruneDirectDependencies || opts.removePackages && opts.removePackages.length)
           ? (await readModulesDir(importer.modulesDir) || [])
           : [],
       )
  
hunk.file_path: packages/modules-cleaner/src/prune.ts
hunk.type: add
file: import {
    removalLogger,
    statsLogger,
  } from '@pnpm/core-loggers'
  import logger from '@pnpm/logger'
  import readModulesDir from '@pnpm/read-modules-dir'
  import { StoreController } from '@pnpm/store-controller-types'
  import { DEPENDENCIES_FIELDS, Registries } from '@pnpm/types'
  import * as dp from 'dependency-path'
  import vacuumCB = require('fs-vacuum')
  import path = require('path')
  import {
    ResolvedPackages,
    Shrinkwrap,
    ShrinkwrapImporter,
  } from 'pnpm-shrinkwrap'
  import R = require('ramda')
  import promisify = require('util.promisify')
  import removeDirectDependency from './removeDirectDependency'
  
  const vacuum = promisify(vacuumCB)
  
  export default async function prune (
    opts: {
      dryRun?: boolean,
      importers: Array<{
        bin: string,
        hoistedAliases: {[depPath: string]: string[]},
        modulesDir: string,
        id: string,
        shamefullyFlatten: boolean,
        prefix: string,
      }>,
      newShrinkwrap: Shrinkwrap,
      oldShrinkwrap: Shrinkwrap,
      pruneDirectDependencies?: boolean,
      pruneStore?: boolean,
      registries: Registries,
      removePackages?: string[],
      virtualStoreDir: string,
      shrinkwrapDirectory: string,
      storeController: StoreController,
    },
  ): Promise<Set<string>> {
    await Promise.all(opts.importers.map(async (importer) => {
      const oldImporterShr = opts.oldShrinkwrap.importers[importer.id] || {} as ShrinkwrapImporter
      const oldPkgs = R.toPairs(mergeDependencies(oldImporterShr))
      const newPkgs = R.toPairs(mergeDependencies(opts.newShrinkwrap.importers[importer.id]))
  
      const depsToRemove = new Set([
        ...opts.removePackages || [],
        ...R.difference(oldPkgs, newPkgs).map(([depName]) => depName),
      ])
      if (opts.pruneDirectDependencies) {
        const allCurrentPackages = await readModulesDir(importer.modulesDir) || []
        if (allCurrentPackages.length > 0) {
          const newPkgsSet = new Set(newPkgs.map(([depName]) => depName))
          for (const currentPackage of allCurrentPackages) {
            if (!newPkgsSet.has(currentPackage)) {
              depsToRemove.add(currentPackage)
            }
          }
        }
      }
  
      const { bin, modulesDir, prefix } = importer
  
      return Promise.all(Array.from(depsToRemove).map((depName) => {
        return removeDirectDependency({
          dependenciesField: oldImporterShr.devDependencies && oldImporterShr.devDependencies[depName] && 'devDependencies' ||
            oldImporterShr.optionalDependencies && oldImporterShr.optionalDependencies[depName] && 'optionalDependencies' ||
            oldImporterShr.dependencies && oldImporterShr.dependencies[depName] && 'dependencies' ||
            undefined,
          name: depName,
        }, {
          bin,
          dryRun: opts.dryRun,
          modulesDir,
          prefix,
        })
      }))
    }))
  
    const oldPkgIdsByDepPaths = getPkgsDepPaths(opts.registries.default, opts.oldShrinkwrap.packages || {})
    const newPkgIdsByDepPaths = getPkgsDepPaths(opts.registries.default, opts.newShrinkwrap.packages || {})
  
    const oldDepPaths = Object.keys(oldPkgIdsByDepPaths)
    const newDepPaths = Object.keys(newPkgIdsByDepPaths)
  
    const orphanDepPaths = R.difference(oldDepPaths, newDepPaths)
    const orphanPkgIds = new Set(R.props<string, string>(orphanDepPaths, oldPkgIdsByDepPaths))
  
    statsLogger.debug({
      prefix: opts.shrinkwrapDirectory,
      removed: orphanPkgIds.size,
    })
  
    if (!opts.dryRun) {
      if (orphanDepPaths.length) {
        if (opts.oldShrinkwrap.packages) {
          await Promise.all(opts.importers.filter((importer) => importer.shamefullyFlatten).map((importer) => {
            const { bin, hoistedAliases, modulesDir, prefix } = importer
            return Promise.all(orphanDepPaths.map(async (orphanDepPath) => {
              if (hoistedAliases[orphanDepPath]) {
                await Promise.all(hoistedAliases[orphanDepPath].map((alias) => {
                  return removeDirectDependency({
                    name: alias,
                  }, {
                    bin,
                    modulesDir,
                    muteLogs: true,
                    prefix,
                  })
                }))
              }
              delete hoistedAliases[orphanDepPath]
            }))
          }))
        }
  
        await Promise.all(orphanDepPaths.map(async (orphanDepPath) => {
          const pathToRemove = path.join(opts.virtualStoreDir, `.${orphanDepPath}`, 'node_modules')
          removalLogger.debug(pathToRemove)
          try {
            await vacuum(pathToRemove, {
              base: opts.virtualStoreDir,
              purge: true,
            })
          } catch (err) {
            logger.warn({
              error: err,
              message: `Failed to remove "${pathToRemove}"`,
              prefix: opts.shrinkwrapDirectory,
            })
          }
        }))
      }
  
      const addedDepPaths = R.difference(newDepPaths, oldDepPaths)
      const addedPkgIds = new Set(R.props<string, string>(addedDepPaths, newPkgIdsByDepPaths))
  
      await opts.storeController.updateConnections(path.dirname(opts.virtualStoreDir), {
        addDependencies: Array.from(addedPkgIds),
        prune: opts.pruneStore || false,
        removeDependencies: Array.from(orphanPkgIds),
      })
  
      await opts.storeController.saveState()
    }
  
    return new Set(orphanDepPaths)
  }
  
  function mergeDependencies (shrImporter: ShrinkwrapImporter): { [depName: string]: string } {
    return R.mergeAll(
      DEPENDENCIES_FIELDS.map((depType) => shrImporter[depType]),
    )
  }
  
  function getPkgsDepPaths (
    registry: string,
    packages: ResolvedPackages,
  ): {[depPath: string]: string} {
    const pkgIdsByDepPath = {}
    for (const relDepPath of Object.keys(packages)) {
      const depPath = dp.resolve(registry, relDepPath)
      pkgIdsByDepPath[depPath] = packages[relDepPath].id
        ? packages[relDepPath].id
        : depPath
    }
    return pkgIdsByDepPath
  }
  