🎨 Optimize the user experience of pnpm
This commit is contained in:
3
.github/workflows/pake-cli.yaml
vendored
3
.github/workflows/pake-cli.yaml
vendored
@@ -64,6 +64,9 @@ jobs:
|
|||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
|
||||||
- name: Install node
|
- name: Install node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
9
.github/workflows/quality-and-test.yml
vendored
9
.github/workflows/quality-and-test.yml
vendored
@@ -22,6 +22,9 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
@@ -89,6 +92,9 @@ jobs:
|
|||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
@@ -157,6 +163,9 @@ jobs:
|
|||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
3
.github/workflows/single-app.yaml
vendored
3
.github/workflows/single-app.yaml
vendored
@@ -72,6 +72,9 @@ jobs:
|
|||||||
toolchain: ${{ matrix.rust }}
|
toolchain: ${{ matrix.rust }}
|
||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
3
.pnpmrc
Normal file
3
.pnpmrc
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
strict-peer-dependencies=false
|
||||||
|
node-linker=hoisted
|
||||||
|
auto-install-peers=true
|
||||||
2
bin/README.md
vendored
2
bin/README.md
vendored
@@ -401,7 +401,7 @@ export const DEFAULT_DEV_PAKE_OPTIONS: PakeCliOptions & { url: string } = {
|
|||||||
then
|
then
|
||||||
|
|
||||||
```bash
|
```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.
|
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
2
bin/README_CN.md
vendored
@@ -400,7 +400,7 @@ export const DEFAULT_DEV_PAKE_OPTIONS: PakeCliOptions & { url: string } = {
|
|||||||
之后运行
|
之后运行
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run cli:dev
|
pnpm run cli:dev
|
||||||
```
|
```
|
||||||
|
|
||||||
脚本会读取上述配置并使用 `watch` 模式打包指定的 `app`,对 `pake-cli` 代码和 `pake` 的修改都会实时热更新。
|
脚本会读取上述配置并使用 `watch` 模式打包指定的 `app`,对 `pake-cli` 代码和 `pake` 的修改都会实时热更新。
|
||||||
|
|||||||
51
bin/builders/BaseBuilder.ts
vendored
51
bin/builders/BaseBuilder.ts
vendored
@@ -16,6 +16,7 @@ import logger from '@/options/logger';
|
|||||||
|
|
||||||
export default abstract class BaseBuilder {
|
export default abstract class BaseBuilder {
|
||||||
protected options: PakeAppOptions;
|
protected options: PakeAppOptions;
|
||||||
|
private static packageManagerCache: string | null = null;
|
||||||
|
|
||||||
protected constructor(options: PakeAppOptions) {
|
protected constructor(options: PakeAppOptions) {
|
||||||
this.options = options;
|
this.options = options;
|
||||||
@@ -39,6 +40,35 @@ export default abstract class BaseBuilder {
|
|||||||
return 900000; // 15 minutes for all builds
|
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() {
|
async prepare() {
|
||||||
const tauriSrcPath = path.join(npmDirectory, 'src-tauri');
|
const tauriSrcPath = path.join(npmDirectory, 'src-tauri');
|
||||||
const tauriTargetPath = path.join(tauriSrcPath, 'target');
|
const tauriTargetPath = path.join(tauriSrcPath, 'target');
|
||||||
@@ -70,8 +100,8 @@ export default abstract class BaseBuilder {
|
|||||||
const projectConf = path.join(rustProjectDir, 'config.toml');
|
const projectConf = path.join(rustProjectDir, 'config.toml');
|
||||||
await fsExtra.ensureDir(rustProjectDir);
|
await fsExtra.ensureDir(rustProjectDir);
|
||||||
|
|
||||||
// 统一使用npm,简单可靠
|
// 智能检测可用的包管理器
|
||||||
const packageManager = 'npm';
|
const packageManager = await this.detectPackageManager();
|
||||||
const registryOption = isChina
|
const registryOption = isChina
|
||||||
? ' --registry=https://registry.npmmirror.com'
|
? ' --registry=https://registry.npmmirror.com'
|
||||||
: '';
|
: '';
|
||||||
@@ -82,7 +112,9 @@ export default abstract class BaseBuilder {
|
|||||||
const buildEnv = this.getBuildEnvironment();
|
const buildEnv = this.getBuildEnvironment();
|
||||||
|
|
||||||
if (isChina) {
|
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');
|
const projectCnConf = path.join(tauriSrcPath, 'rust_proxy.toml');
|
||||||
await fsExtra.copy(projectCnConf, projectConf);
|
await fsExtra.copy(projectCnConf, projectConf);
|
||||||
await shellExec(
|
await shellExec(
|
||||||
@@ -117,9 +149,12 @@ export default abstract class BaseBuilder {
|
|||||||
const { name } = this.options;
|
const { name } = this.options;
|
||||||
await mergeConfig(url, this.options, tauriConfig);
|
await mergeConfig(url, this.options, tauriConfig);
|
||||||
|
|
||||||
|
// Detect available package manager
|
||||||
|
const packageManager = await this.detectPackageManager();
|
||||||
|
|
||||||
// Build app
|
// Build app
|
||||||
const buildSpinner = getSpinner('Building 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));
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||||
buildSpinner.stop();
|
buildSpinner.stop();
|
||||||
// Show static message to keep the status visible
|
// Show static message to keep the status visible
|
||||||
@@ -128,7 +163,7 @@ export default abstract class BaseBuilder {
|
|||||||
const buildEnv = this.getBuildEnvironment();
|
const buildEnv = this.getBuildEnvironment();
|
||||||
|
|
||||||
await shellExec(
|
await shellExec(
|
||||||
`cd "${npmDirectory}" && ${this.getBuildCommand()}`,
|
`cd "${npmDirectory}" && ${this.getBuildCommand(packageManager)}`,
|
||||||
this.getBuildTimeout(),
|
this.getBuildTimeout(),
|
||||||
buildEnv,
|
buildEnv,
|
||||||
);
|
);
|
||||||
@@ -150,10 +185,10 @@ export default abstract class BaseBuilder {
|
|||||||
|
|
||||||
abstract getFileName(): string;
|
abstract getFileName(): string;
|
||||||
|
|
||||||
protected getBuildCommand(): string {
|
protected getBuildCommand(packageManager: string = 'pnpm'): string {
|
||||||
const baseCommand = this.options.debug
|
const baseCommand = this.options.debug
|
||||||
? 'npm run build:debug'
|
? `${packageManager} run build:debug`
|
||||||
: 'npm run build';
|
: `${packageManager} run build`;
|
||||||
|
|
||||||
// Use temporary config directory to avoid modifying source files
|
// Use temporary config directory to avoid modifying source files
|
||||||
const configPath = path.join(
|
const configPath = path.join(
|
||||||
|
|||||||
6
bin/builders/LinuxBuilder.ts
vendored
6
bin/builders/LinuxBuilder.ts
vendored
@@ -58,10 +58,10 @@ export default class LinuxBuilder extends BaseBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getBuildCommand(): string {
|
protected getBuildCommand(packageManager: string = 'pnpm'): string {
|
||||||
const baseCommand = this.options.debug
|
const baseCommand = this.options.debug
|
||||||
? 'npm run build:debug'
|
? `${packageManager} run build:debug`
|
||||||
: 'npm run build';
|
: `${packageManager} run build`;
|
||||||
|
|
||||||
// Use temporary config directory to avoid modifying source files
|
// Use temporary config directory to avoid modifying source files
|
||||||
const configPath = path.join('src-tauri', '.pake', 'tauri.conf.json');
|
const configPath = path.join('src-tauri', '.pake', 'tauri.conf.json');
|
||||||
|
|||||||
14
bin/builders/MacBuilder.ts
vendored
14
bin/builders/MacBuilder.ts
vendored
@@ -48,15 +48,15 @@ export default class MacBuilder extends BaseBuilder {
|
|||||||
return `${name}_${tauriConfig.version}_${arch}`;
|
return `${name}_${tauriConfig.version}_${arch}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getBuildCommand(): string {
|
protected getBuildCommand(packageManager: string = 'pnpm'): string {
|
||||||
// Determine if we need universal build
|
// Determine if we need universal build
|
||||||
const needsUniversal =
|
const needsUniversal =
|
||||||
this.buildArch === 'universal' || this.options.multiArch;
|
this.buildArch === 'universal' || this.options.multiArch;
|
||||||
|
|
||||||
if (needsUniversal) {
|
if (needsUniversal) {
|
||||||
const baseCommand = this.options.debug
|
const baseCommand = this.options.debug
|
||||||
? 'npm run tauri build -- --debug'
|
? `${packageManager} run tauri build -- --debug`
|
||||||
: 'npm run tauri build --';
|
: `${packageManager} run tauri build --`;
|
||||||
|
|
||||||
// Use temporary config directory to avoid modifying source files
|
// Use temporary config directory to avoid modifying source files
|
||||||
const configPath = path.join('src-tauri', '.pake', 'tauri.conf.json');
|
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') {
|
} else if (this.buildArch === 'apple') {
|
||||||
// Build for Apple Silicon only
|
// Build for Apple Silicon only
|
||||||
const baseCommand = this.options.debug
|
const baseCommand = this.options.debug
|
||||||
? 'npm run tauri build -- --debug'
|
? `${packageManager} run tauri build -- --debug`
|
||||||
: 'npm run tauri build --';
|
: `${packageManager} run tauri build --`;
|
||||||
const configPath = path.join('src-tauri', '.pake', 'tauri.conf.json');
|
const configPath = path.join('src-tauri', '.pake', 'tauri.conf.json');
|
||||||
let fullCommand = `${baseCommand} --target aarch64-apple-darwin -c "${configPath}"`;
|
let fullCommand = `${baseCommand} --target aarch64-apple-darwin -c "${configPath}"`;
|
||||||
|
|
||||||
@@ -98,8 +98,8 @@ export default class MacBuilder extends BaseBuilder {
|
|||||||
} else if (this.buildArch === 'intel') {
|
} else if (this.buildArch === 'intel') {
|
||||||
// Build for Intel only
|
// Build for Intel only
|
||||||
const baseCommand = this.options.debug
|
const baseCommand = this.options.debug
|
||||||
? 'npm run tauri build -- --debug'
|
? `${packageManager} run tauri build -- --debug`
|
||||||
: 'npm run tauri build --';
|
: `${packageManager} run tauri build --`;
|
||||||
const configPath = path.join('src-tauri', '.pake', 'tauri.conf.json');
|
const configPath = path.join('src-tauri', '.pake', 'tauri.conf.json');
|
||||||
let fullCommand = `${baseCommand} --target x86_64-apple-darwin -c "${configPath}"`;
|
let fullCommand = `${baseCommand} --target x86_64-apple-darwin -c "${configPath}"`;
|
||||||
|
|
||||||
|
|||||||
6
bin/builders/WinBuilder.ts
vendored
6
bin/builders/WinBuilder.ts
vendored
@@ -37,10 +37,10 @@ export default class WinBuilder extends BaseBuilder {
|
|||||||
return `${name}_${tauriConfig.version}_${targetArch}_${language}`;
|
return `${name}_${tauriConfig.version}_${targetArch}_${language}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getBuildCommand(): string {
|
protected getBuildCommand(packageManager: string = 'pnpm'): string {
|
||||||
const baseCommand = this.options.debug
|
const baseCommand = this.options.debug
|
||||||
? 'npm run build:debug'
|
? `${packageManager} run build:debug`
|
||||||
: 'npm run build';
|
: `${packageManager} run build`;
|
||||||
|
|
||||||
// Use temporary config directory to avoid modifying source files
|
// Use temporary config directory to avoid modifying source files
|
||||||
const configPath = path.join('src-tauri', '.pake', 'tauri.conf.json');
|
const configPath = path.join('src-tauri', '.pake', 'tauri.conf.json');
|
||||||
|
|||||||
@@ -3,10 +3,7 @@
|
|||||||
"version": "3.2.16",
|
"version": "3.2.16",
|
||||||
"description": "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 利用 Rust 轻松构建轻量级多端桌面应用。",
|
"description": "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 利用 Rust 轻松构建轻量级多端桌面应用。",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0",
|
"node": ">=18.0.0"
|
||||||
"pnpm": ">=10.0.0",
|
|
||||||
"npm": "please-use-pnpm",
|
|
||||||
"yarn": "please-use-pnpm"
|
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.15.0",
|
"packageManager": "pnpm@10.15.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|||||||
Reference in New Issue
Block a user