feat: prefer-workspace-packages

close #2136
PR #2980
This commit is contained in:
Zoltan Kochan
2020-11-16 22:25:07 +02:00
committed by GitHub
parent 171efa064e
commit 8698a7060e
14 changed files with 107 additions and 2 deletions

View 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.

View File

@@ -103,6 +103,7 @@ export interface Config {
workspaceDir?: string
reporter?: string
linkWorkspacePackages: boolean | 'deep'
preferWorkspacePackages: boolean
sort: boolean
strictPeerDependencies: boolean
lockfileDir?: string

View File

@@ -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

View File

@@ -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,

View File

@@ -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')

View File

@@ -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,

View File

@@ -286,7 +286,7 @@ export type InstallCommandOptions = Pick<Config,
useBetaCli?: boolean
recursive?: boolean
workspace?: boolean
}
} & Partial<Pick<Config, 'preferWorkspacePackages'>>
export function handler (
opts: InstallCommandOptions

View File

@@ -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')
})

View File

@@ -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

View File

@@ -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,

View File

@@ -58,6 +58,7 @@ export interface ResolveOptions {
projectDir: string
lockfileDir: string
preferredVersions: PreferredVersions
preferWorkspacePackages?: boolean
registry: string
workspacePackages?: WorkspacePackages
}

View File

@@ -90,6 +90,7 @@ export interface RequestPackageOptions {
projectDir: string
lockfileDir: string
preferredVersions: PreferredVersions
preferWorkspacePackages?: boolean
registry: string
sideEffectsCache?: boolean
skipFetch?: boolean

View File

@@ -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,

View File

@@ -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,