From 5cd31e41ca652a3ecdf5966733242267da6083fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=A9stor?= Date: Thu, 13 Oct 2022 23:34:55 +0200 Subject: [PATCH] Add npm to `create-next-app` environment package manager parser (#41279) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gracefully parse the environment package manager when calling `create-next-app` instead of passing extra arguments. Reference: #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 --- .../helpers/get-pkg-manager.ts | 24 +-- packages/create-next-app/index.ts | 26 +-- .../integration/create-next-app/index.test.ts | 162 ++++++++++++++++++ 3 files changed, 184 insertions(+), 28 deletions(-) diff --git a/packages/create-next-app/helpers/get-pkg-manager.ts b/packages/create-next-app/helpers/get-pkg-manager.ts index e3de28a4f9d4..b95ff3abe0bd 100644 --- a/packages/create-next-app/helpers/get-pkg-manager.ts +++ b/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' } } diff --git a/packages/create-next-app/index.ts b/packages/create-next-app/index.ts index aa89b4fd2517..6a1c6e61f53d 100644 --- a/packages/create-next-app/index.ts +++ b/packages/create-next-app/index.ts @@ -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 { if (typeof projectPath === 'string') { projectPath = projectPath.trim() @@ -122,12 +128,6 @@ async function run(): Promise { process.exit(1) } - const packageManager = !!program.useNpm - ? 'npm' - : !!program.usePnpm - ? 'pnpm' - : getPkgManager() - const example = typeof program.example === 'string' && program.example.trim() try { await createApp({ @@ -168,16 +168,18 @@ async function notifyUpdate(): Promise { 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' ) } diff --git a/test/integration/create-next-app/index.test.ts b/test/integration/create-next-app/index.test.ts index ba7fd5df86c7..3b56e3864aa7 100644 --- a/test/integration/create-next-app/index.test.ts +++ b/test/integration/create-next-app/index.test.ts @@ -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() + ) + }) })