🎨 Optimize the user experience of pnpm

This commit is contained in:
Tw93
2025-08-26 15:30:09 +08:00
parent 752623f3dd
commit e805f93ccc
11 changed files with 77 additions and 27 deletions

2
bin/README.md vendored
View File

@@ -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.

2
bin/README_CN.md vendored
View File

@@ -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` 的修改都会实时热更新。

View File

@@ -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<string> {
// 使用缓存避免重复检测
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(

View File

@@ -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');

View File

@@ -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}"`;

View File

@@ -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');