From e036d6b3306ab48a8eec4c246209dfc36920f75c Mon Sep 17 00:00:00 2001 From: Strengthless Date: Mon, 11 Sep 2023 00:02:51 +0800 Subject: [PATCH 1/8] Add support for Bun --- src/detectPackageManager.ts | 18 +++++++++++++----- src/getPackageResolution.ts | 29 +++++++++++++++++++++-------- src/parseBunLockfile.ts | 15 +++++++++++++++ 3 files changed, 49 insertions(+), 13 deletions(-) create mode 100644 src/parseBunLockfile.ts diff --git a/src/detectPackageManager.ts b/src/detectPackageManager.ts index c7bb7559..9aff405d 100644 --- a/src/detectPackageManager.ts +++ b/src/detectPackageManager.ts @@ -4,7 +4,7 @@ import chalk from "chalk" import process from "process" import findWorkspaceRoot from "find-yarn-workspace-root" -export type PackageManager = "yarn" | "npm" | "npm-shrinkwrap" +export type PackageManager = "yarn" | "npm" | "npm-shrinkwrap" | "bun" function printNoYarnLockfileError() { console.log(` @@ -17,9 +17,9 @@ ${chalk.red.bold("**ERROR**")} ${chalk.red( function printNoLockfilesError() { console.log(` ${chalk.red.bold("**ERROR**")} ${chalk.red( - `No package-lock.json, npm-shrinkwrap.json, or yarn.lock file. + `No package-lock.json, npm-shrinkwrap.json, yarn.lock, or bun.lockb file. -You must use either npm@>=5, yarn, or npm-shrinkwrap to manage this project's +You must use either npm@>=5, yarn, npm-shrinkwrap, or bun to manage this project's dependencies.`, )} `) @@ -47,7 +47,13 @@ export const detectPackageManager = ( const shrinkWrapExists = fs.existsSync( join(appRootPath, "npm-shrinkwrap.json"), ) - const yarnLockExists = fs.existsSync(join(appRootPath, "yarn.lock")) + const yarnLockExists = fs.existsSync( + join(findWorkspaceRoot() ?? appRootPath, "pnpm-lock.yaml"), + ) + // Bun workspaces seem to work the same as yarn workspaces - https://bun.sh/docs/install/workspaces + const bunLockbExists = fs.existsSync( + join(findWorkspaceRoot() ?? appRootPath, "bun.lockb"), + ) if ((packageLockExists || shrinkWrapExists) && yarnLockExists) { if (overridePackageManager) { return overridePackageManager @@ -62,8 +68,10 @@ export const detectPackageManager = ( } else { return shrinkWrapExists ? "npm-shrinkwrap" : "npm" } - } else if (yarnLockExists || findWorkspaceRoot()) { + } else if (yarnLockExists) { return "yarn" + } else if (bunLockbExists) { + return "bun" } else { printNoLockfilesError() process.exit(1) diff --git a/src/getPackageResolution.ts b/src/getPackageResolution.ts index a046e7f9..2d740070 100644 --- a/src/getPackageResolution.ts +++ b/src/getPackageResolution.ts @@ -7,6 +7,7 @@ import yaml from "yaml" import findWorkspaceRoot from "find-yarn-workspace-root" import { getPackageVersion } from "./getPackageVersion" import { coerceSemVer } from "./coerceSemVer" +import { parseBunLockfile } from "./parseBunLockfile" export function getPackageResolution({ packageDetails, @@ -17,24 +18,32 @@ export function getPackageResolution({ packageManager: PackageManager appPath: string }) { - if (packageManager === "yarn") { - let lockFilePath = "yarn.lock" + if (packageManager === "yarn" || packageManager === "bun") { + const isBun = packageManager === "bun" + const lockFileName = isBun ? "bun.lockb" : "yarn.lock" + let lockFilePath = lockFileName if (!existsSync(lockFilePath)) { const workspaceRoot = findWorkspaceRoot() if (!workspaceRoot) { - throw new Error("Can't find yarn.lock file") + throw new Error(`Can't find ${lockFileName} file`) } - lockFilePath = join(workspaceRoot, "yarn.lock") + lockFilePath = join(workspaceRoot, lockFilePath) } if (!existsSync(lockFilePath)) { - throw new Error("Can't find yarn.lock file") + throw new Error(`Can't find ${lockFileName} file`) } - const lockFileString = readFileSync(lockFilePath).toString() + const lockFileString = isBun + ? parseBunLockfile(lockFilePath) + : 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") + throw new Error( + `Could not parse yarn v1 lock file ${ + isBun ? "- was originally a bun.lockb file" : "" + }`, + ) } else { appLockFile = parsedYarnLockFile.object } @@ -43,7 +52,11 @@ export function getPackageResolution({ appLockFile = yaml.parse(lockFileString) } catch (e) { console.log(e) - throw new Error("Could not parse yarn v2 lock file") + throw new Error( + `Could not parse yarn v2 lock file ${ + isBun ? "- was originally a bun.lockb file (should not happen)" : "" + }`, + ) } } diff --git a/src/parseBunLockfile.ts b/src/parseBunLockfile.ts new file mode 100644 index 00000000..66e5facb --- /dev/null +++ b/src/parseBunLockfile.ts @@ -0,0 +1,15 @@ +import { spawnSync } from "child_process" + +// From https://github.com/oven-sh/bun/blob/ffe4f561a3af53b9f5a41c182de55d7199b5d692/packages/bun-vscode/src/features/lockfile.ts#L39, +// rewritten to use spawnSync instead of spawn. +export function parseBunLockfile(lockFilePath: string): string { + const process = spawnSync("bun", [lockFilePath], { + stdio: ["ignore", "pipe", "pipe"], + }) + if (process.status !== 0) { + throw new Error( + `Bun exited with code: ${process.status}\n${process.stderr.toString()}`, + ) + } + return process.stdout.toString() +} From 6226e9804d60e44d5ce7c837414982a37ae7ef22 Mon Sep 17 00:00:00 2001 From: Strengthless Date: Mon, 11 Sep 2023 00:06:25 +0800 Subject: [PATCH 2/8] Fixes a few typo --- src/detectPackageManager.ts | 2 +- src/parseBunLockfile.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/detectPackageManager.ts b/src/detectPackageManager.ts index 9aff405d..8ebf7f04 100644 --- a/src/detectPackageManager.ts +++ b/src/detectPackageManager.ts @@ -48,7 +48,7 @@ export const detectPackageManager = ( join(appRootPath, "npm-shrinkwrap.json"), ) const yarnLockExists = fs.existsSync( - join(findWorkspaceRoot() ?? appRootPath, "pnpm-lock.yaml"), + join(findWorkspaceRoot() ?? appRootPath, "yarn.lock"), ) // Bun workspaces seem to work the same as yarn workspaces - https://bun.sh/docs/install/workspaces const bunLockbExists = fs.existsSync( diff --git a/src/parseBunLockfile.ts b/src/parseBunLockfile.ts index 66e5facb..2ebed2d0 100644 --- a/src/parseBunLockfile.ts +++ b/src/parseBunLockfile.ts @@ -1,6 +1,6 @@ import { spawnSync } from "child_process" -// From https://github.com/oven-sh/bun/blob/ffe4f561a3af53b9f5a41c182de55d7199b5d692/packages/bun-vscode/src/features/lockfile.ts#L39, +// Adapted from https://github.com/oven-sh/bun/blob/ffe4f561a3af53b9f5a41c182de55d7199b5d692/packages/bun-vscode/src/features/lockfile.ts#L39, // rewritten to use spawnSync instead of spawn. export function parseBunLockfile(lockFilePath: string): string { const process = spawnSync("bun", [lockFilePath], { From d1d25b6a0c832d4da97e79df14434a9d254a4507 Mon Sep 17 00:00:00 2001 From: Strengthless Date: Mon, 11 Sep 2023 02:02:07 +0800 Subject: [PATCH 3/8] Handle multiple lockfiles including Bun --- README.md | 6 +++--- src/detectPackageManager.ts | 10 ++++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index c3a910b0..bbba3c82 100644 --- a/README.md +++ b/README.md @@ -152,9 +152,9 @@ team. - `--use-yarn` - By default, patch-package checks whether you use npm or yarn based on which - lockfile you have. If you have both, it uses npm by default. Set this option - to override that default and always use yarn. + By default, patch-package checks whether you use npm, yarn or bun based on + which lockfile you have. If you have multiple lockfiles, it uses npm by + default. Set this option to override that default and always use yarn. - `--exclude ` diff --git a/src/detectPackageManager.ts b/src/detectPackageManager.ts index 8ebf7f04..9508bae7 100644 --- a/src/detectPackageManager.ts +++ b/src/detectPackageManager.ts @@ -29,7 +29,7 @@ function printSelectingDefaultMessage() { console.info( `${chalk.bold( "patch-package", - )}: you have both yarn.lock and package-lock.json + )}: you have multiple lockfiles, e.g. yarn.lock and package-lock.json Defaulting to using ${chalk.bold("npm")} You can override this setting by passing --use-yarn or deleting package-lock.json if you don't need it @@ -54,7 +54,13 @@ export const detectPackageManager = ( const bunLockbExists = fs.existsSync( join(findWorkspaceRoot() ?? appRootPath, "bun.lockb"), ) - if ((packageLockExists || shrinkWrapExists) && yarnLockExists) { + if ( + [ + packageLockExists || shrinkWrapExists, + yarnLockExists, + bunLockbExists, + ].filter(Boolean).length > 1 + ) { if (overridePackageManager) { return overridePackageManager } else { From 7482db96c1a4e59c3ecace0409330e0c24b125a7 Mon Sep 17 00:00:00 2001 From: Strengthless Date: Mon, 11 Sep 2023 02:12:50 +0800 Subject: [PATCH 4/8] Update README.md with bun setup instructions --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index bbba3c82..80f11b6b 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,13 @@ yarn 2+ have native support for patching dependencies via [`yarn patch`](https://yarnpkg.com/cli/patch). You do not need to use patch-package on these projects. +### bun + + bun add patch-package + +You can use `--dev` if you don't need to run bun in production, e.g. if you're +making a web frontend. + ### pnpm pnpm has native support for patching dependencies via From 51ee0383555113389afca770b7082dde1e5549d8 Mon Sep 17 00:00:00 2001 From: Strengthless Date: Mon, 11 Sep 2023 02:34:12 +0800 Subject: [PATCH 5/8] Handle --use-yarn in case of only bun.lockb existing --- src/detectPackageManager.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/detectPackageManager.ts b/src/detectPackageManager.ts index 9508bae7..cf3712c2 100644 --- a/src/detectPackageManager.ts +++ b/src/detectPackageManager.ts @@ -77,7 +77,12 @@ export const detectPackageManager = ( } else if (yarnLockExists) { return "yarn" } else if (bunLockbExists) { - return "bun" + if (overridePackageManager === "yarn") { + printNoYarnLockfileError() + process.exit(1) + } else { + return "bun" + } } else { printNoLockfilesError() process.exit(1) From 571aa3577c2e1dbe31b8f9dabd0e0bbf859a68c7 Mon Sep 17 00:00:00 2001 From: Strengthless Date: Mon, 11 Sep 2023 02:59:09 +0800 Subject: [PATCH 6/8] Added --use-bun CLI flag --- README.md | 4 +++ src/detectPackageManager.ts | 49 ++++++++++++++++++++++++------------- src/index.ts | 12 ++++++--- 3 files changed, 44 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 80f11b6b..305f9890 100644 --- a/README.md +++ b/README.md @@ -163,6 +163,10 @@ team. which lockfile you have. If you have multiple lockfiles, it uses npm by default. Set this option to override that default and always use yarn. +- `--use-bun` + + Similar to --use-yarn, but for bun. + - `--exclude ` Ignore paths matching the regexp when creating patch files. Paths are relative diff --git a/src/detectPackageManager.ts b/src/detectPackageManager.ts index cf3712c2..fa37feeb 100644 --- a/src/detectPackageManager.ts +++ b/src/detectPackageManager.ts @@ -14,6 +14,14 @@ ${chalk.red.bold("**ERROR**")} ${chalk.red( `) } +function printNoBunLockfileError() { + console.log(` +${chalk.red.bold("**ERROR**")} ${chalk.red( + `The --use-bun option was specified but there is no bun.lockb file`, + )} +`) +} + function printNoLockfilesError() { console.log(` ${chalk.red.bold("**ERROR**")} ${chalk.red( @@ -31,12 +39,26 @@ function printSelectingDefaultMessage() { "patch-package", )}: you have multiple lockfiles, e.g. yarn.lock and package-lock.json Defaulting to using ${chalk.bold("npm")} -You can override this setting by passing --use-yarn or deleting -package-lock.json if you don't need it +You can override this setting by passing --use-yarn, --use-bun, or +deleting the conflicting lockfile if you don't need it `, ) } +function checkForYarnOverride(overridePackageManager: PackageManager | null) { + if (overridePackageManager === "yarn") { + printNoYarnLockfileError() + process.exit(1) + } +} + +function checkForBunOverride(overridePackageManager: PackageManager | null) { + if (overridePackageManager === "bun") { + printNoBunLockfileError() + process.exit(1) + } +} + export const detectPackageManager = ( appRootPath: string, overridePackageManager: PackageManager | null, @@ -63,26 +85,19 @@ export const detectPackageManager = ( ) { if (overridePackageManager) { return overridePackageManager - } else { - printSelectingDefaultMessage() - return shrinkWrapExists ? "npm-shrinkwrap" : "npm" } + printSelectingDefaultMessage() + return shrinkWrapExists ? "npm-shrinkwrap" : "npm" } else if (packageLockExists || shrinkWrapExists) { - if (overridePackageManager === "yarn") { - printNoYarnLockfileError() - process.exit(1) - } else { - return shrinkWrapExists ? "npm-shrinkwrap" : "npm" - } + checkForYarnOverride(overridePackageManager) + checkForBunOverride(overridePackageManager) + return shrinkWrapExists ? "npm-shrinkwrap" : "npm" } else if (yarnLockExists) { + checkForBunOverride(overridePackageManager) return "yarn" } else if (bunLockbExists) { - if (overridePackageManager === "yarn") { - printNoYarnLockfileError() - process.exit(1) - } else { - return "bun" - } + checkForYarnOverride(overridePackageManager) + return "bun" } else { printNoLockfilesError() process.exit(1) diff --git a/src/index.ts b/src/index.ts index 8ee449a9..7c82bccf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -84,7 +84,7 @@ if (argv.version || argv.v) { ) const packageManager = detectPackageManager( appPath, - argv["use-yarn"] ? "yarn" : null, + argv["use-yarn"] ? "yarn" : argv["use-bun"] ? "bun" : null, ) const createIssue = argv["create-issue"] packageNames.forEach((packagePathSpecifier: string) => { @@ -198,9 +198,13 @@ Usage: ${chalk.bold("--use-yarn")} - By default, patch-package checks whether you use npm or yarn based on - which lockfile you have. If you have both, it uses npm by default. - Set this option to override that default and always use yarn. + By default, patch-package checks whether you use npm, yarn or bun based on + which lockfile you have. If you have multiple lockfiles, it uses npm by + default. Set this option to override that default and always use yarn. + + ${chalk.bold("--use-bun")} + + Similar to --use-yarn, but for bun. ${chalk.bold("--exclude ")} From bb0a0b1c763949bfba39bcbe2c4855601f6af6b9 Mon Sep 17 00:00:00 2001 From: Strengthless Date: Mon, 11 Sep 2023 03:03:29 +0800 Subject: [PATCH 7/8] Update README about precedence of two --use flags --- README.md | 3 ++- src/index.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 305f9890..9b6f648f 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,8 @@ team. - `--use-bun` - Similar to --use-yarn, but for bun. + Similar to --use-yarn, but for bun. If both --use-yarn and --use-bun are + specified, --use-yarn takes precedence. - `--exclude ` diff --git a/src/index.ts b/src/index.ts index 7c82bccf..22b3c222 100644 --- a/src/index.ts +++ b/src/index.ts @@ -204,7 +204,8 @@ Usage: ${chalk.bold("--use-bun")} - Similar to --use-yarn, but for bun. + Similar to --use-yarn, but for bun. If both --use-yarn and --use-bun are + specified, --use-yarn takes precedence. ${chalk.bold("--exclude ")} From 0491b915e20e9e5305c336ae565098bef608c9b5 Mon Sep 17 00:00:00 2001 From: Strengthless Date: Mon, 11 Sep 2023 04:15:26 +0800 Subject: [PATCH 8/8] fix: error related to npm defaulting when only bun and yarn exist --- README.md | 3 ++- src/detectPackageManager.ts | 17 +++++++++++++++++ src/index.ts | 3 ++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9b6f648f..54521de2 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,8 @@ team. By default, patch-package checks whether you use npm, yarn or bun based on which lockfile you have. If you have multiple lockfiles, it uses npm by - default. Set this option to override that default and always use yarn. + default (in cases where npm is not available, it will resort to yarn). Set + this option to override that default and always use yarn. - `--use-bun` diff --git a/src/detectPackageManager.ts b/src/detectPackageManager.ts index fa37feeb..5f2ae183 100644 --- a/src/detectPackageManager.ts +++ b/src/detectPackageManager.ts @@ -45,6 +45,18 @@ deleting the conflicting lockfile if you don't need it ) } +function printSelectingDefaultYarnMessage() { + console.info( + `${chalk.bold( + "patch-package", + )}: you have both yarn.lock and bun.lockb lockfiles +Defaulting to using ${chalk.bold("yarn")} +You can override this setting by passing --use-bun, or +deleting yarn.lock if you don't need it +`, + ) +} + function checkForYarnOverride(overridePackageManager: PackageManager | null) { if (overridePackageManager === "yarn") { printNoYarnLockfileError() @@ -86,6 +98,11 @@ export const detectPackageManager = ( if (overridePackageManager) { return overridePackageManager } + if (!packageLockExists && !shrinkWrapExists) { + // The only case where we don't want to default to npm is when we have both yarn and bun lockfiles. + printSelectingDefaultYarnMessage() + return "yarn" + } printSelectingDefaultMessage() return shrinkWrapExists ? "npm-shrinkwrap" : "npm" } else if (packageLockExists || shrinkWrapExists) { diff --git a/src/index.ts b/src/index.ts index 22b3c222..31a92fec 100644 --- a/src/index.ts +++ b/src/index.ts @@ -200,7 +200,8 @@ Usage: By default, patch-package checks whether you use npm, yarn or bun based on which lockfile you have. If you have multiple lockfiles, it uses npm by - default. Set this option to override that default and always use yarn. + default (in cases where npm is not available, it will resort to yarn). Set + this option to override that default and always use yarn. ${chalk.bold("--use-bun")}