Skip to content

Commit ae90c02

Browse files
committed
feat: support yarn v2+ w/wo corepack enabled
fix #272 fix #496 close #506
1 parent c7c63bf commit ae90c02

File tree

2 files changed

+52
-17
lines changed

2 files changed

+52
-17
lines changed

src/getPackageResolution.ts

+22-9
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,14 @@ export function getPackageResolution({
3030
throw new Error("Can't find yarn.lock file")
3131
}
3232
const lockFileString = readFileSync(lockFilePath).toString()
33-
let appLockFile
33+
let appLockFile: Record<
34+
string,
35+
{
36+
version: string
37+
resolution?: string
38+
resolved?: string
39+
}
40+
>
3441
if (lockFileString.includes("yarn lockfile v1")) {
3542
const parsedYarnLockFile = parseYarnLockFile(lockFileString)
3643
if (parsedYarnLockFile.type !== "success") {
@@ -59,7 +66,6 @@ export function getPackageResolution({
5966
)
6067

6168
const resolutions = entries.map(([_, v]) => {
62-
// @ts-ignore
6369
return v.resolved
6470
})
6571

@@ -71,7 +77,7 @@ export function getPackageResolution({
7177

7278
if (new Set(resolutions).size !== 1) {
7379
console.log(
74-
`Ambigious lockfile entries for ${packageDetails.pathSpecifier}. Using version ${installedVersion}`,
80+
`Ambiguous lockfile entries for ${packageDetails.pathSpecifier}. Using version ${installedVersion}`,
7581
)
7682
return installedVersion
7783
}
@@ -80,18 +86,25 @@ export function getPackageResolution({
8086
return resolutions[0]
8187
}
8288

83-
const resolution = entries[0][0].slice(packageDetails.name.length + 1)
89+
const packageName = packageDetails.name
90+
91+
const resolutionVersion = entries[0][1].version
92+
93+
// `@backstage/integration@npm:^1.5.0, @backstage/integration@npm:^1.7.0, @backstage/integration@npm:^1.7.2`
94+
// ->
95+
// `^1.5.0 ^1.7.0 ^1.7.2`
96+
const resolution = entries[0][0]
97+
.replace(new RegExp(packageName + "@", "g"), "")
98+
.replace(/npm:/g, "")
99+
.replace(/,/g, "")
84100

85101
// resolve relative file path
86102
if (resolution.startsWith("file:.")) {
87103
return `file:${resolve(appPath, resolution.slice("file:".length))}`
88104
}
89105

90-
if (resolution.startsWith("npm:")) {
91-
return resolution.replace("npm:", "")
92-
}
93-
94-
return resolution
106+
// add `resolutionVersion` to ensure correct version, `^1.0.0` could resolve latest `v1.3.0`, but `^1.0.0 1.2.1` won't
107+
return resolutionVersion ? resolution + " " + resolutionVersion : resolution
95108
} else {
96109
const lockfile = require(join(
97110
appPath,

src/makePatch.ts

+30-8
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ export function makePatch({
172172
writeFileSync(
173173
tmpRepoPackageJsonPath,
174174
JSON.stringify({
175+
// support `corepack` enabled without `.yarn/releases`
176+
packageManager: appPackageJson.packageManager,
175177
dependencies: {
176178
[packageDetails.name]: getPackageResolution({
177179
packageDetails,
@@ -193,7 +195,14 @@ export function makePatch({
193195
// copy .npmrc/.yarnrc in case packages are hosted in private registry
194196
// copy .yarn directory as well to ensure installations work in yarn 2
195197
// tslint:disable-next-line:align
196-
;[".npmrc", ".yarnrc", ".yarn"].forEach((rcFile) => {
198+
;[
199+
".npmrc",
200+
".yarnrc",
201+
".yarnrc.yml",
202+
// don't include the whole `.yarn` directory which could contain huge `cache`
203+
".yarn/plugins",
204+
".yarn/releases",
205+
].forEach((rcFile) => {
197206
const rcPath = join(appPath, rcFile)
198207
if (existsSync(rcPath)) {
199208
copySync(rcPath, join(tmpRepo.name, rcFile), { dereference: true })
@@ -205,10 +214,19 @@ export function makePatch({
205214
chalk.grey("•"),
206215
`Installing ${packageDetails.name}@${packageVersion} with yarn`,
207216
)
217+
const yarnArgs = ["install"]
218+
const yarnVersionCmd = spawnSafeSync(`yarn`, ["--version"], {
219+
cwd: tmpRepoNpmRoot,
220+
logStdErrOnError: false,
221+
})
222+
const isYarnV1 = yarnVersionCmd.stdout.toString().startsWith("1.")
223+
if (isYarnV1) {
224+
yarnArgs.push("--ignore-engines")
225+
}
208226
try {
209227
// try first without ignoring scripts in case they are required
210228
// this works in 99.99% of cases
211-
spawnSafeSync(`yarn`, ["install", "--ignore-engines"], {
229+
spawnSafeSync(`yarn`, yarnArgs, {
212230
cwd: tmpRepoNpmRoot,
213231
logStdErrOnError: false,
214232
})
@@ -217,7 +235,7 @@ export function makePatch({
217235
// an implicit context which we haven't reproduced
218236
spawnSafeSync(
219237
`yarn`,
220-
["install", "--ignore-engines", "--ignore-scripts"],
238+
[...yarnArgs, isYarnV1 ? "--ignore-scripts" : "--mode=skip-build"],
221239
{
222240
cwd: tmpRepoNpmRoot,
223241
},
@@ -338,9 +356,8 @@ export function makePatch({
338356
try {
339357
parsePatchFile(diffResult.stdout.toString())
340358
} catch (e) {
341-
if (
342-
(e as Error).message.includes("Unexpected file mode string: 120000")
343-
) {
359+
const err = e as Error
360+
if (err.message.includes("Unexpected file mode string: 120000")) {
344361
console.log(`
345362
⛔️ ${chalk.red.bold("ERROR")}
346363
@@ -358,7 +375,7 @@ export function makePatch({
358375
outPath,
359376
gzipSync(
360377
JSON.stringify({
361-
error: { message: e.message, stack: e.stack },
378+
error: { message: err.message, stack: err.stack },
362379
patch: diffResult.stdout.toString(),
363380
}),
364381
),
@@ -544,7 +561,12 @@ export function makePatch({
544561
}
545562
}
546563
} catch (e) {
547-
console.log(e)
564+
const err = e as Error & {
565+
stdout?: Buffer
566+
stderr?: Buffer
567+
}
568+
// try to log more useful error message
569+
console.log(err.stderr?.toString() || err.stdout?.toString() || e)
548570
throw e
549571
} finally {
550572
tmpRepo.removeCallback()

0 commit comments

Comments
 (0)