From 57ce466502212f3c6f9aea593122fc84f613a3e1 Mon Sep 17 00:00:00 2001 From: Colin McDonnell Date: Thu, 3 Aug 2023 18:00:38 -0700 Subject: [PATCH] Add `--use-bun` to `create-next-app` (#53467) This adds support for `--use-bun` to `create-next-app` to use `bun install` when bootstrapping a new project. ``` npx create-next-app --use-bun ``` As with Yarn and pnpm, it reads from `npm_config_user_agent` to determine if the user ran `bunx create-next-app`. If so, it defaults to using Bun. ```sh bunx create-next-app ``` ## For Contributors ### Improving Documentation - [x] Run `pnpm prettier-fix` - [x] `pnpm build && pnpm lint` - [x] Added test to `test/integration/create-next-app/package-manager.test.ts` --------- --- .../02-api-reference/06-create-next-app.mdx | 4 + packages/create-next-app/README.md | 4 + .../helpers/get-pkg-manager.ts | 6 +- packages/create-next-app/helpers/install.ts | 6 + packages/create-next-app/index.ts | 11 ++ .../create-next-app/package-manager.test.ts | 148 ++++++++++++++++++ 6 files changed, 178 insertions(+), 1 deletion(-) diff --git a/docs/02-app/02-api-reference/06-create-next-app.mdx b/docs/02-app/02-api-reference/06-create-next-app.mdx index 2ee652e108..15480de510 100644 --- a/docs/02-app/02-api-reference/06-create-next-app.mdx +++ b/docs/02-app/02-api-reference/06-create-next-app.mdx @@ -92,6 +92,10 @@ Options: Explicitly tell the CLI to bootstrap the app using Yarn + --use-bun + + Explicitly tell the CLI to bootstrap the app using Bun + -e, --example [name]|[github-url] An example to bootstrap the app with. You can use an example name diff --git a/packages/create-next-app/README.md b/packages/create-next-app/README.md index 2c4dccfcae..423c39bdfc 100644 --- a/packages/create-next-app/README.md +++ b/packages/create-next-app/README.md @@ -53,6 +53,10 @@ Options: Explicitly tell the CLI to bootstrap the app using Yarn + --use-bun + + Explicitly tell the CLI to bootstrap the app using Bun + -e, --example [name]|[github-url] An example to bootstrap the app with. You can use an example name diff --git a/packages/create-next-app/helpers/get-pkg-manager.ts b/packages/create-next-app/helpers/get-pkg-manager.ts index 4bb66a08c6..20900ebcb9 100644 --- a/packages/create-next-app/helpers/get-pkg-manager.ts +++ b/packages/create-next-app/helpers/get-pkg-manager.ts @@ -1,4 +1,4 @@ -export type PackageManager = 'npm' | 'pnpm' | 'yarn' +export type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun' export function getPkgManager(): PackageManager { const userAgent = process.env.npm_config_user_agent || '' @@ -11,5 +11,9 @@ export function getPkgManager(): PackageManager { return 'pnpm' } + if (userAgent.startsWith('bun')) { + return 'bun' + } + return 'npm' } diff --git a/packages/create-next-app/helpers/install.ts b/packages/create-next-app/helpers/install.ts index bfe12b2b2c..a3d2545ac1 100644 --- a/packages/create-next-app/helpers/install.ts +++ b/packages/create-next-app/helpers/install.ts @@ -43,6 +43,7 @@ export function install( let args: string[] let command = packageManager const useYarn = packageManager === 'yarn' + const useBun = packageManager === 'bun' if (dependencies && dependencies.length) { /** @@ -57,6 +58,11 @@ export function install( args.push('--cwd', root) if (devDependencies) args.push('--dev') args.push(...dependencies) + } else if (useBun) { + args = ['add', '--exact'] + args.push('--cwd', root) + if (devDependencies) args.push('--development') + args.push(...dependencies) } else { /** * Call `(p)npm install [--save|--save-dev] ...`. diff --git a/packages/create-next-app/index.ts b/packages/create-next-app/index.ts index ed8358b487..3c12a7469b 100644 --- a/packages/create-next-app/index.ts +++ b/packages/create-next-app/index.ts @@ -106,6 +106,13 @@ const program = new Commander.Command(packageJson.name) ` Explicitly tell the CLI to bootstrap the application using Yarn +` + ) + .option( + '--use-bun', + ` + + Explicitly tell the CLI to bootstrap the application using Bun ` ) .option( @@ -143,6 +150,8 @@ const packageManager = !!program.useNpm ? 'pnpm' : !!program.useYarn ? 'yarn' + : !!program.useBun + ? 'bun' : getPkgManager() async function run(): Promise { @@ -461,6 +470,8 @@ async function notifyUpdate(): Promise { ? 'yarn global add create-next-app' : packageManager === 'pnpm' ? 'pnpm add -g create-next-app' + : packageManager === 'bun' + ? 'bun add -g create-next-app' : 'npm i -g create-next-app' console.log( diff --git a/test/integration/create-next-app/package-manager.test.ts b/test/integration/create-next-app/package-manager.test.ts index d0c1e12b3e..ee582a9087 100644 --- a/test/integration/create-next-app/package-manager.test.ts +++ b/test/integration/create-next-app/package-manager.test.ts @@ -226,6 +226,79 @@ it('should use pnpm as the package manager on supplying --use-pnpm with example' }) }) +it('should use Bun as the package manager on supplying --use-bun', async () => { + await useTempDir(async (cwd) => { + const projectName = 'use-bun' + const res = await run( + [ + projectName, + '--js', + '--no-tailwind', + '--eslint', + '--use-bun', + '--no-src-dir', + '--app', + `--import-alias=@/*`, + ], + { + cwd, + } + ) + + expect(res.exitCode).toBe(0) + projectFilesShouldExist({ + cwd, + projectName, + files: [ + 'package.json', + 'app/page.js', + '.gitignore', + '.eslintrc.json', + 'bun.lockb', + 'node_modules/next', + ], + }) + }) +}) + +it('should use Bun as the package manager on supplying --use-bun with example', async () => { + try { + await execa('bun', ['--version']) + } catch (_) { + // install Bun if not available + await execa('npm', ['i', '-g', 'bun']) + } + + await useTempDir(async (cwd) => { + const projectName = 'use-bun' + const res = await run( + [ + projectName, + '--js', + '--no-tailwind', + '--eslint', + '--use-bun', + '--example', + `${exampleRepo}/${examplePath}`, + ], + { cwd } + ) + + expect(res.exitCode).toBe(0) + projectFilesShouldExist({ + cwd, + projectName, + files: [ + 'package.json', + 'pages/index.tsx', + '.gitignore', + 'bun.lockb', + 'node_modules/next', + ], + }) + }) +}) + it('should infer npm as the package manager', async () => { await useTempDir(async (cwd) => { const projectName = 'infer-package-manager-npm' @@ -436,3 +509,78 @@ it('should infer pnpm as the package manager with example', async () => { projectFilesShouldExist({ cwd, projectName, files }) }) }) + +it('should infer Bun as the package manager', async () => { + try { + await execa('bun', ['--version']) + } catch (_) { + // install Bun if not available + await execa('npm', ['i', '-g', 'bun']) + } + + await useTempDir(async (cwd) => { + const projectName = 'infer-package-manager' + const res = await run( + [ + projectName, + '--js', + '--no-tailwind', + '--eslint', + '--no-src-dir', + '--app', + `--import-alias=@/*`, + ], + { + cwd, + env: { ...process.env, npm_config_user_agent: 'bun' }, + } + ) + + const files = [ + 'package.json', + 'app/page.js', + '.gitignore', + '.eslintrc.json', + 'bun.lockb', + 'node_modules/next', + ] + + expect(res.exitCode).toBe(0) + projectFilesShouldExist({ cwd, projectName, files }) + }) +}) + +it('should infer Bun as the package manager with example', async () => { + try { + await execa('bun', ['--version']) + } catch (_) { + // install Bun if not available + await execa('npm', ['i', '-g', 'bun']) + } + + await useTempDir(async (cwd) => { + const projectName = 'infer-package-manager-npm' + const res = await run( + [ + projectName, + '--js', + '--no-tailwind', + '--eslint', + '--example', + `${exampleRepo}/${examplePath}`, + ], + { cwd, env: { ...process.env, npm_config_user_agent: 'bun' } } + ) + + const files = [ + 'package.json', + 'pages/index.tsx', + '.gitignore', + 'bun.lockb', + 'node_modules/next', + ] + + expect(res.exitCode).toBe(0) + projectFilesShouldExist({ cwd, projectName, files }) + }) +})