pnpm

Форк
0
134 строки · 4.7 Кб
1
import path from 'path'
2
import npa from '@pnpm/npm-package-arg'
3
import { resolveWorkspaceRange } from '@pnpm/resolve-workspace-range'
4
import { parsePref, workspacePrefToNpm } from '@pnpm/npm-resolver'
5
import { type BaseManifest } from '@pnpm/types'
6
import mapValues from 'ramda/src/map'
7

8
export interface Package {
9
  manifest: BaseManifest
10
  dir: string
11
}
12

13
export interface PackageNode<Pkg extends Package> {
14
  package: Pkg
15
  dependencies: string[]
16
}
17

18
export function createPkgGraph<Pkg extends Package> (pkgs: Pkg[], opts?: {
19
  ignoreDevDeps?: boolean
20
  linkWorkspacePackages?: boolean
21
}): {
22
    graph: Record<string, PackageNode<Pkg>>
23
    unmatched: Array<{ pkgName: string, range: string }>
24
  } {
25
  const pkgMap = createPkgMap(pkgs)
26
  const pkgMapValues = Object.values(pkgMap)
27
  let pkgMapByManifestName: Record<string, Package[] | undefined> | undefined
28
  let pkgMapByDir: Record<string, Package | undefined> | undefined
29
  const unmatched: Array<{ pkgName: string, range: string }> = []
30
  const graph = mapValues((pkg) => ({
31
    dependencies: createNode(pkg),
32
    package: pkg,
33
  }), pkgMap) as Record<string, PackageNode<Pkg>>
34
  return { graph, unmatched }
35

36
  function createNode (pkg: Package): string[] {
37
    const dependencies = {
38
      ...pkg.manifest.peerDependencies,
39
      ...(!opts?.ignoreDevDeps && pkg.manifest.devDependencies),
40
      ...pkg.manifest.optionalDependencies,
41
      ...pkg.manifest.dependencies,
42
    }
43

44
    return Object.entries(dependencies)
45
      .map(([depName, rawSpec]) => {
46
        let spec!: { fetchSpec: string, type: string }
47
        const isWorkspaceSpec = rawSpec.startsWith('workspace:')
48
        try {
49
          if (isWorkspaceSpec) {
50
            const { fetchSpec, name } = parsePref(workspacePrefToNpm(rawSpec), depName, 'latest', '')!
51
            rawSpec = fetchSpec
52
            depName = name
53
          }
54
          spec = npa.resolve(depName, rawSpec, pkg.dir)
55
        } catch {
56
          return ''
57
        }
58

59
        if (spec.type === 'directory') {
60
          pkgMapByDir ??= getPkgMapByDir(pkgMapValues)
61
          const resolvedPath = path.resolve(pkg.dir, spec.fetchSpec)
62
          const found = pkgMapByDir[resolvedPath]
63
          if (found) {
64
            return found.dir
65
          }
66

67
          // Slow path; only needed when there are case mismatches on case-insensitive filesystems.
68
          const matchedPkg = pkgMapValues.find(pkg => path.relative(pkg.dir, spec.fetchSpec) === '')
69
          if (matchedPkg == null) {
70
            return ''
71
          }
72
          pkgMapByDir[resolvedPath] = matchedPkg
73
          return matchedPkg.dir
74
        }
75

76
        if (spec.type !== 'version' && spec.type !== 'range') return ''
77

78
        pkgMapByManifestName ??= getPkgMapByManifestName(pkgMapValues)
79
        const pkgs = pkgMapByManifestName[depName]
80
        if (!pkgs || pkgs.length === 0) return ''
81
        const versions = pkgs.filter(({ manifest }) => manifest.version)
82
          .map(pkg => pkg.manifest.version) as string[]
83

84
        // explicitly check if false, backwards-compatibility (can be undefined)
85
        const strictWorkspaceMatching = opts?.linkWorkspacePackages === false && !isWorkspaceSpec
86
        if (strictWorkspaceMatching) {
87
          unmatched.push({ pkgName: depName, range: rawSpec })
88
          return ''
89
        }
90
        if (isWorkspaceSpec && versions.length === 0) {
91
          const matchedPkg = pkgs.find(pkg => pkg.manifest.name === depName)
92
          return matchedPkg!.dir
93
        }
94
        if (versions.includes(rawSpec)) {
95
          const matchedPkg = pkgs.find(pkg => pkg.manifest.name === depName && pkg.manifest.version === rawSpec)
96
          return matchedPkg!.dir
97
        }
98
        const matched = resolveWorkspaceRange(rawSpec, versions)
99
        if (!matched) {
100
          unmatched.push({ pkgName: depName, range: rawSpec })
101
          return ''
102
        }
103
        const matchedPkg = pkgs.find(pkg => pkg.manifest.name === depName && pkg.manifest.version === matched)
104
        return matchedPkg!.dir
105
      })
106
      .filter(Boolean)
107
  }
108
}
109

110
function createPkgMap (pkgs: Package[]): Record<string, Package> {
111
  const pkgMap: Record<string, Package> = {}
112
  for (const pkg of pkgs) {
113
    pkgMap[pkg.dir] = pkg
114
  }
115
  return pkgMap
116
}
117

118
function getPkgMapByManifestName (pkgMapValues: Package[]): Record<string, Package[] | undefined> {
119
  const pkgMapByManifestName: Record<string, Package[] | undefined> = {}
120
  for (const pkg of pkgMapValues) {
121
    if (pkg.manifest.name) {
122
      (pkgMapByManifestName[pkg.manifest.name] ??= []).push(pkg)
123
    }
124
  }
125
  return pkgMapByManifestName
126
}
127

128
function getPkgMapByDir (pkgMapValues: Package[]): Record<string, Package | undefined> {
129
  const pkgMapByDir: Record<string, Package | undefined> = {}
130
  for (const pkg of pkgMapValues) {
131
    pkgMapByDir[path.resolve(pkg.dir)] = pkg
132
  }
133
  return pkgMapByDir
134
}
135

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.