mirror of
https://github.com/pnpm/pnpm.git
synced 2025-12-23 23:29:17 -05:00
12
.changeset/five-baboons-switch.md
Normal file
12
.changeset/five-baboons-switch.md
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
"@pnpm/config": minor
|
||||
"@pnpm/npm-resolver": minor
|
||||
"@pnpm/package-requester": minor
|
||||
"@pnpm/plugin-commands-installation": minor
|
||||
"@pnpm/resolve-dependencies": minor
|
||||
"@pnpm/resolver-base": minor
|
||||
"@pnpm/store-controller-types": minor
|
||||
"supi": patch
|
||||
---
|
||||
|
||||
New option added: preferWorkspacePackages. When it is `true`, dependencies are linked from the workspace even, when there are newer version available in the registry.
|
||||
@@ -103,6 +103,7 @@ export interface Config {
|
||||
workspaceDir?: string
|
||||
reporter?: string
|
||||
linkWorkspacePackages: boolean | 'deep'
|
||||
preferWorkspacePackages: boolean
|
||||
sort: boolean
|
||||
strictPeerDependencies: boolean
|
||||
lockfileDir?: string
|
||||
|
||||
@@ -59,6 +59,7 @@ export const types = Object.assign({
|
||||
'prefer-frozen-lockfile': Boolean,
|
||||
'prefer-frozen-shrinkwrap': Boolean,
|
||||
'prefer-offline': Boolean,
|
||||
'prefer-workspace-packages': Boolean,
|
||||
production: [null, true],
|
||||
'public-hoist-pattern': Array,
|
||||
'publish-branch': String,
|
||||
@@ -153,6 +154,7 @@ export default async (
|
||||
'link-workspace-packages': true,
|
||||
'package-lock': npmDefaults['package-lock'],
|
||||
pending: false,
|
||||
'prefer-workspace-packages': false,
|
||||
'public-hoist-pattern': [
|
||||
// Packages like @types/node, @babel/types
|
||||
// should be publicly hoisted because TypeScript only searches in the root of node_modules
|
||||
|
||||
@@ -97,6 +97,7 @@ export type ResolveFromNpmOptions = {
|
||||
dryRun?: boolean
|
||||
registry: string
|
||||
preferredVersions?: PreferredVersions
|
||||
preferWorkspacePackages?: boolean
|
||||
} & ({
|
||||
projectDir?: string
|
||||
workspacePackages?: undefined
|
||||
@@ -166,7 +167,7 @@ async function resolveNpm (
|
||||
}
|
||||
}
|
||||
const localVersion = pickMatchingLocalVersionOrNull(workspacePackages[pickedPackage.name], spec)
|
||||
if (localVersion && semver.gt(localVersion, pickedPackage.version)) {
|
||||
if (localVersion && (semver.gt(localVersion, pickedPackage.version) || opts.preferWorkspacePackages)) {
|
||||
return {
|
||||
...resolveFromLocalPackage(workspacePackages[pickedPackage.name][localVersion], spec.normalizedPref, opts.projectDir),
|
||||
latest: meta['dist-tags'].latest,
|
||||
|
||||
@@ -1053,6 +1053,46 @@ test('use version from the registry if it is newer than the local one', async ()
|
||||
expect(resolveResult!.manifest!.version).toBe('3.1.0')
|
||||
})
|
||||
|
||||
test('preferWorkspacePackages: use version from the workspace even if there is newer version in the registry', async () => {
|
||||
nock(registry)
|
||||
.get('/is-positive')
|
||||
.reply(200, {
|
||||
...isPositiveMeta,
|
||||
'dist-tags': { latest: '3.1.0' },
|
||||
})
|
||||
|
||||
const resolveFromNpm = createResolveFromNpm({
|
||||
storeDir: tempy.directory(),
|
||||
})
|
||||
const resolveResult = await resolveFromNpm({
|
||||
alias: 'is-positive',
|
||||
pref: '^3.0.0',
|
||||
}, {
|
||||
preferWorkspacePackages: true,
|
||||
projectDir: '/home/istvan/src',
|
||||
registry,
|
||||
workspacePackages: {
|
||||
'is-positive': {
|
||||
'3.0.0': {
|
||||
dir: '/home/istvan/src/is-positive',
|
||||
manifest: {
|
||||
name: 'is-positive',
|
||||
version: '3.0.0',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
expect(resolveResult).toStrictEqual(
|
||||
expect.objectContaining({
|
||||
resolvedVia: 'local-filesystem',
|
||||
id: 'link:is-positive',
|
||||
latest: '3.1.0',
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
test('use local version if it is newer than the latest in the registry', async () => {
|
||||
nock(registry)
|
||||
.get('/is-positive')
|
||||
|
||||
@@ -144,6 +144,7 @@ async function resolveAndFetch (
|
||||
defaultTag: options.defaultTag,
|
||||
lockfileDir: options.lockfileDir,
|
||||
preferredVersions: options.preferredVersions,
|
||||
preferWorkspacePackages: options.preferWorkspacePackages,
|
||||
projectDir: options.projectDir,
|
||||
registry: options.registry,
|
||||
workspacePackages: options.workspacePackages,
|
||||
|
||||
@@ -286,7 +286,7 @@ export type InstallCommandOptions = Pick<Config,
|
||||
useBetaCli?: boolean
|
||||
recursive?: boolean
|
||||
workspace?: boolean
|
||||
}
|
||||
} & Partial<Pick<Config, 'preferWorkspacePackages'>>
|
||||
|
||||
export function handler (
|
||||
opts: InstallCommandOptions
|
||||
|
||||
@@ -3,6 +3,7 @@ import { readProjects } from '@pnpm/filter-workspace-packages'
|
||||
import { Lockfile } from '@pnpm/lockfile-types'
|
||||
import { add, install, remove, update } from '@pnpm/plugin-commands-installation'
|
||||
import { preparePackages } from '@pnpm/prepare'
|
||||
import { addDistTag } from '@pnpm/registry-mock'
|
||||
import { ProjectManifest } from '@pnpm/types'
|
||||
import readYamlFile from 'read-yaml-file'
|
||||
import { DEFAULT_OPTS } from './utils'
|
||||
@@ -604,3 +605,41 @@ test('recursive install in a monorepo with different modules directories', async
|
||||
await projects['project-1'].has('is-positive', 'modules_1')
|
||||
await projects['project-2'].has('is-positive', 'modules_2')
|
||||
})
|
||||
|
||||
test('prefer-workspace-package', async () => {
|
||||
await addDistTag({
|
||||
distTag: 'latest',
|
||||
package: 'foo',
|
||||
version: '100.1.0',
|
||||
})
|
||||
preparePackages(undefined, [
|
||||
{
|
||||
name: 'project-1',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
foo: '^100.0.0',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'foo',
|
||||
version: '100.0.0',
|
||||
},
|
||||
])
|
||||
|
||||
await install.handler({
|
||||
...DEFAULT_OPTS,
|
||||
...await readProjects(process.cwd(), []),
|
||||
dir: process.cwd(),
|
||||
linkWorkspacePackages: true,
|
||||
preferWorkspacePackages: true,
|
||||
lockfileDir: process.cwd(),
|
||||
recursive: true,
|
||||
sharedWorkspaceLockfile: true,
|
||||
workspace: true,
|
||||
workspaceDir: process.cwd(),
|
||||
})
|
||||
|
||||
const lockfile = await readYamlFile<Lockfile>(path.resolve('pnpm-lock.yaml'))
|
||||
expect(lockfile.importers['project-1'].dependencies?.foo).toBe('link:../foo')
|
||||
})
|
||||
|
||||
@@ -136,6 +136,7 @@ export interface ResolutionContext {
|
||||
dependenciesTree: DependenciesTree<ResolvedPackage>
|
||||
force: boolean
|
||||
prefix: string
|
||||
preferWorkspacePackages?: boolean
|
||||
readPackageHook?: ReadPackageHook
|
||||
engineStrict: boolean
|
||||
modulesDir: string
|
||||
@@ -597,6 +598,7 @@ async function resolveDependency (
|
||||
downloadPriority: -options.currentDepth,
|
||||
lockfileDir: ctx.lockfileDir,
|
||||
preferredVersions: options.preferredVersions,
|
||||
preferWorkspacePackages: ctx.preferWorkspacePackages,
|
||||
projectDir: options.currentDepth > 0 ? ctx.lockfileDir : ctx.prefix,
|
||||
registry: wantedDependency.alias && pickRegistryForPackage(ctx.registries, wantedDependency.alias) || ctx.registries.default,
|
||||
// Unfortunately, even when run with --lockfile-only, we need the *real* package.json
|
||||
|
||||
@@ -56,6 +56,7 @@ export interface ResolveDependenciesOptions {
|
||||
nodeVersion: string
|
||||
registries: Registries
|
||||
pnpmVersion: string
|
||||
preferWorkspacePackages?: boolean
|
||||
updateMatching?: (pkgName: string) => boolean
|
||||
linkWorkspacePackagesDepth?: number
|
||||
lockfileDir: string
|
||||
@@ -89,6 +90,7 @@ export default async function<T> (
|
||||
outdatedDependencies: {} as {[pkgId: string]: string},
|
||||
pendingNodes: [] as PendingNode[],
|
||||
pnpmVersion: opts.pnpmVersion,
|
||||
preferWorkspacePackages: opts.preferWorkspacePackages,
|
||||
readPackageHook: opts.hooks.readPackage,
|
||||
registries: opts.registries,
|
||||
resolvedPackagesByDepPath: {} as ResolvedPackagesByDepPath,
|
||||
|
||||
@@ -58,6 +58,7 @@ export interface ResolveOptions {
|
||||
projectDir: string
|
||||
lockfileDir: string
|
||||
preferredVersions: PreferredVersions
|
||||
preferWorkspacePackages?: boolean
|
||||
registry: string
|
||||
workspacePackages?: WorkspacePackages
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@ export interface RequestPackageOptions {
|
||||
projectDir: string
|
||||
lockfileDir: string
|
||||
preferredVersions: PreferredVersions
|
||||
preferWorkspacePackages?: boolean
|
||||
registry: string
|
||||
sideEffectsCache?: boolean
|
||||
skipFetch?: boolean
|
||||
|
||||
@@ -23,6 +23,7 @@ export interface StrictInstallOptions {
|
||||
lockfileOnly: boolean
|
||||
preferFrozenLockfile: boolean
|
||||
saveWorkspaceProtocol: boolean
|
||||
preferWorkspacePackages: boolean
|
||||
preserveWorkspaceProtocol: boolean
|
||||
scriptShell?: string
|
||||
shellEmulator: boolean
|
||||
@@ -115,6 +116,7 @@ const defaults = async (opts: InstallOptions) => {
|
||||
ownLifecycleHooksStdio: 'inherit',
|
||||
packageManager,
|
||||
preferFrozenLockfile: true,
|
||||
preferWorkspacePackages: false,
|
||||
preserveWorkspaceProtocol: true,
|
||||
pruneLockfileImporters: false,
|
||||
pruneStore: false,
|
||||
|
||||
@@ -671,6 +671,7 @@ async function installInContext (
|
||||
lockfileDir: opts.lockfileDir,
|
||||
nodeVersion: opts.nodeVersion,
|
||||
pnpmVersion: opts.packageManager.name === 'pnpm' ? opts.packageManager.version : '',
|
||||
preferWorkspacePackages: opts.preferWorkspacePackages,
|
||||
preserveWorkspaceProtocol: opts.preserveWorkspaceProtocol,
|
||||
registries: opts.registries,
|
||||
saveWorkspaceProtocol: opts.saveWorkspaceProtocol,
|
||||
|
||||
Reference in New Issue
Block a user