Compare commits

...

1 Commits

Author SHA1 Message Date
Zoltan Kochan 3c6620afe2
feat: lockfile v6 2023-01-06 23:02:17 +02:00
10 changed files with 56 additions and 10 deletions

View File

@ -88,6 +88,7 @@ export interface Config {
registrySupportsTimeField?: boolean
failedToLoadBuiltInConfig: boolean
resolvePeersFromWorkspaceRoot?: boolean
useLockfileV6?: boolean
// proxy
httpProxy?: string

View File

@ -111,6 +111,7 @@ export const types = Object.assign({
'strict-peer-dependencies': Boolean,
'use-beta-cli': Boolean,
'use-inline-specifiers-lockfile-format': Boolean,
'use-lockfile-v6': Boolean,
'use-node-version': String,
'use-running-store-server': Boolean,
'use-store-server': Boolean,

View File

@ -30,6 +30,10 @@ export function tryGetPackageId (registries: Registries, relDepPath: string) {
if (relDepPath[0] !== '/') {
return null
}
const sepIndex = relDepPath.indexOf('(', relDepPath.lastIndexOf('/'))
if (sepIndex !== -1) {
return resolve(registries, relDepPath.slice(0, sepIndex))
}
const underscoreIndex = relDepPath.indexOf('_', relDepPath.lastIndexOf('/'))
if (underscoreIndex !== -1) {
return resolve(registries, relDepPath.slice(0, underscoreIndex))
@ -45,7 +49,7 @@ export function refToAbsolute (
if (reference.startsWith('link:')) {
return null
}
if (!reference.includes('/')) {
if (!reference.includes('/') || !reference.replace(/(\([^)]+\))+$/, '').includes('/')) {
const registryName = encodeRegistry(getRegistryByPackageName(registries, pkgName))
return `${registryName}/${pkgName}/${reference}`
}
@ -83,7 +87,7 @@ export function refToRelative (
if (reference.startsWith('file:')) {
return reference
}
if (!reference.includes('/')) {
if (!reference.includes('/') || !reference.replace(/(\([^)]+\))+$/, '').includes('/')) {
return `/${pkgName}/${reference}`
}
return reference
@ -104,13 +108,22 @@ export function parse (dependencyPath: string) {
const name = parts[0].startsWith('@')
? `${parts.shift()}/${parts.shift()}` // eslint-disable-line @typescript-eslint/restrict-template-expressions
: parts.shift()
let version = parts.shift()
let version = parts.join('/')
if (version) {
const underscoreIndex = version.indexOf('_')
let peerSepIndex!: number
let peersSuffix: string | undefined
if (underscoreIndex !== -1) {
peersSuffix = version.substring(underscoreIndex + 1)
version = version.substring(0, underscoreIndex)
if (version.includes('(') && version.endsWith(')')) {
peerSepIndex = version.indexOf('(')
if (peerSepIndex !== -1) {
peersSuffix = version.substring(peerSepIndex)
version = version.substring(0, peerSepIndex)
}
} else {
peerSepIndex = version.indexOf('_')
if (peerSepIndex !== -1) {
peersSuffix = version.substring(peerSepIndex + 1)
version = version.substring(0, peerSepIndex)
}
}
if (semver.valid(version)) {
return {
@ -148,6 +161,11 @@ function depPathToFilenameUnescaped (depPath: string) {
return depPath.replace(':', '+')
}
export function createPeersFolderSuffixNewFormat (peers: Array<{ name: string, version: string }>): string {
const folderName = peers.map(({ name, version }) => `${name}@${version}`).sort().join(')(')
return `(${folderName})`
}
export function createPeersFolderSuffix (peers: Array<{ name: string, version: string }>): string {
const folderName = peers.map(({ name, version }) => `${name.replace('/', '+')}@${version}`).sort().join('+')

View File

@ -82,6 +82,22 @@ test('parse()', () => {
version: '1.0.0',
})
expect(parse('example.com/foo/1.0.0(bar@2.0.0)')).toStrictEqual({
host: 'example.com',
isAbsolute: true,
name: 'foo',
peersSuffix: '(bar@2.0.0)',
version: '1.0.0',
})
expect(parse('/foo/1.0.0(@types/babel__core@7.1.14)(foo@1.0.0)')).toStrictEqual({
host: undefined,
isAbsolute: false,
name: 'foo',
peersSuffix: '(@types/babel__core@7.1.14)(foo@1.0.0)',
version: '1.0.0',
})
expect(() => parse('/foo/bar')).toThrow(/\/foo\/bar is an invalid relative dependency path/)
})
@ -143,4 +159,5 @@ test('depPathToFilename()', () => {
test('tryGetPackageId', () => {
expect(tryGetPackageId({ default: 'https://registry.npmjs.org/' }, '/foo/1.0.0_@types+babel__core@7.1.14')).toEqual('registry.npmjs.org/foo/1.0.0')
expect(tryGetPackageId({ default: 'https://registry.npmjs.org/' }, '/foo/1.0.0(@types+babel__core@7.1.14)')).toEqual('registry.npmjs.org/foo/1.0.0')
})

View File

@ -120,6 +120,7 @@ export interface StrictInstallOptions {
allProjects: ProjectOptions[]
resolveSymlinksInInjectedDirs: boolean
dedupeDirectDeps: boolean
useLockfileV6: boolean
}
export type InstallOptions =
@ -206,6 +207,7 @@ const defaults = async (opts: InstallOptions) => {
resolveSymlinksInInjectedDirs: false,
dedupeDirectDeps: false,
resolvePeersFromWorkspaceRoot: false,
useLockfileV6: false,
} as StrictInstallOptions
}

View File

@ -835,6 +835,7 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
patchedDependencies: opts.patchedDependencies,
lockfileIncludeTarballUrl: opts.lockfileIncludeTarballUrl,
resolvePeersFromWorkspaceRoot: opts.resolvePeersFromWorkspaceRoot,
useLockfileV6: opts.useLockfileV6,
}
)
if (!opts.include.optionalDependencies || !opts.include.devDependencies || !opts.include.dependencies) {

View File

@ -20,7 +20,7 @@ export function depPathToRef (
}
if (depPath[0] === '/' && opts.alias === opts.realName) {
const ref = depPath.replace(`/${opts.realName}/`, '')
if (!ref.includes('/')) return ref
if (!ref.includes('/') || !ref.replace(/(\([^)]+\))+$/, '').includes('/')) return ref
}
return depPath
}

View File

@ -208,6 +208,7 @@ export async function resolveDependencies (
projects: projectsToLink,
virtualStoreDir: opts.virtualStoreDir,
resolvePeersFromWorkspaceRoot: Boolean(opts.resolvePeersFromWorkspaceRoot),
useLockfileV6: Boolean(opts.useLockfileV6),
})
for (const { id, manifest } of projectsToLink) {

View File

@ -86,6 +86,7 @@ export interface ResolveDependenciesOptions {
virtualStoreDir: string
wantedLockfile: Lockfile
workspacePackages: WorkspacePackages
useLockfileV6?: boolean
}
export async function resolveDependencyTree<T> (

View File

@ -6,7 +6,7 @@ import {
PeerDependencyIssues,
PeerDependencyIssuesByProjects,
} from '@pnpm/types'
import { depPathToFilename, createPeersFolderSuffix } from '@pnpm/dependency-path'
import { depPathToFilename, createPeersFolderSuffix, createPeersFolderSuffixNewFormat } from '@pnpm/dependency-path'
import { KeyValuePair } from 'ramda'
import isEmpty from 'ramda/src/isEmpty'
import mapValues from 'ramda/src/map'
@ -62,6 +62,7 @@ export function resolvePeers<T extends PartialResolvedPackage> (
virtualStoreDir: string
lockfileDir: string
resolvePeersFromWorkspaceRoot?: boolean
useLockfileV6: boolean
}
): {
dependenciesGraph: GenericDependenciesGraph<T>
@ -91,6 +92,7 @@ export function resolvePeers<T extends PartialResolvedPackage> (
purePkgs: new Set(),
rootDir,
virtualStoreDir: opts.virtualStoreDir,
useLockfileV6: opts.useLockfileV6,
})
if (!isEmpty(peerDependencyIssues.bad) || !isEmpty(peerDependencyIssues.missing)) {
peerDependencyIssuesByProjects[id] = {
@ -176,6 +178,7 @@ function resolvePeersOfNode<T extends PartialResolvedPackage> (
purePkgs: Set<string> // pure packages are those that don't rely on externally resolved peers
rootDir: string
lockfileDir: string
useLockfileV6: boolean
}
): PeersResolution {
const node = ctx.dependenciesTree[nodeId]
@ -256,7 +259,7 @@ function resolvePeersOfNode<T extends PartialResolvedPackage> (
if (isEmpty(allResolvedPeers)) {
depPath = resolvedPackage.depPath
} else {
const peersFolderSuffix = createPeersFolderSuffix(
const peersFolderSuffix = (ctx.useLockfileV6 ? createPeersFolderSuffixNewFormat : createPeersFolderSuffix)(
Object.entries(allResolvedPeers)
.map(([alias, nodeId]) => {
if (nodeId.startsWith('link:')) {
@ -372,6 +375,7 @@ function resolvePeersOfChildren<T extends PartialResolvedPackage> (
dependenciesTree: DependenciesTree<T>
rootDir: string
lockfileDir: string
useLockfileV6: boolean
}
): PeersResolution {
const allResolvedPeers: Record<string, string> = {}