From a597af490d57d099370f19d91ca85c0446948090 Mon Sep 17 00:00:00 2001 From: jeasonnow Date: Fri, 14 Jul 2023 14:51:25 +0800 Subject: [PATCH] update: 2.1.12-beta.0 cli --- dist/.gitkeep | 0 dist/about_pake.html | 16 - dist/cli.js | 1357 ++++++++++++++++--------------- src-tauri/pake.json | 9 +- src-tauri/tauri.conf.json | 11 +- src-tauri/tauri.macos.conf.json | 46 +- 6 files changed, 736 insertions(+), 703 deletions(-) delete mode 100644 dist/.gitkeep delete mode 100644 dist/about_pake.html diff --git a/dist/.gitkeep b/dist/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/dist/about_pake.html b/dist/about_pake.html deleted file mode 100644 index 83cb1d5..0000000 --- a/dist/about_pake.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - Document - - -
Welcome from Pake!
-

version: 1.0.9

- Project link
- Discussions
- Issues
-

LICENSE: MIT

- - \ No newline at end of file diff --git a/dist/cli.js b/dist/cli.js index 149c43a..d7421d6 100644 --- a/dist/cli.js +++ b/dist/cli.js @@ -11,16 +11,16 @@ import { fileURLToPath } from 'url'; import dns from 'dns'; import http from 'http'; import { promisify } from 'util'; +import fs from 'fs'; import updateNotifier from 'update-notifier'; import axios from 'axios'; import { dir } from 'tmp-promise'; import { fileTypeFromBuffer } from 'file-type'; import psl from 'psl'; import isUrl from 'is-url'; -import fs from 'fs'; var name = "pake-cli"; -var version = "2.1.12"; +var version = "2.1.12-beta.0"; var description = "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。"; var engines = { node: ">=16.0.0" @@ -53,12 +53,14 @@ var scripts = { start: "npm run dev", dev: "npm run tauri dev", build: "npm run tauri build --release", + "build:debug": "npm run tauri build -- --debug", "build:mac": "npm run tauri build -- --target universal-apple-darwin", "build:all-unix": "chmod +x ./script/build.sh && ./script/build.sh", "build:all-windows": "pwsh ./script/build.ps1", analyze: "cd src-tauri && cargo bloat --release --crates", tauri: "tauri", cli: "rollup -c rollup.config.js --watch", + "cli:dev": "cross-env DEV=true tsx watch ./bin/dev.ts & npm run dev", "cli:build": "cross-env NODE_ENV=production rollup -c rollup.config.js", prepublishOnly: "npm run cli:build" }; @@ -123,7 +125,7 @@ var packageJson = { var windows = [ { url: "https://weread.qq.com/", - transparent: true, + transparent: false, fullscreen: false, width: 1200, height: 780, @@ -137,7 +139,7 @@ var user_agent = { 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 menu = { - macos: true, + macos: false, linux: false, windows: false }; @@ -146,11 +148,14 @@ var system_tray = { linux: true, windows: true }; +var inject = [ +]; var pakeConf = { windows: windows, user_agent: user_agent, menu: menu, - system_tray: system_tray + system_tray: system_tray, + inject: inject }; var tauri$3 = { @@ -170,7 +175,7 @@ var tauri$3 = { active: false }, systemTray: { - iconPath: "png/weread_512.png", + iconPath: "png/icon_512.png", iconAsTemplate: true }, allowlist: { @@ -191,11 +196,11 @@ var build = { beforeDevCommand: "" }; var CommonConf = { + tauri: tauri$3, "package": { productName: "WeRead", version: "1.0.0" }, - tauri: tauri$3, build: build }; @@ -239,9 +244,9 @@ var WinConf = { var tauri$1 = { bundle: { icon: [ - "icons/weread.icns" + "icons/icon.icns" ], - identifier: "com.pake.weread", + identifier: "com.pake.5b8ae9", active: true, category: "DeveloperTool", copyright: "", @@ -302,690 +307,740 @@ var LinuxConf = { tauri: tauri }; -const platformConfigs = { - win32: WinConf, - darwin: MacConf, - linux: LinuxConf, -}; -const { platform: platform$2 } = process; -// @ts-ignore -const platformConfig = platformConfigs[platform$2]; -let tauriConfig = { - tauri: { - ...CommonConf.tauri, - bundle: platformConfig.tauri.bundle, - }, - package: CommonConf.package, - build: CommonConf.build, - pake: pakeConf, +const platformConfigs = { + win32: WinConf, + darwin: MacConf, + linux: LinuxConf, +}; +const { platform: platform$2 } = process; +// @ts-ignore +const platformConfig = platformConfigs[platform$2]; +let tauriConfig = { + tauri: { + ...CommonConf.tauri, + bundle: platformConfig.tauri.bundle, + }, + package: CommonConf.package, + build: CommonConf.build, + pake: pakeConf, }; -// Generates an identifier based on the given URL. -function getIdentifier(url) { - const postFixHash = crypto.createHash('md5').update(url).digest('hex').substring(0, 6); - return `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(); +// 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(); } -const { platform: platform$1 } = process; -const IS_MAC = platform$1 === 'darwin'; -const IS_WIN = platform$1 === 'win32'; +const { platform: platform$1 } = process; +const IS_MAC = platform$1 === 'darwin'; +const IS_WIN = platform$1 === 'win32'; const IS_LINUX = platform$1 === 'linux'; -// 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), '..'); +// 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 = process.env.DEV === 'true' ? process.cwd() : path.join(path.dirname(currentModulePath), '..'); -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(`${code}`)); - } - }); - }); +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(`${code}`)); + } + }); + }); } -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))); - }, +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))); + }, }; -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; - } +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 isInChina = await isChinaDomain('sh.rustup.rs'); - const rustInstallScriptForMac = isInChina - ? '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 installRust() { + const isInChina = await isChinaDomain('sh.rustup.rs'); + const rustInstallScriptForMac = isInChina + ? '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 mergeConfig(url, options, tauriConf) { - const { width, height, fullscreen, transparent, userAgent, showMenu, showSystemTray, systemTrayIcon, iterCopyFile, identifier, name, resizable = true, } = options; - const { platform } = process; - // Set Windows parameters. - const tauriConfWindowOptions = { - width, - height, - fullscreen, - transparent, - resizable, - }; - Object.assign(tauriConf.pake.windows[0], { url, ...tauriConfWindowOptions }); - tauriConf.package.productName = name; - tauriConf.tauri.bundle.identifier = identifier; - //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 (!iterCopyFile) { - const urlPath = path.join(distDir, fileName); - await fsExtra.copy(url, urlPath); - } - else { - fsExtra.moveSync(distDir, distBakDir, { overwrite: true }); - fsExtra.copySync(dirName, distDir, { overwrite: true }); - const filesToCopyBack = ['cli.js', 'about_pake.html']; - 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'; - // Set the secure domain for calling window.__TAURI__ to the application domain that has been set. - tauriConf.tauri.security.dangerousRemoteDomainIpcAccess[0].domain = new URL(url).hostname; - } - const platformMap = { - win32: 'windows', - linux: 'linux', - darwin: 'macos', - }; - const currentPlatform = platformMap[platform]; - if (userAgent.length > 0) { - tauriConf.pake.user_agent[currentPlatform] = userAgent; - } - tauriConf.pake.menu[currentPlatform] = showMenu; - tauriConf.pake.system_tray[currentPlatform] = showSystemTray; - // Processing targets are currently only open to Linux. - if (platform === 'linux') { - delete tauriConf.tauri.bundle.deb.files; - const validTargets = ['all', 'deb', 'appimage']; - if (validTargets.includes(options.targets)) { - tauriConf.tauri.bundle.targets = - options.targets === 'all' ? ['deb', 'appimage'] : [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.tauri.bundle.icon = [iconInfo.defaultIcon]; - } - else { - const iconPath = path.join(npmDirectory, 'src-tauri/', iconInfo.path); - tauriConf.tauri.bundle.resources = [iconInfo.path]; - await fsExtra.copy(options.icon, iconPath); - } - if (updateIconPath) { - tauriConf.tauri.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.tauri.bundle.icon = [iconInfo.defaultIcon]; - } - // Set tray icon path. - let trayIconPath = platform === 'darwin' ? 'png/icon_512.png' : tauriConf.tauri.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.tauri.systemTray.iconPath = trayIconPath; - // Save config file. - const platformConfigPaths = { - win32: 'src-tauri/tauri.windows.conf.json', - darwin: 'src-tauri/tauri.macos.conf.json', - linux: 'src-tauri/tauri.linux.conf.json', - }; - const configPath = path.join(npmDirectory, platformConfigPaths[platform]); - const bundleConf = { tauri: { bundle: tauriConf.tauri.bundle } }; - await fsExtra.writeJson(configPath, bundleConf, { spaces: 4 }); - const pakeConfigPath = path.join(npmDirectory, 'src-tauri/pake.json'); - await fsExtra.writeJson(pakeConfigPath, tauriConf.pake, { spaces: 4 }); - let tauriConf2 = JSON.parse(JSON.stringify(tauriConf)); - delete tauriConf2.pake; - delete tauriConf2.tauri.bundle; - const configJsonPath = path.join(npmDirectory, 'src-tauri/tauri.conf.json'); - await fsExtra.writeJson(configJsonPath, tauriConf2, { spaces: 4 }); +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; } -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/v1/guides/getting-started/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'); - 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 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() { - return 'npm run build'; - } - getBasePath() { - return 'src-tauri/target/release/bundle/'; - } - getBuildAppPath(npmDirectory, fileName, fileType) { - return path.join(npmDirectory, this.getBasePath(), fileType.toLowerCase(), `${fileName}.${fileType}`); - } +async function mergeConfig(url, options, tauriConf) { + const { width, height, fullscreen, transparent, userAgent, showMenu, showSystemTray, systemTrayIcon, iterCopyFile, identifier, name, resizable = true, inject, safeDomain, } = options; + const { platform } = process; + // Set Windows parameters. + const tauriConfWindowOptions = { + width, + height, + fullscreen, + transparent, + resizable, + }; + Object.assign(tauriConf.pake.windows[0], { url, ...tauriConfWindowOptions }); + tauriConf.package.productName = name; + tauriConf.tauri.bundle.identifier = identifier; + //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 (!iterCopyFile) { + const urlPath = path.join(distDir, fileName); + await fsExtra.copy(url, urlPath); + } + else { + fsExtra.moveSync(distDir, distBakDir, { overwrite: true }); + fsExtra.copySync(dirName, distDir, { overwrite: true }); + const filesToCopyBack = ['cli.js', 'about_pake.html']; + 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'; + // Set the secure domain for calling window.__TAURI__ to the application domain that has been set. + tauriConf.tauri.security.dangerousRemoteDomainIpcAccess = [ + { + domain: new URL(url).hostname, + windows: ['pake'], + enableTauriAPI: true, + }, + ]; + } + if (safeDomain.length > 0) { + tauriConf.tauri.security.dangerousRemoteDomainIpcAccess = [ + ...tauriConf.tauri.security.dangerousRemoteDomainIpcAccess, + ...safeDomain.map(domain => ({ + domain, + windows: ['pake'], + enableTauriAPI: true, + })), + ]; + } + const platformMap = { + win32: 'windows', + linux: 'linux', + darwin: 'macos', + }; + const currentPlatform = platformMap[platform]; + if (userAgent.length > 0) { + tauriConf.pake.user_agent[currentPlatform] = userAgent; + } + tauriConf.pake.menu[currentPlatform] = showMenu; + tauriConf.pake.system_tray[currentPlatform] = showSystemTray; + // Processing targets are currently only open to Linux. + if (platform === 'linux') { + delete tauriConf.tauri.bundle.deb.files; + const validTargets = ['all', 'deb', 'appimage']; + if (validTargets.includes(options.targets)) { + tauriConf.tauri.bundle.targets = options.targets === 'all' ? ['deb', 'appimage'] : [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.tauri.bundle.icon = [iconInfo.defaultIcon]; + } + else { + const iconPath = path.join(npmDirectory, 'src-tauri/', iconInfo.path); + tauriConf.tauri.bundle.resources = [iconInfo.path]; + await fsExtra.copy(options.icon, iconPath); + } + if (updateIconPath) { + tauriConf.tauri.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.tauri.bundle.icon = [iconInfo.defaultIcon]; + } + // Set tray icon path. + let trayIconPath = platform === 'darwin' ? 'png/icon_512.png' : tauriConf.tauri.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.tauri.systemTray.iconPath = trayIconPath; + const injectFilePath = path.join(npmDirectory, `src-tauri/src/inject/_INJECT_.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, ''); + } + // Save config file. + const platformConfigPaths = { + win32: 'src-tauri/tauri.windows.conf.json', + darwin: 'src-tauri/tauri.macos.conf.json', + linux: 'src-tauri/tauri.linux.conf.json', + }; + const configPath = path.join(npmDirectory, platformConfigPaths[platform]); + const bundleConf = { tauri: { bundle: tauriConf.tauri.bundle } }; + await fsExtra.writeJson(configPath, bundleConf, { spaces: 4 }); + const pakeConfigPath = path.join(npmDirectory, 'src-tauri/pake.json'); + await fsExtra.writeJson(pakeConfigPath, tauriConf.pake, { spaces: 4 }); + let tauriConf2 = JSON.parse(JSON.stringify(tauriConf)); + delete tauriConf2.pake; + delete tauriConf2.tauri.bundle; + const configJsonPath = path.join(npmDirectory, 'src-tauri/tauri.conf.json'); + await fsExtra.writeJson(configJsonPath, tauriConf2, { spaces: 4 }); } -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.package.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 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/v1/guides/getting-started/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'); + 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() { + return 'src-tauri/target/release/bundle/'; + } + getBuildAppPath(npmDirectory, fileName, fileType) { + return path.join(npmDirectory, this.getBasePath(), fileType.toLowerCase(), `${fileName}.${fileType}`); + } } -class WinBuilder extends BaseBuilder { - constructor(options) { - super(options); - this.options.targets = 'msi'; - } - getFileName() { - const { name } = this.options; - const { arch } = process; - const language = tauriConfig.tauri.bundle.windows.wix.language[0]; - return `${name}_${tauriConfig.package.version}_${arch}_${language}`; - } +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.package.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 LinuxBuilder extends BaseBuilder { - constructor(options) { - super(options); - } - getFileName() { - const { name } = this.options; - const arch = process.arch === 'x64' ? 'amd64' : process.arch; - return `${name}_${tauriConfig.package.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); - } +class WinBuilder extends BaseBuilder { + constructor(options) { + super(options); + this.options.targets = 'msi'; + } + getFileName() { + const { name } = this.options; + const { arch } = process; + const language = tauriConfig.tauri.bundle.windows.wix.language[0]; + return `${name}_${tauriConfig.package.version}_${arch}_${language}`; + } } -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); - } +class LinuxBuilder extends BaseBuilder { + constructor(options) { + super(options); + } + getFileName() { + const { name } = this.options; + const arch = process.arch === 'x64' ? 'amd64' : process.arch; + return `${name}_${tauriConfig.package.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 DEFAULT_PAKE_OPTIONS = { - icon: '', - height: 780, - width: 1200, - fullscreen: false, - resizable: true, - transparent: false, - userAgent: '', - showMenu: false, - showSystemTray: false, - multiArch: false, - targets: 'deb', - iterCopyFile: false, - systemTrayIcon: '', - debug: false, +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); + } +} + +const DEFAULT_PAKE_OPTIONS = { + icon: '', + height: 780, + width: 1200, + fullscreen: false, + resizable: true, + transparent: false, + userAgent: '', + showMenu: false, + showSystemTray: false, + multiArch: false, + targets: 'deb', + iterCopyFile: false, + systemTrayIcon: '', + debug: false, + inject: [], + safeDomain: [], }; -async function checkUpdateTips() { - updateNotifier({ pkg: packageJson, updateCheckInterval: 1000 * 60 }).notify({ isGlobal: true }); +async function checkUpdateTips() { + updateNotifier({ pkg: packageJson, updateCheckInterval: 1000 * 60 }).notify({ isGlobal: true }); } -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(); - const iconPath = `${tempPath}/icon.${fileDetails.ext}`; - 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; - } +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(); + const iconPath = `${tempPath}/icon.${fileDetails.ext}`; + 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; - } -} -// Appends 'https://' protocol to the URL if not present. -function appendProtocol(inputUrl) { - try { - new URL(inputUrl); - return inputUrl; - } - catch { - return `https://${inputUrl}`; - } -} -// Normalizes the URL by ensuring it has a protocol and is valid. -function normalizeUrl(urlToNormalize) { - const urlWithProtocol = appendProtocol(urlToNormalize); - if (isUrl(urlWithProtocol)) { - return urlWithProtocol; - } - else { - throw new Error(`Your url "${urlWithProtocol}" is invalid`); - } +// 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; + } +} +// Appends 'https://' protocol to the URL if not present. +function appendProtocol(inputUrl) { + try { + new URL(inputUrl); + return inputUrl; + } + catch { + return `https://${inputUrl}`; + } +} +// Normalizes the URL by ensuring it has a protocol and is valid. +function normalizeUrl(urlToNormalize) { + const urlWithProtocol = appendProtocol(urlToNormalize); + if (isUrl(urlWithProtocol)) { + return urlWithProtocol; + } + else { + throw new Error(`Your url "${urlWithProtocol}" is invalid`); + } } -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]+$/, - }; - 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 must contain at least one letter. Examples: 123pan, 123Pan, Pan123, weread, WeRead, WERead.`; - 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; +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]+$/, + }; + 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 must contain at least one letter. Examples: 123pan, 123Pan, Pan123, weread, WeRead, WERead.`; + 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; } -function validateNumberInput(value) { - const parsedValue = Number(value); - if (isNaN(parsedValue)) { - throw new InvalidArgumentError('Not a number.'); - } - return parsedValue; -} -function validateUrlInput(url) { - const isFile = fs.existsSync(url); - if (!isFile) { - try { - return normalizeUrl(url); - } - catch (error) { - throw new InvalidArgumentError(error.message); - } - } - return url; +function validateNumberInput(value) { + const parsedValue = Number(value); + if (isNaN(parsedValue)) { + throw new InvalidArgumentError('Not a number.'); + } + return parsedValue; +} +function validateUrlInput(url) { + const isFile = fs.existsSync(url); + if (!isFile) { + try { + return normalizeUrl(url); + } + catch (error) { + throw new InvalidArgumentError(error.message); + } + } + return url; } -const { green, yellow } = chalk; +const { green, yellow } = chalk; const logo = `${chalk.green(' ____ _')} ${green('| _ \\ __ _| | _____')} ${green('| |_) / _` | |/ / _ \\')} ${green('| __/ (_| | < __/')} ${yellow('https://github.com/tw93/pake')} ${green('|_| \\__,_|_|\\_\\___| can turn any webpage into a desktop app with Rust.')} -`; -program - .addHelpText('beforeAll', logo) - .usage(`[url] [options]`) - .showHelpAfterError(); -program - .argument('[url]', 'The web URL you want to package', validateUrlInput) - .option('--name ', 'Application name') - .option('--icon ', 'Application icon', DEFAULT_PAKE_OPTIONS.icon) - .option('--width ', 'Window width', validateNumberInput, DEFAULT_PAKE_OPTIONS.width) - .option('--height ', 'Window height', validateNumberInput, DEFAULT_PAKE_OPTIONS.height) - .option('--transparent', 'Only for Mac, hide title bar', DEFAULT_PAKE_OPTIONS.transparent) - .option('--fullscreen', 'Start in full screen', DEFAULT_PAKE_OPTIONS.fullscreen) - .option('--user-agent ', 'Custom user agent', DEFAULT_PAKE_OPTIONS.userAgent) - .option('--show-menu', 'Show menu in app', DEFAULT_PAKE_OPTIONS.showMenu) - .option('--show-system-tray', 'Show system tray in app', DEFAULT_PAKE_OPTIONS.showSystemTray) - .option('--system-tray-icon ', 'Custom system tray icon', DEFAULT_PAKE_OPTIONS.systemTrayIcon) - .option('--iter-copy-file', 'Copy files when URL is a local file', DEFAULT_PAKE_OPTIONS.iterCopyFile) - .option('--multi-arch', 'Only for Mac, supports both Intel and M1', DEFAULT_PAKE_OPTIONS.multiArch) - .option('--targets ', 'Only for Linux, option "deb" or "appimage"', DEFAULT_PAKE_OPTIONS.targets) - .option('--debug', 'Debug mode', DEFAULT_PAKE_OPTIONS.debug) - .version(packageJson.version, '-v, --version', 'Output the current version') - .action(async (url, options) => { - await checkUpdateTips(); - if (!url) { - program.outputHelp(str => { - return str - .split('\n') - .filter(line => !/((-h,|--help)|((-v|-V),|--version))\s+.+$/.test(line)) - .join('\n'); - }); - process.exit(0); - } - log.setDefaultLevel('info'); - if (options.debug) { - log.setLevel('debug'); - } - const appOptions = await handleOptions(options, url); - log.debug('PakeAppOptions', appOptions); - const builder = BuilderProvider.create(appOptions); - await builder.prepare(); - await builder.build(url); -}); +`; +program + .addHelpText('beforeAll', logo) + .usage(`[url] [options]`) + .showHelpAfterError(); +program + .argument('[url]', 'The web URL you want to package', validateUrlInput) + .option('--name ', 'Application name') + .option('--icon ', 'Application icon', DEFAULT_PAKE_OPTIONS.icon) + .option('--width ', 'Window width', validateNumberInput, DEFAULT_PAKE_OPTIONS.width) + .option('--height ', 'Window height', validateNumberInput, DEFAULT_PAKE_OPTIONS.height) + .option('--transparent', 'Only for Mac, hide title bar', DEFAULT_PAKE_OPTIONS.transparent) + .option('--fullscreen', 'Start in full screen', DEFAULT_PAKE_OPTIONS.fullscreen) + .option('--user-agent ', 'Custom user agent', DEFAULT_PAKE_OPTIONS.userAgent) + .option('--show-menu', 'Show menu in app', DEFAULT_PAKE_OPTIONS.showMenu) + .option('--show-system-tray', 'Show system tray in app', DEFAULT_PAKE_OPTIONS.showSystemTray) + .option('--system-tray-icon ', 'Custom system tray icon', DEFAULT_PAKE_OPTIONS.systemTrayIcon) + .option('--iter-copy-file', 'Copy files when URL is a local file', DEFAULT_PAKE_OPTIONS.iterCopyFile) + .option('--multi-arch', 'Only for Mac, supports both Intel and M1', DEFAULT_PAKE_OPTIONS.multiArch) + .option('--targets ', 'Only for Linux, option "deb" or "appimage"', DEFAULT_PAKE_OPTIONS.targets) + .option('--inject [injects...]', 'inject .js or .css for this app', DEFAULT_PAKE_OPTIONS.inject) + .option('--safe-domain [domains...]', 'domains that can call window.__TAURI__ and use ipc', DEFAULT_PAKE_OPTIONS.safeDomain) + .option('--debug', 'Debug mode', DEFAULT_PAKE_OPTIONS.debug) + .version(packageJson.version, '-v, --version', 'Output the current version') + .action(async (url, options) => { + await checkUpdateTips(); + if (!url) { + program.outputHelp(str => { + return str + .split('\n') + .filter(line => !/((-h,|--help)|((-v|-V),|--version))\s+.+$/.test(line)) + .join('\n'); + }); + process.exit(0); + } + log.setDefaultLevel('info'); + if (options.debug) { + log.setLevel('debug'); + } + const appOptions = await handleOptions(options, url); + log.debug('PakeAppOptions', appOptions); + const builder = BuilderProvider.create(appOptions); + await builder.prepare(); + await builder.build(url); +}); program.parse(); diff --git a/src-tauri/pake.json b/src-tauri/pake.json index 41df6ca..96dd6fb 100644 --- a/src-tauri/pake.json +++ b/src-tauri/pake.json @@ -1,8 +1,8 @@ { "windows": [ { - "url": "https://zbook.lol/", - "transparent": true, + "url": "https://weread.qq.com/", + "transparent": false, "fullscreen": false, "width": 1200, "height": 780, @@ -25,8 +25,5 @@ "linux": true, "windows": true }, - "inject": [ - "/Users/guyifeng/workspace/seeyon/pake-patch/index.js", - "/Users/guyifeng/workspace/seeyon/pake-patch/index.css" - ] + "inject": [] } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index b3aba59..c4df1a4 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -4,14 +4,7 @@ "csp": null, "dangerousRemoteDomainIpcAccess": [ { - "domain": "zbook.lol", - "windows": [ - "pake" - ], - "enableTauriAPI": true - }, - { - "domain": "zbook.eu.org", + "domain": "weread.qq.com", "windows": [ "pake" ], @@ -37,7 +30,7 @@ } }, "package": { - "productName": "zBook", + "productName": "WeRead", "version": "1.0.0" }, "build": { diff --git a/src-tauri/tauri.macos.conf.json b/src-tauri/tauri.macos.conf.json index 8294815..76cfbdd 100644 --- a/src-tauri/tauri.macos.conf.json +++ b/src-tauri/tauri.macos.conf.json @@ -1,23 +1,27 @@ { - "tauri": { - "bundle": { - "icon": ["icons/weread.icns"], - "identifier": "com.pake.weread", - "active": true, - "category": "DeveloperTool", - "copyright": "", - "externalBin": [], - "longDescription": "", - "macOS": { - "entitlements": null, - "exceptionDomain": "", - "frameworks": [], - "providerShortName": null, - "signingIdentity": null - }, - "resources": [], - "shortDescription": "", - "targets": ["dmg"] + "tauri": { + "bundle": { + "icon": [ + "icons/icon.icns" + ], + "identifier": "com.pake.5b8ae9", + "active": true, + "category": "DeveloperTool", + "copyright": "", + "externalBin": [], + "longDescription": "", + "macOS": { + "entitlements": null, + "exceptionDomain": "", + "frameworks": [], + "providerShortName": null, + "signingIdentity": null + }, + "resources": [], + "shortDescription": "", + "targets": [ + "dmg" + ] + } } - } -} \ No newline at end of file +}