182 lines
5.5 KiB
TypeScript
Vendored
182 lines
5.5 KiB
TypeScript
Vendored
import path from 'path';
|
|
import fsExtra from 'fs-extra';
|
|
import chalk from 'chalk';
|
|
import prompts from 'prompts';
|
|
|
|
import { PakeAppOptions } from '@/types';
|
|
import { checkRustInstalled, installRust } from '@/helpers/rust';
|
|
import { mergeConfig } from '@/helpers/merge';
|
|
import tauriConfig from '@/helpers/tauriConfig';
|
|
import { npmDirectory } from '@/utils/dir';
|
|
import { getSpinner } from '@/utils/info';
|
|
import { shellExec } from '@/utils/shell';
|
|
import { isChinaDomain } from '@/utils/ip';
|
|
import { IS_MAC } from '@/utils/platform';
|
|
import logger from '@/options/logger';
|
|
|
|
export default abstract class BaseBuilder {
|
|
protected options: PakeAppOptions;
|
|
|
|
protected constructor(options: PakeAppOptions) {
|
|
this.options = options;
|
|
}
|
|
|
|
async prepare() {
|
|
const tauriSrcPath = path.join(npmDirectory, 'src-tauri');
|
|
const tauriTargetPath = path.join(tauriSrcPath, 'target');
|
|
const tauriTargetPathExists = await fsExtra.pathExists(tauriTargetPath);
|
|
|
|
if (!IS_MAC && !tauriTargetPathExists) {
|
|
logger.warn('✼ The first use requires installing system dependencies.');
|
|
logger.warn('✼ See more in https://tauri.app/start/prerequisites/.');
|
|
}
|
|
|
|
if (!checkRustInstalled()) {
|
|
const res = await prompts({
|
|
type: 'confirm',
|
|
message: 'Rust not detected. Install now?',
|
|
name: 'value',
|
|
});
|
|
|
|
if (res.value) {
|
|
await installRust();
|
|
} else {
|
|
logger.error('✕ Rust required to package your webapp.');
|
|
process.exit(0);
|
|
}
|
|
}
|
|
|
|
const isChina = await isChinaDomain('www.npmjs.com');
|
|
const spinner = getSpinner('Installing package...');
|
|
const rustProjectDir = path.join(tauriSrcPath, '.cargo');
|
|
const projectConf = path.join(rustProjectDir, 'config.toml');
|
|
await fsExtra.ensureDir(rustProjectDir);
|
|
|
|
// For global CLI installation, always use npm
|
|
const packageManager = 'npm';
|
|
const registryOption = isChina
|
|
? ' --registry=https://registry.npmmirror.com'
|
|
: '';
|
|
|
|
// Windows环境下需要更长的超时时间
|
|
const timeout = process.platform === 'win32' ? 600000 : 300000;
|
|
|
|
if (isChina) {
|
|
logger.info('✺ Located in China, using npm/rsProxy CN mirror.');
|
|
const projectCnConf = path.join(tauriSrcPath, 'rust_proxy.toml');
|
|
await fsExtra.copy(projectCnConf, projectConf);
|
|
await shellExec(
|
|
`cd "${npmDirectory}" && ${packageManager} install${registryOption}`,
|
|
timeout,
|
|
);
|
|
} else {
|
|
await shellExec(
|
|
`cd "${npmDirectory}" && ${packageManager} install`,
|
|
timeout,
|
|
);
|
|
}
|
|
spinner.succeed(chalk.green('Package installed!'));
|
|
if (!tauriTargetPathExists) {
|
|
logger.warn(
|
|
'✼ The first packaging may be slow, please be patient and wait, it will be faster afterwards.',
|
|
);
|
|
}
|
|
}
|
|
|
|
async build(url: string) {
|
|
await this.buildAndCopy(url, this.options.targets);
|
|
}
|
|
|
|
async start(url: string) {
|
|
await mergeConfig(url, this.options, tauriConfig);
|
|
}
|
|
|
|
async buildAndCopy(url: string, target: string) {
|
|
const { name } = this.options;
|
|
await mergeConfig(url, this.options, tauriConfig);
|
|
|
|
// Build app
|
|
const spinner = getSpinner('Building app...');
|
|
setTimeout(() => spinner.stop(), 3000);
|
|
await shellExec(`cd "${npmDirectory}" && ${this.getBuildCommand()}`);
|
|
|
|
// Copy app
|
|
const fileName = this.getFileName();
|
|
const fileType = this.getFileType(target);
|
|
const appPath = this.getBuildAppPath(npmDirectory, fileName, fileType);
|
|
const distPath = path.resolve(`${name}.${fileType}`);
|
|
await fsExtra.copy(appPath, distPath);
|
|
await fsExtra.remove(appPath);
|
|
logger.success('✔ Build success!');
|
|
logger.success('✔ App installer located in', distPath);
|
|
}
|
|
|
|
protected getFileType(target: string): string {
|
|
return target;
|
|
}
|
|
|
|
abstract getFileName(): string;
|
|
|
|
protected getBuildCommand(): string {
|
|
const baseCommand = this.options.debug
|
|
? 'npm run build:debug'
|
|
: 'npm run build';
|
|
|
|
// Use temporary config directory to avoid modifying source files
|
|
const configPath = path.join(
|
|
npmDirectory,
|
|
'src-tauri',
|
|
'.pake',
|
|
'tauri.conf.json',
|
|
);
|
|
let fullCommand = `${baseCommand} -- -c "${configPath}" --features cli-build`;
|
|
|
|
// For macOS, use app bundles by default unless DMG is explicitly requested
|
|
if (IS_MAC && this.options.targets === 'app') {
|
|
fullCommand += ' --bundles app';
|
|
}
|
|
|
|
// Add macos-proxy feature for modern macOS (Darwin 23+ = macOS 14+)
|
|
if (IS_MAC) {
|
|
const macOSVersion = this.getMacOSMajorVersion();
|
|
if (macOSVersion >= 23) {
|
|
fullCommand += ',macos-proxy';
|
|
}
|
|
}
|
|
|
|
return fullCommand;
|
|
}
|
|
|
|
private getMacOSMajorVersion(): number {
|
|
try {
|
|
const os = require('os');
|
|
const release = os.release();
|
|
const majorVersion = parseInt(release.split('.')[0], 10);
|
|
return majorVersion;
|
|
} catch (error) {
|
|
return 0; // Disable proxy feature if version detection fails
|
|
}
|
|
}
|
|
|
|
protected getBasePath(): string {
|
|
const basePath = this.options.debug ? 'debug' : 'release';
|
|
return `src-tauri/target/${basePath}/bundle/`;
|
|
}
|
|
|
|
protected getBuildAppPath(
|
|
npmDirectory: string,
|
|
fileName: string,
|
|
fileType: string,
|
|
): string {
|
|
// For app bundles on macOS, the directory is 'macos', not 'app'
|
|
const bundleDir =
|
|
fileType.toLowerCase() === 'app' ? 'macos' : fileType.toLowerCase();
|
|
return path.join(
|
|
npmDirectory,
|
|
this.getBasePath(),
|
|
bundleDir,
|
|
`${fileName}.${fileType}`,
|
|
);
|
|
}
|
|
}
|