Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/api/cli/src/util/check-system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ const ALLOWLISTED_VERSIONS: Record<
pnpm: {
all: '>= 8.0.0',
},
bun: {
all: '>= 1.2.0',
},
};

export async function checkPackageManager() {
Expand Down
1 change: 1 addition & 0 deletions packages/api/core/spec/slow/api.slow.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
20 changes: 17 additions & 3 deletions packages/utils/core-utils/spec/package-manager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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', () => {
Expand All @@ -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;
Expand All @@ -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(
Expand All @@ -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'),
);
});
});
Expand Down
8 changes: 7 additions & 1 deletion packages/utils/core-utils/src/electron-version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,13 @@ async function findAncestorNodeModulesPath(
): Promise<string | undefined> {
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) {
Expand Down
22 changes: 19 additions & 3 deletions packages/utils/core-utils/src/package-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -45,12 +45,20 @@ export const PACKAGE_MANAGERS: Record<SupportedPackageManager, PMDetails> = {
dev: '--save-dev',
exact: '--save-exact',
},
bun: {
executable: 'bun',
install: 'add',
dev: '--save-dev',
exact: '--exact',
},
};

const PM_FROM_LOCKFILE: Record<string, SupportedPackageManager> = {
'package-lock.json': 'npm',
'yarn.lock': 'yarn',
'pnpm-lock.yaml': 'pnpm',
'bun.lock': 'bun',
'bun.lockb': 'bun',
};

/**
Expand Down Expand Up @@ -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<PMDetails> = 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) {
Expand Down Expand Up @@ -139,6 +154,7 @@ export const resolvePackageManager: () => Promise<PMDetails> = 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})`,
);
Expand Down
Loading