From 0f672581537f2666d43c980ce7d972f4bd89538d Mon Sep 17 00:00:00 2001 From: Jordan Hall Date: Mon, 11 Sep 2023 21:50:15 +0100 Subject: [PATCH] feat(core): add bun package manager Bun uses yarn lock for it's binary file. Running the binary will produce the content of a yarn lock file (v1) --- docs/generated/devkit/PackageManager.md | 2 +- .../lib/create-application-files.ts | 1 + .../update-16-1-4/update-eas-scripts.ts | 1 + packages/nx/src/plugins/js/index.ts | 11 +++++-- .../nx/src/plugins/js/lock-file/lock-file.ts | 30 ++++++++++++++++++- packages/nx/src/utils/package-manager.spec.ts | 4 ++- packages/nx/src/utils/package-manager.ts | 27 +++++++++++++++-- 7 files changed, 68 insertions(+), 8 deletions(-) diff --git a/docs/generated/devkit/PackageManager.md b/docs/generated/devkit/PackageManager.md index e633d8c96104f1..0bcc0a12921588 100644 --- a/docs/generated/devkit/PackageManager.md +++ b/docs/generated/devkit/PackageManager.md @@ -1,3 +1,3 @@ # Type alias: PackageManager -Ƭ **PackageManager**: `"yarn"` \| `"pnpm"` \| `"npm"` +Ƭ **PackageManager**: `"yarn"` \| `"pnpm"` \| `"npm"` \| `"bun"` diff --git a/packages/expo/src/generators/application/lib/create-application-files.ts b/packages/expo/src/generators/application/lib/create-application-files.ts index 95b9ce18919c35..a9ba429c25432d 100644 --- a/packages/expo/src/generators/application/lib/create-application-files.ts +++ b/packages/expo/src/generators/application/lib/create-application-files.ts @@ -15,6 +15,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]; diff --git a/packages/expo/src/migrations/update-16-1-4/update-eas-scripts.ts b/packages/expo/src/migrations/update-16-1-4/update-eas-scripts.ts index 77db80bcee71fe..61f473936a0991 100644 --- a/packages/expo/src/migrations/update-16-1-4/update-eas-scripts.ts +++ b/packages/expo/src/migrations/update-16-1-4/update-eas-scripts.ts @@ -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()) { diff --git a/packages/nx/src/plugins/js/index.ts b/packages/nx/src/plugins/js/index.ts index 5898c83bc10799..6de2409943cbd3 100644 --- a/packages/nx/src/plugins/js/index.ts +++ b/packages/nx/src/plugins/js/index.ts @@ -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-graph-plugin'; @@ -50,7 +51,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)) { @@ -88,7 +92,10 @@ export const createDependencies: CreateDependencies = ( parsedLockFile ) { 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)) { diff --git a/packages/nx/src/plugins/js/lock-file/lock-file.ts b/packages/nx/src/plugins/js/lock-file/lock-file.ts index f2ec5549ab7ef2..f93f0ee64ee2e7 100644 --- a/packages/nx/src/plugins/js/lock-file/lock-file.ts +++ b/packages/nx/src/plugins/js/lock-file/lock-file.ts @@ -44,11 +44,18 @@ import { readJsonFile } from '../../../utils/fileutils'; 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} @@ -69,6 +76,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({ @@ -100,6 +112,10 @@ export function getLockFileDependencies( if (packageManager === 'npm') { return getNpmLockfileDependencies(contents, lockFileHash, projectGraph); } + if (packageManager === 'bun') { + // bun uses yarn v1 for the file format + return getYarnLockfileDependencies(contents, lockFileHash, projectGraph); + } } catch (e) { if (!isPostInstallProcess()) { output.error({ @@ -122,6 +138,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` ); @@ -142,6 +161,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}`); } @@ -174,6 +196,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 = [ diff --git a/packages/nx/src/utils/package-manager.spec.ts b/packages/nx/src/utils/package-manager.spec.ts index f89d71345267bd..90e87ca73f7a90 100644 --- a/packages/nx/src/utils/package-manager.spec.ts +++ b/packages/nx/src/utils/package-manager.spec.ts @@ -66,13 +66,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); }); }); diff --git a/packages/nx/src/utils/package-manager.ts b/packages/nx/src/utils/package-manager.ts index 3a66103548642c..58c27d1a3cfad8 100644 --- a/packages/nx/src/utils/package-manager.ts +++ b/packages/nx/src/utils/package-manager.ts @@ -12,7 +12,7 @@ import { readNxJson } from '../config/configuration'; const execAsync = promisify(exec); -export type PackageManager = 'yarn' | 'pnpm' | 'npm'; +export type PackageManager = 'yarn' | 'pnpm' | 'npm' | 'bun'; export interface PackageManagerCommands { preInstall?: string; @@ -37,6 +37,8 @@ export function detectPackageManager(dir: string = ''): PackageManager { ? 'yarn' : existsSync(join(dir, 'pnpm-lock.yaml')) ? 'pnpm' + : existsSync(join(dir, 'bun.lockb')) + ? 'bun' : 'npm') ); } @@ -113,6 +115,19 @@ export function getPackageManagerCommand( list: 'npm ls', }; }, + bun: () => { + return { + install: 'bun install', + ciInstall: 'bun install --frozen-lockfile', + add: 'bun add', + addDev: 'bun add -D', + rm: 'bun remove', + exec: 'bunx', + run: (script: string, args: string) => `bun ${script} ${args}`, + // list not aviable yet. But cant see wherte its used + list: '', + }; + }, }; return commands[packageManager](); @@ -314,12 +329,16 @@ export async function packageRegistryView( args: string ): Promise { 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 + * + * inregards to bun. Bun is highly modelled after yarn v1 + * bun info is a "subcommand reserved for future use by Bun." + * currently nothing like npm view/list exists */ pm = 'npm'; } @@ -334,13 +353,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'; }