Skip to content

Commit

Permalink
Add npm to create-next-app environment package manager parser (verc…
Browse files Browse the repository at this point in the history
…el#41279)

Gracefully parse the environment package manager when calling
`create-next-app` instead of passing extra arguments.

Reference: vercel#41090

## Feature

- [ ] Implements an existing feature request or RFC. Make sure the
feature request has been accepted for implementation before opening a
PR.
- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have a helpful link attached, see `contributing.md`

Co-authored-by: Balázs Orbán <info@balazsorban.com>
  • Loading branch information
2 people authored and Kikobeats committed Oct 24, 2022
1 parent ad615fd commit a0057ee
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 28 deletions.
24 changes: 8 additions & 16 deletions packages/create-next-app/helpers/get-pkg-manager.ts
@@ -1,25 +1,17 @@
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' })
const userAgent = process.env.npm_config_user_agent

if (userAgent) {
if (userAgent.startsWith('yarn')) {
return 'yarn'
} catch {
execSync('pnpm --version', { stdio: 'ignore' })
} else if (userAgent.startsWith('pnpm')) {
return 'pnpm'
} else {
return 'npm'
}
} catch {
} else {
return 'npm'
}
}
26 changes: 14 additions & 12 deletions packages/create-next-app/index.ts
Expand Up @@ -62,6 +62,12 @@ const program = new Commander.Command(packageJson.name)
.allowUnknownOption()
.parse(process.argv)

const packageManager = !!program.useNpm
? 'npm'
: !!program.usePnpm
? 'pnpm'
: getPkgManager()

async function run(): Promise<void> {
if (typeof projectPath === 'string') {
projectPath = projectPath.trim()
Expand Down Expand Up @@ -122,12 +128,6 @@ async function run(): Promise<void> {
process.exit(1)
}

const packageManager = !!program.useNpm
? 'npm'
: !!program.usePnpm
? 'pnpm'
: getPkgManager()

const example = typeof program.example === 'string' && program.example.trim()
try {
await createApp({
Expand Down Expand Up @@ -168,16 +168,18 @@ async function notifyUpdate(): Promise<void> {
try {
const res = await update
if (res?.latest) {
const pkgManager = getPkgManager()
const updateMessage =
packageManager === 'yarn'
? 'yarn global add create-next-app'
: packageManager === 'pnpm'
? 'pnpm add -g create-next-app'
: 'npm i -g create-next-app'

console.log(
chalk.yellow.bold('A new version of `create-next-app` is available!') +
'\n' +
'You can update by running: ' +
chalk.cyan(
pkgManager === 'yarn'
? 'yarn global add create-next-app'
: `${pkgManager} install --global create-next-app`
) +
chalk.cyan(updateMessage) +
'\n'
)
}
Expand Down
162 changes: 162 additions & 0 deletions test/integration/create-next-app/index.test.ts
Expand Up @@ -506,10 +506,172 @@ describe('create next app', () => {
'pnpm-lock.yaml',
'node_modules/next',
]
files.forEach((file) =>
expect(fs.existsSync(path.join(cwd, projectName, file))).toBeTruthy()
)
})
})

it('should infer npm as the package manager', async () => {
await usingTempDir(async (cwd) => {
const projectName = 'infer-package-manager-npm'
const res = await run([projectName], {
cwd,
env: { ...process.env, npm_config_user_agent: 'npm' },
})
expect(res.exitCode).toBe(0)

const files = [
'package.json',
'pages/index.js',
'.gitignore',
'.eslintrc.json',
'package-lock.json',
'node_modules/next',
]
files.forEach((file) =>
expect(fs.existsSync(path.join(cwd, projectName, file))).toBeTruthy()
)
})
})

it('should infer npm as the package manager with example', async () => {
await usingTempDir(async (cwd) => {
const projectName = 'infer-package-manager-npm'
const res = await run(
[projectName, '--example', `${exampleRepo}/${examplePath}`],
{ cwd, env: { ...process.env, npm_config_user_agent: 'npm' } }
)
expect(res.exitCode).toBe(0)

const files = [
'package.json',
'pages/index.tsx',
'.gitignore',
'package-lock.json',
'node_modules/next',
]
files.forEach((file) =>
expect(fs.existsSync(path.join(cwd, projectName, file))).toBeTruthy()
)
})
})

it('should infer yarn as the package manager', async () => {
try {
await execa('yarn', ['--version'])
} catch (_) {
// install yarn if not available
await execa('npm', ['i', '-g', 'yarn'])
}

await usingTempDir(async (cwd) => {
const projectName = 'infer-package-manager-yarn'
const res = await run([projectName], {
cwd,
env: { ...process.env, npm_config_user_agent: 'yarn' },
})
expect(res.exitCode).toBe(0)

const files = [
'package.json',
'pages/index.js',
'.gitignore',
'.eslintrc.json',
'yarn.lock',
'node_modules/next',
]
files.forEach((file) =>
expect(fs.existsSync(path.join(cwd, projectName, file))).toBeTruthy()
)
})
})

it('should infer yarn as the package manager with example', async () => {
try {
await execa('yarn', ['--version'])
} catch (_) {
// install yarn if not available
await execa('npm', ['i', '-g', 'yarn'])
}

await usingTempDir(async (cwd) => {
const projectName = 'infer-package-manager-npm'
const res = await run(
[projectName, '--example', `${exampleRepo}/${examplePath}`],
{ cwd, env: { ...process.env, npm_config_user_agent: 'yarn' } }
)
expect(res.exitCode).toBe(0)

const files = [
'package.json',
'pages/index.tsx',
'.gitignore',
'yarn.lock',
'node_modules/next',
]
files.forEach((file) =>
expect(fs.existsSync(path.join(cwd, projectName, file))).toBeTruthy()
)
})
})

it('should infer pnpm as the package manager', async () => {
try {
await execa('pnpm', ['--version'])
} catch (_) {
// install pnpm if not available
await execa('npm', ['i', '-g', 'pnpm'])
}

await usingTempDir(async (cwd) => {
const projectName = 'infer-package-manager'
const res = await run([projectName], {
cwd,
env: { ...process.env, npm_config_user_agent: 'pnpm' },
})
expect(res.exitCode).toBe(0)

const files = [
'package.json',
'pages/index.js',
'.gitignore',
'.eslintrc.json',
'pnpm-lock.yaml',
'node_modules/next',
]
files.forEach((file) =>
expect(fs.existsSync(path.join(cwd, projectName, file))).toBeTruthy()
)
})
})
})

it('should infer pnpm as the package manager with example', async () => {
try {
await execa('pnpm', ['--version'])
} catch (_) {
// install pnpm if not available
await execa('npm', ['i', '-g', 'pnpm'])
}

await usingTempDir(async (cwd) => {
const projectName = 'infer-package-manager-npm'
const res = await run(
[projectName, '--example', `${exampleRepo}/${examplePath}`],
{ cwd, env: { ...process.env, npm_config_user_agent: 'pnpm' } }
)
expect(res.exitCode).toBe(0)

const files = [
'package.json',
'pages/index.tsx',
'.gitignore',
'pnpm-lock.yaml',
'node_modules/next',
]
files.forEach((file) =>
expect(fs.existsSync(path.join(cwd, projectName, file))).toBeTruthy()
)
})
})

0 comments on commit a0057ee

Please sign in to comment.