From 71efdf95a9375108c42f6e0cb5402a4e1330404f 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/schemas/nx-schema.json | 2 +- 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 | 28 +++++++++++++++-- 8 files changed, 70 insertions(+), 9 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 4efeee32010532..a8fed6092bf9cc 100644 --- a/packages/expo/src/generators/application/lib/create-application-files.ts +++ b/packages/expo/src/generators/application/lib/create-application-files.ts @@ -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]; 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/schemas/nx-schema.json b/packages/nx/schemas/nx-schema.json index 1eb695b29894a4..62afadea594597 100644 --- a/packages/nx/schemas/nx-schema.json +++ b/packages/nx/schemas/nx-schema.json @@ -366,7 +366,7 @@ "packageManager": { "type": "string", "description": "The default package manager to use.", - "enum": ["yarn", "pnpm", "npm"] + "enum": ["yarn", "pnpm", "npm", "bun"] } } }, diff --git a/packages/nx/src/plugins/js/index.ts b/packages/nx/src/plugins/js/index.ts index 738013b4d40a3f..f0d44f93752e45 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/dependencies-and-lockfile'; @@ -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)) { @@ -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)) { 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 21687585aff122..f0b9e7d90bc1b0 100644 --- a/packages/nx/src/plugins/js/lock-file/lock-file.ts +++ b/packages/nx/src/plugins/js/lock-file/lock-file.ts @@ -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} @@ -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({ @@ -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({ @@ -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` ); @@ -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}`); } @@ -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 = [ diff --git a/packages/nx/src/utils/package-manager.spec.ts b/packages/nx/src/utils/package-manager.spec.ts index 3ba3c7d8acfe1b..c7b5660724d392 100644 --- a/packages/nx/src/utils/package-manager.spec.ts +++ b/packages/nx/src/utils/package-manager.spec.ts @@ -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); }); }); diff --git a/packages/nx/src/utils/package-manager.ts b/packages/nx/src/utils/package-manager.ts index eebfc8d33fedfb..8dfaa5dec47f4e 100644 --- a/packages/nx/src/utils/package-manager.ts +++ b/packages/nx/src/utils/package-manager.ts @@ -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; @@ -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') ); } @@ -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](); @@ -343,12 +359,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 + * + * 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 " */ pm = 'npm'; } @@ -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'; }