mirror of
https://github.com/pnpm/pnpm.git
synced 2025-12-24 23:58:07 -05:00
6
.changeset/empty-teachers-fry.md
Normal file
6
.changeset/empty-teachers-fry.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@pnpm/plugin-commands-installation": patch
|
||||
"pnpm": patch
|
||||
---
|
||||
|
||||
Warn when linking a package with peerDependencies [615](https://github.com/pnpm/pnpm/issues/615).
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
type LinkFunctionOptions,
|
||||
type WorkspacePackages,
|
||||
} from '@pnpm/core'
|
||||
import { logger } from '@pnpm/logger'
|
||||
import pLimit from 'p-limit'
|
||||
import pathAbsolute from 'path-absolute'
|
||||
import pick from 'ramda/src/pick'
|
||||
@@ -35,6 +36,17 @@ const isWindows = process.platform === 'win32' || global['FAKE_WINDOWS']
|
||||
const isFilespec = isWindows ? /^(?:[.]|~[/]|[/\\]|[a-zA-Z]:)/ : /^(?:[.]|~[/]|[/]|[a-zA-Z]:)/
|
||||
const installLimit = pLimit(4)
|
||||
|
||||
type LinkOpts = CreateStoreControllerOptions & Pick<Config,
|
||||
| 'bin'
|
||||
| 'cliOptions'
|
||||
| 'engineStrict'
|
||||
| 'saveDev'
|
||||
| 'saveOptional'
|
||||
| 'saveProd'
|
||||
| 'workspaceDir'
|
||||
| 'sharedWorkspaceLockfile'
|
||||
> & Partial<Pick<Config, 'linkWorkspacePackages'>>
|
||||
|
||||
export const rcOptionsTypes = cliOptionsTypes
|
||||
|
||||
export function cliOptionsTypes () {
|
||||
@@ -82,17 +94,29 @@ export function help () {
|
||||
})
|
||||
}
|
||||
|
||||
async function checkPeerDeps (linkCwdDir: string, opts: LinkOpts) {
|
||||
const { manifest } = await tryReadProjectManifest(linkCwdDir, opts)
|
||||
|
||||
if (manifest?.peerDependencies && Object.keys(manifest.peerDependencies).length > 0) {
|
||||
const packageName = manifest.name ?? path.basename(linkCwdDir) // Assuming the name property exists in newManifest
|
||||
const peerDeps = Object.entries(manifest.peerDependencies)
|
||||
.map(([key, value]) => ` - ${key}@${value}`)
|
||||
.join(', ')
|
||||
|
||||
logger.warn({
|
||||
message: `The package ${packageName}, which you have just pnpm linked, has the following peerDependencies specified in its package.json:
|
||||
|
||||
${peerDeps}
|
||||
|
||||
The linked in dependency will not resolve the peer dependencies from the target node_modules.
|
||||
This might cause issues in your project. To resolve this, you may use the "file:" protocol to reference the local dependency.`,
|
||||
prefix: opts.dir,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export async function handler (
|
||||
opts: CreateStoreControllerOptions & Pick<Config,
|
||||
| 'bin'
|
||||
| 'cliOptions'
|
||||
| 'engineStrict'
|
||||
| 'saveDev'
|
||||
| 'saveOptional'
|
||||
| 'saveProd'
|
||||
| 'workspaceDir'
|
||||
| 'sharedWorkspaceLockfile'
|
||||
> & Partial<Pick<Config, 'linkWorkspacePackages'>>,
|
||||
opts: LinkOpts,
|
||||
params?: string[]
|
||||
) {
|
||||
const cwd = process.cwd()
|
||||
@@ -122,6 +146,9 @@ export async function handler (
|
||||
if (path.relative(linkOpts.dir, cwd) === '') {
|
||||
throw new PnpmError('LINK_BAD_PARAMS', 'You must provide a parameter')
|
||||
}
|
||||
|
||||
await checkPeerDeps(linkCwdDir, opts)
|
||||
|
||||
const { manifest, writeProjectManifest } = await tryReadProjectManifest(opts.dir, opts)
|
||||
const newManifest = await addDependenciesToPackage(
|
||||
manifest ?? {},
|
||||
@@ -185,6 +212,12 @@ export async function handler (
|
||||
|
||||
const { manifest, writeProjectManifest } = await readProjectManifest(linkCwdDir, opts)
|
||||
|
||||
await Promise.all(
|
||||
pkgPaths.map(async (dir) => {
|
||||
await checkPeerDeps(dir, opts)
|
||||
})
|
||||
)
|
||||
|
||||
const linkConfig = await getConfig(
|
||||
{ ...opts.cliOptions, dir: cwd },
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@ import { install, link } from '@pnpm/plugin-commands-installation'
|
||||
import { prepare, preparePackages } from '@pnpm/prepare'
|
||||
import { assertProject, isExecutable } from '@pnpm/assert-project'
|
||||
import { fixtures } from '@pnpm/test-fixtures'
|
||||
import { logger } from '@pnpm/logger'
|
||||
import { sync as loadJsonFile } from 'load-json-file'
|
||||
import PATH from 'path-name'
|
||||
import writePkg from 'write-pkg'
|
||||
@@ -276,3 +277,83 @@ test('link fails if nothing is linked', async () => {
|
||||
}, [])
|
||||
).rejects.toThrow(/You must provide a parameter/)
|
||||
})
|
||||
|
||||
test('logger warns about peer dependencies when linking', async () => {
|
||||
prepare()
|
||||
|
||||
const warnMock = jest.spyOn(logger, 'warn')
|
||||
|
||||
process.chdir('..')
|
||||
const globalDir = path.resolve('global')
|
||||
|
||||
await writePkg('linked-with-peer-deps', {
|
||||
name: 'linked-with-peer-deps',
|
||||
version: '1.0.0',
|
||||
peerDependencies: {
|
||||
'some-peer-dependency': '1.0.0',
|
||||
},
|
||||
})
|
||||
|
||||
process.chdir('linked-with-peer-deps')
|
||||
|
||||
const linkOpts = {
|
||||
...DEFAULT_OPTS,
|
||||
bin: path.join(globalDir, 'bin'),
|
||||
dir: globalDir,
|
||||
}
|
||||
await link.handler({
|
||||
...linkOpts,
|
||||
})
|
||||
|
||||
process.chdir('..')
|
||||
process.chdir('project')
|
||||
|
||||
await link.handler({
|
||||
...linkOpts,
|
||||
}, ['linked-with-peer-deps'])
|
||||
|
||||
expect(warnMock).toHaveBeenCalledWith(expect.objectContaining({
|
||||
message: expect.stringContaining('has the following peerDependencies specified in its package.json'),
|
||||
}))
|
||||
|
||||
warnMock.mockRestore()
|
||||
})
|
||||
|
||||
test('logger should not warn about peer dependencies when it is an empty object', async () => {
|
||||
prepare()
|
||||
|
||||
const warnMock = jest.spyOn(logger, 'warn')
|
||||
|
||||
process.chdir('..')
|
||||
const globalDir = path.resolve('global')
|
||||
|
||||
await writePkg('linked-with-empty-peer-deps', {
|
||||
name: 'linked-with-empty-peer-deps',
|
||||
version: '1.0.0',
|
||||
peerDependencies: {},
|
||||
})
|
||||
|
||||
process.chdir('linked-with-empty-peer-deps')
|
||||
|
||||
const linkOpts = {
|
||||
...DEFAULT_OPTS,
|
||||
bin: path.join(globalDir, 'bin'),
|
||||
dir: globalDir,
|
||||
}
|
||||
await link.handler({
|
||||
...linkOpts,
|
||||
})
|
||||
|
||||
process.chdir('..')
|
||||
process.chdir('project')
|
||||
|
||||
await link.handler({
|
||||
...linkOpts,
|
||||
}, ['linked-with-empty-peer-deps'])
|
||||
|
||||
expect(warnMock).not.toHaveBeenCalledWith(expect.objectContaining({
|
||||
message: expect.stringContaining('has the following peerDependencies specified in its package.json'),
|
||||
}))
|
||||
|
||||
warnMock.mockRestore()
|
||||
})
|
||||
Reference in New Issue
Block a user