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`

---------
This commit is contained in:
Colin McDonnell 2023-08-03 18:00:38 -07:00 committed by GitHub
parent b224f47f23
commit 57ce466502
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 178 additions and 1 deletions

View file

@ -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

View file

@ -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

View file

@ -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'
}

View file

@ -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] ...`.

View file

@ -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<void> {
@ -461,6 +470,8 @@ async function notifyUpdate(): Promise<void> {
? '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(

View file

@ -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 })
})
})