diff --git a/.gitignore b/.gitignore
index 66c28c6..e7aca0a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,7 @@ dist-ssr
# Editor directories and files
.vscode/*
!.vscode/extensions.json
+!.vscode/settings.json
.idea
.DS_Store
*.suo
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..4b0fe8e
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,8 @@
+{
+ "cSpell.words": [
+ "loglevel",
+ "Pake",
+ "tauri"
+ ],
+ "typescript.preferences.importModuleSpecifierEnding": "js"
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index 8d90bae..74e676e 100644
--- a/README.md
+++ b/README.md
@@ -82,8 +82,28 @@
## 注意点
-- Windows 下不能安装到 C:\Program File,会直接闪退。建议安装到其他目录,比如 D:\Program Files。
-- Linux 下暂时不能存 cookie,即应用关闭后数据清空,账号自动推出。
+- Windows 下不能安装到 C:\Program File,会直接闪退。建议安装到其他目录,比如 D:\Program Files。
+- Linux 下暂时不能存 cookie,即应用关闭后数据清空,账号自动推出。
+
+## 使用命令行打包
+
+Pake 提供了命令行工具,可以更快捷方便地打包。(目前仅支持 MacOS)
+
+### 安装
+
+```bash
+npm install -g pake
+```
+
+如果安装失败提示没有权限,请使用 `sudo` 运行。
+
+### 用法
+
+```bash
+pake [options] url
+```
+
+更多用法可查看[文档](./bin//README.md)。
## 开发
@@ -114,6 +134,8 @@ chmod +x ./script/build.sh && ./script/build.sh
## 打新包
+### 自己构建
+
1. 修改 `src-tauri` 目录下的 `tauri.conf.json` 中的 `url、productName、icon、identifier` 这 4 个字段,其中 icon 可以从 icons 目录选择一个,也可以去 [macOSicons](https://macosicons.com/#/) 下载符合产品名称的
2. 关于窗口属性设置,可以在 `tauri.conf.json` 修改 `windows` 属性对应的 `width/height`,是否全屏 `fullscreen`,是否可以调整大小 `resizable`,假如想适配 Mac 沉浸式头部,可以将 `transparent` 设置成 `true`,找到 Header 元素加一个 `padding-top` 样式即可,不想适配改成 `false` 也行
3. `npm run dev` 本地调试看看效果,此外可以使用 `npm run dev:debug` 进行容器调试
@@ -221,9 +243,9 @@ chmod +x ./script/build.sh && ./script/build.sh
## 支持
-- 我有两只猫,一只叫汤圆,一只叫可乐,假如觉得 Pake 让你生活更美好,可以给汤圆可乐 喂罐头 🥩🍤。
-- 如果你喜欢 Pake,可以在 Github Star,更欢迎 [推荐](https://twitter.com/intent/tweet?url=https://github.com/tw93/Pake&text=Pake%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~) 给你志同道合的朋友使用。
-- 可以关注我的 [Twitter](https://twitter.com/HiTw93) 获取到最新的 Pake 更新消息,也欢迎加入 [Telegram](https://t.me/miaoyan) 聊天群。
+- 我有两只猫,一只叫汤圆,一只叫可乐,假如觉得 Pake 让你生活更美好,可以给汤圆可乐 喂罐头 🥩🍤。
+- 如果你喜欢 Pake,可以在 Github Star,更欢迎 [推荐](https://twitter.com/intent/tweet?url=https://github.com/tw93/Pake&text=Pake%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~) 给你志同道合的朋友使用。
+- 可以关注我的 [Twitter](https://twitter.com/HiTw93) 获取到最新的 Pake 更新消息,也欢迎加入 [Telegram](https://t.me/miaoyan) 聊天群。
## 最后
diff --git a/bin/README.md b/bin/README.md
new file mode 100644
index 0000000..423f857
--- /dev/null
+++ b/bin/README.md
@@ -0,0 +1,72 @@
+## 安装
+
+```bash
+npm install -g pake
+```
+
+如果安装失败提示没有权限,请使用 `sudo` 运行。
+
+## 用法
+
+```bash
+pake [options] url
+```
+打包完成后的应用程序默认为当前工作目录。
+
+Note: 打包需要用 `Rust` 环境,如果没有 `Rust`,会提示确认安装。如遇安装失败或超时,可[自行安装](https://www.rust-lang.org/tools/install)。
+
+Note: 目前仅支持 MacOs,后续会支持其他平台。
+
+
+### url
+url 为你需要打包的网页链接🔗,必须提供。
+
+### [options]
+
+提供了一些特定的选项,打包时可以传递对应参数达到定制化的效果。
+
+#### [name]
+应用名称,如输入时未指定,会提示你输入。
+```shell
+--name
+```
+
+#### [icon]
+应用icon,支持本地/远程文件,默认为 Pake 自带图标。
+- MacOS下必须为 `.icns`
+```shell
+--icon
+```
+
+#### [height]
+打包后的应用窗口高度,默认 `800px`。
+```
+--height
+```
+
+
+#### [width]
+打包后的应用窗口宽度,默认 `1280px`。
+```
+--width
+```
+
+
+#### [transparent]
+是否开启沉浸式头部,默认为 `false` 不开启。
+```
+--transparent
+```
+
+
+#### [resize]
+是否可以拖动大小,默认为 `true` 可拖动。
+```
+--no-resizable
+```
+
+#### [fullscreen]
+打开应用后是否开启全屏,默认为 `false`。
+```
+--fullscreen
+```
diff --git a/bin/builders/BuilderFactory.ts b/bin/builders/BuilderFactory.ts
new file mode 100644
index 0000000..18ec650
--- /dev/null
+++ b/bin/builders/BuilderFactory.ts
@@ -0,0 +1,12 @@
+import { IS_MAC } from '@/utils/platform.js';
+import { IBuilder } from './base.js';
+import MacBuilder from './MacBuilder.js';
+
+export default class BuilderFactory {
+ static create(): IBuilder {
+ if (IS_MAC) {
+ return new MacBuilder();
+ }
+ throw new Error('The current system does not support');
+ }
+}
diff --git a/bin/builders/LinuxBuilder.ts b/bin/builders/LinuxBuilder.ts
new file mode 100644
index 0000000..e69de29
diff --git a/bin/builders/MacBuilder.ts b/bin/builders/MacBuilder.ts
new file mode 100644
index 0000000..55c6578
--- /dev/null
+++ b/bin/builders/MacBuilder.ts
@@ -0,0 +1,65 @@
+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 tauriConf from '../../src-tauri/tauri.conf.json';
+import { fileURLToPath } from 'url';
+import log from 'loglevel';
+
+export default class MacBuilder implements IBuilder {
+ async prepare() {
+ if (checkRustInstalled()) {
+ return;
+ }
+
+ const res = await prompts({
+ type: 'confirm',
+ message: 'Detect 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);
+ }
+ }
+
+ async build(url: string, options: PakeAppOptions) {
+ log.debug('PakeAppOptions', options);
+
+ const { width, height, fullscreen, transparent, resizable, identifier, name } = options;
+
+ const tauriConfWindowOptions = {
+ width,
+ height,
+ fullscreen,
+ transparent,
+ resizable,
+ };
+
+ // TODO 下面这块逻辑还可以再拆 目前比较简单
+ Object.assign(tauriConf.tauri.windows[0], { url, ...tauriConfWindowOptions });
+ tauriConf.package.productName = name;
+ tauriConf.tauri.bundle.identifier = identifier;
+ tauriConf.tauri.bundle.icon = [options.icon];
+
+ const npmDirectory = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
+ const configJsonPath = path.join(npmDirectory, 'src-tauri/tauri.conf.json');
+ await fs.writeFile(configJsonPath, Buffer.from(JSON.stringify(tauriConf), 'utf-8'));
+
+ const code = await shellExec(`cd ${npmDirectory} && npm run build`);
+ const dmgName = `${name}_${'0.2.0'}_universal.dmg`;
+ const appPath = this.getBuildedAppPath(npmDirectory, dmgName);
+ await fs.copyFile(appPath, path.resolve(`${name}_universal.dmg`));
+ }
+
+ getBuildedAppPath(npmDirectory: string, dmgName: string) {
+ return path.join(npmDirectory, 'src-tauri/target/universal-apple-darwin/release/bundle/dmg', dmgName);
+ }
+}
diff --git a/bin/builders/WinBulider.ts b/bin/builders/WinBulider.ts
new file mode 100644
index 0000000..e69de29
diff --git a/bin/builders/base.ts b/bin/builders/base.ts
new file mode 100644
index 0000000..c92e239
--- /dev/null
+++ b/bin/builders/base.ts
@@ -0,0 +1,16 @@
+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
new file mode 100644
index 0000000..6f881d6
--- /dev/null
+++ b/bin/builders/common.ts
@@ -0,0 +1,11 @@
+import prompts from 'prompts';
+
+export async function promptText(message: string, initial?: string) {
+ const response = await prompts({
+ type: 'text',
+ name: 'content',
+ message,
+ initial,
+ });
+ return response.content;
+}
diff --git a/bin/cli.ts b/bin/cli.ts
new file mode 100644
index 0000000..fddffdb
--- /dev/null
+++ b/bin/cli.ts
@@ -0,0 +1,36 @@
+import { program } from 'commander';
+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 log from 'loglevel';
+
+program.version('0.0.1').description('A cli application can package a web page to desktop application');
+
+program
+ .showHelpAfterError()
+ .argument('', '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('--debug', 'debug', DEFAULT_PAKE_OPTIONS.transparent)
+ .action(async (url: string, options: PakeCliOptions) => {
+ log.setDefaultLevel('info')
+ if (options.debug) {
+ log.setLevel('debug');
+ }
+
+ const builder = BuilderFactory.create();
+ await builder.prepare();
+
+ const appOptions = await handleInputOptions(options, url);
+
+ builder.build(url, appOptions);
+ });
+
+program.parse();
diff --git a/bin/defaults.ts b/bin/defaults.ts
new file mode 100644
index 0000000..5f808d2
--- /dev/null
+++ b/bin/defaults.ts
@@ -0,0 +1,13 @@
+import { PakeCliOptions } from './types.js';
+
+export const DEFAULT_PAKE_OPTIONS: PakeCliOptions = {
+ icon: '',
+ height: 800,
+ width: 1280,
+ fullscreen: false,
+ resizable: true,
+ transparent: false,
+ debug: false,
+};
+
+export const DEFAULT_APP_NAME = 'Pake';
diff --git a/bin/helpers/rust.ts b/bin/helpers/rust.ts
new file mode 100644
index 0000000..af9811c
--- /dev/null
+++ b/bin/helpers/rust.ts
@@ -0,0 +1,21 @@
+import ora from 'ora';
+import shelljs from 'shelljs';
+import { shellExec } from '../utils/shell.js';
+
+const InstallRustScript = "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y";
+export async function installRust() {
+ const spinner = ora('Downloading Rust').start();
+ try {
+ await shellExec(InstallRustScript);
+ spinner.succeed();
+ } catch (error) {
+ console.error('install rust return code', error.message);
+ spinner.fail();
+
+ process.exit(1);
+ }
+}
+
+export function checkRustInstalled() {
+ return shelljs.exec('rustc --version', { silent: true }).code === 0;
+}
diff --git a/bin/helpers/tauriConfig.ts b/bin/helpers/tauriConfig.ts
new file mode 100644
index 0000000..fb44d61
--- /dev/null
+++ b/bin/helpers/tauriConfig.ts
@@ -0,0 +1,8 @@
+import crypto from 'crypto';
+
+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}`;
+}
diff --git a/bin/options/icon.ts b/bin/options/icon.ts
new file mode 100644
index 0000000..7e1c5d8
--- /dev/null
+++ b/bin/options/icon.ts
@@ -0,0 +1,91 @@
+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 { fileURLToPath } from 'url';
+import log from 'loglevel';
+
+export async function handleIcon(options: PakeAppOptions, url: string) {
+ if (options.icon) {
+ if (options.icon.startsWith('http')) {
+ return downloadIcon(options.icon);
+ } else {
+ return path.resolve(options.icon);
+ }
+ }
+ if (!options.icon) {
+ return inferIcon(options.name, url);
+ }
+}
+
+export async function inferIcon(name: string, url: string) {
+ log.info('You have not provided an app icon, use the default icon(can use --icon option to assign an icon)')
+ const npmDirectory = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
+ return path.join(npmDirectory, 'pake-default.icns');
+}
+
+// export async function getIconFromPageUrl(url: string) {
+// const icon = await pageIcon(url);
+// console.log(icon);
+// if (icon.ext === '.ico') {
+// const a = await ICO.parse(icon.data);
+// icon.data = Buffer.from(a[0].buffer);
+// }
+
+// const iconDir = (await dir()).path;
+// const iconPath = path.join(iconDir, `/icon.icns`);
+
+// const out = png2icons.createICNS(icon.data, png2icons.BILINEAR, 0);
+
+// await fs.writeFile(iconPath, out);
+// return iconPath;
+// }
+
+// export async function getIconFromMacosIcons(name: string) {
+// const data = {
+// query: name,
+// filters: 'approved:true',
+// hitsPerPage: 10,
+// page: 1,
+// };
+// const res = await axios.post('https://p1txh7zfb3-2.algolianet.com/1/indexes/macOSicons/query?x-algolia-agent=Algolia%20for%20JavaScript%20(4.13.1)%3B%20Browser', data, {
+// headers: {
+// 'x-algolia-api-key': '0ba04276e457028f3e11e38696eab32c',
+// 'x-algolia-application-id': 'P1TXH7ZFB3',
+// },
+// });
+// if (!res.data.hits.length) {
+// return '';
+// } else {
+// return downloadIcon(res.data.hits[0].icnsUrl);
+// }
+// }
+
+export async function downloadIcon(iconUrl: string) {
+ let iconResponse;
+ try {
+ iconResponse = await axios.get(iconUrl, {
+ responseType: 'arraybuffer',
+ });
+ } catch (error) {
+ if (error.response && error.response.status === 404) {
+ return null;
+ }
+ throw error;
+ }
+
+ const iconData = await iconResponse.data;
+ if (!iconData) {
+ return null;
+ }
+ const fileDetails = await fileTypeFromBuffer(iconData);
+ if (!fileDetails) {
+ return null;
+ }
+ const { path } = await dir();
+ const iconPath = `${path}/icon.${fileDetails.ext}`;
+ await fs.writeFile(iconPath, iconData);
+ return iconPath;
+}
diff --git a/bin/options/index.ts b/bin/options/index.ts
new file mode 100644
index 0000000..efcf4f6
--- /dev/null
+++ b/bin/options/index.ts
@@ -0,0 +1,22 @@
+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';
+
+export default async function handleOptions(options: PakeCliOptions, url: string): Promise {
+ const appOptions: PakeAppOptions = {
+ ...options,
+ identifier: '',
+ };
+
+ if (!appOptions.name) {
+ appOptions.name = await promptText('please input your application name', getDomain(url));
+ }
+
+ appOptions.identifier = getIdentifier(appOptions.name, url);
+
+ appOptions.icon = await handleIcon(appOptions, url);
+
+ return appOptions;
+}
diff --git a/bin/types.ts b/bin/types.ts
new file mode 100644
index 0000000..30ce729
--- /dev/null
+++ b/bin/types.ts
@@ -0,0 +1,29 @@
+export interface PakeCliOptions {
+ /** 应用名称 */
+ name?: string;
+
+ /** 应用icon */
+ icon: string;
+
+ /** 应用窗口宽度,默认 1280px */
+ width: number;
+
+ /** 应用窗口高度,默认 800px */
+ height: number;
+
+ /** 是否可以拖动,默认true */
+ resizable: boolean;
+
+ /** 是否可以全屏,默认 false */
+ fullscreen: boolean;
+
+ /** 是否开启沉浸式头部,默认为 false 不开启 ƒ*/
+ transparent: boolean;
+
+ /** 调试模式,会输出更多日志 */
+ debug: boolean;
+}
+
+export interface PakeAppOptions extends PakeCliOptions {
+ identifier: string;
+}
diff --git a/bin/utils/platform.ts b/bin/utils/platform.ts
new file mode 100644
index 0000000..c08cd48
--- /dev/null
+++ b/bin/utils/platform.ts
@@ -0,0 +1,5 @@
+export const IS_MAC = process.platform === 'darwin';
+
+export const IS_WIN = process.platform === 'win32';
+
+export const IS_LINUX = process.platform === 'linux';
diff --git a/bin/utils/shell.ts b/bin/utils/shell.ts
new file mode 100644
index 0000000..42b4015
--- /dev/null
+++ b/bin/utils/shell.ts
@@ -0,0 +1,13 @@
+import shelljs from 'shelljs';
+
+export function shellExec(command: string) {
+ return new Promise((resolve, reject) => {
+ shelljs.exec(command, { async: true, silent: false}, (code) => {
+ if (code === 0) {
+ resolve(0);
+ } else {
+ reject(new Error(`${code}`));
+ }
+ });
+ });
+}
diff --git a/bin/utils/tlds.ts b/bin/utils/tlds.ts
new file mode 100644
index 0000000..5718aca
--- /dev/null
+++ b/bin/utils/tlds.ts
@@ -0,0 +1,1489 @@
+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;
\ No newline at end of file
diff --git a/bin/utils/url.ts b/bin/utils/url.ts
new file mode 100644
index 0000000..8ae79f8
--- /dev/null
+++ b/bin/utils/url.ts
@@ -0,0 +1,47 @@
+import url from 'url';
+import isurl from 'is-url';
+import tlds from './tlds.js';
+
+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;
+
+ // 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: string): string {
+ const parsed = url.parse(inputUrl);
+ if (!parsed.protocol) {
+ const urlWithProtocol = `https://${inputUrl}`;
+ return urlWithProtocol;
+ }
+ return inputUrl;
+}
+
+export function normalizeUrl(urlToNormalize: string): string {
+ const urlWithProtocol = appendProtocol(urlToNormalize);
+
+ 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
new file mode 100644
index 0000000..a97821c
--- /dev/null
+++ b/bin/utils/validate.ts
@@ -0,0 +1,18 @@
+import * as Commander from 'commander';
+import { normalizeUrl } from './url.js';
+
+export function validateNumberInput(value: string) {
+ const parsedValue = Number(value);
+ if (isNaN(parsedValue)) {
+ throw new Commander.InvalidArgumentError('Not a number.');
+ }
+ return parsedValue;
+}
+
+export function validateUrlInput(url: string) {
+ try {
+ return normalizeUrl(url);
+ } catch (error) {
+ throw new Commander.InvalidArgumentError(error.message);
+ }
+}
diff --git a/cli.js b/cli.js
new file mode 100755
index 0000000..d17768a
--- /dev/null
+++ b/cli.js
@@ -0,0 +1,2 @@
+#!/usr/bin/env node
+import './dist/cli.js';
diff --git a/dist/cli.js b/dist/cli.js
new file mode 100644
index 0000000..9a3d28e
--- /dev/null
+++ b/dist/cli.js
@@ -0,0 +1,1917 @@
+import * as Commander from 'commander';
+import { program } from 'commander';
+import url, { fileURLToPath } from 'url';
+import isurl from 'is-url';
+import prompts from 'prompts';
+import crypto from 'crypto';
+import axios from 'axios';
+import { fileTypeFromBuffer } from 'file-type';
+import { dir } from 'tmp-promise';
+import path from 'path';
+import fs from 'fs/promises';
+import log from 'loglevel';
+import ora from 'ora';
+import shelljs from 'shelljs';
+
+/******************************************************************************
+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.
+***************************************************************************** */
+
+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: 800,
+ width: 1280,
+ fullscreen: false,
+ resizable: true,
+ transparent: false,
+ debug: false,
+};
+
+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',
+ 'ελ',
+ 'ευ',
+ 'бг',
+ 'бел',
+ 'дети',
+ 'ею',
+ 'католик',
+ 'ком',
+ 'мкд',
+ 'мон',
+ 'москва',
+ 'онлайн',
+ 'орг',
+ 'рус',
+ 'рф',
+ 'сайт',
+ 'срб',
+ 'укр',
+ 'қаз',
+ 'հայ',
+ 'ישראל',
+ 'קום',
+ 'ابوظبي',
+ 'اتصالات',
+ 'ارامكو',
+ 'الاردن',
+ 'البحرين',
+ 'الجزائر',
+ 'السعودية',
+ 'العليان',
+ 'المغرب',
+ 'امارات',
+ 'ایران',
+ 'بارت',
+ 'بازار',
+ 'بيتك',
+ 'بھارت',
+ 'تونس',
+ 'سودان',
+ 'سورية',
+ 'شبكة',
+ 'عراق',
+ 'عرب',
+ 'عمان',
+ 'فلسطين',
+ 'قطر',
+ 'كاثوليك',
+ 'كوم',
+ 'مصر',
+ 'مليسيا',
+ 'موريتانيا',
+ 'موقع',
+ 'همراه',
+ 'پاکستان',
+ 'ڀارت',
+ 'कॉम',
+ 'नेट',
+ 'भारत',
+ 'भारतम्',
+ 'भारोत',
+ 'संगठन',
+ 'বাংলা',
+ 'ভারত',
+ 'ভাৰত',
+ 'ਭਾਰਤ',
+ 'ભારત',
+ 'ଭାରତ',
+ 'இந்தியா',
+ 'இலங்கை',
+ 'சிங்கப்பூர்',
+ 'భారత్',
+ 'ಭಾರತ',
+ 'ഭാരതം',
+ 'ලංකා',
+ 'คอม',
+ 'ไทย',
+ 'ລາວ',
+ 'გე',
+ 'みんな',
+ 'アマゾン',
+ 'クラウド',
+ 'グーグル',
+ 'コム',
+ 'ストア',
+ 'セール',
+ 'ファッション',
+ 'ポイント',
+ '世界',
+ '中信',
+ '中国',
+ '中國',
+ '中文网',
+ '亚马逊',
+ '企业',
+ '佛山',
+ '信息',
+ '健康',
+ '八卦',
+ '公司',
+ '公益',
+ '台湾',
+ '台灣',
+ '商城',
+ '商店',
+ '商标',
+ '嘉里',
+ '嘉里大酒店',
+ '在线',
+ '大拿',
+ '天主教',
+ '娱乐',
+ '家電',
+ '广东',
+ '微博',
+ '慈善',
+ '我爱你',
+ '手机',
+ '招聘',
+ '政务',
+ '政府',
+ '新加坡',
+ '新闻',
+ '时尚',
+ '書籍',
+ '机构',
+ '淡马锡',
+ '游戏',
+ '澳門',
+ '点看',
+ '移动',
+ '组织机构',
+ '网址',
+ '网店',
+ '网站',
+ '网络',
+ '联通',
+ '诺基亚',
+ '谷歌',
+ '购物',
+ '通販',
+ '集团',
+ '電訊盈科',
+ '飞利浦',
+ '食品',
+ '餐厅',
+ '香格里拉',
+ '香港',
+ '닷넷',
+ '닷컴',
+ '삼성',
+ '한국',
+];
+
+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) {
+ try {
+ return normalizeUrl(url);
+ }
+ catch (error) {
+ throw new Commander.InvalidArgumentError(error.message);
+ }
+}
+
+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 getIdentifier(name, url) {
+ const hash = crypto.createHash('md5');
+ hash.update(url);
+ const postFixHash = hash.digest('hex').substring(0, 6);
+ return `pake-${postFixHash}`;
+}
+
+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 inferIcon(options.name);
+ }
+ });
+}
+function inferIcon(name, url) {
+ return __awaiter(this, void 0, void 0, function* () {
+ log.info('You have not provided an app icon, use the default icon(can use --icon option to assign an icon)');
+ const npmDirectory = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
+ return path.join(npmDirectory, 'pake-default.icns');
+ });
+}
+// 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.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: '' });
+ if (!appOptions.name) {
+ appOptions.name = yield promptText('please input your application name', getDomain(url));
+ }
+ appOptions.identifier = getIdentifier(appOptions.name, url);
+ appOptions.icon = yield handleIcon(appOptions);
+ return appOptions;
+ });
+}
+
+const IS_MAC = process.platform === 'darwin';
+process.platform === 'win32';
+process.platform === 'linux';
+
+function shellExec(command) {
+ return new Promise((resolve, reject) => {
+ shelljs.exec(command, { async: true, silent: false }, (code) => {
+ if (code === 0) {
+ resolve(0);
+ }
+ else {
+ reject(new Error(`${code}`));
+ }
+ });
+ });
+}
+
+const InstallRustScript = "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y";
+function installRust() {
+ return __awaiter(this, void 0, void 0, function* () {
+ const spinner = ora('Downloading Rust').start();
+ try {
+ yield shellExec(InstallRustScript);
+ 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 = {
+ windows: [
+ {
+ url: "https://weread.qq.com/",
+ transparent: true,
+ fullscreen: false,
+ width: 1200,
+ height: 728,
+ resizable: true
+ }
+ ],
+ allowlist: {
+ all: true
+ },
+ bundle: {
+ icon: [
+ "icons/weread.icns",
+ "png/weread_256.ico",
+ "png/weread_32.ico",
+ "png/weread_512.png"
+ ],
+ identifier: "com.tw93.weread",
+ active: true,
+ category: "DeveloperTool",
+ copyright: "",
+ deb: {
+ depends: [
+ "libwebkit2gtk-4.0-dev",
+ "build-essential",
+ "curl",
+ "wget",
+ "libssl-dev",
+ "libgtk-3-dev",
+ "libayatana-appindicator3-dev",
+ "librsvg2-dev"
+ ],
+ files: {
+ "/usr/share/applications/com-tw93-weread.desktop": "assets/com-tw93-weread.desktop"
+ }
+ },
+ externalBin: [
+ ],
+ longDescription: "",
+ macOS: {
+ entitlements: null,
+ exceptionDomain: "",
+ frameworks: [
+ ],
+ providerShortName: null,
+ signingIdentity: null
+ },
+ resources: [
+ ],
+ shortDescription: "",
+ targets: [
+ "deb",
+ "msi",
+ "dmg"
+ ],
+ windows: {
+ certificateThumbprint: null,
+ digestAlgorithm: "sha256",
+ timestampUrl: "",
+ wix: {
+ language: [
+ "en-US",
+ "zh-CN"
+ ]
+ }
+ }
+ },
+ security: {
+ csp: null
+ },
+ updater: {
+ active: false
+ }
+};
+var build = {
+ devPath: "../dist",
+ distDir: "../dist",
+ beforeBuildCommand: "",
+ beforeDevCommand: ""
+};
+var tauriConf = {
+ "package": {
+ productName: "weread",
+ version: "0.2.0"
+},
+ tauri: tauri,
+ build: build
+};
+
+class MacBuilder {
+ prepare() {
+ return __awaiter(this, void 0, void 0, function* () {
+ if (checkRustInstalled()) {
+ return;
+ }
+ const res = yield prompts({
+ type: 'confirm',
+ message: 'Detect 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 { width, height, fullscreen, transparent, resizable, identifier, name } = options;
+ const tauriConfWindowOptions = {
+ width,
+ height,
+ fullscreen,
+ transparent,
+ resizable,
+ };
+ // TODO 下面这块逻辑还可以再拆 目前比较简单
+ Object.assign(tauriConf.tauri.windows[0], Object.assign({ url }, tauriConfWindowOptions));
+ tauriConf.package.productName = name;
+ tauriConf.tauri.bundle.identifier = identifier;
+ tauriConf.tauri.bundle.icon = [options.icon];
+ const npmDirectory = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
+ const configJsonPath = path.join(npmDirectory, 'src-tauri/tauri.conf.json');
+ yield fs.writeFile(configJsonPath, Buffer.from(JSON.stringify(tauriConf), 'utf-8'));
+ yield shellExec(`cd ${npmDirectory} && npm run build`);
+ const dmgName = `${name}_${'0.2.0'}_universal.dmg`;
+ const appPath = this.getBuildedAppPath(npmDirectory, dmgName);
+ yield fs.copyFile(appPath, path.resolve(`${name}_universal.dmg`));
+ });
+ }
+ getBuildedAppPath(npmDirectory, dmgName) {
+ return path.join(npmDirectory, 'src-tauri/target/universal-apple-darwin/release/bundle/dmg', dmgName);
+ }
+}
+
+class BuilderFactory {
+ static create() {
+ if (IS_MAC) {
+ return new MacBuilder();
+ }
+ throw new Error('The current system does not support');
+ }
+}
+
+program.version('0.0.1').description('A cli application can package a web page to desktop application');
+program
+ .showHelpAfterError()
+ .argument('', '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('--debug', 'debug', DEFAULT_PAKE_OPTIONS.transparent)
+ .action((url, options) => __awaiter(void 0, void 0, void 0, function* () {
+ log.setDefaultLevel('info');
+ if (options.debug) {
+ log.setLevel('debug');
+ }
+ const builder = BuilderFactory.create();
+ yield builder.prepare();
+ const appOptions = yield handleOptions(options, url);
+ builder.build(url, appOptions);
+}));
+program.parse();
diff --git a/package.json b/package.json
index e846685..eb41178 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,10 @@
{
- "name": "pake",
+ "name": "pake-cli",
+ "version": "0.0.1",
"description": "用 Rust 来打包你的 App,底层使用 Tauri,当前支持微信读书、Flomo、Vercel",
+ "bin": {
+ "pake": "./cli.js"
+ },
"repository": {
"type": "git",
"url": "https://github.com/tw93/pake.git"
@@ -16,13 +20,42 @@
"build": "npm run tauri build -- --target universal-apple-darwin",
"build:windows": "npm run tauri build -- --target x86_64-pc-windows-msvc",
"build:linux": "npm run tauri build --release",
- "tauri": "tauri"
+ "tauri": "tauri",
+ "cli": "rollup -c rollup.config.js --watch",
+ "cli:build": "rollup -c rollup.config.js"
},
+ "type": "module",
+ "exports": "./dist/pake.js",
"license": "MIT",
"dependencies": {
- "@tauri-apps/api": "^1.2.0"
+ "@tauri-apps/api": "^1.2.0",
+ "@tauri-apps/cli": "^1.2.0",
+ "axios": "^1.1.3",
+ "commander": "^9.4.1",
+ "file-type": "^18.0.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"
},
"devDependencies": {
- "@tauri-apps/cli": "^1.2.0"
+ "@rollup/plugin-alias": "^4.0.2",
+ "@rollup/plugin-commonjs": "^23.0.2",
+ "@rollup/plugin-json": "^5.0.1",
+ "@rollup/plugin-typescript": "^9.0.2",
+ "@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",
+ "app-root-path": "^3.1.0",
+ "concurrently": "^7.5.0",
+ "rollup": "^3.3.0",
+ "rollup-plugin-typescript2": "^0.34.1",
+ "tsc-alias": "^1.7.1",
+ "tslib": "^2.4.1",
+ "typescript": "^4.8.4"
}
}
diff --git a/pake-default.icns b/pake-default.icns
new file mode 100644
index 0000000..577d52f
Binary files /dev/null and b/pake-default.icns differ
diff --git a/rollup.config.js b/rollup.config.js
new file mode 100644
index 0000000..6fdfbba
--- /dev/null
+++ b/rollup.config.js
@@ -0,0 +1,24 @@
+import path from 'path';
+import appRootPath from 'app-root-path';
+import typescript from '@rollup/plugin-typescript';
+import alias from '@rollup/plugin-alias';
+import commonjs from '@rollup/plugin-commonjs';
+import json from '@rollup/plugin-json';
+
+export default {
+ input: 'bin/cli.ts',
+ output: {
+ file: 'dist/cli.js',
+ format: 'es'
+ },
+ plugins: [
+ json(),
+ typescript({
+ sourceMap: false,
+ }),
+ commonjs(),
+ alias({
+ entries: [{ find: '@', replacement: path.join(appRootPath.path, 'bin') }],
+ }),
+ ],
+};
diff --git a/src-tauri/icons/icon.icns b/src-tauri/icons/icon.icns
new file mode 100644
index 0000000..577d52f
Binary files /dev/null and b/src-tauri/icons/icon.icns differ
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..d5cb79e
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,17 @@
+{
+ "compilerOptions": {
+ "module": "Node16",
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "target": "es6",
+ "noImplicitAny": true,
+ "moduleResolution": "Node16",
+ "sourceMap": true,
+ "outDir": "dist",
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["bin/*"]
+ }
+ },
+ "include": ["bin/**/*"]
+}