🎨 Optimize the use of CLI.

This commit is contained in:
Tw93
2023-06-22 20:56:15 +08:00
parent 22e3efea49
commit e1cd40277f
9 changed files with 79 additions and 49 deletions

View File

@@ -1,3 +1,4 @@
import ora from "ora";
import path from 'path'; import path from 'path';
import fsExtra from "fs-extra"; import fsExtra from "fs-extra";
import prompts from 'prompts'; import prompts from 'prompts';
@@ -39,6 +40,8 @@ export default abstract class BaseBuilder {
} }
protected async runBuildCommand(directory: string, command: string) { protected async runBuildCommand(directory: string, command: string) {
const spinner = ora('Building...').start();
setTimeout(() => spinner.succeed(), 5000);
const isChina = await isChinaDomain("www.npmjs.com"); const isChina = await isChinaDomain("www.npmjs.com");
if (isChina) { if (isChina) {
logger.info("Located in China, using npm/Rust CN mirror."); logger.info("Located in China, using npm/Rust CN mirror.");

View File

@@ -3,19 +3,20 @@ import MacBuilder from './MacBuilder';
import WinBuilder from './WinBuilder'; import WinBuilder from './WinBuilder';
import LinuxBuilder from './LinuxBuilder'; import LinuxBuilder from './LinuxBuilder';
import { IS_MAC, IS_WIN, IS_LINUX } from '@/utils/platform'; const { platform } = process;
const buildersMap: Record<string, new () => BaseBuilder> = {
darwin: MacBuilder,
win32: WinBuilder,
linux: LinuxBuilder,
};
export default class BuilderProvider { export default class BuilderProvider {
static create(): BaseBuilder { static create(): BaseBuilder {
if (IS_MAC) { const Builder = buildersMap[platform];
return new MacBuilder(); if (!Builder) {
throw new Error('The current system is not supported!');
} }
if (IS_WIN) { return new Builder();
return new WinBuilder();
}
if (IS_LINUX) {
return new LinuxBuilder();
}
throw new Error('The current system is not supported!');
} }
} }

4
bin/cli.ts vendored
View File

@@ -1,3 +1,4 @@
import ora from "ora";
import log from 'loglevel'; import log from 'loglevel';
import { program } from 'commander'; import { program } from 'commander';
@@ -46,10 +47,11 @@ program
log.setLevel('debug'); log.setLevel('debug');
} }
const spinner = ora('Preparing...').start();
const builder = BuilderProvider.create(); const builder = BuilderProvider.create();
await builder.prepare(); await builder.prepare();
const appOptions = await handleInputOptions(options, url); const appOptions = await handleInputOptions(options, url);
spinner.succeed();
log.debug('PakeAppOptions', appOptions); log.debug('PakeAppOptions', appOptions);

View File

@@ -127,19 +127,19 @@ export async function mergeConfig(
const platformIconMap: PlatformMap = { const platformIconMap: PlatformMap = {
win32: { win32: {
fileExt: '.ico', fileExt: '.ico',
path: `png/${name.toLowerCase()}_32.ico`, path: `png/${name.toLowerCase()}_256.ico`,
defaultIcon: 'png/icon_256.ico', defaultIcon: 'png/icon_256.ico',
message: 'Windows icon must be .ico and 256x256px.', message: 'Windows icon must be .ico and 256x256px.',
}, },
linux: { linux: {
fileExt: '.png', fileExt: '.png',
path: `png/${name.toLowerCase()}_32.png`, path: `png/${name.toLowerCase()}_512.png`,
defaultIcon: 'png/icon_512.png', defaultIcon: 'png/icon_512.png',
message: 'Linux icon must be .png and 512x512px.', message: 'Linux icon must be .png and 512x512px.',
}, },
darwin: { darwin: {
fileExt: '.icns', fileExt: '.icns',
path: `icons/${name.toLowerCase()}_32.icns`, path: `icons/${name.toLowerCase()}.icns`,
defaultIcon: 'icons/icon.icns', defaultIcon: 'icons/icon.icns',
message: 'MacOS icon must be .icns type.', message: 'MacOS icon must be .icns type.',
}, },

6
bin/options/icon.ts vendored
View File

@@ -25,11 +25,10 @@ export async function handleIcon(options: PakeAppOptions) {
export async function downloadIcon(iconUrl: string) { export async function downloadIcon(iconUrl: string) {
try { try {
const iconResponse = await axios.get(iconUrl, { const iconResponse = await axios.get(iconUrl, { responseType: 'arraybuffer' });
responseType: 'arraybuffer',
});
const iconData = await iconResponse.data; const iconData = await iconResponse.data;
if (!iconData) { if (!iconData) {
return null; return null;
} }
@@ -42,6 +41,7 @@ export async function downloadIcon(iconUrl: string) {
const { path: tempPath } = await dir(); const { path: tempPath } = await dir();
const iconPath = `${tempPath}/icon.${fileDetails.ext}`; const iconPath = `${tempPath}/icon.${fileDetails.ext}`;
await fsExtra.outputFile(iconPath, iconData); await fsExtra.outputFile(iconPath, iconData);
return iconPath; return iconPath;
} catch (error) { } catch (error) {
if (error.response && error.response.status === 404) { if (error.response && error.response.status === 404) {

18
bin/utils/ip.ts vendored
View File

@@ -10,7 +10,8 @@ const ping = async (host: string) => {
const ip = await lookup(host); const ip = await lookup(host);
const start = new Date(); const start = new Date();
return new Promise<number>((resolve, reject) => { // Prevent timeouts from affecting user experience.
const requestPromise = new Promise<number>((resolve, reject) => {
const req = http.get(`http://${ip.address}`, (res) => { const req = http.get(`http://${ip.address}`, (res) => {
const delay = new Date().getTime() - start.getTime(); const delay = new Date().getTime() - start.getTime();
res.resume(); res.resume();
@@ -21,14 +22,23 @@ const ping = async (host: string) => {
reject(err); reject(err);
}); });
}); });
const timeoutPromise = new Promise<number>((_, reject) => {
setTimeout(() => {
reject(new Error('Request timed out after 3 seconds'));
}, 3000);
});
return Promise.race([requestPromise, timeoutPromise]);
}; };
async function isChinaDomain(domain: string): Promise<boolean> { async function isChinaDomain(domain: string): Promise<boolean> {
try { try {
const [ip] = await resolve(domain); const [ip] = await resolve(domain);
return await isChinaIP(ip, domain); return await isChinaIP(ip, domain);
} catch (error) { } catch (error) {
logger.info(`${domain} can't be parse!`); logger.debug(`${domain} can't be parse!`);
return false; return false;
} }
} }
@@ -36,10 +46,10 @@ async function isChinaDomain(domain: string): Promise<boolean> {
async function isChinaIP(ip: string, domain: string): Promise<boolean> { async function isChinaIP(ip: string, domain: string): Promise<boolean> {
try { try {
const delay = await ping(ip); const delay = await ping(ip);
logger.info(`${domain} latency is ${delay} ms`); logger.debug(`${domain} latency is ${delay} ms`);
return delay > 500; return delay > 500;
} catch (error) { } catch (error) {
logger.info(`ping ${domain} failed!`); logger.debug(`ping ${domain} failed!`);
return false; return false;
} }
} }

64
dist/cli.js vendored
View File

@@ -1,3 +1,4 @@
import ora from 'ora';
import log from 'loglevel'; import log from 'loglevel';
import { InvalidArgumentError, program } from 'commander'; import { InvalidArgumentError, program } from 'commander';
import fsExtra from 'fs-extra'; import fsExtra from 'fs-extra';
@@ -15,7 +16,6 @@ import shelljs from 'shelljs';
import dns from 'dns'; import dns from 'dns';
import http from 'http'; import http from 'http';
import { promisify } from 'util'; import { promisify } from 'util';
import ora from 'ora';
import updateNotifier from 'update-notifier'; import updateNotifier from 'update-notifier';
import fs from 'fs'; import fs from 'fs';
@@ -42,10 +42,10 @@ const currentModulePath = fileURLToPath(import.meta.url);
// Resolve the parent directory of the current module // Resolve the parent directory of the current module
const npmDirectory = path.join(path.dirname(currentModulePath), '..'); const npmDirectory = path.join(path.dirname(currentModulePath), '..');
const { platform: platform$1 } = process; const { platform: platform$2 } = process;
const IS_MAC = platform$1 === 'darwin'; const IS_MAC = platform$2 === 'darwin';
const IS_WIN = platform$1 === 'win32'; const IS_WIN = platform$2 === 'win32';
const IS_LINUX = platform$1 === 'linux'; const IS_LINUX = platform$2 === 'linux';
async function handleIcon(options) { async function handleIcon(options) {
if (options.icon) { if (options.icon) {
@@ -64,9 +64,7 @@ async function handleIcon(options) {
} }
async function downloadIcon(iconUrl) { async function downloadIcon(iconUrl) {
try { try {
const iconResponse = await axios.get(iconUrl, { const iconResponse = await axios.get(iconUrl, { responseType: 'arraybuffer' });
responseType: 'arraybuffer',
});
const iconData = await iconResponse.data; const iconData = await iconResponse.data;
if (!iconData) { if (!iconData) {
return null; return null;
@@ -347,9 +345,9 @@ const platformConfigs = {
darwin: MacConf, darwin: MacConf,
linux: LinuxConf linux: LinuxConf
}; };
const { platform } = process; const { platform: platform$1 } = process;
// @ts-ignore // @ts-ignore
const platformConfig = platformConfigs[platform]; const platformConfig = platformConfigs[platform$1];
let tauriConfig = { let tauriConfig = {
tauri: { tauri: {
...CommonConf.tauri, ...CommonConf.tauri,
@@ -378,7 +376,8 @@ const ping = async (host) => {
const lookup = promisify(dns.lookup); const lookup = promisify(dns.lookup);
const ip = await lookup(host); const ip = await lookup(host);
const start = new Date(); const start = new Date();
return new Promise((resolve, reject) => { // Prevent timeouts from affecting user experience.
const requestPromise = new Promise((resolve, reject) => {
const req = http.get(`http://${ip.address}`, (res) => { const req = http.get(`http://${ip.address}`, (res) => {
const delay = new Date().getTime() - start.getTime(); const delay = new Date().getTime() - start.getTime();
res.resume(); res.resume();
@@ -388,6 +387,12 @@ const ping = async (host) => {
reject(err); reject(err);
}); });
}); });
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {
reject(new Error('Request timed out after 3 seconds'));
}, 3000);
});
return Promise.race([requestPromise, timeoutPromise]);
}; };
async function isChinaDomain(domain) { async function isChinaDomain(domain) {
try { try {
@@ -395,18 +400,18 @@ async function isChinaDomain(domain) {
return await isChinaIP(ip, domain); return await isChinaIP(ip, domain);
} }
catch (error) { catch (error) {
logger.info(`${domain} can't be parse!`); logger.debug(`${domain} can't be parse!`);
return false; return false;
} }
} }
async function isChinaIP(ip, domain) { async function isChinaIP(ip, domain) {
try { try {
const delay = await ping(ip); const delay = await ping(ip);
logger.info(`${domain} latency is ${delay} ms`); logger.debug(`${domain} latency is ${delay} ms`);
return delay > 500; return delay > 500;
} }
catch (error) { catch (error) {
logger.info(`ping ${domain} failed!`); logger.debug(`ping ${domain} failed!`);
return false; return false;
} }
} }
@@ -457,6 +462,8 @@ class BaseBuilder {
} }
} }
async runBuildCommand(directory, command) { async runBuildCommand(directory, command) {
const spinner = ora('Building...').start();
setTimeout(() => spinner.succeed(), 5000);
const isChina = await isChinaDomain("www.npmjs.com"); const isChina = await isChinaDomain("www.npmjs.com");
if (isChina) { if (isChina) {
logger.info("Located in China, using npm/Rust CN mirror."); logger.info("Located in China, using npm/Rust CN mirror.");
@@ -557,19 +564,19 @@ async function mergeConfig(url, options, tauriConf) {
const platformIconMap = { const platformIconMap = {
win32: { win32: {
fileExt: '.ico', fileExt: '.ico',
path: `png/${name.toLowerCase()}_32.ico`, path: `png/${name.toLowerCase()}_256.ico`,
defaultIcon: 'png/icon_256.ico', defaultIcon: 'png/icon_256.ico',
message: 'Windows icon must be .ico and 256x256px.', message: 'Windows icon must be .ico and 256x256px.',
}, },
linux: { linux: {
fileExt: '.png', fileExt: '.png',
path: `png/${name.toLowerCase()}_32.png`, path: `png/${name.toLowerCase()}_512.png`,
defaultIcon: 'png/icon_512.png', defaultIcon: 'png/icon_512.png',
message: 'Linux icon must be .png and 512x512px.', message: 'Linux icon must be .png and 512x512px.',
}, },
darwin: { darwin: {
fileExt: '.icns', fileExt: '.icns',
path: `icons/${name.toLowerCase()}_32.icns`, path: `icons/${name.toLowerCase()}.icns`,
defaultIcon: 'icons/icon.icns', defaultIcon: 'icons/icon.icns',
message: 'MacOS icon must be .icns type.', message: 'MacOS icon must be .icns type.',
}, },
@@ -718,23 +725,24 @@ class LinuxBuilder extends BaseBuilder {
} }
} }
const { platform } = process;
const buildersMap = {
darwin: MacBuilder,
win32: WinBuilder,
linux: LinuxBuilder,
};
class BuilderProvider { class BuilderProvider {
static create() { static create() {
if (IS_MAC) { const Builder = buildersMap[platform];
return new MacBuilder(); if (!Builder) {
throw new Error('The current system is not supported!');
} }
if (IS_WIN) { return new Builder();
return new WinBuilder();
}
if (IS_LINUX) {
return new LinuxBuilder();
}
throw new Error('The current system is not supported!');
} }
} }
var name = "pake-cli"; var name = "pake-cli";
var version = "2.1.0"; var version = "2.1.1";
var description = "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。"; var description = "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。";
var engines = { var engines = {
node: ">=16.0.0" node: ">=16.0.0"
@@ -907,9 +915,11 @@ program
if (options.debug) { if (options.debug) {
log.setLevel('debug'); log.setLevel('debug');
} }
const spinner = ora('Preparing...').start();
const builder = BuilderProvider.create(); const builder = BuilderProvider.create();
await builder.prepare(); await builder.prepare();
const appOptions = await handleOptions(options, url); const appOptions = await handleOptions(options, url);
spinner.succeed();
log.debug('PakeAppOptions', appOptions); log.debug('PakeAppOptions', appOptions);
await builder.build(url, appOptions); await builder.build(url, appOptions);
}); });

View File

@@ -1,6 +1,6 @@
{ {
"name": "pake-cli", "name": "pake-cli",
"version": "2.1.0", "version": "2.1.1",
"description": "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。", "description": "🤱🏻 Turn any webpage into a desktop app with Rust. 🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App。",
"engines": { "engines": {
"node": ">=16.0.0" "node": ">=16.0.0"

View File

@@ -5,6 +5,10 @@
"types": [ "types": [
"node" "node"
], ],
"lib": [
"es2020",
"dom"
],
"esModuleInterop": true, "esModuleInterop": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,