-
-
Notifications
You must be signed in to change notification settings - Fork 938
/
index.ts
182 lines (168 loc) · 6 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
import { createBase32Hash } from '@pnpm/crypto.base32-hash'
import { Registries } from '@pnpm/types'
import encodeRegistry from 'encode-registry'
import semver from 'semver'
export function isAbsolute (dependencyPath: string) {
return dependencyPath[0] !== '/'
}
export function resolve (
registries: Registries,
resolutionLocation: string
) {
if (!isAbsolute(resolutionLocation)) {
let registryUrl!: string
if (resolutionLocation[1] === '@') {
const slashIndex = resolutionLocation.indexOf('/', 1)
const scope = resolutionLocation.slice(1, slashIndex !== -1 ? slashIndex : 0)
registryUrl = registries[scope] || registries.default
} else {
registryUrl = registries.default
}
const registryDirectory = encodeRegistry(registryUrl)
return `${registryDirectory}${resolutionLocation}`
}
return resolutionLocation
}
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))
}
return resolve(registries, relDepPath)
}
export function refToAbsolute (
reference: string,
pkgName: string,
registries: Registries
) {
if (reference.startsWith('link:')) {
return null
}
if (!reference.includes('/') || !reference.replace(/(\([^)]+\))+$/, '').includes('/')) {
const registryName = encodeRegistry(getRegistryByPackageName(registries, pkgName))
return `${registryName}/${pkgName}/${reference}`
}
if (reference[0] !== '/') return reference
const registryName = encodeRegistry(getRegistryByPackageName(registries, pkgName))
return `${registryName}${reference}`
}
export function getRegistryByPackageName (registries: Registries, packageName: string) {
if (packageName[0] !== '@') return registries.default
const scope = packageName.substring(0, packageName.indexOf('/'))
return registries[scope] || registries.default
}
export function relative (
registries: Registries,
packageName: string,
absoluteResolutionLoc: string
) {
const registryName = encodeRegistry(getRegistryByPackageName(registries, packageName))
if (absoluteResolutionLoc.startsWith(`${registryName}/`)) {
return absoluteResolutionLoc.slice(absoluteResolutionLoc.indexOf('/'))
}
return absoluteResolutionLoc
}
export function refToRelative (
reference: string,
pkgName: string
): string | null {
if (reference.startsWith('link:')) {
return null
}
if (reference.startsWith('file:')) {
return reference
}
if (!reference.includes('/') || !reference.replace(/(\([^)]+\))+$/, '').includes('/')) {
return `/${pkgName}/${reference}`
}
return reference
}
export function parse (dependencyPath: string) {
// eslint-disable-next-line: strict-type-predicates
if (typeof dependencyPath !== 'string') {
throw new TypeError(`Expected \`dependencyPath\` to be of type \`string\`, got \`${
// eslint-disable-next-line: strict-type-predicates
dependencyPath === null ? 'null' : typeof dependencyPath
}\``)
}
const _isAbsolute = isAbsolute(dependencyPath)
const parts = dependencyPath.split('/')
if (!_isAbsolute) parts.shift()
const host = _isAbsolute ? parts.shift() : undefined
const name = parts[0].startsWith('@')
? `${parts.shift()}/${parts.shift()}` // eslint-disable-line @typescript-eslint/restrict-template-expressions
: parts.shift()
let version = parts.join('/')
if (version) {
let peerSepIndex!: number
let peersSuffix: string | undefined
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 {
host,
isAbsolute: _isAbsolute,
name,
peersSuffix,
version,
}
}
}
if (!_isAbsolute) throw new Error(`${dependencyPath} is an invalid relative dependency path`)
return {
host,
isAbsolute: _isAbsolute,
}
}
export function depPathToFilename (depPath: string) {
const filename = depPathToFilenameUnescaped(depPath).replace(/[\\/:*?"<>|]/g, '+')
if (filename.length > 120 || filename !== filename.toLowerCase() && !filename.startsWith('file+')) {
return `${filename.substring(0, 50)}_${createBase32Hash(filename)}`
}
return filename
}
function depPathToFilenameUnescaped (depPath: string) {
if (depPath.indexOf('file:') !== 0) {
if (depPath.startsWith('/')) {
depPath = depPath.substring(1)
}
const index = depPath.lastIndexOf('/', depPath.lastIndexOf('(') - 1)
return `${depPath.substring(0, index)}@${depPath.slice(index + 1)}`
}
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('+')
// We don't want the folder name to get too long.
// Otherwise, an ENAMETOOLONG error might happen.
// see: https://github.com/pnpm/pnpm/issues/977
//
// A bigger limit might be fine but the base32 encoded md5 hash will be 26 symbols,
// so for consistency's sake, we go with 26.
if (folderName.length > 26) {
return `_${createBase32Hash(folderName)}`
}
return `_${folderName}`
}