Skip to content
Next Next commit
feat: support pnpm with create-next-app
  • Loading branch information
balazsorban44 committed Mar 2, 2022
commit 84a072a434549dced0174e636c852bb0e723fe37
23 changes: 11 additions & 12 deletions packages/create-next-app/create-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,20 @@ import { tryGitInit } from './helpers/git'
import { install } from './helpers/install'
import { isFolderEmpty } from './helpers/is-folder-empty'
import { getOnline } from './helpers/is-online'
import { shouldUseYarn } from './helpers/should-use-yarn'
import { isWriteable } from './helpers/is-writeable'
import type { PackageManager } from './helpers/get-pkg-manager'

export class DownloadError extends Error {}

export async function createApp({
appPath,
useNpm,
packageManager,
example,
examplePath,
typescript,
}: {
appPath: string
useNpm: boolean
packageManager: PackageManager
example?: string
examplePath?: string
typescript?: boolean
Expand Down Expand Up @@ -119,11 +119,10 @@ export async function createApp({
process.exit(1)
}

const useYarn = useNpm ? false : shouldUseYarn()
const useYarn = packageManager === 'yarn'
const isOnline = !useYarn || (await getOnline())
const originalDirectory = process.cwd()

const displayedCommand = useYarn ? 'yarn' : 'npm'
console.log(`Creating a new Next.js app in ${chalk.green(root)}.`)
console.log()

Expand Down Expand Up @@ -189,14 +188,14 @@ export async function createApp({
console.log('Installing packages. This might take a couple of minutes.')
console.log()

await install(root, null, { useYarn, isOnline })
await install(root, null, { packageManager, isOnline })
console.log()
} else {
/**
* Otherwise, if an example repository is not provided for cloning, proceed
* by installing from a template.
*/
console.log(chalk.bold(`Using ${displayedCommand}.`))
console.log(chalk.bold(`Using ${packageManager}.`))
/**
* Create a package.json for the new project.
*/
Expand All @@ -221,7 +220,7 @@ export async function createApp({
/**
* These flags will be passed to `install()`.
*/
const installFlags = { useYarn, isOnline }
const installFlags = { packageManager, isOnline }
/**
* Default dependencies.
*/
Expand Down Expand Up @@ -309,20 +308,20 @@ export async function createApp({
console.log(`${chalk.green('Success!')} Created ${appName} at ${appPath}`)
console.log('Inside that directory, you can run several commands:')
console.log()
console.log(chalk.cyan(` ${displayedCommand} ${useYarn ? '' : 'run '}dev`))
console.log(chalk.cyan(` ${packageManager} ${useYarn ? '' : 'run '}dev`))
console.log(' Starts the development server.')
console.log()
console.log(chalk.cyan(` ${displayedCommand} ${useYarn ? '' : 'run '}build`))
console.log(chalk.cyan(` ${packageManager} ${useYarn ? '' : 'run '}build`))
console.log(' Builds the app for production.')
console.log()
console.log(chalk.cyan(` ${displayedCommand} start`))
console.log(chalk.cyan(` ${packageManager} start`))
console.log(' Runs the built app in production mode.')
console.log()
console.log('We suggest that you begin by typing:')
console.log()
console.log(chalk.cyan(' cd'), cdpath)
console.log(
` ${chalk.cyan(`${displayedCommand} ${useYarn ? '' : 'run '}dev`)}`
` ${chalk.cyan(`${packageManager} ${useYarn ? '' : 'run '}dev`)}`
)
console.log()
}
25 changes: 25 additions & 0 deletions packages/create-next-app/helpers/get-pkg-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { execSync } from 'child_process'

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

export function getPkgManager(): PackageManager {
try {
const userAgent = process.env.npm_config_user_agent
if (userAgent) {
if (userAgent.startsWith('yarn')) {
return 'yarn'
} else if (userAgent.startsWith('pnpm')) {
return 'pnpm'
}
}
try {
execSync('yarn --version', { stdio: 'ignore' })
return 'yarn'
} catch {
execSync('pnpm --version', { stdio: 'ignore' })
return 'pnpm'
}
} catch {
return 'npm'
}
}
18 changes: 10 additions & 8 deletions packages/create-next-app/helpers/install.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
/* eslint-disable import/no-extraneous-dependencies */
import chalk from 'chalk'
import spawn from 'cross-spawn'
import type { PackageManager } from './get-pkg-manager'

interface InstallArgs {
/**
* Indicate whether to install packages using Yarn.
* Indicate whether to install packages using npm, pnpm or Yarn.
*/
useYarn: boolean
packageManager: PackageManager
/**
* Indicate whether there is an active Internet connection.
*/
Expand All @@ -25,10 +26,10 @@ interface InstallArgs {
export function install(
root: string,
dependencies: string[] | null,
{ useYarn, isOnline, devDependencies }: InstallArgs
{ packageManager, isOnline, devDependencies }: InstallArgs
): Promise<void> {
/**
* NPM-specific command-line flags.
* (p)npm-specific command-line flags.
*/
const npmFlags: string[] = []
/**
Expand All @@ -40,11 +41,12 @@ export function install(
*/
return new Promise((resolve, reject) => {
let args: string[]
let command: string = useYarn ? 'yarnpkg' : 'npm'
let command = packageManager
const useYarn = packageManager === 'yarn'

if (dependencies && dependencies.length) {
/**
* If there are dependencies, run a variation of `{displayCommand} add`.
* If there are dependencies, run a variation of `{packageManager} add`.
*/
if (useYarn) {
/**
Expand All @@ -57,15 +59,15 @@ export function install(
args.push(...dependencies)
} else {
/**
* Call `npm install [--save|--save-dev] ...`.
* Call `(p)npm install [--save|--save-dev] ...`.
*/
args = ['install', '--save-exact']
args.push(devDependencies ? '--save-dev' : '--save')
args.push(...dependencies)
}
} else {
/**
* If there are no dependencies, run a variation of `{displayCommand}
* If there are no dependencies, run a variation of `{packageManager}
* install`.
*/
args = ['install']
Expand Down
14 changes: 0 additions & 14 deletions packages/create-next-app/helpers/should-use-yarn.ts

This file was deleted.

26 changes: 19 additions & 7 deletions packages/create-next-app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import path from 'path'
import prompts from 'prompts'
import checkForUpdate from 'update-check'
import { createApp, DownloadError } from './create-app'
import { shouldUseYarn } from './helpers/should-use-yarn'
import { getPkgManager } from './helpers/get-pkg-manager'
import { validateNpmName } from './helpers/validate-pkg'
import packageJson from './package.json'

Expand All @@ -31,6 +31,13 @@ const program = new Commander.Command(packageJson.name)
`

Explicitly tell the CLI to bootstrap the app using npm
`
)
.option(
'--use-pnpm',
`

Explicitly tell the CLI to bootstrap the app using pnpm
`
)
.option(
Expand Down Expand Up @@ -116,14 +123,19 @@ async function run(): Promise<void> {
'Please provide an example name or url, otherwise remove the example option.'
)
process.exit(1)
return
}

const packageManager = !!program.useYarn
? 'yarn'
: !!program.usePnpm
? 'pnpm'
: 'npm'

const example = typeof program.example === 'string' && program.example.trim()
try {
await createApp({
appPath: resolvedProjectPath,
useNpm: !!program.useNpm,
packageManager,
example: example && example !== 'default' ? example : undefined,
examplePath: program.examplePath,
typescript: program.typescript,
Expand All @@ -147,7 +159,7 @@ async function run(): Promise<void> {

await createApp({
appPath: resolvedProjectPath,
useNpm: !!program.useNpm,
packageManager,
typescript: program.typescript,
})
}
Expand All @@ -159,7 +171,7 @@ async function notifyUpdate(): Promise<void> {
try {
const res = await update
if (res?.latest) {
const isYarn = shouldUseYarn()
const pkgManager = getPkgManager()

console.log()
console.log(
Expand All @@ -168,9 +180,9 @@ async function notifyUpdate(): Promise<void> {
console.log(
'You can update by running: ' +
chalk.cyan(
isYarn
pkgManager === 'yarn'
? 'yarn global add create-next-app'
: 'npm i -g create-next-app'
: `${pkgManager} install --global create-next-app`
Comment thread
balazsorban44 marked this conversation as resolved.
)
)
console.log()
Expand Down