Skip to content

Commit 5cd31e4

Browse files
Nstttbalazsorban44
andauthored
Add npm to create-next-app environment package manager parser (vercel#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>
1 parent e505e75 commit 5cd31e4

File tree

3 files changed

+184
-28
lines changed

3 files changed

+184
-28
lines changed
Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,17 @@
1-
import { execSync } from 'child_process'
2-
31
export type PackageManager = 'npm' | 'pnpm' | 'yarn'
42

53
export function getPkgManager(): PackageManager {
6-
try {
7-
const userAgent = process.env.npm_config_user_agent
8-
if (userAgent) {
9-
if (userAgent.startsWith('yarn')) {
10-
return 'yarn'
11-
} else if (userAgent.startsWith('pnpm')) {
12-
return 'pnpm'
13-
}
14-
}
15-
try {
16-
execSync('yarn --version', { stdio: 'ignore' })
4+
const userAgent = process.env.npm_config_user_agent
5+
6+
if (userAgent) {
7+
if (userAgent.startsWith('yarn')) {
178
return 'yarn'
18-
} catch {
19-
execSync('pnpm --version', { stdio: 'ignore' })
9+
} else if (userAgent.startsWith('pnpm')) {
2010
return 'pnpm'
11+
} else {
12+
return 'npm'
2113
}
22-
} catch {
14+
} else {
2315
return 'npm'
2416
}
2517
}

packages/create-next-app/index.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ const program = new Commander.Command(packageJson.name)
6262
.allowUnknownOption()
6363
.parse(process.argv)
6464

65+
const packageManager = !!program.useNpm
66+
? 'npm'
67+
: !!program.usePnpm
68+
? 'pnpm'
69+
: getPkgManager()
70+
6571
async function run(): Promise<void> {
6672
if (typeof projectPath === 'string') {
6773
projectPath = projectPath.trim()
@@ -122,12 +128,6 @@ async function run(): Promise<void> {
122128
process.exit(1)
123129
}
124130

125-
const packageManager = !!program.useNpm
126-
? 'npm'
127-
: !!program.usePnpm
128-
? 'pnpm'
129-
: getPkgManager()
130-
131131
const example = typeof program.example === 'string' && program.example.trim()
132132
try {
133133
await createApp({
@@ -168,16 +168,18 @@ async function notifyUpdate(): Promise<void> {
168168
try {
169169
const res = await update
170170
if (res?.latest) {
171-
const pkgManager = getPkgManager()
171+
const updateMessage =
172+
packageManager === 'yarn'
173+
? 'yarn global add create-next-app'
174+
: packageManager === 'pnpm'
175+
? 'pnpm add -g create-next-app'
176+
: 'npm i -g create-next-app'
177+
172178
console.log(
173179
chalk.yellow.bold('A new version of `create-next-app` is available!') +
174180
'\n' +
175181
'You can update by running: ' +
176-
chalk.cyan(
177-
pkgManager === 'yarn'
178-
? 'yarn global add create-next-app'
179-
: `${pkgManager} install --global create-next-app`
180-
) +
182+
chalk.cyan(updateMessage) +
181183
'\n'
182184
)
183185
}

test/integration/create-next-app/index.test.ts

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,10 +506,172 @@ describe('create next app', () => {
506506
'pnpm-lock.yaml',
507507
'node_modules/next',
508508
]
509+
files.forEach((file) =>
510+
expect(fs.existsSync(path.join(cwd, projectName, file))).toBeTruthy()
511+
)
512+
})
513+
})
509514

515+
it('should infer npm as the package manager', async () => {
516+
await usingTempDir(async (cwd) => {
517+
const projectName = 'infer-package-manager-npm'
518+
const res = await run([projectName], {
519+
cwd,
520+
env: { ...process.env, npm_config_user_agent: 'npm' },
521+
})
522+
expect(res.exitCode).toBe(0)
523+
524+
const files = [
525+
'package.json',
526+
'pages/index.js',
527+
'.gitignore',
528+
'.eslintrc.json',
529+
'package-lock.json',
530+
'node_modules/next',
531+
]
510532
files.forEach((file) =>
511533
expect(fs.existsSync(path.join(cwd, projectName, file))).toBeTruthy()
512534
)
513535
})
514536
})
537+
538+
it('should infer npm as the package manager with example', async () => {
539+
await usingTempDir(async (cwd) => {
540+
const projectName = 'infer-package-manager-npm'
541+
const res = await run(
542+
[projectName, '--example', `${exampleRepo}/${examplePath}`],
543+
{ cwd, env: { ...process.env, npm_config_user_agent: 'npm' } }
544+
)
545+
expect(res.exitCode).toBe(0)
546+
547+
const files = [
548+
'package.json',
549+
'pages/index.tsx',
550+
'.gitignore',
551+
'package-lock.json',
552+
'node_modules/next',
553+
]
554+
files.forEach((file) =>
555+
expect(fs.existsSync(path.join(cwd, projectName, file))).toBeTruthy()
556+
)
557+
})
558+
})
559+
560+
it('should infer yarn as the package manager', async () => {
561+
try {
562+
await execa('yarn', ['--version'])
563+
} catch (_) {
564+
// install yarn if not available
565+
await execa('npm', ['i', '-g', 'yarn'])
566+
}
567+
568+
await usingTempDir(async (cwd) => {
569+
const projectName = 'infer-package-manager-yarn'
570+
const res = await run([projectName], {
571+
cwd,
572+
env: { ...process.env, npm_config_user_agent: 'yarn' },
573+
})
574+
expect(res.exitCode).toBe(0)
575+
576+
const files = [
577+
'package.json',
578+
'pages/index.js',
579+
'.gitignore',
580+
'.eslintrc.json',
581+
'yarn.lock',
582+
'node_modules/next',
583+
]
584+
files.forEach((file) =>
585+
expect(fs.existsSync(path.join(cwd, projectName, file))).toBeTruthy()
586+
)
587+
})
588+
})
589+
590+
it('should infer yarn as the package manager with example', async () => {
591+
try {
592+
await execa('yarn', ['--version'])
593+
} catch (_) {
594+
// install yarn if not available
595+
await execa('npm', ['i', '-g', 'yarn'])
596+
}
597+
598+
await usingTempDir(async (cwd) => {
599+
const projectName = 'infer-package-manager-npm'
600+
const res = await run(
601+
[projectName, '--example', `${exampleRepo}/${examplePath}`],
602+
{ cwd, env: { ...process.env, npm_config_user_agent: 'yarn' } }
603+
)
604+
expect(res.exitCode).toBe(0)
605+
606+
const files = [
607+
'package.json',
608+
'pages/index.tsx',
609+
'.gitignore',
610+
'yarn.lock',
611+
'node_modules/next',
612+
]
613+
files.forEach((file) =>
614+
expect(fs.existsSync(path.join(cwd, projectName, file))).toBeTruthy()
615+
)
616+
})
617+
})
618+
619+
it('should infer pnpm as the package manager', async () => {
620+
try {
621+
await execa('pnpm', ['--version'])
622+
} catch (_) {
623+
// install pnpm if not available
624+
await execa('npm', ['i', '-g', 'pnpm'])
625+
}
626+
627+
await usingTempDir(async (cwd) => {
628+
const projectName = 'infer-package-manager'
629+
const res = await run([projectName], {
630+
cwd,
631+
env: { ...process.env, npm_config_user_agent: 'pnpm' },
632+
})
633+
expect(res.exitCode).toBe(0)
634+
635+
const files = [
636+
'package.json',
637+
'pages/index.js',
638+
'.gitignore',
639+
'.eslintrc.json',
640+
'pnpm-lock.yaml',
641+
'node_modules/next',
642+
]
643+
files.forEach((file) =>
644+
expect(fs.existsSync(path.join(cwd, projectName, file))).toBeTruthy()
645+
)
646+
})
647+
})
648+
})
649+
650+
it('should infer pnpm as the package manager with example', async () => {
651+
try {
652+
await execa('pnpm', ['--version'])
653+
} catch (_) {
654+
// install pnpm if not available
655+
await execa('npm', ['i', '-g', 'pnpm'])
656+
}
657+
658+
await usingTempDir(async (cwd) => {
659+
const projectName = 'infer-package-manager-npm'
660+
const res = await run(
661+
[projectName, '--example', `${exampleRepo}/${examplePath}`],
662+
{ cwd, env: { ...process.env, npm_config_user_agent: 'pnpm' } }
663+
)
664+
expect(res.exitCode).toBe(0)
665+
666+
const files = [
667+
'package.json',
668+
'pages/index.tsx',
669+
'.gitignore',
670+
'pnpm-lock.yaml',
671+
'node_modules/next',
672+
]
673+
files.forEach((file) =>
674+
expect(fs.existsSync(path.join(cwd, projectName, file))).toBeTruthy()
675+
)
676+
})
515677
})

0 commit comments

Comments
 (0)