-
-
Notifications
You must be signed in to change notification settings - Fork 282
/
getPackageResolution.ts
132 lines (120 loc) Β· 3.84 KB
/
getPackageResolution.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
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"
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
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.error(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
v.version === installedVersion,
)
const resolutions = entries.map(([_, v]) => {
// @ts-ignore
return v.resolved
})
if (resolutions.length === 0) {
throw new Error(
`Can't find lockfile entry for ${packageDetails.pathSpecifier}`,
)
}
if (new Set(resolutions).size !== 1) {
console.warn(
`Ambigious lockfile entries for ${packageDetails.pathSpecifier}. Using version ${installedVersion}`,
)
return installedVersion
}
if (resolutions[0]) {
return resolutions[0]
}
const resolution = entries[0][0].slice(packageDetails.name.length + 1)
// resolve relative file path
if (resolution.startsWith("file:.")) {
return `file:${resolve(appPath, resolution.slice("file:".length))}`
}
if (resolution.startsWith("npm:")) {
return resolution.replace("npm:", "")
}
return 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) =>
entry.dependencies && packageDetails.name in entry.dependencies,
)
const pkg = relevantStackEntry.dependencies[packageDetails.name]
return pkg.resolved || pkg.from || pkg.version
}
}
if (require.main === module) {
const packageDetails = getPatchDetailsFromCliString(process.argv[2])
if (!packageDetails) {
console.error(`Can't find package ${process.argv[2]}`)
process.exit(1)
throw new Error()
}
console.log(
getPackageResolution({
appPath: process.cwd(),
packageDetails,
packageManager: detectPackageManager(process.cwd(), null),
}),
)
}