Skip to content

Commit

Permalink
feat(core): add bun package manager
Browse files Browse the repository at this point in the history
Bun uses yarn lock for it's binary file. Running the binary will produce the content of a yarn lock file (v1)
  • Loading branch information
Jordan-Hall committed Apr 2, 2024
1 parent 120cde6 commit 71efdf9
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 9 deletions.
2 changes: 1 addition & 1 deletion docs/generated/devkit/PackageManager.md
@@ -1,3 +1,3 @@
# Type alias: PackageManager

Ƭ **PackageManager**: `"yarn"` \| `"pnpm"` \| `"npm"`
Ƭ **PackageManager**: `"yarn"` \| `"pnpm"` \| `"npm"` \| `"bun"`
Expand Up @@ -14,6 +14,7 @@ export function createApplicationFiles(host: Tree, options: NormalizedSchema) {
npm: 'package-lock.json',
yarn: 'yarn.lock',
pnpm: 'pnpm-lock.yaml',
bun: 'bun.lockb',
};
const packageManager = detectPackageManager(host.root);
const packageLockFile = packageManagerLockFile[packageManager];
Expand Down
Expand Up @@ -19,6 +19,7 @@ export default function update(tree: Tree) {
npm: 'package-lock.json',
yarn: 'yarn.lock',
pnpm: 'pnpm-lock.yaml',
bun: 'bun.lockb',
};

for (const [name, config] of projects.entries()) {
Expand Down
2 changes: 1 addition & 1 deletion packages/nx/schemas/nx-schema.json
Expand Up @@ -366,7 +366,7 @@
"packageManager": {
"type": "string",
"description": "The default package manager to use.",
"enum": ["yarn", "pnpm", "npm"]
"enum": ["yarn", "pnpm", "npm", "bun"]
}
}
},
Expand Down
11 changes: 9 additions & 2 deletions packages/nx/src/plugins/js/index.ts
Expand Up @@ -24,6 +24,7 @@ import { hashArray } from '../../hasher/file-hasher';
import { detectPackageManager } from '../../utils/package-manager';
import { workspaceRoot } from '../../utils/workspace-root';
import { nxVersion } from '../../utils/versions';
import { execSync } from 'child_process';

export const name = 'nx/js/dependencies-and-lockfile';

Expand Down Expand Up @@ -51,7 +52,10 @@ export const createNodes: CreateNodes = [
}

const lockFilePath = join(workspaceRoot, lockFile);
const lockFileContents = readFileSync(lockFilePath).toString();
const lockFileContents =
packageManager !== 'bun'
? readFileSync(lockFilePath).toString()
: execSync(lockFilePath).toString();
const lockFileHash = getLockFileHash(lockFileContents);

