pake-cli
Version:
🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 利用 Rust 轻松构建轻量级多端桌面应用。
821 lines (795 loc) • 27 kB
JavaScript
import log from 'loglevel';
import fsExtra from 'fs-extra';
import chalk from 'chalk';
import path from 'path';
import axios from 'axios';
import { dir } from 'tmp-promise';
import { fileURLToPath } from 'url';
import crypto from 'crypto';
import prompts from 'prompts';
import ora from 'ora';
import { fileTypeFromBuffer } from 'file-type';
import * as psl from 'psl';
import 'is-url';
import shelljs from 'shelljs';
import dns from 'dns';
import http from 'http';
import { promisify } from 'util';
import fs from 'fs';
const DEFAULT_PAKE_OPTIONS = {
icon: '',
height: 780,
width: 1200,
fullscreen: false,
resizable: true,
hideTitleBar: false,
alwaysOnTop: false,
appVersion: '1.0.0',
darkMode: false,
disabledWebShortcuts: false,
activationShortcut: '',
userAgent: '',
showSystemTray: false,
multiArch: false,
targets: 'deb',
useLocalFile: false,
systemTrayIcon: '',
proxyUrl: "",
debug: false,
inject: [],
installerLanguage: 'en-US',
};
// Just for cli development
const DEFAULT_DEV_PAKE_OPTIONS = {
...DEFAULT_PAKE_OPTIONS,
url: 'https://weread.qq.com',
name: 'WeRead',
hideTitleBar: true,
};
const logger = {
info(...msg) {
log.info(...msg.map(m => chalk.white(m)));
},
debug(...msg) {
log.debug(...msg);
},
error(...msg) {
log.error(...msg.map(m => chalk.red(m)));
},
warn(...msg) {
log.info(...msg.map(m => chalk.yellow(m)));
},
success(...msg) {
log.info(...msg.map(m => chalk.green(m)));
},
};
// Convert the current module URL to a file path
const currentModulePath = fileURLToPath(import.meta.url);
// Resolve the parent directory of the current module
const npmDirectory = path.join(path.dirname(currentModulePath), '..');
const tauriConfigDirectory = path.join(npmDirectory, 'src-tauri');
const { platform: platform$2 } = process;
const IS_MAC = platform$2 === 'darwin';
const IS_WIN = platform$2 === 'win32';
const IS_LINUX = platform$2 === 'linux';
// Generates an identifier based on the given URL.
function getIdentifier(url) {
const postFixHash = crypto.createHash('md5').update(url).digest('hex').substring(0, 6);
return `com.pake.${postFixHash}`;
}
async function promptText(message, initial) {
const response = await prompts({
type: 'text',
name: 'content',
message,
initial,
});
return response.content;
}
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
function getSpinner(text) {
const loadingType = {
interval: 80,
frames: ['✦', '✶', '✺', '✵', '✸', '✹', '✺'],
};
return ora({
text: `${chalk.cyan(text)}\n`,
spinner: loadingType,
color: 'cyan',
}).start();
}
async function handleIcon(options) {
if (options.icon) {
if (options.icon.startsWith('http')) {
return downloadIcon(options.icon);
}
else {
return path.resolve(options.icon);
}
}
else {
logger.warn('✼ No icon given, default in use. For a custom icon, use --icon option.');
const iconPath = IS_WIN
? 'src-tauri/png/icon_256.ico'
: IS_LINUX
? 'src-tauri/png/icon_512.png'
: 'src-tauri/icons/icon.icns';
return path.join(npmDirectory, iconPath);
}
}
async function downloadIcon(iconUrl) {
const spinner = getSpinner('Downloading icon...');
try {
const iconResponse = await axios.get(iconUrl, { responseType: 'arraybuffer' });
const iconData = await iconResponse.data;
if (!iconData) {
return null;
}
const fileDetails = await fileTypeFromBuffer(iconData);
if (!fileDetails) {
return null;
}
const { path: tempPath } = await dir();
let iconPath = `${tempPath}/icon.${fileDetails.ext}`;
// Fix this for linux
if (IS_LINUX) {
iconPath = 'png/linux_temp.png';
await fsExtra.outputFile(`${npmDirectory}/src-tauri/${iconPath}`, iconData);
}
else {
await fsExtra.outputFile(iconPath, iconData);
}
await fsExtra.outputFile(iconPath, iconData);
spinner.succeed(chalk.green('Icon downloaded successfully!'));
return iconPath;
}
catch (error) {
spinner.fail(chalk.red('Icon download failed!'));
if (error.response && error.response.status === 404) {
return null;
}
throw error;
}
}
// Extracts the domain from a given URL.
function getDomain(inputUrl) {
try {
const url = new URL(inputUrl);
// Use PSL to parse domain names.
const parsed = psl.parse(url.hostname);
// If domain is available, split it and return the SLD.
if ('domain' in parsed && parsed.domain) {
return parsed.domain.split('.')[0];
}
else {
return null;
}
}
catch (error) {
return null;
}
}
function resolveAppName(name, platform) {
const domain = getDomain(name) || 'pake';
return platform !== 'linux' ? capitalizeFirstLetter(domain) : domain;
}
function isValidName(name, platform) {
const platformRegexMapping = {
linux: /^[a-z0-9]+(-[a-z0-9]+)*$/,
default: /^[a-zA-Z0-9]+([-a-zA-Z0-9])*$/,
};
const reg = platformRegexMapping[platform] || platformRegexMapping.default;
return !!name && reg.test(name);
}
async function handleOptions(options, url) {
const { platform } = process;
const isActions = process.env.GITHUB_ACTIONS;
let name = options.name;
const pathExists = await fsExtra.pathExists(url);
if (!options.name) {
const defaultName = pathExists ? '' : resolveAppName(url, platform);
const promptMessage = 'Enter your application name';
const namePrompt = await promptText(promptMessage, defaultName);
name = namePrompt || defaultName;
}
if (!isValidName(name, platform)) {
const LINUX_NAME_ERROR = `✕ name should only include lowercase letters, numbers, and dashes, and must contain at least one lowercase letter. Examples: com-123-xxx, 123pan, pan123, weread, we-read.`;
const DEFAULT_NAME_ERROR = `✕ Name should only include letters and numbers, and dashes (dashes must not at the beginning), and must contain at least one letter. Examples: 123pan, 123Pan, Pan123, weread, WeRead, WERead, we-read.`;
const errorMsg = platform === 'linux' ? LINUX_NAME_ERROR : DEFAULT_NAME_ERROR;
logger.error(errorMsg);
if (isActions) {
name = resolveAppName(url, platform);
logger.warn(`✼ Inside github actions, use the default name: ${name}`);
}
else {
process.exit(1);
}
}
const appOptions = {
...options,
name,
identifier: getIdentifier(url),
};
appOptions.icon = await handleIcon(appOptions);
return appOptions;
}
var windows = [
{
url: "https://weread.qq.com",
url_type: "web",
hide_title_bar: true,
fullscreen: false,
width: 1200,
height: 780,
resizable: true,
always_on_top: false,
dark_mode: false,
activation_shortcut: "",
disabled_web_shortcuts: false
}
];
var user_agent = {
macos: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Safari/605.1.15",
linux: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
windows: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36"
};
var system_tray = {
macos: false,
linux: true,
windows: true
};
var system_tray_path = "icons/icon.png";
var inject = [
];
var proxy_url = "";
var pakeConf = {
windows: windows,
user_agent: user_agent,
system_tray: system_tray,
system_tray_path: system_tray_path,
inject: inject,
proxy_url: proxy_url
};
var productName$1 = "WeRead";
var identifier = "com.pake.weread";
var version = "1.0.0";
var app = {
withGlobalTauri: true,
trayIcon: {
iconPath: "png/weread_512.png",
iconAsTemplate: false,
id: "pake-tray"
}
};
var build = {
frontendDist: "../dist"
};
var CommonConf = {
productName: productName$1,
identifier: identifier,
version: version,
app: app,
build: build
};
var bundle$2 = {
icon: [
"png/weread_256.ico",
"png/weread_32.ico"
],
active: true,
resources: [
"png/weread_32.ico"
],
targets: [
"msi"
],
windows: {
digestAlgorithm: "sha256",
wix: {
language: [
"en-US"
],
template: "assets/main.wxs"
}
}
};
var WinConf = {
bundle: bundle$2
};
var bundle$1 = {
icon: [
"icons/weread.icns"
],
active: true,
macOS: {
},
targets: [
"dmg"
]
};
var MacConf = {
bundle: bundle$1
};
var productName = "we-read";
var bundle = {
icon: [
"png/weread_512.png"
],
active: true,
linux: {
deb: {
depends: [
"curl",
"wget"
],
files: {
"/usr/share/applications/com-pake-weread.desktop": "assets/com-pake-weread.desktop"
}
}
},
targets: [
"deb",
"appimage"
]
};
var LinuxConf = {
productName: productName,
bundle: bundle
};
const platformConfigs = {
win32: WinConf,
darwin: MacConf,
linux: LinuxConf,
};
const { platform: platform$1 } = process;
// @ts-ignore
const platformConfig = platformConfigs[platform$1];
let tauriConfig = {
...CommonConf,
bundle: platformConfig.bundle,
app: {
...CommonConf.app,
trayIcon: {
...(platformConfig?.app?.trayIcon ?? {}),
},
},
build: CommonConf.build,
pake: pakeConf,
};
function shellExec(command) {
return new Promise((resolve, reject) => {
shelljs.exec(command, { async: true, silent: false, cwd: npmDirectory }, code => {
if (code === 0) {
resolve(0);
}
else {
reject(new Error(`Error occurred while executing command "${command}". Exit code: ${code}`));
}
});
});
}
const resolve = promisify(dns.resolve);
const ping = async (host) => {
const lookup = promisify(dns.lookup);
const ip = await lookup(host);
const start = new Date();
// Prevent timeouts from affecting user experience.
const requestPromise = new Promise((resolve, reject) => {
const req = http.get(`http://${ip.address}`, res => {
const delay = new Date().getTime() - start.getTime();
res.resume();
resolve(delay);
});
req.on('error', err => {
reject(err);
});
});
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {
reject(new Error('Request timed out after 3 seconds'));
}, 1000);
});
return Promise.race([requestPromise, timeoutPromise]);
};
async function isChinaDomain(domain) {
try {
const [ip] = await resolve(domain);
return await isChinaIP(ip, domain);
}
catch (error) {
logger.debug(`${domain} can't be parse!`);
return true;
}
}
async function isChinaIP(ip, domain) {
try {
const delay = await ping(ip);
logger.debug(`${domain} latency is ${delay} ms`);
return delay > 1000;
}
catch (error) {
logger.debug(`ping ${domain} failed!`);
return true;
}
}
async function installRust() {
const isActions = process.env.GITHUB_ACTIONS;
const isInChina = await isChinaDomain('sh.rustup.rs');
const rustInstallScriptForMac = isInChina && !isActions
? 'export RUSTUP_DIST_SERVER="https://rsproxy.cn" && export RUSTUP_UPDATE_ROOT="https://rsproxy.cn/rustup" && curl --proto "=https" --tlsv1.2 -sSf https://rsproxy.cn/rustup-init.sh | sh'
: "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y";
const rustInstallScriptForWindows = 'winget install --id Rustlang.Rustup';
const spinner = getSpinner('Downloading Rust...');
try {
await shellExec(IS_WIN ? rustInstallScriptForWindows : rustInstallScriptForMac);
spinner.succeed(chalk.green('Rust installed successfully!'));
}
catch (error) {
console.error('Error installing Rust:', error.message);
spinner.fail(chalk.red('Rust installation failed!'));
process.exit(1);
}
}
function checkRustInstalled() {
return shelljs.exec('rustc --version', { silent: true }).code === 0;
}
async function combineFiles(files, output) {
const contents = files.map(file => {
const fileContent = fs.readFileSync(file);
if (file.endsWith('.css')) {
return ("window.addEventListener('DOMContentLoaded', (_event) => { const css = `" +
fileContent +
"`; const style = document.createElement('style'); style.innerHTML = css; document.head.appendChild(style); });");
}
return "window.addEventListener('DOMContentLoaded', (_event) => { " + fileContent + ' });';
});
fs.writeFileSync(output, contents.join('\n'));
return files;
}
async function mergeConfig(url, options, tauriConf) {
const { width, height, fullscreen, hideTitleBar, alwaysOnTop, appVersion, darkMode, disabledWebShortcuts, activationShortcut, userAgent, showSystemTray, systemTrayIcon, useLocalFile, identifier, name, resizable = true, inject, proxyUrl, installerLanguage, } = options;
const { platform } = process;
// Set Windows parameters.
const tauriConfWindowOptions = {
width,
height,
fullscreen,
resizable,
hide_title_bar: hideTitleBar,
activation_shortcut: activationShortcut,
always_on_top: alwaysOnTop,
dark_mode: darkMode,
disabled_web_shortcuts: disabledWebShortcuts,
};
Object.assign(tauriConf.pake.windows[0], { url, ...tauriConfWindowOptions });
tauriConf.productName = name;
tauriConf.identifier = identifier;
tauriConf.version = appVersion;
if (platform == 'win32') {
tauriConf.bundle.windows.wix.language[0] = installerLanguage;
}
//Judge the type of URL, whether it is a file or a website.
const pathExists = await fsExtra.pathExists(url);
if (pathExists) {
logger.warn('✼ Your input might be a local file.');
tauriConf.pake.windows[0].url_type = 'local';
const fileName = path.basename(url);
const dirName = path.dirname(url);
const distDir = path.join(npmDirectory, 'dist');
const distBakDir = path.join(npmDirectory, 'dist_bak');
if (!useLocalFile) {
const urlPath = path.join(distDir, fileName);
await fsExtra.copy(url, urlPath);
}
else {
fsExtra.moveSync(distDir, distBakDir, { overwrite: true });
fsExtra.copySync(dirName, distDir, { overwrite: true });
// ignore it, because about_pake.html have be erased.
// const filesToCopyBack = ['cli.js', 'about_pake.html'];
const filesToCopyBack = ['cli.js'];
await Promise.all(filesToCopyBack.map(file => fsExtra.copy(path.join(distBakDir, file), path.join(distDir, file))));
}
tauriConf.pake.windows[0].url = fileName;
tauriConf.pake.windows[0].url_type = 'local';
}
else {
tauriConf.pake.windows[0].url_type = 'web';
}
const platformMap = {
win32: 'windows',
linux: 'linux',
darwin: 'macos',
};
const currentPlatform = platformMap[platform];
if (userAgent.length > 0) {
tauriConf.pake.user_agent[currentPlatform] = userAgent;
}
tauriConf.pake.system_tray[currentPlatform] = showSystemTray;
// Processing targets are currently only open to Linux.
if (platform === 'linux') {
delete tauriConf.bundle.linux.deb.files;
const validTargets = ['all', 'deb', 'appimage', 'rpm'];
if (validTargets.includes(options.targets)) {
tauriConf.bundle.targets = options.targets === 'all' ? ['deb', 'appimage', 'rpm'] : [options.targets];
}
else {
logger.warn(`✼ The target must be one of ${validTargets.join(', ')}, the default 'deb' will be used.`);
}
}
// Set icon.
const platformIconMap = {
win32: {
fileExt: '.ico',
path: `png/${name.toLowerCase()}_256.ico`,
defaultIcon: 'png/icon_256.ico',
message: 'Windows icon must be .ico and 256x256px.',
},
linux: {
fileExt: '.png',
path: `png/${name.toLowerCase()}_512.png`,
defaultIcon: 'png/icon_512.png',
message: 'Linux icon must be .png and 512x512px.',
},
darwin: {
fileExt: '.icns',
path: `icons/${name.toLowerCase()}.icns`,
defaultIcon: 'icons/icon.icns',
message: 'macOS icon must be .icns type.',
},
};
const iconInfo = platformIconMap[platform];
const exists = await fsExtra.pathExists(options.icon);
if (exists) {
let updateIconPath = true;
let customIconExt = path.extname(options.icon).toLowerCase();
if (customIconExt !== iconInfo.fileExt) {
updateIconPath = false;
logger.warn(`✼ ${iconInfo.message}, but you give ${customIconExt}`);
tauriConf.bundle.icon = [iconInfo.defaultIcon];
}
else {
const iconPath = path.join(npmDirectory, 'src-tauri/', iconInfo.path);
tauriConf.bundle.resources = [iconInfo.path];
await fsExtra.copy(options.icon, iconPath);
}
if (updateIconPath) {
tauriConf.bundle.icon = [options.icon];
}
else {
logger.warn(`✼ Icon will remain as default.`);
}
}
else {
logger.warn('✼ Custom icon path may be invalid, default icon will be used instead.');
tauriConf.bundle.icon = [iconInfo.defaultIcon];
}
// Set tray icon path.
let trayIconPath = platform === 'darwin' ? 'png/icon_512.png' : tauriConf.bundle.icon[0];
if (systemTrayIcon.length > 0) {
try {
await fsExtra.pathExists(systemTrayIcon);
// 需要判断图标格式,默认只支持ico和png两种
let iconExt = path.extname(systemTrayIcon).toLowerCase();
if (iconExt == '.png' || iconExt == '.ico') {
const trayIcoPath = path.join(npmDirectory, `src-tauri/png/${name.toLowerCase()}${iconExt}`);
trayIconPath = `png/${name.toLowerCase()}${iconExt}`;
await fsExtra.copy(systemTrayIcon, trayIcoPath);
}
else {
logger.warn(`✼ System tray icon must be .ico or .png, but you provided ${iconExt}.`);
logger.warn(`✼ Default system tray icon will be used.`);
}
}
catch {
logger.warn(`✼ ${systemTrayIcon} not exists!`);
logger.warn(`✼ Default system tray icon will remain unchanged.`);
}
}
tauriConf.app.trayIcon.iconPath = trayIconPath;
tauriConf.pake.system_tray_path = trayIconPath;
delete tauriConf.app.trayIcon;
const injectFilePath = path.join(npmDirectory, `src-tauri/src/inject/custom.js`);
// inject js or css files
if (inject?.length > 0) {
if (!inject.every(item => item.endsWith('.css') || item.endsWith('.js'))) {
logger.error('The injected file must be in either CSS or JS format.');
return;
}
const files = inject.map(filepath => (path.isAbsolute(filepath) ? filepath : path.join(process.cwd(), filepath)));
tauriConf.pake.inject = files;
await combineFiles(files, injectFilePath);
}
else {
tauriConf.pake.inject = [];
await fsExtra.writeFile(injectFilePath, '');
}
tauriConf.pake.proxy_url = proxyUrl || '';
// Save config file.
const platformConfigPaths = {
win32: 'tauri.windows.conf.json',
darwin: 'tauri.macos.conf.json',
linux: 'tauri.linux.conf.json',
};
const configPath = path.join(tauriConfigDirectory, platformConfigPaths[platform]);
const bundleConf = { bundle: tauriConf.bundle };
console.log('pakeConfig', tauriConf.pake);
await fsExtra.outputJSON(configPath, bundleConf, { spaces: 4 });
const pakeConfigPath = path.join(tauriConfigDirectory, 'pake.json');
await fsExtra.outputJSON(pakeConfigPath, tauriConf.pake, { spaces: 4 });
let tauriConf2 = JSON.parse(JSON.stringify(tauriConf));
delete tauriConf2.pake;
const configJsonPath = path.join(tauriConfigDirectory, 'tauri.conf.json');
await fsExtra.outputJSON(configJsonPath, tauriConf2, { spaces: 4 });
}
class BaseBuilder {
constructor(options) {
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);
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}" && npm install --registry=https://registry.npmmirror.com`);
}
else {
await shellExec(`cd "${npmDirectory}" && npm install`);
}
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) {
await this.buildAndCopy(url, this.options.targets);
}
async start(url) {
await mergeConfig(url, this.options, tauriConfig);
}
async buildAndCopy(url, target) {
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);
}
getFileType(target) {
return target;
}
getBuildCommand() {
// the debug option should support `--debug` and `--release`
return this.options.debug ? 'npm run build:debug' : 'npm run build';
}
getBasePath() {
const basePath = this.options.debug ? 'debug' : 'release';
return `src-tauri/target/${basePath}/bundle/`;
}
getBuildAppPath(npmDirectory, fileName, fileType) {
return path.join(npmDirectory, this.getBasePath(), fileType.toLowerCase(), `${fileName}.${fileType}`);
}
}
class MacBuilder extends BaseBuilder {
constructor(options) {
super(options);
this.options.targets = 'dmg';
}
getFileName() {
const { name } = this.options;
let arch;
if (this.options.multiArch) {
arch = 'universal';
}
else {
arch = process.arch === 'arm64' ? 'aarch64' : process.arch;
}
return `${name}_${tauriConfig.version}_${arch}`;
}
getBuildCommand() {
return this.options.multiArch ? 'npm run build:mac' : super.getBuildCommand();
}
getBasePath() {
return this.options.multiArch
? 'src-tauri/target/universal-apple-darwin/release/bundle'
: super.getBasePath();
}
}
class WinBuilder extends BaseBuilder {
constructor(options) {
super(options);
this.options.targets = 'msi';
}
getFileName() {
const { name } = this.options;
const { arch } = process;
const language = tauriConfig.bundle.windows.wix.language[0];
return `${name}_${tauriConfig.version}_${arch}_${language}`;
}
}
class LinuxBuilder extends BaseBuilder {
constructor(options) {
super(options);
}
getFileName() {
const { name } = this.options;
const arch = process.arch === 'x64' ? 'amd64' : process.arch;
return `${name}_${tauriConfig.version}_${arch}`;
}
// Customize it, considering that there are all targets.
async build(url) {
const targetTypes = ['deb', 'appimage'];
for (const target of targetTypes) {
if (this.options.targets === target || this.options.targets === 'all') {
await this.buildAndCopy(url, target);
}
}
}
getFileType(target) {
if (target === 'appimage') {
return 'AppImage';
}
return super.getFileType(target);
}
}
const { platform } = process;
const buildersMap = {
darwin: MacBuilder,
win32: WinBuilder,
linux: LinuxBuilder,
};
class BuilderProvider {
static create(options) {
const Builder = buildersMap[platform];
if (!Builder) {
throw new Error('The current system is not supported!');
}
return new Builder(options);
}
}
async function startBuild() {
log.setDefaultLevel('debug');
const appOptions = await handleOptions(DEFAULT_DEV_PAKE_OPTIONS, DEFAULT_DEV_PAKE_OPTIONS.url);
log.debug('PakeAppOptions', appOptions);
const builder = BuilderProvider.create(appOptions);
await builder.prepare();
await builder.start(DEFAULT_DEV_PAKE_OPTIONS.url);
}
startBuild();
//# sourceMappingURL=dev.js.map