From dbfacc8d5960d4136b769ad96df2d39f63d643a2 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Thu, 15 Jun 2023 21:16:10 +0800 Subject: [PATCH 01/40] :art: Update tauri and compatible chatgpt --- package.json | 4 +- src-tauri/Cargo.lock | 272 ++++++++++++++++++++++++++++------ src-tauri/Cargo.toml | 4 +- src-tauri/src/inject/event.js | 6 +- src-tauri/src/inject/style.js | 7 +- src-tauri/tauri.conf.json | 76 +++++----- 6 files changed, 274 insertions(+), 95 deletions(-) diff --git a/package.json b/package.json index 4a133bd..65dcf2a 100644 --- a/package.json +++ b/package.json @@ -46,8 +46,8 @@ "exports": "./dist/pake.js", "license": "MIT", "dependencies": { - "@tauri-apps/api": "^1.3.0", - "@tauri-apps/cli": "^1.3.1", + "@tauri-apps/api": "^1.4.0", + "@tauri-apps/cli": "^1.4.0", "axios": "^1.1.3", "chalk": "^5.1.2", "commander": "^9.4.1", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index fc766e4..434b3c2 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -227,22 +227,6 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" -[[package]] -name = "attohttpc" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fcf00bc6d5abb29b5f97e3c61a90b6d3caa12f3faf897d4a3e3607c050a35a7" -dependencies = [ - "flate2", - "http", - "log", - "native-tls", - "serde", - "serde_json", - "serde_urlencoded", - "url", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -378,6 +362,9 @@ name = "bytes" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +dependencies = [ + "serde", +] [[package]] name = "cairo-rs" @@ -805,7 +792,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c47d9680cf49ce700aba2ea4991f745fbe06b54b2262ea6bf8e1798f436b2bad" dependencies = [ "indicatif", - "reqwest", + "reqwest 0.10.10", "tokio 0.2.25", ] @@ -1426,11 +1413,30 @@ dependencies = [ "indexmap", "slab", "tokio 0.2.25", - "tokio-util", + "tokio-util 0.3.1", "tracing", "tracing-futures", ] +[[package]] +name = "h2" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" +dependencies = [ + "bytes 1.4.0", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio 1.28.2", + "tokio-util 0.7.8", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1508,6 +1514,17 @@ dependencies = [ "http", ] +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes 1.4.0", + "http", + "pin-project-lite 0.2.9", +] + [[package]] name = "http-range" version = "0.1.5" @@ -1526,6 +1543,12 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + [[package]] name = "hyper" version = "0.13.10" @@ -1536,11 +1559,11 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.2.7", "http", - "http-body", + "http-body 0.3.1", "httparse", - "httpdate", + "httpdate 0.3.2", "itoa 0.4.8", "pin-project", "socket2 0.3.19", @@ -1550,6 +1573,30 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "0.14.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +dependencies = [ + "bytes 1.4.0", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.19", + "http", + "http-body 0.4.5", + "httparse", + "httpdate 1.0.2", + "itoa 1.0.6", + "pin-project-lite 0.2.9", + "socket2 0.4.9", + "tokio 1.28.2", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper-tls" version = "0.4.3" @@ -1557,12 +1604,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d979acc56dcb5b8dddba3917601745e877576475aa046df3226eabdecef78eed" dependencies = [ "bytes 0.5.6", - "hyper", + "hyper 0.13.10", "native-tls", "tokio 0.2.25", "tokio-tls", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes 1.4.0", + "hyper 0.14.26", + "native-tls", + "tokio 1.28.2", + "tokio-native-tls", +] + [[package]] name = "iana-time-zone" version = "0.1.56" @@ -2041,6 +2101,17 @@ dependencies = [ "winapi 0.2.8", ] +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + [[package]] name = "mio-named-pipes" version = "0.1.7" @@ -2048,7 +2119,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656" dependencies = [ "log", - "mio", + "mio 0.6.23", "miow 0.3.7", "winapi 0.3.9", ] @@ -2061,7 +2132,7 @@ checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" dependencies = [ "iovec", "libc", - "mio", + "mio 0.6.23", ] [[package]] @@ -2927,9 +2998,9 @@ dependencies = [ "futures-core", "futures-util", "http", - "http-body", - "hyper", - "hyper-tls", + "http-body 0.3.1", + "hyper 0.13.10", + "hyper-tls 0.4.3", "ipnet", "js-sys", "lazy_static", @@ -2950,6 +3021,45 @@ dependencies = [ "winreg 0.7.0", ] +[[package]] +name = "reqwest" +version = "0.11.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" +dependencies = [ + "base64 0.21.2", + "bytes 1.4.0", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.3.19", + "http", + "http-body 0.4.5", + "hyper 0.14.26", + "hyper-tls 0.5.0", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite 0.2.9", + "serde", + "serde_json", + "serde_urlencoded", + "tokio 1.28.2", + "tokio-native-tls", + "tokio-util 0.7.8", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "winreg 0.10.1", +] + [[package]] name = "rfd" version = "0.10.0" @@ -3168,11 +3278,11 @@ dependencies = [ [[package]] name = "serde_with" -version = "2.3.3" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe" +checksum = "9f02d8aa6e3c385bf084924f660ce2a3a6bd333ba55b35e8590b321f35d88513" dependencies = [ - "base64 0.13.1", + "base64 0.21.2", "chrono", "hex", "indexmap", @@ -3184,9 +3294,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "2.3.3" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" +checksum = "edc7d5d3932fb12ce722ee5e64dd38c504efba37567f0c402f6ca728c3b8b070" dependencies = [ "darling", "proc-macro2", @@ -3458,6 +3568,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sys-locale" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8a11bd9c338fdba09f7881ab41551932ad42e405f61d01e8406baea71c07aee" +dependencies = [ + "js-sys", + "libc", + "wasm-bindgen", + "web-sys", + "windows-sys 0.45.0", +] + [[package]] name = "system-deps" version = "5.0.0" @@ -3563,12 +3686,12 @@ checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5" [[package]] name = "tauri" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d42ba3a2e8556722f31336a0750c10dbb6a81396a1c452977f515da83f69f842" +checksum = "cc35893c7e08d9564a9206bd52182dce031b0d5132dc946b3e166e00d03f8cfe" dependencies = [ "anyhow", - "attohttpc", + "bytes 1.4.0", "cocoa", "dirs-next", "embed_plist", @@ -3591,6 +3714,7 @@ dependencies = [ "rand 0.8.5", "raw-window-handle", "regex", + "reqwest 0.11.18", "rfd", "semver", "serde", @@ -3599,6 +3723,7 @@ dependencies = [ "serialize-to-javascript", "shared_child", "state", + "sys-locale", "tar", "tauri-macros", "tauri-runtime", @@ -3616,9 +3741,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "929b3bd1248afc07b63e33a6a53c3f82c32d0b0a5e216e4530e94c467e019389" +checksum = "7d2edd6a259b5591c8efdeb9d5702cb53515b82a6affebd55c7fd6d3a27b7d1b" dependencies = [ "anyhow", "cargo_toml", @@ -3629,14 +3754,13 @@ dependencies = [ "serde_json", "tauri-utils", "tauri-winres", - "winnow", ] [[package]] name = "tauri-codegen" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a2105f807c6f50b2fa2ce5abd62ef207bc6f14c9fcc6b8caec437f6fb13bde" +checksum = "54ad2d49fdeab4a08717f5b49a163bdc72efc3b1950b6758245fcde79b645e1a" dependencies = [ "base64 0.21.2", "brotli", @@ -3660,9 +3784,9 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8784cfe6f5444097e93c69107d1ac5e8f13d02850efa8d8f2a40fe79674cef46" +checksum = "8eb12a2454e747896929338d93b0642144bb51e0dddbb36e579035731f0d76b7" dependencies = [ "heck 0.4.1", "proc-macro2", @@ -3688,9 +3812,9 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3b80ea3fcd5fefb60739a3b577b277e8fc30434538a2f5bba82ad7d4368c422" +checksum = "108683199cb18f96d2d4134187bb789964143c845d2d154848dda209191fd769" dependencies = [ "gtk", "http", @@ -3709,9 +3833,9 @@ dependencies = [ [[package]] name = "tauri-runtime-wry" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1c396950b1ba06aee1b4ffe6c7cd305ff433ca0e30acbc5fa1a2f92a4ce70f1" +checksum = "0b7aa256a1407a3a091b5d843eccc1a5042289baf0a43d1179d9f0fcfea37c1b" dependencies = [ "cocoa", "gtk", @@ -3729,12 +3853,13 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6f9c2dafef5cbcf52926af57ce9561bd33bb41d7394f8bb849c0330260d864" +checksum = "03fc02bb6072bb397e1d473c6f76c953cda48b4a2d0cce605df284aa74a12e84" dependencies = [ "brotli", "ctor", + "dunce", "glob", "heck 0.4.1", "html5ever", @@ -3887,7 +4012,7 @@ dependencies = [ "lazy_static", "libc", "memchr", - "mio", + "mio 0.6.23", "mio-named-pipes", "mio-uds", "num_cpus", @@ -3906,8 +4031,11 @@ checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" dependencies = [ "autocfg", "bytes 1.4.0", + "libc", + "mio 0.8.8", "num_cpus", "pin-project-lite 0.2.9", + "socket2 0.4.9", "windows-sys 0.48.0", ] @@ -3922,6 +4050,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio 1.28.2", +] + [[package]] name = "tokio-tls" version = "0.3.1" @@ -3946,6 +4084,20 @@ dependencies = [ "tokio 0.2.25", ] +[[package]] +name = "tokio-util" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +dependencies = [ + "bytes 1.4.0", + "futures-core", + "futures-sink", + "pin-project-lite 0.2.9", + "tokio 1.28.2", + "tracing", +] + [[package]] name = "toml" version = "0.5.11" @@ -4330,6 +4482,19 @@ version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" +[[package]] +name = "wasm-streams" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bbae3363c08332cadccd13b67db371814cd214c2524020932f0804b8cf7c078" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "web-sys" version = "0.3.63" @@ -4761,6 +4926,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "winreg" version = "0.11.0" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index a45e9cc..4501210 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -12,12 +12,12 @@ rust-version = "1.63.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [build-dependencies] -tauri-build = { version = "1.3.0", features = [] } +tauri-build = { version = "1.4.0", features = [] } [dependencies] serde_json = "1.0.96" serde = { version = "1.0.163", features = ["derive"] } -tauri = { version = "1.3.0", features = ["api-all", "system-tray"] } +tauri = { version = "1.4.0", features = ["api-all", "system-tray"] } download_rs = { version = "0.2.0", features = ["sync_download"] } tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "dev" } diff --git a/src-tauri/src/inject/event.js b/src-tauri/src/inject/event.js index 9124802..388a96e 100644 --- a/src-tauri/src/inject/event.js +++ b/src-tauri/src/inject/event.js @@ -114,17 +114,17 @@ document.addEventListener('DOMContentLoaded', () => { domEl.addEventListener('mousedown', (e) => { e.preventDefault(); if (e.buttons === 1 && e.detail !== 2) { - appWindow.startDragging(); + appWindow.startDragging().then(); } }); domEl.addEventListener('touchstart', () => { - appWindow.startDragging(); + appWindow.startDragging().then(); }); domEl.addEventListener('dblclick', () => { appWindow.isFullscreen().then((fullscreen) => { - appWindow.setFullscreen(!fullscreen); + appWindow.setFullscreen(!fullscreen).then(); }); }); diff --git a/src-tauri/src/inject/style.js b/src-tauri/src/inject/style.js index 7c34aa3..44e6644 100644 --- a/src-tauri/src/inject/style.js +++ b/src-tauri/src/inject/style.js @@ -56,6 +56,10 @@ window.addEventListener('DOMContentLoaded', (_event) => { padding-top: 20px; } + #__next > div.overflow-hidden.w-full.h-full .min-h-\\[20px\\].items-start.gap-4.whitespace-pre-wrap.break-words { + word-break: break-all; + } + #__next .PageWithSidebarLayout_mainSection__i1yOg { width: 100%; max-width: 1000px; @@ -312,7 +316,8 @@ window.addEventListener('DOMContentLoaded', (_event) => { } @media (min-width:1024px){ - #__next .text-base.lg\\:max-w-xl, #__next form.stretch.lg\\:max-w-2xl { + #__next .text-base.lg\\:max-w-xl, #__next form.stretch.lg\\:max-w-2xl, + #__next > .w-full.h-full .lg\\:max-w-\\[38rem\\] { max-width: 44rem; } } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 443c74b..c3cfd2d 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,43 +1,43 @@ { - "package": { - "productName": "WeRead", - "version": "1.0.0" - }, - "tauri": { - "security": { - "csp": null, - "dangerousRemoteDomainIpcAccess": [ - { - "domain": "weread.qq.com", - "windows": [ - "pake" - ], - "enableTauriAPI": true - } - ] - }, - "updater": { - "active": false - }, - "systemTray": { - "iconPath": "png/weread_512.png", - "iconAsTemplate": true - }, - "allowlist": { - "all": true, - "fs": { - "all": true, - "scope": [ - "$DOWNLOAD/*" - ] - } + "package": { + "productName": "WeRead", + "version": "1.0.0" + }, + "tauri": { + "security": { + "csp": null, + "dangerousRemoteDomainIpcAccess": [ + { + "domain": "weread.qq.com", + "windows": [ + "pake" + ], + "enableTauriAPI": true } + ] }, - "build": { - "withGlobalTauri": true, - "devPath": "../dist", - "distDir": "../dist", - "beforeBuildCommand": "", - "beforeDevCommand": "" + "updater": { + "active": false + }, + "systemTray": { + "iconPath": "png/weread_512.png", + "iconAsTemplate": true + }, + "allowlist": { + "all": true, + "fs": { + "all": true, + "scope": [ + "$DOWNLOAD/*" + ] + } } + }, + "build": { + "withGlobalTauri": true, + "devPath": "../dist", + "distDir": "../dist", + "beforeBuildCommand": "", + "beforeDevCommand": "" + } } From aff053ef6a912ef3f7ef2213ffb6cfc5b04e9479 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Fri, 16 Jun 2023 14:14:47 +0800 Subject: [PATCH 02/40] :art: Compatible with chatgpt --- src-tauri/src/inject/style.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src-tauri/src/inject/style.js b/src-tauri/src/inject/style.js index 44e6644..a9ecd2d 100644 --- a/src-tauri/src/inject/style.js +++ b/src-tauri/src/inject/style.js @@ -332,6 +332,20 @@ window.addEventListener('DOMContentLoaded', (_event) => { #__next .overflow-hidden.w-full .max-w-full>.sticky.top-0 { padding-top: 20px; } + + #__next .overflow-hidden.w-full main.relative.h-full.w-full.flex-1{ + padding-bottom: 82px; + } + + #__next > div.overflow-hidden.w-full.h-full main.relative.h-full.w-full.flex-1 > .flex-1.overflow-hidden .h-32.md\\:h-48.flex-shrink-0{ + height: 0px; + } + } + + @media (max-width:565px){ + #__next .overflow-hidden.w-full main.relative.h-full.w-full.flex-1{ + padding-bottom: 98px; + } } #__next .prose ol li p { From afb0a836166197bef69cae6983109b9ad0ce790a Mon Sep 17 00:00:00 2001 From: Tw93 Date: Fri, 16 Jun 2023 18:26:16 +0800 Subject: [PATCH 03/40] :sparkles: 2.0.7-beta --- bin/builders/common.ts | 22 +----- bin/types.ts | 152 +++++++++++++++++++++++++++++++++++++++++ bin/utils/ip_addr.ts | 4 +- dist/cli.js | 21 +++--- package.json | 8 +-- 5 files changed, 170 insertions(+), 37 deletions(-) diff --git a/bin/builders/common.ts b/bin/builders/common.ts index 594ab3d..5136057 100644 --- a/bin/builders/common.ts +++ b/bin/builders/common.ts @@ -1,30 +1,12 @@ -import { PakeAppOptions } from '@/types.js'; +import { PakeAppOptions,TauriConfig } from '@/types.js'; import prompts from 'prompts'; import path from 'path'; import fs from 'fs/promises'; import fs2 from 'fs-extra'; -import {TauriConfig} from 'tauri/src/types'; import { npmDirectory } from '@/utils/dir.js'; import logger from '@/options/logger.js'; -type DangerousRemoteDomainIpAccess = { - domain: string; - windows: string[]; - enableTauriAPI: boolean; - schema?: string; - plugins?: string[]; -} - -// https://tauri.app/v1/api/config/#remotedomainaccessscope -type NextTauriConfig = TauriConfig & { - tauri: { - security: { - dangerousRemoteDomainIpcAccess?: DangerousRemoteDomainIpAccess[] - } - } -} - export async function promptText(message: string, initial?: string) { const response = await prompts({ @@ -36,7 +18,7 @@ export async function promptText(message: string, initial?: string) { return response.content; } -function setSecurityConfigWithUrl(tauriConfig: NextTauriConfig, url: string) { +function setSecurityConfigWithUrl(tauriConfig: TauriConfig, url: string) { const myURL = new URL(url); const hostname = myURL.hostname; tauriConfig.tauri.security.dangerousRemoteDomainIpcAccess[0].domain = hostname; diff --git a/bin/types.ts b/bin/types.ts index 02955f9..79ba8db 100644 --- a/bin/types.ts +++ b/bin/types.ts @@ -48,3 +48,155 @@ export interface PakeCliOptions { export interface PakeAppOptions extends PakeCliOptions { identifier: string; } + +export interface TauriBuildConfig { + /** + * the path to the app's dist dir + * this path must contain your index.html file + */ + distDir: string + /** + * the app's dev server URL, or the path to the directory containing an index.html to open + */ + devPath: string + /** + * a shell command to run before `tauri dev` kicks in + */ + beforeDevCommand?: string + /** + * a shell command to run before `tauri build` kicks in + */ + beforeBuildCommand?: string + withGlobalTauri?: boolean +} + +type DangerousRemoteDomainIpAccess = { + domain: string; + windows: string[]; + enableTauriAPI: boolean; + schema?: string; + plugins?: string[]; +} + +/** + * Tauri configuration + */ +export interface TauriConfig { + /** + * build/dev configuration + */ + build: TauriBuildConfig + /** + * the context of the current `tauri dev` or `tauri build` + */ + ctx: { + /** + * whether we're building for production or not + */ + prod?: boolean + /** + * whether we're running on the dev environment or not + */ + dev?: boolean + /** + * the target of the compilation (see `rustup target list`) + */ + target?: string + /** + * whether the app should be built on debug mode or not + */ + debug?: boolean + /** + * defines we should exit the `tauri dev` process if a Rust code error is found + */ + exitOnPanic?: boolean + } + /** + * tauri root configuration object + */ + tauri: { + /** + * the embedded server configuration + */ + embeddedServer: { + /** + * whether we should use the embedded-server or the no-server mode + */ + active?: boolean + /** + * the embedded server port number or the 'random' string to generate one at runtime + */ + port?: number | 'random' | undefined + } + /** + * tauri bundler configuration + */ + bundle: { + /** + * whether we should build your app with tauri-bundler or plain `cargo build` + */ + active?: boolean + /** + * the bundle targets, currently supports ["deb", "osx", "msi", "appimage", "dmg"] or "all" + */ + targets?: string | string[] + /** + * the app's identifier + */ + identifier: string + /** + * the app's icons + */ + icon: string[] + /** + * app resources to bundle + * each resource is a path to a file or directory + * glob patterns are supported + */ + resources?: string[] + externalBin?: string[] + copyright?: string + category?: string + shortDescription?: string + longDescription?: string + deb?: { + depends?: string[] + useBootstrapper?: boolean + } + osx?: { + frameworks?: string[] + minimumSystemVersion?: string + license?: string + useBootstrapper?: boolean + } + exceptionDomain?: string + } + allowlist: { + all: boolean + [index: string]: boolean + } + window: { + title: string + width?: number + height?: number + resizable?: boolean + fullscreen?: boolean + } + security: { + csp?: string, + dangerousRemoteDomainIpcAccess?: DangerousRemoteDomainIpAccess[] + } + inliner: { + active?: boolean + } + } + plugins?: { + [name: string]: { + [key: string]: any + } + } + /** + * Whether or not to enable verbose logging + */ + verbose?: boolean +} diff --git a/bin/utils/ip_addr.ts b/bin/utils/ip_addr.ts index a89f697..2fd8cac 100644 --- a/bin/utils/ip_addr.ts +++ b/bin/utils/ip_addr.ts @@ -33,7 +33,7 @@ async function isChinaDomain(domain: string): Promise { return await isChinaIP(ip, domain); } catch (error) { // 域名无法解析,返回false - logger.info(`${domain} can't be parse, is not in China!`); + logger.info(`${domain} can't be parse!`); return false; } } @@ -46,7 +46,7 @@ async function isChinaIP(ip: string, domain: string): Promise { return delay > 500; } catch (error) { // 命令执行出错,返回false - logger.info(`ping ${domain} failed!, is not in China!`); + logger.info(`ping ${domain} failed!`); return false; } } diff --git a/dist/cli.js b/dist/cli.js index b6b78a6..d93d2f5 100644 --- a/dist/cli.js +++ b/dist/cli.js @@ -9,7 +9,6 @@ import path from 'path'; import fs$1 from 'fs/promises'; import fs2 from 'fs-extra'; import chalk from 'chalk'; -import URL from 'node:url'; import crypto from 'crypto'; import axios from 'axios'; import { fileTypeFromBuffer } from 'file-type'; @@ -35,6 +34,8 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ +/* global Reflect, Promise */ + function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } @@ -1641,7 +1642,8 @@ function promptText(message, initial) { }); } function setSecurityConfigWithUrl(tauriConfig, url) { - const { hostname } = URL.parse(url); + const myURL = new URL(url); + const hostname = myURL.hostname; tauriConfig.tauri.security.dangerousRemoteDomainIpcAccess[0].domain = hostname; } function mergeTauriConfig(url, options, tauriConf) { @@ -2056,7 +2058,7 @@ function isChinaDomain(domain) { } catch (error) { // 域名无法解析,返回false - logger.info(`${domain} can't be parse, is not in China!`); + logger.info(`${domain} can't be parse!`); return false; } }); @@ -2071,7 +2073,7 @@ function isChinaIP(ip, domain) { } catch (error) { // 命令执行出错,返回false - logger.info(`ping ${domain} failed!, is not in China!`); + logger.info(`ping ${domain} failed!`); return false; } }); @@ -2124,7 +2126,7 @@ var tauri$3 = { active: false }, systemTray: { - iconPath: "png/icon_512.png", + iconPath: "png/weread_512.png", iconAsTemplate: true }, allowlist: { @@ -2545,7 +2547,7 @@ class BuilderFactory { } var name = "pake-cli"; -var version = "2.0.6"; +var version = "2.0.7-beta3"; var description = "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。"; var engines = { node: ">=16.0.0" @@ -2591,11 +2593,11 @@ var type = "module"; var exports = "./dist/pake.js"; var license = "MIT"; var dependencies = { - "@tauri-apps/api": "^1.2.0", - "@tauri-apps/cli": "^1.2.3", + "@tauri-apps/api": "^1.4.0", + "@tauri-apps/cli": "1.3.1", axios: "^1.1.3", chalk: "^5.1.2", - commander: "^9.4.1", + commander: "^11.0.0", "file-type": "^18.0.0", "fs-extra": "^11.1.0", "is-url": "^1.2.4", @@ -2620,7 +2622,6 @@ var devDependencies = { "@types/tmp": "^0.2.3", "@types/update-notifier": "^6.0.1", "app-root-path": "^3.1.0", - concurrently: "^7.5.0", "cross-env": "^7.0.3", rollup: "^3.3.0", tslib: "^2.4.1", diff --git a/package.json b/package.json index 65dcf2a..f294c31 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pake-cli", - "version": "2.0.6", + "version": "2.0.7-beta3", "description": "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。", "engines": { "node": ">=16.0.0" @@ -47,10 +47,10 @@ "license": "MIT", "dependencies": { "@tauri-apps/api": "^1.4.0", - "@tauri-apps/cli": "^1.4.0", + "@tauri-apps/cli": "1.3.1", "axios": "^1.1.3", "chalk": "^5.1.2", - "commander": "^9.4.1", + "commander": "^11.0.0", "file-type": "^18.0.0", "fs-extra": "^11.1.0", "is-url": "^1.2.4", @@ -75,10 +75,8 @@ "@types/tmp": "^0.2.3", "@types/update-notifier": "^6.0.1", "app-root-path": "^3.1.0", - "concurrently": "^7.5.0", "cross-env": "^7.0.3", "rollup": "^3.3.0", - "tauri": "^0.15.0", "tslib": "^2.4.1", "typescript": "^4.9.3" } From 97b517386c64b0ac450b5b36ca2a0e93edc1b6c6 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Sun, 18 Jun 2023 18:20:13 +0800 Subject: [PATCH 04/40] :sparkles: GPT4-Mobile --- src-tauri/src/inject/component.js | 32 +++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src-tauri/src/inject/component.js b/src-tauri/src/inject/component.js index ba9824c..31ee045 100644 --- a/src-tauri/src/inject/component.js +++ b/src-tauri/src/inject/component.js @@ -146,4 +146,36 @@ document.addEventListener('DOMContentLoaded', () => { } window.pakeToast = pakeToast; + + // chatgpt supports unlimited times of GPT4-Mobile + if (window.location.hostname === 'chat.openai.com') { + const originFetch = fetch; + window.fetch = (url, options) => { + return originFetch(url, options).then(async (response) => { + if (url.indexOf('/backend-api/models') === -1) { + return response; + } + const responseClone = response.clone(); + let res = await responseClone.json(); + res.models = res.models.map((m) => { + m.tags = m.tags.filter((t) => { + return t !== 'mobile'; + }); + if (m.slug === 'gpt-4-mobile') { + res.categories.push({ + browsing_model: null, + category: 'gpt_4', + code_interpreter_model: null, + default_model: 'gpt-4-mobile', + human_category_name: 'GPT-4-Mobile', + plugins_model: null, + subscription_level: 'plus', + }); + } + return m; + }); + return new Response(JSON.stringify(res), response); + }); + }; + } }); From d938dd946e36b6025d2b4e9344733c7ae7ae0c9f Mon Sep 17 00:00:00 2001 From: Tw93 Date: Sun, 18 Jun 2023 18:21:43 +0800 Subject: [PATCH 05/40] :bookmark: 2.0.7 --- dist/cli.js | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/cli.js b/dist/cli.js index d93d2f5..da9d1f3 100644 --- a/dist/cli.js +++ b/dist/cli.js @@ -2547,7 +2547,7 @@ class BuilderFactory { } var name = "pake-cli"; -var version = "2.0.7-beta3"; +var version = "2.0.7"; var description = "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。"; var engines = { node: ">=16.0.0" diff --git a/package.json b/package.json index f294c31..51deca7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pake-cli", - "version": "2.0.7-beta3", + "version": "2.0.7", "description": "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。", "engines": { "node": ">=16.0.0" From 532e5e873ec9b1d84c9e3f4e3d9a4e630229621d Mon Sep 17 00:00:00 2001 From: Tw93 Date: Sun, 18 Jun 2023 18:43:07 +0800 Subject: [PATCH 06/40] :pencil: update chatgpt --- README.md | 2 +- README_CN.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 11bfabf..eeb182f 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ - + diff --git a/README_CN.md b/README_CN.md index 146beba..8ec0104 100644 --- a/README_CN.md +++ b/README_CN.md @@ -57,7 +57,7 @@ - + From 6537334f72cae6fdd58f909f1888e94a84aedabd Mon Sep 17 00:00:00 2001 From: Tw93 Date: Sun, 18 Jun 2023 18:44:29 +0800 Subject: [PATCH 07/40] :pencil: update chatgpt --- README.md | 2 +- README_CN.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index eeb182f..c70b216 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ - + diff --git a/README_CN.md b/README_CN.md index 8ec0104..3656c44 100644 --- a/README_CN.md +++ b/README_CN.md @@ -57,7 +57,7 @@ - + From 45757e7e7b285a0f6b5c48625e5748f9bc2bae6f Mon Sep 17 00:00:00 2001 From: Tw93 Date: Sun, 18 Jun 2023 18:47:10 +0800 Subject: [PATCH 08/40] :pencil: update chatgpt --- README.md | 2 +- README_CN.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c70b216..5419f02 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ - + diff --git a/README_CN.md b/README_CN.md index 3656c44..e5b97f0 100644 --- a/README_CN.md +++ b/README_CN.md @@ -57,7 +57,7 @@ - + From 24e514d209862715c91b2599faca29d7bb4d30b0 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Mon, 19 Jun 2023 19:47:42 +0800 Subject: [PATCH 09/40] :sparkles: Support audio and video permissions --- src-tauri/info.plist | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src-tauri/info.plist diff --git a/src-tauri/info.plist b/src-tauri/info.plist new file mode 100644 index 0000000..7d3ae94 --- /dev/null +++ b/src-tauri/info.plist @@ -0,0 +1,9 @@ + + + + NSCameraUsageDescription + Request camera access + NSMicrophoneUsageDescription + Request microphone access + + From fc57e2f03c4f1767ca8fb2add20db56218e57dc4 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Mon, 19 Jun 2023 19:56:52 +0800 Subject: [PATCH 10/40] :pencil: Instructions for use --- README.md | 2 +- README_CN.md | 56 ++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 5419f02..cddb5fa 100644 --- a/README.md +++ b/README.md @@ -194,7 +194,7 @@ npm run build ## Advanced Usage 1. You can refer to the [codebase structure](https://github.com/tw93/Pake/wiki/Description-of-Pake's-code-structure) before working on Pake, which will help you much in development. -2. Modify the `url` and `productName` fields in the `pake.json` file under the src-tauri directory, as well as the `icon` and `identifier` fields in the `tauri.xxx.conf.json` file. You can select a `icon` from the `icons` directory or download one from [macOSicons](https://macosicons.com/#/) to match your product needs. +2. Modify the `url` and `productName` fields in the `pake.json` file under the src-tauri directory, the "domain" field in the `tauri.config.json` file needs to be modified synchronously, as well as the `icon` and `identifier` fields in the `tauri.xxx.conf.json` file. You can select a `icon` from the `icons` directory or download one from [macOSicons](https://macosicons.com/#/) to match your product needs. 3. For configurations on window properties, you can modify the `pake.json` file to change the value of `width`, `height`, `fullscreen` (or not), `resizable` (or not) of the `windows` property. To adapt to the immersive header on Mac, change `transparent` to `true`, look for the `Header` element, and add the `padding-top` property. 4. For advanced usages such as style rewriting, advertisement removal, JS injection, container message communication, and user-defined shortcut keys, see [Advanced Usage of Pake](https://github.com/tw93/Pake/wiki/Advanced-Usage-of-Pake). diff --git a/README_CN.md b/README_CN.md index e5b97f0..f0f7a61 100644 --- a/README_CN.md +++ b/README_CN.md @@ -196,7 +196,7 @@ npm run build ## 高级使用 1. 代码结构可参考 [文档](https://github.com/tw93/Pake/wiki/Pake-%E7%9A%84%E4%BB%A3%E7%A0%81%E7%BB%93%E6%9E%84%E8%AF%B4%E6%98%8E),便于你在开发前了解更多。 -2. 修改 src-tauri 目录下 `pake.json` 中的 `url` 和 `productName` 字段,以及 `tauri.xxx.conf.json` 中的 `icon` 和 `identifier` 字段,其中 `icon` 可以从 icons 目录选择一个,也可以去 [macOSicons](https://macosicons.com/#/) 下载符合效果的。 +2. 修改 src-tauri 目录下 `pake.json` 中的 `url` 和 `productName` 字段,需同步修改下 `tauri.config.json` 中的 `domain` 字段,以及 `tauri.xxx.conf.json` 中的 `icon` 和 `identifier` 字段,其中 `icon` 可以从 icons 目录选择一个,也可以去 [macOSicons](https://macosicons.com/#/) 下载符合效果的。 3. 关于窗口属性设置,可以在 `pake.json` 修改 windows 属性对应的 `width/height`,fullscreen 是否全屏,resizable 是否可以调整大小,假如想适配 Mac 沉浸式头部,可以将 transparent 设置成 `true`,找到 Header 元素加一个 padding-top 样式即可,不想适配改成 `false` 也行。 4. 此外样式改写、屏蔽广告、逻辑代码注入、容器消息通信、自定义快捷键可见 [高级用法](https://github.com/tw93/Pake/wiki/Pake-%E7%9A%84%E9%AB%98%E7%BA%A7%E7%94%A8%E6%B3%95)。 @@ -249,19 +249,26 @@ Pake 的发展离不开这些 Hacker 们,一起贡献了大量能力,也欢 Essesoul + + + AielloChan +
+ Aiello +
+ + m1911star
Horus
- - + - - AielloChan + + Pake-Actions
- Aiello + Pake Actions
@@ -271,6 +278,20 @@ Pake 的发展离不开这些 Hacker 们,一起贡献了大量能力,也欢 Steam + + + exposir +
+ 孟世博 +
+ + + + jeasonnow +
+ Santree +
+ 2nthony @@ -278,6 +299,14 @@ Pake 的发展离不开这些 Hacker 们,一起贡献了大量能力,也欢 2nthony + + + ACGNnsj +
+ Null +
+ + nekomeowww @@ -305,8 +334,7 @@ Pake 的发展离不开这些 Hacker 们,一起贡献了大量能力,也欢
Po Chen
- - + houhoz @@ -314,13 +342,21 @@ Pake 的发展离不开这些 Hacker 们,一起贡献了大量能力,也欢 Hyzhao + + + lakca +
+ Null +
+ liusishan
Liusishan
- + + piaoyidage @@ -333,7 +369,7 @@ Pake 的发展离不开这些 Hacker 们,一起贡献了大量能力,也欢 ## 支持 -1. 我有两只猫,一只叫汤圆,一只叫可乐,假如觉得 Pake 让你生活更美好,可以给汤圆可乐 喂罐头 🥩。 +1. 我有两只猫,一只叫汤圆,一只叫可乐,假如 Pake 让你生活更美好,可以给汤圆可乐 喂罐头 🥩。 2. 如果你喜欢 Pake,可以在 Github Star,更欢迎 [推荐](https://twitter.com/intent/tweet?url=https://github.com/tw93/Pake&text=%23Pake%20%E4%B8%80%E4%B8%AA%E5%BE%88%E7%AE%80%E5%8D%95%E7%9A%84%E7%94%A8%20Rust%20%E6%89%93%E5%8C%85%E7%BD%91%E9%A1%B5%E7%94%9F%E6%88%90%20Mac%20App%20%E7%9A%84%E5%B7%A5%E5%85%B7%EF%BC%8C%E7%9B%B8%E6%AF%94%E4%BC%A0%E7%BB%9F%E7%9A%84%20Electron%20%E5%A5%97%E5%A3%B3%E6%89%93%E5%8C%85%EF%BC%8C%E5%A4%A7%E5%B0%8F%E8%A6%81%E5%B0%8F%E5%B0%86%E8%BF%91%2040%20%E5%80%8D%EF%BC%8C%E4%B8%80%E8%88%AC%202M%20%E5%B7%A6%E5%8F%B3%EF%BC%8C%E5%BA%95%E5%B1%82%E4%BD%BF%E7%94%A8Tauri%20%EF%BC%8C%E6%80%A7%E8%83%BD%E4%BD%93%E9%AA%8C%E8%BE%83%20JS%20%E6%A1%86%E6%9E%B6%E8%A6%81%E8%BD%BB%E5%BF%AB%E4%B8%8D%E5%B0%91%EF%BC%8C%E5%86%85%E5%AD%98%E5%B0%8F%E5%BE%88%E5%A4%9A%EF%BC%8C%E6%94%AF%E6%8C%81%E5%BE%AE%E4%BF%A1%E8%AF%BB%E4%B9%A6%E3%80%81Twitter%E3%80%81Youtube%E3%80%81RunCode%E3%80%81Flomo%E3%80%81%E8%AF%AD%E9%9B%80%E7%AD%89%EF%BC%8C%E5%8F%AF%E4%BB%A5%E5%BE%88%E6%96%B9%E4%BE%BF%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91~) 给你志同道合的朋友使用。 3. 可以关注我的 [Twitter](https://twitter.com/HiTw93) 获取到最新的 Pake 更新消息,也欢迎加入 [Telegram](https://t.me/+GclQS9ZnxyI2ODQ1) 聊天群。 4. 希望大伙玩的过程中有一种学习新技术的喜悦感,假如你发现有很适合做成桌面 App 的网页也很欢迎告诉我。 From 5678cf316d9dc048d76a95a3b8a1def6dfd397c8 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Mon, 19 Jun 2023 21:12:45 +0800 Subject: [PATCH 11/40] :wrench: update version --- package.json | 2 +- src-tauri/Cargo.lock | 257 +++++++++++++++++-------------------------- src-tauri/Cargo.toml | 2 +- 3 files changed, 102 insertions(+), 159 deletions(-) diff --git a/package.json b/package.json index 51deca7..62978a2 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "license": "MIT", "dependencies": { "@tauri-apps/api": "^1.4.0", - "@tauri-apps/cli": "1.3.1", + "@tauri-apps/cli": "^1.4.0", "axios": "^1.1.3", "chalk": "^5.1.2", "commander": "^11.0.0", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 434b3c2..1d26ec6 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -151,24 +151,6 @@ dependencies = [ "event-listener", ] -[[package]] -name = "async-process" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" -dependencies = [ - "async-io", - "async-lock", - "autocfg", - "blocking", - "cfg-if 1.0.0", - "event-listener", - "futures-lite", - "rustix", - "signal-hook", - "windows-sys 0.48.0", -] - [[package]] name = "async-recursion" version = "1.0.4" @@ -268,9 +250,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6776fc96284a0bb647b615056fc496d1fe1644a7ab01829818a6d91cae888b84" +checksum = "6dbe3c979c178231552ecba20214a8272df4e09f232a87aef4320cf06539aded" [[package]] name = "block" @@ -413,7 +395,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "599aa35200ffff8f04c1925aa1acc92fa2e08874379ef42e210a80e527e60838" dependencies = [ "serde", - "toml 0.7.3", + "toml 0.7.4", ] [[package]] @@ -436,7 +418,7 @@ checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" dependencies = [ "byteorder", "fnv", - "uuid 1.3.3", + "uuid 1.3.4", ] [[package]] @@ -601,9 +583,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" dependencies = [ "libc", ] @@ -629,9 +611,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if 1.0.0", ] @@ -683,12 +665,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "cty" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" - [[package]] name = "darling" version = "0.20.1" @@ -825,7 +801,7 @@ checksum = "80663502655af01a2902dff3f06869330782267924bf1788410b74edcd93770a" dependencies = [ "cc", "rustc_version", - "toml 0.7.3", + "toml 0.7.4", "vswhom", "winreg 0.11.0", ] @@ -1625,9 +1601,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.56" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1674,11 +1650,10 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.18" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" +checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" dependencies = [ - "crossbeam-utils", "globset", "lazy_static", "log", @@ -1827,9 +1802,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" -version = "0.3.63" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -1947,9 +1922,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.18" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "loom" @@ -2210,9 +2185,9 @@ dependencies = [ [[package]] name = "net2" -version = "0.2.38" +version = "0.2.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d0df99cfcd2530b2e694f6e17e7f37b8e26bb23983ac530c0c97408837c631" +checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac" dependencies = [ "cfg-if 0.1.10", "libc", @@ -2328,15 +2303,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "num_threads" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" -dependencies = [ - "libc", -] - [[package]] name = "number_prefix" version = "0.3.0" @@ -2720,9 +2686,9 @@ dependencies = [ [[package]] name = "png" -version = "0.17.8" +version = "0.17.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaeebc51f9e7d2c150d3f3bfeb667f2aa985db5ef1e3d212847bdedb488beeaa" +checksum = "59871cc5b6cce7eaccca5a802b4173377a1c2ba90654246789a8fa2334426d11" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -2801,9 +2767,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.59" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" +checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" dependencies = [ "unicode-ident", ] @@ -2918,12 +2884,9 @@ dependencies = [ [[package]] name = "raw-window-handle" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed7e3d950b66e19e0c372f3fa3fbbcf85b1746b571f74e0c2af6042a5c93420a" -dependencies = [ - "cty", -] +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" [[package]] name = "redox_syscall" @@ -3095,9 +3058,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.19" +version = "0.37.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" dependencies = [ "bitflags 1.3.2", "errno", @@ -3215,18 +3178,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.163" +version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.163" +version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" dependencies = [ "proc-macro2", "quote", @@ -3235,9 +3198,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "bdf3bf93142acad5821c99197022e170842cdbc1c30482b98750c688c640842a" dependencies = [ "itoa 1.0.6", "ryu", @@ -3349,9 +3312,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if 1.0.0", "cpufeatures", @@ -3377,16 +3340,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "signal-hook" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" -dependencies = [ - "libc", - "signal-hook-registry", -] - [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -3525,27 +3478,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "strum" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7ac893c7d471c8a21f31cfe213ec4f6d9afeed25537c772e08ef3f005f8729e" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "339f799d8b549e3744c7ac7feb216383e4005d94bdb22561b3ab8f3b808ae9fb" -dependencies = [ - "heck 0.3.3", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "syn" version = "1.0.109" @@ -3603,7 +3535,7 @@ dependencies = [ "cfg-expr 0.15.2", "heck 0.4.1", "pkg-config", - "toml 0.7.3", + "toml 0.7.4", "version-compare 0.1.1", ] @@ -3650,7 +3582,7 @@ dependencies = [ "serde", "tao-macros", "unicode-segmentation", - "uuid 1.3.3", + "uuid 1.3.4", "windows 0.39.0", "windows-implement", "x11-dl", @@ -3686,9 +3618,9 @@ checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5" [[package]] name = "tauri" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc35893c7e08d9564a9206bd52182dce031b0d5132dc946b3e166e00d03f8cfe" +checksum = "7fbe522898e35407a8e60dc3870f7579fea2fc262a6a6072eccdd37ae1e1d91e" dependencies = [ "anyhow", "bytes 1.4.0", @@ -3733,7 +3665,7 @@ dependencies = [ "thiserror", "tokio 1.28.2", "url", - "uuid 1.3.3", + "uuid 1.3.4", "webkit2gtk", "webview2-com", "windows 0.39.0", @@ -3778,7 +3710,7 @@ dependencies = [ "tauri-utils", "thiserror", "time", - "uuid 1.3.3", + "uuid 1.3.4", "walkdir", ] @@ -3802,7 +3734,7 @@ version = "0.1.0" source = "git+https://github.com/tauri-apps/plugins-workspace?branch=dev#dce0f02bc571128308c30278cde3233f341e6a50" dependencies = [ "bincode", - "bitflags 2.3.1", + "bitflags 2.3.2", "log", "serde", "serde_json", @@ -3826,7 +3758,7 @@ dependencies = [ "tauri-utils", "thiserror", "url", - "uuid 1.3.3", + "uuid 1.3.4", "webview2-com", "windows 0.39.0", ] @@ -3844,7 +3776,7 @@ dependencies = [ "raw-window-handle", "tauri-runtime", "tauri-utils", - "uuid 1.3.3", + "uuid 1.3.4", "webkit2gtk", "webview2-com", "windows 0.39.0", @@ -3887,17 +3819,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5993dc129e544393574288923d1ec447c857f3f644187f4fbf7d9a875fbfc4fb" dependencies = [ "embed-resource", - "toml 0.7.3", + "toml 0.7.4", ] [[package]] name = "tauri-winrt-notification" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58de036c4d2e20717024de2a3c4bf56c301f07b21bc8ef9b57189fce06f1f3b" +checksum = "37d70573554e7630c2ca3677ea78d5ae6b030aedee5f9bf33c15d644904fa698" dependencies = [ "quick-xml 0.23.1", - "strum", "windows 0.39.0", ] @@ -3974,14 +3905,29 @@ dependencies = [ [[package]] name = "time" -version = "0.3.15" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d634a985c4d4238ec39cacaed2e7ae552fbd3c476b552c1deac3021b7d7eaf0c" +checksum = "ea9e1b3cf1243ae005d9e74085d4d542f3125458f3a81af210d901dcd7411efd" dependencies = [ "itoa 1.0.6", - "libc", - "num_threads", "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" + +[[package]] +name = "time-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +dependencies = [ + "time-core", ] [[package]] @@ -4109,9 +4055,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21" +checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec" dependencies = [ "serde", "serde_spanned", @@ -4130,9 +4076,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.19.8" +version = "0.19.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" +checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" dependencies = [ "indexmap", "serde", @@ -4319,9 +4265,9 @@ checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" [[package]] name = "uuid" -version = "1.3.3" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2" +checksum = "0fa2982af2eec27de306107c027578ff7f423d65f7250e40ce0fea8f45248b81" dependencies = [ "getrandom 0.2.10", ] @@ -4394,11 +4340,10 @@ dependencies = [ [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -4416,9 +4361,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if 1.0.0", "serde", @@ -4428,9 +4373,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", @@ -4443,9 +4388,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.36" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d1985d03709c53167ce907ff394f5316aa22cb4e12761295c5dc57dacb6297e" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -4455,9 +4400,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4465,9 +4410,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", @@ -4478,9 +4423,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "wasm-streams" @@ -4497,9 +4442,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.63" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -4910,9 +4855,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.1" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" +checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448" dependencies = [ "memchr", ] @@ -5035,16 +4980,15 @@ dependencies = [ [[package]] name = "zbus" -version = "3.13.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c3d77c9966c28321f1907f0b6c5a5561189d1f7311eea6d94180c6be9daab29" +checksum = "29242fa5ec5693629ae74d6eb1f69622a9511f600986d6d9779bccf36ac316e3" dependencies = [ "async-broadcast", "async-executor", "async-fs", "async-io", "async-lock", - "async-process", "async-recursion", "async-task", "async-trait", @@ -5075,16 +5019,15 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "3.13.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e341d12edaff644e539ccbbf7f161601294c9a84ed3d7e015da33155b435af" +checksum = "537793e26e9af85f774801dc52c6f6292352b2b517c5cf0449ffd3735732a53a" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", "regex", "syn 1.0.109", - "winnow", "zvariant_utils", ] @@ -5101,9 +5044,9 @@ dependencies = [ [[package]] name = "zvariant" -version = "3.14.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622cc473f10cef1b0d73b7b34a266be30ebdcfaea40ec297dd8cbda088f9f93c" +checksum = "5cb36cd95352132911c9c99fdcc1635de5c2c139bd34cbcf6dfb8350ee8ff6a7" dependencies = [ "byteorder", "enumflags2", @@ -5115,9 +5058,9 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "3.14.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d9c1b57352c25b778257c661f3c4744b7cefb7fc09dd46909a153cce7773da2" +checksum = "9b34951e1ac64f3a1443fe7181256b9ed6a811a1631917566c3d5ca718d8cf33" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -5128,9 +5071,9 @@ dependencies = [ [[package]] name = "zvariant_utils" -version = "1.0.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" +checksum = "53b22993dbc4d128a17a3b6c92f1c63872dd67198537ee728d8b5d7c40640a8b" dependencies = [ "proc-macro2", "quote", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 4501210..c7719d1 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -17,7 +17,7 @@ tauri-build = { version = "1.4.0", features = [] } [dependencies] serde_json = "1.0.96" serde = { version = "1.0.163", features = ["derive"] } -tauri = { version = "1.4.0", features = ["api-all", "system-tray"] } +tauri = { version = "1.4.1", features = ["api-all", "system-tray"] } download_rs = { version = "0.2.0", features = ["sync_download"] } tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "dev" } From 87e91ecbf5fd2a282df1c14c7df27f3e8856c2b2 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Mon, 19 Jun 2023 21:21:56 +0800 Subject: [PATCH 12/40] :pencil: update docs --- bin/README.md | 1 - bin/README_EN.md | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/bin/README.md b/bin/README.md index be12361..269a1e2 100644 --- a/bin/README.md +++ b/bin/README.md @@ -189,7 +189,6 @@ rustup target add x86_64-apple-darwin --system-tray-icon ``` - #### [copy-iter-file] 递归拷贝,当url为本地文件路径时候,若开启该选项,则将url路径文件所在文件夹以及所有子文件都拷贝到pake静态文件夹,默认不开启 diff --git a/bin/README_EN.md b/bin/README_EN.md index 375a2fe..9d21cea 100644 --- a/bin/README_EN.md +++ b/bin/README_EN.md @@ -161,10 +161,10 @@ rustup target add x86_64-apple-darwin Select the output package format, support deb/appimage/all, if all is selected, deb and appimage will be packaged at the same time, this option only supports Linux, the default is `all`. - #### [user-agent] Custom browser user agent, default is empty. + ```shell --user-agent ``` From ae07492dd3ce3d757330aa967295b274b7d2d4ea Mon Sep 17 00:00:00 2001 From: jeasonnow Date: Wed, 21 Jun 2023 18:21:29 +0800 Subject: [PATCH 13/40] fix: issue-447 use addEventListener to listen anchorElement click --- src-tauri/src/inject/event.js | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src-tauri/src/inject/event.js b/src-tauri/src/inject/event.js index 388a96e..7a66cc7 100644 --- a/src-tauri/src/inject/event.js +++ b/src-tauri/src/inject/event.js @@ -156,6 +156,7 @@ document.addEventListener('DOMContentLoaded', () => { } let filename = anchorElement.download || getFilenameFromUrl(absoluteUrl); + // Process download links for Rust to handle. // If the download attribute is set, the download attribute is used as the file name. if ( @@ -261,29 +262,32 @@ function convertBlobUrlToBinary(blobUrl) { }); } +function downloadFromBlobUrl(blobUrl, filename) { + const tauri = window.__TAURI__; + convertBlobUrlToBinary(blobUrl).then((binary) => { + console.log('binary', binary); + tauri.fs.writeBinaryFile(filename, binary, { + dir: tauri.fs.BaseDirectory.Download, + }).then(() => { + window.pakeToast('Download successful, saved to download directory~'); + }); + }); +} + // detect blob download by createElement("a") function detectDownloadByCreateAnchor() { const createEle = document.createElement; document.createElement = (el) => { if (el !== "a") return createEle.call(document, el); const anchorEle = createEle.call(document, el); - const anchorClick = anchorEle.click; - Object.defineProperties(anchorEle, { - click: { - get: () => { - if (anchorEle.href && anchorEle.href.includes('blob:')) { - const url = anchorEle.href; - convertBlobUrlToBinary(url).then((binary) => { - tauri.fs.writeBinaryFile(anchorEle.download || getFilenameFromUrl(url), binary, { - dir: tauri.fs.BaseDirectory.Download, - }); - }); - } - return anchorClick.bind(anchorEle); - } + // use addEventListener to avoid overriding the original click event. + anchorEle.addEventListener('click', () => { + const url = anchorEle.href; + if (window.blobToUrlCaches.has(url)) { + downloadFromBlobUrl(url, anchorEle.download || getFilenameFromUrl(url)); } - }) + }); return anchorEle; } From 5a2d777396970533ed922a43ce9e080dc1726bd5 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Wed, 21 Jun 2023 19:40:30 +0800 Subject: [PATCH 14/40] docs(contributor): contrib-readme-action has updated readme --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index cddb5fa..5aec3ec 100644 --- a/README.md +++ b/README.md @@ -269,6 +269,13 @@ Pake's development can not be without these Hackers. They contributed a lot of c Pake Actions + + + jeasonnow +
+ Santree +
+ QingZ11 @@ -283,13 +290,6 @@ Pake's development can not be without these Hackers. They contributed a lot of c 孟世博 - - - jeasonnow -
- Santree -
- 2nthony From b2d0d7a2aeef72b21907fff41de4eb71a970a62e Mon Sep 17 00:00:00 2001 From: Tw93 Date: Thu, 22 Jun 2023 14:36:02 +0800 Subject: [PATCH 15/40] :art: Refactoring CLI --- .prettierignore | 3 +- README.md | 2 +- README_CN.md | 2 +- bin/README.md | 110 +- bin/README_CN.md | 196 ++ bin/README_EN.md | 202 -- bin/builders/BaseBuilder.ts | 56 + bin/builders/BuilderFactory.ts | 20 - bin/builders/BuilderProvider.ts | 21 + bin/builders/LinuxBuilder.ts | 95 +- bin/builders/MacBuilder.ts | 99 +- bin/builders/WinBuilder.ts | 35 + bin/builders/WinBulider.ts | 85 - bin/builders/base.ts | 16 - bin/builders/common.ts | 388 +-- bin/builders/tauriConf.js | 30 - bin/cli.ts | 86 +- bin/defaults.ts | 2 - bin/helpers/rust.ts | 30 +- bin/helpers/tauriConfig.ts | 33 +- bin/helpers/updater.ts | 1 - bin/options/icon.ts | 106 +- bin/options/index.ts | 32 +- bin/types.ts | 187 +- bin/utils/dir.ts | 5 +- bin/utils/info.ts | 21 + bin/utils/{ip_addr.ts => ip.ts} | 29 +- bin/utils/platform.ts | 8 +- bin/utils/shell.ts | 4 +- bin/utils/tlds.ts | 1489 --------- bin/utils/url.ts | 55 +- bin/utils/validate.ts | 18 +- dist/cli.js | 3087 ++++-------------- package.json | 8 +- rollup.config.js | 5 +- script/build.bat | 2 +- src-tauri/{cn_config.bak => rust_proxy.toml} | 0 tsconfig.json | 18 +- 38 files changed, 1466 insertions(+), 5120 deletions(-) create mode 100644 bin/README_CN.md delete mode 100644 bin/README_EN.md create mode 100644 bin/builders/BaseBuilder.ts delete mode 100644 bin/builders/BuilderFactory.ts create mode 100644 bin/builders/BuilderProvider.ts create mode 100644 bin/builders/WinBuilder.ts delete mode 100644 bin/builders/WinBulider.ts delete mode 100644 bin/builders/base.ts delete mode 100644 bin/builders/tauriConf.js create mode 100644 bin/utils/info.ts rename bin/utils/{ip_addr.ts => ip.ts} (66%) delete mode 100644 bin/utils/tlds.ts rename src-tauri/{cn_config.bak => rust_proxy.toml} (100%) diff --git a/.prettierignore b/.prettierignore index 42dc01a..e029b86 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,3 @@ -target +src-tauri/target node_modules dist/**/* -!dist/twitter.css diff --git a/README.md b/README.md index cddb5fa..00db4d4 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,7 @@ In addition, double-click the title bar to switch to full-screen mode. For Mac u ![Pake](https://gw.alipayobjects.com/zos/k/zd/pake.gif) -**Pake provides a command line tool, making the flow of package customization quicker and easier. See [documentation](./bin/README_EN.md) for more information.** +**Pake provides a command line tool, making the flow of package customization quicker and easier. See [documentation](./bin/README_CN.md) for more information.** ```bash # Install with npm diff --git a/README_CN.md b/README_CN.md index f0f7a61..406a777 100644 --- a/README_CN.md +++ b/README_CN.md @@ -162,7 +162,7 @@

-**Pake 提供了命令行工具,可以更快捷方便地一键自定义打你需要的包,详细可见 [文档](./bin/README.md)。** +**Pake 提供了命令行工具,可以更快捷方便地一键自定义打你需要的包,详细可见 [文档](./bin/README_CN.md)。** ```bash # 使用 npm 进行安装 diff --git a/bin/README.md b/bin/README.md index 269a1e2..3b0042a 100644 --- a/bin/README.md +++ b/bin/README.md @@ -1,23 +1,23 @@ -## 安装 +## Installation -请确保 Node 版本>=16 如 16.8,不要使用 sudo 进行安装,假如 npm 报没有权限可以参考 [How to fix npm throwing error without sudo](https://stackoverflow.com/questions/16151018/how-to-fix-npm-throwing-error-without-sudo)。 +Ensure that your Node.js version is 16.0 or higher (e.g., 16.8). Avoid using `sudo` for the installation. If you encounter permission issues with npm, refer to [How to fix npm throwing error without sudo](https://stackoverflow.com/questions/16151018/how-to-fix-npm-throwing-error-without-sudo). ```bash -npm install -g pake-cli +npm install pake-cli -g ``` -## Windows/Linux 注意点 +## Considerations for Windows & Linux Users -- **十分重要** 查看 Tauri 提供的[依赖指南](https://tauri.app/v1/guides/getting-started/prerequisites) -- 对于 windows(至少安装了`Win10 SDK(10.0.19041.0)` 与`Visual Studio build tool 2022(>=17.2)`),还需要额外安装: +- **CRITICAL**: Consult [Tauri prerequisites](https://tauri.app/v1/guides/getting-started/prerequisites) before proceeding. +- For Windows users (ensure that `Win10 SDK (10.0.19041.0)` and `Visual Studio build tool 2022 (>=17.2)` are installed), additional installations are required: 1. Microsoft Visual C++ 2015-2022 Redistributable (x64) 2. Microsoft Visual C++ 2015-2022 Redistributable (x86) - 3. Microsoft Visual C++ 2012 Redistributable (x86)(可选) - 4. Microsoft Visual C++ 2013 Redistributable (x86)(可选) - 5. Microsoft Visual C++ 2008 Redistributable (x86)(可选) + 3. Microsoft Visual C++ 2012 Redistributable (x86) (optional) + 4. Microsoft Visual C++ 2013 Redistributable (x86) (optional) + 5. Microsoft Visual C++ 2008 Redistributable (x86) (optional) -- 此外 Ubuntu 在开始之前可以运行如下命令,安装前期所需依赖。 +- For Ubuntu users, execute the following commands to install the required libraries before compiling: ```bash sudo apt install libdbus-1-dev \ @@ -35,131 +35,124 @@ npm install -g pake-cli gnome-video-effects-extra ``` -## 用法 +## Usage ```bash pake url [options] ``` -打包完成后的应用程序默认为当前工作目录,首次打包由于需配置好环境,需要一些时间,请耐心等待即可。 +The packaged application will be located in the current working directory by default. The first packaging might take some time due to environment configuration. Please be patient. -> **Note**: -> 打包需要用 `Rust` 环境,如果没有 `Rust`,会提示确认安装。如遇安装失败或超时,可[自行安装](https://www.rust-lang.org/tools/install)。 +> **Note**: Packaging requires the Rust environment. If Rust is not installed, you will be prompted for installation confirmation. In case of installation failure or timeout, you can [install it manually](https://www.rust-lang.org/tools/install). ### url -url 为你需要打包的网页链接 🔗 或者本地 html 文件,必须提供。 +The URL is the link to the web page you want to package or the path to a local HTML file. This is mandatory. ### [options] -提供了一些特定的选项,打包时可以传递对应参数达到定制化的效果。 +Various options are available for customization. You can pass corresponding arguments during packaging to achieve the desired configuration. #### [name] -应用名称,如输入时未指定,会提示你输入,尽量使用英语。 +Specify the application name. If not provided, you will be prompted to enter it. It is recommended to use English. ```shell --name -# 或者 +# or -n ``` #### [icon] -应用 icon,支持本地/远程文件,默认为 Pake 自带图标,定制的可以去 [icon-icons](https://icon-icons.com) 或 [macOSicons](https://macosicons.com/#/) 搜索下载。 +Specify the application icon. Supports both local and remote files. By default, it uses the Pake brand icon. For custom icons, visit [icon icons](https://icon-icons.com) or [macOSicons](https://macosicons.com/#/). -- MacOS 下必须为 `.icns` -- Windows 下必须为 `.ico` -- Linux 下必须为 `.png` +- For macOS, use `.icns` format. +- For Windows, use `.ico` format. +- For Linux, use `.png` format. ```shell --icon -# 或者 --i ``` #### [height] -打包后的应用窗口高度,默认 `780px`。 +Set the height of the application window. Default is `780px`. ```shell --height -# 或者 --h ``` #### [width] -打包后的应用窗口宽度,默认 `1200px`。 +Set the width of the application window. Default is `1200px`. ```shell --width -# 或者 --w ``` #### [transparent] -是否开启沉浸式头部,默认为 `false` 不开启,输入下面的命令则开启沉浸式,推荐 MacOS 用户开启。 +Enable or disable immersive header. Default is `false`. Use the following command to enable this feature. + +``` ```shell --transparent ``` -#### [resize] - -是否可以拖动大小,默认为 `true` 可拖动,输入下面的命令则不能对窗口大小进行拉伸。 - -```shell ---no-resizable -``` - #### [fullscreen] -打开应用后是否开启全屏,默认为 `false`,输入下面的命令则会自动全屏。 +Determine whether the application launches in full screen. Default is `false`. Use the following command to enable full screen. ```shell --fullscreen ``` +#### [resize] + +Determine whether the window is resizable. Default is `true`. Use the following command to disable window resizing. + +```shell +--no-resizable +``` + #### [multi-arch] -打包结果同时支持英特尔和 m1 芯片,仅适用于 MacOS,默认为 `false`。 +Package the application to support both Intel and M1 chips, exclusively for macOS. Default is `false`. -##### 准备工作 +##### Prerequisites -- 注意:开启该选项后,需要用 rust 官网的 rustup 安装 rust,不支持 brew 安装。 -- 对于 intel 芯片用户,需要安装 arm64 跨平台包,使安装包支持 m1 芯片,使用下面命令安装。 +- Note: After enabling this option, Rust must be installed using rustup from the official Rust website. Installation via brew is not supported. +- For Intel chip users, install the arm64 cross-platform package to support M1 chips using the following command: ```shell rustup target add aarch64-apple-darwin ``` -- 对于 M1 芯片用户,需要安装 x86 跨平台包,使安装包支持 interl 芯片,使用下面的命令安装。 +- For M1 chip users, install the x86 cross-platform package to support Intel chips using the following command: ```shell rustup target add x86_64-apple-darwin ``` -##### 使用方法 +##### Usage ```shell --multi-arch -# 或者 --m ``` #### [targets] -选择输出的包格式,支持 deb/appimage/all,如果选择 all,则同时打包 deb 和 appimage,该选项仅支持 Linux,默认为`all`。 +Select the output package format for Linux. Options include `deb`, `appimage`, or `all`. If `all` is selected, both `deb` and `appimage` will be packaged. Default is `all`. ```shell ---targets xxx +--targets ``` #### [user-agent] -自定义浏览器请求头, 默认为空。 +Customize the browser user agent. Default is empty. ```shell --user-agent @@ -167,7 +160,7 @@ rustup target add x86_64-apple-darwin #### [show-menu] -显示菜单栏, 默认不显示,输入下面的命令则会显示,推荐MacOS用户开启。 +Display the menu bar. Default is not to display. Use the following command to enable the menu bar. Recommended for macOS users. ```shell --show-menu @@ -175,7 +168,7 @@ rustup target add x86_64-apple-darwin #### [show-system-tray] -显示通知栏托盘, 默认不显示,输入下面的命令则会显示。 +Display the system tray. Default is not to display. Use the following command to enable the system tray. ```shell --show-system-tray @@ -183,16 +176,21 @@ rustup target add x86_64-apple-darwin #### [system-tray-icon] -通知栏托盘图标,仅当显示通知栏托盘时有效, 图标必须为.ico或者.png格式的,32x32~256x256像素的图片。 +Specify the system tray icon. This is only effective when the system tray is enabled. The icon must be in `.ico` or `.png` format and should be an image with dimensions ranging from 32x32 to 256x256 pixels. ```shell ---system-tray-icon +--system-tray-icon ``` #### [copy-iter-file] -递归拷贝,当url为本地文件路径时候,若开启该选项,则将url路径文件所在文件夹以及所有子文件都拷贝到pake静态文件夹,默认不开启 +Enable recursive copying. When the URL is a local file path, enabling this option will copy the folder containing the file specified in the URL, as well as all sub-files, to the Pake static folder. This is disabled by default. ```shell --copy-iter-file ``` + +## Conclusion + +After completing the above steps, your application should be successfully packaged. Please note that the packaging process may take some time depending on your system configuration and network conditions. Be patient, and once the packaging is complete, you can find the application installer in the specified directory. + diff --git a/bin/README_CN.md b/bin/README_CN.md new file mode 100644 index 0000000..337486d --- /dev/null +++ b/bin/README_CN.md @@ -0,0 +1,196 @@ +## 安装 + +请确保您的 Node.js 版本为 16 或更高版本(例如 16.8)。请避免使用 `sudo` 进行安装。如果 npm 报告权限问题,请参考 [如何在不使用 sudo 的情况下修复 npm 报错](https://stackoverflow.com/questions/16151018/how-to-fix-npm-throwing-error-without-sudo)。 + +```bash +npm install pake-cli -g +``` + +## Windows/Linux 注意事项 + +- **非常重要**:请参阅 Tauri 的 [依赖项指南](https://tauri.app/v1/guides/getting-started/prerequisites)。 +- 对于 Windows 用户,请确保至少安装了 `Win10 SDK(10.0.19041.0)` 和 `Visual Studio Build Tools 2022(版本 17.2 或更高)` + 。此外,还需要安装以下组件: + + 1. Microsoft Visual C++ 2015-2022 Redistributable (x64) + 2. Microsoft Visual C++ 2015-2022 Redistributable (x86) + 3. Microsoft Visual C++ 2012 Redistributable (x86)(可选) + 4. Microsoft Visual C++ 2013 Redistributable (x86)(可选) + 5. Microsoft Visual C++ 2008 Redistributable (x86)(可选) + +- 对于 Ubuntu 用户,在开始之前,建议运行以下命令以安装所需的依赖项: + + ```bash + sudo apt install libdbus-1-dev \ + libsoup2.4-dev \ + libjavascriptcoregtk-4.0-dev \ + libwebkit2gtk-4.0-dev \ + build-essential \ + curl \ + wget \ + libssl-dev \ + libgtk-3-dev \ + libayatana-appindicator3-dev \ + librsvg2-dev \ + gnome-video-effects \ + gnome-video-effects-extra + ``` + +## 使用方法 + +```bash +pake url [options] +``` + +应用程序的打包结果将默认保存在当前工作目录。由于首次打包需要配置环境,这可能需要一些时间,请耐心等待。 + +> **注意**:打包过程需要使用 `Rust` 环境。如果您没有安装 `Rust`,系统会提示您是否要安装。如果遇到安装失败或超时的问题,您可以 [手动安装](https://www.rust-lang.org/tools/install)。 + +### url + +`url` 是您需要打包的网页链接 🔗 或本地 HTML 文件的路径,此参数为必填。 + +### [options] + +您可以通过传递以下选项来定制打包过程: + +#### [name] + +指定应用程序的名称。如果在输入时未指定,系统会提示您输入。建议使用英文名称。 + +```shell +--name +``` + +#### [icon] + +指定应用程序的图标,支持本地或远程文件。默认使用 Pake 的内置图标。您可以访问 [icon-icons](https://icon-icons.com) +或 [macOSicons](https://macosicons.com/#/) 下载自定义图标。 + +- MacOS 要求使用 `.icns` 格式。 +- Windows 要求使用 `.ico` 格式。 +- Linux 要求使用 `.png` 格式。 + +```shell +--icon +``` + +#### [height] + +设置应用窗口的高度,默认为 `780px`。 + +```shell +--height +``` + +#### [width] + +设置应用窗口的宽度,默认为 `1200px`。 + +```shell +--width +``` + +#### [transparent] + +设置是否启用沉浸式头部,默认为 `false`(不启用)。在 MacOS 上推荐启用此选项。 + +```shell +--transparent +``` + +#### [resize] + +设置应用窗口是否可以调整大小,默认为 `true`(可调整)。使用以下命令可以禁止调整窗口大小。 + +```shell +--no-resizable +``` + +#### [fullscreen] + +设置应用程序是否在启动时自动全屏,默认为 `false`。使用以下命令可以设置应用程序启动时自动全屏。 + +```shell +--fullscreen +``` + +#### [multi-arch] + +设置打包结果同时支持 Intel 和 M1 芯片,仅适用于 MacOS,默认为 `false`。 + +##### 准备工作 + +- 注意:启用此选项后,需要使用 rust 官网的 rustup 安装 rust,不支持通过 brew 安装。 +- 对于 Intel 芯片用户,需要安装 arm64 跨平台包,以使安装包支持 M1 芯片。使用以下命令安装: + +```shell +rustup target add aarch64-apple-darwin +``` + +- 对于 M1 芯片用户,需要安装 x86 跨平台包,以使安装包支持 Intel 芯片。使用以下命令安装: + +```shell +rustup target add x86_64-apple-darwin +``` + +##### 使用方法 + +```shell +--multi-arch +``` + +#### [targets] + +选择输出的包格式,支持 `deb`、`appimage` 或 `all`。如果选择 `all`,则会同时打包 `deb` 和 `appimage`。此选项仅适用于 +Linux,默认为 `all`。 + +```shell +--targets +``` + +#### [user-agent] + +自定义浏览器的用户代理请求头,默认为空。 + +```shell +--user-agent +``` + +#### [show-menu] + +设置是否显示菜单栏,默认不显示。在 MacOS 上推荐启用此选项。 + +```shell +--show-menu +``` + +#### [show-system-tray] + +设置是否显示通知栏托盘,默认不显示。 + +```shell +--show-system-tray +``` + +#### [system-tray-icon] + +设置通知栏托盘图标,仅在启用通知栏托盘时有效。图标必须为 `.ico` 或 `.png` 格式,分辨率为 32x32 到 256x256 像素。 + +```shell +--system-tray-icon +``` + +#### [copy-iter-file] + +当 `url` 为本地文件路径时,如果启用此选项,则会递归地将 `url` 路径文件所在的文件夹及其所有子文件复 + +制到 Pake 的静态文件夹。默认不启用。 + +```shell +--copy-iter-file +``` + +## 结语 + +完成上述步骤后,您的应用程序应该已经成功打包。请注意,根据您的系统配置和网络状况,打包过程可能需要一些时间。请耐心等待,一旦打包完成,您就可以在指定的目录中找到应用程序安装包。 diff --git a/bin/README_EN.md b/bin/README_EN.md deleted file mode 100644 index 9d21cea..0000000 --- a/bin/README_EN.md +++ /dev/null @@ -1,202 +0,0 @@ -## Install - -Ensure the version of your installed Node.js is greater than `16.0` such as `16.8`. Do not use `sudo` to install. If you encountered permission issues/problems while installing using npm, see [How to fix npm throwing error without sudo](https://stackoverflow.com/questions/16151018/how-to-fix-npm-throwing-error-without-sudo). - -```bash -npm install -g pake-cli -``` - -## Notes for Windows & Linux users - -- **VERY IMPORTANT**: Check out [the Tauri prerequisites](https://tauri.app/v1/guides/getting-started/prerequisites) before proceeding. -- For Windows users who had been installed `Win10 SDK (10.0.19041.0)` and `Visual Studio build tool 2022(>=17.2)`, you may need to install these additionally: - - 1. Microsoft Visual C++ 2015-2022 Redistributable (x64) - 2. Microsoft Visual C++ 2015-2022 Redistributable (x86) - 3. Microsoft Visual C++ 2012 Redistributable (x86) (optional) - 4. Microsoft Visual C++ 2013 Redistributable (x86) (optional) - 5. Microsoft Visual C++ 2008 Redistributable (x86) (optional) - -- For Ubuntu users, run the following commands to install the required libraries before compiling: - - ```bash - sudo apt install libdbus-1-dev \ - libsoup2.4-dev \ - libjavascriptcoregtk-4.0-dev \ - libwebkit2gtk-4.0-dev \ - build-essential \ - curl \ - wget \ - libssl-dev \ - libgtk-3-dev \ - libayatana-appindicator3-dev \ - librsvg2-dev \ - gnome-video-effects \ - gnome-video-effects-extra - ``` - -## Usage - -```bash -pake url [options] -``` - -The packaged application will be placed in the current working directory by default. Since the environment needs to be configured for the first packaging, it will take some time. Please wait patiently. - -> **Note**: -> The Rust environment is required for packaging. If you have not installed Rust, you will be prompted to confirm the installation. If the installation fails or times out, you can [install](https://www.rust-lang.org/tools/install) by yourself. - -### url - -The url🔗 is the link to the website you want to package. Required. - -### [options] - -We provide some options for customization. When packaging, the corresponding arguments can be passed to configure your app. - -#### [name] - -The name of your application. We will prompt you to enter this if you do not provide it in this phase. Input must be in English. - -```shell ---name -# or --n -``` - -#### [icon] - -The application icon. Supports local and remote files. By default, it is the brand icon of Pake. For customizing the icon of your product, go to [icon icons](https://icon-icons.com) or [macOSicons](https://macosicons.com/#/) to download it. - -- macOS must be `.icns` -- Windows must be `.ico` -- Linux must be `.png` - -```shell ---icon -# or --i -``` - -#### [height] - -The height of the packaged application window. The default is `780px`. - -```shell ---height -# or --h -``` - -#### [width] - -The width of the packaged application window. The default is `1200px`. - -```shell ---width -# or --w -``` - -#### [transparent] - -Whether to enable the immersive header. The default is `false`. Use the command below to enable this feature. - -```shell ---transparent -``` - -#### [fullscreen] - -Indicates if the window should be full screen on application launch. The default is `false`. -Use the command below to enable this feature. - -```shell ---fullscreen -``` - -#### [resize] - -Indicates if the window can be resized. The default value is `true`. -Use the command below to disable this feature. - -```shell ---no-resizable -#or --r -``` - -#### [multi-arch] - -Package results support both Intel and m1 chips, only for MacOS. The default is `false`. - -```shell ---targets xxx -``` - -##### Preparation - -- Note: After enabling this option, you need to use rustup on the rust official website to install rust, brew installation is not supported. -- For intel chip users, you need to install the arm64 cross-platform package to make the installation package support the m1 chip, and use the following command to install. - -```shell -rustup target add aarch64-apple-darwin -``` - -- For M1 chip users, you need to install the x86 cross-platform package to make the installation package support the interl chip, and use the following command to install. - -```shell -rustup target add x86_64-apple-darwin -``` - -##### Instructions - -```shell ---multi-arch -# or --m -``` - -#### [targets] - -Select the output package format, support deb/appimage/all, if all is selected, deb and appimage will be packaged at the same time, this option only supports Linux, the default is `all`. - -#### [user-agent] - -Custom browser user agent, default is empty. - -```shell ---user-agent -``` - -#### [show-menu] - -Display the menu bar, not display it by default, enter the following command and it will be displayed. MacOS users are recommended to enable. - -```shell ---show-menu -``` - -#### [show-system-tray] - -Display the notification tray, not display it by default, entering the following command will display. - -```shell ---show-system-tray -``` - -#### [system-tray-icon] - -The notification tray icon is only valid when the notification tray is displayed. The icon must be a 32x32~256x256 pixel image in .ico or .png format. - -```shell ---system-tray-icon -``` - -#### [copy-iter-file] - -Recursive copy, when the url is a local file path, if this option is enabled, the folder where the url path file is located and all sub-files are copied to the pake static folder, which is not enabled by default - -```shell ---copy-iter-file -``` diff --git a/bin/builders/BaseBuilder.ts b/bin/builders/BaseBuilder.ts new file mode 100644 index 0000000..645c73c --- /dev/null +++ b/bin/builders/BaseBuilder.ts @@ -0,0 +1,56 @@ +import path from 'path'; +import fsExtra from "fs-extra"; +import prompts from 'prompts'; + +import { shellExec } from '@/utils/shell'; +import { isChinaDomain } from '@/utils/ip'; +import logger from '@/options/logger'; +import { checkRustInstalled, installRust } from '@/helpers/rust'; +import { PakeAppOptions } from '@/types'; +import { IS_MAC } from "@/utils/platform"; + +export default abstract class BaseBuilder { + abstract build(url: string, options: PakeAppOptions): Promise; + + async prepare() { + + // Windows and Linux need to install necessary build tools. + if (!IS_MAC) { + logger.info('Install Rust and required build tools to build the app.'); + logger.info('See more in https://tauri.app/v1/guides/getting-started/prerequisites#installing.'); + } + + if (checkRustInstalled()) { + return; + } + + const res = await prompts({ + type: 'confirm', + message: 'Rust not detected. Install now?', + name: 'value', + }); + + if (res.value) { + await installRust(); + } else { + logger.error('Error: Rust required to package your webapp!'); + process.exit(2); + } + } + + protected async runBuildCommand(directory: string, command: string) { + const isChina = await isChinaDomain("www.npmjs.com"); + if (isChina) { + logger.info("Located in China, using npm/Rust CN mirror."); + const rustProjectDir = path.join(directory, 'src-tauri', ".cargo"); + await fsExtra.ensureDir(rustProjectDir); + const projectCnConf = path.join(directory, "src-tauri", "rust_proxy.toml"); + const projectConf = path.join(rustProjectDir, "config"); + await fsExtra.copy(projectCnConf, projectConf); + + await shellExec(`cd "${directory}" && npm install --registry=https://registry.npmmirror.com && ${command}`); + } else { + await shellExec(`cd "${directory}" && npm install && ${command}`); + } + } +} diff --git a/bin/builders/BuilderFactory.ts b/bin/builders/BuilderFactory.ts deleted file mode 100644 index 759ef70..0000000 --- a/bin/builders/BuilderFactory.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { IS_MAC, IS_WIN, IS_LINUX } from '@/utils/platform.js'; -import { IBuilder } from './base.js'; -import MacBuilder from './MacBuilder.js'; -import WinBuilder from './WinBulider.js'; -import LinuxBuilder from './LinuxBuilder.js'; - -export default class BuilderFactory { - static create(): IBuilder { - if (IS_MAC) { - return new MacBuilder(); - } - if (IS_WIN) { - return new WinBuilder(); - } - if (IS_LINUX) { - return new LinuxBuilder(); - } - throw new Error('The current system does not support!!'); - } -} diff --git a/bin/builders/BuilderProvider.ts b/bin/builders/BuilderProvider.ts new file mode 100644 index 0000000..a13c352 --- /dev/null +++ b/bin/builders/BuilderProvider.ts @@ -0,0 +1,21 @@ +import BaseBuilder from './BaseBuilder'; +import MacBuilder from './MacBuilder'; +import WinBuilder from './WinBuilder'; +import LinuxBuilder from './LinuxBuilder'; + +import { IS_MAC, IS_WIN, IS_LINUX } from '@/utils/platform'; + +export default class BuilderProvider { + static create(): BaseBuilder { + if (IS_MAC) { + return new MacBuilder(); + } + if (IS_WIN) { + return new WinBuilder(); + } + if (IS_LINUX) { + return new LinuxBuilder(); + } + throw new Error('The current system is not supported!'); + } +} diff --git a/bin/builders/LinuxBuilder.ts b/bin/builders/LinuxBuilder.ts index a98e05f..ccae449 100644 --- a/bin/builders/LinuxBuilder.ts +++ b/bin/builders/LinuxBuilder.ts @@ -1,92 +1,39 @@ -import fs from 'fs/promises'; import path from 'path'; -import prompts from 'prompts'; -import { checkRustInstalled, installRust } from '@/helpers/rust.js'; -import { PakeAppOptions } from '@/types.js'; -import { IBuilder } from './base.js'; -import { shellExec } from '@/utils/shell.js'; -import {isChinaDomain} from '@/utils/ip_addr.js'; -// @ts-expect-error 加上resolveJsonModule rollup会打包报错 -// import tauriConf from '../../src-tauri/tauri.windows.conf.json'; -import tauriConf from './tauriConf.js'; +import fsExtra from "fs-extra"; +import BaseBuilder from './BaseBuilder'; -import { fileURLToPath } from 'url'; -import logger from '@/options/logger.js'; -import { mergeTauriConfig } from './common.js'; -import { npmDirectory } from '@/utils/dir.js'; - -export default class LinuxBuilder implements IBuilder { - async prepare() { - logger.info( - 'To build the Linux app, you need to install Rust and Linux package' - ); - logger.info( - 'See more in https://tauri.app/v1/guides/getting-started/prerequisites#installing\n' - ); - if (checkRustInstalled()) { - return; - } - - const res = await prompts({ - type: 'confirm', - message: 'We detected that you have not installed Rust. Install it now?', - name: 'value', - }); - - if (res.value) { - // TODO 国内有可能会超时 - await installRust(); - } else { - logger.error('Error: Pake needs Rust to package your webapp!!!'); - process.exit(2); - } - } +import logger from '@/options/logger'; +import tauriConfig from '@/helpers/tauriConfig'; +import { npmDirectory } from '@/utils/dir'; +import { PakeAppOptions } from '@/types'; +import { mergeConfig } from "@/builders/common"; +export default class LinuxBuilder extends BaseBuilder { async build(url: string, options: PakeAppOptions) { - logger.debug('PakeAppOptions', options); const { name } = options; - await mergeTauriConfig(url, options, tauriConf); - const isChina = await isChinaDomain("www.npmjs.com"); - if (isChina) { - logger.info("it's in China, use npm/rust cn mirror") - const rust_project_dir = path.join(npmDirectory, 'src-tauri', ".cargo"); - const e1 = fs.access(rust_project_dir); - if (e1) { - await fs.mkdir(rust_project_dir, { recursive: true }); - } - const project_cn_conf = path.join(npmDirectory, "src-tauri", "cn_config.bak"); - const project_conf = path.join(rust_project_dir, "config"); - fs.copyFile(project_cn_conf, project_conf); + await mergeConfig(url, options, tauriConfig); + await this.runBuildCommand(npmDirectory, 'npm run build'); + + const arch = process.arch === "x64" ? "amd64" : process.arch; - const _ = await shellExec( - `cd "${npmDirectory}" && npm install --registry=https://registry.npmmirror.com && npm run build` - ); - } else { - const _ = await shellExec(`cd "${npmDirectory}" && npm install && npm run build`); - } - let arch: string; - if (process.arch === "x64") { - arch = "amd64"; - } else { - arch = process.arch; - } if (options.targets === "deb" || options.targets === "all") { - const debName = `${name}_${tauriConf.package.version}_${arch}.deb`; + const debName = `${name}_${tauriConfig.package.version}_${arch}.deb`; const appPath = this.getBuildAppPath(npmDirectory, "deb", debName); const distPath = path.resolve(`${name}.deb`); - await fs.copyFile(appPath, distPath); - await fs.unlink(appPath); + await fsExtra.copy(appPath, distPath); + await fsExtra.remove(appPath); logger.success('Build Deb success!'); - logger.success('You can find the deb app installer in', distPath); + logger.success('Deb app installer located in', distPath); } + if (options.targets === "appimage" || options.targets === "all") { - const appImageName = `${name}_${tauriConf.package.version}_${arch}.AppImage`; + const appImageName = `${name}_${tauriConfig.package.version}_${arch}.AppImage`; const appImagePath = this.getBuildAppPath(npmDirectory, "appimage", appImageName); const distAppPath = path.resolve(`${name}.AppImage`); - await fs.copyFile(appImagePath, distAppPath); - await fs.unlink(appImagePath); + await fsExtra.copy(appImagePath, distAppPath); + await fsExtra.remove(appImagePath); logger.success('Build AppImage success!'); - logger.success('You can find the AppImage app installer in', distAppPath); + logger.success('AppImage installer located in', distAppPath); } } diff --git a/bin/builders/MacBuilder.ts b/bin/builders/MacBuilder.ts index 9572079..8f6e8ef 100644 --- a/bin/builders/MacBuilder.ts +++ b/bin/builders/MacBuilder.ts @@ -1,99 +1,36 @@ -import fs from 'fs/promises'; import path from 'path'; -import prompts from 'prompts'; -import { checkRustInstalled, installRust } from '@/helpers/rust.js'; -import { PakeAppOptions } from '@/types.js'; -import { IBuilder } from './base.js'; -import { shellExec } from '@/utils/shell.js'; -// @ts-expect-error 加上resolveJsonModule rollup会打包报错 -// import tauriConf from '../../src-tauri/tauri.macos.conf.json'; -import tauriConf from './tauriConf.js'; -import log from 'loglevel'; -import { mergeTauriConfig } from './common.js'; -import { npmDirectory } from '@/utils/dir.js'; -import {isChinaDomain} from '@/utils/ip_addr.js'; -import logger from '@/options/logger.js'; +import fsExtra from "fs-extra"; -export default class MacBuilder implements IBuilder { - async prepare() { - if (checkRustInstalled()) { - return; - } - - const res = await prompts({ - type: 'confirm', - message: 'We detected that you have not installed Rust. Install it now?', - name: 'value', - }); - - if (res.value) { - // TODO 国内有可能会超时 - await installRust(); - } else { - log.error('Error: Pake need Rust to package your webapp!!!'); - process.exit(2); - } - } +import logger from '@/options/logger'; +import tauriConfig from '@/helpers/tauriConfig'; +import BaseBuilder from './BaseBuilder'; +import { npmDirectory } from '@/utils/dir'; +import { PakeAppOptions } from '@/types'; +import { mergeConfig } from "@/builders/common"; +export default class MacBuilder extends BaseBuilder { async build(url: string, options: PakeAppOptions) { - log.debug('PakeAppOptions', options); const { name } = options; - - await mergeTauriConfig(url, options, tauriConf); + await mergeConfig(url, options, tauriConfig); let dmgName: string; if (options.multiArch) { - const isChina = await isChinaDomain("www.npmjs.com"); - if (isChina) { - logger.info("it's in China, use npm/rust cn mirror") - const rust_project_dir = path.join(npmDirectory, 'src-tauri', ".cargo"); - const e1 = fs.access(rust_project_dir); - if (e1) { - await fs.mkdir(rust_project_dir, { recursive: true }); - } - const project_cn_conf = path.join(npmDirectory, "src-tauri", "cn_config.bak"); - const project_conf = path.join(rust_project_dir, "config"); - fs.copyFile(project_cn_conf, project_conf); - - const _ = await shellExec( - `cd "${npmDirectory}" && npm install --registry=https://registry.npmmirror.com && npm run build:mac` - ); - } else { - const _ = await shellExec(`cd "${npmDirectory}" && npm install && npm run build:mac`); - } - dmgName = `${name}_${tauriConf.package.version}_universal.dmg`; + await this.runBuildCommand(npmDirectory, 'npm run build:mac'); + dmgName = `${name}_${tauriConfig.package.version}_universal.dmg`; } else { - const isChina = isChinaDomain("www.npmjs.com") - if (isChina) { - const _ = await shellExec( - `cd ${npmDirectory} && npm install --registry=https://registry.npmmirror.com && npm run build` - ); - } else { - const _ = await shellExec(`cd ${npmDirectory} && npm install && npm run build`); - } - let arch = "x64"; - if (process.arch === "arm64") { - arch = "aarch64"; - } else { - arch = process.arch; - } - dmgName = `${name}_${tauriConf.package.version}_${arch}.dmg`; + await this.runBuildCommand(npmDirectory, 'npm run build'); + let arch = process.arch === "arm64" ? "aarch64" : process.arch; + dmgName = `${name}_${tauriConfig.package.version}_${arch}.dmg`; } const appPath = this.getBuildAppPath(npmDirectory, dmgName, options.multiArch); const distPath = path.resolve(`${name}.dmg`); - await fs.copyFile(appPath, distPath); - await fs.unlink(appPath); - + await fsExtra.copy(appPath, distPath); + await fsExtra.remove(appPath); logger.success('Build success!'); - logger.success('You can find the app installer in', distPath); + logger.success('App installer located in', distPath); } getBuildAppPath(npmDirectory: string, dmgName: string, multiArch: boolean) { - let dmgPath: string; - if (multiArch) { - dmgPath = 'src-tauri/target/universal-apple-darwin/release/bundle/dmg'; - } else { - dmgPath = 'src-tauri/target/release/bundle/dmg'; - } + const dmgPath = multiArch ? 'src-tauri/target/universal-apple-darwin/release/bundle/dmg' : 'src-tauri/target/release/bundle/dmg'; return path.join(npmDirectory, dmgPath, dmgName); } } diff --git a/bin/builders/WinBuilder.ts b/bin/builders/WinBuilder.ts new file mode 100644 index 0000000..f831786 --- /dev/null +++ b/bin/builders/WinBuilder.ts @@ -0,0 +1,35 @@ +import path from 'path'; +import fsExtra from 'fs-extra'; +import BaseBuilder from './BaseBuilder'; + +import logger from '@/options/logger'; +import tauriConfig from '@/helpers/tauriConfig'; +import { npmDirectory } from '@/utils/dir'; +import { PakeAppOptions } from '@/types'; +import { mergeConfig } from '@/builders/common'; + +export default class WinBuilder extends BaseBuilder { + async build(url: string, options: PakeAppOptions) { + const { name } = options; + await mergeConfig(url, options, tauriConfig); + await this.runBuildCommand(npmDirectory, 'npm run build'); + + const language = tauriConfig.tauri.bundle.windows.wix.language[0]; + const arch = process.arch; + const msiName = `${name}_${tauriConfig.package.version}_${arch}_${language}.msi`; + const appPath = this.getBuildAppPath(npmDirectory, msiName); + const distPath = path.resolve(`${name}.msi`); + await fsExtra.copy(appPath, distPath); + await fsExtra.remove(appPath); + logger.success('Build success!'); + logger.success('App installer located in', distPath); + } + + getBuildAppPath(npmDirectory: string, msiName: string) { + return path.join( + npmDirectory, + 'src-tauri/target/release/bundle/msi', + msiName + ); + } +} diff --git a/bin/builders/WinBulider.ts b/bin/builders/WinBulider.ts deleted file mode 100644 index c3a655a..0000000 --- a/bin/builders/WinBulider.ts +++ /dev/null @@ -1,85 +0,0 @@ -import fs from 'fs/promises'; -import path from 'path'; -import prompts from 'prompts'; -import { checkRustInstalled, installRust } from '@/helpers/rust.js'; -import { PakeAppOptions } from '@/types.js'; -import { IBuilder } from './base.js'; -import { shellExec } from '@/utils/shell.js'; -// @ts-expect-error 加上resolveJsonModule rollup会打包报错 -// import tauriConf from '../../src-tauri/tauri.windows.conf.json'; -import tauriConf from './tauriConf.js'; - -import { fileURLToPath } from 'url'; -import logger from '@/options/logger.js'; -import { mergeTauriConfig } from './common.js'; -import { npmDirectory } from '@/utils/dir.js'; -import {isChinaDomain} from '@/utils/ip_addr.js'; - -export default class WinBuilder implements IBuilder { - async prepare() { - logger.info( - 'To build the Windows app, you need to install Rust and VS Build Tools.' - ); - logger.info( - 'See more in https://tauri.app/v1/guides/getting-started/prerequisites#installing\n' - ); - if (checkRustInstalled()) { - return; - } - - const res = await prompts({ - type: 'confirm', - message: 'We detected that you have not installed Rust. Install it now?', - name: 'value', - }); - - if (res.value) { - // TODO 国内有可能会超时 - await installRust(); - } else { - logger.error('Error: Pake needs Rust to package your webapp!!!'); - process.exit(2); - } - } - - async build(url: string, options: PakeAppOptions) { - logger.debug('PakeAppOptions', options); - const { name } = options; - await mergeTauriConfig(url, options, tauriConf); - const isChina = await isChinaDomain("www.npmjs.com") - if (isChina) { - logger.info("it's in China, use npm/rust cn mirror") - const rust_project_dir = path.join(npmDirectory, 'src-tauri', ".cargo"); - const e1 = fs.access(rust_project_dir); - if (e1) { - await fs.mkdir(rust_project_dir, { recursive: true }); - } - const project_cn_conf = path.join(npmDirectory, "src-tauri", "cn_config.bak"); - const project_conf = path.join(rust_project_dir, "config"); - fs.copyFile(project_cn_conf, project_conf); - - const _ = await shellExec( - `cd "${npmDirectory}" && npm install --registry=https://registry.npmmirror.com && npm run build` - ); - } else { - const _ = await shellExec(`cd "${npmDirectory}" && npm install && npm run build`); - } - const language = tauriConf.tauri.bundle.windows.wix.language[0]; - const arch = process.arch; - const msiName = `${name}_${tauriConf.package.version}_${arch}_${language}.msi`; - const appPath = this.getBuildedAppPath(npmDirectory, msiName); - const distPath = path.resolve(`${name}.msi`); - await fs.copyFile(appPath, distPath); - await fs.unlink(appPath); - logger.success('Build success!'); - logger.success('You can find the app installer in', distPath); - } - - getBuildedAppPath(npmDirectory: string, dmgName: string) { - return path.join( - npmDirectory, - 'src-tauri/target/release/bundle/msi', - dmgName - ); - } -} diff --git a/bin/builders/base.ts b/bin/builders/base.ts deleted file mode 100644 index c92e239..0000000 --- a/bin/builders/base.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { PakeAppOptions } from '@/types.js'; - -/** - * Builder接口 - * 不同平台打包过程需要实现 prepare 和 build 方法 - */ -export interface IBuilder { - /** 前置检查 */ - prepare(): Promise; - /** - * 开始打包 - * @param url 打包url - * @param options 配置参数 - */ - build(url: string, options: PakeAppOptions): Promise; -} diff --git a/bin/builders/common.ts b/bin/builders/common.ts index 5136057..66f7a78 100644 --- a/bin/builders/common.ts +++ b/bin/builders/common.ts @@ -1,33 +1,13 @@ -import { PakeAppOptions,TauriConfig } from '@/types.js'; -import prompts from 'prompts'; import path from 'path'; -import fs from 'fs/promises'; -import fs2 from 'fs-extra'; - +import fsExtra from 'fs-extra'; import { npmDirectory } from '@/utils/dir.js'; import logger from '@/options/logger.js'; +import { PakeAppOptions, PlatformMap } from '@/types.js'; - -export async function promptText(message: string, initial?: string) { - const response = await prompts({ - type: 'text', - name: 'content', - message, - initial, - }); - return response.content; -} - -function setSecurityConfigWithUrl(tauriConfig: TauriConfig, url: string) { - const myURL = new URL(url); - const hostname = myURL.hostname; - tauriConfig.tauri.security.dangerousRemoteDomainIpcAccess[0].domain = hostname; -} - -export async function mergeTauriConfig( +export async function mergeConfig( url: string, options: PakeAppOptions, - tauriConf: any + tauriConf: any, ) { const { width, @@ -44,6 +24,9 @@ export async function mergeTauriConfig( name, } = options; + const { platform } = process; + + // Set Windows parameters. const tauriConfWindowOptions = { width, height, @@ -51,267 +34,190 @@ export async function mergeTauriConfig( transparent, resizable, }; - // Package name is valid ? - // for Linux, package name must be a-z, 0-9 or "-", not allow to A-Z and other - if (process.platform === "linux") { - const reg = new RegExp(/[0-9]*[a-z]+[0-9]*\-?[0-9]*[a-z]*[0-9]*\-?[0-9]*[a-z]*[0-9]*/); - if (!reg.test(name) || reg.exec(name)[0].length != name.length) { - logger.error("package name is illegal, it must be lowercase letters, numbers, dashes, and it must contain the lowercase letters.") - logger.error("E.g com-123-xxx, 123pan, pan123,weread, we-read"); - process.exit(); - } - } - if (process.platform === "win32" || process.platform === "darwin" ) { - const reg = new RegExp(/([0-9]*[a-zA-Z]+[0-9]*)+/); - if (!reg.test(name) || reg.exec(name)[0].length != name.length) { - logger.error("package name is illegal, it must be letters, numbers, and it must contain the letters") - logger.error("E.g 123pan,123Pan Pan123,weread, WeRead, WERead"); - process.exit(); - } - } - - // logger.warn(JSON.stringify(tauriConf.pake.windows, null, 4)); Object.assign(tauriConf.pake.windows[0], { url, ...tauriConfWindowOptions }); + + // Determine whether the package name is valid. + // for Linux, package name must be a-z, 0-9 or "-", not allow to A-Z and other + const platformRegexMapping: PlatformMap = { + linux: /[0-9]*[a-z]+[0-9]*\-?[0-9]*[a-z]*[0-9]*\-?[0-9]*[a-z]*[0-9]*/, + default: /([0-9]*[a-zA-Z]+[0-9]*)+/, + }; + + const reg = platformRegexMapping[platform] || platformRegexMapping.default; + const nameCheck = reg.test(name) && reg.exec(name)[0].length === name.length; + + if (!nameCheck) { + const errorMsg = + platform === 'linux' + ? `Package name is invalid. It should only include lowercase letters, numbers, and dashes, and must contain at least one lowercase letter.\n Examples: com-123-xxx, 123pan, pan123, weread, we-read.` + : `Package name is invalid. It should only include letters and numbers, and must contain at least one letter.\n Examples: 123pan, 123Pan, Pan123, weread, WeRead, WERead.`; + + logger.error(errorMsg); + process.exit(); + } tauriConf.package.productName = name; tauriConf.tauri.bundle.identifier = identifier; - // 判断一下url类型,是文件还是网站 - // 如果是文件,并且开启了递归拷贝功能,则需要将该文件以及所在文件夹下的所有文件拷贝到src目录下,否则只拷贝单个文件。 - const url_exists = await fs.stat(url) - .then(() => true) - .catch(() => false); - if (url_exists) { - logger.warn("you input may a local file"); - tauriConf.pake.windows[0].url_type = "local"; - const file_name = path.basename(url); - const dir_name = path.dirname(url); + // Judge the type of URL, whether it is a file or a website. + // If it is a file and the recursive copy function is enabled then the file and all files in its parent folder need to be copied to the "src" directory. Otherwise, only the single file will be copied. + const urlExists = await fsExtra.pathExists(url); + + if (urlExists) { + 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 url_path = path.join(npmDirectory,"dist/", file_name); - await fs.copyFile(url, url_path); + const urlPath = path.join(distDir, fileName); + await fsExtra.copy(url, urlPath); } else { - const old_dir = path.join(npmDirectory,"dist/"); - const new_dir = path.join(npmDirectory,"dist_bak/"); - fs2.moveSync(old_dir, new_dir, {"overwrite": true}); - fs2.copySync(dir_name, old_dir, {"overwrite": true}); - // logger.warn("dir name", dir_name); - // 将dist_bak里面的cli.js和about_pake.html拷贝回去 - const cli_path = path.join(new_dir, "cli.js") - const cli_path_target = path.join(old_dir, "cli.js") - const about_pake_path = path.join(new_dir, "about_pake.html"); - const about_pake_path_target = path.join(old_dir, "about_pake.html") - fs.copyFile(cli_path, cli_path_target); - fs.copyFile(about_pake_path, about_pake_path_target); + 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 = file_name; - tauriConf.pake.windows[0].url_type = "local"; + + tauriConf.pake.windows[0].url = fileName; + tauriConf.pake.windows[0].url_type = 'local'; } else { - tauriConf.pake.windows[0].url_type = "web"; + 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; } - // 处理user-agent + const platformMap: PlatformMap = { + win32: 'windows', + linux: 'linux', + darwin: 'macos', + }; + const currentPlatform = platformMap[platform]; + if (userAgent.length > 0) { - if (process.platform === "win32") { - tauriConf.pake.user_agent.windows = userAgent; - } - - if (process.platform === "linux") { - tauriConf.pake.user_agent.linux = userAgent; - } - - if (process.platform === "darwin") { - tauriConf.pake.user_agent.macos = userAgent; - } + tauriConf.pake.user_agent[currentPlatform] = userAgent; } - // 处理菜单栏 - if (showMenu) { - if (process.platform === "win32") { - tauriConf.pake.menu.windows = true; - } + tauriConf.pake.menu[currentPlatform] = showMenu; + tauriConf.pake.system_tray[currentPlatform] = showSystemTray; - if (process.platform === "linux") { - tauriConf.pake.menu.linux = true; - } - - if (process.platform === "darwin") { - tauriConf.pake.menu.macos = true; - } - } else { - if (process.platform === "win32") { - tauriConf.pake.menu.windows = false; - } - - if (process.platform === "linux") { - tauriConf.pake.menu.linux = false; - } - - if (process.platform === "darwin") { - tauriConf.pake.menu.macos = false; - } - } - - // 处理托盘 - if (showSystemTray) { - if (process.platform === "win32") { - tauriConf.pake.system_tray.windows = true; - } - - if (process.platform === "linux") { - tauriConf.pake.system_tray.linux = true; - } - - if (process.platform === "darwin") { - tauriConf.pake.system_tray.macos = true; - } - } else { - if (process.platform === "win32") { - tauriConf.pake.system_tray.windows = false; - } - - if (process.platform === "linux") { - tauriConf.pake.system_tray.linux = false; - } - - if (process.platform === "darwin") { - tauriConf.pake.system_tray.macos = false; - } - } - - // 处理targets 暂时只对linux开放 - if (process.platform === "linux") { + // Processing targets are currently only open to Linux. + if (platform === 'linux') { delete tauriConf.tauri.bundle.deb.files; - if (["all", "deb", "appimage"].includes(options.targets)) { - if (options.targets === "all") { - tauriConf.tauri.bundle.targets = ["deb", "appimage"]; - } else { - tauriConf.tauri.bundle.targets = [options.targets]; - } + const validTargets = ['all', 'deb', 'appimage']; + if (validTargets.includes(options.targets)) { + tauriConf.tauri.bundle.targets = options.targets === 'all' ? ['deb', 'appimage'] : [options.targets]; } else { - logger.warn("targets must be 'all', 'deb', 'appimage', we will use default 'all'"); + logger.warn( + `The target must be one of ${validTargets.join(', ')}, the default 'deb' will be used.`, + ); } } - // 处理应用图标 - const exists = await fs.stat(options.icon) - .then(() => true) - .catch(() => false); + // Set icon. + const platformIconMap: PlatformMap = { + win32: { + fileExt: '.ico', + path: `png/${name.toLowerCase()}_32.ico`, + defaultIcon: 'png/icon_256.ico', + message: 'Windows icon must be .ico and 256x256px.', + }, + linux: { + fileExt: '.png', + path: `png/${name.toLowerCase()}_32.png`, + defaultIcon: 'png/icon_512.png', + message: 'Linux icon must be .png and 512x512px.', + }, + darwin: { + fileExt: '.icns', + path: `icons/${name.toLowerCase()}_32.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 (process.platform === "win32") { - if (customIconExt === ".ico") { - const ico_path = path.join(npmDirectory, `src-tauri/png/${name.toLowerCase()}_32.ico`); - tauriConf.tauri.bundle.resources = [`png/${name.toLowerCase()}_32.ico`]; - await fs.copyFile(options.icon, ico_path); - } else { - updateIconPath = false; - logger.warn(`icon file in Windows must be 256 * 256 pix with .ico type, but you give ${customIconExt}`); - tauriConf.tauri.bundle.icon = ["png/icon_256.ico"]; - } - } - if (process.platform === "linux") { - if (customIconExt != ".png") { - updateIconPath = false; - logger.warn(`icon file in Linux must be 512 * 512 pix with .png type, but you give ${customIconExt}`); - tauriConf.tauri.bundle.icon = ["png/icon_512.png"]; - } + + 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 (process.platform === "darwin" && customIconExt !== ".icns") { - updateIconPath = false; - logger.warn(`icon file in MacOS must be .icns type, but you give ${customIconExt}`); - tauriConf.tauri.bundle.icon = ["icons/icon.icns"]; - } if (updateIconPath) { tauriConf.tauri.bundle.icon = [options.icon]; } else { - logger.warn(`icon file will not change with default.`); + logger.warn(`Icon will remain as default.`); } } else { - logger.warn("the custom icon path may not exists. we will use default icon to replace it"); - if (process.platform === "win32") { - tauriConf.tauri.bundle.icon = ["png/icon_256.ico"]; - } - if (process.platform === "linux") { - tauriConf.tauri.bundle.icon = ["png/icon_512.png"]; - } - if (process.platform === "darwin") { - tauriConf.tauri.bundle.icon = ["icons/icon.icns"]; - } + logger.warn( + 'Custom icon path may be invalid. Default icon will be used instead.', + ); + tauriConf.tauri.bundle.icon = [iconInfo.defaultIcon]; } - // 处理托盘自定义图标 - let useDefaultIcon = true; // 是否使用默认托盘图标 + // Set tray icon path. + let trayIconPath = platform === 'darwin' ? 'png/icon_512.png' : tauriConf.tauri.bundle.icon[0]; if (systemTrayIcon.length > 0) { - const icon_exists = await fs.stat(systemTrayIcon) - .then(() => true) - .catch(() => false); - if (icon_exists) { + try { + await fsExtra.pathExists(systemTrayIcon); // 需要判断图标格式,默认只支持ico和png两种 let iconExt = path.extname(systemTrayIcon).toLowerCase(); - if (iconExt == ".png" || iconExt == ".ico") { - useDefaultIcon = false; - const trayIcoPath = path.join(npmDirectory, `src-tauri/png/${name.toLowerCase()}${iconExt}`); - tauriConf.tauri.systemTray.iconPath = `png/${name.toLowerCase()}${iconExt}`; - await fs.copyFile(systemTrayIcon, trayIcoPath); + 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(`file type for system tray icon mut be .ico or .png , but you give ${iconExt}`); - logger.warn(`system tray icon file will not change with default.`); + logger.warn( + `System tray icon must be .ico or .png, but you provided ${iconExt}.`, + ); + logger.warn(`Default system tray icon will be used.`); } - } else { - logger.warn(`${systemTrayIcon} not exists!`) - logger.warn(`system tray icon file will not change with default.`); + } catch { + logger.warn(`${systemTrayIcon} not exists!`); + logger.warn(`Default system tray icon will remain unchanged.`); } } - // 处理托盘默认图标 - if (useDefaultIcon) { - if (process.platform === "linux" || process.platform === "win32") { - tauriConf.tauri.systemTray.iconPath = tauriConf.tauri.bundle.icon[0]; - } else { - tauriConf.tauri.systemTray.iconPath = "png/icon_512.png"; - } - } + tauriConf.tauri.systemTray.iconPath = trayIconPath; - // 设置安全调用 window.__TAURI__ 的安全域名为设置的应用域名 - setSecurityConfigWithUrl(tauriConf, url); + // Save config file. + const platformConfigPaths: PlatformMap = { + 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]); - // 保存配置文件 - let configPath = ""; - switch (process.platform) { - case "win32": { - configPath = path.join(npmDirectory, 'src-tauri/tauri.windows.conf.json'); - break; - } - case "darwin": { - configPath = path.join(npmDirectory, 'src-tauri/tauri.macos.conf.json'); - break; - } - case "linux": { - configPath = path.join(npmDirectory, 'src-tauri/tauri.linux.conf.json'); - break; - } - } + 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 bundleConf = {tauri: {bundle: tauriConf.tauri.bundle}}; - await fs.writeFile( - configPath, - Buffer.from(JSON.stringify(bundleConf, null, 4), 'utf-8') - ); - const pakeConfigPath = path.join(npmDirectory, 'src-tauri/pake.json') - await fs.writeFile( - pakeConfigPath, - Buffer.from(JSON.stringify(tauriConf.pake, null, 4), 'utf-8') - ); - // logger.info("tauri config", JSON.stringify(tauriConf.build)); 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 fs.writeFile( - configJsonPath, - Buffer.from(JSON.stringify(tauriConf2, null, 4), 'utf-8') - ); + const configJsonPath = path.join(npmDirectory, 'src-tauri/tauri.conf.json'); + await fsExtra.writeJson(configJsonPath, tauriConf2, { spaces: 4 }); } diff --git a/bin/builders/tauriConf.js b/bin/builders/tauriConf.js deleted file mode 100644 index a3aa2f0..0000000 --- a/bin/builders/tauriConf.js +++ /dev/null @@ -1,30 +0,0 @@ -import CommonConf from '../../src-tauri/tauri.conf.json'; -import pakeConf from '../../src-tauri/pake.json'; -import WinConf from '../../src-tauri/tauri.windows.conf.json'; -import MacConf from '../../src-tauri/tauri.macos.conf.json'; -import LinuxConf from '../../src-tauri/tauri.linux.conf.json'; - -let tauriConf = { - package: CommonConf.package, - tauri: CommonConf.tauri, - build: CommonConf.build, - pake: pakeConf -} -switch (process.platform) { - case "win32": { - tauriConf.tauri.bundle = WinConf.tauri.bundle; - break; - } - case "darwin": { - tauriConf.tauri.bundle = MacConf.tauri.bundle; - break; - } - case "linux": { - tauriConf.tauri.bundle = LinuxConf.tauri.bundle; - break; - } -} - -export default tauriConf; - - diff --git a/bin/cli.ts b/bin/cli.ts index 35777a9..3c070c2 100644 --- a/bin/cli.ts +++ b/bin/cli.ts @@ -1,51 +1,43 @@ -import { program } from 'commander'; import log from 'loglevel'; -import chalk from 'chalk'; -import { DEFAULT_PAKE_OPTIONS } from './defaults.js'; -import { PakeCliOptions } from './types.js'; -import { validateNumberInput, validateUrlInput } from './utils/validate.js'; -import handleInputOptions from './options/index.js'; -import BuilderFactory from './builders/BuilderFactory.js'; -import { checkUpdateTips } from './helpers/updater.js'; -// @ts-expect-error -import packageJson from '../package.json'; -import logger from './options/logger.js'; +import { program } from 'commander'; -program.version(packageJson.version).description('A cli application can package a web page to desktop application.'); +import { PakeCliOptions } from './types'; +import handleInputOptions from './options/index'; +import BuilderProvider from './builders/BuilderProvider'; +import { checkUpdateTips } from './helpers/updater'; +import packageJson from '../package.json'; +import { validateNumberInput, validateUrlInput } from './utils/validate'; +import { DEFAULT_PAKE_OPTIONS } from './defaults'; program - .showHelpAfterError() - .argument('[url]', 'the web url you want to package', validateUrlInput) - .option('--name ', 'application name') - .option('--icon ', 'application icon', DEFAULT_PAKE_OPTIONS.icon) - .option('--height ', 'window height', validateNumberInput, DEFAULT_PAKE_OPTIONS.height) - .option('--width ', 'window width', validateNumberInput, DEFAULT_PAKE_OPTIONS.width) - .option('--no-resizable', 'whether the window can be resizable', DEFAULT_PAKE_OPTIONS.resizable) - .option('--fullscreen', 'makes the packaged app start in full screen', DEFAULT_PAKE_OPTIONS.fullscreen) - .option('--transparent', 'transparent title bar', DEFAULT_PAKE_OPTIONS.transparent) - .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 all static file to pake app when url is a local file', - DEFAULT_PAKE_OPTIONS.iterCopyFile) - .option( - '-m, --multi-arch', - "available for Mac only, and supports both Intel and M1", - DEFAULT_PAKE_OPTIONS.multiArch - ) - .option( - '--targets ', - 'only for linux, default is "deb", option "appaimge" or "all"(deb & appimage)', - DEFAULT_PAKE_OPTIONS.targets - ) - .option('--debug', 'debug', DEFAULT_PAKE_OPTIONS.transparent) - .action(async (url: string, options: PakeCliOptions) => { - checkUpdateTips(); + .version(packageJson.version) + .description('A CLI that can turn any webpage into a desktop app with Rust.') + .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('--height ', 'Window height', validateNumberInput, DEFAULT_PAKE_OPTIONS.height) + .option('--width ', 'Window width', validateNumberInput, DEFAULT_PAKE_OPTIONS.width) + .option('--no-resizable', 'Whether the window can be resizable', DEFAULT_PAKE_OPTIONS.resizable) + .option('--fullscreen', 'Start the packaged app in full screen', DEFAULT_PAKE_OPTIONS.fullscreen) + .option('--transparent', 'Transparent title bar', DEFAULT_PAKE_OPTIONS.transparent) + .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 all static files to pake app when URL is a local file', DEFAULT_PAKE_OPTIONS.iterCopyFile) + .option('--multi-arch', 'Available for Mac only, supports both Intel and M1', DEFAULT_PAKE_OPTIONS.multiArch) + .option('--targets ', 'Only for Linux, option "deb", "appimage" or "all"', DEFAULT_PAKE_OPTIONS.targets) + .option('--debug', 'Debug mode', DEFAULT_PAKE_OPTIONS.debug) + .action(async (url: string, options: PakeCliOptions) => { + + //Check for update prompt + await checkUpdateTips(); + + // If no URL is provided, display help information if (!url) { - // 直接 pake 不需要出现url提示 program.help(); } @@ -54,12 +46,14 @@ program log.setLevel('debug'); } - const builder = BuilderFactory.create(); + const builder = BuilderProvider.create(); await builder.prepare(); - // logger.warn("you input url is ", url); + const appOptions = await handleInputOptions(options, url); - // logger.info(JSON.stringify(appOptions, null, 4)); - builder.build(url, appOptions); + + log.debug('PakeAppOptions', appOptions); + + await builder.build(url, appOptions); }); program.parse(); diff --git a/bin/defaults.ts b/bin/defaults.ts index 48e4972..108f001 100644 --- a/bin/defaults.ts +++ b/bin/defaults.ts @@ -16,5 +16,3 @@ export const DEFAULT_PAKE_OPTIONS: PakeCliOptions = { systemTrayIcon: '', debug: false, }; - -export const DEFAULT_APP_NAME = 'Pake'; diff --git a/bin/helpers/rust.ts b/bin/helpers/rust.ts index fec21dc..d31d13e 100644 --- a/bin/helpers/rust.ts +++ b/bin/helpers/rust.ts @@ -1,31 +1,27 @@ -import { IS_WIN } from '@/utils/platform.js'; import ora from 'ora'; import shelljs from 'shelljs'; -import logger from '@/options/logger.js'; -import { shellExec } from '../utils/shell.js'; -import {isChinaDomain} from '@/utils/ip_addr.js' +import { IS_WIN } from '@/utils/platform'; +import { shellExec } from '@/utils/shell'; +import { isChinaDomain } from '@/utils/ip'; export async function installRust() { - const is_china = await isChinaDomain("sh.rustup.rs"); - let RustInstallScriptFocMac = ""; - if (is_china) { - logger.info("it's in China, use rust cn mirror to install rust"); - RustInstallScriptFocMac = - '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'; - } else { - RustInstallScriptFocMac = - "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y"; - } - const RustInstallScriptForWin = 'winget install --id Rustlang.Rustup'; + 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 = ora('Downloading Rust').start(); + try { - await shellExec(IS_WIN ? RustInstallScriptForWin : RustInstallScriptFocMac); + await shellExec(IS_WIN ? rustInstallScriptForWindows : rustInstallScriptForMac); spinner.succeed(); } catch (error) { - console.error('install rust return code', error.message); + console.error('Error installing Rust:', error.message); spinner.fail(); + //@ts-ignore process.exit(1); } } diff --git a/bin/helpers/tauriConfig.ts b/bin/helpers/tauriConfig.ts index fb44d61..998dff7 100644 --- a/bin/helpers/tauriConfig.ts +++ b/bin/helpers/tauriConfig.ts @@ -1,8 +1,27 @@ -import crypto from 'crypto'; +import pakeConf from '../../src-tauri/pake.json'; +import CommonConf from '../../src-tauri/tauri.conf.json'; +import WinConf from '../../src-tauri/tauri.windows.conf.json'; +import MacConf from '../../src-tauri/tauri.macos.conf.json'; +import LinuxConf from '../../src-tauri/tauri.linux.conf.json'; -export function getIdentifier(name: string, url: string) { - const hash = crypto.createHash('md5'); - hash.update(url); - const postFixHash = hash.digest('hex').substring(0, 6); - return `pake-${postFixHash}`; -} +const platformConfigs = { + win32: WinConf, + darwin: MacConf, + linux: LinuxConf +}; + +const {platform} = process; +// @ts-ignore +const platformConfig = platformConfigs[platform]; + +let tauriConfig = { + tauri: { + ...CommonConf.tauri, + bundle: platformConfig.tauri.bundle, + }, + package: CommonConf.package, + build: CommonConf.build, + pake: pakeConf +}; + +export default tauriConfig; diff --git a/bin/helpers/updater.ts b/bin/helpers/updater.ts index a0948b7..b9e74e2 100644 --- a/bin/helpers/updater.ts +++ b/bin/helpers/updater.ts @@ -1,5 +1,4 @@ import updateNotifier from 'update-notifier'; -// @ts-expect-error import packageJson from '../../package.json'; export async function checkUpdateTips() { diff --git a/bin/options/icon.ts b/bin/options/icon.ts index 8aff0d6..f52b645 100644 --- a/bin/options/icon.ts +++ b/bin/options/icon.ts @@ -1,98 +1,52 @@ -import axios from 'axios'; -import { fileTypeFromBuffer } from 'file-type'; -import { PakeAppOptions } from '../types.js'; -import { dir } from 'tmp-promise'; import path from 'path'; -import fs from 'fs/promises'; -import logger from './logger.js'; -import { npmDirectory } from '@/utils/dir.js'; -import { IS_LINUX, IS_WIN } from '@/utils/platform.js'; +import axios from 'axios'; +import fsExtra from "fs-extra"; +import { dir } from 'tmp-promise'; +import { fileTypeFromBuffer } from 'file-type'; -export async function handleIcon(options: PakeAppOptions, url: string) { +import logger from './logger'; +import { PakeAppOptions } from '@/types'; +import { npmDirectory } from '@/utils/dir'; +import { IS_LINUX, IS_WIN } from '@/utils/platform'; + +export async function handleIcon(options: PakeAppOptions) { if (options.icon) { if (options.icon.startsWith('http')) { return downloadIcon(options.icon); } else { return path.resolve(options.icon); } - } - if (!options.icon) { - return getDefaultIcon(); + } else { + logger.info('No app icon provided, default icon used. Use --icon option to assign an icon.'); + 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); } } -export async function getDefaultIcon() { - logger.info('You have not provided an app icon, use the default icon.(use --icon option to assign an icon)') - let iconPath = 'src-tauri/icons/icon.icns'; - if (IS_WIN) { - iconPath = 'src-tauri/png/icon_256.ico'; - } else if (IS_LINUX) { - iconPath = 'src-tauri/png/icon_512.png'; - } - - return path.join(npmDirectory, iconPath); -} - -// export async function getIconFromPageUrl(url: string) { -// const icon = await pageIcon(url); -// console.log(icon); -// if (icon.ext === '.ico') { -// const a = await ICO.parse(icon.data); -// icon.data = Buffer.from(a[0].buffer); -// } - -// const iconDir = (await dir()).path; -// const iconPath = path.join(iconDir, `/icon.icns`); - -// const out = png2icons.createICNS(icon.data, png2icons.BILINEAR, 0); - -// await fs.writeFile(iconPath, out); -// return iconPath; -// } - -// export async function getIconFromMacosIcons(name: string) { -// const data = { -// query: name, -// filters: 'approved:true', -// hitsPerPage: 10, -// page: 1, -// }; -// const res = await axios.post('https://p1txh7zfb3-2.algolianet.com/1/indexes/macOSicons/query?x-algolia-agent=Algolia%20for%20JavaScript%20(4.13.1)%3B%20Browser', data, { -// headers: { -// 'x-algolia-api-key': '0ba04276e457028f3e11e38696eab32c', -// 'x-algolia-application-id': 'P1TXH7ZFB3', -// }, -// }); -// if (!res.data.hits.length) { -// return ''; -// } else { -// return downloadIcon(res.data.hits[0].icnsUrl); -// } -// } - export async function downloadIcon(iconUrl: string) { - let iconResponse; try { - iconResponse = await axios.get(iconUrl, { + 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); + return iconPath; } catch (error) { if (error.response && error.response.status === 404) { return null; } throw error; } - - const iconData = await iconResponse.data; - if (!iconData) { - return null; - } - const fileDetails = await fileTypeFromBuffer(iconData); - if (!fileDetails) { - return null; - } - const { path } = await dir(); - const iconPath = `${path}/icon.${fileDetails.ext}`; - await fs.writeFile(iconPath, iconData); - return iconPath; } diff --git a/bin/options/index.ts b/bin/options/index.ts index f259dd5..2237bf6 100644 --- a/bin/options/index.ts +++ b/bin/options/index.ts @@ -1,29 +1,25 @@ -import { promptText } from '@/builders/common.js'; -import { getDomain } from '@/utils/url.js'; -import { getIdentifier } from '../helpers/tauriConfig.js'; -import { PakeAppOptions, PakeCliOptions } from '../types.js'; -import { handleIcon } from './icon.js'; -import fs from 'fs/promises'; +import fsExtra from "fs-extra"; + +import { handleIcon } from './icon'; +import { getDomain } from '@/utils/url'; +import { getIdentifier, promptText } from '@/utils/info'; +import { PakeAppOptions, PakeCliOptions } from '@/types'; export default async function handleOptions(options: PakeCliOptions, url: string): Promise { const appOptions: PakeAppOptions = { ...options, - identifier: '', + identifier: getIdentifier(url), }; - const url_exists = await fs.stat(url) - .then(() => true) - .catch(() => false); + + let urlExists = await fsExtra.pathExists(url); + if (!appOptions.name) { - if (!url_exists) { - appOptions.name = await promptText('please input your application name', getDomain(url)); - } else { - appOptions.name = await promptText('please input your application name', ""); - } + const defaultName = urlExists ? "" : getDomain(url); + const promptMessage = 'Enter your application name'; + appOptions.name = await promptText(promptMessage, defaultName); } - appOptions.identifier = getIdentifier(appOptions.name, url); - - appOptions.icon = await handleIcon(appOptions, url); + appOptions.icon = await handleIcon(appOptions); return appOptions; } diff --git a/bin/types.ts b/bin/types.ts index 79ba8db..20725a2 100644 --- a/bin/types.ts +++ b/bin/types.ts @@ -1,202 +1,55 @@ + +export interface PlatformMap { + [key: string]: any; +} + export interface PakeCliOptions { - /** 应用名称 */ + // Application name name?: string; - /** 应用icon */ + // Application icon icon: string; - /** 应用窗口宽度,默认 1200px */ + // Application window width, default 1200px width: number; - /** 应用窗口高度,默认 780px */ + // Application window height, default 780px height: number; - /** 是否可以拖动,默认true */ + // Whether the window is resizable, default true resizable: boolean; - /** 是否可以全屏,默认 false */ + // Whether the window can be fullscreen, default false fullscreen: boolean; - /** 是否开启沉浸式头部,默认为 false 不开启 ƒ*/ + // Enable immersive header, default false transparent: boolean; - /** 自定义UA,默认为不开启 ƒ*/ + // Custom User-Agent, default off userAgent: string; - /** 开启菜单栏,MacOS默认开启,Windows,Linux默认不开启 ƒ*/ + // Enable menu bar, default on for MacOS, off for Windows and Linux showMenu: boolean; - /** 开启系统托盘,MacOS默认不开启,Windows,Linux默认开启 ƒ*/ + // Enable system tray, default off for MacOS, on for Windows and Linux showSystemTray: boolean; - /** 托盘图标, Windows、Linux默认和应用图标共用一样的,MacOS需要提别提供, 格式为png或者ico */ + // Tray icon, default same as app icon for Windows and Linux, MacOS requires separate png or ico systemTrayIcon: string; - // /** 递归拷贝,当url为本地文件路径时候,若开启该选项,则将url路径文件所在文件夹以及所有子文件都拷贝到pake静态文件夹,默认不开启 */ + // Recursive copy, when url is a local file path, if this option is enabled, the url path file and all its subfiles will be copied to the pake static file folder, default off iterCopyFile: false; - /** mutli arch, Supports both Intel and m1 chips, only for Mac */ + // Multi arch, supports both Intel and M1 chips, only for Mac multiArch: boolean; - // 包输出产物,对linux用户有效,默认为deb,可选appimage, 或者all(即同时输出deb和all); + // Package output, valid for Linux users, default is deb, optional appimage, or all (i.e., output both deb and all); targets: string; - /** 调试模式,会输出更多日志 */ + // Debug mode, outputs more logs debug: boolean; } export interface PakeAppOptions extends PakeCliOptions { identifier: string; } - -export interface TauriBuildConfig { - /** - * the path to the app's dist dir - * this path must contain your index.html file - */ - distDir: string - /** - * the app's dev server URL, or the path to the directory containing an index.html to open - */ - devPath: string - /** - * a shell command to run before `tauri dev` kicks in - */ - beforeDevCommand?: string - /** - * a shell command to run before `tauri build` kicks in - */ - beforeBuildCommand?: string - withGlobalTauri?: boolean -} - -type DangerousRemoteDomainIpAccess = { - domain: string; - windows: string[]; - enableTauriAPI: boolean; - schema?: string; - plugins?: string[]; -} - -/** - * Tauri configuration - */ -export interface TauriConfig { - /** - * build/dev configuration - */ - build: TauriBuildConfig - /** - * the context of the current `tauri dev` or `tauri build` - */ - ctx: { - /** - * whether we're building for production or not - */ - prod?: boolean - /** - * whether we're running on the dev environment or not - */ - dev?: boolean - /** - * the target of the compilation (see `rustup target list`) - */ - target?: string - /** - * whether the app should be built on debug mode or not - */ - debug?: boolean - /** - * defines we should exit the `tauri dev` process if a Rust code error is found - */ - exitOnPanic?: boolean - } - /** - * tauri root configuration object - */ - tauri: { - /** - * the embedded server configuration - */ - embeddedServer: { - /** - * whether we should use the embedded-server or the no-server mode - */ - active?: boolean - /** - * the embedded server port number or the 'random' string to generate one at runtime - */ - port?: number | 'random' | undefined - } - /** - * tauri bundler configuration - */ - bundle: { - /** - * whether we should build your app with tauri-bundler or plain `cargo build` - */ - active?: boolean - /** - * the bundle targets, currently supports ["deb", "osx", "msi", "appimage", "dmg"] or "all" - */ - targets?: string | string[] - /** - * the app's identifier - */ - identifier: string - /** - * the app's icons - */ - icon: string[] - /** - * app resources to bundle - * each resource is a path to a file or directory - * glob patterns are supported - */ - resources?: string[] - externalBin?: string[] - copyright?: string - category?: string - shortDescription?: string - longDescription?: string - deb?: { - depends?: string[] - useBootstrapper?: boolean - } - osx?: { - frameworks?: string[] - minimumSystemVersion?: string - license?: string - useBootstrapper?: boolean - } - exceptionDomain?: string - } - allowlist: { - all: boolean - [index: string]: boolean - } - window: { - title: string - width?: number - height?: number - resizable?: boolean - fullscreen?: boolean - } - security: { - csp?: string, - dangerousRemoteDomainIpcAccess?: DangerousRemoteDomainIpAccess[] - } - inliner: { - active?: boolean - } - } - plugins?: { - [name: string]: { - [key: string]: any - } - } - /** - * Whether or not to enable verbose logging - */ - verbose?: boolean -} diff --git a/bin/utils/dir.ts b/bin/utils/dir.ts index 76da5af..bfc4f29 100644 --- a/bin/utils/dir.ts +++ b/bin/utils/dir.ts @@ -1,8 +1,11 @@ import path from 'path'; import { fileURLToPath } from 'url'; +// Convert the current module URL to a file path +const currentModulePath = fileURLToPath(import.meta.url); +// Resolve the parent directory of the current module export const npmDirectory = path.join( - path.dirname(fileURLToPath(import.meta.url)), + path.dirname(currentModulePath), '..' ); diff --git a/bin/utils/info.ts b/bin/utils/info.ts new file mode 100644 index 0000000..9090437 --- /dev/null +++ b/bin/utils/info.ts @@ -0,0 +1,21 @@ +import crypto from 'crypto'; +import prompts from "prompts"; + +// Generates an identifier based on the given URL. +export function getIdentifier(url: string) { + const postFixHash = crypto.createHash('md5') + .update(url) + .digest('hex') + .substring(0, 6); + return `pake-${postFixHash}`; +} + +export async function promptText(message: string, initial?: string): Promise { + const response = await prompts({ + type: 'text', + name: 'content', + message, + initial, + }); + return response.content; +} diff --git a/bin/utils/ip_addr.ts b/bin/utils/ip.ts similarity index 66% rename from bin/utils/ip_addr.ts rename to bin/utils/ip.ts index 2fd8cac..d8e9b4c 100644 --- a/bin/utils/ip_addr.ts +++ b/bin/utils/ip.ts @@ -1,9 +1,9 @@ -import { exec } from 'child_process'; -import { promisify } from 'util'; -import logger from '@/options/logger.js'; import dns from 'dns'; import http from 'http'; +import { promisify } from 'util'; +import logger from '@/options/logger'; +const resolve = promisify(dns.resolve); const ping = async (host: string) => { const lookup = promisify(dns.lookup); @@ -23,32 +23,25 @@ const ping = async (host: string) => { }); }; - -const resolve = promisify(dns.resolve); - async function isChinaDomain(domain: string): Promise { try { - // 解析域名为IP地址 const [ip] = await resolve(domain); return await isChinaIP(ip, domain); } catch (error) { - // 域名无法解析,返回false logger.info(`${domain} can't be parse!`); return false; } } async function isChinaIP(ip: string, domain: string): Promise { - try { - const delay = await ping(ip); - logger.info(`${domain} latency is ${delay} ms`); - // 判断延迟是否超过500ms - return delay > 500; - } catch (error) { - // 命令执行出错,返回false - logger.info(`ping ${domain} failed!`); - return false; - } + try { + const delay = await ping(ip); + logger.info(`${domain} latency is ${delay} ms`); + return delay > 500; + } catch (error) { + logger.info(`ping ${domain} failed!`); + return false; + } } export { isChinaDomain, isChinaIP }; diff --git a/bin/utils/platform.ts b/bin/utils/platform.ts index c08cd48..2615816 100644 --- a/bin/utils/platform.ts +++ b/bin/utils/platform.ts @@ -1,5 +1,5 @@ -export const IS_MAC = process.platform === 'darwin'; +const { platform } = process; -export const IS_WIN = process.platform === 'win32'; - -export const IS_LINUX = process.platform === 'linux'; +export const IS_MAC = platform === 'darwin'; +export const IS_WIN = platform === 'win32'; +export const IS_LINUX = platform === 'linux'; diff --git a/bin/utils/shell.ts b/bin/utils/shell.ts index 321d6ab..684dc14 100644 --- a/bin/utils/shell.ts +++ b/bin/utils/shell.ts @@ -1,9 +1,9 @@ import shelljs from "shelljs"; -import { npmDirectory } from "./dir.js"; +import { npmDirectory } from "./dir"; export function shellExec(command: string) { return new Promise((resolve, reject) => { - shelljs.exec(command, { async: true, silent: false, cwd: npmDirectory}, (code) => { + shelljs.exec(command, { async: true, silent: false, cwd: npmDirectory }, (code) => { if (code === 0) { resolve(0); } else { diff --git a/bin/utils/tlds.ts b/bin/utils/tlds.ts deleted file mode 100644 index a682d90..0000000 --- a/bin/utils/tlds.ts +++ /dev/null @@ -1,1489 +0,0 @@ -const tlds = [ - "aaa", - "aarp", - "abarth", - "abb", - "abbott", - "abbvie", - "abc", - "able", - "abogado", - "abudhabi", - "ac", - "academy", - "accenture", - "accountant", - "accountants", - "aco", - "actor", - "ad", - "adac", - "ads", - "adult", - "ae", - "aeg", - "aero", - "aetna", - "af", - "afl", - "africa", - "ag", - "agakhan", - "agency", - "ai", - "aig", - "airbus", - "airforce", - "airtel", - "akdn", - "al", - "alfaromeo", - "alibaba", - "alipay", - "allfinanz", - "allstate", - "ally", - "alsace", - "alstom", - "am", - "amazon", - "americanexpress", - "americanfamily", - "amex", - "amfam", - "amica", - "amsterdam", - "analytics", - "android", - "anquan", - "anz", - "ao", - "aol", - "apartments", - "app", - "apple", - "aq", - "aquarelle", - "ar", - "arab", - "aramco", - "archi", - "army", - "arpa", - "art", - "arte", - "as", - "asda", - "asia", - "associates", - "at", - "athleta", - "attorney", - "au", - "auction", - "audi", - "audible", - "audio", - "auspost", - "author", - "auto", - "autos", - "avianca", - "aw", - "aws", - "ax", - "axa", - "az", - "azure", - "ba", - "baby", - "baidu", - "banamex", - "bananarepublic", - "band", - "bank", - "bar", - "barcelona", - "barclaycard", - "barclays", - "barefoot", - "bargains", - "baseball", - "basketball", - "bauhaus", - "bayern", - "bb", - "bbc", - "bbt", - "bbva", - "bcg", - "bcn", - "bd", - "be", - "beats", - "beauty", - "beer", - "bentley", - "berlin", - "best", - "bestbuy", - "bet", - "bf", - "bg", - "bh", - "bharti", - "bi", - "bible", - "bid", - "bike", - "bing", - "bingo", - "bio", - "biz", - "bj", - "black", - "blackfriday", - "blockbuster", - "blog", - "bloomberg", - "blue", - "bm", - "bms", - "bmw", - "bn", - "bnpparibas", - "bo", - "boats", - "boehringer", - "bofa", - "bom", - "bond", - "boo", - "book", - "booking", - "bosch", - "bostik", - "boston", - "bot", - "boutique", - "box", - "br", - "bradesco", - "bridgestone", - "broadway", - "broker", - "brother", - "brussels", - "bs", - "bt", - "build", - "builders", - "business", - "buy", - "buzz", - "bv", - "bw", - "by", - "bz", - "bzh", - "ca", - "cab", - "cafe", - "cal", - "call", - "calvinklein", - "cam", - "camera", - "camp", - "canon", - "capetown", - "capital", - "capitalone", - "car", - "caravan", - "cards", - "care", - "career", - "careers", - "cars", - "casa", - "case", - "cash", - "casino", - "cat", - "catering", - "catholic", - "cba", - "cbn", - "cbre", - "cbs", - "cc", - "cd", - "center", - "ceo", - "cern", - "cf", - "cfa", - "cfd", - "cg", - "ch", - "chanel", - "channel", - "charity", - "chase", - "chat", - "cheap", - "chintai", - "christmas", - "chrome", - "church", - "ci", - "cipriani", - "circle", - "cisco", - "citadel", - "citi", - "citic", - "city", - "cityeats", - "ck", - "cl", - "claims", - "cleaning", - "click", - "clinic", - "clinique", - "clothing", - "cloud", - "club", - "clubmed", - "cm", - "cn", - "co", - "coach", - "codes", - "coffee", - "college", - "cologne", - "com", - "comcast", - "commbank", - "community", - "company", - "compare", - "computer", - "comsec", - "condos", - "construction", - "consulting", - "contact", - "contractors", - "cooking", - "cookingchannel", - "cool", - "coop", - "corsica", - "country", - "coupon", - "coupons", - "courses", - "cpa", - "cr", - "credit", - "creditcard", - "creditunion", - "cricket", - "crown", - "crs", - "cruise", - "cruises", - "cu", - "cuisinella", - "cv", - "cw", - "cx", - "cy", - "cymru", - "cyou", - "cz", - "dabur", - "dad", - "dance", - "data", - "date", - "dating", - "datsun", - "day", - "dclk", - "dds", - "de", - "deal", - "dealer", - "deals", - "degree", - "delivery", - "dell", - "deloitte", - "delta", - "democrat", - "dental", - "dentist", - "desi", - "design", - "dev", - "dhl", - "diamonds", - "diet", - "digital", - "direct", - "directory", - "discount", - "discover", - "dish", - "diy", - "dj", - "dk", - "dm", - "dnp", - "do", - "docs", - "doctor", - "dog", - "domains", - "dot", - "download", - "drive", - "dtv", - "dubai", - "dunlop", - "dupont", - "durban", - "dvag", - "dvr", - "dz", - "earth", - "eat", - "ec", - "eco", - "edeka", - "edu", - "education", - "ee", - "eg", - "email", - "emerck", - "energy", - "engineer", - "engineering", - "enterprises", - "epson", - "equipment", - "er", - "ericsson", - "erni", - "es", - "esq", - "estate", - "et", - "etisalat", - "eu", - "eurovision", - "eus", - "events", - "exchange", - "expert", - "exposed", - "express", - "extraspace", - "fage", - "fail", - "fairwinds", - "faith", - "family", - "fan", - "fans", - "farm", - "farmers", - "fashion", - "fast", - "fedex", - "feedback", - "ferrari", - "ferrero", - "fi", - "fiat", - "fidelity", - "fido", - "film", - "final", - "finance", - "financial", - "fire", - "firestone", - "firmdale", - "fish", - "fishing", - "fit", - "fitness", - "fj", - "fk", - "flickr", - "flights", - "flir", - "florist", - "flowers", - "fly", - "fm", - "fo", - "foo", - "food", - "foodnetwork", - "football", - "ford", - "forex", - "forsale", - "forum", - "foundation", - "fox", - "fr", - "free", - "fresenius", - "frl", - "frogans", - "frontdoor", - "frontier", - "ftr", - "fujitsu", - "fun", - "fund", - "furniture", - "futbol", - "fyi", - "ga", - "gal", - "gallery", - "gallo", - "gallup", - "game", - "games", - "gap", - "garden", - "gay", - "gb", - "gbiz", - "gd", - "gdn", - "ge", - "gea", - "gent", - "genting", - "george", - "gf", - "gg", - "ggee", - "gh", - "gi", - "gift", - "gifts", - "gives", - "giving", - "gl", - "glass", - "gle", - "global", - "globo", - "gm", - "gmail", - "gmbh", - "gmo", - "gmx", - "gn", - "godaddy", - "gold", - "goldpoint", - "golf", - "goo", - "goodyear", - "goog", - "google", - "gop", - "got", - "gov", - "gp", - "gq", - "gr", - "grainger", - "graphics", - "gratis", - "green", - "gripe", - "grocery", - "group", - "gs", - "gt", - "gu", - "guardian", - "gucci", - "guge", - "guide", - "guitars", - "guru", - "gw", - "gy", - "hair", - "hamburg", - "hangout", - "haus", - "hbo", - "hdfc", - "hdfcbank", - "health", - "healthcare", - "help", - "helsinki", - "here", - "hermes", - "hgtv", - "hiphop", - "hisamitsu", - "hitachi", - "hiv", - "hk", - "hkt", - "hm", - "hn", - "hockey", - "holdings", - "holiday", - "homedepot", - "homegoods", - "homes", - "homesense", - "honda", - "horse", - "hospital", - "host", - "hosting", - "hot", - "hoteles", - "hotels", - "hotmail", - "house", - "how", - "hr", - "hsbc", - "ht", - "hu", - "hughes", - "hyatt", - "hyundai", - "ibm", - "icbc", - "ice", - "icu", - "id", - "ie", - "ieee", - "ifm", - "ikano", - "il", - "im", - "imamat", - "imdb", - "immo", - "immobilien", - "in", - "inc", - "industries", - "infiniti", - "info", - "ing", - "ink", - "institute", - "insurance", - "insure", - "int", - "international", - "intuit", - "investments", - "io", - "ipiranga", - "iq", - "ir", - "irish", - "is", - "ismaili", - "ist", - "istanbul", - "it", - "itau", - "itv", - "jaguar", - "java", - "jcb", - "je", - "jeep", - "jetzt", - "jewelry", - "jio", - "jll", - "jm", - "jmp", - "jnj", - "jo", - "jobs", - "joburg", - "jot", - "joy", - "jp", - "jpmorgan", - "jprs", - "juegos", - "juniper", - "kaufen", - "kddi", - "ke", - "kerryhotels", - "kerrylogistics", - "kerryproperties", - "kfh", - "kg", - "kh", - "ki", - "kia", - "kids", - "kim", - "kinder", - "kindle", - "kitchen", - "kiwi", - "km", - "kn", - "koeln", - "komatsu", - "kosher", - "kp", - "kpmg", - "kpn", - "kr", - "krd", - "kred", - "kuokgroup", - "kw", - "ky", - "kyoto", - "kz", - "la", - "lacaixa", - "lamborghini", - "lamer", - "lancaster", - "lancia", - "land", - "landrover", - "lanxess", - "lasalle", - "lat", - "latino", - "latrobe", - "law", - "lawyer", - "lb", - "lc", - "lds", - "lease", - "leclerc", - "lefrak", - "legal", - "lego", - "lexus", - "lgbt", - "li", - "lidl", - "life", - "lifeinsurance", - "lifestyle", - "lighting", - "like", - "lilly", - "limited", - "limo", - "lincoln", - "linde", - "link", - "lipsy", - "live", - "living", - "lk", - "llc", - "llp", - "loan", - "loans", - "locker", - "locus", - "loft", - "lol", - "london", - "lotte", - "lotto", - "love", - "lpl", - "lplfinancial", - "lr", - "ls", - "lt", - "ltd", - "ltda", - "lu", - "lundbeck", - "luxe", - "luxury", - "lv", - "ly", - "ma", - "macys", - "madrid", - "maif", - "maison", - "makeup", - "man", - "management", - "mango", - "map", - "market", - "marketing", - "markets", - "marriott", - "marshalls", - "maserati", - "mattel", - "mba", - "mc", - "mckinsey", - "md", - "me", - "med", - "media", - "meet", - "melbourne", - "meme", - "memorial", - "men", - "menu", - "merckmsd", - "mg", - "mh", - "miami", - "microsoft", - "mil", - "mini", - "mint", - "mit", - "mitsubishi", - "mk", - "ml", - "mlb", - "mls", - "mm", - "mma", - "mn", - "mo", - "mobi", - "mobile", - "moda", - "moe", - "moi", - "mom", - "monash", - "money", - "monster", - "mormon", - "mortgage", - "moscow", - "moto", - "motorcycles", - "mov", - "movie", - "mp", - "mq", - "mr", - "ms", - "msd", - "mt", - "mtn", - "mtr", - "mu", - "museum", - "music", - "mutual", - "mv", - "mw", - "mx", - "my", - "mz", - "na", - "nab", - "nagoya", - "name", - "natura", - "navy", - "nba", - "nc", - "ne", - "nec", - "net", - "netbank", - "netflix", - "network", - "neustar", - "new", - "news", - "next", - "nextdirect", - "nexus", - "nf", - "nfl", - "ng", - "ngo", - "nhk", - "ni", - "nico", - "nike", - "nikon", - "ninja", - "nissan", - "nissay", - "nl", - "no", - "nokia", - "northwesternmutual", - "norton", - "now", - "nowruz", - "nowtv", - "np", - "nr", - "nra", - "nrw", - "ntt", - "nu", - "nyc", - "nz", - "obi", - "observer", - "office", - "okinawa", - "olayan", - "olayangroup", - "oldnavy", - "ollo", - "om", - "omega", - "one", - "ong", - "onl", - "online", - "ooo", - "open", - "oracle", - "orange", - "org", - "organic", - "origins", - "osaka", - "otsuka", - "ott", - "ovh", - "pa", - "page", - "panasonic", - "paris", - "pars", - "partners", - "parts", - "party", - "passagens", - "pay", - "pccw", - "pe", - "pet", - "pf", - "pfizer", - "pg", - "ph", - "pharmacy", - "phd", - "philips", - "phone", - "photo", - "photography", - "photos", - "physio", - "pics", - "pictet", - "pictures", - "pid", - "pin", - "ping", - "pink", - "pioneer", - "pizza", - "pk", - "pl", - "place", - "play", - "playstation", - "plumbing", - "plus", - "pm", - "pn", - "pnc", - "pohl", - "poker", - "politie", - "porn", - "post", - "pr", - "pramerica", - "praxi", - "press", - "prime", - "pro", - "prod", - "productions", - "prof", - "progressive", - "promo", - "properties", - "property", - "protection", - "pru", - "prudential", - "ps", - "pt", - "pub", - "pw", - "pwc", - "py", - "qa", - "qpon", - "quebec", - "quest", - "racing", - "radio", - "re", - "read", - "realestate", - "realtor", - "realty", - "recipes", - "red", - "redstone", - "redumbrella", - "rehab", - "reise", - "reisen", - "reit", - "reliance", - "ren", - "rent", - "rentals", - "repair", - "report", - "republican", - "rest", - "restaurant", - "review", - "reviews", - "rexroth", - "rich", - "richardli", - "ricoh", - "ril", - "rio", - "rip", - "ro", - "rocher", - "rocks", - "rodeo", - "rogers", - "room", - "rs", - "rsvp", - "ru", - "rugby", - "ruhr", - "run", - "rw", - "rwe", - "ryukyu", - "sa", - "saarland", - "safe", - "safety", - "sakura", - "sale", - "salon", - "samsclub", - "samsung", - "sandvik", - "sandvikcoromant", - "sanofi", - "sap", - "sarl", - "sas", - "save", - "saxo", - "sb", - "sbi", - "sbs", - "sc", - "sca", - "scb", - "schaeffler", - "schmidt", - "scholarships", - "school", - "schule", - "schwarz", - "science", - "scot", - "sd", - "se", - "search", - "seat", - "secure", - "security", - "seek", - "select", - "sener", - "services", - "ses", - "seven", - "sew", - "sex", - "sexy", - "sfr", - "sg", - "sh", - "shangrila", - "sharp", - "shaw", - "shell", - "shia", - "shiksha", - "shoes", - "shop", - "shopping", - "shouji", - "show", - "showtime", - "si", - "silk", - "sina", - "singles", - "site", - "sj", - "sk", - "ski", - "skin", - "sky", - "skype", - "sl", - "sling", - "sm", - "smart", - "smile", - "sn", - "sncf", - "so", - "soccer", - "social", - "softbank", - "software", - "sohu", - "solar", - "solutions", - "song", - "sony", - "soy", - "spa", - "space", - "sport", - "spot", - "sr", - "srl", - "ss", - "st", - "stada", - "staples", - "star", - "statebank", - "statefarm", - "stc", - "stcgroup", - "stockholm", - "storage", - "store", - "stream", - "studio", - "study", - "style", - "su", - "sucks", - "supplies", - "supply", - "support", - "surf", - "surgery", - "suzuki", - "sv", - "swatch", - "swiss", - "sx", - "sy", - "sydney", - "systems", - "sz", - "tab", - "taipei", - "talk", - "taobao", - "target", - "tatamotors", - "tatar", - "tattoo", - "tax", - "taxi", - "tc", - "tci", - "td", - "tdk", - "team", - "tech", - "technology", - "tel", - "temasek", - "tennis", - "teva", - "tf", - "tg", - "th", - "thd", - "theater", - "theatre", - "tiaa", - "tickets", - "tienda", - "tiffany", - "tips", - "tires", - "tirol", - "tj", - "tjmaxx", - "tjx", - "tk", - "tkmaxx", - "tl", - "tm", - "tmall", - "tn", - "to", - "today", - "tokyo", - "tools", - "top", - "toray", - "toshiba", - "total", - "tours", - "town", - "toyota", - "toys", - "tr", - "trade", - "trading", - "training", - "travel", - "travelchannel", - "travelers", - "travelersinsurance", - "trust", - "trv", - "tt", - "tube", - "tui", - "tunes", - "tushu", - "tv", - "tvs", - "tw", - "tz", - "ua", - "ubank", - "ubs", - "ug", - "uk", - "unicom", - "university", - "uno", - "uol", - "ups", - "us", - "uy", - "uz", - "va", - "vacations", - "vana", - "vanguard", - "vc", - "ve", - "vegas", - "ventures", - "verisign", - "vermögensberater", - "vermögensberatung", - "versicherung", - "vet", - "vg", - "vi", - "viajes", - "video", - "vig", - "viking", - "villas", - "vin", - "vip", - "virgin", - "visa", - "vision", - "viva", - "vivo", - "vlaanderen", - "vn", - "vodka", - "volkswagen", - "volvo", - "vote", - "voting", - "voto", - "voyage", - "vu", - "vuelos", - "wales", - "walmart", - "walter", - "wang", - "wanggou", - "watch", - "watches", - "weather", - "weatherchannel", - "webcam", - "weber", - "website", - "wed", - "wedding", - "weibo", - "weir", - "wf", - "whoswho", - "wien", - "wiki", - "williamhill", - "win", - "windows", - "wine", - "winners", - "wme", - "wolterskluwer", - "woodside", - "work", - "works", - "world", - "wow", - "ws", - "wtc", - "wtf", - "xbox", - "xerox", - "xfinity", - "xihuan", - "xin", - "xxx", - "xyz", - "yachts", - "yahoo", - "yamaxun", - "yandex", - "ye", - "yodobashi", - "yoga", - "yokohama", - "you", - "youtube", - "yt", - "yun", - "za", - "zappos", - "zara", - "zero", - "zip", - "zm", - "zone", - "zuerich", - "zw", - "ελ", - "ευ", - "бг", - "бел", - "дети", - "ею", - "католик", - "ком", - "мкд", - "мон", - "москва", - "онлайн", - "орг", - "рус", - "рф", - "сайт", - "срб", - "укр", - "қаз", - "հայ", - "ישראל", - "קום", - "ابوظبي", - "اتصالات", - "ارامكو", - "الاردن", - "البحرين", - "الجزائر", - "السعودية", - "العليان", - "المغرب", - "امارات", - "ایران", - "بارت", - "بازار", - "بيتك", - "بھارت", - "تونس", - "سودان", - "سورية", - "شبكة", - "عراق", - "عرب", - "عمان", - "فلسطين", - "قطر", - "كاثوليك", - "كوم", - "مصر", - "مليسيا", - "موريتانيا", - "موقع", - "همراه", - "پاکستان", - "ڀارت", - "कॉम", - "नेट", - "भारत", - "भारतम्", - "भारोत", - "संगठन", - "বাংলা", - "ভারত", - "ভাৰত", - "ਭਾਰਤ", - "ભારત", - "ଭାରତ", - "இந்தியா", - "இலங்கை", - "சிங்கப்பூர்", - "భారత్", - "ಭಾರತ", - "ഭാരതം", - "ලංකා", - "คอม", - "ไทย", - "ລາວ", - "გე", - "みんな", - "アマゾン", - "クラウド", - "グーグル", - "コム", - "ストア", - "セール", - "ファッション", - "ポイント", - "世界", - "中信", - "中国", - "中國", - "中文网", - "亚马逊", - "企业", - "佛山", - "信息", - "健康", - "八卦", - "公司", - "公益", - "台湾", - "台灣", - "商城", - "商店", - "商标", - "嘉里", - "嘉里大酒店", - "在线", - "大拿", - "天主教", - "娱乐", - "家電", - "广东", - "微博", - "慈善", - "我爱你", - "手机", - "招聘", - "政务", - "政府", - "新加坡", - "新闻", - "时尚", - "書籍", - "机构", - "淡马锡", - "游戏", - "澳門", - "点看", - "移动", - "组织机构", - "网址", - "网店", - "网站", - "网络", - "联通", - "诺基亚", - "谷歌", - "购物", - "通販", - "集团", - "電訊盈科", - "飞利浦", - "食品", - "餐厅", - "香格里拉", - "香港", - "닷넷", - "닷컴", - "삼성", - "한국", -]; - -export default tlds; diff --git a/bin/utils/url.ts b/bin/utils/url.ts index 8ae79f8..51050df 100644 --- a/bin/utils/url.ts +++ b/bin/utils/url.ts @@ -1,45 +1,40 @@ -import url from 'url'; -import isurl from 'is-url'; -import tlds from './tlds.js'; +import psl from 'psl'; +import isUrl from 'is-url'; -export function getDomain(inputUrl: string) { - const parsed = url.parse(inputUrl).host; - var parts = parsed.split('.'); - if (parts[0] === 'www' && parts[1] !== 'com') { - parts.shift(); - } - var ln = parts.length, - i = ln, - minLength = parts[parts.length - 1].length, - part; +// Extracts the domain from a given URL. +export function getDomain(inputUrl: string): string | null { + try { + const url = new URL(inputUrl); + // Use PSL to parse domain names. + const parsed = psl.parse(url.hostname); - // iterate backwards - while ((part = parts[--i])) { - // stop when we find a non-TLD part - if ( - i === 0 || // 'asia.com' (last remaining must be the SLD) - i < ln - 2 || // TLDs only span 2 levels - part.length < minLength || // 'www.cn.com' (valid TLD as second-level domain) - tlds.indexOf(part) < 0 // officialy not a TLD - ) { - return part; + // 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 appendProtocol(inputUrl: string): string { - const parsed = url.parse(inputUrl); - if (!parsed.protocol) { - const urlWithProtocol = `https://${inputUrl}`; - return urlWithProtocol; + +// Appends 'https://' protocol to the URL if not present. +export function appendProtocol(inputUrl: string): string { + try { + new URL(inputUrl); + return inputUrl; + } catch { + return `https://${inputUrl}`; } - return inputUrl; } +// Normalizes the URL by ensuring it has a protocol and is valid. export function normalizeUrl(urlToNormalize: string): string { const urlWithProtocol = appendProtocol(urlToNormalize); - if (isurl(urlWithProtocol)) { + if (isUrl(urlWithProtocol)) { return urlWithProtocol; } else { throw new Error(`Your url "${urlWithProtocol}" is invalid`); diff --git a/bin/utils/validate.ts b/bin/utils/validate.ts index 418c699..e55b9bd 100644 --- a/bin/utils/validate.ts +++ b/bin/utils/validate.ts @@ -1,23 +1,25 @@ -import * as Commander from 'commander'; -import { normalizeUrl } from './url.js'; import fs from 'fs'; +import { InvalidArgumentError } from 'commander'; +import { normalizeUrl } from './url'; export function validateNumberInput(value: string) { const parsedValue = Number(value); if (isNaN(parsedValue)) { - throw new Commander.InvalidArgumentError('Not a number.'); + throw new InvalidArgumentError('Not a number.'); } return parsedValue; } export function validateUrlInput(url: string) { - if(!fs.existsSync(url)) { + const isFile = fs.existsSync(url); + + if (!isFile) { try { - return normalizeUrl(url) + return normalizeUrl(url); } catch (error) { - throw new Commander.InvalidArgumentError(error.message); + throw new InvalidArgumentError(error.message); } - } else { - return url; } + + return url; } diff --git a/dist/cli.js b/dist/cli.js index da9d1f3..35ce0ba 100644 --- a/dist/cli.js +++ b/dist/cli.js @@ -1,2113 +1,211 @@ -import * as Commander from 'commander'; -import { program } from 'commander'; import log from 'loglevel'; -import url, { fileURLToPath } from 'url'; -import isurl from 'is-url'; -import fs from 'fs'; +import { InvalidArgumentError, program } from 'commander'; +import fs from 'fs/promises'; +import psl from 'psl'; +import isUrl from 'is-url'; +import crypto from 'crypto'; import prompts from 'prompts'; import path from 'path'; -import fs$1 from 'fs/promises'; -import fs2 from 'fs-extra'; -import chalk from 'chalk'; -import crypto from 'crypto'; import axios from 'axios'; -import { fileTypeFromBuffer } from 'file-type'; import { dir } from 'tmp-promise'; -import ora from 'ora'; +import { fileTypeFromBuffer } from 'file-type'; +import chalk from 'chalk'; +import { fileURLToPath } from 'url'; import shelljs from 'shelljs'; -import { promisify } from 'util'; import dns from 'dns'; import http from 'http'; +import { promisify } from 'util'; +import ora from 'ora'; +import fs2 from 'fs-extra'; import updateNotifier from 'update-notifier'; +import fs$1 from 'fs'; -/****************************************************************************** -Copyright (c) Microsoft Corporation. - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. -***************************************************************************** */ -/* global Reflect, Promise */ - - -function __awaiter(thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); +// 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`); + } +} + +// 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; } -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, +function formatMessage(color, ...msg) { + return msg.map((m) => color(m)); +} +const logger = { + info(...msg) { + log.info(...formatMessage(chalk.blue, ...msg)); + }, + debug(...msg) { + log.debug(...formatMessage(chalk.magenta, ...msg)); + }, + error(...msg) { + log.error(...formatMessage(chalk.red, ...msg)); + }, + warn(...msg) { + log.warn(...formatMessage(chalk.yellow, ...msg)); + }, + success(...msg) { + log.info(...formatMessage(chalk.green, ...msg)); + } }; -const tlds = [ - "aaa", - "aarp", - "abarth", - "abb", - "abbott", - "abbvie", - "abc", - "able", - "abogado", - "abudhabi", - "ac", - "academy", - "accenture", - "accountant", - "accountants", - "aco", - "actor", - "ad", - "adac", - "ads", - "adult", - "ae", - "aeg", - "aero", - "aetna", - "af", - "afl", - "africa", - "ag", - "agakhan", - "agency", - "ai", - "aig", - "airbus", - "airforce", - "airtel", - "akdn", - "al", - "alfaromeo", - "alibaba", - "alipay", - "allfinanz", - "allstate", - "ally", - "alsace", - "alstom", - "am", - "amazon", - "americanexpress", - "americanfamily", - "amex", - "amfam", - "amica", - "amsterdam", - "analytics", - "android", - "anquan", - "anz", - "ao", - "aol", - "apartments", - "app", - "apple", - "aq", - "aquarelle", - "ar", - "arab", - "aramco", - "archi", - "army", - "arpa", - "art", - "arte", - "as", - "asda", - "asia", - "associates", - "at", - "athleta", - "attorney", - "au", - "auction", - "audi", - "audible", - "audio", - "auspost", - "author", - "auto", - "autos", - "avianca", - "aw", - "aws", - "ax", - "axa", - "az", - "azure", - "ba", - "baby", - "baidu", - "banamex", - "bananarepublic", - "band", - "bank", - "bar", - "barcelona", - "barclaycard", - "barclays", - "barefoot", - "bargains", - "baseball", - "basketball", - "bauhaus", - "bayern", - "bb", - "bbc", - "bbt", - "bbva", - "bcg", - "bcn", - "bd", - "be", - "beats", - "beauty", - "beer", - "bentley", - "berlin", - "best", - "bestbuy", - "bet", - "bf", - "bg", - "bh", - "bharti", - "bi", - "bible", - "bid", - "bike", - "bing", - "bingo", - "bio", - "biz", - "bj", - "black", - "blackfriday", - "blockbuster", - "blog", - "bloomberg", - "blue", - "bm", - "bms", - "bmw", - "bn", - "bnpparibas", - "bo", - "boats", - "boehringer", - "bofa", - "bom", - "bond", - "boo", - "book", - "booking", - "bosch", - "bostik", - "boston", - "bot", - "boutique", - "box", - "br", - "bradesco", - "bridgestone", - "broadway", - "broker", - "brother", - "brussels", - "bs", - "bt", - "build", - "builders", - "business", - "buy", - "buzz", - "bv", - "bw", - "by", - "bz", - "bzh", - "ca", - "cab", - "cafe", - "cal", - "call", - "calvinklein", - "cam", - "camera", - "camp", - "canon", - "capetown", - "capital", - "capitalone", - "car", - "caravan", - "cards", - "care", - "career", - "careers", - "cars", - "casa", - "case", - "cash", - "casino", - "cat", - "catering", - "catholic", - "cba", - "cbn", - "cbre", - "cbs", - "cc", - "cd", - "center", - "ceo", - "cern", - "cf", - "cfa", - "cfd", - "cg", - "ch", - "chanel", - "channel", - "charity", - "chase", - "chat", - "cheap", - "chintai", - "christmas", - "chrome", - "church", - "ci", - "cipriani", - "circle", - "cisco", - "citadel", - "citi", - "citic", - "city", - "cityeats", - "ck", - "cl", - "claims", - "cleaning", - "click", - "clinic", - "clinique", - "clothing", - "cloud", - "club", - "clubmed", - "cm", - "cn", - "co", - "coach", - "codes", - "coffee", - "college", - "cologne", - "com", - "comcast", - "commbank", - "community", - "company", - "compare", - "computer", - "comsec", - "condos", - "construction", - "consulting", - "contact", - "contractors", - "cooking", - "cookingchannel", - "cool", - "coop", - "corsica", - "country", - "coupon", - "coupons", - "courses", - "cpa", - "cr", - "credit", - "creditcard", - "creditunion", - "cricket", - "crown", - "crs", - "cruise", - "cruises", - "cu", - "cuisinella", - "cv", - "cw", - "cx", - "cy", - "cymru", - "cyou", - "cz", - "dabur", - "dad", - "dance", - "data", - "date", - "dating", - "datsun", - "day", - "dclk", - "dds", - "de", - "deal", - "dealer", - "deals", - "degree", - "delivery", - "dell", - "deloitte", - "delta", - "democrat", - "dental", - "dentist", - "desi", - "design", - "dev", - "dhl", - "diamonds", - "diet", - "digital", - "direct", - "directory", - "discount", - "discover", - "dish", - "diy", - "dj", - "dk", - "dm", - "dnp", - "do", - "docs", - "doctor", - "dog", - "domains", - "dot", - "download", - "drive", - "dtv", - "dubai", - "dunlop", - "dupont", - "durban", - "dvag", - "dvr", - "dz", - "earth", - "eat", - "ec", - "eco", - "edeka", - "edu", - "education", - "ee", - "eg", - "email", - "emerck", - "energy", - "engineer", - "engineering", - "enterprises", - "epson", - "equipment", - "er", - "ericsson", - "erni", - "es", - "esq", - "estate", - "et", - "etisalat", - "eu", - "eurovision", - "eus", - "events", - "exchange", - "expert", - "exposed", - "express", - "extraspace", - "fage", - "fail", - "fairwinds", - "faith", - "family", - "fan", - "fans", - "farm", - "farmers", - "fashion", - "fast", - "fedex", - "feedback", - "ferrari", - "ferrero", - "fi", - "fiat", - "fidelity", - "fido", - "film", - "final", - "finance", - "financial", - "fire", - "firestone", - "firmdale", - "fish", - "fishing", - "fit", - "fitness", - "fj", - "fk", - "flickr", - "flights", - "flir", - "florist", - "flowers", - "fly", - "fm", - "fo", - "foo", - "food", - "foodnetwork", - "football", - "ford", - "forex", - "forsale", - "forum", - "foundation", - "fox", - "fr", - "free", - "fresenius", - "frl", - "frogans", - "frontdoor", - "frontier", - "ftr", - "fujitsu", - "fun", - "fund", - "furniture", - "futbol", - "fyi", - "ga", - "gal", - "gallery", - "gallo", - "gallup", - "game", - "games", - "gap", - "garden", - "gay", - "gb", - "gbiz", - "gd", - "gdn", - "ge", - "gea", - "gent", - "genting", - "george", - "gf", - "gg", - "ggee", - "gh", - "gi", - "gift", - "gifts", - "gives", - "giving", - "gl", - "glass", - "gle", - "global", - "globo", - "gm", - "gmail", - "gmbh", - "gmo", - "gmx", - "gn", - "godaddy", - "gold", - "goldpoint", - "golf", - "goo", - "goodyear", - "goog", - "google", - "gop", - "got", - "gov", - "gp", - "gq", - "gr", - "grainger", - "graphics", - "gratis", - "green", - "gripe", - "grocery", - "group", - "gs", - "gt", - "gu", - "guardian", - "gucci", - "guge", - "guide", - "guitars", - "guru", - "gw", - "gy", - "hair", - "hamburg", - "hangout", - "haus", - "hbo", - "hdfc", - "hdfcbank", - "health", - "healthcare", - "help", - "helsinki", - "here", - "hermes", - "hgtv", - "hiphop", - "hisamitsu", - "hitachi", - "hiv", - "hk", - "hkt", - "hm", - "hn", - "hockey", - "holdings", - "holiday", - "homedepot", - "homegoods", - "homes", - "homesense", - "honda", - "horse", - "hospital", - "host", - "hosting", - "hot", - "hoteles", - "hotels", - "hotmail", - "house", - "how", - "hr", - "hsbc", - "ht", - "hu", - "hughes", - "hyatt", - "hyundai", - "ibm", - "icbc", - "ice", - "icu", - "id", - "ie", - "ieee", - "ifm", - "ikano", - "il", - "im", - "imamat", - "imdb", - "immo", - "immobilien", - "in", - "inc", - "industries", - "infiniti", - "info", - "ing", - "ink", - "institute", - "insurance", - "insure", - "int", - "international", - "intuit", - "investments", - "io", - "ipiranga", - "iq", - "ir", - "irish", - "is", - "ismaili", - "ist", - "istanbul", - "it", - "itau", - "itv", - "jaguar", - "java", - "jcb", - "je", - "jeep", - "jetzt", - "jewelry", - "jio", - "jll", - "jm", - "jmp", - "jnj", - "jo", - "jobs", - "joburg", - "jot", - "joy", - "jp", - "jpmorgan", - "jprs", - "juegos", - "juniper", - "kaufen", - "kddi", - "ke", - "kerryhotels", - "kerrylogistics", - "kerryproperties", - "kfh", - "kg", - "kh", - "ki", - "kia", - "kids", - "kim", - "kinder", - "kindle", - "kitchen", - "kiwi", - "km", - "kn", - "koeln", - "komatsu", - "kosher", - "kp", - "kpmg", - "kpn", - "kr", - "krd", - "kred", - "kuokgroup", - "kw", - "ky", - "kyoto", - "kz", - "la", - "lacaixa", - "lamborghini", - "lamer", - "lancaster", - "lancia", - "land", - "landrover", - "lanxess", - "lasalle", - "lat", - "latino", - "latrobe", - "law", - "lawyer", - "lb", - "lc", - "lds", - "lease", - "leclerc", - "lefrak", - "legal", - "lego", - "lexus", - "lgbt", - "li", - "lidl", - "life", - "lifeinsurance", - "lifestyle", - "lighting", - "like", - "lilly", - "limited", - "limo", - "lincoln", - "linde", - "link", - "lipsy", - "live", - "living", - "lk", - "llc", - "llp", - "loan", - "loans", - "locker", - "locus", - "loft", - "lol", - "london", - "lotte", - "lotto", - "love", - "lpl", - "lplfinancial", - "lr", - "ls", - "lt", - "ltd", - "ltda", - "lu", - "lundbeck", - "luxe", - "luxury", - "lv", - "ly", - "ma", - "macys", - "madrid", - "maif", - "maison", - "makeup", - "man", - "management", - "mango", - "map", - "market", - "marketing", - "markets", - "marriott", - "marshalls", - "maserati", - "mattel", - "mba", - "mc", - "mckinsey", - "md", - "me", - "med", - "media", - "meet", - "melbourne", - "meme", - "memorial", - "men", - "menu", - "merckmsd", - "mg", - "mh", - "miami", - "microsoft", - "mil", - "mini", - "mint", - "mit", - "mitsubishi", - "mk", - "ml", - "mlb", - "mls", - "mm", - "mma", - "mn", - "mo", - "mobi", - "mobile", - "moda", - "moe", - "moi", - "mom", - "monash", - "money", - "monster", - "mormon", - "mortgage", - "moscow", - "moto", - "motorcycles", - "mov", - "movie", - "mp", - "mq", - "mr", - "ms", - "msd", - "mt", - "mtn", - "mtr", - "mu", - "museum", - "music", - "mutual", - "mv", - "mw", - "mx", - "my", - "mz", - "na", - "nab", - "nagoya", - "name", - "natura", - "navy", - "nba", - "nc", - "ne", - "nec", - "net", - "netbank", - "netflix", - "network", - "neustar", - "new", - "news", - "next", - "nextdirect", - "nexus", - "nf", - "nfl", - "ng", - "ngo", - "nhk", - "ni", - "nico", - "nike", - "nikon", - "ninja", - "nissan", - "nissay", - "nl", - "no", - "nokia", - "northwesternmutual", - "norton", - "now", - "nowruz", - "nowtv", - "np", - "nr", - "nra", - "nrw", - "ntt", - "nu", - "nyc", - "nz", - "obi", - "observer", - "office", - "okinawa", - "olayan", - "olayangroup", - "oldnavy", - "ollo", - "om", - "omega", - "one", - "ong", - "onl", - "online", - "ooo", - "open", - "oracle", - "orange", - "org", - "organic", - "origins", - "osaka", - "otsuka", - "ott", - "ovh", - "pa", - "page", - "panasonic", - "paris", - "pars", - "partners", - "parts", - "party", - "passagens", - "pay", - "pccw", - "pe", - "pet", - "pf", - "pfizer", - "pg", - "ph", - "pharmacy", - "phd", - "philips", - "phone", - "photo", - "photography", - "photos", - "physio", - "pics", - "pictet", - "pictures", - "pid", - "pin", - "ping", - "pink", - "pioneer", - "pizza", - "pk", - "pl", - "place", - "play", - "playstation", - "plumbing", - "plus", - "pm", - "pn", - "pnc", - "pohl", - "poker", - "politie", - "porn", - "post", - "pr", - "pramerica", - "praxi", - "press", - "prime", - "pro", - "prod", - "productions", - "prof", - "progressive", - "promo", - "properties", - "property", - "protection", - "pru", - "prudential", - "ps", - "pt", - "pub", - "pw", - "pwc", - "py", - "qa", - "qpon", - "quebec", - "quest", - "racing", - "radio", - "re", - "read", - "realestate", - "realtor", - "realty", - "recipes", - "red", - "redstone", - "redumbrella", - "rehab", - "reise", - "reisen", - "reit", - "reliance", - "ren", - "rent", - "rentals", - "repair", - "report", - "republican", - "rest", - "restaurant", - "review", - "reviews", - "rexroth", - "rich", - "richardli", - "ricoh", - "ril", - "rio", - "rip", - "ro", - "rocher", - "rocks", - "rodeo", - "rogers", - "room", - "rs", - "rsvp", - "ru", - "rugby", - "ruhr", - "run", - "rw", - "rwe", - "ryukyu", - "sa", - "saarland", - "safe", - "safety", - "sakura", - "sale", - "salon", - "samsclub", - "samsung", - "sandvik", - "sandvikcoromant", - "sanofi", - "sap", - "sarl", - "sas", - "save", - "saxo", - "sb", - "sbi", - "sbs", - "sc", - "sca", - "scb", - "schaeffler", - "schmidt", - "scholarships", - "school", - "schule", - "schwarz", - "science", - "scot", - "sd", - "se", - "search", - "seat", - "secure", - "security", - "seek", - "select", - "sener", - "services", - "ses", - "seven", - "sew", - "sex", - "sexy", - "sfr", - "sg", - "sh", - "shangrila", - "sharp", - "shaw", - "shell", - "shia", - "shiksha", - "shoes", - "shop", - "shopping", - "shouji", - "show", - "showtime", - "si", - "silk", - "sina", - "singles", - "site", - "sj", - "sk", - "ski", - "skin", - "sky", - "skype", - "sl", - "sling", - "sm", - "smart", - "smile", - "sn", - "sncf", - "so", - "soccer", - "social", - "softbank", - "software", - "sohu", - "solar", - "solutions", - "song", - "sony", - "soy", - "spa", - "space", - "sport", - "spot", - "sr", - "srl", - "ss", - "st", - "stada", - "staples", - "star", - "statebank", - "statefarm", - "stc", - "stcgroup", - "stockholm", - "storage", - "store", - "stream", - "studio", - "study", - "style", - "su", - "sucks", - "supplies", - "supply", - "support", - "surf", - "surgery", - "suzuki", - "sv", - "swatch", - "swiss", - "sx", - "sy", - "sydney", - "systems", - "sz", - "tab", - "taipei", - "talk", - "taobao", - "target", - "tatamotors", - "tatar", - "tattoo", - "tax", - "taxi", - "tc", - "tci", - "td", - "tdk", - "team", - "tech", - "technology", - "tel", - "temasek", - "tennis", - "teva", - "tf", - "tg", - "th", - "thd", - "theater", - "theatre", - "tiaa", - "tickets", - "tienda", - "tiffany", - "tips", - "tires", - "tirol", - "tj", - "tjmaxx", - "tjx", - "tk", - "tkmaxx", - "tl", - "tm", - "tmall", - "tn", - "to", - "today", - "tokyo", - "tools", - "top", - "toray", - "toshiba", - "total", - "tours", - "town", - "toyota", - "toys", - "tr", - "trade", - "trading", - "training", - "travel", - "travelchannel", - "travelers", - "travelersinsurance", - "trust", - "trv", - "tt", - "tube", - "tui", - "tunes", - "tushu", - "tv", - "tvs", - "tw", - "tz", - "ua", - "ubank", - "ubs", - "ug", - "uk", - "unicom", - "university", - "uno", - "uol", - "ups", - "us", - "uy", - "uz", - "va", - "vacations", - "vana", - "vanguard", - "vc", - "ve", - "vegas", - "ventures", - "verisign", - "vermögensberater", - "vermögensberatung", - "versicherung", - "vet", - "vg", - "vi", - "viajes", - "video", - "vig", - "viking", - "villas", - "vin", - "vip", - "virgin", - "visa", - "vision", - "viva", - "vivo", - "vlaanderen", - "vn", - "vodka", - "volkswagen", - "volvo", - "vote", - "voting", - "voto", - "voyage", - "vu", - "vuelos", - "wales", - "walmart", - "walter", - "wang", - "wanggou", - "watch", - "watches", - "weather", - "weatherchannel", - "webcam", - "weber", - "website", - "wed", - "wedding", - "weibo", - "weir", - "wf", - "whoswho", - "wien", - "wiki", - "williamhill", - "win", - "windows", - "wine", - "winners", - "wme", - "wolterskluwer", - "woodside", - "work", - "works", - "world", - "wow", - "ws", - "wtc", - "wtf", - "xbox", - "xerox", - "xfinity", - "xihuan", - "xin", - "xxx", - "xyz", - "yachts", - "yahoo", - "yamaxun", - "yandex", - "ye", - "yodobashi", - "yoga", - "yokohama", - "you", - "youtube", - "yt", - "yun", - "za", - "zappos", - "zara", - "zero", - "zip", - "zm", - "zone", - "zuerich", - "zw", - "ελ", - "ευ", - "бг", - "бел", - "дети", - "ею", - "католик", - "ком", - "мкд", - "мон", - "москва", - "онлайн", - "орг", - "рус", - "рф", - "сайт", - "срб", - "укр", - "қаз", - "հայ", - "ישראל", - "קום", - "ابوظبي", - "اتصالات", - "ارامكو", - "الاردن", - "البحرين", - "الجزائر", - "السعودية", - "العليان", - "المغرب", - "امارات", - "ایران", - "بارت", - "بازار", - "بيتك", - "بھارت", - "تونس", - "سودان", - "سورية", - "شبكة", - "عراق", - "عرب", - "عمان", - "فلسطين", - "قطر", - "كاثوليك", - "كوم", - "مصر", - "مليسيا", - "موريتانيا", - "موقع", - "همراه", - "پاکستان", - "ڀارت", - "कॉम", - "नेट", - "भारत", - "भारतम्", - "भारोत", - "संगठन", - "বাংলা", - "ভারত", - "ভাৰত", - "ਭਾਰਤ", - "ભારત", - "ଭାରତ", - "இந்தியா", - "இலங்கை", - "சிங்கப்பூர்", - "భారత్", - "ಭಾರತ", - "ഭാരതം", - "ලංකා", - "คอม", - "ไทย", - "ລາວ", - "გე", - "みんな", - "アマゾン", - "クラウド", - "グーグル", - "コム", - "ストア", - "セール", - "ファッション", - "ポイント", - "世界", - "中信", - "中国", - "中國", - "中文网", - "亚马逊", - "企业", - "佛山", - "信息", - "健康", - "八卦", - "公司", - "公益", - "台湾", - "台灣", - "商城", - "商店", - "商标", - "嘉里", - "嘉里大酒店", - "在线", - "大拿", - "天主教", - "娱乐", - "家電", - "广东", - "微博", - "慈善", - "我爱你", - "手机", - "招聘", - "政务", - "政府", - "新加坡", - "新闻", - "时尚", - "書籍", - "机构", - "淡马锡", - "游戏", - "澳門", - "点看", - "移动", - "组织机构", - "网址", - "网店", - "网站", - "网络", - "联通", - "诺基亚", - "谷歌", - "购物", - "通販", - "集团", - "電訊盈科", - "飞利浦", - "食品", - "餐厅", - "香格里拉", - "香港", - "닷넷", - "닷컴", - "삼성", - "한국", +// 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 { platform: platform$1 } = process; +const IS_MAC = platform$1 === 'darwin'; +const IS_WIN = platform$1 === 'win32'; +const IS_LINUX = platform$1 === 'linux'; + +async function handleIcon(options, url) { + if (options.icon) { + if (options.icon.startsWith('http')) { + return downloadIcon(options.icon); + } + else { + return path.resolve(options.icon); + } + } + else { + return getDefaultIcon(); + } +} +async function getDefaultIcon() { + logger.info('You have not provided an app icon, use the default icon.(use --icon option to assign an icon)'); + 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) { + 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 fs.writeFile(iconPath, iconData); + return iconPath; + } + catch (error) { + if (error.response && error.response.status === 404) { + return null; + } + throw error; + } +} + +async function handleOptions(options, url) { + const appOptions = { + ...options, + identifier: getIdentifier(url), + }; + let urlExists = false; + try { + await fs.stat(url); + urlExists = true; + } + catch (error) { + // URL does not exist + } + if (!appOptions.name) { + const defaultName = urlExists ? "" : getDomain(url); + const promptMessage = 'Please input your application name'; + appOptions.name = await promptText(promptMessage, defaultName); + } + appOptions.icon = await handleIcon(appOptions); + return appOptions; +} + +var windows = [ + { + url: "https://weread.qq.com/", + transparent: true, + fullscreen: false, + width: 1200, + height: 780, + resizable: true, + url_type: "web" + } ]; - -function getDomain(inputUrl) { - const parsed = url.parse(inputUrl).host; - var parts = parsed.split('.'); - if (parts[0] === 'www' && parts[1] !== 'com') { - parts.shift(); - } - var ln = parts.length, i = ln, minLength = parts[parts.length - 1].length, part; - // iterate backwards - while ((part = parts[--i])) { - // stop when we find a non-TLD part - if (i === 0 || // 'asia.com' (last remaining must be the SLD) - i < ln - 2 || // TLDs only span 2 levels - part.length < minLength || // 'www.cn.com' (valid TLD as second-level domain) - tlds.indexOf(part) < 0 // officialy not a TLD - ) { - return part; - } - } -} -function appendProtocol(inputUrl) { - const parsed = url.parse(inputUrl); - if (!parsed.protocol) { - const urlWithProtocol = `https://${inputUrl}`; - return urlWithProtocol; - } - return inputUrl; -} -function normalizeUrl(urlToNormalize) { - const urlWithProtocol = appendProtocol(urlToNormalize); - if (isurl(urlWithProtocol)) { - return urlWithProtocol; - } - else { - throw new Error(`Your url "${urlWithProtocol}" is invalid`); - } -} - -function validateNumberInput(value) { - const parsedValue = Number(value); - if (isNaN(parsedValue)) { - throw new Commander.InvalidArgumentError('Not a number.'); - } - return parsedValue; -} -function validateUrlInput(url) { - if (!fs.existsSync(url)) { - try { - return normalizeUrl(url); - } - catch (error) { - throw new Commander.InvalidArgumentError(error.message); - } - } - else { - return url; - } -} - -const npmDirectory = path.join(path.dirname(fileURLToPath(import.meta.url)), '..'); - -const logger = { - info(...msg) { - log.info(...msg.map((m) => chalk.blue.bold(m))); - }, - debug(...msg) { - log.debug(...msg); - }, - error(...msg) { - log.error(...msg.map((m) => chalk.red.bold(m))); - }, - warn(...msg) { - log.info(...msg.map((m) => chalk.yellow.bold(m))); - }, - success(...msg) { - log.info(...msg.map((m) => chalk.green.bold(m))); - } +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 menu = { + macos: true, + linux: false, + windows: false +}; +var system_tray = { + macos: false, + linux: true, + windows: true +}; +var pakeConf = { + windows: windows, + user_agent: user_agent, + menu: menu, + system_tray: system_tray }; - -function promptText(message, initial) { - return __awaiter(this, void 0, void 0, function* () { - const response = yield prompts({ - type: 'text', - name: 'content', - message, - initial, - }); - return response.content; - }); -} -function setSecurityConfigWithUrl(tauriConfig, url) { - const myURL = new URL(url); - const hostname = myURL.hostname; - tauriConfig.tauri.security.dangerousRemoteDomainIpcAccess[0].domain = hostname; -} -function mergeTauriConfig(url, options, tauriConf) { - return __awaiter(this, void 0, void 0, function* () { - const { width, height, fullscreen, transparent, resizable, userAgent, showMenu, showSystemTray, systemTrayIcon, iterCopyFile, identifier, name, } = options; - const tauriConfWindowOptions = { - width, - height, - fullscreen, - transparent, - resizable, - }; - // Package name is valid ? - // for Linux, package name must be a-z, 0-9 or "-", not allow to A-Z and other - if (process.platform === "linux") { - const reg = new RegExp(/[0-9]*[a-z]+[0-9]*\-?[0-9]*[a-z]*[0-9]*\-?[0-9]*[a-z]*[0-9]*/); - if (!reg.test(name) || reg.exec(name)[0].length != name.length) { - logger.error("package name is illegal, it must be lowercase letters, numbers, dashes, and it must contain the lowercase letters."); - logger.error("E.g com-123-xxx, 123pan, pan123,weread, we-read"); - process.exit(); - } - } - if (process.platform === "win32" || process.platform === "darwin") { - const reg = new RegExp(/([0-9]*[a-zA-Z]+[0-9]*)+/); - if (!reg.test(name) || reg.exec(name)[0].length != name.length) { - logger.error("package name is illegal, it must be letters, numbers, and it must contain the letters"); - logger.error("E.g 123pan,123Pan Pan123,weread, WeRead, WERead"); - process.exit(); - } - } - // logger.warn(JSON.stringify(tauriConf.pake.windows, null, 4)); - Object.assign(tauriConf.pake.windows[0], Object.assign({ url }, tauriConfWindowOptions)); - tauriConf.package.productName = name; - tauriConf.tauri.bundle.identifier = identifier; - // 判断一下url类型,是文件还是网站 - // 如果是文件,并且开启了递归拷贝功能,则需要将该文件以及所在文件夹下的所有文件拷贝到src目录下,否则只拷贝单个文件。 - const url_exists = yield fs$1.stat(url) - .then(() => true) - .catch(() => false); - if (url_exists) { - logger.warn("you input may a local file"); - tauriConf.pake.windows[0].url_type = "local"; - const file_name = path.basename(url); - const dir_name = path.dirname(url); - if (!iterCopyFile) { - const url_path = path.join(npmDirectory, "dist/", file_name); - yield fs$1.copyFile(url, url_path); - } - else { - const old_dir = path.join(npmDirectory, "dist/"); - const new_dir = path.join(npmDirectory, "dist_bak/"); - fs2.moveSync(old_dir, new_dir, { "overwrite": true }); - fs2.copySync(dir_name, old_dir, { "overwrite": true }); - // logger.warn("dir name", dir_name); - // 将dist_bak里面的cli.js和about_pake.html拷贝回去 - const cli_path = path.join(new_dir, "cli.js"); - const cli_path_target = path.join(old_dir, "cli.js"); - const about_pake_path = path.join(new_dir, "about_pake.html"); - const about_pake_path_target = path.join(old_dir, "about_pake.html"); - fs$1.copyFile(cli_path, cli_path_target); - fs$1.copyFile(about_pake_path, about_pake_path_target); - } - tauriConf.pake.windows[0].url = file_name; - tauriConf.pake.windows[0].url_type = "local"; - } - else { - tauriConf.pake.windows[0].url_type = "web"; - } - // 处理user-agent - if (userAgent.length > 0) { - if (process.platform === "win32") { - tauriConf.pake.user_agent.windows = userAgent; - } - if (process.platform === "linux") { - tauriConf.pake.user_agent.linux = userAgent; - } - if (process.platform === "darwin") { - tauriConf.pake.user_agent.macos = userAgent; - } - } - // 处理菜单栏 - if (showMenu) { - if (process.platform === "win32") { - tauriConf.pake.menu.windows = true; - } - if (process.platform === "linux") { - tauriConf.pake.menu.linux = true; - } - if (process.platform === "darwin") { - tauriConf.pake.menu.macos = true; - } - } - else { - if (process.platform === "win32") { - tauriConf.pake.menu.windows = false; - } - if (process.platform === "linux") { - tauriConf.pake.menu.linux = false; - } - if (process.platform === "darwin") { - tauriConf.pake.menu.macos = false; - } - } - // 处理托盘 - if (showSystemTray) { - if (process.platform === "win32") { - tauriConf.pake.system_tray.windows = true; - } - if (process.platform === "linux") { - tauriConf.pake.system_tray.linux = true; - } - if (process.platform === "darwin") { - tauriConf.pake.system_tray.macos = true; - } - } - else { - if (process.platform === "win32") { - tauriConf.pake.system_tray.windows = false; - } - if (process.platform === "linux") { - tauriConf.pake.system_tray.linux = false; - } - if (process.platform === "darwin") { - tauriConf.pake.system_tray.macos = false; - } - } - // 处理targets 暂时只对linux开放 - if (process.platform === "linux") { - delete tauriConf.tauri.bundle.deb.files; - if (["all", "deb", "appimage"].includes(options.targets)) { - if (options.targets === "all") { - tauriConf.tauri.bundle.targets = ["deb", "appimage"]; - } - else { - tauriConf.tauri.bundle.targets = [options.targets]; - } - } - else { - logger.warn("targets must be 'all', 'deb', 'appimage', we will use default 'all'"); - } - } - // 处理应用图标 - const exists = yield fs$1.stat(options.icon) - .then(() => true) - .catch(() => false); - if (exists) { - let updateIconPath = true; - let customIconExt = path.extname(options.icon).toLowerCase(); - if (process.platform === "win32") { - if (customIconExt === ".ico") { - const ico_path = path.join(npmDirectory, `src-tauri/png/${name.toLowerCase()}_32.ico`); - tauriConf.tauri.bundle.resources = [`png/${name.toLowerCase()}_32.ico`]; - yield fs$1.copyFile(options.icon, ico_path); - } - else { - updateIconPath = false; - logger.warn(`icon file in Windows must be 256 * 256 pix with .ico type, but you give ${customIconExt}`); - tauriConf.tauri.bundle.icon = ["png/icon_256.ico"]; - } - } - if (process.platform === "linux") { - if (customIconExt != ".png") { - updateIconPath = false; - logger.warn(`icon file in Linux must be 512 * 512 pix with .png type, but you give ${customIconExt}`); - tauriConf.tauri.bundle.icon = ["png/icon_512.png"]; - } - } - if (process.platform === "darwin" && customIconExt !== ".icns") { - updateIconPath = false; - logger.warn(`icon file in MacOS must be .icns type, but you give ${customIconExt}`); - tauriConf.tauri.bundle.icon = ["icons/icon.icns"]; - } - if (updateIconPath) { - tauriConf.tauri.bundle.icon = [options.icon]; - } - else { - logger.warn(`icon file will not change with default.`); - } - } - else { - logger.warn("the custom icon path may not exists. we will use default icon to replace it"); - if (process.platform === "win32") { - tauriConf.tauri.bundle.icon = ["png/icon_256.ico"]; - } - if (process.platform === "linux") { - tauriConf.tauri.bundle.icon = ["png/icon_512.png"]; - } - if (process.platform === "darwin") { - tauriConf.tauri.bundle.icon = ["icons/icon.icns"]; - } - } - // 处理托盘自定义图标 - let useDefaultIcon = true; // 是否使用默认托盘图标 - if (systemTrayIcon.length > 0) { - const icon_exists = yield fs$1.stat(systemTrayIcon) - .then(() => true) - .catch(() => false); - if (icon_exists) { - // 需要判断图标格式,默认只支持ico和png两种 - let iconExt = path.extname(systemTrayIcon).toLowerCase(); - if (iconExt == ".png" || iconExt == ".ico") { - useDefaultIcon = false; - const trayIcoPath = path.join(npmDirectory, `src-tauri/png/${name.toLowerCase()}${iconExt}`); - tauriConf.tauri.systemTray.iconPath = `png/${name.toLowerCase()}${iconExt}`; - yield fs$1.copyFile(systemTrayIcon, trayIcoPath); - } - else { - logger.warn(`file type for system tray icon mut be .ico or .png , but you give ${iconExt}`); - logger.warn(`system tray icon file will not change with default.`); - } - } - else { - logger.warn(`${systemTrayIcon} not exists!`); - logger.warn(`system tray icon file will not change with default.`); - } - } - // 处理托盘默认图标 - if (useDefaultIcon) { - if (process.platform === "linux" || process.platform === "win32") { - tauriConf.tauri.systemTray.iconPath = tauriConf.tauri.bundle.icon[0]; - } - else { - tauriConf.tauri.systemTray.iconPath = "png/icon_512.png"; - } - } - // 设置安全调用 window.__TAURI__ 的安全域名为设置的应用域名 - setSecurityConfigWithUrl(tauriConf, url); - // 保存配置文件 - let configPath = ""; - switch (process.platform) { - case "win32": { - configPath = path.join(npmDirectory, 'src-tauri/tauri.windows.conf.json'); - break; - } - case "darwin": { - configPath = path.join(npmDirectory, 'src-tauri/tauri.macos.conf.json'); - break; - } - case "linux": { - configPath = path.join(npmDirectory, 'src-tauri/tauri.linux.conf.json'); - break; - } - } - let bundleConf = { tauri: { bundle: tauriConf.tauri.bundle } }; - yield fs$1.writeFile(configPath, Buffer.from(JSON.stringify(bundleConf, null, 4), 'utf-8')); - const pakeConfigPath = path.join(npmDirectory, 'src-tauri/pake.json'); - yield fs$1.writeFile(pakeConfigPath, Buffer.from(JSON.stringify(tauriConf.pake, null, 4), 'utf-8')); - // logger.info("tauri config", JSON.stringify(tauriConf.build)); - let tauriConf2 = JSON.parse(JSON.stringify(tauriConf)); - delete tauriConf2.pake; - delete tauriConf2.tauri.bundle; - const configJsonPath = path.join(npmDirectory, 'src-tauri/tauri.conf.json'); - yield fs$1.writeFile(configJsonPath, Buffer.from(JSON.stringify(tauriConf2, null, 4), 'utf-8')); - }); -} - -function getIdentifier(name, url) { - const hash = crypto.createHash('md5'); - hash.update(url); - const postFixHash = hash.digest('hex').substring(0, 6); - return `pake-${postFixHash}`; -} - -const IS_MAC = process.platform === 'darwin'; -const IS_WIN = process.platform === 'win32'; -const IS_LINUX = process.platform === 'linux'; - -function handleIcon(options, url) { - return __awaiter(this, void 0, void 0, function* () { - if (options.icon) { - if (options.icon.startsWith('http')) { - return downloadIcon(options.icon); - } - else { - return path.resolve(options.icon); - } - } - if (!options.icon) { - return getDefaultIcon(); - } - }); -} -function getDefaultIcon() { - return __awaiter(this, void 0, void 0, function* () { - logger.info('You have not provided an app icon, use the default icon.(use --icon option to assign an icon)'); - let iconPath = 'src-tauri/icons/icon.icns'; - if (IS_WIN) { - iconPath = 'src-tauri/png/icon_256.ico'; - } - else if (IS_LINUX) { - iconPath = 'src-tauri/png/icon_512.png'; - } - return path.join(npmDirectory, iconPath); - }); -} -// export async function getIconFromPageUrl(url: string) { -// const icon = await pageIcon(url); -// console.log(icon); -// if (icon.ext === '.ico') { -// const a = await ICO.parse(icon.data); -// icon.data = Buffer.from(a[0].buffer); -// } -// const iconDir = (await dir()).path; -// const iconPath = path.join(iconDir, `/icon.icns`); -// const out = png2icons.createICNS(icon.data, png2icons.BILINEAR, 0); -// await fs.writeFile(iconPath, out); -// return iconPath; -// } -// export async function getIconFromMacosIcons(name: string) { -// const data = { -// query: name, -// filters: 'approved:true', -// hitsPerPage: 10, -// page: 1, -// }; -// const res = await axios.post('https://p1txh7zfb3-2.algolianet.com/1/indexes/macOSicons/query?x-algolia-agent=Algolia%20for%20JavaScript%20(4.13.1)%3B%20Browser', data, { -// headers: { -// 'x-algolia-api-key': '0ba04276e457028f3e11e38696eab32c', -// 'x-algolia-application-id': 'P1TXH7ZFB3', -// }, -// }); -// if (!res.data.hits.length) { -// return ''; -// } else { -// return downloadIcon(res.data.hits[0].icnsUrl); -// } -// } -function downloadIcon(iconUrl) { - return __awaiter(this, void 0, void 0, function* () { - let iconResponse; - try { - iconResponse = yield axios.get(iconUrl, { - responseType: 'arraybuffer', - }); - } - catch (error) { - if (error.response && error.response.status === 404) { - return null; - } - throw error; - } - const iconData = yield iconResponse.data; - if (!iconData) { - return null; - } - const fileDetails = yield fileTypeFromBuffer(iconData); - if (!fileDetails) { - return null; - } - const { path } = yield dir(); - const iconPath = `${path}/icon.${fileDetails.ext}`; - yield fs$1.writeFile(iconPath, iconData); - return iconPath; - }); -} - -function handleOptions(options, url) { - return __awaiter(this, void 0, void 0, function* () { - const appOptions = Object.assign(Object.assign({}, options), { identifier: '' }); - const url_exists = yield fs$1.stat(url) - .then(() => true) - .catch(() => false); - if (!appOptions.name) { - if (!url_exists) { - appOptions.name = yield promptText('please input your application name', getDomain(url)); - } - else { - appOptions.name = yield promptText('please input your application name', ""); - } - } - appOptions.identifier = getIdentifier(appOptions.name, url); - appOptions.icon = yield handleIcon(appOptions); - return appOptions; - }); -} - -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 ping = (host) => __awaiter(void 0, void 0, void 0, function* () { - const lookup = promisify(dns.lookup); - const ip = yield lookup(host); - const start = new Date(); - return 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 resolve = promisify(dns.resolve); -function isChinaDomain(domain) { - return __awaiter(this, void 0, void 0, function* () { - try { - // 解析域名为IP地址 - const [ip] = yield resolve(domain); - return yield isChinaIP(ip, domain); - } - catch (error) { - // 域名无法解析,返回false - logger.info(`${domain} can't be parse!`); - return false; - } - }); -} -function isChinaIP(ip, domain) { - return __awaiter(this, void 0, void 0, function* () { - try { - const delay = yield ping(ip); - logger.info(`${domain} latency is ${delay} ms`); - // 判断延迟是否超过500ms - return delay > 500; - } - catch (error) { - // 命令执行出错,返回false - logger.info(`ping ${domain} failed!`); - return false; - } - }); -} - -function installRust() { - return __awaiter(this, void 0, void 0, function* () { - const is_china = yield isChinaDomain("sh.rustup.rs"); - let RustInstallScriptFocMac = ""; - if (is_china) { - logger.info("it's in China, use rust cn mirror to install rust"); - RustInstallScriptFocMac = - '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'; - } - else { - RustInstallScriptFocMac = - "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y"; - } - const RustInstallScriptForWin = 'winget install --id Rustlang.Rustup'; - const spinner = ora('Downloading Rust').start(); - try { - yield shellExec(IS_WIN ? RustInstallScriptForWin : RustInstallScriptFocMac); - spinner.succeed(); - } - catch (error) { - console.error('install rust return code', error.message); - spinner.fail(); - process.exit(1); - } - }); -} -function checkRustInstalled() { - return shelljs.exec('rustc --version', { silent: true }).code === 0; -} var tauri$3 = { security: { @@ -2155,39 +253,6 @@ var CommonConf = { build: build }; -var windows = [ - { - url: "https://weread.qq.com/", - transparent: true, - fullscreen: false, - width: 1200, - height: 780, - resizable: true, - url_type: "web" - } -]; -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 menu = { - macos: true, - linux: false, - windows: false -}; -var system_tray = { - macos: false, - linux: true, - windows: true -}; -var pakeConf = { - windows: windows, - user_agent: user_agent, - menu: menu, - system_tray: system_tray -}; - var tauri$2 = { bundle: { icon: [ @@ -2291,263 +356,418 @@ var LinuxConf = { tauri: tauri }; -let tauriConf = { - package: CommonConf.package, - tauri: CommonConf.tauri, - build: CommonConf.build, - pake: pakeConf +const platformConfigs = { + win32: WinConf, + darwin: MacConf, + linux: LinuxConf +}; +const { platform } = process; +// @ts-ignore +const platformConfig = platformConfigs[platform]; +let tauriConfig = { + tauri: { + ...CommonConf.tauri, + bundle: platformConfig.tauri.bundle, + }, + package: CommonConf.package, + build: CommonConf.build, + pake: pakeConf }; -switch (process.platform) { - case "win32": { - tauriConf.tauri.bundle = WinConf.tauri.bundle; - break; - } - case "darwin": { - tauriConf.tauri.bundle = MacConf.tauri.bundle; - break; - } - case "linux": { - tauriConf.tauri.bundle = LinuxConf.tauri.bundle; - break; - } + +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}`)); + } + }); + }); } -class MacBuilder { - prepare() { - return __awaiter(this, void 0, void 0, function* () { - if (checkRustInstalled()) { - return; - } - const res = yield prompts({ - type: 'confirm', - message: 'We detected that you have not installed Rust. Install it now?', - name: 'value', - }); - if (res.value) { - // TODO 国内有可能会超时 - yield installRust(); - } - else { - log.error('Error: Pake need Rust to package your webapp!!!'); - process.exit(2); - } - }); - } - build(url, options) { - return __awaiter(this, void 0, void 0, function* () { - log.debug('PakeAppOptions', options); - const { name } = options; - yield mergeTauriConfig(url, options, tauriConf); - let dmgName; - if (options.multiArch) { - const isChina = yield isChinaDomain("www.npmjs.com"); - if (isChina) { - logger.info("it's in China, use npm/rust cn mirror"); - const rust_project_dir = path.join(npmDirectory, 'src-tauri', ".cargo"); - const e1 = fs$1.access(rust_project_dir); - if (e1) { - yield fs$1.mkdir(rust_project_dir, { recursive: true }); - } - const project_cn_conf = path.join(npmDirectory, "src-tauri", "cn_config.bak"); - const project_conf = path.join(rust_project_dir, "config"); - fs$1.copyFile(project_cn_conf, project_conf); - yield shellExec(`cd "${npmDirectory}" && npm install --registry=https://registry.npmmirror.com && npm run build:mac`); - } - else { - yield shellExec(`cd "${npmDirectory}" && npm install && npm run build:mac`); - } - dmgName = `${name}_${tauriConf.package.version}_universal.dmg`; - } - else { - const isChina = isChinaDomain("www.npmjs.com"); - if (isChina) { - yield shellExec(`cd ${npmDirectory} && npm install --registry=https://registry.npmmirror.com && npm run build`); - } - else { - yield shellExec(`cd ${npmDirectory} && npm install && npm run build`); - } - let arch = "x64"; - if (process.arch === "arm64") { - arch = "aarch64"; - } - else { - arch = process.arch; - } - dmgName = `${name}_${tauriConf.package.version}_${arch}.dmg`; - } - const appPath = this.getBuildAppPath(npmDirectory, dmgName, options.multiArch); - const distPath = path.resolve(`${name}.dmg`); - yield fs$1.copyFile(appPath, distPath); - yield fs$1.unlink(appPath); - logger.success('Build success!'); - logger.success('You can find the app installer in', distPath); - }); - } - getBuildAppPath(npmDirectory, dmgName, multiArch) { - let dmgPath; - if (multiArch) { - dmgPath = 'src-tauri/target/universal-apple-darwin/release/bundle/dmg'; - } - else { - dmgPath = 'src-tauri/target/release/bundle/dmg'; - } - return path.join(npmDirectory, dmgPath, dmgName); - } +const resolve = promisify(dns.resolve); +const ping = async (host) => { + const lookup = promisify(dns.lookup); + const ip = await lookup(host); + const start = new Date(); + return 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); + }); + }); +}; +async function isChinaDomain(domain) { + try { + const [ip] = await resolve(domain); + return await isChinaIP(ip, domain); + } + catch (error) { + logger.info(`${domain} can't be parse!`); + return false; + } +} +async function isChinaIP(ip, domain) { + try { + const delay = await ping(ip); + logger.info(`${domain} latency is ${delay} ms`); + return delay > 500; + } + catch (error) { + logger.info(`ping ${domain} failed!`); + return false; + } } -class WinBuilder { - prepare() { - return __awaiter(this, void 0, void 0, function* () { - logger.info('To build the Windows app, you need to install Rust and VS Build Tools.'); - logger.info('See more in https://tauri.app/v1/guides/getting-started/prerequisites#installing\n'); - if (checkRustInstalled()) { - return; - } - const res = yield prompts({ - type: 'confirm', - message: 'We detected that you have not installed Rust. Install it now?', - name: 'value', - }); - if (res.value) { - // TODO 国内有可能会超时 - yield installRust(); - } - else { - logger.error('Error: Pake needs Rust to package your webapp!!!'); - process.exit(2); - } - }); - } - build(url, options) { - return __awaiter(this, void 0, void 0, function* () { - logger.debug('PakeAppOptions', options); - const { name } = options; - yield mergeTauriConfig(url, options, tauriConf); - const isChina = yield isChinaDomain("www.npmjs.com"); - if (isChina) { - logger.info("it's in China, use npm/rust cn mirror"); - const rust_project_dir = path.join(npmDirectory, 'src-tauri', ".cargo"); - const e1 = fs$1.access(rust_project_dir); - if (e1) { - yield fs$1.mkdir(rust_project_dir, { recursive: true }); - } - const project_cn_conf = path.join(npmDirectory, "src-tauri", "cn_config.bak"); - const project_conf = path.join(rust_project_dir, "config"); - fs$1.copyFile(project_cn_conf, project_conf); - yield shellExec(`cd "${npmDirectory}" && npm install --registry=https://registry.npmmirror.com && npm run build`); - } - else { - yield shellExec(`cd "${npmDirectory}" && npm install && npm run build`); - } - const language = tauriConf.tauri.bundle.windows.wix.language[0]; - const arch = process.arch; - const msiName = `${name}_${tauriConf.package.version}_${arch}_${language}.msi`; - const appPath = this.getBuildedAppPath(npmDirectory, msiName); - const distPath = path.resolve(`${name}.msi`); - yield fs$1.copyFile(appPath, distPath); - yield fs$1.unlink(appPath); - logger.success('Build success!'); - logger.success('You can find the app installer in', distPath); - }); - } - getBuildedAppPath(npmDirectory, dmgName) { - return path.join(npmDirectory, 'src-tauri/target/release/bundle/msi', dmgName); - } +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 = ora('Downloading Rust').start(); + try { + await shellExec(IS_WIN ? rustInstallScriptForWindows : rustInstallScriptForMac); + spinner.succeed(); + } + catch (error) { + console.error('Error installing Rust:', error.message); + spinner.fail(); + //@ts-ignore + process.exit(1); + } +} +function checkRustInstalled() { + return shelljs.exec('rustc --version', { silent: true }).code === 0; } -class LinuxBuilder { - prepare() { - return __awaiter(this, void 0, void 0, function* () { - logger.info('To build the Linux app, you need to install Rust and Linux package'); - logger.info('See more in https://tauri.app/v1/guides/getting-started/prerequisites#installing\n'); - if (checkRustInstalled()) { - return; - } - const res = yield prompts({ - type: 'confirm', - message: 'We detected that you have not installed Rust. Install it now?', - name: 'value', - }); - if (res.value) { - // TODO 国内有可能会超时 - yield installRust(); - } - else { - logger.error('Error: Pake needs Rust to package your webapp!!!'); - process.exit(2); - } - }); - } - build(url, options) { - return __awaiter(this, void 0, void 0, function* () { - logger.debug('PakeAppOptions', options); - const { name } = options; - yield mergeTauriConfig(url, options, tauriConf); - const isChina = yield isChinaDomain("www.npmjs.com"); - if (isChina) { - logger.info("it's in China, use npm/rust cn mirror"); - const rust_project_dir = path.join(npmDirectory, 'src-tauri', ".cargo"); - const e1 = fs$1.access(rust_project_dir); - if (e1) { - yield fs$1.mkdir(rust_project_dir, { recursive: true }); - } - const project_cn_conf = path.join(npmDirectory, "src-tauri", "cn_config.bak"); - const project_conf = path.join(rust_project_dir, "config"); - fs$1.copyFile(project_cn_conf, project_conf); - yield shellExec(`cd "${npmDirectory}" && npm install --registry=https://registry.npmmirror.com && npm run build`); - } - else { - yield shellExec(`cd "${npmDirectory}" && npm install && npm run build`); - } - let arch; - if (process.arch === "x64") { - arch = "amd64"; - } - else { - arch = process.arch; - } - if (options.targets === "deb" || options.targets === "all") { - const debName = `${name}_${tauriConf.package.version}_${arch}.deb`; - const appPath = this.getBuildAppPath(npmDirectory, "deb", debName); - const distPath = path.resolve(`${name}.deb`); - yield fs$1.copyFile(appPath, distPath); - yield fs$1.unlink(appPath); - logger.success('Build Deb success!'); - logger.success('You can find the deb app installer in', distPath); - } - if (options.targets === "appimage" || options.targets === "all") { - const appImageName = `${name}_${tauriConf.package.version}_${arch}.AppImage`; - const appImagePath = this.getBuildAppPath(npmDirectory, "appimage", appImageName); - const distAppPath = path.resolve(`${name}.AppImage`); - yield fs$1.copyFile(appImagePath, distAppPath); - yield fs$1.unlink(appImagePath); - logger.success('Build AppImage success!'); - logger.success('You can find the AppImage app installer in', distAppPath); - } - }); - } - getBuildAppPath(npmDirectory, packageType, packageName) { - return path.join(npmDirectory, 'src-tauri/target/release/bundle/', packageType, packageName); - } +class BaseBuilder { + async prepare() { + // Windows and Linux need to install necessary build tools. + if (!IS_MAC) { + logger.info('To build the app, you need to install Rust and necessary build tools.'); + logger.info('See more in https://tauri.app/v1/guides/getting-started/prerequisites#installing.'); + } + if (checkRustInstalled()) { + return; + } + const res = await prompts({ + type: 'confirm', + message: 'We detected that you have not installed Rust. Install it now?', + name: 'value', + }); + if (res.value) { + await installRust(); + } + else { + logger.error('Error: Pake needs Rust to package your webapp!'); + process.exit(2); + } + } + async runBuildCommand(directory, command) { + const isChina = await isChinaDomain("www.npmjs.com"); + if (isChina) { + logger.info("it's in China, use npm/rust cn mirror"); + logger.debug("pake npm directory: ", directory); + const rustProjectDir = path.join(directory, 'src-tauri', ".cargo"); + try { + await fs.access(rustProjectDir); + } + catch (e) { + await fs.mkdir(rustProjectDir, { recursive: true }); + } + const projectCnConf = path.join(directory, "src-tauri", "rust_proxy.toml"); + const projectConf = path.join(rustProjectDir, "config"); + await fs.copyFile(projectCnConf, projectConf); + await shellExec(`cd "${directory}" && npm install --registry=https://registry.npmmirror.com && ${command}`); + } + else { + await shellExec(`cd "${directory}" && npm install && ${command}`); + } + } } -class BuilderFactory { - static create() { - if (IS_MAC) { - return new MacBuilder(); - } - if (IS_WIN) { - return new WinBuilder(); - } - if (IS_LINUX) { - return new LinuxBuilder(); - } - throw new Error('The current system does not support!!'); - } +function setSecurityConfigWithUrl(tauriConfig, url) { + tauriConfig.tauri.security.dangerousRemoteDomainIpcAccess[0].domain = + new URL(url).hostname; +} +async function writeFileAsJson(filePath, data) { + await fs.writeFile(filePath, JSON.stringify(data, null, 4), 'utf-8'); +} +async function mergeConfig(url, options, tauriConf) { + const { width, height, fullscreen, transparent, resizable, userAgent, showMenu, showSystemTray, systemTrayIcon, iterCopyFile, identifier, name, } = options; + const tauriConfWindowOptions = { + width, + height, + fullscreen, + transparent, + resizable, + }; + // Package name is valid ? + // for Linux, package name must be a-z, 0-9 or "-", not allow to A-Z and other + const { platform } = process; + const platformRegexMapping = { + linux: /[0-9]*[a-z]+[0-9]*\-?[0-9]*[a-z]*[0-9]*\-?[0-9]*[a-z]*[0-9]*/, + default: /([0-9]*[a-zA-Z]+[0-9]*)+/, + }; + const reg = platformRegexMapping[platform] || platformRegexMapping.default; + const nameCheck = reg.test(name) && reg.exec(name)[0].length === name.length; + if (!nameCheck) { + const errorMsg = platform === 'linux' + ? `Package name is illegal. It must be lowercase letters, numbers, dashes, and it must contain the lowercase letters. E.g com-123-xxx, 123pan, pan123,weread, we-read` + : `Package name is illegal. It must be letters, numbers, and it must contain the letters. E.g 123pan,123Pan Pan123,weread, WeRead, WERead`; + logger.error(errorMsg); + process.exit(); + } + Object.assign(tauriConf.pake.windows[0], { url, ...tauriConfWindowOptions }); + tauriConf.package.productName = name; + tauriConf.tauri.bundle.identifier = identifier; + // 判断一下url类型,是文件还是网站 + // 如果是文件,并且开启了递归拷贝功能,则需要将该文件以及所在文件夹下的所有文件拷贝到src目录下,否则只拷贝单个文件。 + const urlExists = await fs + .stat(url) + .then(() => true) + .catch(() => false); + if (urlExists) { + logger.warn('You input may 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 fs.copyFile(url, urlPath); + } + else { + fs2.moveSync(distDir, distBakDir, { overwrite: true }); + fs2.copySync(dirName, distDir, { overwrite: true }); + const filesToCopyBack = ['cli.js', 'about_pake.html']; + await Promise.all(filesToCopyBack.map((file) => fs.copyFile(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.menu[currentPlatform] = showMenu; + tauriConf.pake.system_tray[currentPlatform] = showSystemTray; + // 处理targets 暂时只对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(`Targets must be one of ${validTargets.join(', ')}, we will use default 'all'`); + } + } + const platformIconMap = { + win32: { + fileExt: '.ico', + path: `png/${name.toLowerCase()}_32.ico`, + defaultIcon: 'png/icon_256.ico', + message: 'Icon file in Windows must be 256 * 256 pix with .ico type', + }, + linux: { + fileExt: '.png', + path: `png/${name.toLowerCase()}_32.png`, + defaultIcon: 'png/icon_512.png', + message: 'Icon file in Linux must be 512 * 512 pix with .png type', + }, + darwin: { + fileExt: '.icns', + path: `icons/${name.toLowerCase()}_32.icns`, + defaultIcon: 'icons/icon.icns', + message: 'Icon file in MacOS must be .icns type', + }, + }; + const exists = await fs + .stat(options.icon) + .then(() => true) + .catch(() => false); + const iconInfo = platformIconMap[platform]; + 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 fs.copyFile(options.icon, iconPath); + } + if (updateIconPath) { + tauriConf.tauri.bundle.icon = [options.icon]; + } + else { + logger.warn(`Icon file will not change with default.`); + } + } + else { + logger.warn('The custom icon path may not exists. we will use default icon to replace it'); + tauriConf.tauri.bundle.icon = [iconInfo.defaultIcon]; + } + // 设定默认托盘图标路径 + let trayIconPath = platform === 'linux' || platform === 'win32' + ? tauriConf.tauri.bundle.icon[0] + : 'png/icon_512.png'; + if (systemTrayIcon.length > 0) { + try { + await fs.stat(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 fs.copyFile(systemTrayIcon, trayIcoPath); + } + else { + logger.warn(`File type for system tray icon mut be .ico or .png , but you give ${iconExt}`); + logger.warn(`System tray icon file will not change with default.`); + } + } + catch { + logger.warn(`${systemTrayIcon} not exists!`); + logger.warn(`System tray icon file will not change with default.`); + } + } + // 设定托盘图标 + tauriConf.tauri.systemTray.iconPath = trayIconPath; + // 设置安全调用 window.__TAURI__ 的安全域名为设置的应用域名 + setSecurityConfigWithUrl(tauriConf, url); + // 保存配置文件 + 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 writeFileAsJson(configPath, bundleConf); + const pakeConfigPath = path.join(npmDirectory, 'src-tauri/pake.json'); + await writeFileAsJson(pakeConfigPath, tauriConf.pake); + 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 writeFileAsJson(configJsonPath, tauriConf2); +} + +class MacBuilder extends BaseBuilder { + async build(url, options) { + const { name } = options; + await mergeConfig(url, options, tauriConfig); + let dmgName; + if (options.multiArch) { + await this.runBuildCommand(npmDirectory, 'npm run build:mac'); + dmgName = `${name}_${tauriConfig.package.version}_universal.dmg`; + } + else { + await this.runBuildCommand(npmDirectory, 'npm run build'); + let arch = process.arch === "arm64" ? "aarch64" : process.arch; + dmgName = `${name}_${tauriConfig.package.version}_${arch}.dmg`; + } + const appPath = this.getBuildAppPath(npmDirectory, dmgName, options.multiArch); + const distPath = path.resolve(`${name}.dmg`); + await fs.copyFile(appPath, distPath); + await fs.unlink(appPath); + logger.success('Build success!'); + logger.success('You can find the app installer in', distPath); + } + getBuildAppPath(npmDirectory, dmgName, multiArch) { + const dmgPath = multiArch ? 'src-tauri/target/universal-apple-darwin/release/bundle/dmg' : 'src-tauri/target/release/bundle/dmg'; + return path.join(npmDirectory, dmgPath, dmgName); + } +} + +class WinBuilder extends BaseBuilder { + async build(url, options) { + const { name } = options; + await mergeConfig(url, options, tauriConfig); + await this.runBuildCommand(npmDirectory, 'npm run build'); + const language = tauriConfig.tauri.bundle.windows.wix.language[0]; + const arch = process.arch; + const msiName = `${name}_${tauriConfig.package.version}_${arch}_${language}.msi`; + const appPath = this.getBuildAppPath(npmDirectory, msiName); + const distPath = path.resolve(`${name}.msi`); + await fs.copyFile(appPath, distPath); + await fs.unlink(appPath); + logger.success('Build success!'); + logger.success('You can find the app installer in', distPath); + } + getBuildAppPath(npmDirectory, msiName) { + return path.join(npmDirectory, 'src-tauri/target/release/bundle/msi', msiName); + } +} + +class LinuxBuilder extends BaseBuilder { + async build(url, options) { + const { name } = options; + await mergeConfig(url, options, tauriConfig); + await this.runBuildCommand(npmDirectory, 'npm run build'); + const arch = process.arch === "x64" ? "amd64" : process.arch; + if (options.targets === "deb" || options.targets === "all") { + const debName = `${name}_${tauriConfig.package.version}_${arch}.deb`; + const appPath = this.getBuildAppPath(npmDirectory, "deb", debName); + const distPath = path.resolve(`${name}.deb`); + await fs.copyFile(appPath, distPath); + await fs.unlink(appPath); + logger.success('Build Deb success!'); + logger.success('You can find the deb app installer in', distPath); + } + if (options.targets === "appimage" || options.targets === "all") { + const appImageName = `${name}_${tauriConfig.package.version}_${arch}.AppImage`; + const appImagePath = this.getBuildAppPath(npmDirectory, "appimage", appImageName); + const distAppPath = path.resolve(`${name}.AppImage`); + await fs.copyFile(appImagePath, distAppPath); + await fs.unlink(appImagePath); + logger.success('Build AppImage success!'); + logger.success('You can find the AppImage app installer in', distAppPath); + } + } + getBuildAppPath(npmDirectory, packageType, packageName) { + return path.join(npmDirectory, 'src-tauri/target/release/bundle/', packageType, packageName); + } +} + +class BuilderProvider { + static create() { + if (IS_MAC) { + return new MacBuilder(); + } + if (IS_WIN) { + return new WinBuilder(); + } + if (IS_LINUX) { + return new LinuxBuilder(); + } + throw new Error('The current system is not supported!'); + } } var name = "pake-cli"; -var version = "2.0.7"; +var version = "2.1.0-beta2"; var description = "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。"; var engines = { node: ">=16.0.0" @@ -2594,7 +814,7 @@ var exports = "./dist/pake.js"; var license = "MIT"; var dependencies = { "@tauri-apps/api": "^1.4.0", - "@tauri-apps/cli": "1.3.1", + "@tauri-apps/cli": "^1.4.0", axios: "^1.1.3", chalk: "^5.1.2", commander: "^11.0.0", @@ -2604,6 +824,7 @@ var dependencies = { loglevel: "^1.8.1", ora: "^6.1.2", prompts: "^2.4.2", + psl: "^1.9.0", shelljs: "^0.8.5", "tmp-promise": "^3.0.3", "update-notifier": "^6.0.2" @@ -2611,19 +832,20 @@ var dependencies = { var devDependencies = { "@rollup/plugin-alias": "^4.0.2", "@rollup/plugin-commonjs": "^23.0.2", - "@rollup/plugin-json": "^5.0.1", + "@rollup/plugin-json": "^5.0.2", "@rollup/plugin-terser": "^0.1.0", - "@rollup/plugin-typescript": "^9.0.2", "@types/fs-extra": "^9.0.13", "@types/is-url": "^1.2.30", "@types/page-icon": "^0.3.4", "@types/prompts": "^2.4.1", + "@types/psl": "^1.1.0", "@types/shelljs": "^0.8.11", "@types/tmp": "^0.2.3", "@types/update-notifier": "^6.0.1", "app-root-path": "^3.1.0", "cross-env": "^7.0.3", rollup: "^3.3.0", + "rollup-plugin-typescript2": "^0.34.1", tslib: "^2.4.1", typescript: "^4.9.3" }; @@ -2645,46 +867,83 @@ var packageJson = { devDependencies: devDependencies }; -function checkUpdateTips() { - return __awaiter(this, void 0, void 0, function* () { - updateNotifier({ pkg: packageJson }).notify(); - }); +async function checkUpdateTips() { + updateNotifier({ pkg: packageJson }).notify(); } -program.version(packageJson.version).description('A cli application can package a web page to desktop application.'); -program - .showHelpAfterError() - .argument('[url]', 'the web url you want to package', validateUrlInput) - .option('--name ', 'application name') - .option('--icon ', 'application icon', DEFAULT_PAKE_OPTIONS.icon) - .option('--height ', 'window height', validateNumberInput, DEFAULT_PAKE_OPTIONS.height) - .option('--width ', 'window width', validateNumberInput, DEFAULT_PAKE_OPTIONS.width) - .option('--no-resizable', 'whether the window can be resizable', DEFAULT_PAKE_OPTIONS.resizable) - .option('--fullscreen', 'makes the packaged app start in full screen', DEFAULT_PAKE_OPTIONS.fullscreen) - .option('--transparent', 'transparent title bar', DEFAULT_PAKE_OPTIONS.transparent) - .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 all static file to pake app when url is a local file', DEFAULT_PAKE_OPTIONS.iterCopyFile) - .option('-m, --multi-arch', "available for Mac only, and supports both Intel and M1", DEFAULT_PAKE_OPTIONS.multiArch) - .option('--targets ', 'only for linux, default is "deb", option "appaimge" or "all"(deb & appimage)', DEFAULT_PAKE_OPTIONS.targets) - .option('--debug', 'debug', DEFAULT_PAKE_OPTIONS.transparent) - .action((url, options) => __awaiter(void 0, void 0, void 0, function* () { - checkUpdateTips(); - if (!url) { - // 直接 pake 不需要出现url提示 - program.help(); - } - log.setDefaultLevel('info'); - if (options.debug) { - log.setLevel('debug'); - } - const builder = BuilderFactory.create(); - yield builder.prepare(); - // logger.warn("you input url is ", url); - const appOptions = yield handleOptions(options, url); - // logger.info(JSON.stringify(appOptions, null, 4)); - builder.build(url, 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$1.existsSync(url); + if (!isFile) { + try { + return normalizeUrl(url); + } + catch (error) { + throw new InvalidArgumentError(error.message); + } + } + return url; +} + +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, +}; + +program + .version(packageJson.version) + .description('A CLI that can turn any webpage into a desktop app with Rust.') + .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('--height ', 'Window height', validateNumberInput, DEFAULT_PAKE_OPTIONS.height) + .option('--width ', 'Window width', validateNumberInput, DEFAULT_PAKE_OPTIONS.width) + .option('--no-resizable', 'Whether the window can be resizable', DEFAULT_PAKE_OPTIONS.resizable) + .option('--fullscreen', 'Start the packaged app in full screen', DEFAULT_PAKE_OPTIONS.fullscreen) + .option('--transparent', 'Transparent title bar', DEFAULT_PAKE_OPTIONS.transparent) + .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 all static files to pake app when URL is a local file', DEFAULT_PAKE_OPTIONS.iterCopyFile) + .option('--multi-arch', 'Available for Mac only, supports both Intel and M1', DEFAULT_PAKE_OPTIONS.multiArch) + .option('--targets ', 'Only for Linux, option "deb", "appimage" or "all"', DEFAULT_PAKE_OPTIONS.targets) + .option('--debug', 'Debug mode', DEFAULT_PAKE_OPTIONS.debug) + .action(async (url, options) => { + //Check for update prompt + await checkUpdateTips(); + // If no URL is provided, display help information + if (!url) { + program.help(); + } + log.setDefaultLevel('info'); + if (options.debug) { + log.setLevel('debug'); + } + const builder = BuilderProvider.create(); + await builder.prepare(); + const appOptions = await handleOptions(options, url); + log.debug('PakeAppOptions', appOptions); + await builder.build(url, appOptions); +}); program.parse(); diff --git a/package.json b/package.json index 62978a2..60e4974 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pake-cli", - "version": "2.0.7", + "version": "2.1.0", "description": "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。", "engines": { "node": ">=16.0.0" @@ -57,6 +57,7 @@ "loglevel": "^1.8.1", "ora": "^6.1.2", "prompts": "^2.4.2", + "psl": "^1.9.0", "shelljs": "^0.8.5", "tmp-promise": "^3.0.3", "update-notifier": "^6.0.2" @@ -64,19 +65,20 @@ "devDependencies": { "@rollup/plugin-alias": "^4.0.2", "@rollup/plugin-commonjs": "^23.0.2", - "@rollup/plugin-json": "^5.0.1", + "@rollup/plugin-json": "^5.0.2", "@rollup/plugin-terser": "^0.1.0", - "@rollup/plugin-typescript": "^9.0.2", "@types/fs-extra": "^9.0.13", "@types/is-url": "^1.2.30", "@types/page-icon": "^0.3.4", "@types/prompts": "^2.4.1", + "@types/psl": "^1.1.0", "@types/shelljs": "^0.8.11", "@types/tmp": "^0.2.3", "@types/update-notifier": "^6.0.1", "app-root-path": "^3.1.0", "cross-env": "^7.0.3", "rollup": "^3.3.0", + "rollup-plugin-typescript2": "^0.34.1", "tslib": "^2.4.1", "typescript": "^4.9.3" } diff --git a/rollup.config.js b/rollup.config.js index 6fdfbba..0c21614 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,6 +1,6 @@ import path from 'path'; import appRootPath from 'app-root-path'; -import typescript from '@rollup/plugin-typescript'; +import typescript from 'rollup-plugin-typescript2'; import alias from '@rollup/plugin-alias'; import commonjs from '@rollup/plugin-commonjs'; import json from '@rollup/plugin-json'; @@ -14,7 +14,8 @@ export default { plugins: [ json(), typescript({ - sourceMap: false, + tsconfig: "tsconfig.json", + clean: true, // 清理缓存 }), commonjs(), alias({ diff --git a/script/build.bat b/script/build.bat index 7dccb34..15344d3 100644 --- a/script/build.bat +++ b/script/build.bat @@ -96,4 +96,4 @@ echo "output dir is output\windows" .\script\sd.exe %url% %init_url% src-tauri\pake.json .\script\sd.exe %title% %init_title% src-tauri\tauri.conf.json .\script\sd.exe %name% %init_name% src-tauri\tauri.conf.json -.\script\sd.exe %name% %init_name% src-tauri\tauri.windows.conf.json \ No newline at end of file +.\script\sd.exe %name% %init_name% src-tauri\tauri.windows.conf.json diff --git a/src-tauri/cn_config.bak b/src-tauri/rust_proxy.toml similarity index 100% rename from src-tauri/cn_config.bak rename to src-tauri/rust_proxy.toml diff --git a/tsconfig.json b/tsconfig.json index d5cb79e..012cdb2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,17 +1,25 @@ { "compilerOptions": { - "module": "Node16", + "module": "ESNext", + "target": "es2020", + "types": [ + "node" + ], "esModuleInterop": true, + "resolveJsonModule": true, "allowSyntheticDefaultImports": true, - "target": "es6", "noImplicitAny": true, - "moduleResolution": "Node16", + "moduleResolution": "node", "sourceMap": true, "outDir": "dist", "baseUrl": ".", "paths": { - "@/*": ["bin/*"] + "@/*": [ + "bin/*" + ] } }, - "include": ["bin/**/*"] + "include": [ + "bin/**/*" + ] } From bb2f83eed64d76c4a5963e743a689231f9071970 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Thu, 22 Jun 2023 14:38:19 +0800 Subject: [PATCH 16/40] :art: Rename common --- bin/builders/LinuxBuilder.ts | 2 +- bin/builders/MacBuilder.ts | 2 +- bin/builders/WinBuilder.ts | 2 +- bin/{builders/common.ts => helpers/merge.ts} | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) rename bin/{builders/common.ts => helpers/merge.ts} (98%) diff --git a/bin/builders/LinuxBuilder.ts b/bin/builders/LinuxBuilder.ts index ccae449..6f0dcc8 100644 --- a/bin/builders/LinuxBuilder.ts +++ b/bin/builders/LinuxBuilder.ts @@ -6,7 +6,7 @@ import logger from '@/options/logger'; import tauriConfig from '@/helpers/tauriConfig'; import { npmDirectory } from '@/utils/dir'; import { PakeAppOptions } from '@/types'; -import { mergeConfig } from "@/builders/common"; +import { mergeConfig } from "@/helpers/merge"; export default class LinuxBuilder extends BaseBuilder { async build(url: string, options: PakeAppOptions) { diff --git a/bin/builders/MacBuilder.ts b/bin/builders/MacBuilder.ts index 8f6e8ef..abfd18e 100644 --- a/bin/builders/MacBuilder.ts +++ b/bin/builders/MacBuilder.ts @@ -6,7 +6,7 @@ import tauriConfig from '@/helpers/tauriConfig'; import BaseBuilder from './BaseBuilder'; import { npmDirectory } from '@/utils/dir'; import { PakeAppOptions } from '@/types'; -import { mergeConfig } from "@/builders/common"; +import { mergeConfig } from "@/helpers/merge"; export default class MacBuilder extends BaseBuilder { async build(url: string, options: PakeAppOptions) { diff --git a/bin/builders/WinBuilder.ts b/bin/builders/WinBuilder.ts index f831786..a0557a4 100644 --- a/bin/builders/WinBuilder.ts +++ b/bin/builders/WinBuilder.ts @@ -6,7 +6,7 @@ import logger from '@/options/logger'; import tauriConfig from '@/helpers/tauriConfig'; import { npmDirectory } from '@/utils/dir'; import { PakeAppOptions } from '@/types'; -import { mergeConfig } from '@/builders/common'; +import { mergeConfig } from '@/helpers/merge'; export default class WinBuilder extends BaseBuilder { async build(url: string, options: PakeAppOptions) { diff --git a/bin/builders/common.ts b/bin/helpers/merge.ts similarity index 98% rename from bin/builders/common.ts rename to bin/helpers/merge.ts index 66f7a78..e4972cc 100644 --- a/bin/builders/common.ts +++ b/bin/helpers/merge.ts @@ -1,8 +1,8 @@ import path from 'path'; import fsExtra from 'fs-extra'; -import { npmDirectory } from '@/utils/dir.js'; -import logger from '@/options/logger.js'; -import { PakeAppOptions, PlatformMap } from '@/types.js'; +import { npmDirectory } from '@/utils/dir'; +import logger from '@/options/logger'; +import { PakeAppOptions, PlatformMap } from '@/types'; export async function mergeConfig( url: string, From 1bb70661dbd9e780b7cb04c1d4c5fd707cbb2514 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Thu, 22 Jun 2023 14:48:38 +0800 Subject: [PATCH 17/40] :bug: Correct spelling --- .github/workflows/pake-cli.yaml | 5 ----- bin/types.ts | 6 +++--- script/build.bat | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/.github/workflows/pake-cli.yaml b/.github/workflows/pake-cli.yaml index 18a7697..e0113ef 100644 --- a/.github/workflows/pake-cli.yaml +++ b/.github/workflows/pake-cli.yaml @@ -33,11 +33,6 @@ on: required: false type: boolean default: false - # fullscreen: - # description: "[FullScreen, Optional]" - # required: false - # type: boolean - # default: false resize: description: "[Resize, Optional]" required: false diff --git a/bin/types.ts b/bin/types.ts index 20725a2..02739b9 100644 --- a/bin/types.ts +++ b/bin/types.ts @@ -28,13 +28,13 @@ export interface PakeCliOptions { // Custom User-Agent, default off userAgent: string; - // Enable menu bar, default on for MacOS, off for Windows and Linux + // Enable menu bar, default on for macOS, off for Windows and Linux showMenu: boolean; - // Enable system tray, default off for MacOS, on for Windows and Linux + // Enable system tray, default off for macOS, on for Windows and Linux showSystemTray: boolean; - // Tray icon, default same as app icon for Windows and Linux, MacOS requires separate png or ico + // Tray icon, default same as app icon for Windows and Linux, macOS requires separate png or ico systemTrayIcon: string; // Recursive copy, when url is a local file path, if this option is enabled, the url path file and all its subfiles will be copied to the pake static file folder, default off diff --git a/script/build.bat b/script/build.bat index 15344d3..05bdbc6 100644 --- a/script/build.bat +++ b/script/build.bat @@ -51,7 +51,7 @@ for /f "skip=1 tokens=1-4 delims=," %%i in (app.csv) do ( ::echo name is !name! !name_zh! !url! :: replace url .\script\sd.exe -s !old_url! !url! src-tauri\pake.json - ::replace pacakge name + ::replace package name .\script\sd.exe !old_title! !title! src-tauri\tauri.conf.json .\script\sd.exe !old_name! !name! src-tauri\tauri.conf.json .\script\sd.exe !old_name! !name! src-tauri\tauri.windows.conf.json From f1f1f790d87628e83b635d5d1a217fd7decec73c Mon Sep 17 00:00:00 2001 From: Tw93 Date: Thu, 22 Jun 2023 14:59:32 +0800 Subject: [PATCH 18/40] :art: Words are clearer --- bin/cli.ts | 2 +- bin/helpers/merge.ts | 4 +- dist/cli.js | 305 +++++++++++++++++++------------------------ 3 files changed, 139 insertions(+), 172 deletions(-) diff --git a/bin/cli.ts b/bin/cli.ts index 3c070c2..9823b16 100644 --- a/bin/cli.ts +++ b/bin/cli.ts @@ -27,7 +27,7 @@ program .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 all static files to pake app when URL is a local file', DEFAULT_PAKE_OPTIONS.iterCopyFile) + .option('--iter-copy-file', 'Copy files to app when URL is a local file', DEFAULT_PAKE_OPTIONS.iterCopyFile) .option('--multi-arch', 'Available for Mac only, supports both Intel and M1', DEFAULT_PAKE_OPTIONS.multiArch) .option('--targets ', 'Only for Linux, option "deb", "appimage" or "all"', DEFAULT_PAKE_OPTIONS.targets) .option('--debug', 'Debug mode', DEFAULT_PAKE_OPTIONS.debug) diff --git a/bin/helpers/merge.ts b/bin/helpers/merge.ts index e4972cc..c822aaf 100644 --- a/bin/helpers/merge.ts +++ b/bin/helpers/merge.ts @@ -49,8 +49,8 @@ export async function mergeConfig( if (!nameCheck) { const errorMsg = platform === 'linux' - ? `Package name is invalid. It should only include lowercase letters, numbers, and dashes, and must contain at least one lowercase letter.\n Examples: com-123-xxx, 123pan, pan123, weread, we-read.` - : `Package name is invalid. It should only include letters and numbers, and must contain at least one letter.\n Examples: 123pan, 123Pan, Pan123, weread, WeRead, WERead.`; + ? `Package name is invalid. It 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.` + : `Package name is invalid. It should only include letters and numbers, and must contain at least one letter. Examples: 123pan, 123Pan, Pan123, weread, WeRead, WERead.`; logger.error(errorMsg); process.exit(); diff --git a/dist/cli.js b/dist/cli.js index 35ce0ba..30ae112 100644 --- a/dist/cli.js +++ b/dist/cli.js @@ -1,24 +1,92 @@ import log from 'loglevel'; import { InvalidArgumentError, program } from 'commander'; -import fs from 'fs/promises'; -import psl from 'psl'; -import isUrl from 'is-url'; -import crypto from 'crypto'; -import prompts from 'prompts'; +import fsExtra from 'fs-extra'; import path from 'path'; import axios from 'axios'; import { dir } from 'tmp-promise'; import { fileTypeFromBuffer } from 'file-type'; import chalk from 'chalk'; import { fileURLToPath } from 'url'; +import psl from 'psl'; +import isUrl from 'is-url'; +import crypto from 'crypto'; +import prompts from 'prompts'; import shelljs from 'shelljs'; import dns from 'dns'; import http from 'http'; import { promisify } from 'util'; import ora from 'ora'; -import fs2 from 'fs-extra'; import updateNotifier from 'update-notifier'; -import fs$1 from 'fs'; +import fs from 'fs'; + +const logger = { + info(...msg) { + log.info(...msg.map((m) => chalk.blue.bold(m))); + }, + debug(...msg) { + log.debug(...msg); + }, + error(...msg) { + log.error(...msg.map((m) => chalk.red.bold(m))); + }, + warn(...msg) { + log.info(...msg.map((m) => chalk.yellow.bold(m))); + }, + success(...msg) { + log.info(...msg.map((m) => chalk.green.bold(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 { platform: platform$1 } = process; +const IS_MAC = platform$1 === 'darwin'; +const IS_WIN = platform$1 === 'win32'; +const IS_LINUX = platform$1 === 'linux'; + +async function handleIcon(options) { + if (options.icon) { + if (options.icon.startsWith('http')) { + return downloadIcon(options.icon); + } + else { + return path.resolve(options.icon); + } + } + else { + logger.info('No app icon provided, default icon used. Use --icon option to assign an icon.'); + 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) { + 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); + return iconPath; + } + catch (error) { + if (error.response && error.response.status === 404) { + return null; + } + throw error; + } +} // Extracts the domain from a given URL. function getDomain(inputUrl) { @@ -77,97 +145,15 @@ async function promptText(message, initial) { return response.content; } -function formatMessage(color, ...msg) { - return msg.map((m) => color(m)); -} -const logger = { - info(...msg) { - log.info(...formatMessage(chalk.blue, ...msg)); - }, - debug(...msg) { - log.debug(...formatMessage(chalk.magenta, ...msg)); - }, - error(...msg) { - log.error(...formatMessage(chalk.red, ...msg)); - }, - warn(...msg) { - log.warn(...formatMessage(chalk.yellow, ...msg)); - }, - success(...msg) { - log.info(...formatMessage(chalk.green, ...msg)); - } -}; - -// 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 { platform: platform$1 } = process; -const IS_MAC = platform$1 === 'darwin'; -const IS_WIN = platform$1 === 'win32'; -const IS_LINUX = platform$1 === 'linux'; - -async function handleIcon(options, url) { - if (options.icon) { - if (options.icon.startsWith('http')) { - return downloadIcon(options.icon); - } - else { - return path.resolve(options.icon); - } - } - else { - return getDefaultIcon(); - } -} -async function getDefaultIcon() { - logger.info('You have not provided an app icon, use the default icon.(use --icon option to assign an icon)'); - 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) { - 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 fs.writeFile(iconPath, iconData); - return iconPath; - } - catch (error) { - if (error.response && error.response.status === 404) { - return null; - } - throw error; - } -} - async function handleOptions(options, url) { const appOptions = { ...options, identifier: getIdentifier(url), }; - let urlExists = false; - try { - await fs.stat(url); - urlExists = true; - } - catch (error) { - // URL does not exist - } + let urlExists = await fsExtra.pathExists(url); if (!appOptions.name) { const defaultName = urlExists ? "" : getDomain(url); - const promptMessage = 'Please input your application name'; + const promptMessage = 'Enter your application name'; appOptions.name = await promptText(promptMessage, defaultName); } appOptions.icon = await handleIcon(appOptions); @@ -451,7 +437,7 @@ class BaseBuilder { async prepare() { // Windows and Linux need to install necessary build tools. if (!IS_MAC) { - logger.info('To build the app, you need to install Rust and necessary build tools.'); + logger.info('Install Rust and required build tools to build the app.'); logger.info('See more in https://tauri.app/v1/guides/getting-started/prerequisites#installing.'); } if (checkRustInstalled()) { @@ -459,32 +445,26 @@ class BaseBuilder { } const res = await prompts({ type: 'confirm', - message: 'We detected that you have not installed Rust. Install it now?', + message: 'Rust not detected. Install now?', name: 'value', }); if (res.value) { await installRust(); } else { - logger.error('Error: Pake needs Rust to package your webapp!'); + logger.error('Error: Rust required to package your webapp!'); process.exit(2); } } async runBuildCommand(directory, command) { const isChina = await isChinaDomain("www.npmjs.com"); if (isChina) { - logger.info("it's in China, use npm/rust cn mirror"); - logger.debug("pake npm directory: ", directory); + logger.info("Located in China, using npm/Rust CN mirror."); const rustProjectDir = path.join(directory, 'src-tauri', ".cargo"); - try { - await fs.access(rustProjectDir); - } - catch (e) { - await fs.mkdir(rustProjectDir, { recursive: true }); - } + await fsExtra.ensureDir(rustProjectDir); const projectCnConf = path.join(directory, "src-tauri", "rust_proxy.toml"); const projectConf = path.join(rustProjectDir, "config"); - await fs.copyFile(projectCnConf, projectConf); + await fsExtra.copy(projectCnConf, projectConf); await shellExec(`cd "${directory}" && npm install --registry=https://registry.npmmirror.com && ${command}`); } else { @@ -493,15 +473,10 @@ class BaseBuilder { } } -function setSecurityConfigWithUrl(tauriConfig, url) { - tauriConfig.tauri.security.dangerousRemoteDomainIpcAccess[0].domain = - new URL(url).hostname; -} -async function writeFileAsJson(filePath, data) { - await fs.writeFile(filePath, JSON.stringify(data, null, 4), 'utf-8'); -} async function mergeConfig(url, options, tauriConf) { const { width, height, fullscreen, transparent, resizable, userAgent, showMenu, showSystemTray, systemTrayIcon, iterCopyFile, identifier, name, } = options; + const { platform } = process; + // Set Windows parameters. const tauriConfWindowOptions = { width, height, @@ -509,9 +484,9 @@ async function mergeConfig(url, options, tauriConf) { transparent, resizable, }; - // Package name is valid ? + Object.assign(tauriConf.pake.windows[0], { url, ...tauriConfWindowOptions }); + // Determine whether the package name is valid. // for Linux, package name must be a-z, 0-9 or "-", not allow to A-Z and other - const { platform } = process; const platformRegexMapping = { linux: /[0-9]*[a-z]+[0-9]*\-?[0-9]*[a-z]*[0-9]*\-?[0-9]*[a-z]*[0-9]*/, default: /([0-9]*[a-zA-Z]+[0-9]*)+/, @@ -520,22 +495,18 @@ async function mergeConfig(url, options, tauriConf) { const nameCheck = reg.test(name) && reg.exec(name)[0].length === name.length; if (!nameCheck) { const errorMsg = platform === 'linux' - ? `Package name is illegal. It must be lowercase letters, numbers, dashes, and it must contain the lowercase letters. E.g com-123-xxx, 123pan, pan123,weread, we-read` - : `Package name is illegal. It must be letters, numbers, and it must contain the letters. E.g 123pan,123Pan Pan123,weread, WeRead, WERead`; + ? `Package name is invalid. It 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.` + : `Package name is invalid. It should only include letters and numbers, and must contain at least one letter. Examples: 123pan, 123Pan, Pan123, weread, WeRead, WERead.`; logger.error(errorMsg); process.exit(); } - Object.assign(tauriConf.pake.windows[0], { url, ...tauriConfWindowOptions }); tauriConf.package.productName = name; tauriConf.tauri.bundle.identifier = identifier; - // 判断一下url类型,是文件还是网站 - // 如果是文件,并且开启了递归拷贝功能,则需要将该文件以及所在文件夹下的所有文件拷贝到src目录下,否则只拷贝单个文件。 - const urlExists = await fs - .stat(url) - .then(() => true) - .catch(() => false); + // Judge the type of URL, whether it is a file or a website. + // If it is a file and the recursive copy function is enabled then the file and all files in its parent folder need to be copied to the "src" directory. Otherwise, only the single file will be copied. + const urlExists = await fsExtra.pathExists(url); if (urlExists) { - logger.warn('You input may a local file'); + 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); @@ -543,19 +514,22 @@ async function mergeConfig(url, options, tauriConf) { const distBakDir = path.join(npmDirectory, 'dist_bak'); if (!iterCopyFile) { const urlPath = path.join(distDir, fileName); - await fs.copyFile(url, urlPath); + await fsExtra.copy(url, urlPath); } else { - fs2.moveSync(distDir, distBakDir, { overwrite: true }); - fs2.copySync(dirName, distDir, { overwrite: true }); + 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) => fs.copyFile(path.join(distBakDir, file), path.join(distDir, file)))); + 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', @@ -568,7 +542,7 @@ async function mergeConfig(url, options, tauriConf) { } tauriConf.pake.menu[currentPlatform] = showMenu; tauriConf.pake.system_tray[currentPlatform] = showSystemTray; - // 处理targets 暂时只对linux开放 + // Processing targets are currently only open to Linux. if (platform === 'linux') { delete tauriConf.tauri.bundle.deb.files; const validTargets = ['all', 'deb', 'appimage']; @@ -576,34 +550,32 @@ async function mergeConfig(url, options, tauriConf) { tauriConf.tauri.bundle.targets = options.targets === 'all' ? ['deb', 'appimage'] : [options.targets]; } else { - logger.warn(`Targets must be one of ${validTargets.join(', ')}, we will use default 'all'`); + 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()}_32.ico`, defaultIcon: 'png/icon_256.ico', - message: 'Icon file in Windows must be 256 * 256 pix with .ico type', + message: 'Windows icon must be .ico and 256x256px.', }, linux: { fileExt: '.png', path: `png/${name.toLowerCase()}_32.png`, defaultIcon: 'png/icon_512.png', - message: 'Icon file in Linux must be 512 * 512 pix with .png type', + message: 'Linux icon must be .png and 512x512px.', }, darwin: { fileExt: '.icns', path: `icons/${name.toLowerCase()}_32.icns`, defaultIcon: 'icons/icon.icns', - message: 'Icon file in MacOS must be .icns type', + message: 'MacOS icon must be .icns type.', }, }; - const exists = await fs - .stat(options.icon) - .then(() => true) - .catch(() => false); const iconInfo = platformIconMap[platform]; + const exists = await fsExtra.pathExists(options.icon); if (exists) { let updateIconPath = true; let customIconExt = path.extname(options.icon).toLowerCase(); @@ -615,48 +587,43 @@ async function mergeConfig(url, options, tauriConf) { else { const iconPath = path.join(npmDirectory, 'src-tauri/', iconInfo.path); tauriConf.tauri.bundle.resources = [iconInfo.path]; - await fs.copyFile(options.icon, iconPath); + await fsExtra.copy(options.icon, iconPath); } if (updateIconPath) { tauriConf.tauri.bundle.icon = [options.icon]; } else { - logger.warn(`Icon file will not change with default.`); + logger.warn(`Icon will remain as default.`); } } else { - logger.warn('The custom icon path may not exists. we will use default icon to replace it'); + logger.warn('Custom icon path may be invalid. Default icon will be used instead.'); tauriConf.tauri.bundle.icon = [iconInfo.defaultIcon]; } - // 设定默认托盘图标路径 - let trayIconPath = platform === 'linux' || platform === 'win32' - ? tauriConf.tauri.bundle.icon[0] - : 'png/icon_512.png'; + // Set tray icon path. + let trayIconPath = platform === 'darwin' ? 'png/icon_512.png' : tauriConf.tauri.bundle.icon[0]; if (systemTrayIcon.length > 0) { try { - await fs.stat(systemTrayIcon); + 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 fs.copyFile(systemTrayIcon, trayIcoPath); + await fsExtra.copy(systemTrayIcon, trayIcoPath); } else { - logger.warn(`File type for system tray icon mut be .ico or .png , but you give ${iconExt}`); - logger.warn(`System tray icon file will not change with default.`); + 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(`System tray icon file will not change with default.`); + logger.warn(`Default system tray icon will remain unchanged.`); } } - // 设定托盘图标 tauriConf.tauri.systemTray.iconPath = trayIconPath; - // 设置安全调用 window.__TAURI__ 的安全域名为设置的应用域名 - setSecurityConfigWithUrl(tauriConf, url); - // 保存配置文件 + // Save config file. const platformConfigPaths = { win32: 'src-tauri/tauri.windows.conf.json', darwin: 'src-tauri/tauri.macos.conf.json', @@ -664,14 +631,14 @@ async function mergeConfig(url, options, tauriConf) { }; const configPath = path.join(npmDirectory, platformConfigPaths[platform]); const bundleConf = { tauri: { bundle: tauriConf.tauri.bundle } }; - await writeFileAsJson(configPath, bundleConf); + await fsExtra.writeJson(configPath, bundleConf, { spaces: 4 }); const pakeConfigPath = path.join(npmDirectory, 'src-tauri/pake.json'); - await writeFileAsJson(pakeConfigPath, tauriConf.pake); + 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 writeFileAsJson(configJsonPath, tauriConf2); + await fsExtra.writeJson(configJsonPath, tauriConf2, { spaces: 4 }); } class MacBuilder extends BaseBuilder { @@ -690,10 +657,10 @@ class MacBuilder extends BaseBuilder { } const appPath = this.getBuildAppPath(npmDirectory, dmgName, options.multiArch); const distPath = path.resolve(`${name}.dmg`); - await fs.copyFile(appPath, distPath); - await fs.unlink(appPath); + await fsExtra.copy(appPath, distPath); + await fsExtra.remove(appPath); logger.success('Build success!'); - logger.success('You can find the app installer in', distPath); + logger.success('App installer located in', distPath); } getBuildAppPath(npmDirectory, dmgName, multiArch) { const dmgPath = multiArch ? 'src-tauri/target/universal-apple-darwin/release/bundle/dmg' : 'src-tauri/target/release/bundle/dmg'; @@ -711,10 +678,10 @@ class WinBuilder extends BaseBuilder { const msiName = `${name}_${tauriConfig.package.version}_${arch}_${language}.msi`; const appPath = this.getBuildAppPath(npmDirectory, msiName); const distPath = path.resolve(`${name}.msi`); - await fs.copyFile(appPath, distPath); - await fs.unlink(appPath); + await fsExtra.copy(appPath, distPath); + await fsExtra.remove(appPath); logger.success('Build success!'); - logger.success('You can find the app installer in', distPath); + logger.success('App installer located in', distPath); } getBuildAppPath(npmDirectory, msiName) { return path.join(npmDirectory, 'src-tauri/target/release/bundle/msi', msiName); @@ -731,19 +698,19 @@ class LinuxBuilder extends BaseBuilder { const debName = `${name}_${tauriConfig.package.version}_${arch}.deb`; const appPath = this.getBuildAppPath(npmDirectory, "deb", debName); const distPath = path.resolve(`${name}.deb`); - await fs.copyFile(appPath, distPath); - await fs.unlink(appPath); + await fsExtra.copy(appPath, distPath); + await fsExtra.remove(appPath); logger.success('Build Deb success!'); - logger.success('You can find the deb app installer in', distPath); + logger.success('Deb app installer located in', distPath); } if (options.targets === "appimage" || options.targets === "all") { const appImageName = `${name}_${tauriConfig.package.version}_${arch}.AppImage`; const appImagePath = this.getBuildAppPath(npmDirectory, "appimage", appImageName); const distAppPath = path.resolve(`${name}.AppImage`); - await fs.copyFile(appImagePath, distAppPath); - await fs.unlink(appImagePath); + await fsExtra.copy(appImagePath, distAppPath); + await fsExtra.remove(appImagePath); logger.success('Build AppImage success!'); - logger.success('You can find the AppImage app installer in', distAppPath); + logger.success('AppImage installer located in', distAppPath); } } getBuildAppPath(npmDirectory, packageType, packageName) { @@ -767,7 +734,7 @@ class BuilderProvider { } var name = "pake-cli"; -var version = "2.1.0-beta2"; +var version = "2.1.0"; var description = "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。"; var engines = { node: ">=16.0.0" @@ -879,7 +846,7 @@ function validateNumberInput(value) { return parsedValue; } function validateUrlInput(url) { - const isFile = fs$1.existsSync(url); + const isFile = fs.existsSync(url); if (!isFile) { try { return normalizeUrl(url); @@ -925,7 +892,7 @@ program .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 all static files to pake app when URL is a local file', DEFAULT_PAKE_OPTIONS.iterCopyFile) + .option('--iter-copy-file', 'Copy files to app when URL is a local file', DEFAULT_PAKE_OPTIONS.iterCopyFile) .option('--multi-arch', 'Available for Mac only, supports both Intel and M1', DEFAULT_PAKE_OPTIONS.multiArch) .option('--targets ', 'Only for Linux, option "deb", "appimage" or "all"', DEFAULT_PAKE_OPTIONS.targets) .option('--debug', 'Debug mode', DEFAULT_PAKE_OPTIONS.debug) From 22e3efea493662faa70b6866dc8fb7d967695a8b Mon Sep 17 00:00:00 2001 From: Tw93 Date: Thu, 22 Jun 2023 17:10:41 +0800 Subject: [PATCH 19/40] :art: More simple --- bin/cli.ts | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/bin/cli.ts b/bin/cli.ts index 9823b16..9f3311d 100644 --- a/bin/cli.ts +++ b/bin/cli.ts @@ -7,7 +7,7 @@ import BuilderProvider from './builders/BuilderProvider'; import { checkUpdateTips } from './helpers/updater'; import packageJson from '../package.json'; import { validateNumberInput, validateUrlInput } from './utils/validate'; -import { DEFAULT_PAKE_OPTIONS } from './defaults'; +import { DEFAULT_PAKE_OPTIONS as DEFAULT } from './defaults'; program .version(packageJson.version) @@ -17,20 +17,20 @@ program program .argument('[url]', 'The web URL you want to package', validateUrlInput) .option('--name ', 'Application name') - .option('--icon ', 'Application icon', DEFAULT_PAKE_OPTIONS.icon) - .option('--height ', 'Window height', validateNumberInput, DEFAULT_PAKE_OPTIONS.height) - .option('--width ', 'Window width', validateNumberInput, DEFAULT_PAKE_OPTIONS.width) - .option('--no-resizable', 'Whether the window can be resizable', DEFAULT_PAKE_OPTIONS.resizable) - .option('--fullscreen', 'Start the packaged app in full screen', DEFAULT_PAKE_OPTIONS.fullscreen) - .option('--transparent', 'Transparent title bar', DEFAULT_PAKE_OPTIONS.transparent) - .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 to app when URL is a local file', DEFAULT_PAKE_OPTIONS.iterCopyFile) - .option('--multi-arch', 'Available for Mac only, supports both Intel and M1', DEFAULT_PAKE_OPTIONS.multiArch) - .option('--targets ', 'Only for Linux, option "deb", "appimage" or "all"', DEFAULT_PAKE_OPTIONS.targets) - .option('--debug', 'Debug mode', DEFAULT_PAKE_OPTIONS.debug) + .option('--icon ', 'Application icon', DEFAULT.icon) + .option('--height ', 'Window height', validateNumberInput, DEFAULT.height) + .option('--width ', 'Window width', validateNumberInput, DEFAULT.width) + .option('--no-resizable', 'Whether the window can be resizable', DEFAULT.resizable) + .option('--fullscreen', 'Start the packaged app in full screen', DEFAULT.fullscreen) + .option('--transparent', 'Transparent title bar', DEFAULT.transparent) + .option('--user-agent ', 'Custom user agent', DEFAULT.userAgent) + .option('--show-menu', 'Show menu in app', DEFAULT.showMenu) + .option('--show-system-tray', 'Show system tray in app', DEFAULT.showSystemTray) + .option('--system-tray-icon ', 'Custom system tray icon', DEFAULT.systemTrayIcon) + .option('--iter-copy-file', 'Copy files to app when URL is a local file', DEFAULT.iterCopyFile) + .option('--multi-arch', 'Available for Mac only, supports both Intel and M1', DEFAULT.multiArch) + .option('--targets ', 'Only for Linux, option "deb", "appimage" or "all"', DEFAULT.targets) + .option('--debug', 'Debug mode', DEFAULT.debug) .action(async (url: string, options: PakeCliOptions) => { //Check for update prompt From e1cd40277fb4f1153b1c2716ab555aa5f8f00278 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Thu, 22 Jun 2023 20:56:15 +0800 Subject: [PATCH 20/40] :art: Optimize the use of CLI. --- bin/builders/BaseBuilder.ts | 3 ++ bin/builders/BuilderProvider.ts | 21 +++++------ bin/cli.ts | 4 ++- bin/helpers/merge.ts | 6 ++-- bin/options/icon.ts | 6 ++-- bin/utils/ip.ts | 18 +++++++--- dist/cli.js | 64 +++++++++++++++++++-------------- package.json | 2 +- tsconfig.json | 4 +++ 9 files changed, 79 insertions(+), 49 deletions(-) diff --git a/bin/builders/BaseBuilder.ts b/bin/builders/BaseBuilder.ts index 645c73c..a8b78b3 100644 --- a/bin/builders/BaseBuilder.ts +++ b/bin/builders/BaseBuilder.ts @@ -1,3 +1,4 @@ +import ora from "ora"; import path from 'path'; import fsExtra from "fs-extra"; import prompts from 'prompts'; @@ -39,6 +40,8 @@ export default abstract class BaseBuilder { } protected async runBuildCommand(directory: string, command: string) { + const spinner = ora('Building...').start(); + setTimeout(() => spinner.succeed(), 5000); const isChina = await isChinaDomain("www.npmjs.com"); if (isChina) { logger.info("Located in China, using npm/Rust CN mirror."); diff --git a/bin/builders/BuilderProvider.ts b/bin/builders/BuilderProvider.ts index a13c352..2670f25 100644 --- a/bin/builders/BuilderProvider.ts +++ b/bin/builders/BuilderProvider.ts @@ -3,19 +3,20 @@ import MacBuilder from './MacBuilder'; import WinBuilder from './WinBuilder'; import LinuxBuilder from './LinuxBuilder'; -import { IS_MAC, IS_WIN, IS_LINUX } from '@/utils/platform'; +const { platform } = process; + +const buildersMap: Record BaseBuilder> = { + darwin: MacBuilder, + win32: WinBuilder, + linux: LinuxBuilder, +}; export default class BuilderProvider { static create(): BaseBuilder { - if (IS_MAC) { - return new MacBuilder(); + const Builder = buildersMap[platform]; + if (!Builder) { + throw new Error('The current system is not supported!'); } - if (IS_WIN) { - return new WinBuilder(); - } - if (IS_LINUX) { - return new LinuxBuilder(); - } - throw new Error('The current system is not supported!'); + return new Builder(); } } diff --git a/bin/cli.ts b/bin/cli.ts index 9f3311d..7390e4a 100644 --- a/bin/cli.ts +++ b/bin/cli.ts @@ -1,3 +1,4 @@ +import ora from "ora"; import log from 'loglevel'; import { program } from 'commander'; @@ -46,10 +47,11 @@ program log.setLevel('debug'); } + const spinner = ora('Preparing...').start(); const builder = BuilderProvider.create(); await builder.prepare(); - const appOptions = await handleInputOptions(options, url); + spinner.succeed(); log.debug('PakeAppOptions', appOptions); diff --git a/bin/helpers/merge.ts b/bin/helpers/merge.ts index c822aaf..9f7a487 100644 --- a/bin/helpers/merge.ts +++ b/bin/helpers/merge.ts @@ -127,19 +127,19 @@ export async function mergeConfig( const platformIconMap: PlatformMap = { win32: { fileExt: '.ico', - path: `png/${name.toLowerCase()}_32.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()}_32.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()}_32.icns`, + path: `icons/${name.toLowerCase()}.icns`, defaultIcon: 'icons/icon.icns', message: 'MacOS icon must be .icns type.', }, diff --git a/bin/options/icon.ts b/bin/options/icon.ts index f52b645..f18fb66 100644 --- a/bin/options/icon.ts +++ b/bin/options/icon.ts @@ -25,11 +25,10 @@ export async function handleIcon(options: PakeAppOptions) { export async function downloadIcon(iconUrl: string) { try { - const iconResponse = await axios.get(iconUrl, { - responseType: 'arraybuffer', - }); + const iconResponse = await axios.get(iconUrl, { responseType: 'arraybuffer' }); const iconData = await iconResponse.data; + if (!iconData) { return null; } @@ -42,6 +41,7 @@ export async function downloadIcon(iconUrl: string) { const { path: tempPath } = await dir(); const iconPath = `${tempPath}/icon.${fileDetails.ext}`; await fsExtra.outputFile(iconPath, iconData); + return iconPath; } catch (error) { if (error.response && error.response.status === 404) { diff --git a/bin/utils/ip.ts b/bin/utils/ip.ts index d8e9b4c..6648dc2 100644 --- a/bin/utils/ip.ts +++ b/bin/utils/ip.ts @@ -10,7 +10,8 @@ const ping = async (host: string) => { const ip = await lookup(host); const start = new Date(); - return new Promise((resolve, reject) => { + // 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(); @@ -21,14 +22,23 @@ const ping = async (host: string) => { reject(err); }); }); + + const timeoutPromise = new Promise((_, reject) => { + setTimeout(() => { + reject(new Error('Request timed out after 3 seconds')); + }, 3000); + }); + + return Promise.race([requestPromise, timeoutPromise]); }; + async function isChinaDomain(domain: string): Promise { try { const [ip] = await resolve(domain); return await isChinaIP(ip, domain); } catch (error) { - logger.info(`${domain} can't be parse!`); + logger.debug(`${domain} can't be parse!`); return false; } } @@ -36,10 +46,10 @@ async function isChinaDomain(domain: string): Promise { async function isChinaIP(ip: string, domain: string): Promise { try { const delay = await ping(ip); - logger.info(`${domain} latency is ${delay} ms`); + logger.debug(`${domain} latency is ${delay} ms`); return delay > 500; } catch (error) { - logger.info(`ping ${domain} failed!`); + logger.debug(`ping ${domain} failed!`); return false; } } diff --git a/dist/cli.js b/dist/cli.js index 30ae112..881a9aa 100644 --- a/dist/cli.js +++ b/dist/cli.js @@ -1,3 +1,4 @@ +import ora from 'ora'; import log from 'loglevel'; import { InvalidArgumentError, program } from 'commander'; import fsExtra from 'fs-extra'; @@ -15,7 +16,6 @@ import shelljs from 'shelljs'; import dns from 'dns'; import http from 'http'; import { promisify } from 'util'; -import ora from 'ora'; import updateNotifier from 'update-notifier'; import fs from 'fs'; @@ -42,10 +42,10 @@ const currentModulePath = fileURLToPath(import.meta.url); // Resolve the parent directory of the current module const npmDirectory = path.join(path.dirname(currentModulePath), '..'); -const { platform: platform$1 } = process; -const IS_MAC = platform$1 === 'darwin'; -const IS_WIN = platform$1 === 'win32'; -const IS_LINUX = platform$1 === 'linux'; +const { platform: platform$2 } = process; +const IS_MAC = platform$2 === 'darwin'; +const IS_WIN = platform$2 === 'win32'; +const IS_LINUX = platform$2 === 'linux'; async function handleIcon(options) { if (options.icon) { @@ -64,9 +64,7 @@ async function handleIcon(options) { } async function downloadIcon(iconUrl) { try { - const iconResponse = await axios.get(iconUrl, { - responseType: 'arraybuffer', - }); + const iconResponse = await axios.get(iconUrl, { responseType: 'arraybuffer' }); const iconData = await iconResponse.data; if (!iconData) { return null; @@ -347,9 +345,9 @@ const platformConfigs = { darwin: MacConf, linux: LinuxConf }; -const { platform } = process; +const { platform: platform$1 } = process; // @ts-ignore -const platformConfig = platformConfigs[platform]; +const platformConfig = platformConfigs[platform$1]; let tauriConfig = { tauri: { ...CommonConf.tauri, @@ -378,7 +376,8 @@ const ping = async (host) => { const lookup = promisify(dns.lookup); const ip = await lookup(host); const start = new Date(); - return new Promise((resolve, reject) => { + // 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(); @@ -388,6 +387,12 @@ const ping = async (host) => { reject(err); }); }); + const timeoutPromise = new Promise((_, reject) => { + setTimeout(() => { + reject(new Error('Request timed out after 3 seconds')); + }, 3000); + }); + return Promise.race([requestPromise, timeoutPromise]); }; async function isChinaDomain(domain) { try { @@ -395,18 +400,18 @@ async function isChinaDomain(domain) { return await isChinaIP(ip, domain); } catch (error) { - logger.info(`${domain} can't be parse!`); + logger.debug(`${domain} can't be parse!`); return false; } } async function isChinaIP(ip, domain) { try { const delay = await ping(ip); - logger.info(`${domain} latency is ${delay} ms`); + logger.debug(`${domain} latency is ${delay} ms`); return delay > 500; } catch (error) { - logger.info(`ping ${domain} failed!`); + logger.debug(`ping ${domain} failed!`); return false; } } @@ -457,6 +462,8 @@ class BaseBuilder { } } async runBuildCommand(directory, command) { + const spinner = ora('Building...').start(); + setTimeout(() => spinner.succeed(), 5000); const isChina = await isChinaDomain("www.npmjs.com"); if (isChina) { logger.info("Located in China, using npm/Rust CN mirror."); @@ -557,19 +564,19 @@ async function mergeConfig(url, options, tauriConf) { const platformIconMap = { win32: { fileExt: '.ico', - path: `png/${name.toLowerCase()}_32.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()}_32.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()}_32.icns`, + path: `icons/${name.toLowerCase()}.icns`, defaultIcon: 'icons/icon.icns', message: 'MacOS icon must be .icns type.', }, @@ -718,23 +725,24 @@ class LinuxBuilder extends BaseBuilder { } } +const { platform } = process; +const buildersMap = { + darwin: MacBuilder, + win32: WinBuilder, + linux: LinuxBuilder, +}; class BuilderProvider { static create() { - if (IS_MAC) { - return new MacBuilder(); + const Builder = buildersMap[platform]; + if (!Builder) { + throw new Error('The current system is not supported!'); } - if (IS_WIN) { - return new WinBuilder(); - } - if (IS_LINUX) { - return new LinuxBuilder(); - } - throw new Error('The current system is not supported!'); + return new Builder(); } } var name = "pake-cli"; -var version = "2.1.0"; +var version = "2.1.1"; var description = "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。"; var engines = { node: ">=16.0.0" @@ -907,9 +915,11 @@ program if (options.debug) { log.setLevel('debug'); } + const spinner = ora('Preparing...').start(); const builder = BuilderProvider.create(); await builder.prepare(); const appOptions = await handleOptions(options, url); + spinner.succeed(); log.debug('PakeAppOptions', appOptions); await builder.build(url, appOptions); }); diff --git a/package.json b/package.json index 60e4974..d4839e8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pake-cli", - "version": "2.1.0", + "version": "2.1.1", "description": "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。", "engines": { "node": ">=16.0.0" diff --git a/tsconfig.json b/tsconfig.json index 012cdb2..c8d6d40 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,10 @@ "types": [ "node" ], + "lib": [ + "es2020", + "dom" + ], "esModuleInterop": true, "resolveJsonModule": true, "allowSyntheticDefaultImports": true, From 6464377464ee8f9e60a16b082ffebaa3c86acd46 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Thu, 22 Jun 2023 21:24:17 +0800 Subject: [PATCH 21/40] :art: import sorting --- bin/builders/BaseBuilder.ts | 6 +++--- bin/builders/LinuxBuilder.ts | 4 ++-- bin/builders/MacBuilder.ts | 4 ++-- bin/builders/WinBuilder.ts | 4 ++-- bin/helpers/merge.ts | 1 + bin/options/icon.ts | 2 +- bin/options/logger.ts | 2 +- bin/utils/ip.ts | 1 + 8 files changed, 13 insertions(+), 11 deletions(-) diff --git a/bin/builders/BaseBuilder.ts b/bin/builders/BaseBuilder.ts index a8b78b3..56b9e22 100644 --- a/bin/builders/BaseBuilder.ts +++ b/bin/builders/BaseBuilder.ts @@ -1,14 +1,14 @@ -import ora from "ora"; import path from 'path'; +import ora from "ora"; import fsExtra from "fs-extra"; import prompts from 'prompts'; +import logger from '@/options/logger'; import { shellExec } from '@/utils/shell'; import { isChinaDomain } from '@/utils/ip'; -import logger from '@/options/logger'; -import { checkRustInstalled, installRust } from '@/helpers/rust'; import { PakeAppOptions } from '@/types'; import { IS_MAC } from "@/utils/platform"; +import { checkRustInstalled, installRust } from '@/helpers/rust'; export default abstract class BaseBuilder { abstract build(url: string, options: PakeAppOptions): Promise; diff --git a/bin/builders/LinuxBuilder.ts b/bin/builders/LinuxBuilder.ts index 6f0dcc8..764109b 100644 --- a/bin/builders/LinuxBuilder.ts +++ b/bin/builders/LinuxBuilder.ts @@ -1,12 +1,12 @@ import path from 'path'; import fsExtra from "fs-extra"; -import BaseBuilder from './BaseBuilder'; +import BaseBuilder from './BaseBuilder'; import logger from '@/options/logger'; import tauriConfig from '@/helpers/tauriConfig'; import { npmDirectory } from '@/utils/dir'; -import { PakeAppOptions } from '@/types'; import { mergeConfig } from "@/helpers/merge"; +import { PakeAppOptions } from '@/types'; export default class LinuxBuilder extends BaseBuilder { async build(url: string, options: PakeAppOptions) { diff --git a/bin/builders/MacBuilder.ts b/bin/builders/MacBuilder.ts index abfd18e..e167075 100644 --- a/bin/builders/MacBuilder.ts +++ b/bin/builders/MacBuilder.ts @@ -1,12 +1,12 @@ import path from 'path'; import fsExtra from "fs-extra"; +import BaseBuilder from './BaseBuilder'; import logger from '@/options/logger'; import tauriConfig from '@/helpers/tauriConfig'; -import BaseBuilder from './BaseBuilder'; import { npmDirectory } from '@/utils/dir'; -import { PakeAppOptions } from '@/types'; import { mergeConfig } from "@/helpers/merge"; +import { PakeAppOptions } from '@/types'; export default class MacBuilder extends BaseBuilder { async build(url: string, options: PakeAppOptions) { diff --git a/bin/builders/WinBuilder.ts b/bin/builders/WinBuilder.ts index a0557a4..3eff8cc 100644 --- a/bin/builders/WinBuilder.ts +++ b/bin/builders/WinBuilder.ts @@ -1,12 +1,12 @@ import path from 'path'; import fsExtra from 'fs-extra'; -import BaseBuilder from './BaseBuilder'; +import BaseBuilder from './BaseBuilder'; import logger from '@/options/logger'; import tauriConfig from '@/helpers/tauriConfig'; import { npmDirectory } from '@/utils/dir'; -import { PakeAppOptions } from '@/types'; import { mergeConfig } from '@/helpers/merge'; +import { PakeAppOptions } from '@/types'; export default class WinBuilder extends BaseBuilder { async build(url: string, options: PakeAppOptions) { diff --git a/bin/helpers/merge.ts b/bin/helpers/merge.ts index 9f7a487..60e81d8 100644 --- a/bin/helpers/merge.ts +++ b/bin/helpers/merge.ts @@ -1,5 +1,6 @@ import path from 'path'; import fsExtra from 'fs-extra'; + import { npmDirectory } from '@/utils/dir'; import logger from '@/options/logger'; import { PakeAppOptions, PlatformMap } from '@/types'; diff --git a/bin/options/icon.ts b/bin/options/icon.ts index f18fb66..afe1464 100644 --- a/bin/options/icon.ts +++ b/bin/options/icon.ts @@ -5,9 +5,9 @@ import { dir } from 'tmp-promise'; import { fileTypeFromBuffer } from 'file-type'; import logger from './logger'; -import { PakeAppOptions } from '@/types'; import { npmDirectory } from '@/utils/dir'; import { IS_LINUX, IS_WIN } from '@/utils/platform'; +import { PakeAppOptions } from '@/types'; export async function handleIcon(options: PakeAppOptions) { if (options.icon) { diff --git a/bin/options/logger.ts b/bin/options/logger.ts index da6cafa..5d6b893 100644 --- a/bin/options/logger.ts +++ b/bin/options/logger.ts @@ -1,5 +1,5 @@ -import log from 'loglevel'; import chalk from 'chalk'; +import log from 'loglevel'; const logger = { info(...msg: any[]) { diff --git a/bin/utils/ip.ts b/bin/utils/ip.ts index 6648dc2..0a98c5f 100644 --- a/bin/utils/ip.ts +++ b/bin/utils/ip.ts @@ -1,6 +1,7 @@ import dns from 'dns'; import http from 'http'; import { promisify } from 'util'; + import logger from '@/options/logger'; const resolve = promisify(dns.resolve); From 7809771a791dca85a2eb727ae7dd4a83c8e2452e Mon Sep 17 00:00:00 2001 From: Tw93 Date: Thu, 22 Jun 2023 21:32:26 +0800 Subject: [PATCH 22/40] :art: Update Cli workflow --- .github/workflows/pake-cli.yaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/pake-cli.yaml b/.github/workflows/pake-cli.yaml index e0113ef..bba7ce3 100644 --- a/.github/workflows/pake-cli.yaml +++ b/.github/workflows/pake-cli.yaml @@ -20,24 +20,24 @@ on: icon: description: "[Icon, Optional]" required: false - height: - description: "[Height, Optional]" - required: false - default: "780" width: description: "[Width, Optional]" required: false default: "1200" - transparent: - description: "[Transparent, Optional]" + height: + description: "[Height, Optional]" required: false - type: boolean - default: false + default: "780" resize: description: "[Resize, Optional]" required: false type: boolean default: true + transparent: + description: "[Transparent, Optional, MacOS only]" + required: false + type: boolean + default: false multi_arch: description: "[MultiArch, Optional, MacOS only]" required: false From ba65ff21da350dfa97b0b41c0112bfcedc93aae0 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Fri, 23 Jun 2023 11:43:06 +0800 Subject: [PATCH 23/40] :art: Simplify usage --- bin/README.md | 12 +- bin/README_CN.md | 8 +- bin/builders/BaseBuilder.ts | 57 ++--- bin/builders/LinuxBuilder.ts | 2 +- bin/builders/MacBuilder.ts | 4 +- bin/builders/WinBuilder.ts | 2 +- bin/cli.ts | 29 +-- bin/helpers/merge.ts | 27 +-- bin/helpers/rust.ts | 10 +- bin/options/icon.ts | 6 +- bin/options/index.ts | 53 ++++- bin/utils/info.ts | 21 ++ bin/utils/ip.ts | 8 +- dist/cli.js | 405 +++++++++++++++++++---------------- 14 files changed, 360 insertions(+), 284 deletions(-) diff --git a/bin/README.md b/bin/README.md index 3b0042a..37ac1b9 100644 --- a/bin/README.md +++ b/bin/README.md @@ -1,3 +1,5 @@ +

English | 简体中文

+ ## Installation Ensure that your Node.js version is 16.0 or higher (e.g., 16.8). Avoid using `sudo` for the installation. If you encounter permission issues with npm, refer to [How to fix npm throwing error without sudo](https://stackoverflow.com/questions/16151018/how-to-fix-npm-throwing-error-without-sudo). @@ -38,14 +40,14 @@ npm install pake-cli -g ## Usage ```bash -pake url [options] +pake [url] [options] ``` The packaged application will be located in the current working directory by default. The first packaging might take some time due to environment configuration. Please be patient. > **Note**: Packaging requires the Rust environment. If Rust is not installed, you will be prompted for installation confirmation. In case of installation failure or timeout, you can [install it manually](https://www.rust-lang.org/tools/install). -### url +### [url] The URL is the link to the web page you want to package or the path to a local HTML file. This is mandatory. @@ -59,8 +61,6 @@ Specify the application name. If not provided, you will be prompted to enter it. ```shell --name -# or --n ``` #### [icon] @@ -95,8 +95,6 @@ Set the width of the application window. Default is `1200px`. Enable or disable immersive header. Default is `false`. Use the following command to enable this feature. -``` - ```shell --transparent ``` @@ -114,7 +112,7 @@ Determine whether the application launches in full screen. Default is `false`. U Determine whether the window is resizable. Default is `true`. Use the following command to disable window resizing. ```shell ---no-resizable +--resizable ``` #### [multi-arch] diff --git a/bin/README_CN.md b/bin/README_CN.md index 337486d..31c088f 100644 --- a/bin/README_CN.md +++ b/bin/README_CN.md @@ -1,3 +1,5 @@ +

English | 简体中文

+ ## 安装 请确保您的 Node.js 版本为 16 或更高版本(例如 16.8)。请避免使用 `sudo` 进行安装。如果 npm 报告权限问题,请参考 [如何在不使用 sudo 的情况下修复 npm 报错](https://stackoverflow.com/questions/16151018/how-to-fix-npm-throwing-error-without-sudo)。 @@ -39,14 +41,14 @@ npm install pake-cli -g ## 使用方法 ```bash -pake url [options] +pake [url] [options] ``` 应用程序的打包结果将默认保存在当前工作目录。由于首次打包需要配置环境,这可能需要一些时间,请耐心等待。 > **注意**:打包过程需要使用 `Rust` 环境。如果您没有安装 `Rust`,系统会提示您是否要安装。如果遇到安装失败或超时的问题,您可以 [手动安装](https://www.rust-lang.org/tools/install)。 -### url +### [url] `url` 是您需要打包的网页链接 🔗 或本地 HTML 文件的路径,此参数为必填。 @@ -104,7 +106,7 @@ pake url [options] 设置应用窗口是否可以调整大小,默认为 `true`(可调整)。使用以下命令可以禁止调整窗口大小。 ```shell ---no-resizable +--resizable ``` #### [fullscreen] diff --git a/bin/builders/BaseBuilder.ts b/bin/builders/BaseBuilder.ts index 56b9e22..2a8134d 100644 --- a/bin/builders/BaseBuilder.ts +++ b/bin/builders/BaseBuilder.ts @@ -1,11 +1,12 @@ import path from 'path'; -import ora from "ora"; import fsExtra from "fs-extra"; import prompts from 'prompts'; import logger from '@/options/logger'; import { shellExec } from '@/utils/shell'; import { isChinaDomain } from '@/utils/ip'; +import { getSpinner } from "@/utils/info"; +import { npmDirectory } from '@/utils/dir'; import { PakeAppOptions } from '@/types'; import { IS_MAC } from "@/utils/platform"; import { checkRustInstalled, installRust } from '@/helpers/rust'; @@ -14,46 +15,46 @@ export default abstract class BaseBuilder { abstract build(url: string, options: PakeAppOptions): Promise; async prepare() { - // Windows and Linux need to install necessary build tools. if (!IS_MAC) { - logger.info('Install Rust and required build tools to build the app.'); + logger.info('The first use requires installing system dependencies.'); logger.info('See more in https://tauri.app/v1/guides/getting-started/prerequisites#installing.'); } - if (checkRustInstalled()) { - return; + if (!checkRustInstalled()) { + const res = await prompts({ + type: 'confirm', + message: 'Rust not detected. Install now?', + name: 'value', + }); + + if (res.value) { + await installRust(); + } else { + logger.error('Error: Rust required to package your webapp!'); + process.exit(0); + } } - const res = await prompts({ - type: 'confirm', - message: 'Rust not detected. Install now?', - name: 'value', - }); - - if (res.value) { - await installRust(); - } else { - logger.error('Error: Rust required to package your webapp!'); - process.exit(2); - } - } - - protected async runBuildCommand(directory: string, command: string) { - const spinner = ora('Building...').start(); - setTimeout(() => spinner.succeed(), 5000); const isChina = await isChinaDomain("www.npmjs.com"); + const spinner = getSpinner('Installing package.'); if (isChina) { - logger.info("Located in China, using npm/Rust CN mirror."); - const rustProjectDir = path.join(directory, 'src-tauri', ".cargo"); + logger.info("Located in China, using npm/rsProxy CN mirror."); + const rustProjectDir = path.join(npmDirectory, 'src-tauri', ".cargo"); await fsExtra.ensureDir(rustProjectDir); - const projectCnConf = path.join(directory, "src-tauri", "rust_proxy.toml"); + const projectCnConf = path.join(npmDirectory, "src-tauri", "rust_proxy.toml"); const projectConf = path.join(rustProjectDir, "config"); await fsExtra.copy(projectCnConf, projectConf); - - await shellExec(`cd "${directory}" && npm install --registry=https://registry.npmmirror.com && ${command}`); + await shellExec(`cd "${npmDirectory}" && npm install --registry=https://registry.npmmirror.com`); } else { - await shellExec(`cd "${directory}" && npm install && ${command}`); + await shellExec(`cd "${npmDirectory}" && npm install`); } + spinner.succeed('Package installed.'); + } + + protected async runBuildCommand(command: string = "npm run build") { + const spinner = getSpinner('Building app.'); + await shellExec(`cd "${npmDirectory}" && ${command}`); + spinner.stop(); } } diff --git a/bin/builders/LinuxBuilder.ts b/bin/builders/LinuxBuilder.ts index 764109b..41d87dd 100644 --- a/bin/builders/LinuxBuilder.ts +++ b/bin/builders/LinuxBuilder.ts @@ -12,7 +12,7 @@ export default class LinuxBuilder extends BaseBuilder { async build(url: string, options: PakeAppOptions) { const { name } = options; await mergeConfig(url, options, tauriConfig); - await this.runBuildCommand(npmDirectory, 'npm run build'); + await this.runBuildCommand(); const arch = process.arch === "x64" ? "amd64" : process.arch; diff --git a/bin/builders/MacBuilder.ts b/bin/builders/MacBuilder.ts index e167075..cfc55f1 100644 --- a/bin/builders/MacBuilder.ts +++ b/bin/builders/MacBuilder.ts @@ -14,10 +14,10 @@ export default class MacBuilder extends BaseBuilder { await mergeConfig(url, options, tauriConfig); let dmgName: string; if (options.multiArch) { - await this.runBuildCommand(npmDirectory, 'npm run build:mac'); + await this.runBuildCommand('npm run build:mac'); dmgName = `${name}_${tauriConfig.package.version}_universal.dmg`; } else { - await this.runBuildCommand(npmDirectory, 'npm run build'); + await this.runBuildCommand(); let arch = process.arch === "arm64" ? "aarch64" : process.arch; dmgName = `${name}_${tauriConfig.package.version}_${arch}.dmg`; } diff --git a/bin/builders/WinBuilder.ts b/bin/builders/WinBuilder.ts index 3eff8cc..910713a 100644 --- a/bin/builders/WinBuilder.ts +++ b/bin/builders/WinBuilder.ts @@ -12,7 +12,7 @@ export default class WinBuilder extends BaseBuilder { async build(url: string, options: PakeAppOptions) { const { name } = options; await mergeConfig(url, options, tauriConfig); - await this.runBuildCommand(npmDirectory, 'npm run build'); + await this.runBuildCommand(); const language = tauriConfig.tauri.bundle.windows.wix.language[0]; const arch = process.arch; diff --git a/bin/cli.ts b/bin/cli.ts index 7390e4a..cfc21ce 100644 --- a/bin/cli.ts +++ b/bin/cli.ts @@ -1,5 +1,5 @@ -import ora from "ora"; import log from 'loglevel'; +import chalk from 'chalk'; import { program } from 'commander'; import { PakeCliOptions } from './types'; @@ -11,8 +11,8 @@ import { validateNumberInput, validateUrlInput } from './utils/validate'; import { DEFAULT_PAKE_OPTIONS as DEFAULT } from './defaults'; program - .version(packageJson.version) - .description('A CLI that can turn any webpage into a desktop app with Rust.') + .description(chalk.green('Pake: A CLI that can turn any webpage into a desktop app with Rust.')) + .usage('[url] [options]') .showHelpAfterError(); program @@ -21,7 +21,7 @@ program .option('--icon ', 'Application icon', DEFAULT.icon) .option('--height ', 'Window height', validateNumberInput, DEFAULT.height) .option('--width ', 'Window width', validateNumberInput, DEFAULT.width) - .option('--no-resizable', 'Whether the window can be resizable', DEFAULT.resizable) + .option('--resizable', 'Whether the window can be resizable', DEFAULT.resizable) .option('--fullscreen', 'Start the packaged app in full screen', DEFAULT.fullscreen) .option('--transparent', 'Transparent title bar', DEFAULT.transparent) .option('--user-agent ', 'Custom user agent', DEFAULT.userAgent) @@ -29,17 +29,23 @@ program .option('--show-system-tray', 'Show system tray in app', DEFAULT.showSystemTray) .option('--system-tray-icon ', 'Custom system tray icon', DEFAULT.systemTrayIcon) .option('--iter-copy-file', 'Copy files to app when URL is a local file', DEFAULT.iterCopyFile) - .option('--multi-arch', 'Available for Mac only, supports both Intel and M1', DEFAULT.multiArch) + .option('--multi-arch', 'Only for Mac, supports both Intel and M1', DEFAULT.multiArch) .option('--targets ', 'Only for Linux, option "deb", "appimage" or "all"', DEFAULT.targets) .option('--debug', 'Debug mode', DEFAULT.debug) + .version(packageJson.version, '-v, --version', 'Output the current version') .action(async (url: string, options: PakeCliOptions) => { - //Check for update prompt await checkUpdateTips(); - // If no URL is provided, display help information if (!url) { - program.help(); + program.outputHelp((str) => { + const filteredOutput = str + .split('\n') + .filter((line) => !/((-h,|--help)|((-v|-V),|--version))\s+.+$/.test(line)) + .join('\n'); + return filteredOutput.trim(); // Trim any leading/trailing whitespace + }); + process.exit(0); } log.setDefaultLevel('info'); @@ -47,14 +53,11 @@ program log.setLevel('debug'); } - const spinner = ora('Preparing...').start(); - const builder = BuilderProvider.create(); - await builder.prepare(); const appOptions = await handleInputOptions(options, url); - spinner.succeed(); - log.debug('PakeAppOptions', appOptions); + const builder = BuilderProvider.create(); + await builder.prepare(); await builder.build(url, appOptions); }); diff --git a/bin/helpers/merge.ts b/bin/helpers/merge.ts index 60e81d8..195bcb8 100644 --- a/bin/helpers/merge.ts +++ b/bin/helpers/merge.ts @@ -37,33 +37,12 @@ export async function mergeConfig( }; Object.assign(tauriConf.pake.windows[0], { url, ...tauriConfWindowOptions }); - // Determine whether the package name is valid. - // for Linux, package name must be a-z, 0-9 or "-", not allow to A-Z and other - const platformRegexMapping: PlatformMap = { - linux: /[0-9]*[a-z]+[0-9]*\-?[0-9]*[a-z]*[0-9]*\-?[0-9]*[a-z]*[0-9]*/, - default: /([0-9]*[a-zA-Z]+[0-9]*)+/, - }; - - const reg = platformRegexMapping[platform] || platformRegexMapping.default; - const nameCheck = reg.test(name) && reg.exec(name)[0].length === name.length; - - if (!nameCheck) { - const errorMsg = - platform === 'linux' - ? `Package name is invalid. It 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.` - : `Package name is invalid. It should only include letters and numbers, and must contain at least one letter. Examples: 123pan, 123Pan, Pan123, weread, WeRead, WERead.`; - - logger.error(errorMsg); - process.exit(); - } tauriConf.package.productName = name; tauriConf.tauri.bundle.identifier = identifier; - // Judge the type of URL, whether it is a file or a website. - // If it is a file and the recursive copy function is enabled then the file and all files in its parent folder need to be copied to the "src" directory. Otherwise, only the single file will be copied. - const urlExists = await fsExtra.pathExists(url); - - if (urlExists) { + //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'; diff --git a/bin/helpers/rust.ts b/bin/helpers/rust.ts index d31d13e..fa8b971 100644 --- a/bin/helpers/rust.ts +++ b/bin/helpers/rust.ts @@ -1,6 +1,6 @@ -import ora from 'ora'; import shelljs from 'shelljs'; +import { getSpinner } from "@/utils/info"; import { IS_WIN } from '@/utils/platform'; import { shellExec } from '@/utils/shell'; import { isChinaDomain } from '@/utils/ip'; @@ -12,16 +12,14 @@ export async function installRust() { : "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y"; const rustInstallScriptForWindows = 'winget install --id Rustlang.Rustup'; - const spinner = ora('Downloading Rust').start(); + const spinner = getSpinner('Downloading Rust.'); try { await shellExec(IS_WIN ? rustInstallScriptForWindows : rustInstallScriptForMac); - spinner.succeed(); + spinner.succeed('Rust installed successfully.'); } catch (error) { console.error('Error installing Rust:', error.message); - spinner.fail(); - - //@ts-ignore + spinner.fail('Rust installation failed.'); process.exit(1); } } diff --git a/bin/options/icon.ts b/bin/options/icon.ts index afe1464..dfce0c7 100644 --- a/bin/options/icon.ts +++ b/bin/options/icon.ts @@ -1,3 +1,4 @@ +import { getSpinner } from "@/utils/info"; import path from 'path'; import axios from 'axios'; import fsExtra from "fs-extra"; @@ -24,9 +25,9 @@ export async function handleIcon(options: PakeAppOptions) { } export async function downloadIcon(iconUrl: string) { + const spinner = getSpinner('Downloading icon.'); try { const iconResponse = await axios.get(iconUrl, { responseType: 'arraybuffer' }); - const iconData = await iconResponse.data; if (!iconData) { @@ -41,9 +42,10 @@ export async function downloadIcon(iconUrl: string) { const { path: tempPath } = await dir(); const iconPath = `${tempPath}/icon.${fileDetails.ext}`; await fsExtra.outputFile(iconPath, iconData); - + spinner.succeed('Icon downloaded successfully.'); return iconPath; } catch (error) { + spinner.fail('Icon download failed.'); if (error.response && error.response.status === 404) { return null; } diff --git a/bin/options/index.ts b/bin/options/index.ts index 2237bf6..a2e157a 100644 --- a/bin/options/index.ts +++ b/bin/options/index.ts @@ -1,24 +1,57 @@ import fsExtra from "fs-extra"; +import logger from "@/options/logger"; import { handleIcon } from './icon'; import { getDomain } from '@/utils/url'; -import { getIdentifier, promptText } from '@/utils/info'; -import { PakeAppOptions, PakeCliOptions } from '@/types'; +import { getIdentifier, promptText, capitalizeFirstLetter } from '@/utils/info'; +import { PakeAppOptions, PakeCliOptions, PlatformMap } from '@/types'; + +function resolveAppName(name: string, platform: NodeJS.Platform): string { + const domain = getDomain(name) || 'pake'; + return platform !== 'linux' ? capitalizeFirstLetter(domain) : domain; +} + +function isValidName(name: string, platform: NodeJS.Platform): boolean { + const platformRegexMapping: PlatformMap = { + linux: /^[a-z0-9]+(-[a-z0-9]+)*$/, + default: /^[a-zA-Z0-9]+$/, + }; + const reg = platformRegexMapping[platform] || platformRegexMapping.default; + return !!name && reg.test(name); +} export default async function handleOptions(options: PakeCliOptions, url: string): Promise { + 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 = `Package name is invalid. It 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 = `Package name is invalid. It 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: PakeAppOptions = { ...options, + name, identifier: getIdentifier(url), }; - let urlExists = await fsExtra.pathExists(url); - - if (!appOptions.name) { - const defaultName = urlExists ? "" : getDomain(url); - const promptMessage = 'Enter your application name'; - appOptions.name = await promptText(promptMessage, defaultName); - } - appOptions.icon = await handleIcon(appOptions); return appOptions; diff --git a/bin/utils/info.ts b/bin/utils/info.ts index 9090437..a7fd3eb 100644 --- a/bin/utils/info.ts +++ b/bin/utils/info.ts @@ -1,5 +1,6 @@ import crypto from 'crypto'; import prompts from "prompts"; +import ora from "ora"; // Generates an identifier based on the given URL. export function getIdentifier(url: string) { @@ -19,3 +20,23 @@ export async function promptText(message: string, initial?: string): Promise { const timeoutPromise = new Promise((_, reject) => { setTimeout(() => { reject(new Error('Request timed out after 3 seconds')); - }, 3000); + }, 1000); }); return Promise.race([requestPromise, timeoutPromise]); @@ -40,7 +40,7 @@ async function isChinaDomain(domain: string): Promise { return await isChinaIP(ip, domain); } catch (error) { logger.debug(`${domain} can't be parse!`); - return false; + return true; } } @@ -48,10 +48,10 @@ async function isChinaIP(ip: string, domain: string): Promise { try { const delay = await ping(ip); logger.debug(`${domain} latency is ${delay} ms`); - return delay > 500; + return delay > 1000; } catch (error) { logger.debug(`ping ${domain} failed!`); - return false; + return true; } } diff --git a/dist/cli.js b/dist/cli.js index 881a9aa..bb0d8fb 100644 --- a/dist/cli.js +++ b/dist/cli.js @@ -1,17 +1,17 @@ -import ora from 'ora'; import log from 'loglevel'; +import chalk from 'chalk'; import { InvalidArgumentError, program } from 'commander'; import fsExtra from 'fs-extra'; +import crypto from 'crypto'; +import prompts from 'prompts'; +import ora from 'ora'; import path from 'path'; import axios from 'axios'; import { dir } from 'tmp-promise'; import { fileTypeFromBuffer } from 'file-type'; -import chalk from 'chalk'; import { fileURLToPath } from 'url'; import psl from 'psl'; import isUrl from 'is-url'; -import crypto from 'crypto'; -import prompts from 'prompts'; import shelljs from 'shelljs'; import dns from 'dns'; import http from 'http'; @@ -37,6 +37,42 @@ const logger = { } }; +// 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": 100, + "frames": [ + "✶", + "✵", + "✸", + "✹", + "✺", + "✹", + "✷", + ] + }; + return ora({ text: `${text}\n`, spinner: loadingType }).start(); +} + // Convert the current module URL to a file path const currentModulePath = fileURLToPath(import.meta.url); // Resolve the parent directory of the current module @@ -63,6 +99,7 @@ async function handleIcon(options) { } } async function downloadIcon(iconUrl) { + const spinner = getSpinner('Downloading icon.'); try { const iconResponse = await axios.get(iconUrl, { responseType: 'arraybuffer' }); const iconData = await iconResponse.data; @@ -76,9 +113,11 @@ async function downloadIcon(iconUrl) { const { path: tempPath } = await dir(); const iconPath = `${tempPath}/icon.${fileDetails.ext}`; await fsExtra.outputFile(iconPath, iconData); + spinner.succeed('Icon downloaded successfully.'); return iconPath; } catch (error) { + spinner.fail('Icon download failed.'); if (error.response && error.response.status === 404) { return null; } @@ -125,39 +164,174 @@ function normalizeUrl(urlToNormalize) { } } -// 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}`; +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 promptText(message, initial) { - const response = await prompts({ - type: 'text', - name: 'content', - message, - initial, - }); - return response.content; -} - 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 = `Package name is invalid. It 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 = `Package name is invalid. It 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), }; - let urlExists = await fsExtra.pathExists(url); - if (!appOptions.name) { - const defaultName = urlExists ? "" : getDomain(url); - const promptMessage = 'Enter your application name'; - appOptions.name = await promptText(promptMessage, defaultName); - } appOptions.icon = await handleIcon(appOptions); return appOptions; } +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 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('Rust installed successfully'); + } + catch (error) { + console.error('Error installing Rust:', error.message); + spinner.fail('Rust installation failed'); + process.exit(1); + } +} +function checkRustInstalled() { + return shelljs.exec('rustc --version', { silent: true }).code === 0; +} + +class BaseBuilder { + async prepare() { + // Windows and Linux need to install necessary build tools. + if (!IS_MAC) { + logger.info('The first use requires installing system dependencies.'); + logger.info('See more in https://tauri.app/v1/guides/getting-started/prerequisites#installing.'); + } + if (!checkRustInstalled()) { + const res = await prompts({ + type: 'confirm', + message: 'Rust not detected. Install now?', + name: 'value', + }); + if (res.value) { + await installRust(); + } + else { + logger.error('Error: Rust required to package your webapp!'); + process.exit(0); + } + } + const isChina = await isChinaDomain("www.npmjs.com"); + const spinner = getSpinner('Installing package.'); + if (isChina) { + logger.info("Located in China, using npm/rsProxy CN mirror."); + const rustProjectDir = path.join(npmDirectory, 'src-tauri', ".cargo"); + await fsExtra.ensureDir(rustProjectDir); + const projectCnConf = path.join(npmDirectory, "src-tauri", "rust_proxy.toml"); + const projectConf = path.join(rustProjectDir, "config"); + 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('Package installed.'); + } + async runBuildCommand(command = "npm run build") { + const spinner = getSpinner('Building app.'); + await shellExec(`cd "${npmDirectory}" && ${command}`); + spinner.stop(); + } +} + var windows = [ { url: "https://weread.qq.com/", @@ -358,128 +532,6 @@ let tauriConfig = { 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(`${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')); - }, 3000); - }); - 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 false; - } -} -async function isChinaIP(ip, domain) { - try { - const delay = await ping(ip); - logger.debug(`${domain} latency is ${delay} ms`); - return delay > 500; - } - catch (error) { - logger.debug(`ping ${domain} failed!`); - return false; - } -} - -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 = ora('Downloading Rust').start(); - try { - await shellExec(IS_WIN ? rustInstallScriptForWindows : rustInstallScriptForMac); - spinner.succeed(); - } - catch (error) { - console.error('Error installing Rust:', error.message); - spinner.fail(); - //@ts-ignore - process.exit(1); - } -} -function checkRustInstalled() { - return shelljs.exec('rustc --version', { silent: true }).code === 0; -} - -class BaseBuilder { - async prepare() { - // Windows and Linux need to install necessary build tools. - if (!IS_MAC) { - logger.info('Install Rust and required build tools to build the app.'); - logger.info('See more in https://tauri.app/v1/guides/getting-started/prerequisites#installing.'); - } - if (checkRustInstalled()) { - return; - } - const res = await prompts({ - type: 'confirm', - message: 'Rust not detected. Install now?', - name: 'value', - }); - if (res.value) { - await installRust(); - } - else { - logger.error('Error: Rust required to package your webapp!'); - process.exit(2); - } - } - async runBuildCommand(directory, command) { - const spinner = ora('Building...').start(); - setTimeout(() => spinner.succeed(), 5000); - const isChina = await isChinaDomain("www.npmjs.com"); - if (isChina) { - logger.info("Located in China, using npm/Rust CN mirror."); - const rustProjectDir = path.join(directory, 'src-tauri', ".cargo"); - await fsExtra.ensureDir(rustProjectDir); - const projectCnConf = path.join(directory, "src-tauri", "rust_proxy.toml"); - const projectConf = path.join(rustProjectDir, "config"); - await fsExtra.copy(projectCnConf, projectConf); - await shellExec(`cd "${directory}" && npm install --registry=https://registry.npmmirror.com && ${command}`); - } - else { - await shellExec(`cd "${directory}" && npm install && ${command}`); - } - } -} - async function mergeConfig(url, options, tauriConf) { const { width, height, fullscreen, transparent, resizable, userAgent, showMenu, showSystemTray, systemTrayIcon, iterCopyFile, identifier, name, } = options; const { platform } = process; @@ -492,27 +544,11 @@ async function mergeConfig(url, options, tauriConf) { resizable, }; Object.assign(tauriConf.pake.windows[0], { url, ...tauriConfWindowOptions }); - // Determine whether the package name is valid. - // for Linux, package name must be a-z, 0-9 or "-", not allow to A-Z and other - const platformRegexMapping = { - linux: /[0-9]*[a-z]+[0-9]*\-?[0-9]*[a-z]*[0-9]*\-?[0-9]*[a-z]*[0-9]*/, - default: /([0-9]*[a-zA-Z]+[0-9]*)+/, - }; - const reg = platformRegexMapping[platform] || platformRegexMapping.default; - const nameCheck = reg.test(name) && reg.exec(name)[0].length === name.length; - if (!nameCheck) { - const errorMsg = platform === 'linux' - ? `Package name is invalid. It 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.` - : `Package name is invalid. It should only include letters and numbers, and must contain at least one letter. Examples: 123pan, 123Pan, Pan123, weread, WeRead, WERead.`; - logger.error(errorMsg); - process.exit(); - } tauriConf.package.productName = name; tauriConf.tauri.bundle.identifier = identifier; - // Judge the type of URL, whether it is a file or a website. - // If it is a file and the recursive copy function is enabled then the file and all files in its parent folder need to be copied to the "src" directory. Otherwise, only the single file will be copied. - const urlExists = await fsExtra.pathExists(url); - if (urlExists) { + //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); @@ -654,11 +690,11 @@ class MacBuilder extends BaseBuilder { await mergeConfig(url, options, tauriConfig); let dmgName; if (options.multiArch) { - await this.runBuildCommand(npmDirectory, 'npm run build:mac'); + await this.runBuildCommand('npm run build:mac'); dmgName = `${name}_${tauriConfig.package.version}_universal.dmg`; } else { - await this.runBuildCommand(npmDirectory, 'npm run build'); + await this.runBuildCommand(); let arch = process.arch === "arm64" ? "aarch64" : process.arch; dmgName = `${name}_${tauriConfig.package.version}_${arch}.dmg`; } @@ -679,7 +715,7 @@ class WinBuilder extends BaseBuilder { async build(url, options) { const { name } = options; await mergeConfig(url, options, tauriConfig); - await this.runBuildCommand(npmDirectory, 'npm run build'); + await this.runBuildCommand(); const language = tauriConfig.tauri.bundle.windows.wix.language[0]; const arch = process.arch; const msiName = `${name}_${tauriConfig.package.version}_${arch}_${language}.msi`; @@ -699,7 +735,7 @@ class LinuxBuilder extends BaseBuilder { async build(url, options) { const { name } = options; await mergeConfig(url, options, tauriConfig); - await this.runBuildCommand(npmDirectory, 'npm run build'); + await this.runBuildCommand(); const arch = process.arch === "x64" ? "amd64" : process.arch; if (options.targets === "deb" || options.targets === "all") { const debName = `${name}_${tauriConfig.package.version}_${arch}.deb`; @@ -884,8 +920,7 @@ const DEFAULT_PAKE_OPTIONS = { }; program - .version(packageJson.version) - .description('A CLI that can turn any webpage into a desktop app with Rust.') + .description(chalk.green('Pake: A CLI that can turn any webpage into a desktop app with Rust.')) .showHelpAfterError(); program .argument('[url]', 'The web URL you want to package', validateUrlInput) @@ -893,7 +928,7 @@ program .option('--icon ', 'Application icon', DEFAULT_PAKE_OPTIONS.icon) .option('--height ', 'Window height', validateNumberInput, DEFAULT_PAKE_OPTIONS.height) .option('--width ', 'Window width', validateNumberInput, DEFAULT_PAKE_OPTIONS.width) - .option('--no-resizable', 'Whether the window can be resizable', DEFAULT_PAKE_OPTIONS.resizable) + .option('--resizable', 'Whether the window can be resizable', DEFAULT_PAKE_OPTIONS.resizable) .option('--fullscreen', 'Start the packaged app in full screen', DEFAULT_PAKE_OPTIONS.fullscreen) .option('--transparent', 'Transparent title bar', DEFAULT_PAKE_OPTIONS.transparent) .option('--user-agent ', 'Custom user agent', DEFAULT_PAKE_OPTIONS.userAgent) @@ -901,26 +936,30 @@ program .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 to app when URL is a local file', DEFAULT_PAKE_OPTIONS.iterCopyFile) - .option('--multi-arch', 'Available for Mac only, supports both Intel and M1', DEFAULT_PAKE_OPTIONS.multiArch) + .option('--multi-arch', 'Only for Mac, supports both Intel and M1', DEFAULT_PAKE_OPTIONS.multiArch) .option('--targets ', 'Only for Linux, option "deb", "appimage" or "all"', 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) => { - //Check for update prompt await checkUpdateTips(); - // If no URL is provided, display help information if (!url) { - program.help(); + program.outputHelp((str) => { + const filteredOutput = str + .replace(/Usage:/g, '') // 隐藏 .usage 信息 + .replace(/(-h,|--help)\s+.+\n/g, '') // 隐藏帮助信息 + .replace(/\n\s*\n/g, '\n'); // 移除空行 + return filteredOutput.trim(); // Trim any leading/trailing whitespace + }); + process.exit(0); } log.setDefaultLevel('info'); if (options.debug) { log.setLevel('debug'); } - const spinner = ora('Preparing...').start(); + const appOptions = await handleOptions(options, url); + log.debug('PakeAppOptions', appOptions); const builder = BuilderProvider.create(); await builder.prepare(); - const appOptions = await handleOptions(options, url); - spinner.succeed(); - log.debug('PakeAppOptions', appOptions); await builder.build(url, appOptions); }); program.parse(); From 6a4bb696311b78db3c9fb48e055992def521a346 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Fri, 23 Jun 2023 13:15:49 +0800 Subject: [PATCH 24/40] :art: Optimize CLI usage. --- bin/builders/BaseBuilder.ts | 8 +- bin/builders/LinuxBuilder.ts | 8 +- bin/builders/MacBuilder.ts | 4 +- bin/builders/WinBuilder.ts | 4 +- bin/cli.ts | 17 +- bin/helpers/rust.ts | 2 +- bin/options/icon.ts | 2 +- dist/cli.js | 628 +++++++++++++++++------------------ package.json | 2 +- 9 files changed, 337 insertions(+), 338 deletions(-) diff --git a/bin/builders/BaseBuilder.ts b/bin/builders/BaseBuilder.ts index 2a8134d..e07ed0c 100644 --- a/bin/builders/BaseBuilder.ts +++ b/bin/builders/BaseBuilder.ts @@ -7,9 +7,9 @@ import { shellExec } from '@/utils/shell'; import { isChinaDomain } from '@/utils/ip'; import { getSpinner } from "@/utils/info"; import { npmDirectory } from '@/utils/dir'; -import { PakeAppOptions } from '@/types'; import { IS_MAC } from "@/utils/platform"; import { checkRustInstalled, installRust } from '@/helpers/rust'; +import { PakeAppOptions } from '@/types'; export default abstract class BaseBuilder { abstract build(url: string, options: PakeAppOptions): Promise; @@ -37,7 +37,7 @@ export default abstract class BaseBuilder { } const isChina = await isChinaDomain("www.npmjs.com"); - const spinner = getSpinner('Installing package.'); + const spinner = getSpinner('Installing package...'); if (isChina) { logger.info("Located in China, using npm/rsProxy CN mirror."); const rustProjectDir = path.join(npmDirectory, 'src-tauri', ".cargo"); @@ -53,8 +53,8 @@ export default abstract class BaseBuilder { } protected async runBuildCommand(command: string = "npm run build") { - const spinner = getSpinner('Building app.'); + const spinner = getSpinner('Building app...'); + setTimeout(() => spinner.stop(), 3000); await shellExec(`cd "${npmDirectory}" && ${command}`); - spinner.stop(); } } diff --git a/bin/builders/LinuxBuilder.ts b/bin/builders/LinuxBuilder.ts index 41d87dd..14c1562 100644 --- a/bin/builders/LinuxBuilder.ts +++ b/bin/builders/LinuxBuilder.ts @@ -22,8 +22,8 @@ export default class LinuxBuilder extends BaseBuilder { const distPath = path.resolve(`${name}.deb`); await fsExtra.copy(appPath, distPath); await fsExtra.remove(appPath); - logger.success('Build Deb success!'); - logger.success('Deb app installer located in', distPath); + logger.success('✔ Build Deb success!'); + logger.success('✔ Deb app installer located in', distPath); } if (options.targets === "appimage" || options.targets === "all") { @@ -32,8 +32,8 @@ export default class LinuxBuilder extends BaseBuilder { const distAppPath = path.resolve(`${name}.AppImage`); await fsExtra.copy(appImagePath, distAppPath); await fsExtra.remove(appImagePath); - logger.success('Build AppImage success!'); - logger.success('AppImage installer located in', distAppPath); + logger.success('✔ Build AppImage success!'); + logger.success('✔ AppImage installer located in', distAppPath); } } diff --git a/bin/builders/MacBuilder.ts b/bin/builders/MacBuilder.ts index cfc55f1..340110f 100644 --- a/bin/builders/MacBuilder.ts +++ b/bin/builders/MacBuilder.ts @@ -25,8 +25,8 @@ export default class MacBuilder extends BaseBuilder { const distPath = path.resolve(`${name}.dmg`); await fsExtra.copy(appPath, distPath); await fsExtra.remove(appPath); - logger.success('Build success!'); - logger.success('App installer located in', distPath); + logger.success('✔ Build success!'); + logger.success('✔ App installer located in', distPath); } getBuildAppPath(npmDirectory: string, dmgName: string, multiArch: boolean) { diff --git a/bin/builders/WinBuilder.ts b/bin/builders/WinBuilder.ts index 910713a..760214a 100644 --- a/bin/builders/WinBuilder.ts +++ b/bin/builders/WinBuilder.ts @@ -21,8 +21,8 @@ export default class WinBuilder extends BaseBuilder { const distPath = path.resolve(`${name}.msi`); await fsExtra.copy(appPath, distPath); await fsExtra.remove(appPath); - logger.success('Build success!'); - logger.success('App installer located in', distPath); + logger.success('✔ Build success!'); + logger.success('✔ App installer located in', distPath); } getBuildAppPath(npmDirectory: string, msiName: string) { diff --git a/bin/cli.ts b/bin/cli.ts index cfc21ce..2cd5f95 100644 --- a/bin/cli.ts +++ b/bin/cli.ts @@ -1,17 +1,17 @@ -import log from 'loglevel'; import chalk from 'chalk'; import { program } from 'commander'; +import log from 'loglevel'; +import packageJson from '../package.json'; +import BuilderProvider from './builders/BuilderProvider'; +import { DEFAULT_PAKE_OPTIONS as DEFAULT } from './defaults'; +import { checkUpdateTips } from './helpers/updater'; +import handleInputOptions from './options/index'; import { PakeCliOptions } from './types'; -import handleInputOptions from './options/index'; -import BuilderProvider from './builders/BuilderProvider'; -import { checkUpdateTips } from './helpers/updater'; -import packageJson from '../package.json'; import { validateNumberInput, validateUrlInput } from './utils/validate'; -import { DEFAULT_PAKE_OPTIONS as DEFAULT } from './defaults'; program - .description(chalk.green('Pake: A CLI that can turn any webpage into a desktop app with Rust.')) + .description(chalk.green('Pake can turn any webpage into a desktop app with Rust.')) .usage('[url] [options]') .showHelpAfterError(); @@ -39,11 +39,10 @@ program if (!url) { program.outputHelp((str) => { - const filteredOutput = str + return str .split('\n') .filter((line) => !/((-h,|--help)|((-v|-V),|--version))\s+.+$/.test(line)) .join('\n'); - return filteredOutput.trim(); // Trim any leading/trailing whitespace }); process.exit(0); } diff --git a/bin/helpers/rust.ts b/bin/helpers/rust.ts index fa8b971..4da648d 100644 --- a/bin/helpers/rust.ts +++ b/bin/helpers/rust.ts @@ -12,7 +12,7 @@ export async function installRust() { : "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.'); + const spinner = getSpinner('Downloading Rust...'); try { await shellExec(IS_WIN ? rustInstallScriptForWindows : rustInstallScriptForMac); diff --git a/bin/options/icon.ts b/bin/options/icon.ts index dfce0c7..b465da7 100644 --- a/bin/options/icon.ts +++ b/bin/options/icon.ts @@ -25,7 +25,7 @@ export async function handleIcon(options: PakeAppOptions) { } export async function downloadIcon(iconUrl: string) { - const spinner = getSpinner('Downloading icon.'); + const spinner = getSpinner('Downloading icon...'); try { const iconResponse = await axios.get(iconUrl, { responseType: 'arraybuffer' }); const iconData = await iconResponse.data; diff --git a/dist/cli.js b/dist/cli.js index bb0d8fb..5771fdb 100644 --- a/dist/cli.js +++ b/dist/cli.js @@ -1,24 +1,125 @@ -import log from 'loglevel'; import chalk from 'chalk'; import { InvalidArgumentError, program } from 'commander'; -import fsExtra from 'fs-extra'; -import crypto from 'crypto'; -import prompts from 'prompts'; -import ora from 'ora'; +import log from 'loglevel'; import path from 'path'; -import axios from 'axios'; -import { dir } from 'tmp-promise'; -import { fileTypeFromBuffer } from 'file-type'; -import { fileURLToPath } from 'url'; -import psl from 'psl'; -import isUrl from 'is-url'; +import fsExtra from 'fs-extra'; +import prompts from 'prompts'; import shelljs from 'shelljs'; +import { fileURLToPath } from 'url'; import dns from 'dns'; import http from 'http'; import { promisify } from 'util'; +import crypto from 'crypto'; +import ora from 'ora'; 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.2"; +var description = "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。"; +var engines = { + node: ">=16.0.0" +}; +var bin = { + pake: "./cli.js" +}; +var repository = { + type: "git", + url: "https://github.com/tw93/pake.git" +}; +var author = { + name: "Tw93", + email: "tw93@qq.com" +}; +var keywords = [ + "pake", + "pake-cli", + "rust", + "tauri", + "no-electron", + "productivity" +]; +var files = [ + "dist", + "src-tauri", + "cli.js" +]; +var scripts = { + start: "npm run dev", + dev: "npm run tauri dev", + build: "npm run tauri build --release", + "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:build": "cross-env NODE_ENV=production rollup -c rollup.config.js", + prepublishOnly: "npm run cli:build" +}; +var type = "module"; +var exports = "./dist/pake.js"; +var license = "MIT"; +var dependencies = { + "@tauri-apps/api": "^1.4.0", + "@tauri-apps/cli": "^1.4.0", + axios: "^1.1.3", + chalk: "^5.1.2", + commander: "^11.0.0", + "file-type": "^18.0.0", + "fs-extra": "^11.1.0", + "is-url": "^1.2.4", + loglevel: "^1.8.1", + ora: "^6.1.2", + prompts: "^2.4.2", + psl: "^1.9.0", + shelljs: "^0.8.5", + "tmp-promise": "^3.0.3", + "update-notifier": "^6.0.2" +}; +var devDependencies = { + "@rollup/plugin-alias": "^4.0.2", + "@rollup/plugin-commonjs": "^23.0.2", + "@rollup/plugin-json": "^5.0.2", + "@rollup/plugin-terser": "^0.1.0", + "@types/fs-extra": "^9.0.13", + "@types/is-url": "^1.2.30", + "@types/page-icon": "^0.3.4", + "@types/prompts": "^2.4.1", + "@types/psl": "^1.1.0", + "@types/shelljs": "^0.8.11", + "@types/tmp": "^0.2.3", + "@types/update-notifier": "^6.0.1", + "app-root-path": "^3.1.0", + "cross-env": "^7.0.3", + rollup: "^3.3.0", + "rollup-plugin-typescript2": "^0.34.1", + tslib: "^2.4.1", + typescript: "^4.9.3" +}; +var packageJson = { + name: name, + version: version, + description: description, + engines: engines, + bin: bin, + repository: repository, + author: author, + keywords: keywords, + files: files, + scripts: scripts, + type: type, + exports: exports, + license: license, + dependencies: dependencies, + devDependencies: devDependencies +}; + const logger = { info(...msg) { log.info(...msg.map((m) => chalk.blue.bold(m))); @@ -37,178 +138,11 @@ const logger = { } }; -// 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": 100, - "frames": [ - "✶", - "✵", - "✸", - "✹", - "✺", - "✹", - "✷", - ] - }; - return ora({ text: `${text}\n`, spinner: loadingType }).start(); -} - // 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 { platform: platform$2 } = process; -const IS_MAC = platform$2 === 'darwin'; -const IS_WIN = platform$2 === 'win32'; -const IS_LINUX = platform$2 === 'linux'; - -async function handleIcon(options) { - if (options.icon) { - if (options.icon.startsWith('http')) { - return downloadIcon(options.icon); - } - else { - return path.resolve(options.icon); - } - } - else { - logger.info('No app icon provided, default icon used. Use --icon option to assign an icon.'); - 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('Icon downloaded successfully.'); - return iconPath; - } - catch (error) { - spinner.fail('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`); - } -} - -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 = `Package name is invalid. It 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 = `Package name is invalid. It 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 shellExec(command) { return new Promise((resolve, reject) => { shelljs.exec(command, { async: true, silent: false, cwd: npmDirectory }, (code) => { @@ -267,20 +201,61 @@ async function isChinaIP(ip, domain) { } } +// 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": 100, + "frames": [ + "✶", + "✵", + "✸", + "✹", + "✺", + "✹", + "✷", + ] + }; + return ora({ text: `${text}\n`, spinner: loadingType }).start(); +} + +const { platform: platform$2 } = process; +const IS_MAC = platform$2 === 'darwin'; +const IS_WIN = platform$2 === 'win32'; +const IS_LINUX = platform$2 === 'linux'; + 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'); + const spinner = getSpinner('Downloading Rust...'); try { await shellExec(IS_WIN ? rustInstallScriptForWindows : rustInstallScriptForMac); - spinner.succeed('Rust installed successfully'); + spinner.succeed('Rust installed successfully.'); } catch (error) { console.error('Error installing Rust:', error.message); - spinner.fail('Rust installation failed'); + spinner.fail('Rust installation failed.'); process.exit(1); } } @@ -310,7 +285,7 @@ class BaseBuilder { } } const isChina = await isChinaDomain("www.npmjs.com"); - const spinner = getSpinner('Installing package.'); + const spinner = getSpinner('Installing package...'); if (isChina) { logger.info("Located in China, using npm/rsProxy CN mirror."); const rustProjectDir = path.join(npmDirectory, 'src-tauri', ".cargo"); @@ -326,9 +301,9 @@ class BaseBuilder { spinner.succeed('Package installed.'); } async runBuildCommand(command = "npm run build") { - const spinner = getSpinner('Building app.'); + const spinner = getSpinner('Building app...'); + setTimeout(() => spinner.stop(), 2000); await shellExec(`cd "${npmDirectory}" && ${command}`); - spinner.stop(); } } @@ -702,8 +677,8 @@ class MacBuilder extends BaseBuilder { const distPath = path.resolve(`${name}.dmg`); await fsExtra.copy(appPath, distPath); await fsExtra.remove(appPath); - logger.success('Build success!'); - logger.success('App installer located in', distPath); + logger.success('✔ Build success!'); + logger.success('✔ App installer located in', distPath); } getBuildAppPath(npmDirectory, dmgName, multiArch) { const dmgPath = multiArch ? 'src-tauri/target/universal-apple-darwin/release/bundle/dmg' : 'src-tauri/target/release/bundle/dmg'; @@ -723,8 +698,8 @@ class WinBuilder extends BaseBuilder { const distPath = path.resolve(`${name}.msi`); await fsExtra.copy(appPath, distPath); await fsExtra.remove(appPath); - logger.success('Build success!'); - logger.success('App installer located in', distPath); + logger.success('✔ Build success!'); + logger.success('✔ App installer located in', distPath); } getBuildAppPath(npmDirectory, msiName) { return path.join(npmDirectory, 'src-tauri/target/release/bundle/msi', msiName); @@ -743,8 +718,8 @@ class LinuxBuilder extends BaseBuilder { const distPath = path.resolve(`${name}.deb`); await fsExtra.copy(appPath, distPath); await fsExtra.remove(appPath); - logger.success('Build Deb success!'); - logger.success('Deb app installer located in', distPath); + logger.success('✔ Build Deb success!'); + logger.success('✔ Deb app installer located in', distPath); } if (options.targets === "appimage" || options.targets === "all") { const appImageName = `${name}_${tauriConfig.package.version}_${arch}.AppImage`; @@ -752,8 +727,8 @@ class LinuxBuilder extends BaseBuilder { const distAppPath = path.resolve(`${name}.AppImage`); await fsExtra.copy(appImagePath, distAppPath); await fsExtra.remove(appImagePath); - logger.success('Build AppImage success!'); - logger.success('AppImage installer located in', distAppPath); + logger.success('✔ Build AppImage success!'); + logger.success('✔ AppImage installer located in', distAppPath); } } getBuildAppPath(npmDirectory, packageType, packageName) { @@ -777,111 +752,153 @@ class BuilderProvider { } } -var name = "pake-cli"; -var version = "2.1.1"; -var description = "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。"; -var engines = { - node: ">=16.0.0" -}; -var bin = { - pake: "./cli.js" -}; -var repository = { - type: "git", - url: "https://github.com/tw93/pake.git" -}; -var author = { - name: "Tw93", - email: "tw93@qq.com" -}; -var keywords = [ - "pake", - "pake-cli", - "rust", - "tauri", - "no-electron", - "productivity" -]; -var files = [ - "dist", - "src-tauri", - "cli.js" -]; -var scripts = { - start: "npm run dev", - dev: "npm run tauri dev", - build: "npm run tauri build --release", - "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:build": "cross-env NODE_ENV=production rollup -c rollup.config.js", - prepublishOnly: "npm run cli:build" -}; -var type = "module"; -var exports = "./dist/pake.js"; -var license = "MIT"; -var dependencies = { - "@tauri-apps/api": "^1.4.0", - "@tauri-apps/cli": "^1.4.0", - axios: "^1.1.3", - chalk: "^5.1.2", - commander: "^11.0.0", - "file-type": "^18.0.0", - "fs-extra": "^11.1.0", - "is-url": "^1.2.4", - loglevel: "^1.8.1", - ora: "^6.1.2", - prompts: "^2.4.2", - psl: "^1.9.0", - shelljs: "^0.8.5", - "tmp-promise": "^3.0.3", - "update-notifier": "^6.0.2" -}; -var devDependencies = { - "@rollup/plugin-alias": "^4.0.2", - "@rollup/plugin-commonjs": "^23.0.2", - "@rollup/plugin-json": "^5.0.2", - "@rollup/plugin-terser": "^0.1.0", - "@types/fs-extra": "^9.0.13", - "@types/is-url": "^1.2.30", - "@types/page-icon": "^0.3.4", - "@types/prompts": "^2.4.1", - "@types/psl": "^1.1.0", - "@types/shelljs": "^0.8.11", - "@types/tmp": "^0.2.3", - "@types/update-notifier": "^6.0.1", - "app-root-path": "^3.1.0", - "cross-env": "^7.0.3", - rollup: "^3.3.0", - "rollup-plugin-typescript2": "^0.34.1", - tslib: "^2.4.1", - typescript: "^4.9.3" -}; -var packageJson = { - name: name, - version: version, - description: description, - engines: engines, - bin: bin, - repository: repository, - author: author, - keywords: keywords, - files: files, - scripts: scripts, - type: type, - exports: exports, - license: license, - dependencies: dependencies, - devDependencies: devDependencies +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, }; async function checkUpdateTips() { updateNotifier({ pkg: packageJson }).notify(); } +async function handleIcon(options) { + if (options.icon) { + if (options.icon.startsWith('http')) { + return downloadIcon(options.icon); + } + else { + return path.resolve(options.icon); + } + } + else { + logger.info('No app icon provided, default icon used. Use --icon option to assign an icon.'); + 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('Icon downloaded successfully.'); + return iconPath; + } + catch (error) { + spinner.fail('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`); + } +} + +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 = `Package name is invalid. It 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 = `Package name is invalid. It 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)) { @@ -902,25 +919,9 @@ function validateUrlInput(url) { return url; } -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, -}; - program - .description(chalk.green('Pake: A CLI that can turn any webpage into a desktop app with Rust.')) + .description(chalk.green('Pake can turn any webpage into a desktop app with Rust.')) + .usage('[url] [options]') .showHelpAfterError(); program .argument('[url]', 'The web URL you want to package', validateUrlInput) @@ -944,11 +945,10 @@ program await checkUpdateTips(); if (!url) { program.outputHelp((str) => { - const filteredOutput = str - .replace(/Usage:/g, '') // 隐藏 .usage 信息 - .replace(/(-h,|--help)\s+.+\n/g, '') // 隐藏帮助信息 - .replace(/\n\s*\n/g, '\n'); // 移除空行 - return filteredOutput.trim(); // Trim any leading/trailing whitespace + return str + .split('\n') + .filter((line) => !/((-h,|--help)|((-v|-V),|--version))\s+.+$/.test(line)) + .join('\n'); }); process.exit(0); } diff --git a/package.json b/package.json index d4839e8..a65e22f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pake-cli", - "version": "2.1.1", + "version": "2.1.2", "description": "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。", "engines": { "node": ">=16.0.0" From 0431bc7c121a27e9c9a3ba319485615901e82c8e Mon Sep 17 00:00:00 2001 From: Tw93 Date: Fri, 23 Jun 2023 16:51:18 +0800 Subject: [PATCH 25/40] :art: CLI is more user-friendly. --- bin/README.md | 8 - bin/README_CN.md | 8 - bin/builders/BaseBuilder.ts | 64 +++- bin/builders/BuilderProvider.ts | 7 +- bin/builders/LinuxBuilder.ts | 59 ++-- bin/builders/MacBuilder.ts | 58 ++-- bin/builders/WinBuilder.ts | 43 +-- bin/cli.ts | 5 +- bin/utils/info.ts | 2 +- dist/cli.js | 524 +++++++++++++++++--------------- 10 files changed, 402 insertions(+), 376 deletions(-) diff --git a/bin/README.md b/bin/README.md index 37ac1b9..47c4cd8 100644 --- a/bin/README.md +++ b/bin/README.md @@ -107,14 +107,6 @@ Determine whether the application launches in full screen. Default is `false`. U --fullscreen ``` -#### [resize] - -Determine whether the window is resizable. Default is `true`. Use the following command to disable window resizing. - -```shell ---resizable -``` - #### [multi-arch] Package the application to support both Intel and M1 chips, exclusively for macOS. Default is `false`. diff --git a/bin/README_CN.md b/bin/README_CN.md index 31c088f..5ca8152 100644 --- a/bin/README_CN.md +++ b/bin/README_CN.md @@ -101,14 +101,6 @@ pake [url] [options] --transparent ``` -#### [resize] - -设置应用窗口是否可以调整大小,默认为 `true`(可调整)。使用以下命令可以禁止调整窗口大小。 - -```shell ---resizable -``` - #### [fullscreen] 设置应用程序是否在启动时自动全屏,默认为 `false`。使用以下命令可以设置应用程序启动时自动全屏。 diff --git a/bin/builders/BaseBuilder.ts b/bin/builders/BaseBuilder.ts index e07ed0c..5d78aff 100644 --- a/bin/builders/BaseBuilder.ts +++ b/bin/builders/BaseBuilder.ts @@ -2,20 +2,25 @@ import path from 'path'; import fsExtra from "fs-extra"; import prompts from 'prompts'; -import logger from '@/options/logger'; +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 { getSpinner } from "@/utils/info"; -import { npmDirectory } from '@/utils/dir'; import { IS_MAC } from "@/utils/platform"; -import { checkRustInstalled, installRust } from '@/helpers/rust'; -import { PakeAppOptions } from '@/types'; +import logger from '@/options/logger'; export default abstract class BaseBuilder { - abstract build(url: string, options: PakeAppOptions): Promise; + protected options: PakeAppOptions; + + protected constructor(options: PakeAppOptions) { + this.options = options; + } async prepare() { - // Windows and Linux need to install necessary build tools. if (!IS_MAC) { logger.info('The first use requires installing system dependencies.'); logger.info('See more in https://tauri.app/v1/guides/getting-started/prerequisites#installing.'); @@ -52,9 +57,50 @@ export default abstract class BaseBuilder { spinner.succeed('Package installed.'); } - protected async runBuildCommand(command: string = "npm run build") { + async buildAndCopy(url: string) { + const { name } = this.options; + await mergeConfig(url, this.options, tauriConfig); + await this.runBuildCommand(); + + const fileName = this.getFileName(); + const appPath = this.getBuildAppPath(npmDirectory, fileName); + const distPath = path.resolve(`${name}.${this.getExtension()}`); + await fsExtra.copy(appPath, distPath); + await fsExtra.remove(appPath); + logger.success('✔ Build success!'); + logger.success('✔ App installer located in', distPath); + } + + abstract build(url: string): Promise; + + abstract getFileName(): string; + + abstract getExtension(): string; + + protected getArch() { + return process.arch === "x64" ? "amd64" : process.arch; + } + + protected getBuildCommand(): string { + return "npm run build"; + } + + protected runBuildCommand() { const spinner = getSpinner('Building app...'); setTimeout(() => spinner.stop(), 3000); - await shellExec(`cd "${npmDirectory}" && ${command}`); + return shellExec(`cd ${npmDirectory} && ${this.getBuildCommand()}`); + } + + protected getBasePath(): string { + return 'src-tauri/target/release/bundle/'; + } + + protected getBuildAppPath(npmDirectory: string, fileName: string): string { + return path.join( + npmDirectory, + this.getBasePath(), + this.getExtension().toLowerCase(), + `${fileName}.${this.getExtension()}` + ); } } diff --git a/bin/builders/BuilderProvider.ts b/bin/builders/BuilderProvider.ts index 2670f25..392c54d 100644 --- a/bin/builders/BuilderProvider.ts +++ b/bin/builders/BuilderProvider.ts @@ -2,21 +2,22 @@ import BaseBuilder from './BaseBuilder'; import MacBuilder from './MacBuilder'; import WinBuilder from './WinBuilder'; import LinuxBuilder from './LinuxBuilder'; +import { PakeAppOptions } from '@/types'; const { platform } = process; -const buildersMap: Record BaseBuilder> = { +const buildersMap: Record BaseBuilder> = { darwin: MacBuilder, win32: WinBuilder, linux: LinuxBuilder, }; export default class BuilderProvider { - static create(): BaseBuilder { + static create(options: PakeAppOptions): BaseBuilder { const Builder = buildersMap[platform]; if (!Builder) { throw new Error('The current system is not supported!'); } - return new Builder(); + return new Builder(options); } } diff --git a/bin/builders/LinuxBuilder.ts b/bin/builders/LinuxBuilder.ts index 14c1562..ffac65f 100644 --- a/bin/builders/LinuxBuilder.ts +++ b/bin/builders/LinuxBuilder.ts @@ -1,48 +1,31 @@ -import path from 'path'; -import fsExtra from "fs-extra"; - import BaseBuilder from './BaseBuilder'; -import logger from '@/options/logger'; -import tauriConfig from '@/helpers/tauriConfig'; -import { npmDirectory } from '@/utils/dir'; -import { mergeConfig } from "@/helpers/merge"; import { PakeAppOptions } from '@/types'; +import tauriConfig from '@/helpers/tauriConfig'; export default class LinuxBuilder extends BaseBuilder { - async build(url: string, options: PakeAppOptions) { - const { name } = options; - await mergeConfig(url, options, tauriConfig); - await this.runBuildCommand(); + constructor(options: PakeAppOptions) { + super(options); + } - const arch = process.arch === "x64" ? "amd64" : process.arch; - - if (options.targets === "deb" || options.targets === "all") { - const debName = `${name}_${tauriConfig.package.version}_${arch}.deb`; - const appPath = this.getBuildAppPath(npmDirectory, "deb", debName); - const distPath = path.resolve(`${name}.deb`); - await fsExtra.copy(appPath, distPath); - await fsExtra.remove(appPath); - logger.success('✔ Build Deb success!'); - logger.success('✔ Deb app installer located in', distPath); - } - - if (options.targets === "appimage" || options.targets === "all") { - const appImageName = `${name}_${tauriConfig.package.version}_${arch}.AppImage`; - const appImagePath = this.getBuildAppPath(npmDirectory, "appimage", appImageName); - const distAppPath = path.resolve(`${name}.AppImage`); - await fsExtra.copy(appImagePath, distAppPath); - await fsExtra.remove(appImagePath); - logger.success('✔ Build AppImage success!'); - logger.success('✔ AppImage installer located in', distAppPath); + async build(url: string) { + const targetTypes = ['deb', 'appimage']; + for (const type of targetTypes) { + if (this.options.targets === type || this.options.targets === "all") { + await this.buildAndCopy(url); + } } } - getBuildAppPath(npmDirectory: string, packageType: string, packageName: string) { - return path.join( - npmDirectory, - 'src-tauri/target/release/bundle/', - packageType, - packageName - ); + getFileName(): string { + const { name } = this.options; + const arch = this.getArch(); + return `${name}_${tauriConfig.package.version}_${arch}`; + } + + getExtension(): string { + if (this.options.targets === 'appimage') { + return 'AppImage'; + } + return this.options.targets; } } diff --git a/bin/builders/MacBuilder.ts b/bin/builders/MacBuilder.ts index 340110f..8958787 100644 --- a/bin/builders/MacBuilder.ts +++ b/bin/builders/MacBuilder.ts @@ -1,36 +1,38 @@ -import path from 'path'; -import fsExtra from "fs-extra"; - -import BaseBuilder from './BaseBuilder'; -import logger from '@/options/logger'; import tauriConfig from '@/helpers/tauriConfig'; -import { npmDirectory } from '@/utils/dir'; -import { mergeConfig } from "@/helpers/merge"; import { PakeAppOptions } from '@/types'; +import BaseBuilder from './BaseBuilder'; export default class MacBuilder extends BaseBuilder { - async build(url: string, options: PakeAppOptions) { - const { name } = options; - await mergeConfig(url, options, tauriConfig); - let dmgName: string; - if (options.multiArch) { - await this.runBuildCommand('npm run build:mac'); - dmgName = `${name}_${tauriConfig.package.version}_universal.dmg`; - } else { - await this.runBuildCommand(); - let arch = process.arch === "arm64" ? "aarch64" : process.arch; - dmgName = `${name}_${tauriConfig.package.version}_${arch}.dmg`; - } - const appPath = this.getBuildAppPath(npmDirectory, dmgName, options.multiArch); - const distPath = path.resolve(`${name}.dmg`); - await fsExtra.copy(appPath, distPath); - await fsExtra.remove(appPath); - logger.success('✔ Build success!'); - logger.success('✔ App installer located in', distPath); + constructor(options: PakeAppOptions) { + super(options); } - getBuildAppPath(npmDirectory: string, dmgName: string, multiArch: boolean) { - const dmgPath = multiArch ? 'src-tauri/target/universal-apple-darwin/release/bundle/dmg' : 'src-tauri/target/release/bundle/dmg'; - return path.join(npmDirectory, dmgPath, dmgName); + async build(url: string) { + await this.buildAndCopy(url); + } + + getFileName(): string { + const { name } = this.options; + let arch: string; + if (this.options.multiArch) { + arch = 'universal'; + } else { + arch = process.arch === "arm64" ? "aarch64" : process.arch; + } + return `${name}_${tauriConfig.package.version}_${arch}`; + } + + getExtension(): string { + return "dmg"; + } + + protected getBuildCommand(): string { + return this.options.multiArch ? 'npm run build:mac' : super.getBuildCommand(); + } + + protected getBasePath(): string { + return this.options.multiArch + ? 'src-tauri/target/universal-apple-darwin/release/bundle' + : super.getBasePath(); } } diff --git a/bin/builders/WinBuilder.ts b/bin/builders/WinBuilder.ts index 760214a..706dea5 100644 --- a/bin/builders/WinBuilder.ts +++ b/bin/builders/WinBuilder.ts @@ -1,35 +1,24 @@ -import path from 'path'; -import fsExtra from 'fs-extra'; - import BaseBuilder from './BaseBuilder'; -import logger from '@/options/logger'; -import tauriConfig from '@/helpers/tauriConfig'; -import { npmDirectory } from '@/utils/dir'; -import { mergeConfig } from '@/helpers/merge'; import { PakeAppOptions } from '@/types'; +import tauriConfig from '@/helpers/tauriConfig'; export default class WinBuilder extends BaseBuilder { - async build(url: string, options: PakeAppOptions) { - const { name } = options; - await mergeConfig(url, options, tauriConfig); - await this.runBuildCommand(); - - const language = tauriConfig.tauri.bundle.windows.wix.language[0]; - const arch = process.arch; - const msiName = `${name}_${tauriConfig.package.version}_${arch}_${language}.msi`; - const appPath = this.getBuildAppPath(npmDirectory, msiName); - const distPath = path.resolve(`${name}.msi`); - await fsExtra.copy(appPath, distPath); - await fsExtra.remove(appPath); - logger.success('✔ Build success!'); - logger.success('✔ App installer located in', distPath); + constructor(options: PakeAppOptions) { + super(options); } - getBuildAppPath(npmDirectory: string, msiName: string) { - return path.join( - npmDirectory, - 'src-tauri/target/release/bundle/msi', - msiName - ); + async build(url: string) { + await this.buildAndCopy(url); + } + + getFileName(): string { + const { name } = this.options; + const arch = this.getArch(); + const language = tauriConfig.tauri.bundle.windows.wix.language[0]; + return `${name}_${tauriConfig.package.version}_${arch}_${language}`; + } + + getExtension(): string { + return "msi"; } } diff --git a/bin/cli.ts b/bin/cli.ts index 2cd5f95..ccdc4e6 100644 --- a/bin/cli.ts +++ b/bin/cli.ts @@ -21,7 +21,6 @@ program .option('--icon ', 'Application icon', DEFAULT.icon) .option('--height ', 'Window height', validateNumberInput, DEFAULT.height) .option('--width ', 'Window width', validateNumberInput, DEFAULT.width) - .option('--resizable', 'Whether the window can be resizable', DEFAULT.resizable) .option('--fullscreen', 'Start the packaged app in full screen', DEFAULT.fullscreen) .option('--transparent', 'Transparent title bar', DEFAULT.transparent) .option('--user-agent ', 'Custom user agent', DEFAULT.userAgent) @@ -55,9 +54,9 @@ program const appOptions = await handleInputOptions(options, url); log.debug('PakeAppOptions', appOptions); - const builder = BuilderProvider.create(); + const builder = BuilderProvider.create(appOptions); await builder.prepare(); - await builder.build(url, appOptions); + await builder.build(url); }); program.parse(); diff --git a/bin/utils/info.ts b/bin/utils/info.ts index a7fd3eb..68a1122 100644 --- a/bin/utils/info.ts +++ b/bin/utils/info.ts @@ -27,7 +27,7 @@ export function capitalizeFirstLetter(string: string) { export function getSpinner(text: string) { const loadingType = { - "interval": 100, + "interval": 80, "frames": [ "✶", "✵", diff --git a/dist/cli.js b/dist/cli.js index 5771fdb..00f186b 100644 --- a/dist/cli.js +++ b/dist/cli.js @@ -5,12 +5,12 @@ import path from 'path'; import fsExtra from 'fs-extra'; import prompts from 'prompts'; import shelljs from 'shelljs'; +import crypto from 'crypto'; +import ora from 'ora'; import { fileURLToPath } from 'url'; import dns from 'dns'; import http from 'http'; import { promisify } from 'util'; -import crypto from 'crypto'; -import ora from 'ora'; import updateNotifier from 'update-notifier'; import axios from 'axios'; import { dir } from 'tmp-promise'; @@ -120,193 +120,6 @@ var packageJson = { devDependencies: devDependencies }; -const logger = { - info(...msg) { - log.info(...msg.map((m) => chalk.blue.bold(m))); - }, - debug(...msg) { - log.debug(...msg); - }, - error(...msg) { - log.error(...msg.map((m) => chalk.red.bold(m))); - }, - warn(...msg) { - log.info(...msg.map((m) => chalk.yellow.bold(m))); - }, - success(...msg) { - log.info(...msg.map((m) => chalk.green.bold(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), '..'); - -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 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; - } -} - -// 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": 100, - "frames": [ - "✶", - "✵", - "✸", - "✹", - "✺", - "✹", - "✷", - ] - }; - return ora({ text: `${text}\n`, spinner: loadingType }).start(); -} - -const { platform: platform$2 } = process; -const IS_MAC = platform$2 === 'darwin'; -const IS_WIN = platform$2 === 'win32'; -const IS_LINUX = platform$2 === 'linux'; - -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('Rust installed successfully.'); - } - catch (error) { - console.error('Error installing Rust:', error.message); - spinner.fail('Rust installation failed.'); - process.exit(1); - } -} -function checkRustInstalled() { - return shelljs.exec('rustc --version', { silent: true }).code === 0; -} - -class BaseBuilder { - async prepare() { - // Windows and Linux need to install necessary build tools. - if (!IS_MAC) { - logger.info('The first use requires installing system dependencies.'); - logger.info('See more in https://tauri.app/v1/guides/getting-started/prerequisites#installing.'); - } - if (!checkRustInstalled()) { - const res = await prompts({ - type: 'confirm', - message: 'Rust not detected. Install now?', - name: 'value', - }); - if (res.value) { - await installRust(); - } - else { - logger.error('Error: Rust required to package your webapp!'); - process.exit(0); - } - } - const isChina = await isChinaDomain("www.npmjs.com"); - const spinner = getSpinner('Installing package...'); - if (isChina) { - logger.info("Located in China, using npm/rsProxy CN mirror."); - const rustProjectDir = path.join(npmDirectory, 'src-tauri', ".cargo"); - await fsExtra.ensureDir(rustProjectDir); - const projectCnConf = path.join(npmDirectory, "src-tauri", "rust_proxy.toml"); - const projectConf = path.join(rustProjectDir, "config"); - 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('Package installed.'); - } - async runBuildCommand(command = "npm run build") { - const spinner = getSpinner('Building app...'); - setTimeout(() => spinner.stop(), 2000); - await shellExec(`cd "${npmDirectory}" && ${command}`); - } -} - var windows = [ { url: "https://weread.qq.com/", @@ -494,9 +307,9 @@ const platformConfigs = { darwin: MacConf, linux: LinuxConf }; -const { platform: platform$1 } = process; +const { platform: platform$2 } = process; // @ts-ignore -const platformConfig = platformConfigs[platform$1]; +const platformConfig = platformConfigs[platform$2]; let tauriConfig = { tauri: { ...CommonConf.tauri, @@ -507,6 +320,149 @@ let tauriConfig = { 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: `${text}\n`, spinner: loadingType }).start(); +} + +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), '..'); + +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.blue.bold(m))); + }, + debug(...msg) { + log.debug(...msg); + }, + error(...msg) { + log.error(...msg.map((m) => chalk.red.bold(m))); + }, + warn(...msg) { + log.info(...msg.map((m) => chalk.yellow.bold(m))); + }, + success(...msg) { + log.info(...msg.map((m) => chalk.green.bold(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; + } +} + +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('Rust installed successfully.'); + } + catch (error) { + console.error('Error installing Rust:', error.message); + spinner.fail('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, resizable, userAgent, showMenu, showSystemTray, systemTrayIcon, iterCopyFile, identifier, name, } = options; const { platform } = process; @@ -659,80 +615,147 @@ async function mergeConfig(url, options, tauriConf) { await fsExtra.writeJson(configJsonPath, tauriConf2, { spaces: 4 }); } -class MacBuilder extends BaseBuilder { - async build(url, options) { - const { name } = options; - await mergeConfig(url, options, tauriConfig); - let dmgName; - if (options.multiArch) { - await this.runBuildCommand('npm run build:mac'); - dmgName = `${name}_${tauriConfig.package.version}_universal.dmg`; +class BaseBuilder { + constructor(options) { + this.options = options; + } + async prepare() { + if (!IS_MAC) { + logger.info('The first use requires installing system dependencies.'); + logger.info('See more in https://tauri.app/v1/guides/getting-started/prerequisites#installing.'); + } + if (!checkRustInstalled()) { + const res = await prompts({ + type: 'confirm', + message: 'Rust not detected. Install now?', + name: 'value', + }); + if (res.value) { + await installRust(); + } + else { + logger.error('Error: Rust required to package your webapp!'); + process.exit(0); + } + } + const isChina = await isChinaDomain("www.npmjs.com"); + const spinner = getSpinner('Installing package...'); + if (isChina) { + logger.info("Located in China, using npm/rsProxy CN mirror."); + const rustProjectDir = path.join(npmDirectory, 'src-tauri', ".cargo"); + await fsExtra.ensureDir(rustProjectDir); + const projectCnConf = path.join(npmDirectory, "src-tauri", "rust_proxy.toml"); + const projectConf = path.join(rustProjectDir, "config"); + await fsExtra.copy(projectCnConf, projectConf); + await shellExec(`cd "${npmDirectory}" && npm install --registry=https://registry.npmmirror.com`); } else { - await this.runBuildCommand(); - let arch = process.arch === "arm64" ? "aarch64" : process.arch; - dmgName = `${name}_${tauriConfig.package.version}_${arch}.dmg`; + await shellExec(`cd "${npmDirectory}" && npm install`); } - const appPath = this.getBuildAppPath(npmDirectory, dmgName, options.multiArch); - const distPath = path.resolve(`${name}.dmg`); + spinner.succeed('Package installed.'); + } + async buildAndCopy(url) { + const { name } = this.options; + await mergeConfig(url, this.options, tauriConfig); + await this.runBuildCommand(); + const fileName = this.getFileName(); + const appPath = this.getBuildAppPath(npmDirectory, fileName); + const distPath = path.resolve(`${name}.${this.getExtension()}`); await fsExtra.copy(appPath, distPath); await fsExtra.remove(appPath); logger.success('✔ Build success!'); logger.success('✔ App installer located in', distPath); } - getBuildAppPath(npmDirectory, dmgName, multiArch) { - const dmgPath = multiArch ? 'src-tauri/target/universal-apple-darwin/release/bundle/dmg' : 'src-tauri/target/release/bundle/dmg'; - return path.join(npmDirectory, dmgPath, dmgName); + getArch() { + return process.arch === "x64" ? "amd64" : process.arch; + } + getBuildCommand() { + return "npm run build"; + } + runBuildCommand() { + const spinner = getSpinner('Building app...'); + setTimeout(() => spinner.stop(), 3000); + return shellExec(`cd ${npmDirectory} && ${this.getBuildCommand()}`); + } + getBasePath() { + return 'src-tauri/target/release/bundle/'; + } + getBuildAppPath(npmDirectory, fileName) { + return path.join(npmDirectory, this.getBasePath(), this.getExtension().toLowerCase(), `${fileName}.${this.getExtension()}`); + } +} + +class MacBuilder extends BaseBuilder { + constructor(options) { + super(options); + } + async build(url) { + await this.buildAndCopy(url); + } + 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}`; + } + getExtension() { + return "dmg"; + } + 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 { - async build(url, options) { - const { name } = options; - await mergeConfig(url, options, tauriConfig); - await this.runBuildCommand(); - const language = tauriConfig.tauri.bundle.windows.wix.language[0]; - const arch = process.arch; - const msiName = `${name}_${tauriConfig.package.version}_${arch}_${language}.msi`; - const appPath = this.getBuildAppPath(npmDirectory, msiName); - const distPath = path.resolve(`${name}.msi`); - await fsExtra.copy(appPath, distPath); - await fsExtra.remove(appPath); - logger.success('✔ Build success!'); - logger.success('✔ App installer located in', distPath); + constructor(options) { + super(options); } - getBuildAppPath(npmDirectory, msiName) { - return path.join(npmDirectory, 'src-tauri/target/release/bundle/msi', msiName); + async build(url) { + await this.buildAndCopy(url); + } + getFileName() { + const { name } = this.options; + const arch = this.getArch(); + const language = tauriConfig.tauri.bundle.windows.wix.language[0]; + return `${name}_${tauriConfig.package.version}_${arch}_${language}`; + } + getExtension() { + return "msi"; } } class LinuxBuilder extends BaseBuilder { - async build(url, options) { - const { name } = options; - await mergeConfig(url, options, tauriConfig); - await this.runBuildCommand(); - const arch = process.arch === "x64" ? "amd64" : process.arch; - if (options.targets === "deb" || options.targets === "all") { - const debName = `${name}_${tauriConfig.package.version}_${arch}.deb`; - const appPath = this.getBuildAppPath(npmDirectory, "deb", debName); - const distPath = path.resolve(`${name}.deb`); - await fsExtra.copy(appPath, distPath); - await fsExtra.remove(appPath); - logger.success('✔ Build Deb success!'); - logger.success('✔ Deb app installer located in', distPath); - } - if (options.targets === "appimage" || options.targets === "all") { - const appImageName = `${name}_${tauriConfig.package.version}_${arch}.AppImage`; - const appImagePath = this.getBuildAppPath(npmDirectory, "appimage", appImageName); - const distAppPath = path.resolve(`${name}.AppImage`); - await fsExtra.copy(appImagePath, distAppPath); - await fsExtra.remove(appImagePath); - logger.success('✔ Build AppImage success!'); - logger.success('✔ AppImage installer located in', distAppPath); + constructor(options) { + super(options); + } + async build(url) { + const targetTypes = ['deb', 'appimage']; + for (const type of targetTypes) { + if (this.options.targets === type || this.options.targets === "all") { + await this.buildAndCopy(url); + } } } - getBuildAppPath(npmDirectory, packageType, packageName) { - return path.join(npmDirectory, 'src-tauri/target/release/bundle/', packageType, packageName); + getFileName() { + const { name } = this.options; + const arch = this.getArch(); + return `${name}_${tauriConfig.package.version}_${arch}`; + } + getExtension() { + if (this.options.targets === 'appimage') { + return 'AppImage'; + } + return this.options.targets; } } @@ -743,12 +766,12 @@ const buildersMap = { linux: LinuxBuilder, }; class BuilderProvider { - static create() { + static create(options) { const Builder = buildersMap[platform]; if (!Builder) { throw new Error('The current system is not supported!'); } - return new Builder(); + return new Builder(options); } } @@ -929,7 +952,6 @@ program .option('--icon ', 'Application icon', DEFAULT_PAKE_OPTIONS.icon) .option('--height ', 'Window height', validateNumberInput, DEFAULT_PAKE_OPTIONS.height) .option('--width ', 'Window width', validateNumberInput, DEFAULT_PAKE_OPTIONS.width) - .option('--resizable', 'Whether the window can be resizable', DEFAULT_PAKE_OPTIONS.resizable) .option('--fullscreen', 'Start the packaged app in full screen', DEFAULT_PAKE_OPTIONS.fullscreen) .option('--transparent', 'Transparent title bar', DEFAULT_PAKE_OPTIONS.transparent) .option('--user-agent ', 'Custom user agent', DEFAULT_PAKE_OPTIONS.userAgent) @@ -958,8 +980,8 @@ program } const appOptions = await handleOptions(options, url); log.debug('PakeAppOptions', appOptions); - const builder = BuilderProvider.create(); + const builder = BuilderProvider.create(appOptions); await builder.prepare(); - await builder.build(url, appOptions); + await builder.build(url); }); program.parse(); From 2e020a652816fd5061aadfc1a99ca256ecdda267 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Fri, 23 Jun 2023 16:52:35 +0800 Subject: [PATCH 26/40] :art: Remove Resize --- .github/workflows/pake-cli.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/pake-cli.yaml b/.github/workflows/pake-cli.yaml index bba7ce3..0261b44 100644 --- a/.github/workflows/pake-cli.yaml +++ b/.github/workflows/pake-cli.yaml @@ -28,11 +28,6 @@ on: description: "[Height, Optional]" required: false default: "780" - resize: - description: "[Resize, Optional]" - required: false - type: boolean - default: true transparent: description: "[Transparent, Optional, MacOS only]" required: false From aa0964e2c1ac93e52bf29e5ff39d0cf5bfef0c68 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Fri, 23 Jun 2023 18:12:10 +0800 Subject: [PATCH 27/40] :art: Fix the packaging issue for Linux and Windows --- bin/builders/BaseBuilder.ts | 40 ++++++++++---------- bin/builders/LinuxBuilder.ts | 28 +++++++------- bin/builders/MacBuilder.ts | 9 +---- bin/builders/WinBuilder.ts | 11 +----- dist/cli.js | 72 +++++++++++++++++------------------- package.json | 2 +- 6 files changed, 72 insertions(+), 90 deletions(-) diff --git a/bin/builders/BaseBuilder.ts b/bin/builders/BaseBuilder.ts index 5d78aff..e1c7aed 100644 --- a/bin/builders/BaseBuilder.ts +++ b/bin/builders/BaseBuilder.ts @@ -57,50 +57,50 @@ export default abstract class BaseBuilder { spinner.succeed('Package installed.'); } - async buildAndCopy(url: string) { + async build(url: string) { + await this.buildAndCopy(url, this.options.targets); + } + + async buildAndCopy(url: string, target: string) { const { name } = this.options; await mergeConfig(url, this.options, tauriConfig); - await this.runBuildCommand(); + // Build app + const spinner = getSpinner('Building app...'); + setTimeout(() => spinner.stop(), 3000); + await shellExec(`cd ${npmDirectory} && ${this.getBuildCommand()}`); + + // Copy app const fileName = this.getFileName(); - const appPath = this.getBuildAppPath(npmDirectory, fileName); - const distPath = path.resolve(`${name}.${this.getExtension()}`); + 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); } - abstract build(url: string): Promise; + protected getFileType(target: string): string { + return target.toLowerCase(); + } abstract getFileName(): string; - abstract getExtension(): string; - - protected getArch() { - return process.arch === "x64" ? "amd64" : process.arch; - } - protected getBuildCommand(): string { return "npm run build"; } - protected runBuildCommand() { - const spinner = getSpinner('Building app...'); - setTimeout(() => spinner.stop(), 3000); - return shellExec(`cd ${npmDirectory} && ${this.getBuildCommand()}`); - } - protected getBasePath(): string { return 'src-tauri/target/release/bundle/'; } - protected getBuildAppPath(npmDirectory: string, fileName: string): string { + protected getBuildAppPath(npmDirectory: string, fileName: string, fileType: string): string { return path.join( npmDirectory, this.getBasePath(), - this.getExtension().toLowerCase(), - `${fileName}.${this.getExtension()}` + fileType, + `${fileName}.${fileType}` ); } } diff --git a/bin/builders/LinuxBuilder.ts b/bin/builders/LinuxBuilder.ts index ffac65f..25737a4 100644 --- a/bin/builders/LinuxBuilder.ts +++ b/bin/builders/LinuxBuilder.ts @@ -3,29 +3,31 @@ import { PakeAppOptions } from '@/types'; import tauriConfig from '@/helpers/tauriConfig'; export default class LinuxBuilder extends BaseBuilder { + constructor(options: PakeAppOptions) { super(options); } + getFileName(): string { + 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: string) { - const targetTypes = ['deb', 'appimage']; - for (const type of targetTypes) { - if (this.options.targets === type || this.options.targets === "all") { - await this.buildAndCopy(url); + const targetTypes = ["deb", "appimage"]; + for (const target of targetTypes) { + if (this.options.targets === target || this.options.targets === "all") { + await this.buildAndCopy(url, target); } } } - getFileName(): string { - const { name } = this.options; - const arch = this.getArch(); - return `${name}_${tauriConfig.package.version}_${arch}`; - } - - getExtension(): string { - if (this.options.targets === 'appimage') { + protected getFileType(target: string): string { + if (target === 'appimage') { return 'AppImage'; } - return this.options.targets; + return super.getFileType(target); } } diff --git a/bin/builders/MacBuilder.ts b/bin/builders/MacBuilder.ts index 8958787..d0441d8 100644 --- a/bin/builders/MacBuilder.ts +++ b/bin/builders/MacBuilder.ts @@ -5,10 +5,7 @@ import BaseBuilder from './BaseBuilder'; export default class MacBuilder extends BaseBuilder { constructor(options: PakeAppOptions) { super(options); - } - - async build(url: string) { - await this.buildAndCopy(url); + this.options.targets = "dmg"; } getFileName(): string { @@ -22,10 +19,6 @@ export default class MacBuilder extends BaseBuilder { return `${name}_${tauriConfig.package.version}_${arch}`; } - getExtension(): string { - return "dmg"; - } - protected getBuildCommand(): string { return this.options.multiArch ? 'npm run build:mac' : super.getBuildCommand(); } diff --git a/bin/builders/WinBuilder.ts b/bin/builders/WinBuilder.ts index 706dea5..c4ca2a1 100644 --- a/bin/builders/WinBuilder.ts +++ b/bin/builders/WinBuilder.ts @@ -5,20 +5,13 @@ import tauriConfig from '@/helpers/tauriConfig'; export default class WinBuilder extends BaseBuilder { constructor(options: PakeAppOptions) { super(options); - } - - async build(url: string) { - await this.buildAndCopy(url); + this.options.targets = "msi"; } getFileName(): string { const { name } = this.options; - const arch = this.getArch(); + const { arch } = process; const language = tauriConfig.tauri.bundle.windows.wix.language[0]; return `${name}_${tauriConfig.package.version}_${arch}_${language}`; } - - getExtension(): string { - return "msi"; - } } diff --git a/dist/cli.js b/dist/cli.js index 00f186b..e7dfa79 100644 --- a/dist/cli.js +++ b/dist/cli.js @@ -20,7 +20,7 @@ import isUrl from 'is-url'; import fs from 'fs'; var name = "pake-cli"; -var version = "2.1.2"; +var version = "2.1.3"; var description = "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。"; var engines = { node: ">=16.0.0" @@ -654,43 +654,44 @@ class BaseBuilder { } spinner.succeed('Package installed.'); } - async buildAndCopy(url) { + async build(url) { + await this.buildAndCopy(url, this.options.targets); + } + async buildAndCopy(url, target) { const { name } = this.options; await mergeConfig(url, this.options, tauriConfig); - await this.runBuildCommand(); + // Build app + const spinner = getSpinner('Building app...'); + setTimeout(() => spinner.stop(), 3000); + await shellExec(`cd ${npmDirectory} && ${this.getBuildCommand()}`); + // Copy app const fileName = this.getFileName(); - const appPath = this.getBuildAppPath(npmDirectory, fileName); - const distPath = path.resolve(`${name}.${this.getExtension()}`); + 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); } - getArch() { - return process.arch === "x64" ? "amd64" : process.arch; + getFileType(target) { + return target.toLowerCase(); } getBuildCommand() { return "npm run build"; } - runBuildCommand() { - const spinner = getSpinner('Building app...'); - setTimeout(() => spinner.stop(), 3000); - return shellExec(`cd ${npmDirectory} && ${this.getBuildCommand()}`); - } getBasePath() { return 'src-tauri/target/release/bundle/'; } - getBuildAppPath(npmDirectory, fileName) { - return path.join(npmDirectory, this.getBasePath(), this.getExtension().toLowerCase(), `${fileName}.${this.getExtension()}`); + getBuildAppPath(npmDirectory, fileName, fileType) { + return path.join(npmDirectory, this.getBasePath(), fileType, `${fileName}.${fileType}`); } } class MacBuilder extends BaseBuilder { constructor(options) { super(options); - } - async build(url) { - await this.buildAndCopy(url); + this.options.targets = "dmg"; } getFileName() { const { name } = this.options; @@ -703,9 +704,6 @@ class MacBuilder extends BaseBuilder { } return `${name}_${tauriConfig.package.version}_${arch}`; } - getExtension() { - return "dmg"; - } getBuildCommand() { return this.options.multiArch ? 'npm run build:mac' : super.getBuildCommand(); } @@ -719,43 +717,39 @@ class MacBuilder extends BaseBuilder { class WinBuilder extends BaseBuilder { constructor(options) { super(options); - } - async build(url) { - await this.buildAndCopy(url); + this.options.targets = "msi"; } getFileName() { const { name } = this.options; - const arch = this.getArch(); + const { arch } = process; const language = tauriConfig.tauri.bundle.windows.wix.language[0]; return `${name}_${tauriConfig.package.version}_${arch}_${language}`; } - getExtension() { - return "msi"; - } } 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 type of targetTypes) { - if (this.options.targets === type || this.options.targets === "all") { - await this.buildAndCopy(url); + const targetTypes = ["deb", "appimage"]; + for (const target of targetTypes) { + if (this.options.targets === target || this.options.targets === "all") { + await this.buildAndCopy(url, target); } } } - getFileName() { - const { name } = this.options; - const arch = this.getArch(); - return `${name}_${tauriConfig.package.version}_${arch}`; - } - getExtension() { - if (this.options.targets === 'appimage') { + getFileType(target) { + if (target === 'appimage') { return 'AppImage'; } - return this.options.targets; + return super.getFileType(target); } } diff --git a/package.json b/package.json index a65e22f..c4c7839 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pake-cli", - "version": "2.1.2", + "version": "2.1.3", "description": "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。", "engines": { "node": ">=16.0.0" From b3c087d1618749d0a62e8e1d41f17c726a2ebc6d Mon Sep 17 00:00:00 2001 From: Tw93 Date: Fri, 23 Jun 2023 18:14:03 +0800 Subject: [PATCH 28/40] :sparkles: update cli name --- .github/workflows/pake-cli.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pake-cli.yaml b/.github/workflows/pake-cli.yaml index 0261b44..6509aab 100644 --- a/.github/workflows/pake-cli.yaml +++ b/.github/workflows/pake-cli.yaml @@ -1,4 +1,4 @@ -name: Build App with Pake Cli +name: Build App with Pake CLI on: workflow_dispatch: inputs: From 88655005934b5008632d02a6fe8bf0d7fed66a56 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Fri, 23 Jun 2023 19:06:17 +0800 Subject: [PATCH 29/40] :art: Fix the packaging issue for Linux and Windows. --- bin/builders/BaseBuilder.ts | 4 ++-- bin/helpers/merge.ts | 2 +- dist/cli.js | 8 ++++---- package.json | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/bin/builders/BaseBuilder.ts b/bin/builders/BaseBuilder.ts index e1c7aed..fc2bdfe 100644 --- a/bin/builders/BaseBuilder.ts +++ b/bin/builders/BaseBuilder.ts @@ -82,7 +82,7 @@ export default abstract class BaseBuilder { } protected getFileType(target: string): string { - return target.toLowerCase(); + return target; } abstract getFileName(): string; @@ -99,7 +99,7 @@ export default abstract class BaseBuilder { return path.join( npmDirectory, this.getBasePath(), - fileType, + fileType.toLowerCase(), `${fileName}.${fileType}` ); } diff --git a/bin/helpers/merge.ts b/bin/helpers/merge.ts index 195bcb8..7259588 100644 --- a/bin/helpers/merge.ts +++ b/bin/helpers/merge.ts @@ -15,7 +15,6 @@ export async function mergeConfig( height, fullscreen, transparent, - resizable, userAgent, showMenu, showSystemTray, @@ -23,6 +22,7 @@ export async function mergeConfig( iterCopyFile, identifier, name, + resizable = true, } = options; const { platform } = process; diff --git a/dist/cli.js b/dist/cli.js index e7dfa79..067b910 100644 --- a/dist/cli.js +++ b/dist/cli.js @@ -20,7 +20,7 @@ import isUrl from 'is-url'; import fs from 'fs'; var name = "pake-cli"; -var version = "2.1.3"; +var version = "2.1.4"; var description = "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。"; var engines = { node: ">=16.0.0" @@ -464,7 +464,7 @@ function checkRustInstalled() { } async function mergeConfig(url, options, tauriConf) { - const { width, height, fullscreen, transparent, resizable, userAgent, showMenu, showSystemTray, systemTrayIcon, iterCopyFile, identifier, name, } = options; + const { width, height, fullscreen, transparent, userAgent, showMenu, showSystemTray, systemTrayIcon, iterCopyFile, identifier, name, resizable = true, } = options; const { platform } = process; // Set Windows parameters. const tauriConfWindowOptions = { @@ -675,7 +675,7 @@ class BaseBuilder { logger.success('✔ App installer located in', distPath); } getFileType(target) { - return target.toLowerCase(); + return target; } getBuildCommand() { return "npm run build"; @@ -684,7 +684,7 @@ class BaseBuilder { return 'src-tauri/target/release/bundle/'; } getBuildAppPath(npmDirectory, fileName, fileType) { - return path.join(npmDirectory, this.getBasePath(), fileType, `${fileName}.${fileType}`); + return path.join(npmDirectory, this.getBasePath(), fileType.toLowerCase(), `${fileName}.${fileType}`); } } diff --git a/package.json b/package.json index c4c7839..cfa66d6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pake-cli", - "version": "2.1.3", + "version": "2.1.4", "description": "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。", "engines": { "node": ">=16.0.0" From d300ac8a16615846a13fc3af1cc82e3d424bb685 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Fri, 23 Jun 2023 19:16:38 +0800 Subject: [PATCH 30/40] :sparkles: update docs --- bin/README.md | 2 +- bin/README_CN.md | 8 ++++---- bin/helpers/merge.ts | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bin/README.md b/bin/README.md index 47c4cd8..6ba58ec 100644 --- a/bin/README.md +++ b/bin/README.md @@ -93,7 +93,7 @@ Set the width of the application window. Default is `1200px`. #### [transparent] -Enable or disable immersive header. Default is `false`. Use the following command to enable this feature. +Enable or disable immersive header. Default is `false`. Use the following command to enable this feature, macOS only. ```shell --transparent diff --git a/bin/README_CN.md b/bin/README_CN.md index 5ca8152..e28be37 100644 --- a/bin/README_CN.md +++ b/bin/README_CN.md @@ -69,7 +69,7 @@ pake [url] [options] 指定应用程序的图标,支持本地或远程文件。默认使用 Pake 的内置图标。您可以访问 [icon-icons](https://icon-icons.com) 或 [macOSicons](https://macosicons.com/#/) 下载自定义图标。 -- MacOS 要求使用 `.icns` 格式。 +- macOS 要求使用 `.icns` 格式。 - Windows 要求使用 `.ico` 格式。 - Linux 要求使用 `.png` 格式。 @@ -95,7 +95,7 @@ pake [url] [options] #### [transparent] -设置是否启用沉浸式头部,默认为 `false`(不启用)。在 MacOS 上推荐启用此选项。 +设置是否启用沉浸式头部,默认为 `false`(不启用)。当前只对 macOS 上有效。 ```shell --transparent @@ -111,7 +111,7 @@ pake [url] [options] #### [multi-arch] -设置打包结果同时支持 Intel 和 M1 芯片,仅适用于 MacOS,默认为 `false`。 +设置打包结果同时支持 Intel 和 M1 芯片,仅适用于 macOS,默认为 `false`。 ##### 准备工作 @@ -153,7 +153,7 @@ Linux,默认为 `all`。 #### [show-menu] -设置是否显示菜单栏,默认不显示。在 MacOS 上推荐启用此选项。 +设置是否显示菜单栏,默认不显示。在 macOS 上推荐启用此选项。 ```shell --show-menu diff --git a/bin/helpers/merge.ts b/bin/helpers/merge.ts index 7259588..91f240c 100644 --- a/bin/helpers/merge.ts +++ b/bin/helpers/merge.ts @@ -121,7 +121,7 @@ export async function mergeConfig( fileExt: '.icns', path: `icons/${name.toLowerCase()}.icns`, defaultIcon: 'icons/icon.icns', - message: 'MacOS icon must be .icns type.', + message: 'macOS icon must be .icns type.', }, }; const iconInfo = platformIconMap[platform]; From baf59a330010e719db0309ff1b91b5e27bd07205 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Sat, 24 Jun 2023 00:01:44 +0800 Subject: [PATCH 31/40] :sparkles: CLI more beautiful. --- bin/builders/BaseBuilder.ts | 11 ++++--- bin/helpers/merge.ts | 20 ++++++------ bin/helpers/rust.ts | 5 +-- bin/options/icon.ts | 12 ++++--- bin/options/index.ts | 6 ++-- bin/options/logger.ts | 8 ++--- bin/utils/info.ts | 8 +++-- dist/cli.js | 63 +++++++++++++++++++------------------ 8 files changed, 69 insertions(+), 64 deletions(-) diff --git a/bin/builders/BaseBuilder.ts b/bin/builders/BaseBuilder.ts index fc2bdfe..5070195 100644 --- a/bin/builders/BaseBuilder.ts +++ b/bin/builders/BaseBuilder.ts @@ -1,5 +1,6 @@ import path from 'path'; import fsExtra from "fs-extra"; +import chalk from "chalk"; import prompts from 'prompts'; import { PakeAppOptions } from '@/types'; @@ -22,8 +23,8 @@ export default abstract class BaseBuilder { async prepare() { if (!IS_MAC) { - logger.info('The first use requires installing system dependencies.'); - logger.info('See more in https://tauri.app/v1/guides/getting-started/prerequisites#installing.'); + logger.info('⚙︎ The first use requires installing system dependencies.'); + logger.info('⚙︎ See more in https://tauri.app/v1/guides/getting-started/prerequisites.'); } if (!checkRustInstalled()) { @@ -36,7 +37,7 @@ export default abstract class BaseBuilder { if (res.value) { await installRust(); } else { - logger.error('Error: Rust required to package your webapp!'); + logger.error('✕ Rust required to package your webapp.'); process.exit(0); } } @@ -44,7 +45,7 @@ export default abstract class BaseBuilder { const isChina = await isChinaDomain("www.npmjs.com"); const spinner = getSpinner('Installing package...'); if (isChina) { - logger.info("Located in China, using npm/rsProxy CN mirror."); + logger.info("⚙︎ Located in China, using npm/rsProxy CN mirror."); const rustProjectDir = path.join(npmDirectory, 'src-tauri', ".cargo"); await fsExtra.ensureDir(rustProjectDir); const projectCnConf = path.join(npmDirectory, "src-tauri", "rust_proxy.toml"); @@ -54,7 +55,7 @@ export default abstract class BaseBuilder { } else { await shellExec(`cd "${npmDirectory}" && npm install`); } - spinner.succeed('Package installed.'); + spinner.succeed(chalk.green('Package installed!')); } async build(url: string) { diff --git a/bin/helpers/merge.ts b/bin/helpers/merge.ts index 91f240c..5004fb2 100644 --- a/bin/helpers/merge.ts +++ b/bin/helpers/merge.ts @@ -43,7 +43,7 @@ export async function mergeConfig( //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.'); + logger.warn('✼ Your input might be a local file.'); tauriConf.pake.windows[0].url_type = 'local'; const fileName = path.basename(url); @@ -98,7 +98,7 @@ export async function mergeConfig( 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.`, + `✼ The target must be one of ${validTargets.join(', ')}, the default 'deb' will be used.`, ); } } @@ -132,7 +132,7 @@ export async function mergeConfig( if (customIconExt !== iconInfo.fileExt) { updateIconPath = false; - logger.warn(`${iconInfo.message}, but you give ${customIconExt}`); + logger.warn(`✼ ${iconInfo.message}, but you give ${customIconExt}`); tauriConf.tauri.bundle.icon = [iconInfo.defaultIcon]; } else { const iconPath = path.join(npmDirectory, 'src-tauri/', iconInfo.path); @@ -143,12 +143,10 @@ export async function mergeConfig( if (updateIconPath) { tauriConf.tauri.bundle.icon = [options.icon]; } else { - logger.warn(`Icon will remain as default.`); + logger.warn(`✼ Icon will remain as default.`); } } else { - logger.warn( - 'Custom icon path may be invalid. Default icon will be used instead.', - ); + logger.warn('✼ Custom icon path may be invalid, default icon will be used instead.'); tauriConf.tauri.bundle.icon = [iconInfo.defaultIcon]; } @@ -168,13 +166,13 @@ export async function mergeConfig( await fsExtra.copy(systemTrayIcon, trayIcoPath); } else { logger.warn( - `System tray icon must be .ico or .png, but you provided ${iconExt}.`, + `✼ System tray icon must be .ico or .png, but you provided ${iconExt}.`, ); - logger.warn(`Default system tray icon will be used.`); + logger.warn(`✼ Default system tray icon will be used.`); } } catch { - logger.warn(`${systemTrayIcon} not exists!`); - logger.warn(`Default system tray icon will remain unchanged.`); + logger.warn(`✼ ${systemTrayIcon} not exists!`); + logger.warn(`✼ Default system tray icon will remain unchanged.`); } } diff --git a/bin/helpers/rust.ts b/bin/helpers/rust.ts index 4da648d..c15d91f 100644 --- a/bin/helpers/rust.ts +++ b/bin/helpers/rust.ts @@ -1,3 +1,4 @@ +import chalk from "chalk"; import shelljs from 'shelljs'; import { getSpinner } from "@/utils/info"; @@ -16,10 +17,10 @@ export async function installRust() { try { await shellExec(IS_WIN ? rustInstallScriptForWindows : rustInstallScriptForMac); - spinner.succeed('Rust installed successfully.'); + spinner.succeed(chalk.green('Rust installed successfully!')); } catch (error) { console.error('Error installing Rust:', error.message); - spinner.fail('Rust installation failed.'); + spinner.fail(chalk.red('Rust installation failed!')); process.exit(1); } } diff --git a/bin/options/icon.ts b/bin/options/icon.ts index b465da7..0b6951a 100644 --- a/bin/options/icon.ts +++ b/bin/options/icon.ts @@ -1,13 +1,15 @@ -import { getSpinner } from "@/utils/info"; import path from 'path'; import axios from 'axios'; import fsExtra from "fs-extra"; +import chalk from 'chalk'; import { dir } from 'tmp-promise'; -import { fileTypeFromBuffer } from 'file-type'; + import logger from './logger'; import { npmDirectory } from '@/utils/dir'; import { IS_LINUX, IS_WIN } from '@/utils/platform'; +import { getSpinner } from "@/utils/info"; +import { fileTypeFromBuffer } from 'file-type'; import { PakeAppOptions } from '@/types'; export async function handleIcon(options: PakeAppOptions) { @@ -18,7 +20,7 @@ export async function handleIcon(options: PakeAppOptions) { return path.resolve(options.icon); } } else { - logger.info('No app icon provided, default icon used. Use --icon option to assign an icon.'); + 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); } @@ -42,10 +44,10 @@ export async function downloadIcon(iconUrl: string) { const { path: tempPath } = await dir(); const iconPath = `${tempPath}/icon.${fileDetails.ext}`; await fsExtra.outputFile(iconPath, iconData); - spinner.succeed('Icon downloaded successfully.'); + spinner.succeed(chalk.green('Icon downloaded successfully!')); return iconPath; } catch (error) { - spinner.fail('Icon download failed.'); + spinner.fail(chalk.red('Icon download failed!')); if (error.response && error.response.status === 404) { return null; } diff --git a/bin/options/index.ts b/bin/options/index.ts index a2e157a..a8adad2 100644 --- a/bin/options/index.ts +++ b/bin/options/index.ts @@ -34,13 +34,13 @@ export default async function handleOptions(options: PakeCliOptions, url: string } if (!isValidName(name, platform)) { - const LINUX_NAME_ERROR = `Package name is invalid. It 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 = `Package name is invalid. It should only include letters and numbers, and must contain at least one letter. Examples: 123pan, 123Pan, Pan123, weread, WeRead, WERead.`; + 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}`); + logger.warn(`✼ Inside github actions, use the default name: ${name}`); } else { process.exit(1); } diff --git a/bin/options/logger.ts b/bin/options/logger.ts index 5d6b893..6039488 100644 --- a/bin/options/logger.ts +++ b/bin/options/logger.ts @@ -3,19 +3,19 @@ import log from 'loglevel'; const logger = { info(...msg: any[]) { - log.info(...msg.map((m) => chalk.blue.bold(m))); + log.info(...msg.map((m) => chalk.white(m))); }, debug(...msg: any[]) { log.debug(...msg); }, error(...msg: any[]) { - log.error(...msg.map((m) => chalk.red.bold(m))); + log.error(...msg.map((m) => chalk.red(m))); }, warn(...msg: any[]) { - log.info(...msg.map((m) => chalk.yellow.bold(m))); + log.info(...msg.map((m) => chalk.yellow(m))); }, success(...msg: any[]) { - log.info(...msg.map((m) => chalk.green.bold(m))); + log.info(...msg.map((m) => chalk.green(m))); } }; diff --git a/bin/utils/info.ts b/bin/utils/info.ts index 68a1122..21f7992 100644 --- a/bin/utils/info.ts +++ b/bin/utils/info.ts @@ -1,6 +1,7 @@ import crypto from 'crypto'; import prompts from "prompts"; import ora from "ora"; +import chalk from 'chalk'; // Generates an identifier based on the given URL. export function getIdentifier(url: string) { @@ -29,14 +30,15 @@ export function getSpinner(text: string) { const loadingType = { "interval": 80, "frames": [ + "✦", "✶", + "✺", "✵", "✸", + "✴︎", "✹", "✺", - "✹", - "✷", ] } - return ora({ text: `${text}\n`, spinner: loadingType }).start(); + return ora({ text: `${chalk.blue(text)}\n`, spinner: loadingType, color: 'blue' }).start(); } diff --git a/dist/cli.js b/dist/cli.js index 067b910..f790083 100644 --- a/dist/cli.js +++ b/dist/cli.js @@ -342,18 +342,19 @@ function capitalizeFirstLetter(string) { } function getSpinner(text) { const loadingType = { - "interval": 80, + "interval": 100, "frames": [ + "✦", "✶", + "✺", "✵", "✸", + "✴︎", "✹", "✺", - "✹", - "✷", ] }; - return ora({ text: `${text}\n`, spinner: loadingType }).start(); + return ora({ text: `${chalk.blue(text)}\n`, spinner: loadingType, color: 'blue' }).start(); } const { platform: platform$1 } = process; @@ -381,19 +382,19 @@ function shellExec(command) { const logger = { info(...msg) { - log.info(...msg.map((m) => chalk.blue.bold(m))); + log.info(...msg.map((m) => chalk.white(m))); }, debug(...msg) { log.debug(...msg); }, error(...msg) { - log.error(...msg.map((m) => chalk.red.bold(m))); + log.error(...msg.map((m) => chalk.red(m))); }, warn(...msg) { - log.info(...msg.map((m) => chalk.yellow.bold(m))); + log.info(...msg.map((m) => chalk.yellow(m))); }, success(...msg) { - log.info(...msg.map((m) => chalk.green.bold(m))); + log.info(...msg.map((m) => chalk.green(m))); } }; @@ -451,11 +452,11 @@ async function installRust() { const spinner = getSpinner('Downloading Rust...'); try { await shellExec(IS_WIN ? rustInstallScriptForWindows : rustInstallScriptForMac); - spinner.succeed('Rust installed successfully.'); + spinner.succeed(chalk.green('Rust installed successfully!')); } catch (error) { console.error('Error installing Rust:', error.message); - spinner.fail('Rust installation failed.'); + spinner.fail(chalk.red('Rust installation failed!')); process.exit(1); } } @@ -480,7 +481,7 @@ async function mergeConfig(url, options, tauriConf) { //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.'); + 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); @@ -524,7 +525,7 @@ async function mergeConfig(url, options, tauriConf) { 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.`); + logger.warn(`✼ The target must be one of ${validTargets.join(', ')}, the default 'deb' will be used.`); } } // Set icon. @@ -545,7 +546,7 @@ async function mergeConfig(url, options, tauriConf) { fileExt: '.icns', path: `icons/${name.toLowerCase()}.icns`, defaultIcon: 'icons/icon.icns', - message: 'MacOS icon must be .icns type.', + message: 'macOS icon must be .icns type.', }, }; const iconInfo = platformIconMap[platform]; @@ -555,7 +556,7 @@ async function mergeConfig(url, options, tauriConf) { let customIconExt = path.extname(options.icon).toLowerCase(); if (customIconExt !== iconInfo.fileExt) { updateIconPath = false; - logger.warn(`${iconInfo.message}, but you give ${customIconExt}`); + logger.warn(`✼ ${iconInfo.message}, but you give ${customIconExt}`); tauriConf.tauri.bundle.icon = [iconInfo.defaultIcon]; } else { @@ -567,11 +568,11 @@ async function mergeConfig(url, options, tauriConf) { tauriConf.tauri.bundle.icon = [options.icon]; } else { - logger.warn(`Icon will remain as default.`); + logger.warn(`✼ Icon will remain as default.`); } } else { - logger.warn('Custom icon path may be invalid. Default icon will be used instead.'); + logger.warn('✼ Custom icon path may be invalid, default icon will be used instead.'); tauriConf.tauri.bundle.icon = [iconInfo.defaultIcon]; } // Set tray icon path. @@ -587,13 +588,13 @@ async function mergeConfig(url, options, tauriConf) { 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.`); + 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.`); + logger.warn(`✼ ${systemTrayIcon} not exists!`); + logger.warn(`✼ Default system tray icon will remain unchanged.`); } } tauriConf.tauri.systemTray.iconPath = trayIconPath; @@ -621,8 +622,8 @@ class BaseBuilder { } async prepare() { if (!IS_MAC) { - logger.info('The first use requires installing system dependencies.'); - logger.info('See more in https://tauri.app/v1/guides/getting-started/prerequisites#installing.'); + logger.info('⚙︎ The first use requires installing system dependencies.'); + logger.info('⚙︎ See more in https://tauri.app/v1/guides/getting-started/prerequisites.'); } if (!checkRustInstalled()) { const res = await prompts({ @@ -634,14 +635,14 @@ class BaseBuilder { await installRust(); } else { - logger.error('Error: Rust required to package your webapp!'); + logger.error('✕ Rust required to package your webapp.'); process.exit(0); } } const isChina = await isChinaDomain("www.npmjs.com"); const spinner = getSpinner('Installing package...'); if (isChina) { - logger.info("Located in China, using npm/rsProxy CN mirror."); + logger.info("⚙︎ Located in China, using npm/rsProxy CN mirror."); const rustProjectDir = path.join(npmDirectory, 'src-tauri', ".cargo"); await fsExtra.ensureDir(rustProjectDir); const projectCnConf = path.join(npmDirectory, "src-tauri", "rust_proxy.toml"); @@ -652,7 +653,7 @@ class BaseBuilder { else { await shellExec(`cd "${npmDirectory}" && npm install`); } - spinner.succeed('Package installed.'); + spinner.succeed(chalk.green('Package installed!')); } async build(url) { await this.buildAndCopy(url, this.options.targets); @@ -800,7 +801,7 @@ async function handleIcon(options) { } } else { - logger.info('No app icon provided, default icon used. Use --icon option to assign an icon.'); + 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); } @@ -820,11 +821,11 @@ async function downloadIcon(iconUrl) { const { path: tempPath } = await dir(); const iconPath = `${tempPath}/icon.${fileDetails.ext}`; await fsExtra.outputFile(iconPath, iconData); - spinner.succeed('Icon downloaded successfully.'); + spinner.succeed(chalk.green('Icon downloaded successfully!')); return iconPath; } catch (error) { - spinner.fail('Icon download failed.'); + spinner.fail(chalk.red('Icon download failed!')); if (error.response && error.response.status === 404) { return null; } @@ -895,13 +896,13 @@ async function handleOptions(options, url) { name = namePrompt || defaultName; } if (!isValidName(name, platform)) { - const LINUX_NAME_ERROR = `Package name is invalid. It 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 = `Package name is invalid. It should only include letters and numbers, and must contain at least one letter. Examples: 123pan, 123Pan, Pan123, weread, WeRead, WERead.`; + const LINUX_NAME_ERROR = `✕ Package name is invalid. It 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 = `✕ Package name is invalid. It 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}`); + logger.warn(`✼ Inside github actions, use the default name: ${name}`); } else { process.exit(1); From 3b8235e279f9ddf18a0b0fcb9e2cb26d156011ad Mon Sep 17 00:00:00 2001 From: Tw93 Date: Sat, 24 Jun 2023 00:02:52 +0800 Subject: [PATCH 32/40] :bookmark: 2.1.5 --- dist/cli.js | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dist/cli.js b/dist/cli.js index f790083..315ba07 100644 --- a/dist/cli.js +++ b/dist/cli.js @@ -20,7 +20,7 @@ import isUrl from 'is-url'; import fs from 'fs'; var name = "pake-cli"; -var version = "2.1.4"; +var version = "2.1.5"; var description = "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。"; var engines = { node: ">=16.0.0" @@ -342,7 +342,7 @@ function capitalizeFirstLetter(string) { } function getSpinner(text) { const loadingType = { - "interval": 100, + "interval": 80, "frames": [ "✦", "✶", @@ -896,8 +896,8 @@ async function handleOptions(options, url) { name = namePrompt || defaultName; } if (!isValidName(name, platform)) { - const LINUX_NAME_ERROR = `✕ Package name is invalid. It 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 = `✕ Package name is invalid. It should only include letters and numbers, and must contain at least one letter. Examples: 123pan, 123Pan, Pan123, weread, WeRead, WERead.`; + 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) { diff --git a/package.json b/package.json index cfa66d6..0098e99 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pake-cli", - "version": "2.1.4", + "version": "2.1.5", "description": "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。", "engines": { "node": ">=16.0.0" From 9f9a6b6e75a2d602e8cd40bc312e0f2034be86fb Mon Sep 17 00:00:00 2001 From: Tw93 Date: Sat, 24 Jun 2023 00:06:46 +0800 Subject: [PATCH 33/40] :wrench: Configuration specification --- .editorconfig | 1 - CODE_OF_CONDUCT.md | 1 + CONTRIBUTING.md | 4 ---- cli.js | 1 + icns2png.py | 3 --- 5 files changed, 2 insertions(+), 8 deletions(-) diff --git a/.editorconfig b/.editorconfig index 259fb8e..927b794 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,3 @@ -# http://editorconfig.org root = true [*] diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index a771bbb..2ac9ed7 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -126,3 +126,4 @@ enforcement ladder](https://github.com/mozilla/diversity). For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4c58c4d..a75ccba 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,10 +17,6 @@ graph LR - `master` is the release branch, we will make tag and publish version on this branch. - If it is a document modification, it can be submitted to this branch. -## Commit Log - -please use - ## More It is a good habit to create a feature request issue to discuss whether the feature is necessary before you implement it. However, it's unnecessary to create an issue to claim that you found a typo or improved the readability of documentation, just create a pull request. diff --git a/cli.js b/cli.js index d17768a..92115ec 100755 --- a/cli.js +++ b/cli.js @@ -1,2 +1,3 @@ #!/usr/bin/env node import './dist/cli.js'; + diff --git a/icns2png.py b/icns2png.py index cf9a998..6c749fa 100644 --- a/icns2png.py +++ b/icns2png.py @@ -10,7 +10,6 @@ except ImportError: os.system("pip install Pillow") from PIL import Image - if __name__ == "__main__": now_dir = os.path.dirname(os.path.abspath(__file__)) icons_dir = os.path.join(now_dir, "src-tauri", "icons") @@ -34,5 +33,3 @@ if __name__ == "__main__": image_32.save(image_32_path, "ICO") print("png file write success.") print(f"There are {len(os.listdir(png_dir))} png picture in ", png_dir) - - From bc9a03cde9062f4cc84373fcab3c99d9b9e9406d Mon Sep 17 00:00:00 2001 From: Tw93 Date: Sat, 24 Jun 2023 00:07:43 +0800 Subject: [PATCH 34/40] :wrench: Configuration specification --- .ecrc.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.ecrc.json b/.ecrc.json index 4da6169..d456439 100644 --- a/.ecrc.json +++ b/.ecrc.json @@ -1,3 +1,4 @@ { "Exclude": ["Cargo\\.lock$", "dist", "*\\.md"] } + From f4f80f3fc2b159a392708cfa0ca3ced5e789e2fb Mon Sep 17 00:00:00 2001 From: Tw93 Date: Sat, 24 Jun 2023 00:08:27 +0800 Subject: [PATCH 35/40] :wrench: Configuration specification --- .ecrc.json | 1 - CODE_OF_CONDUCT.md | 1 - cli.js | 1 - 3 files changed, 3 deletions(-) diff --git a/.ecrc.json b/.ecrc.json index d456439..4da6169 100644 --- a/.ecrc.json +++ b/.ecrc.json @@ -1,4 +1,3 @@ { "Exclude": ["Cargo\\.lock$", "dist", "*\\.md"] } - diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 2ac9ed7..a771bbb 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -126,4 +126,3 @@ enforcement ladder](https://github.com/mozilla/diversity). For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. - diff --git a/cli.js b/cli.js index 92115ec..d17768a 100755 --- a/cli.js +++ b/cli.js @@ -1,3 +1,2 @@ #!/usr/bin/env node import './dist/cli.js'; - From 07938f02f42eab0267bd110f6804141c41d9ecfc Mon Sep 17 00:00:00 2001 From: Tw93 Date: Sat, 24 Jun 2023 08:58:26 +0800 Subject: [PATCH 36/40] :bookmark: 2.1.6 CLI --- bin/cli.ts | 10 +++++----- dist/cli.js | 12 ++++++------ package.json | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/bin/cli.ts b/bin/cli.ts index ccdc4e6..9b7a143 100644 --- a/bin/cli.ts +++ b/bin/cli.ts @@ -19,17 +19,17 @@ program .argument('[url]', 'The web URL you want to package', validateUrlInput) .option('--name ', 'Application name') .option('--icon ', 'Application icon', DEFAULT.icon) - .option('--height ', 'Window height', validateNumberInput, DEFAULT.height) .option('--width ', 'Window width', validateNumberInput, DEFAULT.width) - .option('--fullscreen', 'Start the packaged app in full screen', DEFAULT.fullscreen) - .option('--transparent', 'Transparent title bar', DEFAULT.transparent) + .option('--height ', 'Window height', validateNumberInput, DEFAULT.height) + .option('--transparent', 'Only for Mac, hide title bar', DEFAULT.transparent) + .option('--fullscreen', 'Start in full screen', DEFAULT.fullscreen) .option('--user-agent ', 'Custom user agent', DEFAULT.userAgent) .option('--show-menu', 'Show menu in app', DEFAULT.showMenu) .option('--show-system-tray', 'Show system tray in app', DEFAULT.showSystemTray) .option('--system-tray-icon ', 'Custom system tray icon', DEFAULT.systemTrayIcon) - .option('--iter-copy-file', 'Copy files to app when URL is a local file', DEFAULT.iterCopyFile) + .option('--iter-copy-file', 'Copy files when URL is a local file', DEFAULT.iterCopyFile) .option('--multi-arch', 'Only for Mac, supports both Intel and M1', DEFAULT.multiArch) - .option('--targets ', 'Only for Linux, option "deb", "appimage" or "all"', DEFAULT.targets) + .option('--targets ', 'Only for Linux, option "deb" or "appimage"', DEFAULT.targets) .option('--debug', 'Debug mode', DEFAULT.debug) .version(packageJson.version, '-v, --version', 'Output the current version') .action(async (url: string, options: PakeCliOptions) => { diff --git a/dist/cli.js b/dist/cli.js index 315ba07..692f9af 100644 --- a/dist/cli.js +++ b/dist/cli.js @@ -20,7 +20,7 @@ import isUrl from 'is-url'; import fs from 'fs'; var name = "pake-cli"; -var version = "2.1.5"; +var version = "2.1.6"; var description = "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。"; var engines = { node: ">=16.0.0" @@ -945,17 +945,17 @@ program .argument('[url]', 'The web URL you want to package', validateUrlInput) .option('--name ', 'Application name') .option('--icon ', 'Application icon', DEFAULT_PAKE_OPTIONS.icon) - .option('--height ', 'Window height', validateNumberInput, DEFAULT_PAKE_OPTIONS.height) .option('--width ', 'Window width', validateNumberInput, DEFAULT_PAKE_OPTIONS.width) - .option('--fullscreen', 'Start the packaged app in full screen', DEFAULT_PAKE_OPTIONS.fullscreen) - .option('--transparent', 'Transparent title bar', DEFAULT_PAKE_OPTIONS.transparent) + .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 to app when URL is a local file', DEFAULT_PAKE_OPTIONS.iterCopyFile) + .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", "appimage" or "all"', DEFAULT_PAKE_OPTIONS.targets) + .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) => { diff --git a/package.json b/package.json index 0098e99..25a8089 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pake-cli", - "version": "2.1.5", + "version": "2.1.6", "description": "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。", "engines": { "node": ">=16.0.0" From 00c01a9638a9f21c009a79ee679acec3d6935be1 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Sat, 24 Jun 2023 20:17:52 +0800 Subject: [PATCH 37/40] :art: Unified code style --- .prettierrc.json | 15 ++++++++++++++ bin/builders/BaseBuilder.ts | 33 +++++++++++++------------------ bin/builders/LinuxBuilder.ts | 7 +++---- bin/builders/MacBuilder.ts | 4 ++-- bin/builders/WinBuilder.ts | 2 +- bin/cli.ts | 5 ++--- bin/helpers/merge.ts | 19 ++++++------------ bin/helpers/rust.ts | 6 +++--- bin/helpers/tauriConfig.ts | 6 +++--- bin/options/icon.ts | 11 +++++++---- bin/options/index.ts | 11 +++++++---- bin/options/logger.ts | 10 +++++----- bin/types.ts | 1 - bin/utils/dir.ts | 5 +---- bin/utils/info.ts | 30 +++++++++++----------------- bin/utils/ip.ts | 5 ++--- bin/utils/shell.ts | 6 +++--- bin/utils/url.ts | 3 +-- src-tauri/src/inject/component.js | 9 ++++----- src-tauri/src/inject/event.js | 22 ++++++++++----------- src-tauri/src/inject/style.js | 2 +- src-tauri/tauri.conf.json | 8 ++------ src-tauri/tauri.linux.conf.json | 4 +++- 23 files changed, 107 insertions(+), 117 deletions(-) create mode 100644 .prettierrc.json diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..9b8066c --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,15 @@ +{ + "arrowParens": "avoid", + "bracketSpacing": true, + "endOfLine": "lf", + "bracketSameLine": false, + "jsxSingleQuote": false, + "printWidth": 120, + "proseWrap": "preserve", + "quoteProps": "as-needed", + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "all", + "useTabs": false +} diff --git a/bin/builders/BaseBuilder.ts b/bin/builders/BaseBuilder.ts index 5070195..66adbbf 100644 --- a/bin/builders/BaseBuilder.ts +++ b/bin/builders/BaseBuilder.ts @@ -1,17 +1,17 @@ import path from 'path'; -import fsExtra from "fs-extra"; -import chalk from "chalk"; +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 { mergeConfig } from '@/helpers/merge'; import tauriConfig from '@/helpers/tauriConfig'; import { npmDirectory } from '@/utils/dir'; -import { getSpinner } from "@/utils/info"; +import { getSpinner } from '@/utils/info'; import { shellExec } from '@/utils/shell'; import { isChinaDomain } from '@/utils/ip'; -import { IS_MAC } from "@/utils/platform"; +import { IS_MAC } from '@/utils/platform'; import logger from '@/options/logger'; export default abstract class BaseBuilder { @@ -23,8 +23,8 @@ export default abstract class BaseBuilder { async prepare() { if (!IS_MAC) { - logger.info('⚙︎ The first use requires installing system dependencies.'); - logger.info('⚙︎ See more in https://tauri.app/v1/guides/getting-started/prerequisites.'); + logger.info('✺ The first use requires installing system dependencies.'); + logger.info('✺ See more in https://tauri.app/v1/guides/getting-started/prerequisites.'); } if (!checkRustInstalled()) { @@ -42,14 +42,14 @@ export default abstract class BaseBuilder { } } - const isChina = await isChinaDomain("www.npmjs.com"); + const isChina = await isChinaDomain('www.npmjs.com'); const spinner = getSpinner('Installing package...'); if (isChina) { - logger.info("⚙︎ Located in China, using npm/rsProxy CN mirror."); - const rustProjectDir = path.join(npmDirectory, 'src-tauri', ".cargo"); + logger.info('✺ Located in China, using npm/rsProxy CN mirror.'); + const rustProjectDir = path.join(npmDirectory, 'src-tauri', '.cargo'); await fsExtra.ensureDir(rustProjectDir); - const projectCnConf = path.join(npmDirectory, "src-tauri", "rust_proxy.toml"); - const projectConf = path.join(rustProjectDir, "config"); + const projectCnConf = path.join(npmDirectory, 'src-tauri', 'rust_proxy.toml'); + const projectConf = path.join(rustProjectDir, 'config'); await fsExtra.copy(projectCnConf, projectConf); await shellExec(`cd "${npmDirectory}" && npm install --registry=https://registry.npmmirror.com`); } else { @@ -89,7 +89,7 @@ export default abstract class BaseBuilder { abstract getFileName(): string; protected getBuildCommand(): string { - return "npm run build"; + return 'npm run build'; } protected getBasePath(): string { @@ -97,11 +97,6 @@ export default abstract class BaseBuilder { } protected getBuildAppPath(npmDirectory: string, fileName: string, fileType: string): string { - return path.join( - npmDirectory, - this.getBasePath(), - fileType.toLowerCase(), - `${fileName}.${fileType}` - ); + return path.join(npmDirectory, this.getBasePath(), fileType.toLowerCase(), `${fileName}.${fileType}`); } } diff --git a/bin/builders/LinuxBuilder.ts b/bin/builders/LinuxBuilder.ts index 25737a4..93ce82a 100644 --- a/bin/builders/LinuxBuilder.ts +++ b/bin/builders/LinuxBuilder.ts @@ -3,22 +3,21 @@ import { PakeAppOptions } from '@/types'; import tauriConfig from '@/helpers/tauriConfig'; export default class LinuxBuilder extends BaseBuilder { - constructor(options: PakeAppOptions) { super(options); } getFileName(): string { const { name } = this.options; - const arch = process.arch === "x64" ? "amd64" : process.arch; + 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: string) { - const targetTypes = ["deb", "appimage"]; + const targetTypes = ['deb', 'appimage']; for (const target of targetTypes) { - if (this.options.targets === target || this.options.targets === "all") { + if (this.options.targets === target || this.options.targets === 'all') { await this.buildAndCopy(url, target); } } diff --git a/bin/builders/MacBuilder.ts b/bin/builders/MacBuilder.ts index d0441d8..28d124d 100644 --- a/bin/builders/MacBuilder.ts +++ b/bin/builders/MacBuilder.ts @@ -5,7 +5,7 @@ import BaseBuilder from './BaseBuilder'; export default class MacBuilder extends BaseBuilder { constructor(options: PakeAppOptions) { super(options); - this.options.targets = "dmg"; + this.options.targets = 'dmg'; } getFileName(): string { @@ -14,7 +14,7 @@ export default class MacBuilder extends BaseBuilder { if (this.options.multiArch) { arch = 'universal'; } else { - arch = process.arch === "arm64" ? "aarch64" : process.arch; + arch = process.arch === 'arm64' ? 'aarch64' : process.arch; } return `${name}_${tauriConfig.package.version}_${arch}`; } diff --git a/bin/builders/WinBuilder.ts b/bin/builders/WinBuilder.ts index c4ca2a1..61c687a 100644 --- a/bin/builders/WinBuilder.ts +++ b/bin/builders/WinBuilder.ts @@ -5,7 +5,7 @@ import tauriConfig from '@/helpers/tauriConfig'; export default class WinBuilder extends BaseBuilder { constructor(options: PakeAppOptions) { super(options); - this.options.targets = "msi"; + this.options.targets = 'msi'; } getFileName(): string { diff --git a/bin/cli.ts b/bin/cli.ts index 9b7a143..d501856 100644 --- a/bin/cli.ts +++ b/bin/cli.ts @@ -33,14 +33,13 @@ program .option('--debug', 'Debug mode', DEFAULT.debug) .version(packageJson.version, '-v, --version', 'Output the current version') .action(async (url: string, options: PakeCliOptions) => { - await checkUpdateTips(); if (!url) { - program.outputHelp((str) => { + program.outputHelp(str => { return str .split('\n') - .filter((line) => !/((-h,|--help)|((-v|-V),|--version))\s+.+$/.test(line)) + .filter(line => !/((-h,|--help)|((-v|-V),|--version))\s+.+$/.test(line)) .join('\n'); }); process.exit(0); diff --git a/bin/helpers/merge.ts b/bin/helpers/merge.ts index 5004fb2..d4095a3 100644 --- a/bin/helpers/merge.ts +++ b/bin/helpers/merge.ts @@ -5,11 +5,7 @@ import { npmDirectory } from '@/utils/dir'; import logger from '@/options/logger'; import { PakeAppOptions, PlatformMap } from '@/types'; -export async function mergeConfig( - url: string, - options: PakeAppOptions, - tauriConf: any, -) { +export async function mergeConfig(url: string, options: PakeAppOptions, tauriConf: any) { const { width, height, @@ -61,7 +57,7 @@ export async function mergeConfig( const filesToCopyBack = ['cli.js', 'about_pake.html']; await Promise.all( - filesToCopyBack.map((file) => + filesToCopyBack.map(file => fsExtra.copy(path.join(distBakDir, file), path.join(distDir, file)), ), ); @@ -72,8 +68,7 @@ export async function mergeConfig( } 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; + tauriConf.tauri.security.dangerousRemoteDomainIpcAccess[0].domain = new URL(url).hostname; } const platformMap: PlatformMap = { @@ -95,7 +90,8 @@ export async function mergeConfig( 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]; + 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.`, @@ -165,9 +161,7 @@ export async function mergeConfig( 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(`✼ System tray icon must be .ico or .png, but you provided ${iconExt}.`); logger.warn(`✼ Default system tray icon will be used.`); } } catch { @@ -192,7 +186,6 @@ export async function mergeConfig( 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; diff --git a/bin/helpers/rust.ts b/bin/helpers/rust.ts index c15d91f..bf53087 100644 --- a/bin/helpers/rust.ts +++ b/bin/helpers/rust.ts @@ -1,13 +1,13 @@ -import chalk from "chalk"; +import chalk from 'chalk'; import shelljs from 'shelljs'; -import { getSpinner } from "@/utils/info"; +import { getSpinner } from '@/utils/info'; import { IS_WIN } from '@/utils/platform'; import { shellExec } from '@/utils/shell'; import { isChinaDomain } from '@/utils/ip'; export async function installRust() { - const isInChina = await isChinaDomain("sh.rustup.rs"); + 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"; diff --git a/bin/helpers/tauriConfig.ts b/bin/helpers/tauriConfig.ts index 998dff7..32c3b83 100644 --- a/bin/helpers/tauriConfig.ts +++ b/bin/helpers/tauriConfig.ts @@ -7,10 +7,10 @@ import LinuxConf from '../../src-tauri/tauri.linux.conf.json'; const platformConfigs = { win32: WinConf, darwin: MacConf, - linux: LinuxConf + linux: LinuxConf, }; -const {platform} = process; +const { platform } = process; // @ts-ignore const platformConfig = platformConfigs[platform]; @@ -21,7 +21,7 @@ let tauriConfig = { }, package: CommonConf.package, build: CommonConf.build, - pake: pakeConf + pake: pakeConf, }; export default tauriConfig; diff --git a/bin/options/icon.ts b/bin/options/icon.ts index 0b6951a..6ee54e6 100644 --- a/bin/options/icon.ts +++ b/bin/options/icon.ts @@ -1,14 +1,13 @@ import path from 'path'; import axios from 'axios'; -import fsExtra from "fs-extra"; +import fsExtra from 'fs-extra'; import chalk from 'chalk'; import { dir } from 'tmp-promise'; - import logger from './logger'; import { npmDirectory } from '@/utils/dir'; import { IS_LINUX, IS_WIN } from '@/utils/platform'; -import { getSpinner } from "@/utils/info"; +import { getSpinner } from '@/utils/info'; import { fileTypeFromBuffer } from 'file-type'; import { PakeAppOptions } from '@/types'; @@ -21,7 +20,11 @@ export async function handleIcon(options: PakeAppOptions) { } } 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'; + 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); } } diff --git a/bin/options/index.ts b/bin/options/index.ts index a8adad2..17f41d9 100644 --- a/bin/options/index.ts +++ b/bin/options/index.ts @@ -1,5 +1,5 @@ -import fsExtra from "fs-extra"; -import logger from "@/options/logger"; +import fsExtra from 'fs-extra'; +import logger from '@/options/logger'; import { handleIcon } from './icon'; import { getDomain } from '@/utils/url'; @@ -20,14 +20,17 @@ function isValidName(name: string, platform: NodeJS.Platform): boolean { return !!name && reg.test(name); } -export default async function handleOptions(options: PakeCliOptions, url: string): Promise { +export default async function handleOptions( + options: PakeCliOptions, + url: string, +): Promise { 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 defaultName = pathExists ? '' : resolveAppName(url, platform); const promptMessage = 'Enter your application name'; const namePrompt = await promptText(promptMessage, defaultName); name = namePrompt || defaultName; diff --git a/bin/options/logger.ts b/bin/options/logger.ts index 6039488..0461d4c 100644 --- a/bin/options/logger.ts +++ b/bin/options/logger.ts @@ -3,20 +3,20 @@ import log from 'loglevel'; const logger = { info(...msg: any[]) { - log.info(...msg.map((m) => chalk.white(m))); + log.info(...msg.map(m => chalk.white(m))); }, debug(...msg: any[]) { log.debug(...msg); }, error(...msg: any[]) { - log.error(...msg.map((m) => chalk.red(m))); + log.error(...msg.map(m => chalk.red(m))); }, warn(...msg: any[]) { - log.info(...msg.map((m) => chalk.yellow(m))); + log.info(...msg.map(m => chalk.yellow(m))); }, success(...msg: any[]) { - log.info(...msg.map((m) => chalk.green(m))); - } + log.info(...msg.map(m => chalk.green(m))); + }, }; export default logger; diff --git a/bin/types.ts b/bin/types.ts index 02739b9..836d9bb 100644 --- a/bin/types.ts +++ b/bin/types.ts @@ -1,4 +1,3 @@ - export interface PlatformMap { [key: string]: any; } diff --git a/bin/utils/dir.ts b/bin/utils/dir.ts index bfc4f29..cb5a697 100644 --- a/bin/utils/dir.ts +++ b/bin/utils/dir.ts @@ -5,7 +5,4 @@ import { fileURLToPath } from 'url'; const currentModulePath = fileURLToPath(import.meta.url); // Resolve the parent directory of the current module -export const npmDirectory = path.join( - path.dirname(currentModulePath), - '..' -); +export const npmDirectory = path.join(path.dirname(currentModulePath), '..'); diff --git a/bin/utils/info.ts b/bin/utils/info.ts index 21f7992..a08a726 100644 --- a/bin/utils/info.ts +++ b/bin/utils/info.ts @@ -1,14 +1,11 @@ import crypto from 'crypto'; -import prompts from "prompts"; -import ora from "ora"; +import prompts from 'prompts'; +import ora from 'ora'; import chalk from 'chalk'; // Generates an identifier based on the given URL. export function getIdentifier(url: string) { - const postFixHash = crypto.createHash('md5') - .update(url) - .digest('hex') - .substring(0, 6); + const postFixHash = crypto.createHash('md5').update(url).digest('hex').substring(0, 6); return `pake-${postFixHash}`; } @@ -28,17 +25,12 @@ export function capitalizeFirstLetter(string: string) { export function getSpinner(text: string) { const loadingType = { - "interval": 80, - "frames": [ - "✦", - "✶", - "✺", - "✵", - "✸", - "✴︎", - "✹", - "✺", - ] - } - return ora({ text: `${chalk.blue(text)}\n`, spinner: loadingType, color: 'blue' }).start(); + interval: 80, + frames: ['✦', '✶', '✺', '✵', '✸', '✹', '✺'], + }; + return ora({ + text: `${chalk.cyan(text)}\n`, + spinner: loadingType, + color: 'cyan', + }).start(); } diff --git a/bin/utils/ip.ts b/bin/utils/ip.ts index 588e1e7..c3b6434 100644 --- a/bin/utils/ip.ts +++ b/bin/utils/ip.ts @@ -13,13 +13,13 @@ const ping = async (host: string) => { // Prevent timeouts from affecting user experience. const requestPromise = new Promise((resolve, reject) => { - const req = http.get(`http://${ip.address}`, (res) => { + const req = http.get(`http://${ip.address}`, res => { const delay = new Date().getTime() - start.getTime(); res.resume(); resolve(delay); }); - req.on('error', (err) => { + req.on('error', err => { reject(err); }); }); @@ -33,7 +33,6 @@ const ping = async (host: string) => { return Promise.race([requestPromise, timeoutPromise]); }; - async function isChinaDomain(domain: string): Promise { try { const [ip] = await resolve(domain); diff --git a/bin/utils/shell.ts b/bin/utils/shell.ts index 684dc14..5463075 100644 --- a/bin/utils/shell.ts +++ b/bin/utils/shell.ts @@ -1,9 +1,9 @@ -import shelljs from "shelljs"; -import { npmDirectory } from "./dir"; +import shelljs from 'shelljs'; +import { npmDirectory } from './dir'; export function shellExec(command: string) { return new Promise((resolve, reject) => { - shelljs.exec(command, { async: true, silent: false, cwd: npmDirectory }, (code) => { + shelljs.exec(command, { async: true, silent: false, cwd: npmDirectory }, code => { if (code === 0) { resolve(0); } else { diff --git a/bin/utils/url.ts b/bin/utils/url.ts index 51050df..6c19ff3 100644 --- a/bin/utils/url.ts +++ b/bin/utils/url.ts @@ -9,7 +9,7 @@ export function getDomain(inputUrl: string): string | null { const parsed = psl.parse(url.hostname); // If domain is available, split it and return the SLD. - if ("domain" in parsed && parsed.domain) { + if ('domain' in parsed && parsed.domain) { return parsed.domain.split('.')[0]; } else { return null; @@ -19,7 +19,6 @@ export function getDomain(inputUrl: string): string | null { } } - // Appends 'https://' protocol to the URL if not present. export function appendProtocol(inputUrl: string): string { try { diff --git a/src-tauri/src/inject/component.js b/src-tauri/src/inject/component.js index 31ee045..f77a975 100644 --- a/src-tauri/src/inject/component.js +++ b/src-tauri/src/inject/component.js @@ -136,8 +136,7 @@ document.addEventListener('DOMContentLoaded', () => { document.body.appendChild(m); setTimeout(function () { const d = 0.5; - m.style.transition = - 'transform ' + d + 's ease-in, opacity ' + d + 's ease-in'; + m.style.transition = 'transform ' + d + 's ease-in, opacity ' + d + 's ease-in'; m.style.opacity = '0'; setTimeout(function () { document.body.removeChild(m); @@ -151,14 +150,14 @@ document.addEventListener('DOMContentLoaded', () => { if (window.location.hostname === 'chat.openai.com') { const originFetch = fetch; window.fetch = (url, options) => { - return originFetch(url, options).then(async (response) => { + return originFetch(url, options).then(async response => { if (url.indexOf('/backend-api/models') === -1) { return response; } const responseClone = response.clone(); let res = await responseClone.json(); - res.models = res.models.map((m) => { - m.tags = m.tags.filter((t) => { + res.models = res.models.map(m => { + m.tags = m.tags.filter(t => { return t !== 'mobile'; }); if (m.slug === 'gpt-4-mobile') { diff --git a/src-tauri/src/inject/event.js b/src-tauri/src/inject/event.js index 7a66cc7..75460a9 100644 --- a/src-tauri/src/inject/event.js +++ b/src-tauri/src/inject/event.js @@ -82,14 +82,14 @@ async function invoke(cmd, args) { // Judgment of file download. function isDownloadLink(url) { - const fileExtensions = [ - '3gp', '7z', 'ai', 'apk', 'avi', 'bmp', 'csv', 'dmg', 'doc', 'docx', 'fla', 'flv', 'gif', 'gz', 'gzip', - 'ico', 'iso', 'indd', 'jar', 'jpeg', 'jpg', 'm3u8', 'mov', 'mp3', 'mp4', 'mpa', 'mpg', - 'mpeg', 'msi', 'odt', 'ogg', 'ogv', 'pdf', 'png', 'ppt', 'pptx', 'psd', 'rar', 'raw', 'rss', 'svg', - 'swf', 'tar', 'tif', 'tiff', 'ts', 'txt', 'wav', 'webm', 'webp', 'wma', 'wmv', 'xls', 'xlsx', 'xml', 'zip' - ]; - const downloadLinkPattern = new RegExp(`\\.(${fileExtensions.join('|')})$`, 'i'); - return downloadLinkPattern.test(url); + const fileExtensions = [ + '3gp', '7z', 'ai', 'apk', 'avi', 'bmp', 'csv', 'dmg', 'doc', 'docx', 'fla', 'flv', 'gif', 'gz', 'gzip', + 'ico', 'iso', 'indd', 'jar', 'jpeg', 'jpg', 'm3u8', 'mov', 'mp3', 'mp4', 'mpa', 'mpg', + 'mpeg', 'msi', 'odt', 'ogg', 'ogv', 'pdf', 'png', 'ppt', 'pptx', 'psd', 'rar', 'raw', 'rss', 'svg', + 'swf', 'tar', 'tif', 'tiff', 'ts', 'txt', 'wav', 'webm', 'webp', 'wma', 'wmv', 'xls', 'xlsx', 'xml', 'zip', + ]; + const downloadLinkPattern = new RegExp(`\\.(${fileExtensions.join('|')})$`, 'i'); + return downloadLinkPattern.test(url); } // No need to go to the download link. @@ -185,7 +185,7 @@ document.addEventListener('DOMContentLoaded', () => { // Rewrite the window.open function. const originalWindowOpen = window.open; - window.open = function (url, name, specs) { + window.open = function(url, name, specs) { // Apple login and google login if (name === 'AppleAuthentication') { //do nothing @@ -278,7 +278,7 @@ function downloadFromBlobUrl(blobUrl, filename) { function detectDownloadByCreateAnchor() { const createEle = document.createElement; document.createElement = (el) => { - if (el !== "a") return createEle.call(document, el); + if (el !== 'a') return createEle.call(document, el); const anchorEle = createEle.call(document, el); // use addEventListener to avoid overriding the original click event. @@ -290,5 +290,5 @@ function detectDownloadByCreateAnchor() { }); return anchorEle; - } + }; } diff --git a/src-tauri/src/inject/style.js b/src-tauri/src/inject/style.js index a9ecd2d..03730c3 100644 --- a/src-tauri/src/inject/style.js +++ b/src-tauri/src/inject/style.js @@ -1,4 +1,4 @@ -window.addEventListener('DOMContentLoaded', (_event) => { +window.addEventListener('DOMContentLoaded', _event => { const css = ` #page #footer-wrapper, .drawing-board .toolbar .toolbar-action, diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index c3cfd2d..2661614 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -9,9 +9,7 @@ "dangerousRemoteDomainIpcAccess": [ { "domain": "weread.qq.com", - "windows": [ - "pake" - ], + "windows": ["pake"], "enableTauriAPI": true } ] @@ -27,9 +25,7 @@ "all": true, "fs": { "all": true, - "scope": [ - "$DOWNLOAD/*" - ] + "scope": ["$DOWNLOAD/*"] } } }, diff --git a/src-tauri/tauri.linux.conf.json b/src-tauri/tauri.linux.conf.json index e0993fb..d9db8ea 100644 --- a/src-tauri/tauri.linux.conf.json +++ b/src-tauri/tauri.linux.conf.json @@ -8,7 +8,9 @@ "copyright": "", "deb": { "depends": ["curl", "wget"], - "files": {"/usr/share/applications/com-tw93-weread.desktop": "assets/com-tw93-weread.desktop"} + "files": { + "/usr/share/applications/com-tw93-weread.desktop": "assets/com-tw93-weread.desktop" + } }, "externalBin": [], "longDescription": "", From 974a1f45de4c8d4774d77a02d3179dcfa5fb8c4a Mon Sep 17 00:00:00 2001 From: Tw93 Date: Sat, 24 Jun 2023 20:20:34 +0800 Subject: [PATCH 38/40] :bookmark: 2.1.7 --- dist/cli.js | 98 +++++++++++++++++++++++++--------------------------- package.json | 2 +- 2 files changed, 48 insertions(+), 52 deletions(-) diff --git a/dist/cli.js b/dist/cli.js index 692f9af..8c7d9d9 100644 --- a/dist/cli.js +++ b/dist/cli.js @@ -20,7 +20,7 @@ import isUrl from 'is-url'; import fs from 'fs'; var name = "pake-cli"; -var version = "2.1.6"; +var version = "2.1.7"; var description = "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。"; var engines = { node: ">=16.0.0" @@ -305,7 +305,7 @@ var LinuxConf = { const platformConfigs = { win32: WinConf, darwin: MacConf, - linux: LinuxConf + linux: LinuxConf, }; const { platform: platform$2 } = process; // @ts-ignore @@ -317,15 +317,12 @@ let tauriConfig = { }, package: CommonConf.package, build: CommonConf.build, - pake: pakeConf + 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); + const postFixHash = crypto.createHash('md5').update(url).digest('hex').substring(0, 6); return `pake-${postFixHash}`; } async function promptText(message, initial) { @@ -342,19 +339,14 @@ function capitalizeFirstLetter(string) { } function getSpinner(text) { const loadingType = { - "interval": 80, - "frames": [ - "✦", - "✶", - "✺", - "✵", - "✸", - "✴︎", - "✹", - "✺", - ] + interval: 80, + frames: ['✦', '✶', '✺', '✵', '✸', '✹', '✺'], }; - return ora({ text: `${chalk.blue(text)}\n`, spinner: loadingType, color: 'blue' }).start(); + return ora({ + text: `${chalk.cyan(text)}\n`, + spinner: loadingType, + color: 'cyan', + }).start(); } const { platform: platform$1 } = process; @@ -369,7 +361,7 @@ const npmDirectory = path.join(path.dirname(currentModulePath), '..'); function shellExec(command) { return new Promise((resolve, reject) => { - shelljs.exec(command, { async: true, silent: false, cwd: npmDirectory }, (code) => { + shelljs.exec(command, { async: true, silent: false, cwd: npmDirectory }, code => { if (code === 0) { resolve(0); } @@ -382,20 +374,20 @@ function shellExec(command) { const logger = { info(...msg) { - log.info(...msg.map((m) => chalk.white(m))); + log.info(...msg.map(m => chalk.white(m))); }, debug(...msg) { log.debug(...msg); }, error(...msg) { - log.error(...msg.map((m) => chalk.red(m))); + log.error(...msg.map(m => chalk.red(m))); }, warn(...msg) { - log.info(...msg.map((m) => chalk.yellow(m))); + log.info(...msg.map(m => chalk.yellow(m))); }, success(...msg) { - log.info(...msg.map((m) => chalk.green(m))); - } + log.info(...msg.map(m => chalk.green(m))); + }, }; const resolve = promisify(dns.resolve); @@ -405,12 +397,12 @@ const ping = async (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 req = http.get(`http://${ip.address}`, res => { const delay = new Date().getTime() - start.getTime(); res.resume(); resolve(delay); }); - req.on('error', (err) => { + req.on('error', err => { reject(err); }); }); @@ -444,7 +436,7 @@ async function isChinaIP(ip, domain) { } async function installRust() { - const isInChina = await isChinaDomain("sh.rustup.rs"); + 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"; @@ -495,7 +487,7 @@ async function mergeConfig(url, options, tauriConf) { 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)))); + 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'; @@ -503,8 +495,7 @@ async function mergeConfig(url, options, tauriConf) { 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; + tauriConf.tauri.security.dangerousRemoteDomainIpcAccess[0].domain = new URL(url).hostname; } const platformMap = { win32: 'windows', @@ -522,7 +513,8 @@ async function mergeConfig(url, options, tauriConf) { 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]; + 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.`); @@ -622,8 +614,8 @@ class BaseBuilder { } async prepare() { if (!IS_MAC) { - logger.info('⚙︎ The first use requires installing system dependencies.'); - logger.info('⚙︎ See more in https://tauri.app/v1/guides/getting-started/prerequisites.'); + logger.info('✺ The first use requires installing system dependencies.'); + logger.info('✺ See more in https://tauri.app/v1/guides/getting-started/prerequisites.'); } if (!checkRustInstalled()) { const res = await prompts({ @@ -639,14 +631,14 @@ class BaseBuilder { process.exit(0); } } - const isChina = await isChinaDomain("www.npmjs.com"); + const isChina = await isChinaDomain('www.npmjs.com'); const spinner = getSpinner('Installing package...'); if (isChina) { - logger.info("⚙︎ Located in China, using npm/rsProxy CN mirror."); - const rustProjectDir = path.join(npmDirectory, 'src-tauri', ".cargo"); + logger.info('✺ Located in China, using npm/rsProxy CN mirror.'); + const rustProjectDir = path.join(npmDirectory, 'src-tauri', '.cargo'); await fsExtra.ensureDir(rustProjectDir); - const projectCnConf = path.join(npmDirectory, "src-tauri", "rust_proxy.toml"); - const projectConf = path.join(rustProjectDir, "config"); + const projectCnConf = path.join(npmDirectory, 'src-tauri', 'rust_proxy.toml'); + const projectConf = path.join(rustProjectDir, 'config'); await fsExtra.copy(projectCnConf, projectConf); await shellExec(`cd "${npmDirectory}" && npm install --registry=https://registry.npmmirror.com`); } @@ -679,7 +671,7 @@ class BaseBuilder { return target; } getBuildCommand() { - return "npm run build"; + return 'npm run build'; } getBasePath() { return 'src-tauri/target/release/bundle/'; @@ -692,7 +684,7 @@ class BaseBuilder { class MacBuilder extends BaseBuilder { constructor(options) { super(options); - this.options.targets = "dmg"; + this.options.targets = 'dmg'; } getFileName() { const { name } = this.options; @@ -701,7 +693,7 @@ class MacBuilder extends BaseBuilder { arch = 'universal'; } else { - arch = process.arch === "arm64" ? "aarch64" : process.arch; + arch = process.arch === 'arm64' ? 'aarch64' : process.arch; } return `${name}_${tauriConfig.package.version}_${arch}`; } @@ -718,7 +710,7 @@ class MacBuilder extends BaseBuilder { class WinBuilder extends BaseBuilder { constructor(options) { super(options); - this.options.targets = "msi"; + this.options.targets = 'msi'; } getFileName() { const { name } = this.options; @@ -734,14 +726,14 @@ class LinuxBuilder extends BaseBuilder { } getFileName() { const { name } = this.options; - const arch = process.arch === "x64" ? "amd64" : process.arch; + 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"]; + const targetTypes = ['deb', 'appimage']; for (const target of targetTypes) { - if (this.options.targets === target || this.options.targets === "all") { + if (this.options.targets === target || this.options.targets === 'all') { await this.buildAndCopy(url, target); } } @@ -802,7 +794,11 @@ async function handleIcon(options) { } 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'; + 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); } } @@ -840,7 +836,7 @@ function getDomain(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) { + if ('domain' in parsed && parsed.domain) { return parsed.domain.split('.')[0]; } else { @@ -890,7 +886,7 @@ async function handleOptions(options, url) { let name = options.name; const pathExists = await fsExtra.pathExists(url); if (!options.name) { - const defaultName = pathExists ? "" : resolveAppName(url, platform); + const defaultName = pathExists ? '' : resolveAppName(url, platform); const promptMessage = 'Enter your application name'; const namePrompt = await promptText(promptMessage, defaultName); name = namePrompt || defaultName; @@ -961,10 +957,10 @@ program .action(async (url, options) => { await checkUpdateTips(); if (!url) { - program.outputHelp((str) => { + program.outputHelp(str => { return str .split('\n') - .filter((line) => !/((-h,|--help)|((-v|-V),|--version))\s+.+$/.test(line)) + .filter(line => !/((-h,|--help)|((-v|-V),|--version))\s+.+$/.test(line)) .join('\n'); }); process.exit(0); diff --git a/package.json b/package.json index 25a8089..6616bfe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pake-cli", - "version": "2.1.6", + "version": "2.1.7", "description": "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。", "engines": { "node": ">=16.0.0" From 088e14ca29b931f4cbf667d920b9b0e101ca13f6 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Sat, 24 Jun 2023 21:24:31 +0800 Subject: [PATCH 39/40] :sparkles: First-time installation prompt --- bin/builders/BaseBuilder.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/bin/builders/BaseBuilder.ts b/bin/builders/BaseBuilder.ts index 66adbbf..619181c 100644 --- a/bin/builders/BaseBuilder.ts +++ b/bin/builders/BaseBuilder.ts @@ -22,7 +22,11 @@ export default abstract class BaseBuilder { } async prepare() { - if (!IS_MAC) { + const tauriSrcPath = path.join(npmDirectory, 'src-tauri'); + const tauriTargetPath = path.join(tauriSrcPath, 'target'); + const tauriTargetPathExists = await fsExtra.pathExists(tauriTargetPath); + + if (!IS_MAC && !tauriTargetPathExists) { logger.info('✺ The first use requires installing system dependencies.'); logger.info('✺ See more in https://tauri.app/v1/guides/getting-started/prerequisites.'); } @@ -44,20 +48,25 @@ export default abstract class BaseBuilder { 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 rustProjectDir = path.join(npmDirectory, 'src-tauri', '.cargo'); - await fsExtra.ensureDir(rustProjectDir); - const projectCnConf = path.join(npmDirectory, 'src-tauri', 'rust_proxy.toml'); - const projectConf = path.join(rustProjectDir, 'config'); + 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: string) { await this.buildAndCopy(url, this.options.targets); } From e3df6dae886b89e4bcbde30eca2b892bda8efe53 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Sat, 24 Jun 2023 21:24:53 +0800 Subject: [PATCH 40/40] :art: Update detection time --- bin/helpers/updater.ts | 2 +- dist/cli.js | 20 +++++++++++++------- package.json | 2 +- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/bin/helpers/updater.ts b/bin/helpers/updater.ts index b9e74e2..549e989 100644 --- a/bin/helpers/updater.ts +++ b/bin/helpers/updater.ts @@ -2,5 +2,5 @@ import updateNotifier from 'update-notifier'; import packageJson from '../../package.json'; export async function checkUpdateTips() { - updateNotifier({ pkg: packageJson }).notify(); + updateNotifier({ pkg: packageJson, updateCheckInterval: 1000 * 60 }).notify(); } diff --git a/dist/cli.js b/dist/cli.js index 8c7d9d9..34ac94d 100644 --- a/dist/cli.js +++ b/dist/cli.js @@ -20,7 +20,7 @@ import isUrl from 'is-url'; import fs from 'fs'; var name = "pake-cli"; -var version = "2.1.7"; +var version = "2.1.8"; var description = "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。"; var engines = { node: ">=16.0.0" @@ -613,7 +613,10 @@ class BaseBuilder { this.options = options; } async prepare() { - if (!IS_MAC) { + const tauriSrcPath = path.join(npmDirectory, 'src-tauri'); + const tauriTargetPath = path.join(tauriSrcPath, 'target'); + const tauriTargetPathExists = await fsExtra.pathExists(tauriTargetPath); + if (!IS_MAC && !tauriTargetPathExists) { logger.info('✺ The first use requires installing system dependencies.'); logger.info('✺ See more in https://tauri.app/v1/guides/getting-started/prerequisites.'); } @@ -633,12 +636,12 @@ class BaseBuilder { } 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 rustProjectDir = path.join(npmDirectory, 'src-tauri', '.cargo'); - await fsExtra.ensureDir(rustProjectDir); - const projectCnConf = path.join(npmDirectory, 'src-tauri', 'rust_proxy.toml'); - const projectConf = path.join(rustProjectDir, 'config'); + const projectCnConf = path.join(tauriSrcPath, 'rust_proxy.toml'); await fsExtra.copy(projectCnConf, projectConf); await shellExec(`cd "${npmDirectory}" && npm install --registry=https://registry.npmmirror.com`); } @@ -646,6 +649,9 @@ class BaseBuilder { 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); @@ -780,7 +786,7 @@ const DEFAULT_PAKE_OPTIONS = { }; async function checkUpdateTips() { - updateNotifier({ pkg: packageJson }).notify(); + updateNotifier({ pkg: packageJson, updateCheckInterval: 1000 * 60 }).notify(); } async function handleIcon(options) { diff --git a/package.json b/package.json index 6616bfe..de6933a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pake-cli", - "version": "2.1.7", + "version": "2.1.8", "description": "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。", "engines": { "node": ">=16.0.0"