From 7456f4243e47398bca68cf70586753c0307ede0a Mon Sep 17 00:00:00 2001 From: thislight Date: Wed, 3 Sep 2025 13:33:16 +0800 Subject: [PATCH] feat: support bun Closes #3906 --- packages/api/cli/src/util/check-system.ts | 3 +++ packages/api/core/spec/slow/api.slow.spec.ts | 1 + .../core-utils/spec/package-manager.spec.ts | 20 ++++++++++++++--- .../utils/core-utils/src/electron-version.ts | 8 ++++++- .../utils/core-utils/src/package-manager.ts | 22 ++++++++++++++++--- 5 files changed, 47 insertions(+), 7 deletions(-) diff --git a/packages/api/cli/src/util/check-system.ts b/packages/api/cli/src/util/check-system.ts index e2c70493f5..1ba2fc42b2 100644 --- a/packages/api/cli/src/util/check-system.ts +++ b/packages/api/cli/src/util/check-system.ts @@ -103,6 +103,9 @@ const ALLOWLISTED_VERSIONS: Record< pnpm: { all: '>= 8.0.0', }, + bun: { + all: '>= 1.2.0', + }, }; export async function checkPackageManager() { diff --git a/packages/api/core/spec/slow/api.slow.spec.ts b/packages/api/core/spec/slow/api.slow.spec.ts index be85e3c991..dfe9c0f553 100644 --- a/packages/api/core/spec/slow/api.slow.spec.ts +++ b/packages/api/core/spec/slow/api.slow.spec.ts @@ -49,6 +49,7 @@ describe.each([ PACKAGE_MANAGERS['npm'], PACKAGE_MANAGERS['yarn'], PACKAGE_MANAGERS['pnpm'], + PACKAGE_MANAGERS['bun'], ])(`init (with $executable)`, (pm) => { let dir: string; diff --git a/packages/utils/core-utils/spec/package-manager.spec.ts b/packages/utils/core-utils/spec/package-manager.spec.ts index 408ee7ae6d..01ca26c889 100644 --- a/packages/utils/core-utils/spec/package-manager.spec.ts +++ b/packages/utils/core-utils/spec/package-manager.spec.ts @@ -41,6 +41,11 @@ describe('package-manager', () => { pm: 'npm', version: '10.9.2', }, + { + ua: 'bun/1.2.21 npm/? node/v24.3.0 linux x64', + pm: 'bun', + version: '1.2.21', + }, ])('with $ua', async ({ ua, pm, version }) => { process.env.npm_config_user_agent = ua; await expect(resolvePackageManager()).resolves.toHaveProperty( @@ -83,6 +88,15 @@ describe('package-manager', () => { 'npm', ); }); + + it('should return bun if npm_config_user_agent=bun', async () => { + process.env.npm_config_user_agent = + 'bun/1.2.21 npm/? node/v24.3.0 linux x64'; + await expect(resolvePackageManager()).resolves.toHaveProperty( + 'executable', + 'bun', + ); + }); }); describe('NODE_INSTALLER', () => { @@ -107,7 +121,7 @@ describe('package-manager', () => { }; }); - it.each([{ pm: 'yarn' }, { pm: 'npm' }, { pm: 'pnpm' }])( + it.each([{ pm: 'yarn' }, { pm: 'npm' }, { pm: 'pnpm' }, { pm: 'bun' }])( 'should return $pm if NODE_INSTALLER=$pm', async ({ pm }) => { process.env.NODE_INSTALLER = pm; @@ -124,7 +138,7 @@ describe('package-manager', () => { ); it('should return npm if package manager is unsupported', async () => { - process.env.NODE_INSTALLER = 'bun'; + process.env.NODE_INSTALLER = 'nosupppm'; console.warn = vi.fn(); vi.mocked(spawn).mockResolvedValue('1.22.22'); await expect(resolvePackageManager()).resolves.toHaveProperty( @@ -133,7 +147,7 @@ describe('package-manager', () => { ); expect(console.warn).toHaveBeenCalledWith( '⚠', - expect.stringContaining('Package manager bun is unsupported'), + expect.stringContaining('Package manager nosupppm is unsupported'), ); }); }); diff --git a/packages/utils/core-utils/src/electron-version.ts b/packages/utils/core-utils/src/electron-version.ts index f7a595564c..305007f6f3 100644 --- a/packages/utils/core-utils/src/electron-version.ts +++ b/packages/utils/core-utils/src/electron-version.ts @@ -24,7 +24,13 @@ async function findAncestorNodeModulesPath( ): Promise { d('Looking for a lock file to indicate the root of the repo'); const lockPath = await findUp( - ['package-lock.json', 'yarn.lock', 'pnpm-lock.yaml'], + [ + 'package-lock.json', + 'yarn.lock', + 'pnpm-lock.yaml', + 'bun.lock', + 'bun.lockb', + ], { cwd: dir, type: 'file' }, ); if (lockPath) { diff --git a/packages/utils/core-utils/src/package-manager.ts b/packages/utils/core-utils/src/package-manager.ts index 660ccbf916..5816b909b4 100644 --- a/packages/utils/core-utils/src/package-manager.ts +++ b/packages/utils/core-utils/src/package-manager.ts @@ -12,7 +12,7 @@ import logSymbols from 'log-symbols'; const d = debug('electron-forge:package-manager'); -export type SupportedPackageManager = 'yarn' | 'npm' | 'pnpm'; +export type SupportedPackageManager = 'yarn' | 'npm' | 'pnpm' | 'bun'; export type PMDetails = { executable: SupportedPackageManager; version?: string; @@ -45,12 +45,20 @@ export const PACKAGE_MANAGERS: Record = { dev: '--save-dev', exact: '--save-exact', }, + bun: { + executable: 'bun', + install: 'add', + dev: '--save-dev', + exact: '--exact', + }, }; const PM_FROM_LOCKFILE: Record = { 'package-lock.json': 'npm', 'yarn.lock': 'yarn', 'pnpm-lock.yaml': 'pnpm', + 'bun.lock': 'bun', + 'bun.lockb': 'bun', }; /** @@ -82,14 +90,21 @@ function pmFromUserAgent() { * * The version of the executing package manager is also returned if it is detected via user agent. * - * Supported package managers are `yarn`, `pnpm`, and `npm`. + * Supported package managers are `yarn`, `pnpm`, `npm` and `bun`. * */ export const resolvePackageManager: () => Promise = async () => { const executingPM = pmFromUserAgent(); let lockfilePM; const lockfile = await findUp( - ['package-lock.json', 'yarn.lock', 'pnpm-lock.yaml', 'pnpm-workspace.yaml'], + [ + 'package-lock.json', + 'yarn.lock', + 'pnpm-lock.yaml', + 'pnpm-workspace.yaml', + 'bun.lock', + 'bun.lockb', + ], { type: 'file' }, ); if (lockfile) { @@ -139,6 +154,7 @@ export const resolvePackageManager: () => Promise = async () => { case 'yarn': case 'npm': case 'pnpm': + case 'bun': d( `Resolved package manager to ${installer}. (Derived from NODE_INSTALLER: ${process.env.NODE_INSTALLER}, npm_config_user_agent: ${process.env.npm_config_user_agent}, lockfile: ${lockfilePM})`, );