-
-
Notifications
You must be signed in to change notification settings - Fork 320
Expand file tree
/
Copy pathgetPackageResolution.ts
More file actions
151 lines (138 loc) Β· 4.84 KB
/
getPackageResolution.ts
File metadata and controls
151 lines (138 loc) Β· 4.84 KB
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
import { join, resolve } from "./path"
import { PackageDetails, getPatchDetailsFromCliString } from "./PackageDetails"
import { PackageManager, detectPackageManager } from "./detectPackageManager"
import { readFileSync, existsSync } from "fs-extra"
import { parse as parseYarnLockFile } from "@yarnpkg/lockfile"
import yaml from "yaml"
import findWorkspaceRoot from "find-yarn-workspace-root"
import { getPackageVersion } from "./getPackageVersion"
import { coerceSemVer } from "./coerceSemVer"
export function getPackageResolution({
packageDetails,
packageManager,
appPath,
}: {
packageDetails: PackageDetails
packageManager: PackageManager
appPath: string
}) {
if (packageManager === "yarn") {
let lockFilePath = "yarn.lock"
if (!existsSync(lockFilePath)) {
const workspaceRoot = findWorkspaceRoot()
if (!workspaceRoot) {
throw new Error("Can't find yarn.lock file")
}
lockFilePath = join(workspaceRoot, "yarn.lock")
}
if (!existsSync(lockFilePath)) {
throw new Error("Can't find yarn.lock file")
}
const lockFileString = readFileSync(lockFilePath).toString()
let appLockFile: Record<
string,
{
version: string
resolution?: string
resolved?: string
}
>
if (lockFileString.includes("yarn lockfile v1")) {
const parsedYarnLockFile = parseYarnLockFile(lockFileString)
if (parsedYarnLockFile.type !== "success") {
throw new Error("Could not parse yarn v1 lock file")
} else {
appLockFile = parsedYarnLockFile.object
}
} else {
try {
appLockFile = yaml.parse(lockFileString)
} catch (e) {
console.log(e)
throw new Error("Could not parse yarn v2 lock file")
}
}
const installedVersion = getPackageVersion(
join(resolve(appPath, packageDetails.path), "package.json"),
)
const entries = Object.entries(appLockFile).filter(
([k, v]) =>
k.startsWith(packageDetails.name + "@") &&
// @ts-ignore
coerceSemVer(v.version) === coerceSemVer(installedVersion),
)
const resolutions = entries.map(([_, v]) => {
return v.resolved
})
if (resolutions.length === 0) {
throw new Error(
`\`${packageDetails.pathSpecifier}\`'s installed version is ${installedVersion} but a lockfile entry for it couldn't be found. Your lockfile is likely to be corrupt or you forgot to reinstall your packages.`,
)
}
if (new Set(resolutions).size !== 1) {
console.log(
`Ambiguous lockfile entries for ${packageDetails.pathSpecifier}. Using version ${installedVersion}`,
)
return installedVersion
}
if (resolutions[0]) {
return resolutions[0]
}
const packageName = packageDetails.name
const resolutionVersion = entries[0][1].version
// `@backstage/integration@npm:^1.5.0, @backstage/integration@npm:^1.7.0, @backstage/integration@npm:^1.7.2`
// ->
// `^1.5.0 ^1.7.0 ^1.7.2`
const resolution = entries[0][0]
.replace(new RegExp(packageName + "@", "g"), "")
.replace(/npm:/g, "")
.replace(/,/g, "")
// resolve relative file path
if (resolution.startsWith("file:.")) {
return `file:${resolve(appPath, resolution.slice("file:".length))}`
}
// add `resolutionVersion` to ensure correct version, `^1.0.0` could resolve latest `v1.3.0`, but `^1.0.0 1.2.1` won't
return resolutionVersion ? resolution + " " + resolutionVersion : resolution
} else {
const lockfile = require(join(
appPath,
packageManager === "npm-shrinkwrap"
? "npm-shrinkwrap.json"
: "package-lock.json",
))
const lockFileStack = [lockfile]
for (const name of packageDetails.packageNames.slice(0, -1)) {
const child = lockFileStack[0].dependencies
if (child && name in child) {
lockFileStack.push(child[name])
}
}
lockFileStack.reverse()
const relevantStackEntry = lockFileStack.find((entry) => {
if (entry.dependencies) {
return entry.dependencies && packageDetails.name in entry.dependencies
} else if (entry.packages) {
return entry.packages && packageDetails.path in entry.packages
}
throw new Error("Cannot find dependencies or packages in lockfile")
})
const pkg = relevantStackEntry.dependencies
? relevantStackEntry.dependencies[packageDetails.name]
: relevantStackEntry.packages[packageDetails.path]
return pkg.resolved || pkg.version || pkg.from
}
}
if (require.main === module) {
const packageDetails = getPatchDetailsFromCliString(process.argv[2])
if (!packageDetails) {
console.log(`Can't find package ${process.argv[2]}`)
process.exit(1)
}
console.log(
getPackageResolution({
appPath: process.cwd(),
packageDetails,
packageManager: detectPackageManager(process.cwd(), null),
}),
)
}