refactor!: pass always all projects to mutateModules() (#5338)

This commit is contained in:
Zoltan Kochan
2022-10-13 03:24:02 +03:00
committed by GitHub
parent 3ea9389f6c
commit 645384bfd0
56 changed files with 1917 additions and 1680 deletions

View File

@@ -0,0 +1,5 @@
---
"@pnpm/get-context": major
---
Breaking changes to the API.

View File

@@ -0,0 +1,5 @@
---
"@pnpm/config": minor
---
New field returned: allProjectsGraph.

View File

@@ -0,0 +1,5 @@
---
"@pnpm/plugin-commands-installation": major
---
New required option added: `allProjectsGraph`.

View File

@@ -0,0 +1,6 @@
---
"@pnpm/core": major
"@pnpm/headless": major
---
Breaking changes to the API. All projects must be passed via a new field in options.

View File

@@ -0,0 +1,5 @@
---
"@pnpm/filter-workspace-packages": minor
---
`readProjects()` returns `allProjectsGraph`.

View File

@@ -11,6 +11,7 @@ export type UniversalOptions = Pick<Config, 'color' | 'dir' | 'rawConfig' | 'raw
export interface Config {
allProjects?: Project[]
selectedProjectsGraph?: ProjectsGraph
allProjectsGraph?: ProjectsGraph
allowNew: boolean
autoInstallPeers?: boolean

View File

@@ -27,7 +27,7 @@ export async function getPeerDependencyIssues (
opts: ListMissingPeersOptions
): Promise<PeerDependencyIssuesByProjects> {
const lockfileDir = opts.lockfileDir ?? process.cwd()
const ctx = await getContext(projects, {
const ctx = await getContext({
force: false,
forceSharedLockfile: false,
extraBinPaths: [],
@@ -35,9 +35,10 @@ export async function getPeerDependencyIssues (
nodeLinker: opts.nodeLinker ?? 'isolated',
registries: DEFAULT_REGISTRIES,
useLockfile: true,
allProjects: projects,
...opts,
})
const projectsToResolve = ctx.projects.map((project) => ({
const projectsToResolve = Object.values(ctx.projects).map((project) => ({
...project,
updatePackageManifest: false,
wantedDependencies: getWantedDependencies(project.manifest),

View File

@@ -1,5 +1,6 @@
import { WANTED_LOCKFILE } from '@pnpm/constants'
import PnpmError from '@pnpm/error'
import { ProjectOptions } from '@pnpm/get-context'
import { HoistingLimits } from '@pnpm/headless'
import { createReadPackageHook } from '@pnpm/hooks.read-package-hook'
import { Lockfile } from '@pnpm/lockfile-file'
@@ -113,6 +114,8 @@ export interface StrictInstallOptions {
global: boolean
globalBin?: string
patchedDependencies?: Record<string, string>
allProjects: ProjectOptions[]
}
export type InstallOptions =

View File

@@ -12,8 +12,8 @@ import {
} from '@pnpm/core-loggers'
import { createBase32HashFromFile } from '@pnpm/crypto.base32-hash'
import PnpmError from '@pnpm/error'
import getContext, { PnpmContext, ProjectOptions } from '@pnpm/get-context'
import headless, { Project } from '@pnpm/headless'
import getContext, { PnpmContext } from '@pnpm/get-context'
import headless from '@pnpm/headless'
import runLifecycleHook, {
makeNodeRequireOption,
runLifecycleHooksConcurrently,
@@ -85,7 +85,6 @@ const DEV_PREINSTALL = 'pnpm:devPreinstall'
export type DependenciesMutation = (
{
buildIndex: number
mutation: 'install'
pruneDirectDependencies?: boolean
} | {
@@ -106,30 +105,32 @@ export type DependenciesMutation = (
mutation: 'unlinkSome'
dependencyNames: string[]
}
) & (
{
manifest: ProjectManifest
}
)
export async function install (
manifest: ProjectManifest,
opts: InstallOptions & {
opts: Omit<InstallOptions, 'allProjects'> & {
preferredVersions?: PreferredVersions
pruneDirectDependencies?: boolean
}
) {
const rootDir = opts.dir ?? process.cwd()
const projects = await mutateModules(
[
{
buildIndex: 0,
manifest,
mutation: 'install',
pruneDirectDependencies: opts.pruneDirectDependencies,
rootDir: opts.dir ?? process.cwd(),
rootDir,
},
],
opts
{
...opts,
allProjects: [{
buildIndex: 0,
manifest,
rootDir,
}],
}
)
return projects[0].manifest
}
@@ -142,7 +143,7 @@ interface ProjectToBeInstalled {
rootDir: string
}
export type MutatedProject = ProjectOptions & DependenciesMutation
export type MutatedProject = DependenciesMutation & { rootDir: string }
export type MutateModulesOptions = InstallOptions & {
preferredVersions?: PreferredVersions
@@ -151,6 +152,28 @@ export type MutateModulesOptions = InstallOptions & {
} | InstallOptions['hooks']
}
export async function mutateModulesInSingleProject (
project: MutatedProject & {
binsDir?: string
manifest: ProjectManifest
rootDir: string
modulesDir?: string
},
maybeOpts: Omit<MutateModulesOptions, 'allProjects'>
): Promise<UpdatedProject> {
const [updatedProject] = await mutateModules(
[project],
{
...maybeOpts,
allProjects: [{
buildIndex: 0,
...project,
}],
}
)
return updatedProject
}
export async function mutateModules (
projects: MutatedProject[],
maybeOpts: MutateModulesOptions
@@ -169,12 +192,12 @@ export async function mutateModules (
const installsOnly = projects.every((project) => project.mutation === 'install')
if (!installsOnly) opts.strictPeerDependencies = false
opts['forceNewModules'] = installsOnly
const rootProjectManifest = projects.find(({ rootDir }) => rootDir === opts.lockfileDir)?.manifest ??
const rootProjectManifest = opts.allProjects.find(({ rootDir }) => rootDir === opts.lockfileDir)?.manifest ??
// When running install/update on a subset of projects, the root project might not be included,
// so reading its manifest explicitly here.
await safeReadProjectManifestOnly(opts.lockfileDir)
const ctx = await getContext(projects, opts)
const ctx = await getContext(opts)
if (opts.hooks.preResolution) {
await opts.hooks.preResolution({
@@ -192,7 +215,7 @@ export async function mutateModules (
: true
if (!maybeOpts.ignorePackageManifest) {
for (const { manifest, rootDir } of ctx.projects) {
for (const { manifest, rootDir } of Object.values(ctx.projects)) {
if (!manifest) {
throw new Error(`No package.json found in "${rootDir}"`)
}
@@ -277,10 +300,10 @@ export async function mutateModules (
opts.ignorePackageManifest ||
!needsFullResolution &&
opts.preferFrozenLockfile &&
(!opts.pruneLockfileImporters || Object.keys(ctx.wantedLockfile.importers).length === ctx.projects.length) &&
(!opts.pruneLockfileImporters || Object.keys(ctx.wantedLockfile.importers).length === Object.keys(ctx.projects).length) &&
ctx.existsWantedLockfile &&
ctx.wantedLockfile.lockfileVersion === LOCKFILE_VERSION &&
await allProjectsAreUpToDate(ctx.projects, {
await allProjectsAreUpToDate(Object.values(ctx.projects), {
autoInstallPeers: opts.autoInstallPeers,
linkWorkspacePackages: opts.linkWorkspacePackagesDepth >= 0,
wantedLockfile: ctx.wantedLockfile,
@@ -294,10 +317,10 @@ export async function mutateModules (
})
}
if (opts.lockfileOnly) {
return projects
return projects.map((mutatedProject) => ctx.projects[mutatedProject.rootDir])
}
if (!ctx.existsWantedLockfile) {
if (ctx.projects.some((project) => pkgHasDependencies(project.manifest))) {
if (Object.values(ctx.projects).some((project) => pkgHasDependencies(project.manifest))) {
throw new Error(`Headless installation requires a ${WANTED_LOCKFILE} file`)
}
} else {
@@ -315,7 +338,8 @@ export async function mutateModules (
pnpmVersion: opts.packageManager.name === 'pnpm' ? opts.packageManager.version : '',
},
patchedDependencies: patchedDependenciesWithResolvedPath,
projects: ctx.projects as Project[],
selectedProjectDirs: projects.map((project) => project.rootDir),
allProjects: ctx.projects,
prunedAt: ctx.modulesFile?.prunedAt,
pruneVirtualStore,
wantedLockfile: maybeOpts.ignorePackageManifest ? undefined : ctx.wantedLockfile,
@@ -332,7 +356,13 @@ export async function mutateModules (
mergeGitBranchLockfiles: opts.mergeGitBranchLockfiles,
})
}
return projects
return projects.map((mutatedProject) => {
const project = ctx.projects[mutatedProject.rootDir]
return {
...project,
manifest: project.originalManifest ?? project.manifest,
}
})
} catch (error: any) { // eslint-disable-line
if (
frozenLockfile ||
@@ -359,12 +389,16 @@ export async function mutateModules (
let preferredSpecs: Record<string, string> | null = null
// TODO: make it concurrent
for (const project of ctx.projects) {
for (const project of projects) {
const projectOpts = {
...project,
...ctx.projects[project.rootDir],
}
switch (project.mutation) {
case 'uninstallSome':
projectsToInstall.push({
pruneDirectDependencies: false,
...project,
...projectOpts,
removePackages: project.dependencyNames,
updatePackageManifest: true,
wantedDependencies: [],
@@ -372,48 +406,50 @@ export async function mutateModules (
break
case 'install': {
await installCase({
...project,
...projectOpts,
updatePackageManifest: opts.updatePackageManifest ?? opts.update,
})
break
}
case 'installSome': {
await installSome({
...project,
...projectOpts,
updatePackageManifest: opts.updatePackageManifest !== false,
})
break
}
case 'unlink': {
const packageDirs = await readModulesDirs(project.modulesDir)
const packageDirs = await readModulesDirs(projectOpts.modulesDir)
const externalPackages = await pFilter(
packageDirs!,
async (packageDir: string) => isExternalLink(ctx.storeDir, project.modulesDir, packageDir)
async (packageDir: string) => isExternalLink(ctx.storeDir, projectOpts.modulesDir, packageDir)
)
const allDeps = getAllDependenciesFromManifest(project.manifest)
const allDeps = getAllDependenciesFromManifest(projectOpts.manifest)
const packagesToInstall: string[] = []
for (const pkgName of externalPackages) {
await rimraf(path.join(project.modulesDir, pkgName))
await rimraf(path.join(projectOpts.modulesDir, pkgName))
if (allDeps[pkgName]) {
packagesToInstall.push(pkgName)
}
}
if (packagesToInstall.length === 0) return projects
if (packagesToInstall.length === 0) {
return projects.map((mutatedProject) => ctx.projects[mutatedProject.rootDir])
}
// TODO: install only those that were unlinked
// but don't update their version specs in package.json
await installCase({ ...project, mutation: 'install' })
await installCase({ ...projectOpts, mutation: 'install' })
break
}
case 'unlinkSome': {
if (project.manifest?.name && opts.globalBin) {
await removeBin(path.join(opts.globalBin, project.manifest?.name))
if (projectOpts.manifest?.name && opts.globalBin) {
await removeBin(path.join(opts.globalBin, projectOpts.manifest?.name))
}
const packagesToInstall: string[] = []
const allDeps = getAllDependenciesFromManifest(project.manifest)
const allDeps = getAllDependenciesFromManifest(projectOpts.manifest)
for (const depName of project.dependencyNames) {
try {
if (!await isExternalLink(ctx.storeDir, project.modulesDir, depName)) {
if (!await isExternalLink(ctx.storeDir, projectOpts.modulesDir, depName)) {
logger.warn({
message: `${depName} is not an external link`,
prefix: project.rootDir,
@@ -423,17 +459,19 @@ export async function mutateModules (
} catch (err: any) { // eslint-disable-line
if (err['code'] !== 'ENOENT') throw err
}
await rimraf(path.join(project.modulesDir, depName))
await rimraf(path.join(projectOpts.modulesDir, depName))
if (allDeps[depName]) {
packagesToInstall.push(depName)
}
}
if (packagesToInstall.length === 0) return projects
if (packagesToInstall.length === 0) {
return projects.map((mutatedProject) => ctx.projects[mutatedProject.rootDir])
}
// TODO: install only those that were unlinked
// but don't update their version specs in package.json
await installSome({
...project,
...projectOpts,
dependencySelectors: packagesToInstall,
mutation: 'installSome',
updatePackageManifest: false,
@@ -599,7 +637,7 @@ function forgetResolutionsOfPrevWantedDeps (importer: ProjectSnapshot, wantedDep
export async function addDependenciesToPackage (
manifest: ProjectManifest,
dependencySelectors: string[],
opts: InstallOptions & {
opts: Omit<InstallOptions, 'allProjects'> & {
bin?: string
allowNew?: boolean
peer?: boolean
@@ -607,28 +645,36 @@ export async function addDependenciesToPackage (
targetDependenciesField?: DependenciesField
}
) {
const rootDir = opts.dir ?? process.cwd()
const projects = await mutateModules(
[
{
binsDir: opts.bin,
allowNew: opts.allowNew,
dependencySelectors,
manifest,
mutation: 'installSome',
peer: opts.peer,
pinnedVersion: opts.pinnedVersion,
rootDir: opts.dir ?? process.cwd(),
rootDir,
targetDependenciesField: opts.targetDependenciesField,
},
],
{
...opts,
lockfileDir: opts.lockfileDir ?? opts.dir,
allProjects: [
{
buildIndex: 0,
binsDir: opts.bin,
manifest,
rootDir,
},
],
})
return projects[0].manifest
}
export type ImporterToUpdate = {
buildIndex: number
binsDir: string
id: string
manifest: ProjectManifest
@@ -654,7 +700,7 @@ interface InstallFunctionResult {
type InstallFunction = (
projects: ImporterToUpdate[],
ctx: PnpmContext<DependenciesMutation>,
ctx: PnpmContext,
opts: Omit<StrictInstallOptions, 'patchedDependencies'> & {
patchedDependencies?: Record<string, PatchFile>
makePartialCurrentLockfile: boolean
@@ -1133,7 +1179,8 @@ const installInContext: InstallFunction = async (projects, ctx, opts) => {
nodeVersion: opts.nodeVersion,
pnpmVersion: opts.packageManager.name === 'pnpm' ? opts.packageManager.version : '',
},
projects: ctx.projects as Project[],
selectedProjectDirs: projects.map((project) => project.rootDir),
allProjects: ctx.projects,
prunedAt: ctx.modulesFile?.prunedAt,
wantedLockfile: result.newLockfile,
})

View File

@@ -16,6 +16,7 @@ const workspacePackages = {
test('allProjectsAreUpToDate(): works with packages linked through the workspace protocol using relative path', async () => {
expect(await allProjectsAreUpToDate([
{
buildIndex: 0,
id: 'bar',
manifest: {
dependencies: {
@@ -25,6 +26,7 @@ test('allProjectsAreUpToDate(): works with packages linked through the workspace
rootDir: 'bar',
},
{
buildIndex: 0,
id: 'foo',
manifest: fooManifest,
rootDir: 'foo',
@@ -55,6 +57,7 @@ test('allProjectsAreUpToDate(): works with packages linked through the workspace
test('allProjectsAreUpToDate(): works with aliased local dependencies', async () => {
expect(await allProjectsAreUpToDate([
{
buildIndex: 0,
id: 'bar',
manifest: {
dependencies: {
@@ -64,6 +67,7 @@ test('allProjectsAreUpToDate(): works with aliased local dependencies', async ()
rootDir: 'bar',
},
{
buildIndex: 0,
id: 'foo',
manifest: fooManifest,
rootDir: 'foo',
@@ -94,6 +98,7 @@ test('allProjectsAreUpToDate(): works with aliased local dependencies', async ()
test('allProjectsAreUpToDate(): works with aliased local dependencies that specify versions', async () => {
expect(await allProjectsAreUpToDate([
{
buildIndex: 0,
id: 'bar',
manifest: {
dependencies: {
@@ -103,6 +108,7 @@ test('allProjectsAreUpToDate(): works with aliased local dependencies that speci
rootDir: 'bar',
},
{
buildIndex: 0,
id: 'foo',
manifest: fooManifest,
rootDir: 'foo',
@@ -133,6 +139,7 @@ test('allProjectsAreUpToDate(): works with aliased local dependencies that speci
test('allProjectsAreUpToDate(): returns false if the aliased dependency version is out of date', async () => {
expect(await allProjectsAreUpToDate([
{
buildIndex: 0,
id: 'bar',
manifest: {
dependencies: {
@@ -142,6 +149,7 @@ test('allProjectsAreUpToDate(): returns false if the aliased dependency version
rootDir: 'bar',
},
{
buildIndex: 0,
id: 'foo',
manifest: fooManifest,
rootDir: 'foo',
@@ -174,6 +182,7 @@ test('allProjectsAreUpToDate(): use link and registry version if linkWorkspacePa
await allProjectsAreUpToDate(
[
{
buildIndex: 0,
id: 'bar',
manifest: {
dependencies: {
@@ -185,6 +194,7 @@ test('allProjectsAreUpToDate(): use link and registry version if linkWorkspacePa
rootDir: 'bar',
},
{
buildIndex: 0,
id: 'bar2',
manifest: {
dependencies: {
@@ -194,11 +204,13 @@ test('allProjectsAreUpToDate(): use link and registry version if linkWorkspacePa
rootDir: 'bar2',
},
{
buildIndex: 0,
id: 'foo',
manifest: fooManifest,
rootDir: 'foo',
},
{
buildIndex: 0,
id: 'foo2',
manifest: {
name: 'foo2',
@@ -207,6 +219,7 @@ test('allProjectsAreUpToDate(): use link and registry version if linkWorkspacePa
rootDir: 'foo2',
},
{
buildIndex: 0,
id: 'foo3',
manifest: {
name: 'foo3',
@@ -261,6 +274,7 @@ test('allProjectsAreUpToDate(): use link and registry version if linkWorkspacePa
test('allProjectsAreUpToDate(): returns false if dependenciesMeta differs', async () => {
expect(await allProjectsAreUpToDate([
{
buildIndex: 0,
id: 'bar',
manifest: {
dependencies: {
@@ -275,6 +289,7 @@ test('allProjectsAreUpToDate(): returns false if dependenciesMeta differs', asyn
rootDir: 'bar',
},
{
buildIndex: 0,
id: 'foo',
manifest: fooManifest,
rootDir: 'foo',
@@ -305,6 +320,7 @@ test('allProjectsAreUpToDate(): returns false if dependenciesMeta differs', asyn
test('allProjectsAreUpToDate(): returns true if dependenciesMeta matches', async () => {
expect(await allProjectsAreUpToDate([
{
buildIndex: 0,
id: 'bar',
manifest: {
dependencies: {
@@ -319,6 +335,7 @@ test('allProjectsAreUpToDate(): returns true if dependenciesMeta matches', async
rootDir: 'bar',
},
{
buildIndex: 0,
id: 'foo',
manifest: fooManifest,
rootDir: 'foo',

View File

@@ -5,7 +5,7 @@ import rimraf from '@zkochan/rimraf'
import clone from 'ramda/src/clone'
import {
addDependenciesToPackage,
mutateModules,
mutateModulesInSingleProject,
} from '@pnpm/core'
import writeYamlFile from 'write-yaml-file'
import { testDefaults } from './utils'
@@ -27,23 +27,17 @@ test('installation breaks if the lockfile contains the wrong checksum', async ()
corruptedLockfile.packages['/@pnpm.e2e/pkg-with-1-dep/100.0.0'].resolution['integrity'] = corruptedLockfile.packages['/@pnpm.e2e/dep-of-pkg-with-1-dep/100.0.0'].resolution['integrity']
await writeYamlFile(WANTED_LOCKFILE, corruptedLockfile, { lineWidth: 1000 })
await expect(mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ frozenLockfile: true }))).rejects.toThrowError(/Package name mismatch found while reading/)
await expect(mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ frozenLockfile: true }))).rejects.toThrowError(/Package name mismatch found while reading/)
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults())
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults())
expect(await project.readLockfile()).toStrictEqual(correctLockfile)
@@ -52,14 +46,11 @@ test('installation breaks if the lockfile contains the wrong checksum', async ()
await rimraf('node_modules')
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ preferFrozenLockfile: false }))
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ preferFrozenLockfile: false }))
expect(await project.readLockfile()).toStrictEqual(correctLockfile)
})
@@ -82,25 +73,18 @@ test('installation breaks if the lockfile contains the wrong checksum and the st
await writeYamlFile(WANTED_LOCKFILE, corruptedLockfile, { lineWidth: 1000 })
await expect(
mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
],
await testDefaults({ frozenLockfile: true }, { retry: { retries: 0 } }))
).rejects.toThrowError(/Got unexpected checksum/)
await mutateModules([
{
buildIndex: 0,
mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({}, { retry: { retries: 0 } }))
}, await testDefaults({ frozenLockfile: true }, { retry: { retries: 0 } }))
).rejects.toThrowError(/Got unexpected checksum/)
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({}, { retry: { retries: 0 } }))
{
const lockfile = await project.readLockfile()
@@ -113,14 +97,11 @@ test('installation breaks if the lockfile contains the wrong checksum and the st
await rimraf('node_modules')
const reporter = jest.fn()
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ preferFrozenLockfile: false, reporter }, { retry: { retries: 0 } }))
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ preferFrozenLockfile: false, reporter }, { retry: { retries: 0 } }))
expect(reporter).toBeCalledWith(expect.objectContaining({
level: 'warn',

View File

@@ -7,6 +7,7 @@ test('cannot resolve peer dependency for top-level dependency', async () => {
const peerDependencyIssues = await getPeerDependencyIssues([
{
buildIndex: 0,
manifest: {
dependencies: {
'ajv-keywords': '1.5.0',
@@ -24,6 +25,7 @@ test('a conflict is detected when the same peer is required with ranges that do
const peerDependencyIssues = await getPeerDependencyIssues([
{
buildIndex: 0,
manifest: {
dependencies: {
'@pnpm.e2e/has-foo100-peer': '1.0.0',

View File

@@ -1,6 +1,6 @@
import fs from 'fs'
import path from 'path'
import { addDependenciesToPackage, install, mutateModules } from '@pnpm/core'
import { addDependenciesToPackage, install, mutateModules, mutateModulesInSingleProject } from '@pnpm/core'
import { prepareEmpty } from '@pnpm/prepare'
import { addDistTag } from '@pnpm/registry-mock'
import { sync as loadJsonFile } from 'load-json-file'
@@ -107,32 +107,42 @@ test('adding a new dependency to one of the workspace projects', async () => {
let [{ manifest }] = await mutateModules([
{
buildIndex: 0,
manifest: {
name: 'project-1',
version: '1.0.0',
dependencies: {
'@pnpm.e2e/bar': '100.0.0',
},
},
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
buildIndex: 1,
manifest: {
name: 'project-2',
version: '1.0.0',
dependencies: {
'@pnpm.e2e/foobarqar': '1.0.0',
},
},
mutation: 'install',
rootDir: path.resolve('project-2'),
},
], await testDefaults({ nodeLinker: 'hoisted' }))
], await testDefaults({
allProjects: [
{
buildIndex: 0,
manifest: {
name: 'project-1',
version: '1.0.0',
dependencies: {
'@pnpm.e2e/bar': '100.0.0',
},
},
rootDir: path.resolve('project-1'),
},
{
buildIndex: 1,
manifest: {
name: 'project-2',
version: '1.0.0',
dependencies: {
'@pnpm.e2e/foobarqar': '1.0.0',
},
},
rootDir: path.resolve('project-2'),
},
],
nodeLinker: 'hoisted',
}))
manifest = await addDependenciesToPackage(
manifest,
['is-negative@1.0.0'],
@@ -181,21 +191,18 @@ test('run pre/postinstall scripts. bin files should be linked in a hoisted node_
test('running install scripts in a workspace that has no root project', async () => {
prepareEmpty()
await mutateModules([
{
buildIndex: 0,
manifest: {
name: 'project-1',
version: '1.0.0',
await mutateModulesInSingleProject({
manifest: {
name: 'project-1',
version: '1.0.0',
dependencies: {
'@pnpm.e2e/pre-and-postinstall-scripts-example': '1.0.0',
},
dependencies: {
'@pnpm.e2e/pre-and-postinstall-scripts-example': '1.0.0',
},
mutation: 'install',
rootDir: path.resolve('project-1'),
},
], await testDefaults({ fastUnpack: false, nodeLinker: 'hoisted' }))
mutation: 'install',
rootDir: path.resolve('project-1'),
}, await testDefaults({ fastUnpack: false, nodeLinker: 'hoisted' }))
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js')).toBeTruthy()
})

View File

@@ -5,7 +5,7 @@ import {
import { prepareEmpty } from '@pnpm/prepare'
import {
addDependenciesToPackage,
mutateModules,
mutateModulesInSingleProject,
} from '@pnpm/core'
import sinon from 'sinon'
import { testDefaults } from './../utils'
@@ -20,14 +20,12 @@ test('uninstall package with no dependencies', async () => {
)
const reporter = sinon.spy()
manifest = (await mutateModules([
{
dependencyNames: ['is-negative'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
},
], await testDefaults({ nodeLinker: 'hoisted', save: true, reporter })))[0].manifest
manifest = (await mutateModulesInSingleProject({
dependencyNames: ['is-negative'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
}, await testDefaults({ nodeLinker: 'hoisted', save: true, reporter }))).manifest
expect(reporter.calledWithMatch({
initial: {
@@ -80,14 +78,12 @@ test('uninstall package with dependencies and do not touch other deps', async ()
['is-negative@2.1.0', 'camelcase-keys@3.0.0'],
await testDefaults({ nodeLinker: 'hoisted', save: true })
)
manifest = (await mutateModules([
{
dependencyNames: ['camelcase-keys'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
},
], await testDefaults({ nodeLinker: 'hoisted', pruneStore: true, save: true })))[0].manifest
manifest = (await mutateModulesInSingleProject({
dependencyNames: ['camelcase-keys'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
}, await testDefaults({ nodeLinker: 'hoisted', pruneStore: true, save: true }))).manifest
await project.storeHasNot('camelcase-keys', '3.0.0')
await project.hasNot('camelcase-keys')

View File

@@ -1,6 +1,6 @@
import path from 'path'
import assertProject from '@pnpm/assert-project'
import { addDependenciesToPackage, install, mutateModules, PackageManifest } from '@pnpm/core'
import { addDependenciesToPackage, install, mutateModules, mutateModulesInSingleProject, PackageManifest } from '@pnpm/core'
import { prepareEmpty, preparePackages } from '@pnpm/prepare'
import { addDistTag, REGISTRY_MOCK_PORT } from '@pnpm/registry-mock'
import rimraf from '@zkochan/rimraf'
@@ -221,14 +221,12 @@ test('automatically install root peer dependencies', async () => {
}
// The auto installed peer is not removed when a dependency is removed
await mutateModules([
{
dependencyNames: ['is-odd'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
},
], await testDefaults({ autoInstallPeers: true }))
await mutateModulesInSingleProject({
dependencyNames: ['is-odd'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
}, await testDefaults({ autoInstallPeers: true }))
await project.hasNot('is-odd')
await project.has('is-positive')
await project.has('is-negative')
@@ -251,28 +249,38 @@ test('automatically install peer dependency when it is a dev dependency in anoth
await mutateModules([
{
buildIndex: 0,
manifest: {
name: 'project-1',
devDependencies: {
'is-positive': '1.0.0',
},
},
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
buildIndex: 0,
manifest: {
name: 'project-2',
peerDependencies: {
'is-positive': '1.0.0',
},
},
mutation: 'install',
rootDir: path.resolve('project-2'),
},
], await testDefaults({ autoInstallPeers: true }))
], await testDefaults({
allProjects: [
{
buildIndex: 0,
manifest: {
name: 'project-1',
devDependencies: {
'is-positive': '1.0.0',
},
},
rootDir: path.resolve('project-1'),
},
{
buildIndex: 0,
manifest: {
name: 'project-2',
peerDependencies: {
'is-positive': '1.0.0',
},
},
rootDir: path.resolve('project-2'),
},
],
autoInstallPeers: true,
}))
const project = assertProject(process.cwd())
const lockfile = await project.readLockfile()
@@ -289,57 +297,77 @@ test('auto install peer deps in a workspace. test #1', async () => {
prepareEmpty()
await mutateModules([
{
buildIndex: 0,
manifest: {
name: 'root-project',
devDependencies: {
'@pnpm.e2e/abc-parent-with-ab': '1.0.0',
},
},
mutation: 'install',
rootDir: process.cwd(),
},
{
buildIndex: 0,
manifest: {
name: 'project',
peerDependencies: {
'@pnpm.e2e/abc-parent-with-ab': '1.0.0',
},
},
mutation: 'install',
rootDir: path.resolve('project'),
},
], await testDefaults({ autoInstallPeers: true }))
], await testDefaults({
autoInstallPeers: true,
allProjects: [
{
buildIndex: 0,
manifest: {
name: 'root-project',
devDependencies: {
'@pnpm.e2e/abc-parent-with-ab': '1.0.0',
},
},
rootDir: process.cwd(),
},
{
buildIndex: 0,
manifest: {
name: 'project',
peerDependencies: {
'@pnpm.e2e/abc-parent-with-ab': '1.0.0',
},
},
rootDir: path.resolve('project'),
},
],
}))
})
test('auto install peer deps in a workspace. test #2', async () => {
prepareEmpty()
await mutateModules([
{
buildIndex: 0,
manifest: {
name: 'root-project',
devDependencies: {
'@pnpm.e2e/abc-parent-with-ab': '1.0.0',
'@pnpm.e2e/peer-c': '1.0.0',
},
},
mutation: 'install',
rootDir: process.cwd(),
},
{
buildIndex: 0,
manifest: {
name: 'project',
peerDependencies: {
'@pnpm.e2e/abc-parent-with-ab': '1.0.0',
},
},
mutation: 'install',
rootDir: path.resolve('project'),
},
], await testDefaults({ autoInstallPeers: true }))
], await testDefaults({
autoInstallPeers: true,
allProjects: [
{
buildIndex: 0,
manifest: {
name: 'root-project',
devDependencies: {
'@pnpm.e2e/abc-parent-with-ab': '1.0.0',
'@pnpm.e2e/peer-c': '1.0.0',
},
},
rootDir: process.cwd(),
},
{
buildIndex: 0,
manifest: {
name: 'project',
peerDependencies: {
'@pnpm.e2e/abc-parent-with-ab': '1.0.0',
},
},
rootDir: path.resolve('project'),
},
],
}))
})
// This test may be removed if autoInstallPeers will become true by default
@@ -353,140 +381,152 @@ test('installation on a workspace with many complex circular dependencies does n
prepareEmpty()
await mutateModules([
{
buildIndex: 0,
manifest: {
name: 'project1',
dependencies: {
'@angular/common': '14.2.4',
'@angular/core': '14.2.4',
'@angular/forms': '14.2.4',
'@angular/platform-browser': '14.2.4',
'@angular/platform-browser-dynamic': '14.2.4',
'@angular/router': '14.2.4',
'@capacitor/app': '4.0.1',
'@capacitor/core': '4.3.0',
'@capacitor/haptics': '4.0.1',
'@capacitor/keyboard': '4.0.1',
'@capacitor/status-bar': '4.0.1',
'@ionic/angular': '6.2.9',
'@ionic/core': '6.2.9',
ionicons: '6.0.3',
'ng-particles': '3.3.3',
rxjs: '7.5.7',
tslib: '2.4.0',
tsparticles: '2.3.4',
'tsparticles-engine': '2.3.3',
'tsparticles-interaction-external-attract': '2.3.3',
'tsparticles-interaction-external-bounce': '2.3.3',
'tsparticles-interaction-external-bubble': '2.3.3',
'tsparticles-interaction-external-connect': '2.3.3',
'tsparticles-interaction-external-grab': '2.3.3',
'tsparticles-interaction-external-pause': '2.3.3',
'tsparticles-interaction-external-push': '2.3.3',
'tsparticles-interaction-external-remove': '2.3.3',
'tsparticles-interaction-external-repulse': '2.3.4',
'tsparticles-interaction-external-slow': '2.3.3',
'tsparticles-interaction-external-trail': '2.3.3',
'tsparticles-interaction-particles-attract': '2.3.3',
'tsparticles-interaction-particles-collisions': '2.3.3',
'tsparticles-interaction-particles-links': '2.3.3',
'tsparticles-move-base': '2.3.3',
'tsparticles-move-parallax': '2.3.3',
'tsparticles-particles.js': '2.3.3',
'tsparticles-plugin-absorbers': '2.3.4',
'tsparticles-plugin-emitters': '2.3.4',
'tsparticles-plugin-polygon-mask': '2.3.3',
'tsparticles-shape-circle': '2.3.3',
'tsparticles-shape-image': '2.3.3',
'tsparticles-shape-line': '2.3.3',
'tsparticles-shape-polygon': '2.3.3',
'tsparticles-shape-square': '2.3.3',
'tsparticles-shape-star': '2.3.3',
'tsparticles-shape-text': '2.3.3',
'tsparticles-slim': '2.3.4',
'tsparticles-updater-angle': '2.3.3',
'tsparticles-updater-color': '2.3.3',
'tsparticles-updater-destroy': '2.3.3',
'tsparticles-updater-life': '2.3.3',
'tsparticles-updater-opacity': '2.3.3',
'tsparticles-updater-out-modes': '2.3.3',
'tsparticles-updater-roll': '2.3.3',
'tsparticles-updater-size': '2.3.3',
'tsparticles-updater-stroke-color': '2.3.3',
'tsparticles-updater-tilt': '2.3.3',
'tsparticles-updater-twinkle': '2.3.3',
'tsparticles-updater-wobble': '2.3.3',
'zone.js': '0.11.8',
},
devDependencies: {
'@angular-devkit/build-angular': '14.2.4',
'@angular-eslint/builder': '14.1.2',
'@angular-eslint/eslint-plugin': '14.1.2',
'@angular-eslint/eslint-plugin-template': '14.1.2',
'@angular-eslint/template-parser': '14.1.2',
'@angular/cli': '14.2.4',
'@angular/compiler': '14.2.4',
'@angular/compiler-cli': '14.2.4',
'@angular/language-service': '14.2.4',
'@capacitor/cli': '4.3.0',
'@ionic/angular-toolkit': '7.0.0',
'@types/jasmine': '4.3.0',
'@types/jasminewd2': '2.0.10',
'@types/node': '18.7.23',
'@typescript-eslint/eslint-plugin': '5.38.1',
'@typescript-eslint/parser': '5.38.1',
eslint: '8.24.0',
'eslint-plugin-import': '2.26.0',
'eslint-plugin-jsdoc': '39.3.6',
'eslint-plugin-prefer-arrow': '1.2.3',
'jasmine-core': '4.4.0',
'jasmine-spec-reporter': '7.0.0',
karma: '6.4.1',
'karma-chrome-launcher': '3.1.1',
'karma-coverage': '2.2.0',
'karma-coverage-istanbul-reporter': '3.0.3',
'karma-jasmine': '5.1.0',
'karma-jasmine-html-reporter': '2.0.0',
protractor: '7.0.0',
'ts-node': '10.9.1',
typescript: '4.8.4',
},
},
mutation: 'install',
rootDir: path.resolve('project1'),
},
{
buildIndex: 0,
manifest: {
name: 'project2',
devDependencies: {
'@typescript-eslint/eslint-plugin': '5.38.1',
'@typescript-eslint/parser': '5.38.1',
copyfiles: '2.4.1',
enzyme: '3.11.0',
'enzyme-adapter-preact-pure': '4.0.1',
eslint: '8.24.0',
'eslint-config-preact': '1.3.0',
'eslint-config-prettier': '8.5.0',
'eslint-plugin-react-hooks': '4.6.0',
'identity-obj-proxy': '3.0.0',
'preact-cli': '3.4.1',
prettier: '2.7.1',
'sirv-cli': '2.0.2',
},
dependencies: {
preact: '10.11.0',
'preact-particles': '2.3.3',
'preact-render-to-string': '5.2.4',
'preact-router': '4.1.0',
tsparticles: '2.3.4',
'tsparticles-engine': '2.3.3',
},
},
mutation: 'install',
rootDir: path.resolve('project2'),
},
], await testDefaults({ autoInstallPeers: true, ignoreScripts: true, lockfileOnly: true }))
], await testDefaults({
autoInstallPeers: true,
ignoreScripts: true,
lockfileOnly: true,
allProjects: [
{
buildIndex: 0,
manifest: {
name: 'project1',
dependencies: {
'@angular/common': '14.2.4',
'@angular/core': '14.2.4',
'@angular/forms': '14.2.4',
'@angular/platform-browser': '14.2.4',
'@angular/platform-browser-dynamic': '14.2.4',
'@angular/router': '14.2.4',
'@capacitor/app': '4.0.1',
'@capacitor/core': '4.3.0',
'@capacitor/haptics': '4.0.1',
'@capacitor/keyboard': '4.0.1',
'@capacitor/status-bar': '4.0.1',
'@ionic/angular': '6.2.9',
'@ionic/core': '6.2.9',
ionicons: '6.0.3',
'ng-particles': '3.3.3',
rxjs: '7.5.7',
tslib: '2.4.0',
tsparticles: '2.3.4',
'tsparticles-engine': '2.3.3',
'tsparticles-interaction-external-attract': '2.3.3',
'tsparticles-interaction-external-bounce': '2.3.3',
'tsparticles-interaction-external-bubble': '2.3.3',
'tsparticles-interaction-external-connect': '2.3.3',
'tsparticles-interaction-external-grab': '2.3.3',
'tsparticles-interaction-external-pause': '2.3.3',
'tsparticles-interaction-external-push': '2.3.3',
'tsparticles-interaction-external-remove': '2.3.3',
'tsparticles-interaction-external-repulse': '2.3.4',
'tsparticles-interaction-external-slow': '2.3.3',
'tsparticles-interaction-external-trail': '2.3.3',
'tsparticles-interaction-particles-attract': '2.3.3',
'tsparticles-interaction-particles-collisions': '2.3.3',
'tsparticles-interaction-particles-links': '2.3.3',
'tsparticles-move-base': '2.3.3',
'tsparticles-move-parallax': '2.3.3',
'tsparticles-particles.js': '2.3.3',
'tsparticles-plugin-absorbers': '2.3.4',
'tsparticles-plugin-emitters': '2.3.4',
'tsparticles-plugin-polygon-mask': '2.3.3',
'tsparticles-shape-circle': '2.3.3',
'tsparticles-shape-image': '2.3.3',
'tsparticles-shape-line': '2.3.3',
'tsparticles-shape-polygon': '2.3.3',
'tsparticles-shape-square': '2.3.3',
'tsparticles-shape-star': '2.3.3',
'tsparticles-shape-text': '2.3.3',
'tsparticles-slim': '2.3.4',
'tsparticles-updater-angle': '2.3.3',
'tsparticles-updater-color': '2.3.3',
'tsparticles-updater-destroy': '2.3.3',
'tsparticles-updater-life': '2.3.3',
'tsparticles-updater-opacity': '2.3.3',
'tsparticles-updater-out-modes': '2.3.3',
'tsparticles-updater-roll': '2.3.3',
'tsparticles-updater-size': '2.3.3',
'tsparticles-updater-stroke-color': '2.3.3',
'tsparticles-updater-tilt': '2.3.3',
'tsparticles-updater-twinkle': '2.3.3',
'tsparticles-updater-wobble': '2.3.3',
'zone.js': '0.11.8',
},
devDependencies: {
'@angular-devkit/build-angular': '14.2.4',
'@angular-eslint/builder': '14.1.2',
'@angular-eslint/eslint-plugin': '14.1.2',
'@angular-eslint/eslint-plugin-template': '14.1.2',
'@angular-eslint/template-parser': '14.1.2',
'@angular/cli': '14.2.4',
'@angular/compiler': '14.2.4',
'@angular/compiler-cli': '14.2.4',
'@angular/language-service': '14.2.4',
'@capacitor/cli': '4.3.0',
'@ionic/angular-toolkit': '7.0.0',
'@types/jasmine': '4.3.0',
'@types/jasminewd2': '2.0.10',
'@types/node': '18.7.23',
'@typescript-eslint/eslint-plugin': '5.38.1',
'@typescript-eslint/parser': '5.38.1',
eslint: '8.24.0',
'eslint-plugin-import': '2.26.0',
'eslint-plugin-jsdoc': '39.3.6',
'eslint-plugin-prefer-arrow': '1.2.3',
'jasmine-core': '4.4.0',
'jasmine-spec-reporter': '7.0.0',
karma: '6.4.1',
'karma-chrome-launcher': '3.1.1',
'karma-coverage': '2.2.0',
'karma-coverage-istanbul-reporter': '3.0.3',
'karma-jasmine': '5.1.0',
'karma-jasmine-html-reporter': '2.0.0',
protractor: '7.0.0',
'ts-node': '10.9.1',
typescript: '4.8.4',
},
},
rootDir: path.resolve('project1'),
},
{
buildIndex: 0,
manifest: {
name: 'project2',
devDependencies: {
'@typescript-eslint/eslint-plugin': '5.38.1',
'@typescript-eslint/parser': '5.38.1',
copyfiles: '2.4.1',
enzyme: '3.11.0',
'enzyme-adapter-preact-pure': '4.0.1',
eslint: '8.24.0',
'eslint-config-preact': '1.3.0',
'eslint-config-prettier': '8.5.0',
'eslint-plugin-react-hooks': '4.6.0',
'identity-obj-proxy': '3.0.0',
'preact-cli': '3.4.1',
prettier: '2.7.1',
'sirv-cli': '2.0.2',
},
dependencies: {
preact: '10.11.0',
'preact-particles': '2.3.3',
'preact-render-to-string': '5.2.4',
'preact-router': '4.1.0',
tsparticles: '2.3.4',
'tsparticles-engine': '2.3.3',
},
},
rootDir: path.resolve('project2'),
},
],
}))
})
test('do not override the direct dependency with an auto installed peer dependency', async () => {

View File

@@ -79,35 +79,14 @@ test('--fix-lockfile should preserve all locked dependencies version', async ()
const importers: MutatedProject[] = [
{
buildIndex: 0,
manifest: {
name: 'root',
version: '1.0.0',
},
mutation: 'install',
rootDir: path.resolve('.'),
},
{
buildIndex: 0,
manifest: {
name: 'project-1',
version: '1.0.0',
dependencies: {
'@babel/runtime-corejs3': '7.15.3',
},
},
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
buildIndex: 0,
manifest: {
name: 'project-3',
version: '1.0.0',
dependencies: {
'@babel/runtime-corejs3': '7.15.4',
},
},
mutation: 'install',
rootDir: path.resolve('project-2'),
},
@@ -177,7 +156,42 @@ test('--fix-lockfile should preserve all locked dependencies version', async ()
},
}, { lineWidth: 1000 })
await mutateModules(importers, await testDefaults({ fixLockfile: true, lockfileOnly: true }))
await mutateModules(importers, await testDefaults({
fixLockfile: true,
lockfileOnly: true,
allProjects: [
{
buildIndex: 0,
manifest: {
name: 'root',
version: '1.0.0',
},
rootDir: path.resolve('.'),
},
{
buildIndex: 0,
manifest: {
name: 'project-1',
version: '1.0.0',
dependencies: {
'@babel/runtime-corejs3': '7.15.3',
},
},
rootDir: path.resolve('project-1'),
},
{
buildIndex: 0,
manifest: {
name: 'project-3',
version: '1.0.0',
dependencies: {
'@babel/runtime-corejs3': '7.15.4',
},
},
rootDir: path.resolve('project-2'),
},
],
}))
const lockfile: Lockfile = await readYamlFile(WANTED_LOCKFILE)

View File

@@ -53,40 +53,49 @@ test(`frozen-lockfile: fail on a shared ${WANTED_LOCKFILE} that does not satisfy
const projects: MutatedProject[] = [
{
buildIndex: 0,
manifest: {
name: 'p1',
dependencies: {
'is-positive': '^3.0.0',
},
},
mutation: 'install',
rootDir: path.resolve('p1'),
},
{
buildIndex: 0,
manifest: {
name: 'p2',
dependencies: {
'is-negative': '1.0.0',
},
},
mutation: 'install',
rootDir: path.resolve('p2'),
},
]
await mutateModules(projects, await testDefaults())
const project1 = {
buildIndex: 0,
manifest: {
name: 'p1',
projects[0].manifest = {
dependencies: {
'is-positive': '^3.0.0',
},
},
rootDir: path.resolve('p1'),
}
const project2 = {
buildIndex: 0,
manifest: {
name: 'p2',
dependencies: {
'is-negative': '1.0.0',
},
},
rootDir: path.resolve('p2'),
}
await mutateModules(projects, await testDefaults({
allProjects: [project1, project2],
}))
project1.manifest = {
...project1.manifest,
dependencies: {
'is-positive': '^3.1.0',
},
}
await expect(
mutateModules(projects, await testDefaults({ frozenLockfile: true }))
mutateModules(projects, await testDefaults({ frozenLockfile: true, allProjects: [project1, project2] }))
).rejects.toThrow(`Cannot install with "frozen-lockfile" because ${WANTED_LOCKFILE} is not up to date with p1${path.sep}package.json`)
})
@@ -227,6 +236,16 @@ test('prefer-frozen-lockfile: should prefer frozen-lockfile when package has lin
])
const mutatedProjects: MutatedProject[] = [
{
mutation: 'install',
rootDir: path.resolve('p1'),
},
{
mutation: 'install',
rootDir: path.resolve('p2'),
},
]
const allProjects = [
{
buildIndex: 0,
manifest: {
@@ -236,7 +255,6 @@ test('prefer-frozen-lockfile: should prefer frozen-lockfile when package has lin
p2: 'link:../p2',
},
},
mutation: 'install',
rootDir: path.resolve('p1'),
},
{
@@ -248,14 +266,14 @@ test('prefer-frozen-lockfile: should prefer frozen-lockfile when package has lin
'is-negative': '1.0.0',
},
},
mutation: 'install',
rootDir: path.resolve('p2'),
},
]
await mutateModules(mutatedProjects, await testDefaults())
await mutateModules(mutatedProjects, await testDefaults({ allProjects }))
const reporter = sinon.spy()
await mutateModules(mutatedProjects, await testDefaults({
allProjects,
preferFrozenLockfile: true,
reporter,
}))

View File

@@ -90,24 +90,35 @@ test('install a workspace with git-branch-lockfile = true', async () => {
const opts = await testDefaults({
useGitBranchLockfile: true,
allProjects: [
{
buildIndex: 0,
manifest: rootManifest,
rootDir: process.cwd(),
},
{
buildIndex: 0,
manifest: project1Manifest,
rootDir: path.resolve('project-1'),
},
{
buildIndex: 0,
manifest: project2Manifest,
rootDir: path.resolve('project-2'),
},
],
})
await mutateModules([
{
buildIndex: 0,
manifest: rootManifest,
mutation: 'install',
rootDir: process.cwd(),
},
{
buildIndex: 0,
manifest: project1Manifest,
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
buildIndex: 0,
manifest: project2Manifest,
mutation: 'install',
rootDir: path.resolve('project-2'),
},

View File

@@ -7,6 +7,7 @@ import {
install,
MutatedProject,
mutateModules,
mutateModulesInSingleProject,
} from '@pnpm/core'
import rimraf from '@zkochan/rimraf'
import resolveLinkTarget from 'resolve-link-target'
@@ -85,14 +86,12 @@ test('should remove hoisted dependencies', async () => {
const project = prepareEmpty()
const manifest = await addDependenciesToPackage({}, ['express'], await testDefaults({ fastUnpack: false, hoistPattern: '*' }))
await mutateModules([
{
dependencyNames: ['express'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
},
], await testDefaults({ hoistPattern: '*' }))
await mutateModulesInSingleProject({
dependencyNames: ['express'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
}, await testDefaults({ hoistPattern: '*' }))
await project.hasNot('express')
await project.hasNot('.pnpm/node_modules/debug')
@@ -116,14 +115,12 @@ test('should rehoist when uninstalling a package', async () => {
// this installs debug@3.1.0 and express@4.16.0
const manifest = await addDependenciesToPackage({}, ['debug@3.1.0', 'express@4.16.0'], await testDefaults({ fastUnpack: false, hoistPattern: '*' }))
// uninstall debug@3.1.0 to check if debug@2.6.9 gets reflattened
await mutateModules([
{
dependencyNames: ['debug'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
},
], await testDefaults({ hoistPattern: '*' }))
await mutateModulesInSingleProject({
dependencyNames: ['debug'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
}, await testDefaults({ hoistPattern: '*' }))
expect(project.requireModule('.pnpm/node_modules/debug/package.json').version).toEqual('2.6.9')
expect(project.requireModule('express/package.json').version).toEqual('4.16.0')
@@ -226,14 +223,12 @@ test('should remove aliased hoisted dependencies', async () => {
const project = prepareEmpty()
const manifest = await addDependenciesToPackage({}, ['@pnpm.e2e/pkg-with-1-aliased-dep'], await testDefaults({ hoistPattern: '*' }))
await mutateModules([
{
dependencyNames: ['@pnpm.e2e/pkg-with-1-aliased-dep'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
},
], await testDefaults({ hoistPattern: '*' }))
await mutateModulesInSingleProject({
dependencyNames: ['@pnpm.e2e/pkg-with-1-aliased-dep'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
}, await testDefaults({ hoistPattern: '*' }))
await project.hasNot('@pnpm.e2e/pkg-with-1-aliased-dep')
await project.hasNot('@pnpm.e2e/dep-of-pkg-with-1-dep')
@@ -305,14 +300,12 @@ test('should hoist correctly peer dependencies', async () => {
test('should uninstall correctly peer dependencies', async () => {
prepareEmpty()
const manifest = await addDependenciesToPackage({}, ['@pnpm.e2e/using-ajv'], await testDefaults({ hoistPattern: '*' }))
await mutateModules([
{
dependencyNames: ['@pnpm.e2e/using-ajv'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
},
], await testDefaults({ hoistPattern: '*' }))
await mutateModulesInSingleProject({
dependencyNames: ['@pnpm.e2e/using-ajv'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
}, await testDefaults({ hoistPattern: '*' }))
// symlink to peer dependency is deleted
expect(() => fs.lstatSync('node_modules/ajv-keywords')).toThrow()
@@ -345,20 +338,28 @@ test('hoist-pattern: hoist all dependencies to the virtual store node_modules',
])
const mutatedProjects: MutatedProject[] = [
{
mutation: 'install',
rootDir: process.cwd(),
},
{
mutation: 'install',
rootDir: path.resolve('package'),
},
]
const allProjects = [
{
buildIndex: 0,
manifest: workspaceRootManifest,
mutation: 'install',
rootDir: process.cwd(),
},
{
buildIndex: 0,
manifest: workspacePackageManifest,
mutation: 'install',
rootDir: path.resolve('package'),
},
]
await mutateModules(mutatedProjects, await testDefaults({ hoistPattern: '*' }))
await mutateModules(mutatedProjects, await testDefaults({ allProjects, hoistPattern: '*' }))
await projects['root'].has('@pnpm.e2e/pkg-with-1-dep')
await projects['root'].has('.pnpm/node_modules/@pnpm.e2e/dep-of-pkg-with-1-dep')
@@ -376,7 +377,7 @@ test('hoist-pattern: hoist all dependencies to the virtual store node_modules',
await rimraf('node_modules')
await rimraf('package/node_modules')
await mutateModules(mutatedProjects, await testDefaults({ frozenLockfile: true, hoistPattern: '*' }))
await mutateModules(mutatedProjects, await testDefaults({ allProjects, frozenLockfile: true, hoistPattern: '*' }))
await projects['root'].has('@pnpm.e2e/pkg-with-1-dep')
await projects['root'].has('.pnpm/node_modules/@pnpm.e2e/dep-of-pkg-with-1-dep')
@@ -421,20 +422,28 @@ test('hoist when updating in one of the workspace projects', async () => {
])
const mutatedProjects: MutatedProject[] = [
{
mutation: 'install',
rootDir: process.cwd(),
},
{
mutation: 'install',
rootDir: path.resolve('package'),
},
]
const allProjects = [
{
buildIndex: 0,
manifest: workspaceRootManifest,
mutation: 'install',
rootDir: process.cwd(),
},
{
buildIndex: 0,
manifest: workspacePackageManifest,
mutation: 'install',
rootDir: path.resolve('package'),
},
]
await mutateModules(mutatedProjects, await testDefaults({ hoistPattern: '*' }))
await mutateModules(mutatedProjects, await testDefaults({ allProjects, hoistPattern: '*' }))
const rootModules = assertProject(process.cwd())
{
@@ -451,7 +460,7 @@ test('hoist when updating in one of the workspace projects', async () => {
dependencySelectors: ['@pnpm.e2e/foo@100.1.0'],
mutation: 'installSome',
},
], await testDefaults({ hoistPattern: '*', pruneLockfileImporters: false }))
], await testDefaults({ allProjects, hoistPattern: '*', pruneLockfileImporters: false }))
const lockfile = await rootModules.readCurrentLockfile()
@@ -485,14 +494,11 @@ test('should recreate node_modules with hoisting', async () => {
expect(modulesManifest?.hoistedDependencies).toStrictEqual({})
}
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ hoistPattern: '*' }))
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ hoistPattern: '*' }))
await project.has('@pnpm.e2e/pkg-with-1-dep')
await project.has('.pnpm/node_modules/@pnpm.e2e/dep-of-pkg-with-1-dep')
@@ -567,13 +573,22 @@ test('hoist packages which is in the dependencies tree of the selected projects'
])
const importers: MutatedProject[] = [
{
mutation: 'install',
rootDir: path.resolve('.'),
},
{
mutation: 'install',
rootDir: path.resolve('project-2'),
},
]
const allProjects = [
{
buildIndex: 0,
manifest: {
name: 'root',
version: '1.0.0',
},
mutation: 'install',
rootDir: path.resolve('.'),
},
{
@@ -585,7 +600,6 @@ test('hoist packages which is in the dependencies tree of the selected projects'
'is-positive': '3.0.0',
},
},
mutation: 'install',
rootDir: path.resolve('project-2'),
},
]
@@ -632,7 +646,7 @@ test('hoist packages which is in the dependencies tree of the selected projects'
},
}, { lineWidth: 1000 })
await mutateModules(importers, await testDefaults({ hoistPattern: '*' }))
await mutateModules(importers, await testDefaults({ allProjects, hoistPattern: '*' }))
await root.has('.pnpm/node_modules/is-positive')
const { version } = root.requireModule('.pnpm/node_modules/is-positive/package.json')
@@ -655,21 +669,6 @@ test('only hoist packages which is in the dependencies tree of the selected proj
},
])
const importers: MutatedProject[] = [
{
buildIndex: 0,
manifest: {
name: 'project-2',
version: '1.0.0',
dependencies: {
'@babel/runtime-corejs3': '7.15.4',
},
},
mutation: 'install',
rootDir: path.resolve('project-2'),
},
]
await writeYamlFile(WANTED_LOCKFILE, {
lockfileVersion: 5.3,
importers: {
@@ -729,7 +728,17 @@ test('only hoist packages which is in the dependencies tree of the selected proj
},
}, { lineWidth: 1000 })
await mutateModules(importers, await testDefaults({ hoistPattern: '*' }))
await mutateModulesInSingleProject({
manifest: {
name: 'project-2',
version: '1.0.0',
dependencies: {
'@babel/runtime-corejs3': '7.15.4',
},
},
mutation: 'install',
rootDir: path.resolve('project-2'),
}, await testDefaults({ hoistPattern: '*' }))
await root.has('.pnpm/node_modules/@babel/runtime-corejs3')
const { version: runtimeVersion } = root.requireModule('.pnpm/node_modules/@babel/runtime-corejs3/package.json')

View File

@@ -68,22 +68,33 @@ test('inject local packages', async () => {
])
const importers: MutatedProject[] = [
{
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
mutation: 'install',
rootDir: path.resolve('project-2'),
},
{
mutation: 'install',
rootDir: path.resolve('project-3'),
},
]
const allProjects = [
{
buildIndex: 0,
manifest: project1Manifest,
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
buildIndex: 0,
manifest: project2Manifest,
mutation: 'install',
rootDir: path.resolve('project-2'),
},
{
buildIndex: 0,
manifest: project3Manifest,
mutation: 'install',
rootDir: path.resolve('project-3'),
},
]
@@ -108,6 +119,7 @@ test('inject local packages', async () => {
},
}
await mutateModules(importers, await testDefaults({
allProjects,
workspacePackages,
}))
@@ -175,6 +187,7 @@ test('inject local packages', async () => {
await rimraf('project-3/node_modules')
await mutateModules(importers, await testDefaults({
allProjects,
frozenLockfile: true,
workspacePackages,
}))
@@ -192,8 +205,8 @@ test('inject local packages', async () => {
expect(fs.readdirSync('node_modules/.pnpm').length).toBe(8)
// The injected project is updated when one of its dependencies needs to be updated
importers[0].manifest.dependencies!['is-negative'] = '2.0.0'
await mutateModules(importers, await testDefaults({ workspacePackages }))
allProjects[0].manifest.dependencies!['is-negative'] = '2.0.0'
await mutateModules(importers, await testDefaults({ allProjects, workspacePackages }))
{
const lockfile = await rootModules.readLockfile()
expect(lockfile.importers['project-2'].dependenciesMeta).toEqual({
@@ -285,22 +298,33 @@ test('inject local packages declared via file protocol', async () => {
])
const importers: MutatedProject[] = [
{
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
mutation: 'install',
rootDir: path.resolve('project-2'),
},
{
mutation: 'install',
rootDir: path.resolve('project-3'),
},
]
const allProjects = [
{
buildIndex: 0,
manifest: project1Manifest,
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
buildIndex: 0,
manifest: project2Manifest,
mutation: 'install',
rootDir: path.resolve('project-2'),
},
{
buildIndex: 0,
manifest: project3Manifest,
mutation: 'install',
rootDir: path.resolve('project-3'),
},
]
@@ -325,6 +349,7 @@ test('inject local packages declared via file protocol', async () => {
},
}
await mutateModules(importers, await testDefaults({
allProjects,
workspacePackages,
}))
@@ -392,6 +417,7 @@ test('inject local packages declared via file protocol', async () => {
await rimraf('project-3/node_modules')
await mutateModules(importers, await testDefaults({
allProjects,
frozenLockfile: true,
workspacePackages,
}))
@@ -409,9 +435,9 @@ test('inject local packages declared via file protocol', async () => {
expect(fs.readdirSync('node_modules/.pnpm').length).toBe(8)
// The injected project is updated when one of its dependencies needs to be updated
importers[0].manifest.dependencies!['is-negative'] = '2.0.0'
writeJsonFile('project-1/package.json', importers[0].manifest)
await mutateModules(importers, await testDefaults({ workspacePackages }))
allProjects[0].manifest.dependencies!['is-negative'] = '2.0.0'
writeJsonFile('project-1/package.json', allProjects[0].manifest)
await mutateModules(importers, await testDefaults({ allProjects, workspacePackages }))
{
const lockfile = await rootModules.readLockfile()
expect(lockfile.importers['project-2'].dependenciesMeta).toEqual({
@@ -493,22 +519,33 @@ test('inject local packages when the file protocol is used', async () => {
])
const importers: MutatedProject[] = [
{
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
mutation: 'install',
rootDir: path.resolve('project-2'),
},
{
mutation: 'install',
rootDir: path.resolve('project-3'),
},
]
const allProjects = [
{
buildIndex: 0,
manifest: project1Manifest,
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
buildIndex: 0,
manifest: project2Manifest,
mutation: 'install',
rootDir: path.resolve('project-2'),
},
{
buildIndex: 0,
manifest: project3Manifest,
mutation: 'install',
rootDir: path.resolve('project-3'),
},
]
@@ -533,6 +570,7 @@ test('inject local packages when the file protocol is used', async () => {
},
}
await mutateModules(importers, await testDefaults({
allProjects,
workspacePackages,
}))
@@ -595,6 +633,7 @@ test('inject local packages when the file protocol is used', async () => {
await rimraf('project-3/node_modules')
await mutateModules(importers, await testDefaults({
allProjects,
frozenLockfile: true,
workspacePackages,
}))
@@ -612,9 +651,12 @@ test('inject local packages when the file protocol is used', async () => {
expect(fs.readdirSync('node_modules/.pnpm').length).toBe(8)
// The injected project is updated when one of its dependencies needs to be updated
importers[0].manifest.dependencies!['is-negative'] = '2.0.0'
writeJsonFile('project-1/package.json', importers[0].manifest)
await mutateModules(importers, await testDefaults({ workspacePackages }))
allProjects[0].manifest.dependencies!['is-negative'] = '2.0.0'
writeJsonFile('project-1/package.json', allProjects[0].manifest)
await mutateModules(importers, await testDefaults({
allProjects,
workspacePackages,
}))
{
const lockfile = await rootModules.readLockfile()
expect(lockfile.packages['file:project-1_is-positive@1.0.0']).toEqual({
@@ -683,16 +725,24 @@ test('inject local packages and relink them after build', async () => {
])
const importers: MutatedProject[] = [
{
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
mutation: 'install',
rootDir: path.resolve('project-2'),
},
]
const allProjects = [
{
buildIndex: 0,
manifest: project1Manifest,
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
buildIndex: 0,
manifest: project2Manifest,
mutation: 'install',
rootDir: path.resolve('project-2'),
},
]
@@ -711,6 +761,7 @@ test('inject local packages and relink them after build', async () => {
},
}
await mutateModules(importers, await testDefaults({
allProjects,
workspacePackages,
}))
@@ -754,6 +805,7 @@ test('inject local packages and relink them after build', async () => {
await rimraf('project-2/node_modules')
await mutateModules(importers, await testDefaults({
allProjects,
frozenLockfile: true,
workspacePackages,
}))
@@ -805,20 +857,28 @@ test('inject local packages and relink them after build (file protocol is used)'
])
const importers: MutatedProject[] = [
{
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
mutation: 'install',
rootDir: path.resolve('project-2'),
},
]
const allProjects = [
{
buildIndex: 0,
manifest: project1Manifest,
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
buildIndex: 0,
manifest: project2Manifest,
mutation: 'install',
rootDir: path.resolve('project-2'),
},
]
await mutateModules(importers, await testDefaults())
await mutateModules(importers, await testDefaults({ allProjects }))
await projects['project-1'].has('is-negative')
await projects['project-1'].has('@pnpm.e2e/dep-of-pkg-with-1-dep')
@@ -855,6 +915,7 @@ test('inject local packages and relink them after build (file protocol is used)'
await rimraf('project-2/node_modules')
await mutateModules(importers, await testDefaults({
allProjects,
frozenLockfile: true,
}))
@@ -927,22 +988,33 @@ test('inject local packages when node-linker is hoisted', async () => {
])
const importers: MutatedProject[] = [
{
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
mutation: 'install',
rootDir: path.resolve('project-2'),
},
{
mutation: 'install',
rootDir: path.resolve('project-3'),
},
]
const allProjects = [
{
buildIndex: 0,
manifest: project1Manifest,
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
buildIndex: 0,
manifest: project2Manifest,
mutation: 'install',
rootDir: path.resolve('project-2'),
},
{
buildIndex: 0,
manifest: project3Manifest,
mutation: 'install',
rootDir: path.resolve('project-3'),
},
]
@@ -967,6 +1039,7 @@ test('inject local packages when node-linker is hoisted', async () => {
},
}
await mutateModules(importers, await testDefaults({
allProjects,
nodeLinker: 'hoisted',
workspacePackages,
}))
@@ -1080,22 +1153,33 @@ test('inject local packages when node-linker is hoisted and dependenciesMeta is
])
const importers: MutatedProject[] = [
{
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
mutation: 'install',
rootDir: path.resolve('project-2'),
},
{
mutation: 'install',
rootDir: path.resolve('project-3'),
},
]
const allProjects = [
{
buildIndex: 0,
manifest: project1Manifest,
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
buildIndex: 0,
manifest: project2Manifest,
mutation: 'install',
rootDir: path.resolve('project-2'),
},
{
buildIndex: 0,
manifest: project3Manifest,
mutation: 'install',
rootDir: path.resolve('project-3'),
},
]
@@ -1120,6 +1204,7 @@ test('inject local packages when node-linker is hoisted and dependenciesMeta is
},
}
await mutateModules(importers, await testDefaults({
allProjects,
nodeLinker: 'hoisted',
workspacePackages,
hooks: {
@@ -1248,22 +1333,33 @@ test('peer dependency of injected project should be resolved correctly', async (
])
const importers: MutatedProject[] = [
{
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
mutation: 'install',
rootDir: path.resolve('project-2'),
},
{
mutation: 'install',
rootDir: path.resolve('project-3'),
},
]
const allProjects = [
{
buildIndex: 0,
manifest: project1Manifest,
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
buildIndex: 0,
manifest: project2Manifest,
mutation: 'install',
rootDir: path.resolve('project-2'),
},
{
buildIndex: 0,
manifest: project3Manifest,
mutation: 'install',
rootDir: path.resolve('project-3'),
},
]
@@ -1288,6 +1384,7 @@ test('peer dependency of injected project should be resolved correctly', async (
},
}
await mutateModules(importers, await testDefaults({
allProjects,
nodeLinker: 'hoisted',
workspacePackages,
}))
@@ -1336,16 +1433,24 @@ test('do not modify the manifest of the injected workpspace project', async () =
])
const importers: MutatedProject[] = [
{
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
mutation: 'install',
rootDir: path.resolve('project-2'),
},
]
const allProjects = [
{
buildIndex: 0,
manifest: project1Manifest,
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
buildIndex: 0,
manifest: project2Manifest,
mutation: 'install',
rootDir: path.resolve('project-2'),
},
]
@@ -1364,6 +1469,7 @@ test('do not modify the manifest of the injected workpspace project', async () =
},
}
const [project1] = await mutateModules(importers, await testDefaults({
allProjects,
workspacePackages,
}))
expect(project1.manifest).toStrictEqual({

View File

@@ -5,7 +5,7 @@ import { prepareEmpty } from '@pnpm/prepare'
import {
addDependenciesToPackage,
install,
mutateModules,
mutateModulesInSingleProject,
} from '@pnpm/core'
import rimraf from '@zkochan/rimraf'
import isWindows from 'is-windows'
@@ -145,22 +145,19 @@ test('INIT_CWD is always set to lockfile directory', async () => {
const rootDir = process.cwd()
await fs.mkdir('subd')
process.chdir('subd')
await mutateModules([
{
buildIndex: 0,
mutation: 'install',
manifest: {
dependencies: {
'json-append': '1.1.1',
'@pnpm.e2e/write-lifecycle-env': '1.0.0',
},
scripts: {
install: 'node -e "process.stdout.write(process.env.INIT_CWD)" | json-append output.json',
},
await mutateModulesInSingleProject({
mutation: 'install',
manifest: {
dependencies: {
'json-append': '1.1.1',
'@pnpm.e2e/write-lifecycle-env': '1.0.0',
},
scripts: {
install: 'node -e "process.stdout.write(process.env.INIT_CWD)" | json-append output.json',
},
rootDir,
},
], await testDefaults({
rootDir,
}, await testDefaults({
fastUnpack: false,
lockfileDir: rootDir,
}))
@@ -297,17 +294,11 @@ test('lifecycle scripts run before linking bins', async () => {
await rimraf('node_modules')
await mutateModules(
[
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
],
await testDefaults({ frozenLockfile: true })
)
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ frozenLockfile: true }))
await project.isExecutable('.bin/cmd1')
await project.isExecutable('.bin/cmd2')
@@ -324,17 +315,11 @@ test('hoisting does not fail on commands that will be created by lifecycle scrip
// Testing the same with headless installation
await rimraf('node_modules')
await mutateModules(
[
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
],
await testDefaults({ frozenLockfile: true, hoistPattern: '*' })
)
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ frozenLockfile: true, hoistPattern: '*' }))
// await project.isExecutable('.pnpm/node_modules/.bin/cmd1')
// await project.isExecutable('.pnpm/node_modules/.bin/cmd2')
@@ -362,17 +347,11 @@ test('bins are linked even if lifecycle scripts are ignored', async () => {
await rimraf('node_modules')
await mutateModules(
[
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
],
await testDefaults({ frozenLockfile: true, ignoreScripts: true })
)
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ frozenLockfile: true, ignoreScripts: true }))
await project.isExecutable('.bin/peer-with-bin')
await project.isExecutable('@pnpm.e2e/pkg-with-peer-having-bin/node_modules/.bin/hello-world-js-bin')
@@ -397,17 +376,11 @@ test('dependency should not be added to current lockfile if it was not built suc
)
await expect(
mutateModules(
[
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
],
await testDefaults({ frozenLockfile: true })
)
mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ frozenLockfile: true }))
).rejects.toThrow()
expect(await project.readCurrentLockfile()).toBeFalsy()
@@ -504,14 +477,11 @@ test('lockfile is updated if neverBuiltDependencies is changed', async () => {
}
const neverBuiltDependencies = ['@pnpm.e2e/pre-and-postinstall-scripts-example']
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ neverBuiltDependencies }))
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ neverBuiltDependencies }))
{
const lockfile = await project.readLockfile()
@@ -536,14 +506,11 @@ test('lockfile is updated if onlyBuiltDependencies is changed', async () => {
}
const onlyBuiltDependencies: string[] = []
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ onlyBuiltDependencies }))
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ onlyBuiltDependencies }))
{
const lockfile = await project.readLockfile()
@@ -553,14 +520,11 @@ test('lockfile is updated if onlyBuiltDependencies is changed', async () => {
}
onlyBuiltDependencies.push('@pnpm.e2e/pre-and-postinstall-scripts-example')
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ onlyBuiltDependencies }))
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ onlyBuiltDependencies }))
{
const lockfile = await project.readLockfile()
@@ -590,31 +554,19 @@ test('lifecycle scripts run after linking root dependencies', async () => {
},
}
await mutateModules(
[
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
],
await testDefaults({ fastUnpack: false })
)
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ fastUnpack: false }))
await rimraf('node_modules')
await mutateModules(
[
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
],
await testDefaults({ fastUnpack: false, frozenLockfile: true })
)
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ fastUnpack: false, frozenLockfile: true }))
// if there was no exception, the test passed
})

View File

@@ -8,7 +8,7 @@ import fixtures from '@pnpm/test-fixtures'
import {
addDependenciesToPackage,
install,
mutateModules,
mutateModulesInSingleProject,
} from '@pnpm/core'
import rimraf from '@zkochan/rimraf'
import normalizePath from 'normalize-path'
@@ -212,14 +212,11 @@ test('do not update deps when installing in a project that has local tarball dep
await addDistTag({ package: '@pnpm.e2e/peer-a', version: '1.0.1', distTag: 'latest' })
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults())
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults())
const latestLockfile = await project.readLockfile()

View File

@@ -4,7 +4,7 @@ import { Lockfile } from '@pnpm/lockfile-file'
import { prepareEmpty } from '@pnpm/prepare'
import fixtures from '@pnpm/test-fixtures'
import readYamlFile from 'read-yaml-file'
import { addDependenciesToPackage, mutateModules } from '@pnpm/core'
import { addDependenciesToPackage, mutateModulesInSingleProject } from '@pnpm/core'
import rimraf from '@zkochan/rimraf'
import { testDefaults } from '../utils'
@@ -45,18 +45,13 @@ test(`tarball location is correctly saved to ${WANTED_LOCKFILE} when a shared ${
f.copy('tar-pkg-with-dep-2/tar-pkg-with-dep-1.0.0.tgz', 'pkg.tgz')
const lockfileDir = path.resolve('..')
const [{ manifest }] = await mutateModules(
[
{
allowNew: true,
dependencySelectors: ['file:pkg.tgz'],
manifest: {},
mutation: 'installSome',
rootDir: process.cwd(),
},
],
await testDefaults({ lockfileDir })
)
const { manifest } = await mutateModulesInSingleProject({
allowNew: true,
dependencySelectors: ['file:pkg.tgz'],
manifest: {},
mutation: 'installSome',
rootDir: process.cwd(),
}, await testDefaults({ lockfileDir }))
const lockfile = await readYamlFile<Lockfile>(path.resolve('..', WANTED_LOCKFILE))
expect(lockfile.packages!['file:project/pkg.tgz']).toBeTruthy()
@@ -64,17 +59,11 @@ test(`tarball location is correctly saved to ${WANTED_LOCKFILE} when a shared ${
await rimraf('node_modules')
await mutateModules(
[
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
],
await testDefaults({ frozenLockfile: true, lockfileDir })
)
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ frozenLockfile: true, lockfileDir }))
await project.has('tar-pkg-with-dep')
})

View File

@@ -16,7 +16,7 @@ import { addDistTag, getIntegrity, REGISTRY_MOCK_PORT } from '@pnpm/registry-moc
import {
addDependenciesToPackage,
install,
mutateModules,
mutateModulesInSingleProject,
UnexpectedStoreError,
UnexpectedVirtualStoreDirError,
} from '@pnpm/core'
@@ -912,14 +912,11 @@ test('do not update deps when lockfile is present', async () => {
await addDistTag({ package: '@pnpm.e2e/peer-a', version: '1.0.1', distTag: 'latest' })
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ preferFrozenLockfile: false }))
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ preferFrozenLockfile: false }))
const latestLockfile = await project.readLockfile()
@@ -929,18 +926,15 @@ test('do not update deps when lockfile is present', async () => {
test('all the subdeps of dependencies are linked when a node_modules is partially up to date', async () => {
prepareEmpty()
await mutateModules([
{
buildIndex: 0,
manifest: {
dependencies: {
'@pnpm.e2e/foobarqar': '1.0.0',
},
await mutateModulesInSingleProject({
manifest: {
dependencies: {
'@pnpm.e2e/foobarqar': '1.0.0',
},
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults())
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults())
await writeYamlFile(path.resolve('pnpm-lock.yaml'), {
dependencies: {
@@ -986,18 +980,15 @@ test('all the subdeps of dependencies are linked when a node_modules is partiall
},
}, { lineWidth: 1000 })
await mutateModules([
{
buildIndex: 0,
manifest: {
dependencies: {
'@pnpm.e2e/foobarqar': '1.0.1',
},
await mutateModulesInSingleProject({
manifest: {
dependencies: {
'@pnpm.e2e/foobarqar': '1.0.1',
},
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ preferFrozenLockfile: false }))
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ preferFrozenLockfile: false }))
expect(
[...await fs.readdir(path.resolve('node_modules/.pnpm/@pnpm.e2e+foobarqar@1.0.1/node_modules/@pnpm.e2e'))].sort()
@@ -1015,18 +1006,15 @@ test('subdep symlinks are updated if the lockfile has new subdep versions specif
await addDistTag({ package: '@pnpm.e2e/dep-of-pkg-with-1-dep', version: '100.0.0', distTag: 'latest' })
const project = prepareEmpty()
await mutateModules([
{
buildIndex: 0,
manifest: {
dependencies: {
'@pnpm.e2e/parent-of-pkg-with-1-dep': '1.0.0',
},
await mutateModulesInSingleProject({
manifest: {
dependencies: {
'@pnpm.e2e/parent-of-pkg-with-1-dep': '1.0.0',
},
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults())
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults())
const lockfile = await project.readLockfile()
@@ -1076,18 +1064,15 @@ test('subdep symlinks are updated if the lockfile has new subdep versions specif
},
}, { lineWidth: 1000 })
await mutateModules([
{
buildIndex: 0,
manifest: {
dependencies: {
'@pnpm.e2e/parent-of-pkg-with-1-dep': '1.0.0',
},
await mutateModulesInSingleProject({
manifest: {
dependencies: {
'@pnpm.e2e/parent-of-pkg-with-1-dep': '1.0.0',
},
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ preferFrozenLockfile: false }))
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ preferFrozenLockfile: false }))
expect(await exists(path.resolve('node_modules/.pnpm/@pnpm.e2e+pkg-with-1-dep@100.0.0/node_modules/@pnpm.e2e/dep-of-pkg-with-1-dep/package.json'))).toBeTruthy()
})
@@ -1097,18 +1082,15 @@ test('fail if none of the available resolvers support a version spec', async ()
let err!: PnpmError
try {
await mutateModules([
{
buildIndex: 0,
manifest: {
dependencies: {
'@types/plotly.js': '1.44.29',
},
await mutateModulesInSingleProject({
manifest: {
dependencies: {
'@types/plotly.js': '1.44.29',
},
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults())
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults())
throw new Error('should have failed')
} catch (_err: any) { // eslint-disable-line
err = _err
@@ -1132,18 +1114,15 @@ test('globally installed package which don\'t have bins should log warning messa
const opts = await testDefaults({ global: true, reporter })
await mutateModules([
{
buildIndex: 0,
manifest: {
dependencies: {
'is-positive': '1.0.0',
},
await mutateModulesInSingleProject({
manifest: {
dependencies: {
'is-positive': '1.0.0',
},
mutation: 'install',
rootDir: process.cwd(),
},
], opts)
mutation: 'install',
rootDir: process.cwd(),
}, opts)
expect(reporter.calledWithMatch({
message: 'is-positive has no binaries',
@@ -1269,19 +1248,16 @@ test('installing dependencies with the same name in different case', async () =>
},
])
await mutateModules([
{
buildIndex: 0,
mutation: 'install',
manifest: {
dependencies: {
File: 'https://registry.npmjs.org/File/-/File-0.10.2.tgz',
file: 'https://registry.npmjs.org/file/-/file-0.2.2.tgz',
},
await mutateModulesInSingleProject({
mutation: 'install',
manifest: {
dependencies: {
File: 'https://registry.npmjs.org/File/-/File-0.10.2.tgz',
file: 'https://registry.npmjs.org/file/-/file-0.2.2.tgz',
},
rootDir: path.resolve('project-1'),
},
], await testDefaults({ fastUnpack: false }))
rootDir: path.resolve('project-1'),
}, await testDefaults({ fastUnpack: false }))
// if it did not fail, it is fine
})
@@ -1289,19 +1265,16 @@ test('installing dependencies with the same name in different case', async () =>
test('two dependencies have the same version and name. The only difference is the casing in the name', async () => {
prepareEmpty()
await mutateModules([
{
buildIndex: 0,
mutation: 'install',
manifest: {
dependencies: {
a: 'npm:JSONStream@1.0.3',
b: 'npm:jsonstream@1.0.3',
},
await mutateModulesInSingleProject({
mutation: 'install',
manifest: {
dependencies: {
a: 'npm:JSONStream@1.0.3',
b: 'npm:jsonstream@1.0.3',
},
rootDir: process.cwd(),
},
], await testDefaults({
rootDir: process.cwd(),
}, await testDefaults({
fastUnpack: false,
registries: {
default: 'https://registry.npmjs.org/',

View File

@@ -4,7 +4,7 @@ import { prepareEmpty } from '@pnpm/prepare'
import {
addDependenciesToPackage,
install,
mutateModules,
mutateModulesInSingleProject,
} from '@pnpm/core'
import { testDefaults } from '../utils'
@@ -22,14 +22,12 @@ test('the modules cache is pruned when it expires', async () => {
expect(modulesFile?.prunedAt).toBeTruthy()
manifest = (await mutateModules([
{
dependencyNames: ['is-negative'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
},
], await testDefaults({})))[0].manifest
manifest = (await mutateModulesInSingleProject({
dependencyNames: ['is-negative'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
}, await testDefaults({}))).manifest
await project.has('.pnpm/is-negative@1.0.0/node_modules/is-negative')
@@ -60,14 +58,12 @@ test('the modules cache is pruned when it expires and headless install is used',
expect(modulesFile?.prunedAt).toBeTruthy()
manifest = (await mutateModules([
{
dependencyNames: ['is-negative'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
},
], await testDefaults({ lockfileOnly: true })))[0].manifest
manifest = (await mutateModulesInSingleProject({
dependencyNames: ['is-negative'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
}, await testDefaults({ lockfileOnly: true }))).manifest
manifest = await install(manifest, await testDefaults({ frozenLockfile: true }))

View File

@@ -52,6 +52,16 @@ test('using different custom modules directory for every project', async () => {
])
const importers: MutatedProject[] = [
{
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
mutation: 'install',
rootDir: path.resolve('project-2'),
},
]
const allProjects = [
{
buildIndex: 0,
manifest: {
@@ -63,7 +73,6 @@ test('using different custom modules directory for every project', async () => {
},
},
modulesDir: 'modules_1',
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
@@ -77,11 +86,10 @@ test('using different custom modules directory for every project', async () => {
},
},
modulesDir: 'modules_2',
mutation: 'install',
rootDir: path.resolve('project-2'),
},
]
await mutateModules(importers, await testDefaults())
await mutateModules(importers, await testDefaults({ allProjects }))
await projects['project-1'].has('is-positive', 'modules_1')
await projects['project-2'].has('is-positive', 'modules_2')

View File

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,7 @@ import {
install,
MutatedProject,
mutateModules,
mutateModulesInSingleProject,
} from '@pnpm/core'
import rimraf from '@zkochan/rimraf'
import exists from 'path-exists'
@@ -115,17 +116,11 @@ test('skip optional dependency that does not support the current OS', async () =
await rimraf('node_modules')
await mutateModules(
[
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
],
await testDefaults({ frozenLockfile: true })
)
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ frozenLockfile: true }))
await project.hasNot('@pnpm.e2e/not-compatible-with-any-os')
await project.has('@pnpm.e2e/dep-of-optional-pkg')
@@ -217,6 +212,16 @@ test('optional subdependency is not removed from current lockfile when new depen
])
const importers: MutatedProject[] = [
{
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
mutation: 'install',
rootDir: path.resolve('project-2'),
},
]
const allProjects = [
{
buildIndex: 0,
manifest: {
@@ -227,7 +232,6 @@ test('optional subdependency is not removed from current lockfile when new depen
'@pnpm.e2e/pkg-with-optional': '1.0.0',
},
},
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
@@ -236,12 +240,11 @@ test('optional subdependency is not removed from current lockfile when new depen
name: 'project-2',
version: '1.0.0',
},
mutation: 'install',
rootDir: path.resolve('project-2'),
},
]
await mutateModules(importers,
await testDefaults({ hoistPattern: ['*'] })
await testDefaults({ allProjects, hoistPattern: ['*'] })
)
{
@@ -258,7 +261,7 @@ test('optional subdependency is not removed from current lockfile when new depen
dependencySelectors: ['is-positive@1.0.0'],
mutation: 'installSome',
},
], await testDefaults({ fastUnpack: false, hoistPattern: ['*'] }))
], await testDefaults({ allProjects, fastUnpack: false, hoistPattern: ['*'] }))
{
const currentLockfile = await readYamlFile<Lockfile>(path.resolve('node_modules/.pnpm/lock.yaml'))
@@ -296,16 +299,11 @@ test('optional subdependency is skipped', async () => {
expect(await exists('pnpm-lock.yaml')).toBeTruthy()
await rimraf('pnpm-lock.yaml')
await mutateModules(
[
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
],
await testDefaults()
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults()
)
const lockfile = await project.readLockfile()
@@ -316,17 +314,11 @@ test('optional subdependency is skipped', async () => {
// forced headless install should install non-compatible optional deps
// TODO: move next case to @pnpm/headless tests
await mutateModules(
[
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
],
await testDefaults({ force: true, frozenLockfile: true })
)
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ force: true, frozenLockfile: true }))
expect(await exists('node_modules/.pnpm/@pnpm.e2e+not-compatible-with-any-os@1.0.0')).toBeTruthy()
@@ -372,19 +364,11 @@ test('only that package is skipped which is an optional dependency only and not
await rimraf('node_modules')
await mutateModules(
[
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
],
await testDefaults({
frozenLockfile: true,
})
)
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ frozenLockfile: true }))
{
const modulesInfo = await readYamlFile<{ skipped: string[] }>(path.join('node_modules', '.modules.yaml'))
@@ -484,53 +468,55 @@ test('skip optional dependency that does not support the current OS, when doing
const [{ manifest }] = await mutateModules(
[
{
buildIndex: 0,
manifest: {
name: 'project1',
version: '1.0.0',
optionalDependencies: {
'@pnpm.e2e/not-compatible-with-any-os': '*',
},
},
mutation: 'install',
rootDir: path.resolve('project1'),
},
{
buildIndex: 0,
manifest: {
name: 'project2',
version: '1.0.0',
dependencies: {
'@pnpm.e2e/pkg-with-1-dep': '100.0.0',
},
},
mutation: 'install',
rootDir: path.resolve('project2'),
},
],
await testDefaults({
allProjects: [
{
buildIndex: 0,
manifest: {
name: 'project1',
version: '1.0.0',
optionalDependencies: {
'@pnpm.e2e/not-compatible-with-any-os': '*',
},
},
rootDir: path.resolve('project1'),
},
{
buildIndex: 0,
manifest: {
name: 'project2',
version: '1.0.0',
dependencies: {
'@pnpm.e2e/pkg-with-1-dep': '100.0.0',
},
},
rootDir: path.resolve('project2'),
},
],
lockfileDir: process.cwd(),
lockfileOnly: true,
})
)
await mutateModules(
[
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: path.resolve('project1'),
},
],
await testDefaults({
frozenLockfile: false,
lockfileDir: process.cwd(),
preferFrozenLockfile: false,
})
)
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: path.resolve('project1'),
}, await testDefaults({
frozenLockfile: false,
lockfileDir: process.cwd(),
preferFrozenLockfile: false,
}))
const modulesInfo = await readYamlFile<{ skipped: string[] }>(path.join('node_modules', '.modules.yaml'))
expect(modulesInfo.skipped).toStrictEqual([

View File

@@ -1,7 +1,7 @@
import PnpmError from '@pnpm/error'
import { prepareEmpty } from '@pnpm/prepare'
import { addDistTag } from '@pnpm/registry-mock'
import { addDependenciesToPackage, mutateModules } from '@pnpm/core'
import { addDependenciesToPackage, mutateModulesInSingleProject } from '@pnpm/core'
import {
testDefaults,
} from '../utils'
@@ -37,27 +37,21 @@ test('versions are replaced with versions specified through overrides option', a
expect(lockfile.overrides).toStrictEqual(currentLockfile.overrides)
}
// shall be able to install when package manifest is ignored
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], { ...await testDefaults(), ignorePackageManifest: true, overrides })
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, { ...await testDefaults(), ignorePackageManifest: true, overrides })
// The lockfile is updated if the overrides are changed
overrides['@pnpm.e2e/bar@^100.0.0'] = '100.0.0'
// A direct dependency may be overriden as well
overrides['@pnpm.e2e/foobarqar'] = '1.0.1'
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ overrides }))
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ overrides }))
{
const lockfile = await project.readLockfile()
@@ -74,14 +68,11 @@ test('versions are replaced with versions specified through overrides option', a
expect(lockfile.overrides).toStrictEqual(currentLockfile.overrides)
}
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ frozenLockfile: true, overrides }))
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ frozenLockfile: true, overrides }))
{
const lockfile = await project.readLockfile()
@@ -97,14 +88,11 @@ test('versions are replaced with versions specified through overrides option', a
overrides['@pnpm.e2e/bar@^100.0.0'] = '100.0.1'
await expect(
mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ frozenLockfile: true, overrides }))
mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ frozenLockfile: true, overrides }))
).rejects.toThrow(
new PnpmError('FROZEN_LOCKFILE_WITH_OUTDATED_LOCKFILE',
'Cannot perform a frozen installation because the lockfile needs updates'

View File

@@ -1,6 +1,6 @@
import PnpmError from '@pnpm/error'
import { prepareEmpty } from '@pnpm/prepare'
import { addDependenciesToPackage, mutateModules } from '@pnpm/core'
import { addDependenciesToPackage, mutateModulesInSingleProject } from '@pnpm/core'
import { createObjectChecksum } from '../../lib/install/index'
import {
testDefaults,
@@ -38,14 +38,11 @@ test('manifests are extended with fields specified by packageExtensions', async
// The lockfile is updated if the overrides are changed
packageExtensions['is-positive'].dependencies!['@pnpm.e2e/foobar'] = '100.0.0'
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ packageExtensions }))
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ packageExtensions }))
{
const lockfile = await project.readLockfile()
@@ -62,14 +59,11 @@ test('manifests are extended with fields specified by packageExtensions', async
expect(lockfile.packageExtensionsChecksum).toStrictEqual(currentLockfile.packageExtensionsChecksum)
}
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ frozenLockfile: true, packageExtensions }))
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ frozenLockfile: true, packageExtensions }))
{
const lockfile = await project.readLockfile()
@@ -87,14 +81,11 @@ test('manifests are extended with fields specified by packageExtensions', async
packageExtensions['is-positive'].dependencies!['@pnpm.e2e/bar'] = '100.0.1'
await expect(
mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ frozenLockfile: true, packageExtensions }))
mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ frozenLockfile: true, packageExtensions }))
).rejects.toThrow(
new PnpmError('FROZEN_LOCKFILE_WITH_OUTDATED_LOCKFILE',
'Cannot perform a frozen installation because the lockfile needs updates'

View File

@@ -11,6 +11,7 @@ import {
install,
MutatedProject,
mutateModules,
mutateModulesInSingleProject,
PeerDependencyIssuesError,
} from '@pnpm/core'
import rimraf from '@zkochan/rimraf'
@@ -65,14 +66,12 @@ test('nothing is needlessly removed from node_modules', async () => {
expect(await exists(path.resolve('node_modules/.pnpm/ajv-keywords@1.5.0/node_modules/ajv-keywords'))).toBeTruthy()
expect(deepRequireCwd(['@pnpm.e2e/using-ajv', 'ajv-keywords', 'ajv', './package.json']).version).toBe('4.10.4')
await mutateModules([
{
dependencyNames: ['ajv-keywords'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
},
], opts)
await mutateModulesInSingleProject({
dependencyNames: ['ajv-keywords'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
}, opts)
expect(await exists(path.resolve('node_modules/.pnpm/ajv-keywords@1.5.0_ajv@4.10.4/node_modules/ajv'))).toBeTruthy()
expect(await exists(path.resolve('node_modules/.pnpm/ajv-keywords@1.5.0/node_modules/ajv-keywords'))).toBeFalsy()
@@ -91,14 +90,11 @@ test('peer dependency is grouped with dependent when the peer is a top dependenc
expect(await exists(path.resolve('node_modules/.pnpm/ajv-keywords@1.5.0_ajv@4.10.4/node_modules/ajv-keywords'))).toBeTruthy()
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ preferFrozenLockfile: false }))
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ preferFrozenLockfile: false }))
const lockfile = await project.readLockfile()
expect(lockfile.packages['/ajv-keywords/1.5.0_ajv@4.10.4'].dependencies).toHaveProperty(['ajv'])
@@ -132,20 +128,28 @@ test('the right peer dependency is used in every workspace package', async () =>
])
const importers: MutatedProject[] = [
{
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
mutation: 'install',
rootDir: path.resolve('project-2'),
},
]
const allProjects = [
{
buildIndex: 0,
manifest: manifest1,
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
buildIndex: 0,
manifest: manifest2,
mutation: 'install',
rootDir: path.resolve('project-2'),
},
]
await mutateModules(importers, await testDefaults({ lockfileOnly: true, strictPeerDependencies: false }))
await mutateModules(importers, await testDefaults({ allProjects, lockfileOnly: true, strictPeerDependencies: false }))
const lockfile = await readYamlFile<Lockfile>(path.resolve(WANTED_LOCKFILE))
@@ -403,14 +407,11 @@ test('the list of transitive peer dependencies is kept up to date', async () =>
expect(lockfile.packages['/@pnpm.e2e/abc-grand-parent/1.0.0_@pnpm.e2e+peer-c@1.0.0'].transitivePeerDependencies).toStrictEqual(['@pnpm.e2e/peer-c'])
}
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ update: true, depth: Infinity }))
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ update: true, depth: Infinity }))
expect(await exists(path.resolve('node_modules/.pnpm/@pnpm.e2e+abc-grand-parent@1.0.0/node_modules/@pnpm.e2e/abc-grand-parent'))).toBeTruthy()
@@ -669,19 +670,15 @@ test('peer dependency is grouped with dependent when the peer is a top dependenc
}
// Covers https://github.com/pnpm/pnpm/issues/1506
await mutateModules(
[
{
dependencyNames: ['ajv'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
},
],
await testDefaults({
lockfileDir,
strictPeerDependencies: false,
})
await mutateModulesInSingleProject({
dependencyNames: ['ajv'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
}, await testDefaults({
lockfileDir,
strictPeerDependencies: false,
})
)
{
@@ -799,14 +796,12 @@ test('peer dependency is resolved from parent package', async () => {
name: 'pkg',
},
])
await mutateModules([
{
dependencySelectors: ['@pnpm.e2e/tango@1.0.0'],
manifest: {},
mutation: 'installSome',
rootDir: path.resolve('pkg'),
},
], await testDefaults())
await mutateModulesInSingleProject({
dependencySelectors: ['@pnpm.e2e/tango@1.0.0'],
manifest: {},
mutation: 'installSome',
rootDir: path.resolve('pkg'),
}, await testDefaults())
const lockfile = await readYamlFile<Lockfile>(WANTED_LOCKFILE)
expect(Object.keys(lockfile.packages ?? {})).toStrictEqual([
@@ -821,23 +816,19 @@ test('transitive peerDependencies field does not break the lockfile on subsequen
name: 'pkg',
},
])
const [{ manifest }] = await mutateModules([
{
dependencySelectors: ['most@1.7.3'],
manifest: {},
mutation: 'installSome',
rootDir: path.resolve('pkg'),
},
], await testDefaults())
const { manifest } = await mutateModulesInSingleProject({
dependencySelectors: ['most@1.7.3'],
manifest: {},
mutation: 'installSome',
rootDir: path.resolve('pkg'),
}, await testDefaults())
await mutateModules([
{
dependencySelectors: ['is-positive'],
manifest,
mutation: 'installSome',
rootDir: path.resolve('pkg'),
},
], await testDefaults())
await mutateModulesInSingleProject({
dependencySelectors: ['is-positive'],
manifest,
mutation: 'installSome',
rootDir: path.resolve('pkg'),
}, await testDefaults())
const lockfile = await readYamlFile<Lockfile>(WANTED_LOCKFILE)
@@ -854,14 +845,12 @@ test('peer dependency is resolved from parent package via its alias', async () =
name: 'pkg',
},
])
await mutateModules([
{
dependencySelectors: ['@pnpm.e2e/tango@npm:@pnpm.e2e/tango-tango@1.0.0'],
manifest: {},
mutation: 'installSome',
rootDir: path.resolve('pkg'),
},
], await testDefaults())
await mutateModulesInSingleProject({
dependencySelectors: ['@pnpm.e2e/tango@npm:@pnpm.e2e/tango-tango@1.0.0'],
manifest: {},
mutation: 'installSome',
rootDir: path.resolve('pkg'),
}, await testDefaults())
const lockfile = await readYamlFile<Lockfile>(WANTED_LOCKFILE)
const suffix = createPeersFolderSuffix([{ name: '@pnpm.e2e/tango-tango', version: '1.0.0' }])
@@ -890,14 +879,12 @@ test('peer dependency is saved', async () => {
}
)
const [mutatedImporter] = await mutateModules([
{
dependencyNames: ['is-positive'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
},
], await testDefaults())
const mutatedImporter = await mutateModulesInSingleProject({
dependencyNames: ['is-positive'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
}, await testDefaults())
expect(mutatedImporter.manifest).toStrictEqual(
{
@@ -1061,14 +1048,11 @@ test('local tarball dependency with peer dependency', async () => {
await rimraf('node_modules')
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults())
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults())
{
const updatedLocalPkgDirs = (await fs.readdir('node_modules/.pnpm'))
@@ -1088,26 +1072,20 @@ test('peer dependency that is resolved by a dev dependency', async () => {
},
}
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ fastUnpack: false, lockfileOnly: true, strictPeerDependencies: false }))
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ fastUnpack: false, lockfileOnly: true, strictPeerDependencies: false }))
const lockfile = await project.readLockfile()
expect(lockfile.packages['/@types/mongoose/5.7.32'].dev).toBeUndefined()
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({
frozenLockfile: true,
include: {
dependencies: true,
@@ -1144,20 +1122,28 @@ test('peer dependency is grouped with dependency when peer is resolved not from
},
])
const importers: MutatedProject[] = [
{
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
mutation: 'install',
rootDir: path.resolve('ajv'),
},
]
const allProjects = [
{
buildIndex: 0,
manifest: project1Manifest,
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
buildIndex: 0,
manifest: project2Manifest,
mutation: 'install',
rootDir: path.resolve('ajv'),
},
]
await mutateModules(importers, await testDefaults({}))
await mutateModules(importers, await testDefaults({ allProjects }))
const lockfile = await readYamlFile<Lockfile>(path.resolve(WANTED_LOCKFILE))
expect(lockfile.packages?.['/ajv-keywords/1.5.0_ajv@ajv'].dependencies?.['ajv']).toBe('link:ajv')

View File

@@ -2,7 +2,7 @@ import { prepareEmpty } from '@pnpm/prepare'
import {
addDependenciesToPackage,
install,
mutateModules,
mutateModulesInSingleProject,
} from '@pnpm/core'
import { addDistTag } from '@pnpm/registry-mock'
import { testDefaults } from '../utils'
@@ -197,24 +197,21 @@ test('an update bumps the versions in the manifest', async () => {
prepareEmpty()
const [{ manifest }] = await mutateModules([
{
buildIndex: 0,
manifest: {
dependencies: {
'@pnpm.e2e/peer-a': '~1.0.0',
},
devDependencies: {
'@pnpm.e2e/foo': '^100.0.0',
},
optionalDependencies: {
'@pnpm.e2e/peer-c': '^1.0.1',
},
const { manifest } = await mutateModulesInSingleProject({
manifest: {
dependencies: {
'@pnpm.e2e/peer-a': '~1.0.0',
},
devDependencies: {
'@pnpm.e2e/foo': '^100.0.0',
},
optionalDependencies: {
'@pnpm.e2e/peer-c': '^1.0.1',
},
mutation: 'install',
rootDir: process.cwd(),
},
],
mutation: 'install',
rootDir: process.cwd(),
},
await testDefaults({
update: true,
}))

View File

@@ -13,6 +13,7 @@ import {
addDependenciesToPackage,
install,
mutateModules,
mutateModulesInSingleProject,
} from '@pnpm/core'
import rimraf from '@zkochan/rimraf'
import loadJsonFile from 'load-json-file'
@@ -692,14 +693,12 @@ test(`don't update ${WANTED_LOCKFILE} during uninstall when useLockfile: false`,
{
const reporter = sinon.spy()
await mutateModules([
{
dependencyNames: ['is-positive'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
},
], await testDefaults({ useLockfile: false, reporter }))
await mutateModulesInSingleProject({
dependencyNames: ['is-positive'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
}, await testDefaults({ useLockfile: false, reporter }))
expect(reporter.calledWithMatch(LOCKFILE_WARN_LOG)).toBeTruthy()
}
@@ -943,19 +942,28 @@ test(`doing named installation when shared ${WANTED_LOCKFILE} exists already`, a
await mutateModules(
[
{
buildIndex: 0,
manifest: pkg1,
mutation: 'install',
rootDir: path.resolve('pkg1'),
},
{
buildIndex: 0,
manifest: pkg2,
mutation: 'install',
rootDir: path.resolve('pkg2'),
},
],
await testDefaults()
await testDefaults({
allProjects: [
{
buildIndex: 0,
manifest: pkg1,
rootDir: path.resolve('pkg1'),
},
{
buildIndex: 0,
manifest: pkg2,
rootDir: path.resolve('pkg2'),
},
],
})
)
await projects['pkg1'].has('is-negative')
@@ -989,14 +997,11 @@ test('existing dependencies are preserved when updating a lockfile to a newer fo
await addDistTag({ package: '@pnpm.e2e/dep-of-pkg-with-1-dep', version: '100.1.0', distTag: 'latest' })
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults())
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults())
const updatedLockfile = await project.readLockfile()
@@ -1017,14 +1022,11 @@ test('lockfile is not getting broken if the used registry changes', async () =>
}
expect(err.code).toBe('ERR_PNPM_REGISTRIES_MISMATCH')
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], newOpts)
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, newOpts)
await addDependenciesToPackage(manifest, ['is-negative@1'], newOpts)
expect(Object.keys((await project.readLockfile()).packages)).toStrictEqual([
@@ -1047,27 +1049,21 @@ test('broken lockfile is fixed even if it seems like up to date at first. Unless
let err!: PnpmError
try {
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ frozenLockfile: true }))
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ frozenLockfile: true }))
} catch (_err: any) { // eslint-disable-line
err = _err
}
expect(err.code).toBe('ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY')
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ preferFrozenLockfile: true }))
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ preferFrozenLockfile: true }))
await project.has('@pnpm.e2e/pkg-with-1-dep')
const lockfile = await project.readLockfile()
@@ -1300,14 +1296,11 @@ packages:
const reporter = jest.fn()
await mutateModules([
{
buildIndex: 0,
mutation: 'install',
manifest,
rootDir: process.cwd(),
},
], await testDefaults({ reporter }))
await mutateModulesInSingleProject({
mutation: 'install',
manifest,
rootDir: process.cwd(),
}, await testDefaults({ reporter }))
expect(reporter).toBeCalledWith(expect.objectContaining({
level: 'warn',
@@ -1352,14 +1345,11 @@ test('a broken lockfile should not break the store', async () => {
await writeYamlFile(WANTED_LOCKFILE, lockfile)
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ lockfileOnly: true, storeDir: path.resolve('store2') }))
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ lockfileOnly: true, storeDir: path.resolve('store2') }))
delete lockfile.packages!['/is-positive/1.0.0'].name
delete lockfile.packages!['/is-positive/1.0.0'].version
@@ -1367,14 +1357,11 @@ test('a broken lockfile should not break the store', async () => {
await writeYamlFile(WANTED_LOCKFILE, lockfile)
await rimraf(path.resolve('node_modules'))
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ lockfileOnly: true, storeDir: path.resolve('store2') }))
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
rootDir: process.cwd(),
}, await testDefaults({ lockfileOnly: true, storeDir: path.resolve('store2') }))
})
test('include tarball URL', async () => {

View File

@@ -6,7 +6,7 @@ import {
addDependenciesToPackage,
install,
link,
mutateModules,
mutateModulesInSingleProject,
} from '@pnpm/core'
import sinon from 'sinon'
import { testDefaults } from './utils'
@@ -31,22 +31,16 @@ test('prune removes extraneous packages', async () => {
const reporter = sinon.spy()
await mutateModules(
[
{
buildIndex: 0,
manifest,
mutation: 'install',
pruneDirectDependencies: true,
rootDir: process.cwd(),
},
],
{
...opts,
pruneStore: true,
reporter,
}
)
await mutateModulesInSingleProject({
manifest,
mutation: 'install',
pruneDirectDependencies: true,
rootDir: process.cwd(),
}, {
...opts,
pruneStore: true,
reporter,
})
expect(reporter.calledWithMatch({
level: 'debug',

View File

@@ -15,6 +15,7 @@ import {
addDependenciesToPackage,
link,
mutateModules,
mutateModulesInSingleProject,
} from '@pnpm/core'
import exists from 'path-exists'
import sinon from 'sinon'
@@ -30,14 +31,12 @@ test('uninstall package with no dependencies', async () => {
let manifest = await addDependenciesToPackage({}, ['is-negative@2.1.0'], await testDefaults({ save: true }))
const reporter = sinon.spy()
manifest = (await mutateModules([
{
dependencyNames: ['is-negative'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
},
], await testDefaults({ save: true, reporter })))[0].manifest
manifest = (await mutateModulesInSingleProject({
dependencyNames: ['is-negative'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
}, await testDefaults({ save: true, reporter }))).manifest
expect(reporter.calledWithMatch({
initial: {
@@ -85,14 +84,12 @@ test('uninstall a dependency that is not present in node_modules', async () => {
prepareEmpty()
const reporter = sinon.spy()
await mutateModules([
{
dependencyNames: ['is-negative'],
manifest: {},
mutation: 'uninstallSome',
rootDir: process.cwd(),
},
], await testDefaults({ reporter }))
await mutateModulesInSingleProject({
dependencyNames: ['is-negative'],
manifest: {},
mutation: 'uninstallSome',
rootDir: process.cwd(),
}, await testDefaults({ reporter }))
expect(reporter.calledWithMatch({
level: 'debug',
@@ -106,14 +103,12 @@ test('uninstall a dependency that is not present in node_modules', async () => {
test('uninstall scoped package', async () => {
const project = prepareEmpty()
let manifest = await addDependenciesToPackage({}, ['@zkochan/logger@0.1.0'], await testDefaults({ save: true }))
manifest = (await mutateModules([
{
dependencyNames: ['@zkochan/logger'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
},
], await testDefaults({ save: true })))[0].manifest
manifest = (await mutateModulesInSingleProject({
dependencyNames: ['@zkochan/logger'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
}, await testDefaults({ save: true }))).manifest
await project.storeHas('@zkochan/logger', '0.1.0')
@@ -127,14 +122,12 @@ test('uninstall tarball dependency', async () => {
const opts = await testDefaults({ save: true })
let manifest = await addDependenciesToPackage({}, [`http://localhost:${REGISTRY_MOCK_PORT}/is-array/-/is-array-1.0.1.tgz`], opts)
manifest = (await mutateModules([
{
dependencyNames: ['is-array'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
},
], opts))[0].manifest
manifest = (await mutateModulesInSingleProject({
dependencyNames: ['is-array'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
}, opts)).manifest
await project.storeHas('is-array', '1.0.1')
await project.hasNot('is-array')
@@ -145,14 +138,12 @@ test('uninstall tarball dependency', async () => {
test('uninstall package with dependencies and do not touch other deps', async () => {
const project = prepareEmpty()
let manifest = await addDependenciesToPackage({}, ['is-negative@2.1.0', 'camelcase-keys@3.0.0'], await testDefaults({ save: true }))
manifest = (await mutateModules([
{
dependencyNames: ['camelcase-keys'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
},
], await testDefaults({ pruneStore: true, save: true })))[0].manifest
manifest = (await mutateModulesInSingleProject({
dependencyNames: ['camelcase-keys'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
}, await testDefaults({ pruneStore: true, save: true }))).manifest
await project.storeHasNot('camelcase-keys', '3.0.0')
await project.hasNot('camelcase-keys')
@@ -180,14 +171,12 @@ test('uninstall package with dependencies and do not touch other deps', async ()
test('uninstall package with its bin files', async () => {
prepareEmpty()
const manifest = await addDependenciesToPackage({}, ['@pnpm.e2e/sh-hello-world@1.0.1'], await testDefaults({ fastUnpack: false, save: true }))
await mutateModules([
{
dependencyNames: ['@pnpm.e2e/sh-hello-world'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
},
], await testDefaults({ save: true }))
await mutateModulesInSingleProject({
dependencyNames: ['@pnpm.e2e/sh-hello-world'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
}, await testDefaults({ save: true }))
// check for both a symlink and a file because in some cases the file will be a proxied not symlinked
const stat = await existsSymlink(path.resolve('node_modules', '.bin', 'sh-hello-world'))
@@ -207,14 +196,12 @@ test('relative link is uninstalled', async () => {
f.copy(linkedPkgName, linkedPkgPath)
const manifest = await link([`../${linkedPkgName}`], path.join(process.cwd(), 'node_modules'), opts as (typeof opts & { dir: string, manifest: PackageManifest }))
await mutateModules([
{
dependencyNames: [linkedPkgName],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
},
], opts)
await mutateModulesInSingleProject({
dependencyNames: [linkedPkgName],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
}, opts)
await project.hasNot(linkedPkgName)
})
@@ -231,14 +218,12 @@ test('pendingBuilds gets updated after uninstall', async () => {
expect(modules1).toBeTruthy()
expect(modules1!.pendingBuilds.length).toBe(2)
await mutateModules([
{
dependencyNames: ['@pnpm.e2e/with-postinstall-b'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
},
], await testDefaults({ save: true }))
await mutateModulesInSingleProject({
dependencyNames: ['@pnpm.e2e/with-postinstall-b'],
manifest,
mutation: 'uninstallSome',
rootDir: process.cwd(),
}, await testDefaults({ save: true }))
const modules2 = await project.readModulesManifest()
expect(modules2).toBeTruthy()
@@ -272,19 +257,27 @@ test('uninstalling a dependency from package that uses shared lockfile', async (
await mutateModules(
[
{
buildIndex: 0,
manifest: pkgs[0],
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
buildIndex: 0,
manifest: pkgs[1],
mutation: 'install',
rootDir: path.resolve('project-2'),
},
],
await testDefaults({
allProjects: [
{
buildIndex: 0,
manifest: pkgs[0],
rootDir: path.resolve('project-1'),
},
{
buildIndex: 0,
manifest: pkgs[1],
rootDir: path.resolve('project-2'),
},
],
store,
workspacePackages: {
'project-2': {
@@ -307,14 +300,12 @@ test('uninstalling a dependency from package that uses shared lockfile', async (
await projects['project-1'].has('is-positive')
await projects['project-2'].has('is-negative')
await mutateModules([
{
dependencyNames: ['is-positive', 'project-2'],
manifest: pkgs[0],
mutation: 'uninstallSome',
rootDir: path.resolve('project-1'),
},
], await testDefaults({
await mutateModulesInSingleProject({
dependencyNames: ['is-positive', 'project-2'],
manifest: pkgs[0],
mutation: 'uninstallSome',
rootDir: path.resolve('project-1'),
}, await testDefaults({
lockfileDir: process.cwd(),
store,
pruneLockfileImporters: false,
@@ -361,17 +352,12 @@ test('uninstall remove modules that is not in package.json', async () => {
await project.has('foo')
await mutateModules(
[
{
dependencyNames: ['foo'],
manifest: {},
mutation: 'uninstallSome',
rootDir: process.cwd(),
},
],
await testDefaults()
)
await mutateModulesInSingleProject({
dependencyNames: ['foo'],
manifest: {},
mutation: 'uninstallSome',
rootDir: process.cwd(),
}, await testDefaults())
await project.hasNot('foo')
})

View File

@@ -4,7 +4,7 @@ import {
addDependenciesToPackage,
install,
link,
mutateModules,
mutateModulesInSingleProject,
} from '@pnpm/core'
import { prepareEmpty } from '@pnpm/prepare'
import { WANTED_LOCKFILE } from '@pnpm/constants'
@@ -54,17 +54,12 @@ test('unlink 1 package that exists in package.json', async () => {
manifest = await install(manifest, opts)
await mutateModules(
[
{
dependencyNames: ['is-subdir'],
manifest,
mutation: 'unlinkSome',
rootDir: process.cwd(),
},
],
opts
)
await mutateModulesInSingleProject({
dependencyNames: ['is-subdir'],
manifest,
mutation: 'unlinkSome',
rootDir: process.cwd(),
}, opts)
expect(typeof project.requireModule('is-subdir')).toBe('function')
expect((await isInnerLink('node_modules', 'is-positive')).isInner).toBeFalsy()
@@ -88,17 +83,12 @@ test("don't update package when unlinking", async () => {
await addDistTag({ package: '@pnpm.e2e/foo', version: '100.1.0', distTag: 'latest' })
process.chdir('project')
await mutateModules(
[
{
dependencyNames: ['@pnpm.e2e/foo'],
manifest,
mutation: 'unlinkSome',
rootDir: process.cwd(),
},
],
opts
)
await mutateModulesInSingleProject({
dependencyNames: ['@pnpm.e2e/foo'],
manifest,
mutation: 'unlinkSome',
rootDir: process.cwd(),
}, opts)
expect(project.requireModule('@pnpm.e2e/foo/package.json').version).toBe('100.0.0')
})
@@ -125,20 +115,15 @@ test(`don't update package when unlinking. Initial link is done on a package w/o
await addDistTag({ package: '@pnpm.e2e/foo', version: '100.1.0', distTag: 'latest' })
process.chdir('project')
const unlinkResult = await mutateModules(
[
{
dependencyNames: ['@pnpm.e2e/foo'],
manifest,
mutation: 'unlinkSome',
rootDir: process.cwd(),
},
],
opts
)
const unlinkResult = await mutateModulesInSingleProject({
dependencyNames: ['@pnpm.e2e/foo'],
manifest,
mutation: 'unlinkSome',
rootDir: process.cwd(),
}, opts)
expect(project.requireModule('@pnpm.e2e/foo/package.json').version).toBe('100.1.0')
expect(unlinkResult[0].manifest.dependencies).toStrictEqual({ '@pnpm.e2e/foo': '^100.0.0' })
expect(unlinkResult.manifest.dependencies).toStrictEqual({ '@pnpm.e2e/foo': '^100.0.0' })
})
test('unlink 2 packages. One of them exists in package.json', async () => {
@@ -170,17 +155,12 @@ test('unlink 2 packages. One of them exists in package.json', async () => {
})
process.chdir('project')
await mutateModules(
[
{
dependencyNames: ['is-subdir', 'is-positive'],
manifest,
mutation: 'unlinkSome',
rootDir: process.cwd(),
},
],
opts
)
await mutateModulesInSingleProject({
dependencyNames: ['is-subdir', 'is-positive'],
manifest,
mutation: 'unlinkSome',
rootDir: process.cwd(),
}, opts)
expect(typeof project.requireModule('is-subdir')).toBe('function')
expect(await exists(path.join('node_modules', 'is-positive'))).toBeFalsy()
@@ -215,16 +195,11 @@ test('unlink all packages', async () => {
},
})
await mutateModules(
[
{
manifest,
mutation: 'unlink',
rootDir: path.resolve('project'),
},
],
opts
)
await mutateModulesInSingleProject({
manifest,
mutation: 'unlink',
rootDir: path.resolve('project'),
}, opts)
expect(typeof project.requireModule('is-subdir')).toBe('function')
expect(typeof project.requireModule('@zkochan/logger')).toBe('object')
@@ -236,16 +211,11 @@ test("don't warn about scoped packages when running unlink w/o params", async ()
const manifest = await addDependenciesToPackage({}, ['@zkochan/logger'], await testDefaults())
const reporter = sinon.spy()
await mutateModules(
[
{
manifest,
mutation: 'unlink',
rootDir: process.cwd(),
},
],
await testDefaults({ reporter })
)
await mutateModulesInSingleProject({
manifest,
mutation: 'unlink',
rootDir: process.cwd(),
}, await testDefaults({ reporter }))
expect(reporter.calledWithMatch({
level: 'warn',
@@ -260,17 +230,12 @@ test("don't unlink package that is not a link", async () => {
const manifest = await addDependenciesToPackage({}, ['is-positive'], await testDefaults())
await mutateModules(
[
{
dependencyNames: ['is-positive'],
manifest,
mutation: 'unlinkSome',
rootDir: process.cwd(),
},
],
await testDefaults({ reporter })
)
await mutateModulesInSingleProject({
dependencyNames: ['is-positive'],
manifest,
mutation: 'unlinkSome',
rootDir: process.cwd(),
}, await testDefaults({ reporter }))
expect(reporter.calledWithMatch({
level: 'warn',
@@ -319,17 +284,12 @@ test('unlink would remove global bin', async () => {
)
expect(fs.existsSync(path.resolve('bin/is-subdir'))).toBeTruthy()
await mutateModules(
[
{
dependencyNames: ['is-subdir'],
manifest,
mutation: 'unlinkSome',
rootDir: process.cwd(),
},
],
opts
)
await mutateModulesInSingleProject({
dependencyNames: ['is-subdir'],
manifest,
mutation: 'unlinkSome',
rootDir: process.cwd(),
}, opts)
expect(fs.existsSync(path.resolve('bin/is-subdir'))).toBeFalsy()
})

View File

@@ -1,4 +1,4 @@
import findWorkspacePackages from '@pnpm/find-workspace-packages'
import findWorkspacePackages, { Project } from '@pnpm/find-workspace-packages'
import matcher from '@pnpm/matcher'
import createPkgGraph, { Package, PackageNode } from 'pkgs-graph'
import isSubdir from 'is-subdir'
@@ -29,6 +29,12 @@ interface FilteredGraph<T> {
unmatchedFilters: string[]
}
export interface ReadProjectsResult {
allProjects: Project[]
allProjectsGraph: PackageGraph<Project>
selectedProjectsGraph: PackageGraph<Project>
}
export async function readProjects (
workspaceDir: string,
pkgSelectors: PackageSelector[],
@@ -37,9 +43,9 @@ export async function readProjects (
linkWorkspacePackages?: boolean
changedFilesIgnorePattern?: string[]
}
) {
): Promise<ReadProjectsResult> {
const allProjects = await findWorkspacePackages(workspaceDir, { engineStrict: opts?.engineStrict })
const { selectedProjectsGraph } = await filterPkgsBySelectorObjects(
const { allProjectsGraph, selectedProjectsGraph } = await filterPkgsBySelectorObjects(
allProjects,
pkgSelectors,
{
@@ -48,7 +54,7 @@ export async function readProjects (
changedFilesIgnorePattern: opts?.changedFilesIgnorePattern,
}
)
return { allProjects, selectedProjectsGraph }
return { allProjects, allProjectsGraph, selectedProjectsGraph }
}
export interface FilterPackagesOptions {

View File

@@ -7,14 +7,14 @@ import readYamlFile from 'read-yaml-file'
export { Project }
export default async (
export default async function (
workspaceRoot: string,
opts?: {
engineStrict?: boolean
nodeVersion?: string
patterns?: string[]
}
) => {
): Promise<Project[]> {
const pkgs = await findWorkspacePackagesNoCheck(workspaceRoot, opts)
for (const pkg of pkgs) {
packageIsInstallable(pkg.dir, pkg.manifest, opts ?? {})

View File

@@ -20,6 +20,7 @@ import rimraf from '@zkochan/rimraf'
import pathAbsolute from 'path-absolute'
import clone from 'ramda/src/clone'
import equals from 'ramda/src/equals'
import fromPairs from 'ramda/src/fromPairs'
import checkCompatibility from './checkCompatibility'
import UnexpectedStoreError from './checkCompatibility/UnexpectedStoreError'
import UnexpectedVirtualStoreDirError from './checkCompatibility/UnexpectedVirtualStoreDirError'
@@ -27,7 +28,7 @@ import readLockfileFile from './readLockfiles'
export { UnexpectedStoreError, UnexpectedVirtualStoreDirError }
export interface PnpmContext<T> {
export interface PnpmContext {
currentLockfile: Lockfile
currentLockfileIsUpToDate: boolean
existsCurrentLockfile: boolean
@@ -39,10 +40,10 @@ export interface PnpmContext<T> {
include: IncludedDependencies
modulesFile: Modules | null
pendingBuilds: string[]
projects: Array<{
projects: Record<string, {
modulesDir: string
id: string
} & HookOptions & T & Required<ProjectOptions>>
} & HookOptions & Required<ProjectOptions>>
rootModulesDir: string
hoistPattern: string[] | undefined
hoistedModulesDir: string
@@ -56,6 +57,7 @@ export interface PnpmContext<T> {
}
export interface ProjectOptions {
buildIndex: number
binsDir?: string
manifest: ProjectManifest
modulesDir?: string
@@ -67,6 +69,7 @@ interface HookOptions {
}
export interface GetContextOptions {
allProjects: Array<ProjectOptions & HookOptions>
force: boolean
forceNewModules?: boolean
forceSharedLockfile: boolean
@@ -91,12 +94,11 @@ export interface GetContextOptions {
forcePublicHoistPattern?: boolean
}
export default async function getContext<T> (
projects: Array<ProjectOptions & HookOptions & T>,
export default async function getContext (
opts: GetContextOptions
): Promise<PnpmContext<T>> {
): Promise<PnpmContext> {
const modulesDir = opts.modulesDir ?? 'node_modules'
let importersContext = await readProjectsContext(projects, { lockfileDir: opts.lockfileDir, modulesDir })
let importersContext = await readProjectsContext(opts.allProjects, { lockfileDir: opts.lockfileDir, modulesDir })
const virtualStoreDir = pathAbsolute(opts.virtualStoreDir ?? path.join(modulesDir, '.pnpm'), opts.lockfileDir)
if (importersContext.modules != null) {
@@ -118,7 +120,7 @@ export default async function getContext<T> (
publicHoistPattern: opts.publicHoistPattern,
})
if (purged) {
importersContext = await readProjectsContext(projects, {
importersContext = await readProjectsContext(opts.allProjects, {
lockfileDir: opts.lockfileDir,
modulesDir,
})
@@ -127,7 +129,7 @@ export default async function getContext<T> (
await fs.mkdir(opts.storeDir, { recursive: true })
projects.forEach((project) => {
opts.allProjects.forEach((project) => {
packageManifestLogger.debug({
initial: project.manifest,
prefix: project.rootDir,
@@ -148,7 +150,7 @@ export default async function getContext<T> (
extraBinPaths.unshift(path.join(hoistedModulesDir, '.bin'))
}
const hoistPattern = importersContext.currentHoistPattern ?? opts.hoistPattern
const ctx: PnpmContext<T> = {
const ctx: PnpmContext = {
extraBinPaths,
extraNodePaths: getExtraNodePaths({ nodeLinker: opts.nodeLinker, hoistPattern, virtualStoreDir }),
hoistedDependencies: importersContext.hoistedDependencies,
@@ -158,7 +160,7 @@ export default async function getContext<T> (
lockfileDir: opts.lockfileDir,
modulesFile: importersContext.modules,
pendingBuilds: importersContext.pendingBuilds,
projects: importersContext.projects,
projects: fromPairs(importersContext.projects.map((project) => [project.rootDir, project])),
publicHoistPattern: importersContext.currentPublicHoistPattern ?? opts.publicHoistPattern,
registries: {
...opts.registries,

View File

@@ -63,6 +63,7 @@ import equals from 'ramda/src/equals'
import fromPairs from 'ramda/src/fromPairs'
import isEmpty from 'ramda/src/isEmpty'
import omit from 'ramda/src/omit'
import pick from 'ramda/src/pick'
import props from 'ramda/src/props'
import union from 'ramda/src/union'
import realpathMissing from 'realpath-missing'
@@ -108,7 +109,8 @@ export interface HeadlessOptions {
ignoreScripts: boolean
ignorePackageManifest?: boolean
include: IncludedDependencies
projects: Project[]
selectedProjectDirs: string[]
allProjects: Record<string, Project>
prunedAt?: string
hoistedDependencies: HoistedDependencies
hoistPattern?: string[]
@@ -171,9 +173,10 @@ export default async (opts: HeadlessOptions) => {
const currentLockfile = opts.currentLockfile ?? await readCurrentLockfile(virtualStoreDir, { ignoreIncompatible: false })
const hoistedModulesDir = path.join(virtualStoreDir, 'node_modules')
const publicHoistedModulesDir = rootModulesDir
const selectedProjects = Object.values(pick(opts.selectedProjectDirs, opts.allProjects))
if (!opts.ignorePackageManifest) {
for (const { id, manifest, rootDir } of opts.projects) {
for (const { id, manifest, rootDir } of selectedProjects) {
if (!satisfiesPackageManifest(wantedLockfile, manifest, id, { autoInstallPeers: opts.autoInstallPeers })) {
throw new PnpmError('OUTDATED_LOCKFILE',
`Cannot install with "frozen-lockfile" because ${WANTED_LOCKFILE} is not up to date with ` +
@@ -201,7 +204,7 @@ export default async (opts: HeadlessOptions) => {
if (opts.nodeLinker !== 'hoisted') {
if (currentLockfile != null && !opts.ignorePackageManifest) {
await prune(
opts.projects,
selectedProjects,
{
currentLockfile,
dryRun: false,
@@ -239,7 +242,7 @@ export default async (opts: HeadlessOptions) => {
}
const importerIds = (opts.ignorePackageManifest === true || opts.nodeLinker === 'hoisted')
? Object.keys(wantedLockfile.importers)
: opts.projects.map(({ id }) => id)
: selectedProjects.map(({ id }) => id)
const filteredLockfile = filterLockfileByImportersAndEngine(wantedLockfile, importerIds, {
...filterOpts,
currentEngine: opts.currentEngine,
@@ -279,7 +282,7 @@ export default async (opts: HeadlessOptions) => {
)
if (opts.enablePnp) {
const importerNames = fromPairs(
opts.projects.map(({ manifest, id }) => [id, manifest.name ?? id])
selectedProjects.map(({ manifest, id }) => [id, manifest.name ?? id])
)
await writePnpFile(filteredLockfile, {
importerNames,
@@ -321,7 +324,7 @@ export default async (opts: HeadlessOptions) => {
directDependenciesByImporterId: symlinkedDirectDependenciesByImporterId!,
filteredLockfile,
lockfileDir,
projects: opts.projects,
projects: selectedProjects,
registries: opts.registries,
symlink: opts.symlink,
})
@@ -389,7 +392,7 @@ export default async (opts: HeadlessOptions) => {
directDependenciesByImporterId,
filteredLockfile,
lockfileDir,
projects: opts.projects,
projects: selectedProjects,
registries: opts.registries,
symlink: opts.symlink,
})
@@ -397,7 +400,7 @@ export default async (opts: HeadlessOptions) => {
}
if (opts.ignoreScripts) {
for (const { id, manifest } of opts.projects) {
for (const { id, manifest } of selectedProjects) {
if (opts.ignoreScripts && ((manifest?.scripts) != null) &&
(manifest.scripts.preinstall ?? manifest.scripts.prepublish ??
manifest.scripts.install ??
@@ -457,7 +460,7 @@ export default async (opts: HeadlessOptions) => {
})
}
const projectsToBeBuilt = extendProjectsWithTargetDirs(opts.projects, wantedLockfile, {
const projectsToBeBuilt = extendProjectsWithTargetDirs(selectedProjects, wantedLockfile, {
pkgLocationByDepPath,
virtualStoreDir,
})
@@ -465,7 +468,7 @@ export default async (opts: HeadlessOptions) => {
if (opts.enableModulesDir !== false) {
/** Skip linking and due to no project manifest */
if (!opts.ignorePackageManifest) {
await Promise.all(opts.projects.map(async (project) => {
await Promise.all(selectedProjects.map(async (project) => {
if (opts.publicHoistPattern?.length && path.relative(opts.lockfileDir, project.rootDir) === '') {
await linkBinsOfImporter(project, {
extraNodePaths: opts.extraNodePaths,
@@ -540,9 +543,10 @@ export default async (opts: HeadlessOptions) => {
}
}
type SymlinkDirectDependenciesOpts = Pick<HeadlessOptions, 'projects' | 'registries' | 'symlink' | 'lockfileDir'> & {
type SymlinkDirectDependenciesOpts = Pick<HeadlessOptions, 'registries' | 'symlink' | 'lockfileDir'> & {
filteredLockfile: Lockfile
directDependenciesByImporterId: DirectDependenciesByImporterId
projects: Project[]
}
async function symlinkDirectDependencies (

View File

@@ -14,8 +14,6 @@ import headless from '@pnpm/headless'
import { readWantedLockfile } from '@pnpm/lockfile-file'
import { read as readModulesYaml } from '@pnpm/modules-yaml'
import { tempDir } from '@pnpm/prepare'
import { fromDir as readPackageJsonFromDir } from '@pnpm/read-package-json'
import readprojectsContext from '@pnpm/read-projects-context'
import { getIntegrity, REGISTRY_MOCK_PORT } from '@pnpm/registry-mock'
import fixtures from '@pnpm/test-fixtures'
import rimraf from '@zkochan/rimraf'
@@ -657,22 +655,9 @@ test('installing with publicHoistPattern=* in a project with external lockfile',
const lockfileDir = f.prepare('pkg-with-external-lockfile')
const prefix = path.join(lockfileDir, 'pkg')
let { projects } = await readprojectsContext(
[
{
rootDir: prefix,
},
],
{ lockfileDir }
)
projects = await Promise.all(
projects.map(async (project) => ({ ...project, manifest: await readPackageJsonFromDir(project.rootDir) }))
)
await headless(await testDefaults({
lockfileDir,
projects,
projects: [prefix],
publicHoistPattern: '*',
}))
@@ -725,23 +710,11 @@ test.each([['isolated'], ['hoisted']])('using side effects cache with nodeLinker
test.skip('using side effects cache and hoistPattern=*', async () => {
const lockfileDir = f.prepare('side-effects-of-subdep')
const { projects } = await readprojectsContext(
[
{
rootDir: lockfileDir,
},
],
{ lockfileDir }
)
// Right now, hardlink does not work with side effects, so we specify copy as the packageImportMethod
// We disable verifyStoreIntegrity because we are going to change the cache
const opts = await testDefaults({
hoistPattern: '*',
lockfileDir,
projects: await Promise.all(
projects.map(async (project) => ({ ...project, manifest: await readPackageJsonFromDir(project.rootDir) }))
),
sideEffectsCacheRead: true,
sideEffectsCacheWrite: true,
verifyStoreIntegrity: false,
@@ -765,21 +738,10 @@ test.skip('using side effects cache and hoistPattern=*', async () => {
test('installing in a workspace', async () => {
const workspaceFixture = f.prepare('workspace')
let { projects } = await readprojectsContext(
[
{
rootDir: path.join(workspaceFixture, 'foo'),
},
{
rootDir: path.join(workspaceFixture, 'bar'),
},
],
{ lockfileDir: workspaceFixture }
)
projects = await Promise.all(
projects.map(async (project) => ({ ...project, manifest: await readPackageJsonFromDir(project.rootDir) }))
)
const projects = [
path.join(workspaceFixture, 'foo'),
path.join(workspaceFixture, 'bar'),
]
await headless(await testDefaults({
lockfileDir: workspaceFixture,
@@ -849,25 +811,13 @@ test('installing with node-linker=hoisted', async () => {
test('installing in a workspace with node-linker=hoisted', async () => {
const prefix = f.prepare('workspace2')
let { projects } = await readprojectsContext(
[
{
rootDir: path.join(prefix, 'foo'),
},
{
rootDir: path.join(prefix, 'bar'),
},
],
{ lockfileDir: prefix }
)
projects = await Promise.all(
projects.map(async (project) => ({ ...project, manifest: await readPackageJsonFromDir(project.rootDir) }))
)
await headless(await testDefaults({
lockfileDir: prefix,
nodeLinker: 'hoisted',
projects,
projects: [
path.join(prefix, 'foo'),
path.join(prefix, 'bar'),
],
}))
expect(realpathSync('bar/node_modules/foo')).toBe(path.resolve('foo'))

View File

@@ -2,10 +2,11 @@ import path from 'path'
import createClient from '@pnpm/client'
import { HeadlessOptions } from '@pnpm/headless'
import createStore from '@pnpm/package-store'
import { fromDir as readPackageJsonFromDir } from '@pnpm/read-package-json'
import { safeReadPackageFromDir } from '@pnpm/read-package-json'
import readProjectsContext from '@pnpm/read-projects-context'
import { REGISTRY_MOCK_PORT } from '@pnpm/registry-mock'
import storePath from '@pnpm/store-path'
import fromPairs from 'ramda/src/fromPairs'
import tempy from 'tempy'
const registry = `http://localhost:${REGISTRY_MOCK_PORT}/`
@@ -28,11 +29,13 @@ export default async function testDefaults (
const cacheDir = path.join(tmp, 'cache')
const lockfileDir = opts?.lockfileDir ?? process.cwd()
const { include, pendingBuilds, projects, registries } = await readProjectsContext(
[
{
rootDir: lockfileDir,
},
],
opts.projects
? opts.projects.map((rootDir: string) => ({ rootDir }))
: [
{
rootDir: lockfileDir,
},
],
{ lockfileDir }
)
storeDir = await storePath({
@@ -72,11 +75,10 @@ export default async function testDefaults (
version: '1.0.0',
},
pendingBuilds,
projects: opts.projects
? opts.projects
: await Promise.all(
projects.map(async (project) => ({ ...project, manifest: await readPackageJsonFromDir(project.rootDir) }))
),
selectedProjectDirs: projects.map((project) => project.rootDir),
allProjects: fromPairs(
await Promise.all(projects.map(async (project) => [project.rootDir, { ...project, manifest: await safeReadPackageFromDir(project.rootDir) }]))
),
rawConfig: {},
registries: registries ?? {
default: registry,

View File

@@ -2,7 +2,7 @@ import { docsUrl } from '@pnpm/cli-utils'
import { UNIVERSAL_OPTIONS } from '@pnpm/common-cli-options-help'
import { Config } from '@pnpm/config'
import { createOrConnectStoreController, CreateStoreControllerOptions } from '@pnpm/store-connection-manager'
import { InstallOptions, mutateModules } from '@pnpm/core'
import { InstallOptions, mutateModulesInSingleProject } from '@pnpm/core'
import renderHelp from 'render-help'
import { cliOptionsTypes } from './install'
@@ -47,15 +47,12 @@ export async function handler (
// when including optional deps, production is also required when perform headless install
optionalDependencies: opts.production !== false,
}
return mutateModules([
{
buildIndex: 0,
manifest: {},
mutation: 'install',
pruneDirectDependencies: true,
rootDir: process.cwd(),
},
], {
return mutateModulesInSingleProject({
manifest: {},
mutation: 'install',
pruneDirectDependencies: true,
rootDir: process.cwd(),
}, {
...opts,
ignorePackageManifest: true,
include,

View File

@@ -1,4 +1,4 @@
export default function getPinnedVersion (opts: { saveExact?: boolean, savePrefix?: string }) {
export default function getPinnedVersion (opts: { saveExact?: boolean, savePrefix?: string }): 'major' | 'minor' | 'patch' {
if (opts.saveExact === true || opts.savePrefix === '') return 'patch'
return opts.savePrefix === '~' ? 'minor' : 'major'
}

View File

@@ -84,6 +84,7 @@ export const commandNames = ['import']
export type ImportCommandOptions = Pick<Config,
| 'allProjects'
| 'allProjectsGraph'
| 'selectedProjectsGraph'
| 'workspaceDir'
> & CreateStoreControllerOptions & Omit<InstallOptions, 'storeController' | 'lockfileOnly' | 'preferredVersions'>

View File

@@ -269,6 +269,7 @@ export type InstallCommandOptions = Pick<Config,
| 'saveProd'
| 'saveWorkspaceProtocol'
| 'lockfileIncludeTarballUrl'
| 'allProjectsGraph'
| 'selectedProjectsGraph'
| 'sideEffectsCache'
| 'sideEffectsCacheReadonly'

View File

@@ -12,9 +12,8 @@ import { createOrConnectStoreController, CreateStoreControllerOptions } from '@p
import { IncludedDependencies, Project } from '@pnpm/types'
import {
install,
mutateModules,
mutateModulesInSingleProject,
MutateModulesOptions,
MutatedProject,
WorkspacePackages,
} from '@pnpm/core'
import logger from '@pnpm/logger'
@@ -35,6 +34,7 @@ const OVERWRITE_UPDATE_OPTIONS = {
export type InstallDepsOptions = Pick<Config,
| 'allProjects'
| 'allProjectsGraph'
| 'autoInstallPeers'
| 'bail'
| 'bin'
@@ -149,6 +149,7 @@ when running add/update with the --workspace option')
...opts,
forceHoistPattern,
forcePublicHoistPattern,
allProjectsGraph: selectedProjectsGraph,
selectedProjectsGraph,
workspaceDir: opts.workspaceDir,
},
@@ -176,7 +177,7 @@ when running add/update with the --workspace option')
}
const store = await createOrConnectStoreController(opts)
const installOpts: MutateModulesOptions = {
const installOpts: Omit<MutateModulesOptions, 'allProjects'> = {
...opts,
...getOptionsFromRootManifest(manifest),
forceHoistPattern,
@@ -234,18 +235,18 @@ when running add/update with the --workspace option')
}
}
if (params?.length) {
const mutatedProject: MutatedProject = {
const mutatedProject = {
allowNew: opts.allowNew,
binsDir: opts.bin,
dependencySelectors: params,
manifest,
mutation: 'installSome',
mutation: 'installSome' as const,
peer: opts.savePeer,
pinnedVersion: getPinnedVersion(opts),
rootDir: opts.dir,
targetDependenciesField: getSaveType(opts),
}
const [updatedImporter] = await mutateModules([mutatedProject], installOpts)
const updatedImporter = await mutateModulesInSingleProject(mutatedProject, installOpts)
if (opts.save !== false) {
await writeProjectManifest(updatedImporter.manifest)
}
@@ -270,6 +271,7 @@ when running add/update with the --workspace option')
await recursive(allProjects, [], {
...opts,
...OVERWRITE_UPDATE_OPTIONS,
allProjectsGraph: opts.allProjectsGraph!,
selectedProjectsGraph,
workspaceDir: opts.workspaceDir, // Otherwise TypeScript doesn't understand that is not undefined
}, 'install')

View File

@@ -27,6 +27,7 @@ import {
InstallOptions,
MutatedProject,
mutateModules,
ProjectOptions,
} from '@pnpm/core'
import camelcaseKeys from 'camelcase-keys'
import isSubdir from 'is-subdir'
@@ -79,6 +80,7 @@ type RecursiveOptions = CreateStoreControllerOptions & Pick<Config,
ignoredPackages?: Set<string>
update?: boolean
useBetaCli?: boolean
allProjectsGraph: ProjectsGraph
selectedProjectsGraph: ProjectsGraph
preferredVersions?: PreferredVersions
pruneDirectDependencies?: boolean
@@ -107,17 +109,10 @@ export default async function recursive (
if (pkgs.length === 0) {
return false
}
const manifestsByPath: { [dir: string]: Omit<Project, 'dir'> } = {}
for (const { dir, manifest, writeProjectManifest } of allProjects) {
manifestsByPath[dir] = { manifest, writeProjectManifest }
}
const manifestsByPath = getManifestsByPath(allProjects)
const throwOnFail = throwOnCommandFail.bind(null, `pnpm recursive ${cmdFullName}`)
const chunks = opts.sort !== false
? sortPackages(opts.selectedProjectsGraph)
: [Object.keys(opts.selectedProjectsGraph).sort()]
const store = await createOrConnectStoreController(opts)
const workspacePackages = cmdFullName !== 'unlink'
@@ -126,6 +121,7 @@ export default async function recursive (
const targetDependenciesField = getSaveType(opts)
const installOpts = Object.assign(opts, {
...getOptionsFromRootManifest(manifestsByPath[opts.lockfileDir ?? opts.dir]?.manifest ?? {}),
allProjects: getAllProjects(manifestsByPath, opts.allProjectsGraph, opts.sort),
linkWorkspacePackagesDepth: opts.linkWorkspacePackages === 'deep' ? Infinity : opts.linkWorkspacePackages ? 0 : -1,
ownLifecycleHooksStdio: 'pipe',
peer: opts.savePeer,
@@ -147,25 +143,6 @@ export default async function recursive (
const memReadLocalConfig = mem(readLocalConfig)
async function getImporters () {
const importers = [] as Array<{ buildIndex: number, manifest: ProjectManifest, rootDir: string }>
await Promise.all(chunks.map(async (prefixes: string[], buildIndex) => {
if (opts.ignoredPackages != null) {
prefixes = prefixes.filter((prefix) => !opts.ignoredPackages!.has(prefix))
}
return Promise.all(
prefixes.map(async (prefix) => {
importers.push({
buildIndex,
manifest: manifestsByPath[prefix].manifest,
rootDir: prefix,
})
})
)
}))
return importers
}
const updateToLatest = opts.update && opts.latest
const includeDirect = opts.includeDirect ?? {
dependencies: true,
@@ -187,7 +164,7 @@ export default async function recursive (
}
// For a workspace with shared lockfile
if (opts.lockfileDir && ['add', 'install', 'remove', 'update', 'import'].includes(cmdFullName)) {
let importers = await getImporters()
let importers = getImporters(opts)
const calculatedRepositoryRoot = await fs.realpath(calculateRepositoryRoot(opts.workspaceDir, importers.map(x => x.rootDir)))
const isFromWorkspace = isSubdir.bind(null, calculatedRepositoryRoot)
importers = await pFilter(importers, async ({ rootDir }: { rootDir: string }) => isFromWorkspace(await fs.realpath(rootDir)))
@@ -206,7 +183,7 @@ export default async function recursive (
}
const writeProjectManifests = [] as Array<(manifest: ProjectManifest) => Promise<void>>
const mutatedImporters = [] as MutatedProject[]
await Promise.all(importers.map(async ({ buildIndex, rootDir }) => {
await Promise.all(importers.map(async ({ rootDir }) => {
const localConfig = await memReadLocalConfig(rootDir)
const modulesDir = localConfig.modulesDir ?? opts.modulesDir
const { manifest, writeProjectManifest } = manifestsByPath[rootDir]
@@ -241,7 +218,6 @@ export default async function recursive (
case 'uninstallSome':
mutatedImporters.push({
dependencyNames: currentInput,
manifest,
modulesDir,
mutation,
rootDir,
@@ -252,7 +228,6 @@ export default async function recursive (
mutatedImporters.push({
allowNew: cmdFullName === 'install' || cmdFullName === 'add',
dependencySelectors: currentInput,
manifest,
modulesDir,
mutation,
peer: opts.savePeer,
@@ -266,8 +241,6 @@ export default async function recursive (
return
case 'install':
mutatedImporters.push({
buildIndex,
manifest,
modulesDir,
mutation,
pruneDirectDependencies: opts.pruneDirectDependencies,
@@ -292,9 +265,7 @@ export default async function recursive (
return true
}
const pkgPaths = chunks.length === 0
? chunks[0]
: Object.keys(opts.selectedProjectsGraph).sort()
const pkgPaths = Object.keys(opts.selectedProjectsGraph).sort()
const limitInstallation = pLimit(opts.workspaceConcurrency ?? 4)
await Promise.all(pkgPaths.map(async (rootDir: string) =>
@@ -347,7 +318,6 @@ export default async function recursive (
const [{ manifest: newManifest }] = await mutateModules([
{
dependencyNames: currentInput,
manifest,
mutation: 'uninstallSome',
rootDir,
},
@@ -434,7 +404,6 @@ async function unlink (manifest: ProjectManifest, opts: any) { // eslint-disable
return mutateModules(
[
{
manifest,
mutation: 'unlink',
rootDir: opts.dir,
},
@@ -448,7 +417,6 @@ async function unlinkPkgs (dependencyNames: string[], manifest: ProjectManifest,
[
{
dependencyNames,
manifest,
mutation: 'unlinkSome',
rootDir: opts.dir,
},
@@ -535,3 +503,31 @@ export function createMatcher (params: string[]): UpdateDepsMatcher {
export function makeIgnorePatterns (ignoredDependencies: string[]): string[] {
return ignoredDependencies.map(depName => `!${depName}`)
}
function getAllProjects (manifestsByPath: ManifestsByPath, allProjectsGraph: ProjectsGraph, sort?: boolean): ProjectOptions[] {
const chunks = sort !== false
? sortPackages(allProjectsGraph)
: [Object.keys(allProjectsGraph).sort()]
return chunks.map((prefixes: string[], buildIndex) => prefixes.map((rootDir) => ({
buildIndex,
manifest: manifestsByPath[rootDir].manifest,
rootDir,
}))).flat()
}
interface ManifestsByPath { [dir: string]: Omit<Project, 'dir'> }
function getManifestsByPath (projects: Project[]) {
return projects.reduce((manifestsByPath, { dir, manifest, writeProjectManifest }) => {
manifestsByPath[dir] = { manifest, writeProjectManifest }
return manifestsByPath
}, {})
}
function getImporters (opts: Pick<RecursiveOptions, 'selectedProjectsGraph' | 'ignoredPackages'>) {
let rootDirs = Object.keys(opts.selectedProjectsGraph)
if (opts.ignoredPackages != null) {
rootDirs = rootDirs.filter((rootDir) => !opts.ignoredPackages!.has(rootDir))
}
return rootDirs.map((rootDir) => ({ rootDir }))
}

View File

@@ -11,9 +11,7 @@ import findWorkspacePackages, { arrayOfWorkspacePackagesToMap } from '@pnpm/find
import { getAllDependenciesFromManifest } from '@pnpm/manifest-utils'
import { createOrConnectStoreController, CreateStoreControllerOptions } from '@pnpm/store-connection-manager'
import { DependenciesField } from '@pnpm/types'
import {
mutateModules,
} from '@pnpm/core'
import { mutateModulesInSingleProject } from '@pnpm/core'
import pick from 'ramda/src/pick'
import without from 'ramda/src/without'
import renderHelp from 'render-help'
@@ -129,6 +127,7 @@ export const completion: CompletionFunc = async (cliOpts, params) => {
export async function handler (
opts: CreateStoreControllerOptions & Pick<Config,
| 'allProjects'
| 'allProjectsGraph'
| 'bail'
| 'bin'
| 'dev'
@@ -161,7 +160,13 @@ export async function handler (
optionalDependencies: opts.optional !== false,
}
if (opts.recursive && (opts.allProjects != null) && (opts.selectedProjectsGraph != null) && opts.workspaceDir) {
await recursive(opts.allProjects, params, { ...opts, include, selectedProjectsGraph: opts.selectedProjectsGraph, workspaceDir: opts.workspaceDir }, 'remove')
await recursive(opts.allProjects, params, {
...opts,
allProjectsGraph: opts.allProjectsGraph!,
include,
selectedProjectsGraph: opts.selectedProjectsGraph,
workspaceDir: opts.workspaceDir,
}, 'remove')
return
}
const store = await createOrConnectStoreController(opts)
@@ -192,17 +197,15 @@ export async function handler (
targetDependenciesField,
})
}
const [mutationResult] = await mutateModules(
[
{
binsDir: opts.bin,
dependencyNames: params,
manifest: currentManifest,
mutation: 'uninstallSome',
rootDir: opts.dir,
targetDependenciesField,
},
],
const mutationResult = await mutateModulesInSingleProject(
{
binsDir: opts.bin,
dependencyNames: params,
manifest: currentManifest,
mutation: 'uninstallSome',
rootDir: opts.dir,
targetDependenciesField,
},
removeOpts
)
await writeProjectManifest(mutationResult.manifest)

View File

@@ -2,7 +2,7 @@ import { docsUrl, readProjectManifestOnly } from '@pnpm/cli-utils'
import { UNIVERSAL_OPTIONS } from '@pnpm/common-cli-options-help'
import { Config } from '@pnpm/config'
import { createOrConnectStoreController, CreateStoreControllerOptions } from '@pnpm/store-connection-manager'
import { mutateModules } from '@pnpm/core'
import { mutateModulesInSingleProject } from '@pnpm/core'
import renderHelp from 'render-help'
import getOptionsFromRootManifest from './getOptionsFromRootManifest'
import { cliOptionsTypes, rcOptionsTypes } from './install'
@@ -44,6 +44,7 @@ export async function handler (
opts: CreateStoreControllerOptions &
Pick<Config,
| 'allProjects'
| 'allProjectsGraph'
| 'bail'
| 'bin'
| 'engineStrict'
@@ -61,7 +62,12 @@ export async function handler (
params: string[]
) {
if (opts.recursive && (opts.allProjects != null) && (opts.selectedProjectsGraph != null) && opts.workspaceDir) {
await recursive(opts.allProjects, params, { ...opts, selectedProjectsGraph: opts.selectedProjectsGraph, workspaceDir: opts.workspaceDir }, 'unlink')
await recursive(opts.allProjects, params, {
...opts,
allProjectsGraph: opts.allProjectsGraph!,
selectedProjectsGraph: opts.selectedProjectsGraph,
workspaceDir: opts.workspaceDir,
}, 'unlink')
return
}
const store = await createOrConnectStoreController(opts)
@@ -73,20 +79,16 @@ export async function handler (
})
if (!params || (params.length === 0)) {
return mutateModules([
{
dependencyNames: params,
manifest: await readProjectManifestOnly(opts.dir, opts),
mutation: 'unlinkSome',
rootDir: opts.dir,
},
], unlinkOpts)
}
return mutateModules([
{
return mutateModulesInSingleProject({
dependencyNames: params,
manifest: await readProjectManifestOnly(opts.dir, opts),
mutation: 'unlink',
mutation: 'unlinkSome',
rootDir: opts.dir,
},
], unlinkOpts)
}, unlinkOpts)
}
return mutateModulesInSingleProject({
manifest: await readProjectManifestOnly(opts.dir, opts),
mutation: 'unlink',
rootDir: opts.dir,
}, unlinkOpts)
}

View File

@@ -42,10 +42,11 @@ const DEFAULT_OPTS = {
test('import from shared yarn.lock of monorepo', async () => {
f.prepare('workspace-has-shared-yarn-lock')
const { allProjects, selectedProjectsGraph } = await readProjects(process.cwd(), [])
const { allProjects, allProjectsGraph, selectedProjectsGraph } = await readProjects(process.cwd(), [])
await importCommand.handler({
...DEFAULT_OPTS,
allProjects,
allProjects: allProjects as any, // eslint-disable-line @typescript-eslint/no-explicit-any
allProjectsGraph,
selectedProjectsGraph,
workspaceDir: process.cwd(),
lockfileDir: process.cwd(),
@@ -64,10 +65,11 @@ test('import from shared yarn.lock of monorepo', async () => {
test('import from shared package-lock.json of monorepo', async () => {
f.prepare('workspace-has-shared-package-lock-json')
const { allProjects, selectedProjectsGraph } = await readProjects(process.cwd(), [])
const { allProjects, allProjectsGraph, selectedProjectsGraph } = await readProjects(process.cwd(), [])
await importCommand.handler({
...DEFAULT_OPTS,
allProjects,
allProjects: allProjects as any, // eslint-disable-line @typescript-eslint/no-explicit-any
allProjectsGraph,
selectedProjectsGraph,
workspaceDir: process.cwd(),
lockfileDir: process.cwd(),
@@ -86,10 +88,11 @@ test('import from shared package-lock.json of monorepo', async () => {
test('import from shared npm-shrinkwrap.json of monorepo', async () => {
f.prepare('workspace-has-shared-npm-shrinkwrap-json')
const { allProjects, selectedProjectsGraph } = await readProjects(process.cwd(), [])
const { allProjects, allProjectsGraph, selectedProjectsGraph } = await readProjects(process.cwd(), [])
await importCommand.handler({
...DEFAULT_OPTS,
allProjects,
allProjects: allProjects as any, // eslint-disable-line @typescript-eslint/no-explicit-any
allProjectsGraph,
selectedProjectsGraph,
workspaceDir: process.cwd(),
lockfileDir: process.cwd(),

View File

@@ -26,10 +26,11 @@ test('recursive linking/unlinking', async () => {
},
])
const { allProjects, selectedProjectsGraph } = await readProjects(process.cwd(), [])
const { allProjects, allProjectsGraph, selectedProjectsGraph } = await readProjects(process.cwd(), [])
await install.handler({
...DEFAULT_OPTS,
allProjects,
allProjectsGraph,
dir: process.cwd(),
recursive: true,
selectedProjectsGraph,
@@ -47,6 +48,7 @@ test('recursive linking/unlinking', async () => {
await unlink.handler({
...DEFAULT_OPTS,
allProjects,
allProjectsGraph,
dir: process.cwd(),
recursive: true,
selectedProjectsGraph,
@@ -87,10 +89,11 @@ test('recursive unlink specific package', async () => {
},
])
const { allProjects, selectedProjectsGraph } = await readProjects(process.cwd(), [])
const { allProjects, allProjectsGraph, selectedProjectsGraph } = await readProjects(process.cwd(), [])
await install.handler({
...DEFAULT_OPTS,
allProjects,
allProjectsGraph,
dir: process.cwd(),
recursive: true,
selectedProjectsGraph,
@@ -108,6 +111,7 @@ test('recursive unlink specific package', async () => {
await unlink.handler({
...DEFAULT_OPTS,
allProjects,
allProjectsGraph,
dir: process.cwd(),
recursive: true,
selectedProjectsGraph,

View File

@@ -34,10 +34,11 @@ test('recursive add/remove', async () => {
},
])
const { allProjects, selectedProjectsGraph } = await readProjects(process.cwd(), [])
const { allProjects, allProjectsGraph, selectedProjectsGraph } = await readProjects(process.cwd(), [])
await install.handler({
...DEFAULT_OPTS,
allProjects,
allProjectsGraph,
dir: process.cwd(),
recursive: true,
selectedProjectsGraph,
@@ -51,6 +52,7 @@ test('recursive add/remove', async () => {
await add.handler({
...DEFAULT_OPTS,
allProjects,
allProjectsGraph,
dir: process.cwd(),
recursive: true,
selectedProjectsGraph,
@@ -63,6 +65,7 @@ test('recursive add/remove', async () => {
await remove.handler({
...DEFAULT_OPTS,
allProjects,
allProjectsGraph,
dir: process.cwd(),
recursive: true,
selectedProjectsGraph,
@@ -92,10 +95,11 @@ test('recursive add/remove in workspace with many lockfiles', async () => {
},
])
const { allProjects, selectedProjectsGraph } = await readProjects(process.cwd(), [])
const { allProjects, allProjectsGraph, selectedProjectsGraph } = await readProjects(process.cwd(), [])
await install.handler({
...DEFAULT_OPTS,
allProjects,
allProjectsGraph,
dir: process.cwd(),
recursive: true,
selectedProjectsGraph,
@@ -110,6 +114,7 @@ test('recursive add/remove in workspace with many lockfiles', async () => {
await add.handler({
...DEFAULT_OPTS,
allProjects,
allProjectsGraph,
dir: process.cwd(),
recursive: true,
selectedProjectsGraph,
@@ -122,6 +127,7 @@ test('recursive add/remove in workspace with many lockfiles', async () => {
await remove.handler({
...DEFAULT_OPTS,
allProjects,
allProjectsGraph,
dir: process.cwd(),
recursive: true,
selectedProjectsGraph,
@@ -328,10 +334,11 @@ test('second run of `recursive install` after package.json has been edited manua
},
])
const { allProjects, selectedProjectsGraph } = await readProjects(process.cwd(), [])
const { allProjects, allProjectsGraph, selectedProjectsGraph } = await readProjects(process.cwd(), [])
await install.handler({
...DEFAULT_OPTS,
allProjects,
allProjectsGraph,
dir: process.cwd(),
recursive: true,
selectedProjectsGraph,
@@ -350,6 +357,7 @@ test('second run of `recursive install` after package.json has been edited manua
await install.handler({
...DEFAULT_OPTS,
allProjects,
allProjectsGraph,
dir: process.cwd(),
recursive: true,
selectedProjectsGraph,
@@ -597,10 +605,11 @@ test('recursive install on workspace with custom lockfile-dir', async () => {
])
const lockfileDir = path.resolve('_')
const { allProjects, selectedProjectsGraph } = await readProjects(process.cwd(), [])
const { allProjects, allProjectsGraph, selectedProjectsGraph } = await readProjects(process.cwd(), [])
await install.handler({
...DEFAULT_OPTS,
allProjects,
allProjectsGraph,
dir: process.cwd(),
lockfileDir,
recursive: true,
@@ -634,10 +643,11 @@ test('recursive install in a monorepo with different modules directories', async
await fs.writeFile('project-1/.npmrc', 'modules-dir=modules_1', 'utf8')
await fs.writeFile('project-2/.npmrc', 'modules-dir=modules_2', 'utf8')
const { allProjects, selectedProjectsGraph } = await readProjects(process.cwd(), [])
const { allProjects, allProjectsGraph, selectedProjectsGraph } = await readProjects(process.cwd(), [])
await install.handler({
...DEFAULT_OPTS,
allProjects,
allProjectsGraph,
dir: process.cwd(),
recursive: true,
selectedProjectsGraph,
@@ -702,11 +712,12 @@ test('installing in monorepo with shared lockfile should work on virtual drives'
const virtualPath = process.cwd() + '-virtual-disk'
// symlink simulates windows' subst
await symlinkDir(process.cwd(), virtualPath)
const { allProjects, selectedProjectsGraph } = await readProjects(virtualPath, [])
const { allProjects, allProjectsGraph, selectedProjectsGraph } = await readProjects(virtualPath, [])
await install.handler({
...DEFAULT_OPTS,
lockfileDir: virtualPath,
allProjects,
allProjectsGraph,
dir: virtualPath,
recursive: true,
selectedProjectsGraph,

View File

@@ -77,7 +77,7 @@ type PackageSelector = string | {
}
export async function rebuildSelectedPkgs (
projects: Array<{ manifest: ProjectManifest, rootDir: string }>,
projects: Array<{ buildIndex: number, manifest: ProjectManifest, rootDir: string }>,
pkgSpecs: string[],
maybeOpts: RebuildOptions
) {
@@ -86,7 +86,7 @@ export async function rebuildSelectedPkgs (
streamParser.on('data', reporter)
}
const opts = await extendOptions(maybeOpts)
const ctx = await getContext(projects, opts)
const ctx = await getContext({ ...opts, allProjects: projects })
if (!ctx.currentLockfile || (ctx.currentLockfile.packages == null)) return
const packages = ctx.currentLockfile.packages
@@ -131,7 +131,7 @@ export async function rebuildProjects (
streamParser.on('data', reporter)
}
const opts = await extendOptions(maybeOpts)
const ctx = await getContext(projects, opts)
const ctx = await getContext({ ...opts, allProjects: projects })
let idsToRebuild: string[] = []
@@ -164,11 +164,11 @@ export async function rebuildProjects (
}
await runLifecycleHooksConcurrently(
['preinstall', 'install', 'postinstall', 'prepublish', 'prepare'],
ctx.projects,
Object.values(ctx.projects),
opts.childConcurrency || 5,
scriptsOpts
)
for (const { id, manifest } of ctx.projects) {
for (const { id, manifest } of Object.values(ctx.projects)) {
if (((manifest?.scripts) != null) && (!opts.pending || ctx.pendingBuilds.includes(id))) {
ctx.pendingBuilds.splice(ctx.pendingBuilds.indexOf(id), 1)
}
@@ -227,7 +227,7 @@ async function _rebuild (
virtualStoreDir: string
rootModulesDir: string
currentLockfile: Lockfile
projects: Array<{ id: string, rootDir: string }>
projects: Record<string, { id: string, rootDir: string }>
extraBinPaths: string[]
extraNodePaths: string[]
},
@@ -241,7 +241,7 @@ async function _rebuild (
getSubgraphToBuild(
lockfileWalker(
ctx.currentLockfile,
ctx.projects.map(({ id }) => id),
Object.values(ctx.projects).map(({ id }) => id),
{
include: {
dependencies: opts.production,
@@ -324,7 +324,7 @@ async function _rebuild (
return linkBins(modules, binPath, { warn })
}))
)
await Promise.all(ctx.projects.map(async ({ rootDir }) => limitLinking(async () => {
await Promise.all(Object.values(ctx.projects).map(async ({ rootDir }) => limitLinking(async () => {
const modules = path.join(rootDir, 'node_modules')
const binPath = path.join(modules, '.bin')
return linkBins(modules, binPath, {

View File

@@ -121,6 +121,7 @@ export async function handler (
await rebuildSelectedPkgs(
[
{
buildIndex: 0,
manifest: await readProjectManifestOnly(rebuildOpts.dir, opts),
rootDir: rebuildOpts.dir,
},

View File

@@ -203,6 +203,7 @@ export default async function run (inputArgv: string[]) {
process.exitCode = 0
return
}
config.allProjectsGraph = filterResults.allProjectsGraph
config.selectedProjectsGraph = filterResults.selectedProjectsGraph
if (isEmpty(config.selectedProjectsGraph)) {
if (printLogs) {