feat(CTA): add official support for pnpm package manager (#2348)

* feat(CTA): add official support for `pnpm` package manager

* adjust post-message formatting [skip ci]

* optimize package managers checks
This commit is contained in:
Amr Bashir
2021-08-06 17:35:39 +02:00
committed by GitHub
parent fee3c5820b
commit 235e0f6785
11 changed files with 123 additions and 99 deletions

View File

@@ -0,0 +1,5 @@
---
"create-tauri-app": patch
---
[`pnpm`](https://pnpm.io) package manager is now officially supported, either run `pnpx create-tauri-app` or explicitly specifiy it `npx create-tauri-app --manager pnpm`.

View File

@@ -5,7 +5,7 @@
import { ManagementType, Result } from './types/deps'
import { shell } from './shell'
export type PackageManager = 'npm' | 'yarn'
export type PackageManager = 'npm' | 'yarn' | 'pnpm'
export async function install({
appDir,
@@ -50,16 +50,18 @@ async function installNpmPackage(
packageManager: PackageManager,
appDir: string
): Promise<void> {
if (packageNames.length === 0) return
console.log(`Installing ${packageNames.join(', ')}...`)
if (packageManager === 'yarn') {
await shell('yarn', ['add', packageNames.join(' ')], {
cwd: appDir
})
} else {
await shell('npm', ['install', packageNames.join(' ')], {
cwd: appDir
})
const packages = packageNames.filter((p) => p !== '')
if (packages.length !== 0) {
console.log(`- Installing ${packages.join(', ')}...`)
if (packageManager === 'npm') {
await shell('npm', ['install', packageNames.join(' ')], {
cwd: appDir
})
} else {
await shell(packageManager, ['add', packageNames.join(' ')], {
cwd: appDir
})
}
}
}
@@ -68,23 +70,25 @@ async function installNpmDevPackage(
packageManager: PackageManager,
appDir: string
): Promise<void> {
if (packageNames.length === 0) return
console.log(`Installing ${packageNames.join(', ')}...`)
if (packageManager === 'yarn') {
await shell(
'yarn',
['add', '--dev', '--ignore-scripts', packageNames.join(' ')],
{
cwd: appDir
}
)
} else {
await shell(
'npm',
['install', '--save-dev', '--ignore-scripts', packageNames.join(' ')],
{
cwd: appDir
}
)
const packages = packageNames.filter((p) => p !== '')
if (packages.length !== 0) {
console.log(`- Installing ${packages.join(', ')}...`)
if (packageManager === 'npm') {
await shell(
'npm',
['install', '--save-dev', '--ignore-scripts', packageNames.join(' ')],
{
cwd: appDir
}
)
} else {
await shell(
packageManager,
['add', '-D', '--ignore-scripts', packageNames.join(' ')],
{
cwd: appDir
}
)
}
}
}

View File

@@ -63,7 +63,7 @@ const printUsage = (): void => {
--ci Skip prompts
--force, -f Force init to overwrite [conf|template|all]
--log, -l Logging [boolean]
--manager, -d Set package manager to use [npm|yarn]
--manager, -m Set package manager to use [npm|yarn|pnpm]
--directory, -d Set target directory for init
--app-name, -A Name of your Tauri application
--window-title, -W Window title of your Tauri application
@@ -255,12 +255,15 @@ const runInit = async (argv: Argv): Promise<void> => {
}
const packageManager =
argv.m === 'yarn' || argv.m === 'npm'
argv.m === 'yarn' || argv.m === 'npm' || argv.m === 'pnpm'
? argv.m
: // @ts-expect-error
// this little fun snippet pulled from vite determines the package manager the script was run from
/yarn/.test(process?.env?.npm_execpath)
? 'yarn'
: // @ts-expect-error
/pnpm/.test(process?.env?.npm_execpath)
? 'pnpm'
: 'npm'
const buildConfig = {
@@ -367,10 +370,13 @@ const runInit = async (argv: Argv): Promise<void> => {
logStep(`Running: ${reset(yellow('tauri init'))}`)
const binary = !argv.b ? packageManager : resolve(appDirectory, argv.b)
// pnpm is equivalent to yarn and can run srcipts without using "run" but due to this bug https://github.com/pnpm/pnpm/issues/2764
// we need to pass "--" to pnpm or arguments won't be parsed correctly so for this command only we are gonna treat pnpm as npm equivalent/
const runTauriArgs =
packageManager === 'npm' && !argv.b
? ['run', 'tauri', '--', 'init']
: ['tauri', 'init']
packageManager === 'yarn' || argv.b
? ['tauri', 'init']
: ['run', 'tauri', '--', 'init']
await shell(binary, [...runTauriArgs, ...initArgs, '--ci'], {
cwd: appDirectory
})

View File

@@ -17,9 +17,11 @@ export const dominator: Recipe = {
...cfg,
distDir: `../dist`,
devPath: 'http://localhost:10001/',
beforeDevCommand: `${packageManager === 'yarn' ? 'yarn' : 'npm run'} start`,
beforeDevCommand: `${
packageManager === 'npm' ? 'npm run' : packageManager
} start`,
beforeBuildCommand: `${
packageManager === 'yarn' ? 'yarn' : 'npm run'
packageManager === 'npm' ? 'npm run' : packageManager
} build`
}),
extraNpmDevDependencies: [],
@@ -46,12 +48,11 @@ export const dominator: Recipe = {
console.log(`
Your installation completed.
$ cd ${cfg.appName}.
$ cd ${cfg.appName}
$ ${packageManager} install
$ ${packageManager === 'yarn' ? 'yarn' : 'npm run'} tauri ${
$ ${packageManager === 'npm' ? 'npm run' : packageManager} tauri ${
packageManager === 'npm' ? '--' : ''
} dev
}dev
`)
return await Promise.resolve()
}

View File

@@ -1,6 +1,7 @@
import { PackageManager } from '../dependency-manager'
import { shell } from '../shell'
import { Recipe } from '../types/recipe'
import { join } from 'path'
const addAdditionalPackage = async (
packageManager: PackageManager,
@@ -10,13 +11,13 @@ const addAdditionalPackage = async (
): Promise<void> => {
const ngCommand = ['ng', 'add', packageName, '--skip-confirmation']
if (packageManager === 'yarn') {
await shell('yarn', ngCommand, {
cwd: `${cwd}/${appName}`
if (packageManager === 'npm') {
await shell('npm', ['run', ...ngCommand], {
cwd: join(cwd, appName)
})
} else {
await shell('npm', ['run', ...ngCommand], {
cwd: `${cwd}/${appName}`
await shell(packageManager, ngCommand, {
cwd: join(cwd, appName)
})
}
}
@@ -33,9 +34,11 @@ const ngcli: Recipe = {
...cfg,
distDir: `../dist/${cfg.appName}`,
devPath: 'http://localhost:4200',
beforeDevCommand: `${packageManager === 'yarn' ? 'yarn' : 'npm run'} start`,
beforeDevCommand: `${
packageManager === 'npm' ? 'npm run' : packageManager
} start`,
beforeBuildCommand: `${
packageManager === 'yarn' ? 'yarn' : 'npm run'
packageManager === 'npm' ? 'npm run' : packageManager
} build`
}),
extraQuestions: ({ ci }) => {
@@ -94,12 +97,12 @@ const ngcli: Recipe = {
},
postInit: async ({ packageManager, cfg }) => {
console.log(`
Your installation completed.
Your installation completed.
$ cd ${cfg.appName}
$ ${packageManager === 'yarn' ? 'yarn' : 'npm run'} tauri ${
$ cd ${cfg.appName}
$ ${packageManager === 'npm' ? 'npm run' : packageManager} tauri ${
packageManager === 'npm' ? '--' : ''
} dev
}dev
`)
return await Promise.resolve()

View File

@@ -7,6 +7,7 @@ import { join } from 'path'
import scaffe from 'scaffe'
import { shell } from '../shell'
import { Recipe } from '../types/recipe'
import { rmSync, existsSync } from 'fs'
const afterCra = async (
cwd: string,
@@ -38,9 +39,11 @@ export const cra: Recipe = {
...cfg,
distDir: `../build`,
devPath: 'http://localhost:3000',
beforeDevCommand: `${packageManager === 'yarn' ? 'yarn' : 'npm run'} start`,
beforeDevCommand: `${
packageManager === 'npm' ? 'npm run' : packageManager
} start`,
beforeBuildCommand: `${
packageManager === 'yarn' ? 'yarn' : 'npm run'
packageManager === 'npm' ? 'npm run' : packageManager
} build`
}),
extraNpmDevDependencies: [],
@@ -50,7 +53,7 @@ export const cra: Recipe = {
{
type: 'list',
name: 'template',
message: 'Which vite template would you like to use?',
message: 'Which create-react-app template would you like to use?',
choices: [
{ name: 'create-react-app (JavaScript)', value: 'cra.js' },
{ name: 'create-react-app (Typescript)', value: 'cra.ts' }
@@ -94,6 +97,22 @@ export const cra: Recipe = {
}
)
}
// create-react-app doesn't support pnpm, so we remove `node_modules` and any lock files then install them again using pnpm
if (packageManager === 'pnpm') {
const npmLock = join(cwd, cfg.appName, 'package-lock.json')
const yarnLock = join(cwd, cfg.appName, 'yarn.lock')
const nodeModules = join(cwd, cfg.appName, 'node_modules')
if (existsSync(npmLock)) rmSync(npmLock)
if (existsSync(yarnLock)) rmSync(yarnLock)
if (existsSync(nodeModules))
rmSync(nodeModules, {
recursive: true,
force: true
})
await shell('pnpm', ['install'], { cwd })
}
await afterCra(cwd, cfg.appName, template === 'cra.ts')
},
postInit: async ({ packageManager, cfg }) => {
@@ -101,10 +120,10 @@ export const cra: Recipe = {
Your installation completed.
$ cd ${cfg.appName}
$ ${packageManager === 'yarn' ? 'yarn' : 'npm run'} tauri ${
$ ${packageManager === 'npm' ? 'npm run' : packageManager} tauri ${
packageManager === 'npm' ? '--' : ''
} dev
`)
}dev
`)
return await Promise.resolve()
}
}

View File

@@ -30,9 +30,11 @@ const svelte: Recipe = {
...cfg,
distDir: `../public`,
devPath: 'http://localhost:5000',
beforeDevCommand: `${packageManager === 'yarn' ? 'yarn' : 'npm run'} dev`,
beforeDevCommand: `${
packageManager === 'yarn' ? 'npm run' : packageManager
} dev`,
beforeBuildCommand: `${
packageManager === 'yarn' ? 'yarn' : 'npm run'
packageManager === 'yarn' ? 'npm run' : packageManager
} build`
}),
preInit: async ({ cwd, cfg, answers }) => {
@@ -56,11 +58,11 @@ const svelte: Recipe = {
console.log(`
Your installation completed.
$ cd ${cfg.appName}.
$ ${packageManager === 'yarn' ? 'yarn' : 'npm run'} tauri ${
$ cd ${cfg.appName}
$ ${packageManager} install
$ ${packageManager === 'npm' ? 'npm run' : packageManager} tauri ${
packageManager === 'npm' ? '--' : ''
} dev
}dev
`)
return await Promise.resolve()

View File

@@ -43,12 +43,12 @@ export const vanillajs: Recipe = {
console.log(`
Your installation completed.
$ cd ${cfg.appName}
$ ${packageManager} install
$ ${packageManager === 'yarn' ? 'yarn' : 'npm run'} tauri ${
packageManager === 'npm' ? '-- ' : ''
$ cd ${cfg.appName}
$ ${packageManager} install
$ ${packageManager === 'npm' ? 'npm run' : packageManager} tauri ${
packageManager === 'npm' ? '--' : ''
}dev
`)
`)
return await Promise.resolve()
}
}

View File

@@ -7,23 +7,6 @@
import { shell } from '../shell'
import { Recipe } from '../types/recipe'
const afterViteCA = async (
cwd: string,
appName: string,
template: string
): Promise<void> => {
// template dir temp removed, will eventually add it back for APIs
// leaving this here until then
// const templateDir = join(__dirname, `../src/templates/vite/${template}`)
// try {
// await scaffe.generate(templateDir, join(cwd, appName), {
// overwrite: true
// })
// } catch (err) {
// console.log(err)
// }
}
const vite: Recipe = {
descriptiveName: {
name: 'create-vite (https://vitejs.dev/guide/#scaffolding-your-first-vite-project)',
@@ -34,9 +17,11 @@ const vite: Recipe = {
...cfg,
distDir: `../dist`,
devPath: 'http://localhost:3000',
beforeDevCommand: `${packageManager === 'yarn' ? 'yarn' : 'npm run'} dev`,
beforeDevCommand: `${
packageManager === 'npm' ? 'npm run' : packageManager
} dev`,
beforeBuildCommand: `${
packageManager === 'yarn' ? 'yarn' : 'npm run'
packageManager === 'npm' ? 'npm run' : packageManager
} build`
}),
extraNpmDevDependencies: [],
@@ -84,33 +69,32 @@ const vite: Recipe = {
)
} else {
await shell(
'npx',
packageManager === 'pnpm' ? 'pnpx' : 'npx',
['create-vite@latest', `${cfg.appName}`, '--template', `${template}`],
{
cwd
}
)
}
await afterViteCA(cwd, cfg.appName, template)
},
postInit: async ({ cwd, packageManager, cfg }) => {
// we don't have a consistent way to rebuild and
// esbuild has hit all the bugs and struggles to install on the postinstall
await shell('node', ['./node_modules/esbuild/install.js'], { cwd })
if (packageManager === 'yarn') {
await shell('yarn', ['build'], { cwd })
} else {
if (packageManager === 'npm') {
await shell('npm', ['run', 'build'], { cwd })
} else {
await shell(packageManager, ['build'], { cwd })
}
console.log(`
Your installation completed.
$ cd ${cfg.appName}.
$ ${packageManager === 'yarn' ? 'yarn' : 'npm run'} tauri ${
$ cd ${cfg.appName}
$ ${packageManager === 'npm' ? 'npm run' : packageManager} tauri ${
packageManager === 'npm' ? '--' : ''
} dev
`)
}dev
`)
return await Promise.resolve()
}
}

View File

@@ -50,7 +50,7 @@ const vuecli: Recipe = {
Your installation completed.
$ cd ${cfg.appName}
$ ${packageManager === 'yarn' ? 'yarn' : 'npm run'} tauri:serve
$ ${packageManager === 'npm' ? 'npm run' : packageManager} tauri:serve
`)
return await Promise.resolve()
}

View File

@@ -12,7 +12,7 @@ export const shell = async (
): Promise<execa.ExecaReturnValue> => {
try {
if (options && options.shell === true) {
const stringCommand = [command, ...(!args ? [] : args)].join(' ')
const stringCommand = [command, ...(args ?? [])].join(' ')
if (log) console.log(`[running]: ${stringCommand}`)
return await execa(stringCommand, {
stdio: 'inherit',