Skip to content

Commit

Permalink
feat(core): overrides, packageExtensions, and neverBuiltDependencies …
Browse files Browse the repository at this point in the history
…are options (#4050)
  • Loading branch information
zkochan committed Nov 30, 2021
1 parent 112d713 commit 8a99a01
Show file tree
Hide file tree
Showing 23 changed files with 152 additions and 121 deletions.
5 changes: 5 additions & 0 deletions .changeset/angry-moles-give.md
@@ -0,0 +1,5 @@
---
"@pnpm/plugin-commands-installation": major
---

Pass `packageExtensions`, `overrides`, and `neverBuiltDependencies` to the core API. Take this information from `rootProjectManifest`, which should be passed in via the options.
5 changes: 5 additions & 0 deletions .changeset/chatty-toys-talk.md
@@ -0,0 +1,5 @@
---
"@pnpm/core": major
---

`packageExtensions`, `overrides`, and `neverBuiltDependencies` are passed through as options to the core API. These settings are not read from the root manifest's `package.json`.
5 changes: 5 additions & 0 deletions .changeset/six-suits-float.md
@@ -0,0 +1,5 @@
---
"@pnpm/config": minor
---

Read the root project manifest and write it to the config object.
1 change: 1 addition & 0 deletions packages/config/package.json
Expand Up @@ -36,6 +36,7 @@
"@pnpm/error": "workspace:2.0.0",
"@pnpm/global-bin-dir": "workspace:3.0.0",
"@pnpm/pnpmfile": "workspace:1.2.0",
"@pnpm/read-project-manifest": "workspace:2.0.7",
"@pnpm/types": "workspace:7.6.0",
"@zkochan/npm-conf": "2.0.2",
"camelcase": "^6.2.0",
Expand Down
2 changes: 2 additions & 0 deletions packages/config/src/Config.ts
@@ -1,5 +1,6 @@
import {
Project,
ProjectManifest,
ProjectsGraph,
Registries,
} from '@pnpm/types'
Expand Down Expand Up @@ -140,6 +141,7 @@ export interface Config {
testPattern?: string[]
changedFilesIgnorePattern?: string[]
extendNodePath?: boolean
rootProjectManifest?: ProjectManifest
}

export interface ConfigWithDeprecatedSettings extends Config {
Expand Down
2 changes: 2 additions & 0 deletions packages/config/src/index.ts
Expand Up @@ -5,6 +5,7 @@ import { LAYOUT_VERSION } from '@pnpm/constants'
import PnpmError from '@pnpm/error'
import globalBinDir from '@pnpm/global-bin-dir'
import { requireHooks } from '@pnpm/pnpmfile'
import { safeReadProjectManifestOnly } from '@pnpm/read-project-manifest'
import camelcase from 'camelcase'
import loadNpmConf from '@zkochan/npm-conf'
import npmTypes from '@zkochan/npm-conf/lib/types'
Expand Down Expand Up @@ -483,6 +484,7 @@ export default async (
if (!pnpmConfig.ignorePnpmfile) {
pnpmConfig.hooks = requireHooks(pnpmConfig.lockfileDir ?? pnpmConfig.dir, pnpmConfig)
}
pnpmConfig.rootProjectManifest = await safeReadProjectManifestOnly(pnpmConfig.lockfileDir ?? pnpmConfig.dir) ?? undefined

return { config: pnpmConfig, warnings }
}
Expand Down
3 changes: 3 additions & 0 deletions packages/config/tsconfig.json
Expand Up @@ -24,6 +24,9 @@
{
"path": "../pnpmfile"
},
{
"path": "../read-project-manifest"
},
{
"path": "../types"
}
Expand Down
7 changes: 7 additions & 0 deletions packages/core/src/install/extendInstallOptions.ts
Expand Up @@ -6,6 +6,7 @@ import normalizeRegistries, { DEFAULT_REGISTRIES } from '@pnpm/normalize-registr
import { WorkspacePackages } from '@pnpm/resolver-base'
import { StoreController } from '@pnpm/store-controller-types'
import {
PackageExtension,
ReadPackageHook,
Registries,
} from '@pnpm/types'
Expand Down Expand Up @@ -44,8 +45,10 @@ export interface StrictInstallOptions {
rawConfig: object
verifyStoreIntegrity: boolean
engineStrict: boolean
neverBuiltDependencies: string[]
nodeExecPath?: string
nodeVersion: string
packageExtensions: Record<string, PackageExtension>
packageManager: {
name: string
version: string
Expand All @@ -67,6 +70,7 @@ export interface StrictInstallOptions {
unsafePerm: boolean
registries: Registries
tag: string
overrides: Record<string, string>
ownLifecycleHooksStdio: 'inherit' | 'pipe'
workspacePackages: WorkspacePackages
pruneStore: boolean
Expand Down Expand Up @@ -120,9 +124,12 @@ const defaults = async (opts: InstallOptions) => {
},
lockfileDir: opts.lockfileDir ?? opts.dir ?? process.cwd(),
lockfileOnly: false,
neverBuiltDependencies: [] as string[],
nodeVersion: process.version,
overrides: {},
ownLifecycleHooksStdio: 'inherit',
ignorePackageManifest: false,
packageExtensions: {},
packageManager,
preferFrozenLockfile: true,
preferWorkspacePackages: false,
Expand Down
24 changes: 7 additions & 17 deletions packages/core/src/install/index.ts
Expand Up @@ -166,19 +166,11 @@ export async function mutateModules (
// 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)
// We read Yarn's resolutions field for compatibility
// but we really replace the version specs to any other version spec, not only to exact versions,
// so we cannot call it resolutions
const overrides = (rootProjectManifest != null)
? rootProjectManifest.pnpm?.overrides ?? rootProjectManifest.resolutions
: undefined
const neverBuiltDependencies = rootProjectManifest?.pnpm?.neverBuiltDependencies ?? []
const packageExtensions = rootProjectManifest?.pnpm?.packageExtensions
opts.hooks.readPackage = createReadPackageHook({
readPackageHook: opts.hooks.readPackage,
overrides,
overrides: opts.overrides,
lockfileDir: opts.lockfileDir,
packageExtensions,
packageExtensions: opts.packageExtensions,
})
const ctx = await getContext(projects, opts)
const pruneVirtualStore = ctx.modulesFile?.prunedAt && opts.modulesCacheMaxAge > 0
Expand Down Expand Up @@ -225,15 +217,15 @@ export async function mutateModules (
}
)
}
const packageExtensionsChecksum = isEmpty(packageExtensions ?? {}) ? undefined : createObjectChecksum(packageExtensions!)
const packageExtensionsChecksum = isEmpty(opts.packageExtensions ?? {}) ? undefined : createObjectChecksum(opts.packageExtensions!)
let needsFullResolution = !maybeOpts.ignorePackageManifest && (
!equals(ctx.wantedLockfile.overrides ?? {}, overrides ?? {}) ||
!equals((ctx.wantedLockfile.neverBuiltDependencies ?? []).sort(), (neverBuiltDependencies ?? []).sort()) ||
!equals(ctx.wantedLockfile.overrides ?? {}, opts.overrides ?? {}) ||
!equals((ctx.wantedLockfile.neverBuiltDependencies ?? []).sort(), (opts.neverBuiltDependencies ?? []).sort()) ||
ctx.wantedLockfile.packageExtensionsChecksum !== packageExtensionsChecksum) ||
opts.fixLockfile
if (needsFullResolution) {
ctx.wantedLockfile.overrides = overrides
ctx.wantedLockfile.neverBuiltDependencies = neverBuiltDependencies
ctx.wantedLockfile.overrides = opts.overrides
ctx.wantedLockfile.neverBuiltDependencies = opts.neverBuiltDependencies
ctx.wantedLockfile.packageExtensionsChecksum = packageExtensionsChecksum
}
const frozenLockfile = opts.frozenLockfile ||
Expand Down Expand Up @@ -496,8 +488,6 @@ export async function mutateModules (
currentLockfileIsUpToDate: !ctx.existsWantedLockfile || ctx.currentLockfileIsUpToDate,
makePartialCurrentLockfile,
needsFullResolution,
neverBuiltDependencies,
overrides,
pruneVirtualStore,
updateLockfileMinorVersion: true,
})
Expand Down
9 changes: 4 additions & 5 deletions packages/core/test/install/lifecycleScripts.ts
Expand Up @@ -446,9 +446,9 @@ test('scripts have access to unlisted bins when hoisting is used', async () => {
test('selectively ignore scripts in some dependencies', async () => {
const project = prepareEmpty()
const neverBuiltDependencies = ['pre-and-postinstall-scripts-example']
const manifest = await addDependenciesToPackage({ pnpm: { neverBuiltDependencies } },
const manifest = await addDependenciesToPackage({},
['pre-and-postinstall-scripts-example', 'install-script-example'],
await testDefaults({ fastUnpack: false })
await testDefaults({ fastUnpack: false, neverBuiltDependencies })
)

expect(await exists('node_modules/pre-and-postinstall-scripts-example/generated-by-preinstall.js')).toBeFalsy()
Expand All @@ -462,7 +462,7 @@ test('selectively ignore scripts in some dependencies', async () => {

await rimraf('node_modules')

await install(manifest, await testDefaults({ fastUnpack: false, frozenLockfile: true }))
await install(manifest, await testDefaults({ fastUnpack: false, frozenLockfile: true, neverBuiltDependencies }))

expect(await exists('node_modules/pre-and-postinstall-scripts-example/generated-by-preinstall.js')).toBeFalsy()
expect(await exists('node_modules/pre-and-postinstall-scripts-example/generated-by-postinstall.js')).toBeFalsy()
Expand All @@ -484,15 +484,14 @@ test('lockfile is updated if neverBuiltDependencies is changed', async () => {
}

const neverBuiltDependencies = ['pre-and-postinstall-scripts-example']
manifest.pnpm = { neverBuiltDependencies }
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults())
], await testDefaults({ neverBuiltDependencies }))

{
const lockfile = await project.readLockfile()
Expand Down
20 changes: 9 additions & 11 deletions packages/core/test/install/multipleImporters.ts
Expand Up @@ -1174,16 +1174,6 @@ test('resolve a subdependency from the workspace and use it as a peer', async ()

test('resolve a subdependency from the workspace, when it uses the workspace protocol', async () => {
preparePackages([
{
location: '.',
package: {
pnpm: {
overrides: {
'dep-of-pkg-with-1-dep': 'workspace:*',
},
},
},
},
{
location: 'project',
package: { name: 'project' },
Expand Down Expand Up @@ -1229,7 +1219,14 @@ test('resolve a subdependency from the workspace, when it uses the workspace pro
},
},
}
await mutateModules(importers, await testDefaults({ linkWorkspacePackagesDepth: -1, workspacePackages }))
const overrides = {
'dep-of-pkg-with-1-dep': 'workspace:*',
}
await mutateModules(importers, await testDefaults({
linkWorkspacePackagesDepth: -1,
overrides,
workspacePackages,
}))

const project = assertProject(process.cwd())

Expand All @@ -1241,6 +1238,7 @@ test('resolve a subdependency from the workspace, when it uses the workspace pro
// Testing that headless installation does not fail with links in subdeps
await mutateModules(importers, await testDefaults({
frozenLockfile: true,
overrides,
workspacePackages,
}))
})
Expand Down
78 changes: 17 additions & 61 deletions packages/core/test/install/overrides.ts
Expand Up @@ -6,21 +6,21 @@ import {
testDefaults,
} from '../utils'

test('versions are replaced with versions specified through pnpm.overrides field', async () => {
test('versions are replaced with versions specified through overrides option', async () => {
const project = prepareEmpty()

await addDistTag({ package: 'bar', version: '100.0.0', distTag: 'latest' })
await addDistTag({ package: 'foo', version: '100.0.0', distTag: 'latest' })

const manifest = await addDependenciesToPackage({
pnpm: {
overrides: {
'foobarqar>foo': 'npm:qar@100.0.0',
'bar@^100.0.0': '100.1.0',
'dep-of-pkg-with-1-dep': '101.0.0',
},
},
}, ['pkg-with-1-dep@100.0.0', 'foobar@100.0.0', 'foobarqar@1.0.0'], await testDefaults())
const overrides = {
'foobarqar>foo': 'npm:qar@100.0.0',
'bar@^100.0.0': '100.1.0',
'dep-of-pkg-with-1-dep': '101.0.0',
}
const manifest = await addDependenciesToPackage({},
['pkg-with-1-dep@100.0.0', 'foobar@100.0.0', 'foobarqar@1.0.0'],
await testDefaults({ overrides })
)

{
const lockfile = await project.readLockfile()
Expand All @@ -44,20 +44,20 @@ test('versions are replaced with versions specified through pnpm.overrides field
mutation: 'install',
rootDir: process.cwd(),
},
], { ...await testDefaults(), ignorePackageManifest: true })
], { ...await testDefaults(), ignorePackageManifest: true, overrides })

// The lockfile is updated if the overrides are changed
manifest.pnpm!.overrides!['bar@^100.0.0'] = '100.0.0'
overrides['bar@^100.0.0'] = '100.0.0'
// A direct dependency may be overriden as well
manifest.pnpm!.overrides!['foobarqar'] = '1.0.1'
overrides['foobarqar'] = '1.0.1'
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults())
], await testDefaults({ overrides }))

{
const lockfile = await project.readLockfile()
Expand All @@ -81,7 +81,7 @@ test('versions are replaced with versions specified through pnpm.overrides field
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ frozenLockfile: true }))
], await testDefaults({ frozenLockfile: true, overrides }))

{
const lockfile = await project.readLockfile()
Expand All @@ -95,7 +95,7 @@ test('versions are replaced with versions specified through pnpm.overrides field
expect(lockfile.overrides).toStrictEqual(currentLockfile.overrides)
}

manifest.pnpm!.overrides!['bar@^100.0.0'] = '100.0.1'
overrides['bar@^100.0.0'] = '100.0.1'
await expect(
mutateModules([
{
Expand All @@ -104,54 +104,10 @@ test('versions are replaced with versions specified through pnpm.overrides field
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ frozenLockfile: true }))
], await testDefaults({ frozenLockfile: true, overrides }))
).rejects.toThrow(
new PnpmError('FROZEN_LOCKFILE_WITH_OUTDATED_LOCKFILE',
'Cannot perform a frozen installation because the lockfile needs updates'
)
)
})

test('versions are replaced with versions specified through "resolutions" field (for Yarn compatibility)', async () => {
const project = prepareEmpty()

await addDistTag({ package: 'bar', version: '100.0.0', distTag: 'latest' })

const manifest = await addDependenciesToPackage({
resolutions: {
'bar@^100.0.0': '100.1.0',
'dep-of-pkg-with-1-dep': '101.0.0',
},
}, ['pkg-with-1-dep@100.0.0', 'foobar@100.0.0'], await testDefaults())

{
const lockfile = await project.readLockfile()
expect(lockfile.packages).toHaveProperty(['/dep-of-pkg-with-1-dep/101.0.0'])
expect(lockfile.packages).toHaveProperty(['/bar/100.1.0'])
expect(lockfile.overrides).toStrictEqual({
'bar@^100.0.0': '100.1.0',
'dep-of-pkg-with-1-dep': '101.0.0',
})
}

// The lockfile is updated if the resolutions are changed
manifest.resolutions!['bar@^100.0.0'] = '100.0.0'
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults())

{
const lockfile = await project.readLockfile()
expect(lockfile.packages).toHaveProperty(['/dep-of-pkg-with-1-dep/101.0.0'])
expect(lockfile.packages).toHaveProperty(['/bar/100.0.0'])
expect(lockfile.overrides).toStrictEqual({
'bar@^100.0.0': '100.0.0',
'dep-of-pkg-with-1-dep': '101.0.0',
})
}
})

0 comments on commit 8a99a01

Please sign in to comment.