diff --git a/.editorconfig b/.editorconfig index 259fb8e..927b794 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,3 @@ -# http://editorconfig.org root = true [*] diff --git a/.github/workflows/pake-cli.yaml b/.github/workflows/pake-cli.yaml index 18a7697..6509aab 100644 --- a/.github/workflows/pake-cli.yaml +++ b/.github/workflows/pake-cli.yaml @@ -1,4 +1,4 @@ -name: Build App with Pake Cli +name: Build App with Pake CLI on: workflow_dispatch: inputs: @@ -20,29 +20,19 @@ on: icon: description: "[Icon, Optional]" required: false - height: - description: "[Height, Optional]" - required: false - default: "780" width: description: "[Width, Optional]" required: false default: "1200" + height: + description: "[Height, Optional]" + required: false + default: "780" transparent: - description: "[Transparent, Optional]" + description: "[Transparent, Optional, MacOS only]" required: false type: boolean default: false - # fullscreen: - # description: "[FullScreen, Optional]" - # required: false - # type: boolean - # default: false - resize: - description: "[Resize, Optional]" - required: false - type: boolean - default: true multi_arch: description: "[MultiArch, Optional, MacOS only]" required: false 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/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..9b8066c --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,15 @@ +{ + "arrowParens": "avoid", + "bracketSpacing": true, + "endOfLine": "lf", + "bracketSameLine": false, + "jsxSingleQuote": false, + "printWidth": 120, + "proseWrap": "preserve", + "quoteProps": "as-needed", + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "all", + "useTabs": false +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4c58c4d..a75ccba 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,10 +17,6 @@ graph LR - `master` is the release branch, we will make tag and publish version on this branch. - If it is a document modification, it can be submitted to this branch. -## Commit Log - -please use - ## More It is a good habit to create a feature request issue to discuss whether the feature is necessary before you implement it. However, it's unnecessary to create an issue to claim that you found a typo or improved the readability of documentation, just create a pull request. diff --git a/README.md b/README.md index 11bfabf..2a88fc1 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ - + @@ -159,7 +159,7 @@ In addition, double-click the title bar to switch to full-screen mode. For Mac u ![Pake](https://gw.alipayobjects.com/zos/k/zd/pake.gif) -**Pake provides a command line tool, making the flow of package customization quicker and easier. See [documentation](./bin/README_EN.md) for more information.** +**Pake provides a command line tool, making the flow of package customization quicker and easier. See [documentation](./bin/README_CN.md) for more information.** ```bash # Install with npm @@ -194,7 +194,7 @@ npm run build ## Advanced Usage 1. You can refer to the [codebase structure](https://github.com/tw93/Pake/wiki/Description-of-Pake's-code-structure) before working on Pake, which will help you much in development. -2. Modify the `url` and `productName` fields in the `pake.json` file under the src-tauri directory, as well as the `icon` and `identifier` fields in the `tauri.xxx.conf.json` file. You can select a `icon` from the `icons` directory or download one from [macOSicons](https://macosicons.com/#/) to match your product needs. +2. Modify the `url` and `productName` fields in the `pake.json` file under the src-tauri directory, the "domain" field in the `tauri.config.json` file needs to be modified synchronously, as well as the `icon` and `identifier` fields in the `tauri.xxx.conf.json` file. You can select a `icon` from the `icons` directory or download one from [macOSicons](https://macosicons.com/#/) to match your product needs. 3. For configurations on window properties, you can modify the `pake.json` file to change the value of `width`, `height`, `fullscreen` (or not), `resizable` (or not) of the `windows` property. To adapt to the immersive header on Mac, change `transparent` to `true`, look for the `Header` element, and add the `padding-top` property. 4. For advanced usages such as style rewriting, advertisement removal, JS injection, container message communication, and user-defined shortcut keys, see [Advanced Usage of Pake](https://github.com/tw93/Pake/wiki/Advanced-Usage-of-Pake). @@ -269,6 +269,13 @@ Pake's development can not be without these Hackers. They contributed a lot of c Pake Actions + + + jeasonnow +
+ Santree +
+ QingZ11 @@ -283,13 +290,6 @@ Pake's development can not be without these Hackers. They contributed a lot of c 孟世博 - - - jeasonnow -
- Santree -
- 2nthony diff --git a/README_CN.md b/README_CN.md index 146beba..406a777 100644 --- a/README_CN.md +++ b/README_CN.md @@ -57,7 +57,7 @@ - + @@ -162,7 +162,7 @@

-**Pake 提供了命令行工具,可以更快捷方便地一键自定义打你需要的包,详细可见 [文档](./bin/README.md)。** +**Pake 提供了命令行工具,可以更快捷方便地一键自定义打你需要的包,详细可见 [文档](./bin/README_CN.md)。** ```bash # 使用 npm 进行安装 @@ -196,7 +196,7 @@ npm run build ## 高级使用 1. 代码结构可参考 [文档](https://github.com/tw93/Pake/wiki/Pake-%E7%9A%84%E4%BB%A3%E7%A0%81%E7%BB%93%E6%9E%84%E8%AF%B4%E6%98%8E),便于你在开发前了解更多。 -2. 修改 src-tauri 目录下 `pake.json` 中的 `url` 和 `productName` 字段,以及 `tauri.xxx.conf.json` 中的 `icon` 和 `identifier` 字段,其中 `icon` 可以从 icons 目录选择一个,也可以去 [macOSicons](https://macosicons.com/#/) 下载符合效果的。 +2. 修改 src-tauri 目录下 `pake.json` 中的 `url` 和 `productName` 字段,需同步修改下 `tauri.config.json` 中的 `domain` 字段,以及 `tauri.xxx.conf.json` 中的 `icon` 和 `identifier` 字段,其中 `icon` 可以从 icons 目录选择一个,也可以去 [macOSicons](https://macosicons.com/#/) 下载符合效果的。 3. 关于窗口属性设置,可以在 `pake.json` 修改 windows 属性对应的 `width/height`,fullscreen 是否全屏,resizable 是否可以调整大小,假如想适配 Mac 沉浸式头部,可以将 transparent 设置成 `true`,找到 Header 元素加一个 padding-top 样式即可,不想适配改成 `false` 也行。 4. 此外样式改写、屏蔽广告、逻辑代码注入、容器消息通信、自定义快捷键可见 [高级用法](https://github.com/tw93/Pake/wiki/Pake-%E7%9A%84%E9%AB%98%E7%BA%A7%E7%94%A8%E6%B3%95)。 @@ -249,19 +249,26 @@ Pake 的发展离不开这些 Hacker 们,一起贡献了大量能力,也欢 Essesoul
+ + + AielloChan +
+ Aiello +
+ + m1911star
Horus
- - + - - AielloChan + + Pake-Actions
- Aiello + Pake Actions
@@ -271,6 +278,20 @@ Pake 的发展离不开这些 Hacker 们,一起贡献了大量能力,也欢 Steam + + + exposir +
+ 孟世博 +
+ + + + jeasonnow +
+ Santree +
+ 2nthony @@ -278,6 +299,14 @@ Pake 的发展离不开这些 Hacker 们,一起贡献了大量能力,也欢 2nthony + + + ACGNnsj +
+ Null +
+ + nekomeowww @@ -305,8 +334,7 @@ Pake 的发展离不开这些 Hacker 们,一起贡献了大量能力,也欢
Po Chen
- - + houhoz @@ -314,13 +342,21 @@ Pake 的发展离不开这些 Hacker 们,一起贡献了大量能力,也欢 Hyzhao + + + lakca +
+ Null +
+ liusishan
Liusishan
- + + piaoyidage @@ -333,7 +369,7 @@ Pake 的发展离不开这些 Hacker 们,一起贡献了大量能力,也欢 ## 支持 -1. 我有两只猫,一只叫汤圆,一只叫可乐,假如觉得 Pake 让你生活更美好,可以给汤圆可乐 喂罐头 🥩。 +1. 我有两只猫,一只叫汤圆,一只叫可乐,假如 Pake 让你生活更美好,可以给汤圆可乐 喂罐头 🥩。 2. 如果你喜欢 Pake,可以在 Github Star,更欢迎 [推荐](https://twitter.com/intent/tweet?url=https://github.com/tw93/Pake&text=%23Pake%20%E4%B8%80%E4%B8%AA%E5%BE%88%E7%AE%80%E5%8D%95%E7%9A%84%E7%94%A8%20Rust%20%E6%89%93%E5%8C%85%E7%BD%91%E9%A1%B5%E7%94%9F%E6%88%90%20Mac%20App%20%E7%9A%84%E5%B7%A5%E5%85%B7%EF%BC%8C%E7%9B%B8%E6%AF%94%E4%BC%A0%E7%BB%9F%E7%9A%84%20Electron%20%E5%A5%97%E5%A3%B3%E6%89%93%E5%8C%85%EF%BC%8C%E5%A4%A7%E5%B0%8F%E8%A6%81%E5%B0%8F%E5%B0%86%E8%BF%91%2040%20%E5%80%8D%EF%BC%8C%E4%B8%80%E8%88%AC%202M%20%E5%B7%A6%E5%8F%B3%EF%BC%8C%E5%BA%95%E5%B1%82%E4%BD%BF%E7%94%A8Tauri%20%EF%BC%8C%E6%80%A7%E8%83%BD%E4%BD%93%E9%AA%8C%E8%BE%83%20JS%20%E6%A1%86%E6%9E%B6%E8%A6%81%E8%BD%BB%E5%BF%AB%E4%B8%8D%E5%B0%91%EF%BC%8C%E5%86%85%E5%AD%98%E5%B0%8F%E5%BE%88%E5%A4%9A%EF%BC%8C%E6%94%AF%E6%8C%81%E5%BE%AE%E4%BF%A1%E8%AF%BB%E4%B9%A6%E3%80%81Twitter%E3%80%81Youtube%E3%80%81RunCode%E3%80%81Flomo%E3%80%81%E8%AF%AD%E9%9B%80%E7%AD%89%EF%BC%8C%E5%8F%AF%E4%BB%A5%E5%BE%88%E6%96%B9%E4%BE%BF%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91~) 给你志同道合的朋友使用。 3. 可以关注我的 [Twitter](https://twitter.com/HiTw93) 获取到最新的 Pake 更新消息,也欢迎加入 [Telegram](https://t.me/+GclQS9ZnxyI2ODQ1) 聊天群。 4. 希望大伙玩的过程中有一种学习新技术的喜悦感,假如你发现有很适合做成桌面 App 的网页也很欢迎告诉我。 diff --git a/bin/README.md b/bin/README.md index be12361..6ba58ec 100644 --- a/bin/README.md +++ b/bin/README.md @@ -1,23 +1,25 @@ -## 安装 +

English | 简体中文

-请确保 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)。 +## Installation + +Ensure that your Node.js version is 16.0 or higher (e.g., 16.8). Avoid using `sudo` for the installation. If you encounter permission issues with npm, refer to [How to fix npm throwing error without sudo](https://stackoverflow.com/questions/16151018/how-to-fix-npm-throwing-error-without-sudo). ```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,88 +37,71 @@ npm install -g pake-cli gnome-video-effects-extra ``` -## 用法 +## Usage ```bash -pake url [options] +pake [url] [options] ``` -打包完成后的应用程序默认为当前工作目录,首次打包由于需配置好环境,需要一些时间,请耐心等待即可。 +The packaged application will be located in the current working directory by default. The first packaging might take some time due to environment configuration. Please be patient. -> **Note**: -> 打包需要用 `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] -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 -# 或者 --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, macOS only. ```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 @@ -124,42 +109,40 @@ url 为你需要打包的网页链接 🔗 或者本地 html 文件,必须提 #### [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 +150,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 +158,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,17 +166,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..e28be37 --- /dev/null +++ b/bin/README_CN.md @@ -0,0 +1,190 @@ +

English | 简体中文

