diff --git a/.ecrc.json b/.ecrc.json index dc2d0af..39e2e37 100644 --- a/.ecrc.json +++ b/.ecrc.json @@ -1,3 +1,3 @@ { - "Exclude": ["Cargo\\.lock$"] + "Exclude": ["Cargo\\.lock$", "dist"] } diff --git a/.gitignore b/.gitignore index 66c28c6..6f8dd99 100644 --- a/.gitignore +++ b/.gitignore @@ -9,11 +9,13 @@ lerna-debug.log* node_modules dist-ssr +dist/cli.js *.local # 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..e23975b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "cSpell.words": ["loglevel", "Pake", "tauri"], + "typescript.preferences.importModuleSpecifierEnding": "js" +} diff --git a/README.md b/README.md index 7f78a78..f5ef149 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,26 @@ - Windows 下不能安装到 C:\Program File,会直接闪退。建议安装到其他非管理员权限目录,比如 D:\Program Files (x86) 。 - ~~Linux 下暂时不能存 cookie,即应用关闭后数据清空,账号自动推出(已修复)~~。 +## 使用命令行打包 + +Pake 提供了命令行工具,可以更快捷方便地打包。(目前仅支持 MacOS) + +### 安装 + +```bash +npm install -g pake-cli +``` + +如果安装失败提示没有权限,请使用 `sudo` 运行。 + +### 用法 + +```bash +pake [options] url +``` + +更多用法可查看[文档](./bin//README.md)。 + ## 开发 开始前参考 [Tauri](https://tauri.app/v1/guides/getting-started/prerequisites#setting-up-macos) 快速配置好环境。 diff --git a/bin/README.md b/bin/README.md new file mode 100644 index 0000000..e64a495 --- /dev/null +++ b/bin/README.md @@ -0,0 +1,72 @@ +## 安装 + +```bash +npm install -g pake-cli +``` + +如果安装失败提示没有权限,请使用 `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..ed281de --- /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: '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); + } + } + + 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..2df7979 --- /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..a682d90 --- /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; 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..ac098d0 --- /dev/null +++ b/dist/cli.js @@ -0,0 +1,1918 @@ +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: [ + "png/weread_32.ico" + ], + 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: '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 { 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..50a47e0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,10 @@ { - "name": "pake", + "name": "pake-cli", + "version": "0.0.2", "description": "用 Rust 来打包你的 App,底层使用 Tauri,当前支持微信读书、Flomo、Vercel", + "bin": { + "pake": "./cli.js" + }, "repository": { "type": "git", "url": "https://github.com/tw93/pake.git" @@ -16,13 +20,43 @@ "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", + "cli:publish": "npm run cli:build && npm publish" }, + "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/**/*"] +}