diff --git a/.changes/cta-pnpm-support.md b/.changes/cta-pnpm-support.md new file mode 100644 index 000000000..287192e86 --- /dev/null +++ b/.changes/cta-pnpm-support.md @@ -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`. diff --git a/tooling/create-tauri-app/src/dependency-manager.ts b/tooling/create-tauri-app/src/dependency-manager.ts index c671f6c8c..48f7cdc65 100644 --- a/tooling/create-tauri-app/src/dependency-manager.ts +++ b/tooling/create-tauri-app/src/dependency-manager.ts @@ -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 { - 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 { - 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 + } + ) + } } } diff --git a/tooling/create-tauri-app/src/index.ts b/tooling/create-tauri-app/src/index.ts index 4498a7a21..0cb7811ce 100644 --- a/tooling/create-tauri-app/src/index.ts +++ b/tooling/create-tauri-app/src/index.ts @@ -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 => { } 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 => { 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 }) diff --git a/tooling/create-tauri-app/src/recipes/dominator.ts b/tooling/create-tauri-app/src/recipes/dominator.ts index 799c5ee88..771bbc865 100644 --- a/tooling/create-tauri-app/src/recipes/dominator.ts +++ b/tooling/create-tauri-app/src/recipes/dominator.ts @@ -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() } diff --git a/tooling/create-tauri-app/src/recipes/ng-cli.ts b/tooling/create-tauri-app/src/recipes/ng-cli.ts index f73ba4438..b0fff28b9 100644 --- a/tooling/create-tauri-app/src/recipes/ng-cli.ts +++ b/tooling/create-tauri-app/src/recipes/ng-cli.ts @@ -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 => { 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() diff --git a/tooling/create-tauri-app/src/recipes/react.ts b/tooling/create-tauri-app/src/recipes/react.ts index 164abf8ad..c2e75688e 100644 --- a/tooling/create-tauri-app/src/recipes/react.ts +++ b/tooling/create-tauri-app/src/recipes/react.ts @@ -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() } } diff --git a/tooling/create-tauri-app/src/recipes/svelte.ts b/tooling/create-tauri-app/src/recipes/svelte.ts index 089109f52..c8365db39 100644 --- a/tooling/create-tauri-app/src/recipes/svelte.ts +++ b/tooling/create-tauri-app/src/recipes/svelte.ts @@ -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() diff --git a/tooling/create-tauri-app/src/recipes/vanilla.ts b/tooling/create-tauri-app/src/recipes/vanilla.ts index c564dcdf3..e083d0e48 100644 --- a/tooling/create-tauri-app/src/recipes/vanilla.ts +++ b/tooling/create-tauri-app/src/recipes/vanilla.ts @@ -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() } } diff --git a/tooling/create-tauri-app/src/recipes/vite.ts b/tooling/create-tauri-app/src/recipes/vite.ts index 0e8ff5d08..456852b33 100644 --- a/tooling/create-tauri-app/src/recipes/vite.ts +++ b/tooling/create-tauri-app/src/recipes/vite.ts @@ -7,23 +7,6 @@ import { shell } from '../shell' import { Recipe } from '../types/recipe' -const afterViteCA = async ( - cwd: string, - appName: string, - template: string -): Promise => { - // 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() } } diff --git a/tooling/create-tauri-app/src/recipes/vue-cli.ts b/tooling/create-tauri-app/src/recipes/vue-cli.ts index 746ca2554..fdb19d994 100644 --- a/tooling/create-tauri-app/src/recipes/vue-cli.ts +++ b/tooling/create-tauri-app/src/recipes/vue-cli.ts @@ -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() } diff --git a/tooling/create-tauri-app/src/shell.ts b/tooling/create-tauri-app/src/shell.ts index 0473e994e..5ffd710df 100644 --- a/tooling/create-tauri-app/src/shell.ts +++ b/tooling/create-tauri-app/src/shell.ts @@ -12,7 +12,7 @@ export const shell = async ( ): Promise => { 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',