From e805f93ccccd36266e395531c56ffad2e796b8a6 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Tue, 26 Aug 2025 15:30:09 +0800 Subject: [PATCH] :art: Optimize the user experience of pnpm --- .github/workflows/pake-cli.yaml | 3 ++ .github/workflows/quality-and-test.yml | 9 +++++ .github/workflows/single-app.yaml | 3 ++ .pnpmrc | 3 ++ bin/README.md | 2 +- bin/README_CN.md | 2 +- bin/builders/BaseBuilder.ts | 51 ++++++++++++++++++++++---- bin/builders/LinuxBuilder.ts | 6 +-- bin/builders/MacBuilder.ts | 14 +++---- bin/builders/WinBuilder.ts | 6 +-- package.json | 5 +-- 11 files changed, 77 insertions(+), 27 deletions(-) create mode 100644 .pnpmrc diff --git a/.github/workflows/pake-cli.yaml b/.github/workflows/pake-cli.yaml index 27d3c81..92a6fe9 100644 --- a/.github/workflows/pake-cli.yaml +++ b/.github/workflows/pake-cli.yaml @@ -64,6 +64,9 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + - name: Install pnpm + uses: pnpm/action-setup@v4 + - name: Install node uses: actions/setup-node@v4 with: diff --git a/.github/workflows/quality-and-test.yml b/.github/workflows/quality-and-test.yml index 0418403..0a0763a 100644 --- a/.github/workflows/quality-and-test.yml +++ b/.github/workflows/quality-and-test.yml @@ -22,6 +22,9 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Install pnpm + uses: pnpm/action-setup@v4 + - name: Setup Node.js uses: actions/setup-node@v4 with: @@ -89,6 +92,9 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + - name: Install pnpm + uses: pnpm/action-setup@v4 + - name: Setup Node.js uses: actions/setup-node@v4 with: @@ -157,6 +163,9 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + - name: Install pnpm + uses: pnpm/action-setup@v4 + - name: Setup Node.js uses: actions/setup-node@v4 with: diff --git a/.github/workflows/single-app.yaml b/.github/workflows/single-app.yaml index cf339e2..7eed295 100644 --- a/.github/workflows/single-app.yaml +++ b/.github/workflows/single-app.yaml @@ -72,6 +72,9 @@ jobs: toolchain: ${{ matrix.rust }} target: ${{ matrix.target }} + - name: Install pnpm + uses: pnpm/action-setup@v4 + - name: Setup Node.js uses: actions/setup-node@v4 with: diff --git a/.pnpmrc b/.pnpmrc new file mode 100644 index 0000000..4390944 --- /dev/null +++ b/.pnpmrc @@ -0,0 +1,3 @@ +strict-peer-dependencies=false +node-linker=hoisted +auto-install-peers=true diff --git a/bin/README.md b/bin/README.md index 744cb2c..e3106c5 100644 --- a/bin/README.md +++ b/bin/README.md @@ -401,7 +401,7 @@ export const DEFAULT_DEV_PAKE_OPTIONS: PakeCliOptions & { url: string } = { then ```bash -npm run cli:dev +pnpm run cli:dev ``` The script will read the above configuration and packages the specified `app` using `watch` mode, and changes to the `pake-cli` code and `pake` are hot updated in real time. diff --git a/bin/README_CN.md b/bin/README_CN.md index fbed04f..a580db3 100644 --- a/bin/README_CN.md +++ b/bin/README_CN.md @@ -400,7 +400,7 @@ export const DEFAULT_DEV_PAKE_OPTIONS: PakeCliOptions & { url: string } = { 之后运行 ```bash -npm run cli:dev +pnpm run cli:dev ``` 脚本会读取上述配置并使用 `watch` 模式打包指定的 `app`,对 `pake-cli` 代码和 `pake` 的修改都会实时热更新。 diff --git a/bin/builders/BaseBuilder.ts b/bin/builders/BaseBuilder.ts index a89ec5b..a6251d3 100644 --- a/bin/builders/BaseBuilder.ts +++ b/bin/builders/BaseBuilder.ts @@ -16,6 +16,7 @@ import logger from '@/options/logger'; export default abstract class BaseBuilder { protected options: PakeAppOptions; + private static packageManagerCache: string | null = null; protected constructor(options: PakeAppOptions) { this.options = options; @@ -39,6 +40,35 @@ export default abstract class BaseBuilder { return 900000; // 15 minutes for all builds } + private async detectPackageManager(): Promise { + // 使用缓存避免重复检测 + if (BaseBuilder.packageManagerCache) { + return BaseBuilder.packageManagerCache; + } + + const { execa } = await import('execa'); + + // 优先使用pnpm(如果可用) + try { + await execa('pnpm', ['--version'], { stdio: 'ignore' }); + logger.info('✺ Using pnpm for package management.'); + BaseBuilder.packageManagerCache = 'pnpm'; + return 'pnpm'; + } catch { + // pnpm不可用,回退到npm + try { + await execa('npm', ['--version'], { stdio: 'ignore' }); + logger.info('✺ pnpm not available, using npm for package management.'); + BaseBuilder.packageManagerCache = 'npm'; + return 'npm'; + } catch { + throw new Error( + 'Neither pnpm nor npm is available. Please install a package manager.', + ); + } + } + } + async prepare() { const tauriSrcPath = path.join(npmDirectory, 'src-tauri'); const tauriTargetPath = path.join(tauriSrcPath, 'target'); @@ -70,8 +100,8 @@ export default abstract class BaseBuilder { const projectConf = path.join(rustProjectDir, 'config.toml'); await fsExtra.ensureDir(rustProjectDir); - // 统一使用npm,简单可靠 - const packageManager = 'npm'; + // 智能检测可用的包管理器 + const packageManager = await this.detectPackageManager(); const registryOption = isChina ? ' --registry=https://registry.npmmirror.com' : ''; @@ -82,7 +112,9 @@ export default abstract class BaseBuilder { const buildEnv = this.getBuildEnvironment(); if (isChina) { - logger.info('✺ Located in China, using npm/rsProxy CN mirror.'); + logger.info( + `✺ Located in China, using ${packageManager}/rsProxy CN mirror.`, + ); const projectCnConf = path.join(tauriSrcPath, 'rust_proxy.toml'); await fsExtra.copy(projectCnConf, projectConf); await shellExec( @@ -117,9 +149,12 @@ export default abstract class BaseBuilder { const { name } = this.options; await mergeConfig(url, this.options, tauriConfig); + // Detect available package manager + const packageManager = await this.detectPackageManager(); + // Build app const buildSpinner = getSpinner('Building app...'); - // Let spinner run for a moment so user can see it, then stop before npm command + // Let spinner run for a moment so user can see it, then stop before package manager command await new Promise((resolve) => setTimeout(resolve, 500)); buildSpinner.stop(); // Show static message to keep the status visible @@ -128,7 +163,7 @@ export default abstract class BaseBuilder { const buildEnv = this.getBuildEnvironment(); await shellExec( - `cd "${npmDirectory}" && ${this.getBuildCommand()}`, + `cd "${npmDirectory}" && ${this.getBuildCommand(packageManager)}`, this.getBuildTimeout(), buildEnv, ); @@ -150,10 +185,10 @@ export default abstract class BaseBuilder { abstract getFileName(): string; - protected getBuildCommand(): string { + protected getBuildCommand(packageManager: string = 'pnpm'): string { const baseCommand = this.options.debug - ? 'npm run build:debug' - : 'npm run build'; + ? `${packageManager} run build:debug` + : `${packageManager} run build`; // Use temporary config directory to avoid modifying source files const configPath = path.join( diff --git a/bin/builders/LinuxBuilder.ts b/bin/builders/LinuxBuilder.ts index a620ea6..f692f70 100644 --- a/bin/builders/LinuxBuilder.ts +++ b/bin/builders/LinuxBuilder.ts @@ -58,10 +58,10 @@ export default class LinuxBuilder extends BaseBuilder { } } - protected getBuildCommand(): string { + protected getBuildCommand(packageManager: string = 'pnpm'): string { const baseCommand = this.options.debug - ? 'npm run build:debug' - : 'npm run build'; + ? `${packageManager} run build:debug` + : `${packageManager} run build`; // Use temporary config directory to avoid modifying source files const configPath = path.join('src-tauri', '.pake', 'tauri.conf.json'); diff --git a/bin/builders/MacBuilder.ts b/bin/builders/MacBuilder.ts index eee29d2..22479ba 100644 --- a/bin/builders/MacBuilder.ts +++ b/bin/builders/MacBuilder.ts @@ -48,15 +48,15 @@ export default class MacBuilder extends BaseBuilder { return `${name}_${tauriConfig.version}_${arch}`; } - protected getBuildCommand(): string { + protected getBuildCommand(packageManager: string = 'pnpm'): string { // Determine if we need universal build const needsUniversal = this.buildArch === 'universal' || this.options.multiArch; if (needsUniversal) { const baseCommand = this.options.debug - ? 'npm run tauri build -- --debug' - : 'npm run tauri build --'; + ? `${packageManager} run tauri build -- --debug` + : `${packageManager} run tauri build --`; // Use temporary config directory to avoid modifying source files const configPath = path.join('src-tauri', '.pake', 'tauri.conf.json'); @@ -79,8 +79,8 @@ export default class MacBuilder extends BaseBuilder { } else if (this.buildArch === 'apple') { // Build for Apple Silicon only const baseCommand = this.options.debug - ? 'npm run tauri build -- --debug' - : 'npm run tauri build --'; + ? `${packageManager} run tauri build -- --debug` + : `${packageManager} run tauri build --`; const configPath = path.join('src-tauri', '.pake', 'tauri.conf.json'); let fullCommand = `${baseCommand} --target aarch64-apple-darwin -c "${configPath}"`; @@ -98,8 +98,8 @@ export default class MacBuilder extends BaseBuilder { } else if (this.buildArch === 'intel') { // Build for Intel only const baseCommand = this.options.debug - ? 'npm run tauri build -- --debug' - : 'npm run tauri build --'; + ? `${packageManager} run tauri build -- --debug` + : `${packageManager} run tauri build --`; const configPath = path.join('src-tauri', '.pake', 'tauri.conf.json'); let fullCommand = `${baseCommand} --target x86_64-apple-darwin -c "${configPath}"`; diff --git a/bin/builders/WinBuilder.ts b/bin/builders/WinBuilder.ts index 42e834c..418f00e 100644 --- a/bin/builders/WinBuilder.ts +++ b/bin/builders/WinBuilder.ts @@ -37,10 +37,10 @@ export default class WinBuilder extends BaseBuilder { return `${name}_${tauriConfig.version}_${targetArch}_${language}`; } - protected getBuildCommand(): string { + protected getBuildCommand(packageManager: string = 'pnpm'): string { const baseCommand = this.options.debug - ? 'npm run build:debug' - : 'npm run build'; + ? `${packageManager} run build:debug` + : `${packageManager} run build`; // Use temporary config directory to avoid modifying source files const configPath = path.join('src-tauri', '.pake', 'tauri.conf.json'); diff --git a/package.json b/package.json index d8163bf..7fea0b1 100644 --- a/package.json +++ b/package.json @@ -3,10 +3,7 @@ "version": "3.2.16", "description": "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 利用 Rust 轻松构建轻量级多端桌面应用。", "engines": { - "node": ">=18.0.0", - "pnpm": ">=10.0.0", - "npm": "please-use-pnpm", - "yarn": "please-use-pnpm" + "node": ">=18.0.0" }, "packageManager": "pnpm@10.15.0", "bin": {