Files
pnpm/packages/dependency-path/src/index.ts
2024-01-08 11:57:44 +01:00

125 lines
3.6 KiB
TypeScript

import { createBase32Hash } from '@pnpm/crypto.base32-hash'
import { type Registries } from '@pnpm/types'
import semver from 'semver'
export function isAbsolute (dependencyPath: string) {
return dependencyPath[0] !== '/'
}
export function indexOfPeersSuffix (depPath: string) {
if (!depPath.endsWith(')')) return -1
let open = true
for (let i = depPath.length - 2; i >= 0; i--) {
if (depPath[i] === '(') {
open = false
} else if (depPath[i] === ')') {
if (open) return -1
open = true
} else if (!open) {
return i + 1
}
}
return -1
}
export function tryGetPackageId (relDepPath: string) {
if (relDepPath[0] !== '/') {
return null
}
const sepIndex = indexOfPeersSuffix(relDepPath)
if (sepIndex !== -1) {
return relDepPath.substring(0, sepIndex)
}
return relDepPath
}
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 refToRelative (
reference: string,
pkgName: string
): string | null {
if (reference.startsWith('link:')) {
return null
}
if (reference.startsWith('file:')) {
return reference
}
if (!reference.includes('/') || reference.includes('(') && reference.lastIndexOf('/', reference.indexOf('(')) === -1) {
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 sepIndex = dependencyPath.indexOf('@', 2)
if (sepIndex === -1) {
return {}
}
const name = dependencyPath.substring(1, sepIndex)
let version = dependencyPath.substring(sepIndex + 1)
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)
}
}
if (semver.valid(version)) {
return {
name,
peersSuffix,
version,
}
}
}
return {
}
}
const MAX_LENGTH_WITHOUT_HASH = 120 - 26 - 1
export function depPathToFilename (depPath: string) {
let filename = depPathToFilenameUnescaped(depPath).replace(/[\\/:*?"<>|]/g, '+')
if (filename.includes('(')) {
filename = filename
.replace(/(\)\()|\(/g, '_')
.replace(/\)$/, '')
}
if (filename.length > 120 || filename !== filename.toLowerCase() && !filename.startsWith('file+')) {
return `${filename.substring(0, MAX_LENGTH_WITHOUT_HASH)}_${createBase32Hash(filename)}`
}
return filename
}
function depPathToFilenameUnescaped (depPath: string) {
if (depPath.indexOf('file:') !== 0) {
if (depPath[0] === '/') {
depPath = depPath.substring(1)
}
const index = depPath.indexOf('@', 1)
if (index === -1) return depPath
return `${depPath.substring(0, index)}@${depPath.slice(index + 1)}`
}
return depPath.replace(':', '+')
}
export function createPeersFolderSuffix (peers: Array<{ name: string, version: string }>): string {
const folderName = peers.map(({ name, version }) => `${name}@${version}`).sort().join(')(')
return `(${folderName})`
}