if (!lockFileNeedsReprocessing(lockFileHash)) {
Expand Down Expand Up @@ -91,7 +95,10 @@ export const createDependencies: CreateDependencies = (
parsedLockFile.externalNodes
) {
const lockFilePath = join(workspaceRoot, getLockFileName(packageManager));
const lockFileContents = readFileSync(lockFilePath).toString();
const lockFileContents =
packageManager !== 'bun'
? readFileSync(lockFilePath).toString()
: execSync(lockFilePath).toString();
const lockFileHash = getLockFileHash(lockFileContents);

if (!lockFileNeedsReprocessing(lockFileHash)) {
Expand Down
30 changes: 29 additions & 1 deletion packages/nx/src/plugins/js/lock-file/lock-file.ts
Expand Up @@ -45,11 +45,18 @@ import {
const YARN_LOCK_FILE = 'yarn.lock';
const NPM_LOCK_FILE = 'package-lock.json';
const PNPM_LOCK_FILE = 'pnpm-lock.yaml';
export const LOCKFILES = [YARN_LOCK_FILE, NPM_LOCK_FILE, PNPM_LOCK_FILE];
const BUN_LOCK_FILE = 'bun.lockb';
export const LOCKFILES = [
YARN_LOCK_FILE,
NPM_LOCK_FILE,
PNPM_LOCK_FILE,
BUN_LOCK_FILE,
];

const YARN_LOCK_PATH = join(workspaceRoot, YARN_LOCK_FILE);
const NPM_LOCK_PATH = join(workspaceRoot, NPM_LOCK_FILE);
const PNPM_LOCK_PATH = join(workspaceRoot, PNPM_LOCK_FILE);
const BUN_LOCK_PATH = join(workspaceRoot, BUN_LOCK_FILE);

/**
* Parses lock file and maps dependencies and metadata to {@link LockFileGraph}
Expand All @@ -73,6 +80,11 @@ export function getLockFileNodes(
if (packageManager === 'npm') {
return getNpmLockfileNodes(contents, lockFileHash);
}
if (packageManager === 'bun') {
// bun uses yarn v1 for the file format
const packageJson = readJsonFile('package.json');
return getYarnLockfileNodes(contents, lockFileHash, packageJson);
}
} catch (e) {
if (!isPostInstallProcess()) {
output.error({
Expand Down Expand Up @@ -104,6 +116,10 @@ export function getLockFileDependencies(
if (packageManager === 'npm') {
return getNpmLockfileDependencies(contents, lockFileHash, context);
}
if (packageManager === 'bun') {
// bun uses yarn v1 for the file format
return getYarnLockfileDependencies(contents, lockFileHash, context);
}
} catch (e) {
if (!isPostInstallProcess()) {
output.error({
Expand All @@ -126,6 +142,9 @@ export function lockFileExists(packageManager: PackageManager): boolean {
if (packageManager === 'npm') {
return existsSync(NPM_LOCK_PATH);
}
if (packageManager === 'bun') {
return existsSync(BUN_LOCK_PATH);
}
throw new Error(
`Unknown package manager ${packageManager} or lock file missing`
);
Expand All @@ -146,6 +165,9 @@ export function getLockFileName(packageManager: PackageManager): string {
if (packageManager === 'npm') {
return NPM_LOCK_FILE;
}
if (packageManager === 'bun') {
return BUN_LOCK_FILE;
}
throw new Error(`Unknown package manager: ${packageManager}`);
}

Expand Down Expand Up @@ -191,6 +213,12 @@ export function createLockFile(
const prunedGraph = pruneProjectGraph(graph, packageJson);
return stringifyNpmLockfile(prunedGraph, content, normalizedPackageJson);
}
if (packageManager === 'bun') {
output.log({
title:
"Unable to create bun lock files. Run bun install it's just as quick",
});
}
} catch (e) {
if (!isPostInstallProcess()) {
const additionalInfo = [
Expand Down
4 changes: 3 additions & 1 deletion packages/nx/src/utils/package-manager.spec.ts
Expand Up @@ -68,13 +68,15 @@ describe('package-manager', () => {
return false;
case 'package-lock.json':
return false;
case 'bun.lockb':
return false;
default:
return jest.requireActual('fs').existsSync(p);
}
});
const packageManager = detectPackageManager();
expect(packageManager).toEqual('npm');
expect(fs.existsSync).toHaveBeenCalledTimes(5);
expect(fs.existsSync).toHaveBeenCalledTimes(6);
});
});

Expand Down
28 changes: 25 additions & 3 deletions packages/nx/src/utils/package-manager.ts
Expand Up @@ -13,7 +13,7 @@ import { workspaceRoot } from './workspace-root';

const execAsync = promisify(exec);

export type PackageManager = 'yarn' | 'pnpm' | 'npm';
export type PackageManager = 'yarn' | 'pnpm' | 'npm' | 'bun';

export interface PackageManagerCommands {
preInstall?: string;
Expand All @@ -40,6 +40,8 @@ export function detectPackageManager(dir: string = ''): PackageManager {
? 'yarn'
: existsSync(join(dir, 'pnpm-lock.yaml'))
? 'pnpm'
: existsSync(join(dir, 'bun.lockb'))
? 'bun'
: 'npm')
);
}
Expand Down Expand Up @@ -142,6 +144,20 @@ export function getPackageManagerCommand(
list: 'npm ls',
};
},
bun: () => {
return {
install: 'bun install',
ciInstall: 'bun install --no-cache',
updateLockFile: 'bun install --frozen-lockfile',
add: 'bun install',
addDev: 'bun install -D',
rm: 'bun rm',
exec: 'bun',
dlx: 'bunx',
run: (script: string, args: string) => `bun run ${script} -- ${args}`,
list: 'bun pm ls',
};
},
};

return commands[packageManager]();
Expand Down Expand Up @@ -343,12 +359,16 @@ export async function packageRegistryView(
args: string
): Promise<string> {
let pm = detectPackageManager();
if (pm === 'yarn') {
if (pm === 'yarn' || pm === 'bun') {
/**
* yarn has `yarn info` but it behaves differently than (p)npm,
* which makes it's usage unreliable
*
* @see https://github.com/nrwl/nx/pull/9667#discussion_r842553994
*
* Bun has a pm ls function but it only relates to it's lockfile
* and acts differently from all other package managers
* from Jarred"it probably would be bun pm view <package-name>"
*/
pm = 'npm';
}
Expand All @@ -363,13 +383,15 @@ export async function packageRegistryPack(
version: string
): Promise<{ tarballPath: string }> {
let pm = detectPackageManager();
if (pm === 'yarn') {
if (pm === 'yarn' || pm === 'bun') {
/**
* `(p)npm pack` will download a tarball of the specified version,
* whereas `yarn` pack creates a tarball of the active workspace, so it
* does not work for getting the content of a library.
*
* @see https://github.com/nrwl/nx/pull/9667#discussion_r842553994
*
* bun doesn't current support pack
*/
pm = 'npm';
}
Expand Down

0 comments on commit 71efdf9

Please sign in to comment.