+ +## 安装 + +请确保您的 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 +``` + +#### [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 375a2fe..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..619181c --- /dev/null +++ b/bin/builders/BaseBuilder.ts @@ -0,0 +1,111 @@ +import path from 'path'; +import fsExtra from 'fs-extra'; +import chalk from 'chalk'; +import prompts from 'prompts'; + +import { PakeAppOptions } from '@/types'; +import { checkRustInstalled, installRust } from '@/helpers/rust'; +import { mergeConfig } from '@/helpers/merge'; +import tauriConfig from '@/helpers/tauriConfig'; +import { npmDirectory } from '@/utils/dir'; +import { getSpinner } from '@/utils/info'; +import { shellExec } from '@/utils/shell'; +import { isChinaDomain } from '@/utils/ip'; +import { IS_MAC } from '@/utils/platform'; +import logger from '@/options/logger'; + +export default abstract class BaseBuilder { + protected options: PakeAppOptions; + + protected constructor(options: PakeAppOptions) { + this.options = options; + } + + async prepare() { + const tauriSrcPath = path.join(npmDirectory, 'src-tauri'); + const tauriTargetPath = path.join(tauriSrcPath, 'target'); + const tauriTargetPathExists = await fsExtra.pathExists(tauriTargetPath); + + if (!IS_MAC && !tauriTargetPathExists) { + logger.info('✺ The first use requires installing system dependencies.'); + logger.info('✺ See more in https://tauri.app/v1/guides/getting-started/prerequisites.'); + } + + if (!checkRustInstalled()) { + const res = await prompts({ + type: 'confirm', + message: 'Rust not detected. Install now?', + name: 'value', + }); + + if (res.value) { + await installRust(); + } else { + logger.error('✕ Rust required to package your webapp.'); + process.exit(0); + } + } + + const isChina = await isChinaDomain('www.npmjs.com'); + const spinner = getSpinner('Installing package...'); + const rustProjectDir = path.join(tauriSrcPath, '.cargo'); + const projectConf = path.join(rustProjectDir, 'config'); + await fsExtra.ensureDir(rustProjectDir); + + if (isChina) { + logger.info('✺ Located in China, using npm/rsProxy CN mirror.'); + const projectCnConf = path.join(tauriSrcPath, 'rust_proxy.toml'); + await fsExtra.copy(projectCnConf, projectConf); + await shellExec(`cd "${npmDirectory}" && npm install --registry=https://registry.npmmirror.com`); + } else { + await shellExec(`cd "${npmDirectory}" && npm install`); + } + spinner.succeed(chalk.green('Package installed!')); + if (!tauriTargetPathExists) { + logger.warn('✼ The first packaging may be slow, please be patient and wait, it will be faster afterwards.'); + } + } + + + async build(url: string) { + await this.buildAndCopy(url, this.options.targets); + } + + async buildAndCopy(url: string, target: string) { + const { name } = this.options; + await mergeConfig(url, this.options, tauriConfig); + + // Build app + const spinner = getSpinner('Building app...'); + setTimeout(() => spinner.stop(), 3000); + await shellExec(`cd ${npmDirectory} && ${this.getBuildCommand()}`); + + // Copy app + const fileName = this.getFileName(); + const fileType = this.getFileType(target); + const appPath = this.getBuildAppPath(npmDirectory, fileName, fileType); + const distPath = path.resolve(`${name}.${fileType}`); + await fsExtra.copy(appPath, distPath); + await fsExtra.remove(appPath); + logger.success('✔ Build success!'); + logger.success('✔ App installer located in', distPath); + } + + protected getFileType(target: string): string { + return target; + } + + abstract getFileName(): string; + + protected getBuildCommand(): string { + return 'npm run build'; + } + + protected getBasePath(): string { + return 'src-tauri/target/release/bundle/'; + } + + protected getBuildAppPath(npmDirectory: string, fileName: string, fileType: string): string { + return path.join(npmDirectory, this.getBasePath(), fileType.toLowerCase(), `${fileName}.${fileType}`); + } +} 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..392c54d --- /dev/null +++ b/bin/builders/BuilderProvider.ts @@ -0,0 +1,23 @@ +import BaseBuilder from './BaseBuilder'; +import MacBuilder from './MacBuilder'; +import WinBuilder from './WinBuilder'; +import LinuxBuilder from './LinuxBuilder'; +import { PakeAppOptions } from '@/types'; + +const { platform } = process; + +const buildersMap: Record BaseBuilder> = { + darwin: MacBuilder, + win32: WinBuilder, + linux: LinuxBuilder, +}; + +export default class BuilderProvider { + static create(options: PakeAppOptions): BaseBuilder { + const Builder = buildersMap[platform]; + if (!Builder) { + throw new Error('The current system is not supported!'); + } + return new Builder(options); + } +} diff --git a/bin/builders/LinuxBuilder.ts b/bin/builders/LinuxBuilder.ts index a98e05f..93ce82a 100644 --- a/bin/builders/LinuxBuilder.ts +++ b/bin/builders/LinuxBuilder.ts @@ -1,101 +1,32 @@ -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 BaseBuilder from './BaseBuilder'; +import { PakeAppOptions } from '@/types'; +import tauriConfig from '@/helpers/tauriConfig'; -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); - } +export default class LinuxBuilder extends BaseBuilder { + constructor(options: PakeAppOptions) { + super(options); } - 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 }); + getFileName(): string { + const { name } = this.options; + const arch = process.arch === 'x64' ? 'amd64' : process.arch; + return `${name}_${tauriConfig.package.version}_${arch}`; + } + + // Customize it, considering that there are all targets. + async build(url: string) { + const targetTypes = ['deb', 'appimage']; + for (const target of targetTypes) { + if (this.options.targets === target || this.options.targets === 'all') { + await this.buildAndCopy(url, target); } - 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`); - } - 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 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}_${tauriConf.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: string, packageType: string, packageName: string) { - return path.join( - npmDirectory, - 'src-tauri/target/release/bundle/', - packageType, - packageName - ); + protected getFileType(target: string): string { + if (target === 'appimage') { + return 'AppImage'; + } + return super.getFileType(target); } } diff --git a/bin/builders/MacBuilder.ts b/bin/builders/MacBuilder.ts index 9572079..28d124d 100644 --- a/bin/builders/MacBuilder.ts +++ b/bin/builders/MacBuilder.ts @@ -1,99 +1,31 @@ -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 tauriConfig from '@/helpers/tauriConfig'; +import { PakeAppOptions } from '@/types'; +import BaseBuilder from './BaseBuilder'; -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); - } +export default class MacBuilder extends BaseBuilder { + constructor(options: PakeAppOptions) { + super(options); + this.options.targets = 'dmg'; } - async build(url: string, options: PakeAppOptions) { - log.debug('PakeAppOptions', options); - const { name } = options; - - await mergeTauriConfig(url, options, tauriConf); - 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`; + getFileName(): string { + const { name } = this.options; + let arch: string; + if (this.options.multiArch) { + arch = 'universal'; } 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`; + arch = process.arch === 'arm64' ? 'aarch64' : process.arch; } - 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); + return `${name}_${tauriConfig.package.version}_${arch}`; } - 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'; - } - return path.join(npmDirectory, dmgPath, dmgName); + protected getBuildCommand(): string { + return this.options.multiArch ? 'npm run build:mac' : super.getBuildCommand(); + } + + protected getBasePath(): string { + return this.options.multiArch + ? 'src-tauri/target/universal-apple-darwin/release/bundle' + : super.getBasePath(); } } diff --git a/bin/builders/WinBuilder.ts b/bin/builders/WinBuilder.ts new file mode 100644 index 0000000..61c687a --- /dev/null +++ b/bin/builders/WinBuilder.ts @@ -0,0 +1,17 @@ +import BaseBuilder from './BaseBuilder'; +import { PakeAppOptions } from '@/types'; +import tauriConfig from '@/helpers/tauriConfig'; + +export default class WinBuilder extends BaseBuilder { + constructor(options: PakeAppOptions) { + super(options); + this.options.targets = 'msi'; + } + + getFileName(): string { + const { name } = this.options; + const { arch } = process; + const language = tauriConfig.tauri.bundle.windows.wix.language[0]; + return `${name}_${tauriConfig.package.version}_${arch}_${language}`; + } +} 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 deleted file mode 100644 index 1995fa5..0000000 --- a/bin/builders/common.ts +++ /dev/null @@ -1,355 +0,0 @@ -import { PakeAppOptions } from '@/types.js'; -import prompts from 'prompts'; -import path from 'path'; -import fs from 'fs/promises'; -import fs2 from 'fs-extra'; -import {TauriConfig} from 'tauri/src/types'; - -import { npmDirectory } from '@/utils/dir.js'; -import logger from '@/options/logger.js'; -import combineFiles from '@/helpers/combine.js'; - -type DangerousRemoteDomainIpAccess = { - domain: string; - windows: string[]; - enableTauriAPI: boolean; - schema?: string; - plugins?: string[]; -} - -// https://tauri.app/v1/api/config/#remotedomainaccessscope -type NextTauriConfig = TauriConfig & { - tauri: { - security: { - dangerousRemoteDomainIpcAccess?: DangerousRemoteDomainIpAccess[] - } - } -} - - -export async function promptText(message: string, initial?: string) { - const response = await prompts({ - type: 'text', - name: 'content', - message, - initial, - }); - return response.content; -} - -function setSecurityConfigWithUrl(tauriConfig: NextTauriConfig, url: string) { - const myURL = new URL(url); - const currentUrlConfig: DangerousRemoteDomainIpAccess = { - domain: myURL.hostname, - windows: ["pake"], - enableTauriAPI: true, - }; - tauriConfig.tauri.security.dangerousRemoteDomainIpcAccess = [currentUrlConfig]; -} - -export async function mergeTauriConfig( - url: string, - options: PakeAppOptions, - tauriConf: any -) { - const { - width, - height, - fullscreen, - transparent, - resizable, - userAgent, - showMenu, - showSystemTray, - systemTrayIcon, - iterCopyFile, - identifier, - name, - inject, - } = 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], { url, ...tauriConfWindowOptions }); - 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); - if (!iterCopyFile) { - const url_path = path.join(npmDirectory,"dist/", file_name); - await fs.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.copyFile(cli_path, cli_path_target); - fs.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 = await fs.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`]; - 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 (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 = await fs.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}`; - 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.`); - } - } 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 injectFiles: string[] = []; - // 注入外部 js css - if (inject?.length > 0) { - if (!inject.every(item => item.endsWith('.css') || item.endsWith('.js'))) { - logger.error("The injected file must be in either CSS or JS format."); - return; - } - const files = inject.map(relativePath => path.join(process.cwd(), relativePath)); - injectFiles = injectFiles.concat(...files); - tauriConf.pake.inject = files; - } - combineFiles(injectFiles); - - - // 保存配置文件 - 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}}; - 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') - ); -} 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 dbd426d..7f733c3 100644 --- a/bin/cli.ts +++ b/bin/cli.ts @@ -1,53 +1,49 @@ +import chalk from 'chalk'; 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 BuilderProvider from './builders/BuilderProvider'; +import { DEFAULT_PAKE_OPTIONS as DEFAULT } from './defaults'; +import { checkUpdateTips } from './helpers/updater'; +import handleInputOptions from './options/index'; -program.version(packageJson.version).description('A cli application can package a web page to desktop application.'); +import { PakeCliOptions } from './types'; +import { validateNumberInput, validateUrlInput } from './utils/validate'; 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) - .option('--inject [injects...]', 'inject .js or .css for this app', DEFAULT_PAKE_OPTIONS.inject) + .description(chalk.green('Pake can turn any webpage into a desktop app with Rust.')) + .usage('[url] [options]') + .showHelpAfterError(); + +program + .argument('[url]', 'The web URL you want to package', validateUrlInput) + .option('--name ', 'Application name') + .option('--icon ', 'Application icon', DEFAULT.icon) + .option('--width ', 'Window width', validateNumberInput, DEFAULT.width) + .option('--height ', 'Window height', validateNumberInput, DEFAULT.height) + .option('--transparent', 'Only for Mac, hide title bar', DEFAULT.transparent) + .option('--fullscreen', 'Start in full screen', DEFAULT.fullscreen) + .option('--user-agent ', 'Custom user agent', DEFAULT.userAgent) + .option('--show-menu', 'Show menu in app', DEFAULT.showMenu) + .option('--show-system-tray', 'Show system tray in app', DEFAULT.showSystemTray) + .option('--system-tray-icon ', 'Custom system tray icon', DEFAULT.systemTrayIcon) + .option('--iter-copy-file', 'Copy files when URL is a local file', DEFAULT.iterCopyFile) + .option('--multi-arch', 'Only for Mac, supports both Intel and M1', DEFAULT.multiArch) + .option('--targets ', 'Only for Linux, option "deb" or "appimage"', DEFAULT.targets) + .option('--inject [injects...]', 'inject .js or .css for this app', DEFAULT.inject) + .option('--debug', 'Debug mode', DEFAULT.debug) + .version(packageJson.version, '-v, --version', 'Output the current version') .action(async (url: string, options: PakeCliOptions) => { - checkUpdateTips(); + await checkUpdateTips(); if (!url) { - // 直接 pake 不需要出现url提示 - program.help(); + program.outputHelp(str => { + return str + .split('\n') + .filter(line => !/((-h,|--help)|((-v|-V),|--version))\s+.+$/.test(line)) + .join('\n'); + }); + process.exit(0); } log.setDefaultLevel('info'); @@ -55,12 +51,12 @@ program log.setLevel('debug'); } - const builder = BuilderFactory.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); + + const builder = BuilderProvider.create(appOptions); + await builder.prepare(); + await builder.build(url); }); program.parse(); diff --git a/bin/defaults.ts b/bin/defaults.ts index 830f114..fe3aaa6 100644 --- a/bin/defaults.ts +++ b/bin/defaults.ts @@ -17,5 +17,3 @@ export const DEFAULT_PAKE_OPTIONS: PakeCliOptions = { debug: false, inject: [], }; - -export const DEFAULT_APP_NAME = 'Pake'; diff --git a/bin/helpers/merge.ts b/bin/helpers/merge.ts new file mode 100644 index 0000000..d4095a3 --- /dev/null +++ b/bin/helpers/merge.ts @@ -0,0 +1,194 @@ +import path from 'path'; +import fsExtra from 'fs-extra'; + +import { npmDirectory } from '@/utils/dir'; +import logger from '@/options/logger'; +import { PakeAppOptions, PlatformMap } from '@/types'; + +export async function mergeConfig(url: string, options: PakeAppOptions, tauriConf: any) { + const { + width, + height, + fullscreen, + transparent, + userAgent, + showMenu, + showSystemTray, + systemTrayIcon, + iterCopyFile, + identifier, + name, + resizable = true, + } = options; + + const { platform } = process; + + // Set Windows parameters. + const tauriConfWindowOptions = { + width, + height, + fullscreen, + transparent, + resizable, + }; + Object.assign(tauriConf.pake.windows[0], { url, ...tauriConfWindowOptions }); + + tauriConf.package.productName = name; + tauriConf.tauri.bundle.identifier = identifier; + + //Judge the type of URL, whether it is a file or a website. + const pathExists = await fsExtra.pathExists(url); + if (pathExists) { + logger.warn('✼ Your input might be a local file.'); + tauriConf.pake.windows[0].url_type = 'local'; + + const fileName = path.basename(url); + const dirName = path.dirname(url); + + const distDir = path.join(npmDirectory, 'dist'); + const distBakDir = path.join(npmDirectory, 'dist_bak'); + + if (!iterCopyFile) { + const urlPath = path.join(distDir, fileName); + await fsExtra.copy(url, urlPath); + } else { + fsExtra.moveSync(distDir, distBakDir, { overwrite: true }); + fsExtra.copySync(dirName, distDir, { overwrite: true }); + + const filesToCopyBack = ['cli.js', 'about_pake.html']; + await Promise.all( + filesToCopyBack.map(file => + fsExtra.copy(path.join(distBakDir, file), path.join(distDir, file)), + ), + ); + } + + tauriConf.pake.windows[0].url = fileName; + tauriConf.pake.windows[0].url_type = 'local'; + } else { + tauriConf.pake.windows[0].url_type = 'web'; + // Set the secure domain for calling window.__TAURI__ to the application domain that has been set. + tauriConf.tauri.security.dangerousRemoteDomainIpcAccess[0].domain = new URL(url).hostname; + } + + const platformMap: PlatformMap = { + win32: 'windows', + linux: 'linux', + darwin: 'macos', + }; + const currentPlatform = platformMap[platform]; + + if (userAgent.length > 0) { + tauriConf.pake.user_agent[currentPlatform] = userAgent; + } + + tauriConf.pake.menu[currentPlatform] = showMenu; + tauriConf.pake.system_tray[currentPlatform] = showSystemTray; + + // Processing targets are currently only open to Linux. + if (platform === 'linux') { + delete tauriConf.tauri.bundle.deb.files; + const validTargets = ['all', 'deb', 'appimage']; + if (validTargets.includes(options.targets)) { + tauriConf.tauri.bundle.targets = + options.targets === 'all' ? ['deb', 'appimage'] : [options.targets]; + } else { + logger.warn( + `✼ The target must be one of ${validTargets.join(', ')}, the default 'deb' will be used.`, + ); + } + } + + // Set icon. + const platformIconMap: PlatformMap = { + win32: { + fileExt: '.ico', + path: `png/${name.toLowerCase()}_256.ico`, + defaultIcon: 'png/icon_256.ico', + message: 'Windows icon must be .ico and 256x256px.', + }, + linux: { + fileExt: '.png', + path: `png/${name.toLowerCase()}_512.png`, + defaultIcon: 'png/icon_512.png', + message: 'Linux icon must be .png and 512x512px.', + }, + darwin: { + fileExt: '.icns', + path: `icons/${name.toLowerCase()}.icns`, + defaultIcon: 'icons/icon.icns', + message: 'macOS icon must be .icns type.', + }, + }; + const iconInfo = platformIconMap[platform]; + const exists = await fsExtra.pathExists(options.icon); + if (exists) { + let updateIconPath = true; + let customIconExt = path.extname(options.icon).toLowerCase(); + + if (customIconExt !== iconInfo.fileExt) { + updateIconPath = false; + logger.warn(`✼ ${iconInfo.message}, but you give ${customIconExt}`); + tauriConf.tauri.bundle.icon = [iconInfo.defaultIcon]; + } else { + const iconPath = path.join(npmDirectory, 'src-tauri/', iconInfo.path); + tauriConf.tauri.bundle.resources = [iconInfo.path]; + await fsExtra.copy(options.icon, iconPath); + } + + if (updateIconPath) { + tauriConf.tauri.bundle.icon = [options.icon]; + } else { + logger.warn(`✼ Icon will remain as default.`); + } + } else { + logger.warn('✼ Custom icon path may be invalid, default icon will be used instead.'); + tauriConf.tauri.bundle.icon = [iconInfo.defaultIcon]; + } + + // Set tray icon path. + let trayIconPath = platform === 'darwin' ? 'png/icon_512.png' : tauriConf.tauri.bundle.icon[0]; + if (systemTrayIcon.length > 0) { + try { + await fsExtra.pathExists(systemTrayIcon); + // 需要判断图标格式,默认只支持ico和png两种 + let iconExt = path.extname(systemTrayIcon).toLowerCase(); + if (iconExt == '.png' || iconExt == '.ico') { + const trayIcoPath = path.join( + npmDirectory, + `src-tauri/png/${name.toLowerCase()}${iconExt}`, + ); + trayIconPath = `png/${name.toLowerCase()}${iconExt}`; + await fsExtra.copy(systemTrayIcon, trayIcoPath); + } else { + logger.warn(`✼ System tray icon must be .ico or .png, but you provided ${iconExt}.`); + logger.warn(`✼ Default system tray icon will be used.`); + } + } catch { + logger.warn(`✼ ${systemTrayIcon} not exists!`); + logger.warn(`✼ Default system tray icon will remain unchanged.`); + } + } + + tauriConf.tauri.systemTray.iconPath = trayIconPath; + + // Save config file. + const platformConfigPaths: 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]); + + const bundleConf = { tauri: { bundle: tauriConf.tauri.bundle } }; + await fsExtra.writeJson(configPath, bundleConf, { spaces: 4 }); + + const pakeConfigPath = path.join(npmDirectory, 'src-tauri/pake.json'); + await fsExtra.writeJson(pakeConfigPath, tauriConf.pake, { spaces: 4 }); + + let tauriConf2 = JSON.parse(JSON.stringify(tauriConf)); + delete tauriConf2.pake; + delete tauriConf2.tauri.bundle; + const configJsonPath = path.join(npmDirectory, 'src-tauri/tauri.conf.json'); + await fsExtra.writeJson(configJsonPath, tauriConf2, { spaces: 4 }); +} diff --git a/bin/helpers/rust.ts b/bin/helpers/rust.ts index fec21dc..bf53087 100644 --- a/bin/helpers/rust.ts +++ b/bin/helpers/rust.ts @@ -1,31 +1,26 @@ -import { IS_WIN } from '@/utils/platform.js'; -import ora from 'ora'; +import chalk from 'chalk'; import shelljs from 'shelljs'; -import logger from '@/options/logger.js'; -import { shellExec } from '../utils/shell.js'; -import {isChinaDomain} from '@/utils/ip_addr.js' +import { getSpinner } from '@/utils/info'; +import { IS_WIN } from '@/utils/platform'; +import { shellExec } from '@/utils/shell'; +import { isChinaDomain } from '@/utils/ip'; export async function installRust() { - const 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 spinner = ora('Downloading Rust').start(); - try { - await shellExec(IS_WIN ? RustInstallScriptForWin : RustInstallScriptFocMac); - spinner.succeed(); - } catch (error) { - console.error('install rust return code', error.message); - spinner.fail(); + const isInChina = await isChinaDomain('sh.rustup.rs'); + const rustInstallScriptForMac = isInChina + ? 'export RUSTUP_DIST_SERVER="https://rsproxy.cn" && export RUSTUP_UPDATE_ROOT="https://rsproxy.cn/rustup" && curl --proto "=https" --tlsv1.2 -sSf https://rsproxy.cn/rustup-init.sh | sh' + : "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y"; + const rustInstallScriptForWindows = 'winget install --id Rustlang.Rustup'; + const spinner = getSpinner('Downloading Rust...'); + + try { + await shellExec(IS_WIN ? rustInstallScriptForWindows : rustInstallScriptForMac); + spinner.succeed(chalk.green('Rust installed successfully!')); + } catch (error) { + console.error('Error installing Rust:', error.message); + spinner.fail(chalk.red('Rust installation failed!')); process.exit(1); } } diff --git a/bin/helpers/tauriConfig.ts b/bin/helpers/tauriConfig.ts index fb44d61..32c3b83 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..549e989 100644 --- a/bin/helpers/updater.ts +++ b/bin/helpers/updater.ts @@ -1,7 +1,6 @@ import updateNotifier from 'update-notifier'; -// @ts-expect-error import packageJson from '../../package.json'; export async function checkUpdateTips() { - updateNotifier({ pkg: packageJson }).notify(); + updateNotifier({ pkg: packageJson, updateCheckInterval: 1000 * 60 }).notify(); } diff --git a/bin/options/icon.ts b/bin/options/icon.ts index 8aff0d6..6ee54e6 100644 --- a/bin/options/icon.ts +++ b/bin/options/icon.ts @@ -1,98 +1,59 @@ -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 chalk from 'chalk'; +import { dir } from 'tmp-promise'; -export async function handleIcon(options: PakeAppOptions, url: string) { +import logger from './logger'; +import { npmDirectory } from '@/utils/dir'; +import { IS_LINUX, IS_WIN } from '@/utils/platform'; +import { getSpinner } from '@/utils/info'; +import { fileTypeFromBuffer } from 'file-type'; +import { PakeAppOptions } from '@/types'; + +export async function handleIcon(options: PakeAppOptions) { 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.warn('✼ No icon given, default in use. For a custom icon, use --icon option.'); + const iconPath = IS_WIN + ? 'src-tauri/png/icon_256.ico' + : IS_LINUX + ? 'src-tauri/png/icon_512.png' + : 'src-tauri/icons/icon.icns'; + return path.join(npmDirectory, iconPath); } } -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; + const spinner = getSpinner('Downloading icon...'); try { - iconResponse = await axios.get(iconUrl, { - responseType: 'arraybuffer', - }); + const iconResponse = await axios.get(iconUrl, { responseType: 'arraybuffer' }); + const iconData = await iconResponse.data; + + if (!iconData) { + return null; + } + + const fileDetails = await fileTypeFromBuffer(iconData); + if (!fileDetails) { + return null; + } + + const { path: tempPath } = await dir(); + const iconPath = `${tempPath}/icon.${fileDetails.ext}`; + await fsExtra.outputFile(iconPath, iconData); + spinner.succeed(chalk.green('Icon downloaded successfully!')); + return iconPath; } catch (error) { + spinner.fail(chalk.red('Icon download failed!')); if (error.response && error.response.status === 404) { return null; } throw error; } - - 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..17f41d9 100644 --- a/bin/options/index.ts +++ b/bin/options/index.ts @@ -1,29 +1,61 @@ -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 logger from '@/options/logger'; -export default async function handleOptions(options: PakeCliOptions, url: string): Promise { - const appOptions: PakeAppOptions = { - ...options, - identifier: '', +import { handleIcon } from './icon'; +import { getDomain } from '@/utils/url'; +import { getIdentifier, promptText, capitalizeFirstLetter } from '@/utils/info'; +import { PakeAppOptions, PakeCliOptions, PlatformMap } from '@/types'; + +function resolveAppName(name: string, platform: NodeJS.Platform): string { + const domain = getDomain(name) || 'pake'; + return platform !== 'linux' ? capitalizeFirstLetter(domain) : domain; +} + +function isValidName(name: string, platform: NodeJS.Platform): boolean { + const platformRegexMapping: PlatformMap = { + linux: /^[a-z0-9]+(-[a-z0-9]+)*$/, + default: /^[a-zA-Z0-9]+$/, }; - const url_exists = await fs.stat(url) - .then(() => true) - .catch(() => false); - if (!appOptions.name) { - if (!url_exists) { - appOptions.name = await promptText('please input your application name', getDomain(url)); + const reg = platformRegexMapping[platform] || platformRegexMapping.default; + return !!name && reg.test(name); +} + +export default async function handleOptions( + options: PakeCliOptions, + url: string, +): Promise { + const { platform } = process; + const isActions = process.env.GITHUB_ACTIONS; + let name = options.name; + + const pathExists = await fsExtra.pathExists(url); + if (!options.name) { + const defaultName = pathExists ? '' : resolveAppName(url, platform); + const promptMessage = 'Enter your application name'; + const namePrompt = await promptText(promptMessage, defaultName); + name = namePrompt || defaultName; + } + + if (!isValidName(name, platform)) { + const LINUX_NAME_ERROR = `✕ name should only include lowercase letters, numbers, and dashes, and must contain at least one lowercase letter. Examples: com-123-xxx, 123pan, pan123, weread, we-read.`; + const DEFAULT_NAME_ERROR = `✕ Name should only include letters and numbers, and must contain at least one letter. Examples: 123pan, 123Pan, Pan123, weread, WeRead, WERead.`; + const errorMsg = platform === 'linux' ? LINUX_NAME_ERROR : DEFAULT_NAME_ERROR; + logger.error(errorMsg); + if (isActions) { + name = resolveAppName(url, platform); + logger.warn(`✼ Inside github actions, use the default name: ${name}`); } else { - appOptions.name = await promptText('please input your application name', ""); + process.exit(1); } } - appOptions.identifier = getIdentifier(appOptions.name, url); + const appOptions: PakeAppOptions = { + ...options, + name, + identifier: getIdentifier(url), + }; - appOptions.icon = await handleIcon(appOptions, url); + appOptions.icon = await handleIcon(appOptions); return appOptions; } diff --git a/bin/options/logger.ts b/bin/options/logger.ts index da6cafa..0461d4c 100644 --- a/bin/options/logger.ts +++ b/bin/options/logger.ts @@ -1,22 +1,22 @@ -import log from 'loglevel'; import chalk from 'chalk'; +import log from 'loglevel'; const logger = { info(...msg: any[]) { - log.info(...msg.map((m) => chalk.blue.bold(m))); + log.info(...msg.map(m => chalk.white(m))); }, debug(...msg: any[]) { log.debug(...msg); }, error(...msg: any[]) { - log.error(...msg.map((m) => chalk.red.bold(m))); + log.error(...msg.map(m => chalk.red(m))); }, warn(...msg: any[]) { - log.info(...msg.map((m) => chalk.yellow.bold(m))); + log.info(...msg.map(m => chalk.yellow(m))); }, success(...msg: any[]) { - log.info(...msg.map((m) => chalk.green.bold(m))); - } + log.info(...msg.map(m => chalk.green(m))); + }, }; export default logger; diff --git a/bin/types.ts b/bin/types.ts index 778a07f..9bda71a 100644 --- a/bin/types.ts +++ b/bin/types.ts @@ -1,47 +1,51 @@ +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; /** 需要注入页面的外部脚本 */ diff --git a/bin/utils/dir.ts b/bin/utils/dir.ts index 76da5af..cb5a697 100644 --- a/bin/utils/dir.ts +++ b/bin/utils/dir.ts @@ -1,8 +1,8 @@ import path from 'path'; import { fileURLToPath } from 'url'; +// Convert the current module URL to a file path +const currentModulePath = fileURLToPath(import.meta.url); -export const npmDirectory = path.join( - path.dirname(fileURLToPath(import.meta.url)), - '..' -); +// Resolve the parent directory of the current module +export const npmDirectory = path.join(path.dirname(currentModulePath), '..'); diff --git a/bin/utils/info.ts b/bin/utils/info.ts new file mode 100644 index 0000000..a08a726 --- /dev/null +++ b/bin/utils/info.ts @@ -0,0 +1,36 @@ +import crypto from 'crypto'; +import prompts from 'prompts'; +import ora from 'ora'; +import chalk from 'chalk'; + +// Generates an identifier based on the given URL. +export function getIdentifier(url: string) { + 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; +} + +export function capitalizeFirstLetter(string: string) { + return string.charAt(0).toUpperCase() + string.slice(1); +} + +export function getSpinner(text: string) { + const loadingType = { + interval: 80, + frames: ['✦', '✶', '✺', '✵', '✸', '✹', '✺'], + }; + return ora({ + text: `${chalk.cyan(text)}\n`, + spinner: loadingType, + color: 'cyan', + }).start(); +} diff --git a/bin/utils/ip.ts b/bin/utils/ip.ts new file mode 100644 index 0000000..c3b6434 --- /dev/null +++ b/bin/utils/ip.ts @@ -0,0 +1,57 @@ +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); + const ip = await lookup(host); + const start = new Date(); + + // Prevent timeouts from affecting user experience. + const requestPromise = new Promise((resolve, reject) => { + const req = http.get(`http://${ip.address}`, res => { + const delay = new Date().getTime() - start.getTime(); + res.resume(); + resolve(delay); + }); + + req.on('error', err => { + reject(err); + }); + }); + + const timeoutPromise = new Promise((_, reject) => { + setTimeout(() => { + reject(new Error('Request timed out after 3 seconds')); + }, 1000); + }); + + return Promise.race([requestPromise, timeoutPromise]); +}; + +async function isChinaDomain(domain: string): Promise { + try { + const [ip] = await resolve(domain); + return await isChinaIP(ip, domain); + } catch (error) { + logger.debug(`${domain} can't be parse!`); + return true; + } +} + +async function isChinaIP(ip: string, domain: string): Promise { + try { + const delay = await ping(ip); + logger.debug(`${domain} latency is ${delay} ms`); + return delay > 1000; + } catch (error) { + logger.debug(`ping ${domain} failed!`); + return true; + } +} + +export { isChinaDomain, isChinaIP }; diff --git a/bin/utils/ip_addr.ts b/bin/utils/ip_addr.ts deleted file mode 100644 index a89f697..0000000 --- a/bin/utils/ip_addr.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { exec } from 'child_process'; -import { promisify } from 'util'; -import logger from '@/options/logger.js'; -import dns from 'dns'; -import http from 'http'; - - -const ping = async (host: string) => { - 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); - }); - }); -}; - - -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, is not in China!`); - 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!, is not in China!`); - 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..5463075 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 shelljs from 'shelljs'; +import { npmDirectory } from './dir'; export function shellExec(command: string) { return new Promise((resolve, reject) => { - shelljs.exec(command, { async: true, silent: false, cwd: npmDirectory}, (code) => { + shelljs.exec(command, { async: true, silent: false, cwd: npmDirectory }, code => { if (code === 0) { resolve(0); } else { diff --git a/bin/utils/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..6c19ff3 100644 --- a/bin/utils/url.ts +++ b/bin/utils/url.ts @@ -1,45 +1,39 @@ -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 4a4514d..c170218 100644 --- a/dist/cli.js +++ b/dist/cli.js @@ -1,2187 +1,123 @@ -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 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 { InvalidArgumentError, program } from 'commander'; +import log from 'loglevel'; +import path from 'path'; +import fsExtra from 'fs-extra'; +import prompts from 'prompts'; import shelljs from 'shelljs'; -import { promisify } from 'util'; +import crypto from 'crypto'; +import ora from 'ora'; +import { fileURLToPath } from 'url'; import dns from 'dns'; import http from 'http'; +import { promisify } from 'util'; import updateNotifier from 'update-notifier'; +import axios from 'axios'; +import { dir } from 'tmp-promise'; +import { fileTypeFromBuffer } from 'file-type'; +import psl from 'psl'; +import isUrl from 'is-url'; +import fs from 'fs'; -/****************************************************************************** -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()); - }); -} - -const DEFAULT_PAKE_OPTIONS = { - icon: '', - height: 780, - width: 1200, - fullscreen: false, - resizable: true, - transparent: false, - userAgent: '', - showMenu: false, - showSystemTray: false, - multiArch: false, - targets: 'deb', - iterCopyFile: false, - systemTrayIcon: '', - debug: false, - inject: [], +var name = "pake-cli"; +var version = "2.1.8"; +var description = "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。"; +var engines = { + node: ">=16.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", - "ελ", - "ευ", - "бг", - "бел", - "дети", - "ею", - "католик", - "ком", - "мкд", - "мон", - "москва", - "онлайн", - "орг", - "рус", - "рф", - "сайт", - "срб", - "укр", - "қаз", - "հայ", - "ישראל", - "קום", - "ابوظبي", - "اتصالات", - "ارامكو", - "الاردن", - "البحرين", - "الجزائر", - "السعودية", - "العليان", - "المغرب", - "امارات", - "ایران", - "بارت", - "بازار", - "بيتك", - "بھارت", - "تونس", - "سودان", - "سورية", - "شبكة", - "عراق", - "عرب", - "عمان", - "فلسطين", - "قطر", - "كاثوليك", - "كوم", - "مصر", - "مليسيا", - "موريتانيا", - "موقع", - "همراه", - "پاکستان", - "ڀارت", - "कॉम", - "नेट", - "भारत", - "भारतम्", - "भारोत", - "संगठन", - "বাংলা", - "ভারত", - "ভাৰত", - "ਭਾਰਤ", - "ભારત", - "ଭାରତ", - "இந்தியா", - "இலங்கை", - "சிங்கப்பூர்", - "భారత్", - "ಭಾರತ", - "ഭാരതം", - "ලංකා", - "คอม", - "ไทย", - "ລາວ", - "გე", - "みんな", - "アマゾン", - "クラウド", - "グーグル", - "コム", - "ストア", - "セール", - "ファッション", - "ポイント", - "世界", - "中信", - "中国", - "中國", - "中文网", - "亚马逊", - "企业", - "佛山", - "信息", - "健康", - "八卦", - "公司", - "公益", - "台湾", - "台灣", - "商城", - "商店", - "商标", - "嘉里", - "嘉里大酒店", - "在线", - "大拿", - "天主教", - "娱乐", - "家電", - "广东", - "微博", - "慈善", - "我爱你", - "手机", - "招聘", - "政务", - "政府", - "新加坡", - "新闻", - "时尚", - "書籍", - "机构", - "淡马锡", - "游戏", - "澳門", - "点看", - "移动", - "组织机构", - "网址", - "网店", - "网站", - "网络", - "联通", - "诺基亚", - "谷歌", - "购物", - "通販", - "集团", - "電訊盈科", - "飞利浦", - "食品", - "餐厅", - "香格里拉", - "香港", - "닷넷", - "닷컴", - "삼성", - "한국", +var bin = { + pake: "./cli.js" +}; +var repository = { + type: "git", + url: "https://github.com/tw93/pake.git" +}; +var author = { + name: "Tw93", + email: "tw93@qq.com" +}; +var keywords = [ + "pake", + "pake-cli", + "rust", + "tauri", + "no-electron", + "productivity" ]; - -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 files = [ + "dist", + "src-tauri", + "cli.js" +]; +var scripts = { + start: "npm run dev", + dev: "npm run tauri dev", + build: "npm run tauri build --release", + "build:mac": "npm run tauri build -- --target universal-apple-darwin", + "build:all-unix": "chmod +x ./script/build.sh && ./script/build.sh", + "build:all-windows": "pwsh ./script/build.ps1", + analyze: "cd src-tauri && cargo bloat --release --crates", + tauri: "tauri", + cli: "rollup -c rollup.config.js --watch", + "cli:build": "cross-env NODE_ENV=production rollup -c rollup.config.js", + prepublishOnly: "npm run cli:build" }; - -function combineFiles(files) { - return __awaiter(this, void 0, void 0, function* () { - const output = path.join(npmDirectory, `src-tauri/src/inject/_INJECT_.js`); - const contents = files.map(file => { - const fileContent = fs.readFileSync(file); - if (file.endsWith('.css')) { - return "window.addEventListener('DOMContentLoaded', (_event) => { const css = `" + fileContent + "`; const style = document.createElement('style'); style.innerHTML = css; document.head.appendChild(style); });"; - } - return fileContent; - }); - fs.writeFileSync(output, contents.join('\n')); - return files; - }); -} - -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 currentUrlConfig = { - domain: myURL.hostname, - windows: ["pake"], - enableTauriAPI: true, - }; - tauriConfig.tauri.security.dangerousRemoteDomainIpcAccess = [currentUrlConfig]; -} -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, inject, } = 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); - // 内部注入文件 - const internalInjectScripts = [ - path.join(npmDirectory, 'bin/inject/component.js'), - path.join(npmDirectory, 'bin/inject/event.js'), - path.join(npmDirectory, 'bin/inject/style.js'), - ]; - let injectFiles = [...internalInjectScripts]; - // 注入外部 js css - if ((inject === null || inject === void 0 ? void 0 : inject.length) > 0) { - if (!inject.every(item => item.endsWith('.css') || item.endsWith('.js'))) { - logger.error("The injected file must be in either CSS or JS format."); - return; - } - const files = inject.map(relativePath => path.join(process.cwd(), relativePath)); - injectFiles = injectFiles.concat(...files); - tauriConf.pake.inject = files; - } - combineFiles(injectFiles); - // 保存配置文件 - 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, is not in China!`); - 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!, is not in China!`); - 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: { - csp: null - }, - updater: { - active: false - }, - systemTray: { - iconPath: "png/weread_512.png", - iconAsTemplate: true - }, - allowlist: { - all: true, - fs: { - all: true, - scope: [ - "$DOWNLOAD/*" - ] - } - } +var type = "module"; +var exports = "./dist/pake.js"; +var license = "MIT"; +var dependencies = { + "@tauri-apps/api": "^1.4.0", + "@tauri-apps/cli": "^1.4.0", + axios: "^1.1.3", + chalk: "^5.1.2", + commander: "^11.0.0", + "file-type": "^18.0.0", + "fs-extra": "^11.1.0", + "is-url": "^1.2.4", + loglevel: "^1.8.1", + ora: "^6.1.2", + prompts: "^2.4.2", + psl: "^1.9.0", + shelljs: "^0.8.5", + "tmp-promise": "^3.0.3", + "update-notifier": "^6.0.2" }; -var build = { - withGlobalTauri: true, - devPath: "../dist", - distDir: "../dist", - beforeBuildCommand: "", - beforeDevCommand: "" +var devDependencies = { + "@rollup/plugin-alias": "^4.0.2", + "@rollup/plugin-commonjs": "^23.0.2", + "@rollup/plugin-json": "^5.0.2", + "@rollup/plugin-terser": "^0.1.0", + "@types/fs-extra": "^9.0.13", + "@types/is-url": "^1.2.30", + "@types/page-icon": "^0.3.4", + "@types/prompts": "^2.4.1", + "@types/psl": "^1.1.0", + "@types/shelljs": "^0.8.11", + "@types/tmp": "^0.2.3", + "@types/update-notifier": "^6.0.1", + "app-root-path": "^3.1.0", + "cross-env": "^7.0.3", + rollup: "^3.3.0", + "rollup-plugin-typescript2": "^0.34.1", + tslib: "^2.4.1", + typescript: "^4.9.3" }; -var CommonConf = { - "package": { - productName: "WeRead", - version: "1.0.0" -}, - tauri: tauri$3, - build: build +var packageJson = { + name: name, + version: version, + description: description, + engines: engines, + bin: bin, + repository: repository, + author: author, + keywords: keywords, + files: files, + scripts: scripts, + type: type, + exports: exports, + license: license, + dependencies: dependencies, + devDependencies: devDependencies }; var windows = [ @@ -2217,6 +153,52 @@ var pakeConf = { system_tray: system_tray }; +var tauri$3 = { + security: { + csp: null, + dangerousRemoteDomainIpcAccess: [ + { + domain: "weread.qq.com", + windows: [ + "pake" + ], + enableTauriAPI: true + } + ] + }, + updater: { + active: false + }, + systemTray: { + iconPath: "png/weread_512.png", + iconAsTemplate: true + }, + allowlist: { + all: true, + fs: { + all: true, + scope: [ + "$DOWNLOAD/*" + ] + } + } +}; +var build = { + withGlobalTauri: true, + devPath: "../dist", + distDir: "../dist", + beforeBuildCommand: "", + beforeDevCommand: "" +}; +var CommonConf = { + "package": { + productName: "WeRead", + version: "1.0.0" +}, + tauri: tauri$3, + build: build +}; + var tauri$2 = { bundle: { icon: [ @@ -2320,403 +302,683 @@ 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, }; -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; - } +const { platform: platform$2 } = process; +// @ts-ignore +const platformConfig = platformConfigs[platform$2]; +let tauriConfig = { + tauri: { + ...CommonConf.tauri, + bundle: platformConfig.tauri.bundle, + }, + package: CommonConf.package, + build: CommonConf.build, + pake: pakeConf, +}; + +// Generates an identifier based on the given URL. +function getIdentifier(url) { + const postFixHash = crypto.createHash('md5').update(url).digest('hex').substring(0, 6); + return `pake-${postFixHash}`; +} +async function promptText(message, initial) { + const response = await prompts({ + type: 'text', + name: 'content', + message, + initial, + }); + return response.content; +} +function capitalizeFirstLetter(string) { + return string.charAt(0).toUpperCase() + string.slice(1); +} +function getSpinner(text) { + const loadingType = { + interval: 80, + frames: ['✦', '✶', '✺', '✵', '✸', '✹', '✺'], + }; + return ora({ + text: `${chalk.cyan(text)}\n`, + spinner: loadingType, + color: 'cyan', + }).start(); } -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 { platform: platform$1 } = process; +const IS_MAC = platform$1 === 'darwin'; +const IS_WIN = platform$1 === 'win32'; +const IS_LINUX = platform$1 === 'linux'; -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(); +// Convert the current module URL to a file path +const currentModulePath = fileURLToPath(import.meta.url); +// Resolve the parent directory of the current module +const npmDirectory = path.join(path.dirname(currentModulePath), '..'); + +function shellExec(command) { + return new Promise((resolve, reject) => { + shelljs.exec(command, { async: true, silent: false, cwd: npmDirectory }, code => { + if (code === 0) { + resolve(0); } else { - logger.error('Error: Pake needs Rust to package your webapp!!!'); - process.exit(2); + reject(new Error(`${code}`)); } }); - } - 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); - } -} - -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 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!!'); - } -} - -var name = "pake-cli"; -var version = "2.0.6"; -var description = "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。"; -var engines = { - node: ">=16.0.0" -}; -var bin = { - pake: "./cli.js" -}; -var repository = { - type: "git", - url: "https://github.com/tw93/pake.git" -}; -var author = { - name: "Tw93", - email: "tw93@qq.com" -}; -var keywords = [ - "pake", - "pake-cli", - "rust", - "tauri", - "no-electron", - "productivity" -]; -var files = [ - "dist", - "src-tauri", - "cli.js" -]; -var scripts = { - start: "npm run dev", - dev: "npm run tauri dev", - build: "npm run tauri build --release", - "build:mac": "npm run tauri build -- --target universal-apple-darwin", - "build:all-unix": "chmod +x ./script/build.sh && ./script/build.sh", - "build:all-windows": "pwsh ./script/build.ps1", - analyze: "cd src-tauri && cargo bloat --release --crates", - tauri: "tauri", - cli: "rollup -c rollup.config.js --watch", - "cli:build": "cross-env NODE_ENV=production rollup -c rollup.config.js", - prepublishOnly: "npm run cli:build" -}; -var type = "module"; -var exports = "./dist/pake.js"; -var license = "MIT"; -var dependencies = { - "@tauri-apps/api": "^1.3.0", - "@tauri-apps/cli": "^1.3.1", - axios: "^1.1.3", - chalk: "^5.1.2", - commander: "^9.4.1", - "file-type": "^18.0.0", - "fs-extra": "^11.1.0", - "is-url": "^1.2.4", - loglevel: "^1.8.1", - ora: "^6.1.2", - prompts: "^2.4.2", - shelljs: "^0.8.5", - "tmp-promise": "^3.0.3", - "update-notifier": "^6.0.2" -}; -var devDependencies = { - "@rollup/plugin-alias": "^4.0.2", - "@rollup/plugin-commonjs": "^23.0.2", - "@rollup/plugin-json": "^5.0.1", - "@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/shelljs": "^0.8.11", - "@types/tmp": "^0.2.3", - "@types/update-notifier": "^6.0.1", - "app-root-path": "^3.1.0", - concurrently: "^7.5.0", - "cross-env": "^7.0.3", - rollup: "^3.3.0", - tauri: "^0.15.0", - tslib: "^2.4.1", - typescript: "^4.9.3" -}; -var packageJson = { - name: name, - version: version, - description: description, - engines: engines, - bin: bin, - repository: repository, - author: author, - keywords: keywords, - files: files, - scripts: scripts, - type: type, - exports: exports, - license: license, - dependencies: dependencies, - devDependencies: devDependencies -}; - -function checkUpdateTips() { - return __awaiter(this, void 0, void 0, function* () { - updateNotifier({ pkg: packageJson }).notify(); }); } -program.version(packageJson.version).description('A cli application can package a web page to desktop application.'); +const logger = { + info(...msg) { + log.info(...msg.map(m => chalk.white(m))); + }, + debug(...msg) { + log.debug(...msg); + }, + error(...msg) { + log.error(...msg.map(m => chalk.red(m))); + }, + warn(...msg) { + log.info(...msg.map(m => chalk.yellow(m))); + }, + success(...msg) { + log.info(...msg.map(m => chalk.green(m))); + }, +}; + +const resolve = promisify(dns.resolve); +const ping = async (host) => { + const lookup = promisify(dns.lookup); + const ip = await lookup(host); + const start = new Date(); + // Prevent timeouts from affecting user experience. + const requestPromise = new Promise((resolve, reject) => { + const req = http.get(`http://${ip.address}`, res => { + const delay = new Date().getTime() - start.getTime(); + res.resume(); + resolve(delay); + }); + req.on('error', err => { + reject(err); + }); + }); + const timeoutPromise = new Promise((_, reject) => { + setTimeout(() => { + reject(new Error('Request timed out after 3 seconds')); + }, 1000); + }); + return Promise.race([requestPromise, timeoutPromise]); +}; +async function isChinaDomain(domain) { + try { + const [ip] = await resolve(domain); + return await isChinaIP(ip, domain); + } + catch (error) { + logger.debug(`${domain} can't be parse!`); + return true; + } +} +async function isChinaIP(ip, domain) { + try { + const delay = await ping(ip); + logger.debug(`${domain} latency is ${delay} ms`); + return delay > 1000; + } + catch (error) { + logger.debug(`ping ${domain} failed!`); + return true; + } +} + +async function installRust() { + const isInChina = await isChinaDomain('sh.rustup.rs'); + const rustInstallScriptForMac = isInChina + ? 'export RUSTUP_DIST_SERVER="https://rsproxy.cn" && export RUSTUP_UPDATE_ROOT="https://rsproxy.cn/rustup" && curl --proto "=https" --tlsv1.2 -sSf https://rsproxy.cn/rustup-init.sh | sh' + : "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y"; + const rustInstallScriptForWindows = 'winget install --id Rustlang.Rustup'; + const spinner = getSpinner('Downloading Rust...'); + try { + await shellExec(IS_WIN ? rustInstallScriptForWindows : rustInstallScriptForMac); + spinner.succeed(chalk.green('Rust installed successfully!')); + } + catch (error) { + console.error('Error installing Rust:', error.message); + spinner.fail(chalk.red('Rust installation failed!')); + process.exit(1); + } +} +function checkRustInstalled() { + return shelljs.exec('rustc --version', { silent: true }).code === 0; +} + +async function mergeConfig(url, options, tauriConf) { + const { width, height, fullscreen, transparent, userAgent, showMenu, showSystemTray, systemTrayIcon, iterCopyFile, identifier, name, resizable = true, } = options; + const { platform } = process; + // Set Windows parameters. + const tauriConfWindowOptions = { + width, + height, + fullscreen, + transparent, + resizable, + }; + Object.assign(tauriConf.pake.windows[0], { url, ...tauriConfWindowOptions }); + tauriConf.package.productName = name; + tauriConf.tauri.bundle.identifier = identifier; + //Judge the type of URL, whether it is a file or a website. + const pathExists = await fsExtra.pathExists(url); + if (pathExists) { + logger.warn('✼ Your input might be a local file.'); + tauriConf.pake.windows[0].url_type = 'local'; + const fileName = path.basename(url); + const dirName = path.dirname(url); + const distDir = path.join(npmDirectory, 'dist'); + const distBakDir = path.join(npmDirectory, 'dist_bak'); + if (!iterCopyFile) { + const urlPath = path.join(distDir, fileName); + await fsExtra.copy(url, urlPath); + } + else { + fsExtra.moveSync(distDir, distBakDir, { overwrite: true }); + fsExtra.copySync(dirName, distDir, { overwrite: true }); + const filesToCopyBack = ['cli.js', 'about_pake.html']; + await Promise.all(filesToCopyBack.map(file => fsExtra.copy(path.join(distBakDir, file), path.join(distDir, file)))); + } + tauriConf.pake.windows[0].url = fileName; + tauriConf.pake.windows[0].url_type = 'local'; + } + else { + tauriConf.pake.windows[0].url_type = 'web'; + // Set the secure domain for calling window.__TAURI__ to the application domain that has been set. + tauriConf.tauri.security.dangerousRemoteDomainIpcAccess[0].domain = new URL(url).hostname; + } + const platformMap = { + win32: 'windows', + linux: 'linux', + darwin: 'macos', + }; + const currentPlatform = platformMap[platform]; + if (userAgent.length > 0) { + tauriConf.pake.user_agent[currentPlatform] = userAgent; + } + tauriConf.pake.menu[currentPlatform] = showMenu; + tauriConf.pake.system_tray[currentPlatform] = showSystemTray; + // Processing targets are currently only open to Linux. + if (platform === 'linux') { + delete tauriConf.tauri.bundle.deb.files; + const validTargets = ['all', 'deb', 'appimage']; + if (validTargets.includes(options.targets)) { + tauriConf.tauri.bundle.targets = + options.targets === 'all' ? ['deb', 'appimage'] : [options.targets]; + } + else { + logger.warn(`✼ The target must be one of ${validTargets.join(', ')}, the default 'deb' will be used.`); + } + } + // Set icon. + const platformIconMap = { + win32: { + fileExt: '.ico', + path: `png/${name.toLowerCase()}_256.ico`, + defaultIcon: 'png/icon_256.ico', + message: 'Windows icon must be .ico and 256x256px.', + }, + linux: { + fileExt: '.png', + path: `png/${name.toLowerCase()}_512.png`, + defaultIcon: 'png/icon_512.png', + message: 'Linux icon must be .png and 512x512px.', + }, + darwin: { + fileExt: '.icns', + path: `icons/${name.toLowerCase()}.icns`, + defaultIcon: 'icons/icon.icns', + message: 'macOS icon must be .icns type.', + }, + }; + const iconInfo = platformIconMap[platform]; + const exists = await fsExtra.pathExists(options.icon); + if (exists) { + let updateIconPath = true; + let customIconExt = path.extname(options.icon).toLowerCase(); + if (customIconExt !== iconInfo.fileExt) { + updateIconPath = false; + logger.warn(`✼ ${iconInfo.message}, but you give ${customIconExt}`); + tauriConf.tauri.bundle.icon = [iconInfo.defaultIcon]; + } + else { + const iconPath = path.join(npmDirectory, 'src-tauri/', iconInfo.path); + tauriConf.tauri.bundle.resources = [iconInfo.path]; + await fsExtra.copy(options.icon, iconPath); + } + if (updateIconPath) { + tauriConf.tauri.bundle.icon = [options.icon]; + } + else { + logger.warn(`✼ Icon will remain as default.`); + } + } + else { + logger.warn('✼ Custom icon path may be invalid, default icon will be used instead.'); + tauriConf.tauri.bundle.icon = [iconInfo.defaultIcon]; + } + // Set tray icon path. + let trayIconPath = platform === 'darwin' ? 'png/icon_512.png' : tauriConf.tauri.bundle.icon[0]; + if (systemTrayIcon.length > 0) { + try { + await fsExtra.pathExists(systemTrayIcon); + // 需要判断图标格式,默认只支持ico和png两种 + let iconExt = path.extname(systemTrayIcon).toLowerCase(); + if (iconExt == '.png' || iconExt == '.ico') { + const trayIcoPath = path.join(npmDirectory, `src-tauri/png/${name.toLowerCase()}${iconExt}`); + trayIconPath = `png/${name.toLowerCase()}${iconExt}`; + await fsExtra.copy(systemTrayIcon, trayIcoPath); + } + else { + logger.warn(`✼ System tray icon must be .ico or .png, but you provided ${iconExt}.`); + logger.warn(`✼ Default system tray icon will be used.`); + } + } + catch { + logger.warn(`✼ ${systemTrayIcon} not exists!`); + logger.warn(`✼ Default system tray icon will remain unchanged.`); + } + } + tauriConf.tauri.systemTray.iconPath = trayIconPath; + // Save config file. + const platformConfigPaths = { + win32: 'src-tauri/tauri.windows.conf.json', + darwin: 'src-tauri/tauri.macos.conf.json', + linux: 'src-tauri/tauri.linux.conf.json', + }; + const configPath = path.join(npmDirectory, platformConfigPaths[platform]); + const bundleConf = { tauri: { bundle: tauriConf.tauri.bundle } }; + await fsExtra.writeJson(configPath, bundleConf, { spaces: 4 }); + const pakeConfigPath = path.join(npmDirectory, 'src-tauri/pake.json'); + await fsExtra.writeJson(pakeConfigPath, tauriConf.pake, { spaces: 4 }); + let tauriConf2 = JSON.parse(JSON.stringify(tauriConf)); + delete tauriConf2.pake; + delete tauriConf2.tauri.bundle; + const configJsonPath = path.join(npmDirectory, 'src-tauri/tauri.conf.json'); + await fsExtra.writeJson(configJsonPath, tauriConf2, { spaces: 4 }); +} + +class BaseBuilder { + constructor(options) { + this.options = options; + } + async prepare() { + const tauriSrcPath = path.join(npmDirectory, 'src-tauri'); + const tauriTargetPath = path.join(tauriSrcPath, 'target'); + const tauriTargetPathExists = await fsExtra.pathExists(tauriTargetPath); + if (!IS_MAC && !tauriTargetPathExists) { + logger.info('✺ The first use requires installing system dependencies.'); + logger.info('✺ See more in https://tauri.app/v1/guides/getting-started/prerequisites.'); + } + if (!checkRustInstalled()) { + const res = await prompts({ + type: 'confirm', + message: 'Rust not detected. Install now?', + name: 'value', + }); + if (res.value) { + await installRust(); + } + else { + logger.error('✕ Rust required to package your webapp.'); + process.exit(0); + } + } + const isChina = await isChinaDomain('www.npmjs.com'); + const spinner = getSpinner('Installing package...'); + const rustProjectDir = path.join(tauriSrcPath, '.cargo'); + const projectConf = path.join(rustProjectDir, 'config'); + await fsExtra.ensureDir(rustProjectDir); + if (isChina) { + logger.info('✺ Located in China, using npm/rsProxy CN mirror.'); + const projectCnConf = path.join(tauriSrcPath, 'rust_proxy.toml'); + await fsExtra.copy(projectCnConf, projectConf); + await shellExec(`cd "${npmDirectory}" && npm install --registry=https://registry.npmmirror.com`); + } + else { + await shellExec(`cd "${npmDirectory}" && npm install`); + } + spinner.succeed(chalk.green('Package installed!')); + if (!tauriTargetPathExists) { + logger.warn('✼ The first packaging may be slow, please be patient and wait, it will be faster afterwards.'); + } + } + async build(url) { + await this.buildAndCopy(url, this.options.targets); + } + async buildAndCopy(url, target) { + const { name } = this.options; + await mergeConfig(url, this.options, tauriConfig); + // Build app + const spinner = getSpinner('Building app...'); + setTimeout(() => spinner.stop(), 3000); + await shellExec(`cd ${npmDirectory} && ${this.getBuildCommand()}`); + // Copy app + const fileName = this.getFileName(); + const fileType = this.getFileType(target); + const appPath = this.getBuildAppPath(npmDirectory, fileName, fileType); + const distPath = path.resolve(`${name}.${fileType}`); + await fsExtra.copy(appPath, distPath); + await fsExtra.remove(appPath); + logger.success('✔ Build success!'); + logger.success('✔ App installer located in', distPath); + } + getFileType(target) { + return target; + } + getBuildCommand() { + return 'npm run build'; + } + getBasePath() { + return 'src-tauri/target/release/bundle/'; + } + getBuildAppPath(npmDirectory, fileName, fileType) { + return path.join(npmDirectory, this.getBasePath(), fileType.toLowerCase(), `${fileName}.${fileType}`); + } +} + +class MacBuilder extends BaseBuilder { + constructor(options) { + super(options); + this.options.targets = 'dmg'; + } + getFileName() { + const { name } = this.options; + let arch; + if (this.options.multiArch) { + arch = 'universal'; + } + else { + arch = process.arch === 'arm64' ? 'aarch64' : process.arch; + } + return `${name}_${tauriConfig.package.version}_${arch}`; + } + getBuildCommand() { + return this.options.multiArch ? 'npm run build:mac' : super.getBuildCommand(); + } + getBasePath() { + return this.options.multiArch + ? 'src-tauri/target/universal-apple-darwin/release/bundle' + : super.getBasePath(); + } +} + +class WinBuilder extends BaseBuilder { + constructor(options) { + super(options); + this.options.targets = 'msi'; + } + getFileName() { + const { name } = this.options; + const { arch } = process; + const language = tauriConfig.tauri.bundle.windows.wix.language[0]; + return `${name}_${tauriConfig.package.version}_${arch}_${language}`; + } +} + +class LinuxBuilder extends BaseBuilder { + constructor(options) { + super(options); + } + getFileName() { + const { name } = this.options; + const arch = process.arch === 'x64' ? 'amd64' : process.arch; + return `${name}_${tauriConfig.package.version}_${arch}`; + } + // Customize it, considering that there are all targets. + async build(url) { + const targetTypes = ['deb', 'appimage']; + for (const target of targetTypes) { + if (this.options.targets === target || this.options.targets === 'all') { + await this.buildAndCopy(url, target); + } + } + } + getFileType(target) { + if (target === 'appimage') { + return 'AppImage'; + } + return super.getFileType(target); + } +} + +const { platform } = process; +const buildersMap = { + darwin: MacBuilder, + win32: WinBuilder, + linux: LinuxBuilder, +}; +class BuilderProvider { + static create(options) { + const Builder = buildersMap[platform]; + if (!Builder) { + throw new Error('The current system is not supported!'); + } + return new Builder(options); + } +} + +const DEFAULT_PAKE_OPTIONS = { + icon: '', + height: 780, + width: 1200, + fullscreen: false, + resizable: true, + transparent: false, + userAgent: '', + showMenu: false, + showSystemTray: false, + multiArch: false, + targets: 'deb', + iterCopyFile: false, + systemTrayIcon: '', + debug: false, +}; + +async function checkUpdateTips() { + updateNotifier({ pkg: packageJson, updateCheckInterval: 1000 * 60 }).notify(); +} + +async function handleIcon(options) { + if (options.icon) { + if (options.icon.startsWith('http')) { + return downloadIcon(options.icon); + } + else { + return path.resolve(options.icon); + } + } + else { + logger.warn('✼ No icon given, default in use. For a custom icon, use --icon option.'); + const iconPath = IS_WIN + ? 'src-tauri/png/icon_256.ico' + : IS_LINUX + ? 'src-tauri/png/icon_512.png' + : 'src-tauri/icons/icon.icns'; + return path.join(npmDirectory, iconPath); + } +} +async function downloadIcon(iconUrl) { + const spinner = getSpinner('Downloading icon...'); + try { + const iconResponse = await axios.get(iconUrl, { responseType: 'arraybuffer' }); + const iconData = await iconResponse.data; + if (!iconData) { + return null; + } + const fileDetails = await fileTypeFromBuffer(iconData); + if (!fileDetails) { + return null; + } + const { path: tempPath } = await dir(); + const iconPath = `${tempPath}/icon.${fileDetails.ext}`; + await fsExtra.outputFile(iconPath, iconData); + spinner.succeed(chalk.green('Icon downloaded successfully!')); + return iconPath; + } + catch (error) { + spinner.fail(chalk.red('Icon download failed!')); + if (error.response && error.response.status === 404) { + return null; + } + throw error; + } +} + +// Extracts the domain from a given URL. +function getDomain(inputUrl) { + try { + const url = new URL(inputUrl); + // Use PSL to parse domain names. + const parsed = psl.parse(url.hostname); + // If domain is available, split it and return the SLD. + if ('domain' in parsed && parsed.domain) { + return parsed.domain.split('.')[0]; + } + else { + return null; + } + } + catch (error) { + return null; + } +} +// Appends 'https://' protocol to the URL if not present. +function appendProtocol(inputUrl) { + try { + new URL(inputUrl); + return inputUrl; + } + catch { + return `https://${inputUrl}`; + } +} +// Normalizes the URL by ensuring it has a protocol and is valid. +function normalizeUrl(urlToNormalize) { + const urlWithProtocol = appendProtocol(urlToNormalize); + if (isUrl(urlWithProtocol)) { + return urlWithProtocol; + } + else { + throw new Error(`Your url "${urlWithProtocol}" is invalid`); + } +} + +function resolveAppName(name, platform) { + const domain = getDomain(name) || 'pake'; + return platform !== 'linux' ? capitalizeFirstLetter(domain) : domain; +} +function isValidName(name, platform) { + const platformRegexMapping = { + linux: /^[a-z0-9]+(-[a-z0-9]+)*$/, + default: /^[a-zA-Z0-9]+$/, + }; + const reg = platformRegexMapping[platform] || platformRegexMapping.default; + return !!name && reg.test(name); +} +async function handleOptions(options, url) { + const { platform } = process; + const isActions = process.env.GITHUB_ACTIONS; + let name = options.name; + const pathExists = await fsExtra.pathExists(url); + if (!options.name) { + const defaultName = pathExists ? '' : resolveAppName(url, platform); + const promptMessage = 'Enter your application name'; + const namePrompt = await promptText(promptMessage, defaultName); + name = namePrompt || defaultName; + } + if (!isValidName(name, platform)) { + const LINUX_NAME_ERROR = `✕ name should only include lowercase letters, numbers, and dashes, and must contain at least one lowercase letter. Examples: com-123-xxx, 123pan, pan123, weread, we-read.`; + const DEFAULT_NAME_ERROR = `✕ Name should only include letters and numbers, and must contain at least one letter. Examples: 123pan, 123Pan, Pan123, weread, WeRead, WERead.`; + const errorMsg = platform === 'linux' ? LINUX_NAME_ERROR : DEFAULT_NAME_ERROR; + logger.error(errorMsg); + if (isActions) { + name = resolveAppName(url, platform); + logger.warn(`✼ Inside github actions, use the default name: ${name}`); + } + else { + process.exit(1); + } + } + const appOptions = { + ...options, + name, + identifier: getIdentifier(url), + }; + appOptions.icon = await handleIcon(appOptions); + return appOptions; +} + +function validateNumberInput(value) { + const parsedValue = Number(value); + if (isNaN(parsedValue)) { + throw new InvalidArgumentError('Not a number.'); + } + return parsedValue; +} +function validateUrlInput(url) { + const isFile = fs.existsSync(url); + if (!isFile) { + try { + return normalizeUrl(url); + } + catch (error) { + throw new InvalidArgumentError(error.message); + } + } + return url; +} + 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) - .option('--inject [injects...]', 'inject .js or .css for this app', DEFAULT_PAKE_OPTIONS.inject) - .action((url, options) => __awaiter(void 0, void 0, void 0, function* () { - checkUpdateTips(); + .description(chalk.green('Pake can turn any webpage into a desktop app with Rust.')) + .usage('[url] [options]') + .showHelpAfterError(); +program + .argument('[url]', 'The web URL you want to package', validateUrlInput) + .option('--name ', 'Application name') + .option('--icon ', 'Application icon', DEFAULT_PAKE_OPTIONS.icon) + .option('--width ', 'Window width', validateNumberInput, DEFAULT_PAKE_OPTIONS.width) + .option('--height ', 'Window height', validateNumberInput, DEFAULT_PAKE_OPTIONS.height) + .option('--transparent', 'Only for Mac, hide title bar', DEFAULT_PAKE_OPTIONS.transparent) + .option('--fullscreen', 'Start in full screen', DEFAULT_PAKE_OPTIONS.fullscreen) + .option('--user-agent ', 'Custom user agent', DEFAULT_PAKE_OPTIONS.userAgent) + .option('--show-menu', 'Show menu in app', DEFAULT_PAKE_OPTIONS.showMenu) + .option('--show-system-tray', 'Show system tray in app', DEFAULT_PAKE_OPTIONS.showSystemTray) + .option('--system-tray-icon ', 'Custom system tray icon', DEFAULT_PAKE_OPTIONS.systemTrayIcon) + .option('--iter-copy-file', 'Copy files when URL is a local file', DEFAULT_PAKE_OPTIONS.iterCopyFile) + .option('--multi-arch', 'Only for Mac, supports both Intel and M1', DEFAULT_PAKE_OPTIONS.multiArch) + .option('--targets ', 'Only for Linux, option "deb" or "appimage"', DEFAULT_PAKE_OPTIONS.targets) + .option('--debug', 'Debug mode', DEFAULT_PAKE_OPTIONS.debug) + .version(packageJson.version, '-v, --version', 'Output the current version') + .action(async (url, options) => { + await checkUpdateTips(); if (!url) { - // 直接 pake 不需要出现url提示 - program.help(); + program.outputHelp(str => { + return str + .split('\n') + .filter(line => !/((-h,|--help)|((-v|-V),|--version))\s+.+$/.test(line)) + .join('\n'); + }); + process.exit(0); } log.setDefaultLevel('info'); if (options.debug) { log.setLevel('debug'); } - const 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); -})); + const appOptions = await handleOptions(options, url); + log.debug('PakeAppOptions', appOptions); + const builder = BuilderProvider.create(appOptions); + await builder.prepare(); + await builder.build(url); +}); program.parse(); diff --git a/icns2png.py b/icns2png.py index cf9a998..6c749fa 100644 --- a/icns2png.py +++ b/icns2png.py @@ -10,7 +10,6 @@ except ImportError: os.system("pip install Pillow") from PIL import Image - if __name__ == "__main__": now_dir = os.path.dirname(os.path.abspath(__file__)) icons_dir = os.path.join(now_dir, "src-tauri", "icons") @@ -34,5 +33,3 @@ if __name__ == "__main__": image_32.save(image_32_path, "ICO") print("png file write success.") print(f"There are {len(os.listdir(png_dir))} png picture in ", png_dir) - - diff --git a/package.json b/package.json index 4a133bd..de6933a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pake-cli", - "version": "2.0.6", + "version": "2.1.8", "description": "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。", "engines": { "node": ">=16.0.0" @@ -46,17 +46,18 @@ "exports": "./dist/pake.js", "license": "MIT", "dependencies": { - "@tauri-apps/api": "^1.3.0", - "@tauri-apps/cli": "^1.3.1", + "@tauri-apps/api": "^1.4.0", + "@tauri-apps/cli": "^1.4.0", "axios": "^1.1.3", "chalk": "^5.1.2", - "commander": "^9.4.1", + "commander": "^11.0.0", "file-type": "^18.0.0", "fs-extra": "^11.1.0", "is-url": "^1.2.4", "loglevel": "^1.8.1", "ora": "^6.1.2", "prompts": "^2.4.2", + "psl": "^1.9.0", "shelljs": "^0.8.5", "tmp-promise": "^3.0.3", "update-notifier": "^6.0.2" @@ -64,21 +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", - "concurrently": "^7.5.0", "cross-env": "^7.0.3", "rollup": "^3.3.0", - "tauri": "^0.15.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..05bdbc6 100644 --- a/script/build.bat +++ b/script/build.bat @@ -51,7 +51,7 @@ for /f "skip=1 tokens=1-4 delims=," %%i in (app.csv) do ( ::echo name is !name! !name_zh! !url! :: replace url .\script\sd.exe -s !old_url! !url! src-tauri\pake.json - ::replace pacakge name + ::replace package name .\script\sd.exe !old_title! !title! src-tauri\tauri.conf.json .\script\sd.exe !old_name! !name! src-tauri\tauri.conf.json .\script\sd.exe !old_name! !name! src-tauri\tauri.windows.conf.json @@ -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/Cargo.lock b/src-tauri/Cargo.lock index fc766e4..1d26ec6 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -151,24 +151,6 @@ dependencies = [ "event-listener", ] -[[package]] -name = "async-process" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" -dependencies = [ - "async-io", - "async-lock", - "autocfg", - "blocking", - "cfg-if 1.0.0", - "event-listener", - "futures-lite", - "rustix", - "signal-hook", - "windows-sys 0.48.0", -] - [[package]] name = "async-recursion" version = "1.0.4" @@ -227,22 +209,6 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" -[[package]] -name = "attohttpc" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fcf00bc6d5abb29b5f97e3c61a90b6d3caa12f3faf897d4a3e3607c050a35a7" -dependencies = [ - "flate2", - "http", - "log", - "native-tls", - "serde", - "serde_json", - "serde_urlencoded", - "url", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -284,9 +250,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6776fc96284a0bb647b615056fc496d1fe1644a7ab01829818a6d91cae888b84" +checksum = "6dbe3c979c178231552ecba20214a8272df4e09f232a87aef4320cf06539aded" [[package]] name = "block" @@ -378,6 +344,9 @@ name = "bytes" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +dependencies = [ + "serde", +] [[package]] name = "cairo-rs" @@ -426,7 +395,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "599aa35200ffff8f04c1925aa1acc92fa2e08874379ef42e210a80e527e60838" dependencies = [ "serde", - "toml 0.7.3", + "toml 0.7.4", ] [[package]] @@ -449,7 +418,7 @@ checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" dependencies = [ "byteorder", "fnv", - "uuid 1.3.3", + "uuid 1.3.4", ] [[package]] @@ -614,9 +583,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" dependencies = [ "libc", ] @@ -642,9 +611,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if 1.0.0", ] @@ -696,12 +665,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "cty" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" - [[package]] name = "darling" version = "0.20.1" @@ -805,7 +768,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c47d9680cf49ce700aba2ea4991f745fbe06b54b2262ea6bf8e1798f436b2bad" dependencies = [ "indicatif", - "reqwest", + "reqwest 0.10.10", "tokio 0.2.25", ] @@ -838,7 +801,7 @@ checksum = "80663502655af01a2902dff3f06869330782267924bf1788410b74edcd93770a" dependencies = [ "cc", "rustc_version", - "toml 0.7.3", + "toml 0.7.4", "vswhom", "winreg 0.11.0", ] @@ -1426,11 +1389,30 @@ dependencies = [ "indexmap", "slab", "tokio 0.2.25", - "tokio-util", + "tokio-util 0.3.1", "tracing", "tracing-futures", ] +[[package]] +name = "h2" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" +dependencies = [ + "bytes 1.4.0", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio 1.28.2", + "tokio-util 0.7.8", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1508,6 +1490,17 @@ dependencies = [ "http", ] +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes 1.4.0", + "http", + "pin-project-lite 0.2.9", +] + [[package]] name = "http-range" version = "0.1.5" @@ -1526,6 +1519,12 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + [[package]] name = "hyper" version = "0.13.10" @@ -1536,11 +1535,11 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.2.7", "http", - "http-body", + "http-body 0.3.1", "httparse", - "httpdate", + "httpdate 0.3.2", "itoa 0.4.8", "pin-project", "socket2 0.3.19", @@ -1550,6 +1549,30 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "0.14.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +dependencies = [ + "bytes 1.4.0", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.19", + "http", + "http-body 0.4.5", + "httparse", + "httpdate 1.0.2", + "itoa 1.0.6", + "pin-project-lite 0.2.9", + "socket2 0.4.9", + "tokio 1.28.2", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper-tls" version = "0.4.3" @@ -1557,17 +1580,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d979acc56dcb5b8dddba3917601745e877576475aa046df3226eabdecef78eed" dependencies = [ "bytes 0.5.6", - "hyper", + "hyper 0.13.10", "native-tls", "tokio 0.2.25", "tokio-tls", ] [[package]] -name = "iana-time-zone" -version = "0.1.56" +name = "hyper-tls" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes 1.4.0", + "hyper 0.14.26", + "native-tls", + "tokio 1.28.2", + "tokio-native-tls", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1614,11 +1650,10 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.18" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" +checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" dependencies = [ - "crossbeam-utils", "globset", "lazy_static", "log", @@ -1767,9 +1802,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" -version = "0.3.63" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -1887,9 +1922,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.18" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "loom" @@ -2041,6 +2076,17 @@ dependencies = [ "winapi 0.2.8", ] +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + [[package]] name = "mio-named-pipes" version = "0.1.7" @@ -2048,7 +2094,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656" dependencies = [ "log", - "mio", + "mio 0.6.23", "miow 0.3.7", "winapi 0.3.9", ] @@ -2061,7 +2107,7 @@ checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" dependencies = [ "iovec", "libc", - "mio", + "mio 0.6.23", ] [[package]] @@ -2139,9 +2185,9 @@ dependencies = [ [[package]] name = "net2" -version = "0.2.38" +version = "0.2.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d0df99cfcd2530b2e694f6e17e7f37b8e26bb23983ac530c0c97408837c631" +checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac" dependencies = [ "cfg-if 0.1.10", "libc", @@ -2257,15 +2303,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "num_threads" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" -dependencies = [ - "libc", -] - [[package]] name = "number_prefix" version = "0.3.0" @@ -2649,9 +2686,9 @@ dependencies = [ [[package]] name = "png" -version = "0.17.8" +version = "0.17.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaeebc51f9e7d2c150d3f3bfeb667f2aa985db5ef1e3d212847bdedb488beeaa" +checksum = "59871cc5b6cce7eaccca5a802b4173377a1c2ba90654246789a8fa2334426d11" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -2730,9 +2767,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.59" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" +checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" dependencies = [ "unicode-ident", ] @@ -2847,12 +2884,9 @@ dependencies = [ [[package]] name = "raw-window-handle" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed7e3d950b66e19e0c372f3fa3fbbcf85b1746b571f74e0c2af6042a5c93420a" -dependencies = [ - "cty", -] +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" [[package]] name = "redox_syscall" @@ -2927,9 +2961,9 @@ dependencies = [ "futures-core", "futures-util", "http", - "http-body", - "hyper", - "hyper-tls", + "http-body 0.3.1", + "hyper 0.13.10", + "hyper-tls 0.4.3", "ipnet", "js-sys", "lazy_static", @@ -2950,6 +2984,45 @@ dependencies = [ "winreg 0.7.0", ] +[[package]] +name = "reqwest" +version = "0.11.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" +dependencies = [ + "base64 0.21.2", + "bytes 1.4.0", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.3.19", + "http", + "http-body 0.4.5", + "hyper 0.14.26", + "hyper-tls 0.5.0", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite 0.2.9", + "serde", + "serde_json", + "serde_urlencoded", + "tokio 1.28.2", + "tokio-native-tls", + "tokio-util 0.7.8", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "winreg 0.10.1", +] + [[package]] name = "rfd" version = "0.10.0" @@ -2985,9 +3058,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.19" +version = "0.37.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" dependencies = [ "bitflags 1.3.2", "errno", @@ -3105,18 +3178,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.163" +version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.163" +version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" dependencies = [ "proc-macro2", "quote", @@ -3125,9 +3198,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "bdf3bf93142acad5821c99197022e170842cdbc1c30482b98750c688c640842a" dependencies = [ "itoa 1.0.6", "ryu", @@ -3168,11 +3241,11 @@ dependencies = [ [[package]] name = "serde_with" -version = "2.3.3" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe" +checksum = "9f02d8aa6e3c385bf084924f660ce2a3a6bd333ba55b35e8590b321f35d88513" dependencies = [ - "base64 0.13.1", + "base64 0.21.2", "chrono", "hex", "indexmap", @@ -3184,9 +3257,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "2.3.3" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" +checksum = "edc7d5d3932fb12ce722ee5e64dd38c504efba37567f0c402f6ca728c3b8b070" dependencies = [ "darling", "proc-macro2", @@ -3239,9 +3312,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if 1.0.0", "cpufeatures", @@ -3267,16 +3340,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "signal-hook" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" -dependencies = [ - "libc", - "signal-hook-registry", -] - [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -3415,27 +3478,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "strum" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7ac893c7d471c8a21f31cfe213ec4f6d9afeed25537c772e08ef3f005f8729e" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "339f799d8b549e3744c7ac7feb216383e4005d94bdb22561b3ab8f3b808ae9fb" -dependencies = [ - "heck 0.3.3", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "syn" version = "1.0.109" @@ -3458,6 +3500,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sys-locale" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8a11bd9c338fdba09f7881ab41551932ad42e405f61d01e8406baea71c07aee" +dependencies = [ + "js-sys", + "libc", + "wasm-bindgen", + "web-sys", + "windows-sys 0.45.0", +] + [[package]] name = "system-deps" version = "5.0.0" @@ -3480,7 +3535,7 @@ dependencies = [ "cfg-expr 0.15.2", "heck 0.4.1", "pkg-config", - "toml 0.7.3", + "toml 0.7.4", "version-compare 0.1.1", ] @@ -3527,7 +3582,7 @@ dependencies = [ "serde", "tao-macros", "unicode-segmentation", - "uuid 1.3.3", + "uuid 1.3.4", "windows 0.39.0", "windows-implement", "x11-dl", @@ -3563,12 +3618,12 @@ checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5" [[package]] name = "tauri" -version = "1.3.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d42ba3a2e8556722f31336a0750c10dbb6a81396a1c452977f515da83f69f842" +checksum = "7fbe522898e35407a8e60dc3870f7579fea2fc262a6a6072eccdd37ae1e1d91e" dependencies = [ "anyhow", - "attohttpc", + "bytes 1.4.0", "cocoa", "dirs-next", "embed_plist", @@ -3591,6 +3646,7 @@ dependencies = [ "rand 0.8.5", "raw-window-handle", "regex", + "reqwest 0.11.18", "rfd", "semver", "serde", @@ -3599,6 +3655,7 @@ dependencies = [ "serialize-to-javascript", "shared_child", "state", + "sys-locale", "tar", "tauri-macros", "tauri-runtime", @@ -3608,7 +3665,7 @@ dependencies = [ "thiserror", "tokio 1.28.2", "url", - "uuid 1.3.3", + "uuid 1.3.4", "webkit2gtk", "webview2-com", "windows 0.39.0", @@ -3616,9 +3673,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "929b3bd1248afc07b63e33a6a53c3f82c32d0b0a5e216e4530e94c467e019389" +checksum = "7d2edd6a259b5591c8efdeb9d5702cb53515b82a6affebd55c7fd6d3a27b7d1b" dependencies = [ "anyhow", "cargo_toml", @@ -3629,14 +3686,13 @@ dependencies = [ "serde_json", "tauri-utils", "tauri-winres", - "winnow", ] [[package]] name = "tauri-codegen" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a2105f807c6f50b2fa2ce5abd62ef207bc6f14c9fcc6b8caec437f6fb13bde" +checksum = "54ad2d49fdeab4a08717f5b49a163bdc72efc3b1950b6758245fcde79b645e1a" dependencies = [ "base64 0.21.2", "brotli", @@ -3654,15 +3710,15 @@ dependencies = [ "tauri-utils", "thiserror", "time", - "uuid 1.3.3", + "uuid 1.3.4", "walkdir", ] [[package]] name = "tauri-macros" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8784cfe6f5444097e93c69107d1ac5e8f13d02850efa8d8f2a40fe79674cef46" +checksum = "8eb12a2454e747896929338d93b0642144bb51e0dddbb36e579035731f0d76b7" dependencies = [ "heck 0.4.1", "proc-macro2", @@ -3678,7 +3734,7 @@ version = "0.1.0" source = "git+https://github.com/tauri-apps/plugins-workspace?branch=dev#dce0f02bc571128308c30278cde3233f341e6a50" dependencies = [ "bincode", - "bitflags 2.3.1", + "bitflags 2.3.2", "log", "serde", "serde_json", @@ -3688,9 +3744,9 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3b80ea3fcd5fefb60739a3b577b277e8fc30434538a2f5bba82ad7d4368c422" +checksum = "108683199cb18f96d2d4134187bb789964143c845d2d154848dda209191fd769" dependencies = [ "gtk", "http", @@ -3702,16 +3758,16 @@ dependencies = [ "tauri-utils", "thiserror", "url", - "uuid 1.3.3", + "uuid 1.3.4", "webview2-com", "windows 0.39.0", ] [[package]] name = "tauri-runtime-wry" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1c396950b1ba06aee1b4ffe6c7cd305ff433ca0e30acbc5fa1a2f92a4ce70f1" +checksum = "0b7aa256a1407a3a091b5d843eccc1a5042289baf0a43d1179d9f0fcfea37c1b" dependencies = [ "cocoa", "gtk", @@ -3720,7 +3776,7 @@ dependencies = [ "raw-window-handle", "tauri-runtime", "tauri-utils", - "uuid 1.3.3", + "uuid 1.3.4", "webkit2gtk", "webview2-com", "windows 0.39.0", @@ -3729,12 +3785,13 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6f9c2dafef5cbcf52926af57ce9561bd33bb41d7394f8bb849c0330260d864" +checksum = "03fc02bb6072bb397e1d473c6f76c953cda48b4a2d0cce605df284aa74a12e84" dependencies = [ "brotli", "ctor", + "dunce", "glob", "heck 0.4.1", "html5ever", @@ -3762,17 +3819,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5993dc129e544393574288923d1ec447c857f3f644187f4fbf7d9a875fbfc4fb" dependencies = [ "embed-resource", - "toml 0.7.3", + "toml 0.7.4", ] [[package]] name = "tauri-winrt-notification" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58de036c4d2e20717024de2a3c4bf56c301f07b21bc8ef9b57189fce06f1f3b" +checksum = "37d70573554e7630c2ca3677ea78d5ae6b030aedee5f9bf33c15d644904fa698" dependencies = [ "quick-xml 0.23.1", - "strum", "windows 0.39.0", ] @@ -3849,14 +3905,29 @@ dependencies = [ [[package]] name = "time" -version = "0.3.15" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d634a985c4d4238ec39cacaed2e7ae552fbd3c476b552c1deac3021b7d7eaf0c" +checksum = "ea9e1b3cf1243ae005d9e74085d4d542f3125458f3a81af210d901dcd7411efd" dependencies = [ "itoa 1.0.6", - "libc", - "num_threads", "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" + +[[package]] +name = "time-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +dependencies = [ + "time-core", ] [[package]] @@ -3887,7 +3958,7 @@ dependencies = [ "lazy_static", "libc", "memchr", - "mio", + "mio 0.6.23", "mio-named-pipes", "mio-uds", "num_cpus", @@ -3906,8 +3977,11 @@ checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" dependencies = [ "autocfg", "bytes 1.4.0", + "libc", + "mio 0.8.8", "num_cpus", "pin-project-lite 0.2.9", + "socket2 0.4.9", "windows-sys 0.48.0", ] @@ -3922,6 +3996,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio 1.28.2", +] + [[package]] name = "tokio-tls" version = "0.3.1" @@ -3946,6 +4030,20 @@ dependencies = [ "tokio 0.2.25", ] +[[package]] +name = "tokio-util" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +dependencies = [ + "bytes 1.4.0", + "futures-core", + "futures-sink", + "pin-project-lite 0.2.9", + "tokio 1.28.2", + "tracing", +] + [[package]] name = "toml" version = "0.5.11" @@ -3957,9 +4055,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21" +checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec" dependencies = [ "serde", "serde_spanned", @@ -3978,9 +4076,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.19.8" +version = "0.19.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" +checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" dependencies = [ "indexmap", "serde", @@ -4167,9 +4265,9 @@ checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" [[package]] name = "uuid" -version = "1.3.3" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2" +checksum = "0fa2982af2eec27de306107c027578ff7f423d65f7250e40ce0fea8f45248b81" dependencies = [ "getrandom 0.2.10", ] @@ -4242,11 +4340,10 @@ dependencies = [ [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -4264,9 +4361,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if 1.0.0", "serde", @@ -4276,9 +4373,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", @@ -4291,9 +4388,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.36" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d1985d03709c53167ce907ff394f5316aa22cb4e12761295c5dc57dacb6297e" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -4303,9 +4400,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4313,9 +4410,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", @@ -4326,15 +4423,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "wasm-streams" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bbae3363c08332cadccd13b67db371814cd214c2524020932f0804b8cf7c078" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] [[package]] name = "web-sys" -version = "0.3.63" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -4745,9 +4855,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.1" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" +checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448" dependencies = [ "memchr", ] @@ -4761,6 +4871,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "winreg" version = "0.11.0" @@ -4861,16 +4980,15 @@ dependencies = [ [[package]] name = "zbus" -version = "3.13.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c3d77c9966c28321f1907f0b6c5a5561189d1f7311eea6d94180c6be9daab29" +checksum = "29242fa5ec5693629ae74d6eb1f69622a9511f600986d6d9779bccf36ac316e3" dependencies = [ "async-broadcast", "async-executor", "async-fs", "async-io", "async-lock", - "async-process", "async-recursion", "async-task", "async-trait", @@ -4901,16 +5019,15 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "3.13.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e341d12edaff644e539ccbbf7f161601294c9a84ed3d7e015da33155b435af" +checksum = "537793e26e9af85f774801dc52c6f6292352b2b517c5cf0449ffd3735732a53a" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", "regex", "syn 1.0.109", - "winnow", "zvariant_utils", ] @@ -4927,9 +5044,9 @@ dependencies = [ [[package]] name = "zvariant" -version = "3.14.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622cc473f10cef1b0d73b7b34a266be30ebdcfaea40ec297dd8cbda088f9f93c" +checksum = "5cb36cd95352132911c9c99fdcc1635de5c2c139bd34cbcf6dfb8350ee8ff6a7" dependencies = [ "byteorder", "enumflags2", @@ -4941,9 +5058,9 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "3.14.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d9c1b57352c25b778257c661f3c4744b7cefb7fc09dd46909a153cce7773da2" +checksum = "9b34951e1ac64f3a1443fe7181256b9ed6a811a1631917566c3d5ca718d8cf33" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -4954,9 +5071,9 @@ dependencies = [ [[package]] name = "zvariant_utils" -version = "1.0.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" +checksum = "53b22993dbc4d128a17a3b6c92f1c63872dd67198537ee728d8b5d7c40640a8b" dependencies = [ "proc-macro2", "quote", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index a45e9cc..c7719d1 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -12,12 +12,12 @@ rust-version = "1.63.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [build-dependencies] -tauri-build = { version = "1.3.0", features = [] } +tauri-build = { version = "1.4.0", features = [] } [dependencies] serde_json = "1.0.96" serde = { version = "1.0.163", features = ["derive"] } -tauri = { version = "1.3.0", features = ["api-all", "system-tray"] } +tauri = { version = "1.4.1", features = ["api-all", "system-tray"] } download_rs = { version = "0.2.0", features = ["sync_download"] } tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "dev" } diff --git a/src-tauri/info.plist b/src-tauri/info.plist new file mode 100644 index 0000000..7d3ae94 --- /dev/null +++ b/src-tauri/info.plist @@ -0,0 +1,9 @@ + + + + NSCameraUsageDescription + Request camera access + NSMicrophoneUsageDescription + Request microphone access + + 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/src-tauri/src/inject/component.js b/src-tauri/src/inject/component.js index ba9824c..f77a975 100644 --- a/src-tauri/src/inject/component.js +++ b/src-tauri/src/inject/component.js @@ -136,8 +136,7 @@ document.addEventListener('DOMContentLoaded', () => { document.body.appendChild(m); setTimeout(function () { const d = 0.5; - m.style.transition = - 'transform ' + d + 's ease-in, opacity ' + d + 's ease-in'; + m.style.transition = 'transform ' + d + 's ease-in, opacity ' + d + 's ease-in'; m.style.opacity = '0'; setTimeout(function () { document.body.removeChild(m); @@ -146,4 +145,36 @@ document.addEventListener('DOMContentLoaded', () => { } window.pakeToast = pakeToast; + + // chatgpt supports unlimited times of GPT4-Mobile + if (window.location.hostname === 'chat.openai.com') { + const originFetch = fetch; + window.fetch = (url, options) => { + return originFetch(url, options).then(async response => { + if (url.indexOf('/backend-api/models') === -1) { + return response; + } + const responseClone = response.clone(); + let res = await responseClone.json(); + res.models = res.models.map(m => { + m.tags = m.tags.filter(t => { + return t !== 'mobile'; + }); + if (m.slug === 'gpt-4-mobile') { + res.categories.push({ + browsing_model: null, + category: 'gpt_4', + code_interpreter_model: null, + default_model: 'gpt-4-mobile', + human_category_name: 'GPT-4-Mobile', + plugins_model: null, + subscription_level: 'plus', + }); + } + return m; + }); + return new Response(JSON.stringify(res), response); + }); + }; + } }); diff --git a/src-tauri/src/inject/event.js b/src-tauri/src/inject/event.js index 41ffde8..2beca4d 100644 --- a/src-tauri/src/inject/event.js +++ b/src-tauri/src/inject/event.js @@ -82,14 +82,14 @@ async function invoke(cmd, args) { // Judgment of file download. function isDownloadLink(url) { - const fileExtensions = [ - '3gp', '7z', 'ai', 'apk', 'avi', 'bmp', 'csv', 'dmg', 'doc', 'docx', 'fla', 'flv', 'gif', 'gz', 'gzip', - 'ico', 'iso', 'indd', 'jar', 'jpeg', 'jpg', 'm3u8', 'mov', 'mp3', 'mp4', 'mpa', 'mpg', - 'mpeg', 'msi', 'odt', 'ogg', 'ogv', 'pdf', 'png', 'ppt', 'pptx', 'psd', 'rar', 'raw', 'rss', 'svg', - 'swf', 'tar', 'tif', 'tiff', 'ts', 'txt', 'wav', 'webm', 'webp', 'wma', 'wmv', 'xls', 'xlsx', 'xml', 'zip' - ]; - const downloadLinkPattern = new RegExp(`\\.(${fileExtensions.join('|')})$`, 'i'); - return downloadLinkPattern.test(url); + const fileExtensions = [ + '3gp', '7z', 'ai', 'apk', 'avi', 'bmp', 'csv', 'dmg', 'doc', 'docx', 'fla', 'flv', 'gif', 'gz', 'gzip', + 'ico', 'iso', 'indd', 'jar', 'jpeg', 'jpg', 'm3u8', 'mov', 'mp3', 'mp4', 'mpa', 'mpg', + 'mpeg', 'msi', 'odt', 'ogg', 'ogv', 'pdf', 'png', 'ppt', 'pptx', 'psd', 'rar', 'raw', 'rss', 'svg', + 'swf', 'tar', 'tif', 'tiff', 'ts', 'txt', 'wav', 'webm', 'webp', 'wma', 'wmv', 'xls', 'xlsx', 'xml', 'zip', + ]; + const downloadLinkPattern = new RegExp(`\\.(${fileExtensions.join('|')})$`, 'i'); + return downloadLinkPattern.test(url); } // No need to go to the download link. @@ -114,17 +114,17 @@ document.addEventListener('DOMContentLoaded', () => { domEl.addEventListener('mousedown', (e) => { e.preventDefault(); if (e.buttons === 1 && e.detail !== 2) { - appWindow.startDragging(); + appWindow.startDragging().then(); } }); domEl.addEventListener('touchstart', () => { - appWindow.startDragging(); + appWindow.startDragging().then(); }); domEl.addEventListener('dblclick', () => { appWindow.isFullscreen().then((fullscreen) => { - appWindow.setFullscreen(!fullscreen); + appWindow.setFullscreen(!fullscreen).then(); }); }); @@ -156,6 +156,7 @@ document.addEventListener('DOMContentLoaded', () => { } let filename = anchorElement.download || getFilenameFromUrl(absoluteUrl); + // Process download links for Rust to handle. // If the download attribute is set, the download attribute is used as the file name. if ( @@ -184,7 +185,7 @@ document.addEventListener('DOMContentLoaded', () => { // Rewrite the window.open function. const originalWindowOpen = window.open; - window.open = function (url, name, specs) { + window.open = function(url, name, specs) { // Apple login and google login if (name === 'AppleAuthentication') { //do nothing @@ -261,31 +262,34 @@ function convertBlobUrlToBinary(blobUrl) { }); } +function downloadFromBlobUrl(blobUrl, filename) { + const tauri = window.__TAURI__; + convertBlobUrlToBinary(blobUrl).then((binary) => { + console.log('binary', binary); + tauri.fs.writeBinaryFile(filename, binary, { + dir: tauri.fs.BaseDirectory.Download, + }).then(() => { + window.pakeToast('Download successful, saved to download directory~'); + }); + }); +} + // detect blob download by createElement("a") function detectDownloadByCreateAnchor() { const createEle = document.createElement; const tauri = window.__TAURI__; document.createElement = (el) => { - if (el !== "a") return createEle.call(document, el); + if (el !== 'a') return createEle.call(document, el); const anchorEle = createEle.call(document, el); - const anchorClick = anchorEle.click; - Object.defineProperties(anchorEle, { - click: { - get: () => { - if (anchorEle.href && anchorEle.href.includes('blob:')) { - const url = anchorEle.href; - convertBlobUrlToBinary(url).then((binary) => { - tauri.fs.writeBinaryFile(anchorEle.download || getFilenameFromUrl(url), binary, { - dir: tauri.fs.BaseDirectory.Download, - }); - }); - } - return anchorClick.bind(anchorEle); - } + // use addEventListener to avoid overriding the original click event. + anchorEle.addEventListener('click', () => { + const url = anchorEle.href; + if (window.blobToUrlCaches.has(url)) { + downloadFromBlobUrl(url, anchorEle.download || getFilenameFromUrl(url)); } - }) + }); return anchorEle; - } + }; } diff --git a/src-tauri/src/inject/style.js b/src-tauri/src/inject/style.js index 7c34aa3..03730c3 100644 --- a/src-tauri/src/inject/style.js +++ b/src-tauri/src/inject/style.js @@ -1,4 +1,4 @@ -window.addEventListener('DOMContentLoaded', (_event) => { +window.addEventListener('DOMContentLoaded', _event => { const css = ` #page #footer-wrapper, .drawing-board .toolbar .toolbar-action, @@ -56,6 +56,10 @@ window.addEventListener('DOMContentLoaded', (_event) => { padding-top: 20px; } + #__next > div.overflow-hidden.w-full.h-full .min-h-\\[20px\\].items-start.gap-4.whitespace-pre-wrap.break-words { + word-break: break-all; + } + #__next .PageWithSidebarLayout_mainSection__i1yOg { width: 100%; max-width: 1000px; @@ -312,7 +316,8 @@ window.addEventListener('DOMContentLoaded', (_event) => { } @media (min-width:1024px){ - #__next .text-base.lg\\:max-w-xl, #__next form.stretch.lg\\:max-w-2xl { + #__next .text-base.lg\\:max-w-xl, #__next form.stretch.lg\\:max-w-2xl, + #__next > .w-full.h-full .lg\\:max-w-\\[38rem\\] { max-width: 44rem; } } @@ -327,6 +332,20 @@ window.addEventListener('DOMContentLoaded', (_event) => { #__next .overflow-hidden.w-full .max-w-full>.sticky.top-0 { padding-top: 20px; } + + #__next .overflow-hidden.w-full main.relative.h-full.w-full.flex-1{ + padding-bottom: 82px; + } + + #__next > div.overflow-hidden.w-full.h-full main.relative.h-full.w-full.flex-1 > .flex-1.overflow-hidden .h-32.md\\:h-48.flex-shrink-0{ + height: 0px; + } + } + + @media (max-width:565px){ + #__next .overflow-hidden.w-full main.relative.h-full.w-full.flex-1{ + padding-bottom: 98px; + } } #__next .prose ol li p { diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index b871205..bf8bcc4 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,34 +1,34 @@ { - "package": { - "productName": "WeRead", - "version": "1.0.0" + "package": { + "productName": "WeRead", + "version": "1.0.0" + }, + "tauri": { + "security": { + "csp": null }, - "tauri": { - "security": { - "csp": null - }, - "updater": { - "active": false - }, - "systemTray": { - "iconPath": "png/weread_512.png", - "iconAsTemplate": true - }, - "allowlist": { - "all": true, - "fs": { - "all": true, - "scope": [ - "$DOWNLOAD/*" - ] - } - } + "updater": { + "active": false }, - "build": { - "withGlobalTauri": true, - "devPath": "../dist", - "distDir": "../dist", - "beforeBuildCommand": "", - "beforeDevCommand": "" + "systemTray": { + "iconPath": "png/weread_512.png", + "iconAsTemplate": true + }, + "allowlist": { + "all": true, + "fs": { + "all": true, + "scope": [ + "$DOWNLOAD/*" + ] + } } -} + }, + "build": { + "withGlobalTauri": true, + "devPath": "../dist", + "distDir": "../dist", + "beforeBuildCommand": "", + "beforeDevCommand": "" + } +} \ No newline at end of file diff --git a/src-tauri/tauri.linux.conf.json b/src-tauri/tauri.linux.conf.json index e0993fb..d9db8ea 100644 --- a/src-tauri/tauri.linux.conf.json +++ b/src-tauri/tauri.linux.conf.json @@ -8,7 +8,9 @@ "copyright": "", "deb": { "depends": ["curl", "wget"], - "files": {"/usr/share/applications/com-tw93-weread.desktop": "assets/com-tw93-weread.desktop"} + "files": { + "/usr/share/applications/com-tw93-weread.desktop": "assets/com-tw93-weread.desktop" + } }, "externalBin": [], "longDescription": "", diff --git a/tsconfig.json b/tsconfig.json index d5cb79e..c8d6d40 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,17 +1,29 @@ { "compilerOptions": { - "module": "Node16", + "module": "ESNext", + "target": "es2020", + "types": [ + "node" + ], + "lib": [ + "es2020", + "dom" + ], "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/**/*" + ] }