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 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/**/*"
+ ]
}