pake-cli 2.1.1 โ 2.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js
CHANGED
|
@@ -1,162 +1,124 @@
|
|
|
1
|
-
import
|
|
2
|
-
import log from 'loglevel';
|
|
1
|
+
import chalk from 'chalk';
|
|
3
2
|
import { InvalidArgumentError, program } from 'commander';
|
|
4
|
-
import
|
|
3
|
+
import log from 'loglevel';
|
|
5
4
|
import path from 'path';
|
|
6
|
-
import
|
|
7
|
-
import { dir } from 'tmp-promise';
|
|
8
|
-
import { fileTypeFromBuffer } from 'file-type';
|
|
9
|
-
import chalk from 'chalk';
|
|
10
|
-
import { fileURLToPath } from 'url';
|
|
11
|
-
import psl from 'psl';
|
|
12
|
-
import isUrl from 'is-url';
|
|
13
|
-
import crypto from 'crypto';
|
|
5
|
+
import fsExtra from 'fs-extra';
|
|
14
6
|
import prompts from 'prompts';
|
|
15
7
|
import shelljs from 'shelljs';
|
|
8
|
+
import crypto from 'crypto';
|
|
9
|
+
import ora from 'ora';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
16
11
|
import dns from 'dns';
|
|
17
12
|
import http from 'http';
|
|
18
13
|
import { promisify } from 'util';
|
|
19
14
|
import updateNotifier from 'update-notifier';
|
|
15
|
+
import axios from 'axios';
|
|
16
|
+
import { dir } from 'tmp-promise';
|
|
17
|
+
import { fileTypeFromBuffer } from 'file-type';
|
|
18
|
+
import psl from 'psl';
|
|
19
|
+
import isUrl from 'is-url';
|
|
20
20
|
import fs from 'fs';
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
22
|
+
var name = "pake-cli";
|
|
23
|
+
var version = "2.1.7";
|
|
24
|
+
var description = "๐คฑ๐ป Turn any webpage into a desktop app with Rust. ๐คฑ๐ป ๅพ็ฎๅ็็จ Rust ๆๅ
็ฝ้กต็ๆๅพๅฐ็ๆก้ข Appใ";
|
|
25
|
+
var engines = {
|
|
26
|
+
node: ">=16.0.0"
|
|
27
|
+
};
|
|
28
|
+
var bin = {
|
|
29
|
+
pake: "./cli.js"
|
|
30
|
+
};
|
|
31
|
+
var repository = {
|
|
32
|
+
type: "git",
|
|
33
|
+
url: "https://github.com/tw93/pake.git"
|
|
34
|
+
};
|
|
35
|
+
var author = {
|
|
36
|
+
name: "Tw93",
|
|
37
|
+
email: "tw93@qq.com"
|
|
38
|
+
};
|
|
39
|
+
var keywords = [
|
|
40
|
+
"pake",
|
|
41
|
+
"pake-cli",
|
|
42
|
+
"rust",
|
|
43
|
+
"tauri",
|
|
44
|
+
"no-electron",
|
|
45
|
+
"productivity"
|
|
46
|
+
];
|
|
47
|
+
var files = [
|
|
48
|
+
"dist",
|
|
49
|
+
"src-tauri",
|
|
50
|
+
"cli.js"
|
|
51
|
+
];
|
|
52
|
+
var scripts = {
|
|
53
|
+
start: "npm run dev",
|
|
54
|
+
dev: "npm run tauri dev",
|
|
55
|
+
build: "npm run tauri build --release",
|
|
56
|
+
"build:mac": "npm run tauri build -- --target universal-apple-darwin",
|
|
57
|
+
"build:all-unix": "chmod +x ./script/build.sh && ./script/build.sh",
|
|
58
|
+
"build:all-windows": "pwsh ./script/build.ps1",
|
|
59
|
+
analyze: "cd src-tauri && cargo bloat --release --crates",
|
|
60
|
+
tauri: "tauri",
|
|
61
|
+
cli: "rollup -c rollup.config.js --watch",
|
|
62
|
+
"cli:build": "cross-env NODE_ENV=production rollup -c rollup.config.js",
|
|
63
|
+
prepublishOnly: "npm run cli:build"
|
|
64
|
+
};
|
|
65
|
+
var type = "module";
|
|
66
|
+
var exports = "./dist/pake.js";
|
|
67
|
+
var license = "MIT";
|
|
68
|
+
var dependencies = {
|
|
69
|
+
"@tauri-apps/api": "^1.4.0",
|
|
70
|
+
"@tauri-apps/cli": "^1.4.0",
|
|
71
|
+
axios: "^1.1.3",
|
|
72
|
+
chalk: "^5.1.2",
|
|
73
|
+
commander: "^11.0.0",
|
|
74
|
+
"file-type": "^18.0.0",
|
|
75
|
+
"fs-extra": "^11.1.0",
|
|
76
|
+
"is-url": "^1.2.4",
|
|
77
|
+
loglevel: "^1.8.1",
|
|
78
|
+
ora: "^6.1.2",
|
|
79
|
+
prompts: "^2.4.2",
|
|
80
|
+
psl: "^1.9.0",
|
|
81
|
+
shelljs: "^0.8.5",
|
|
82
|
+
"tmp-promise": "^3.0.3",
|
|
83
|
+
"update-notifier": "^6.0.2"
|
|
84
|
+
};
|
|
85
|
+
var devDependencies = {
|
|
86
|
+
"@rollup/plugin-alias": "^4.0.2",
|
|
87
|
+
"@rollup/plugin-commonjs": "^23.0.2",
|
|
88
|
+
"@rollup/plugin-json": "^5.0.2",
|
|
89
|
+
"@rollup/plugin-terser": "^0.1.0",
|
|
90
|
+
"@types/fs-extra": "^9.0.13",
|
|
91
|
+
"@types/is-url": "^1.2.30",
|
|
92
|
+
"@types/page-icon": "^0.3.4",
|
|
93
|
+
"@types/prompts": "^2.4.1",
|
|
94
|
+
"@types/psl": "^1.1.0",
|
|
95
|
+
"@types/shelljs": "^0.8.11",
|
|
96
|
+
"@types/tmp": "^0.2.3",
|
|
97
|
+
"@types/update-notifier": "^6.0.1",
|
|
98
|
+
"app-root-path": "^3.1.0",
|
|
99
|
+
"cross-env": "^7.0.3",
|
|
100
|
+
rollup: "^3.3.0",
|
|
101
|
+
"rollup-plugin-typescript2": "^0.34.1",
|
|
102
|
+
tslib: "^2.4.1",
|
|
103
|
+
typescript: "^4.9.3"
|
|
104
|
+
};
|
|
105
|
+
var packageJson = {
|
|
106
|
+
name: name,
|
|
107
|
+
version: version,
|
|
108
|
+
description: description,
|
|
109
|
+
engines: engines,
|
|
110
|
+
bin: bin,
|
|
111
|
+
repository: repository,
|
|
112
|
+
author: author,
|
|
113
|
+
keywords: keywords,
|
|
114
|
+
files: files,
|
|
115
|
+
scripts: scripts,
|
|
116
|
+
type: type,
|
|
117
|
+
exports: exports,
|
|
118
|
+
license: license,
|
|
119
|
+
dependencies: dependencies,
|
|
120
|
+
devDependencies: devDependencies
|
|
38
121
|
};
|
|
39
|
-
|
|
40
|
-
// Convert the current module URL to a file path
|
|
41
|
-
const currentModulePath = fileURLToPath(import.meta.url);
|
|
42
|
-
// Resolve the parent directory of the current module
|
|
43
|
-
const npmDirectory = path.join(path.dirname(currentModulePath), '..');
|
|
44
|
-
|
|
45
|
-
const { platform: platform$2 } = process;
|
|
46
|
-
const IS_MAC = platform$2 === 'darwin';
|
|
47
|
-
const IS_WIN = platform$2 === 'win32';
|
|
48
|
-
const IS_LINUX = platform$2 === 'linux';
|
|
49
|
-
|
|
50
|
-
async function handleIcon(options) {
|
|
51
|
-
if (options.icon) {
|
|
52
|
-
if (options.icon.startsWith('http')) {
|
|
53
|
-
return downloadIcon(options.icon);
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
return path.resolve(options.icon);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
logger.info('No app icon provided, default icon used. Use --icon option to assign an icon.');
|
|
61
|
-
const iconPath = IS_WIN ? 'src-tauri/png/icon_256.ico' : IS_LINUX ? 'src-tauri/png/icon_512.png' : 'src-tauri/icons/icon.icns';
|
|
62
|
-
return path.join(npmDirectory, iconPath);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
async function downloadIcon(iconUrl) {
|
|
66
|
-
try {
|
|
67
|
-
const iconResponse = await axios.get(iconUrl, { responseType: 'arraybuffer' });
|
|
68
|
-
const iconData = await iconResponse.data;
|
|
69
|
-
if (!iconData) {
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
const fileDetails = await fileTypeFromBuffer(iconData);
|
|
73
|
-
if (!fileDetails) {
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
const { path: tempPath } = await dir();
|
|
77
|
-
const iconPath = `${tempPath}/icon.${fileDetails.ext}`;
|
|
78
|
-
await fsExtra.outputFile(iconPath, iconData);
|
|
79
|
-
return iconPath;
|
|
80
|
-
}
|
|
81
|
-
catch (error) {
|
|
82
|
-
if (error.response && error.response.status === 404) {
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
throw error;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Extracts the domain from a given URL.
|
|
90
|
-
function getDomain(inputUrl) {
|
|
91
|
-
try {
|
|
92
|
-
const url = new URL(inputUrl);
|
|
93
|
-
// Use PSL to parse domain names.
|
|
94
|
-
const parsed = psl.parse(url.hostname);
|
|
95
|
-
// If domain is available, split it and return the SLD.
|
|
96
|
-
if ("domain" in parsed && parsed.domain) {
|
|
97
|
-
return parsed.domain.split('.')[0];
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
catch (error) {
|
|
104
|
-
return null;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
// Appends 'https://' protocol to the URL if not present.
|
|
108
|
-
function appendProtocol(inputUrl) {
|
|
109
|
-
try {
|
|
110
|
-
new URL(inputUrl);
|
|
111
|
-
return inputUrl;
|
|
112
|
-
}
|
|
113
|
-
catch {
|
|
114
|
-
return `https://${inputUrl}`;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
// Normalizes the URL by ensuring it has a protocol and is valid.
|
|
118
|
-
function normalizeUrl(urlToNormalize) {
|
|
119
|
-
const urlWithProtocol = appendProtocol(urlToNormalize);
|
|
120
|
-
if (isUrl(urlWithProtocol)) {
|
|
121
|
-
return urlWithProtocol;
|
|
122
|
-
}
|
|
123
|
-
else {
|
|
124
|
-
throw new Error(`Your url "${urlWithProtocol}" is invalid`);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Generates an identifier based on the given URL.
|
|
129
|
-
function getIdentifier(url) {
|
|
130
|
-
const postFixHash = crypto.createHash('md5')
|
|
131
|
-
.update(url)
|
|
132
|
-
.digest('hex')
|
|
133
|
-
.substring(0, 6);
|
|
134
|
-
return `pake-${postFixHash}`;
|
|
135
|
-
}
|
|
136
|
-
async function promptText(message, initial) {
|
|
137
|
-
const response = await prompts({
|
|
138
|
-
type: 'text',
|
|
139
|
-
name: 'content',
|
|
140
|
-
message,
|
|
141
|
-
initial,
|
|
142
|
-
});
|
|
143
|
-
return response.content;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
async function handleOptions(options, url) {
|
|
147
|
-
const appOptions = {
|
|
148
|
-
...options,
|
|
149
|
-
identifier: getIdentifier(url),
|
|
150
|
-
};
|
|
151
|
-
let urlExists = await fsExtra.pathExists(url);
|
|
152
|
-
if (!appOptions.name) {
|
|
153
|
-
const defaultName = urlExists ? "" : getDomain(url);
|
|
154
|
-
const promptMessage = 'Enter your application name';
|
|
155
|
-
appOptions.name = await promptText(promptMessage, defaultName);
|
|
156
|
-
}
|
|
157
|
-
appOptions.icon = await handleIcon(appOptions);
|
|
158
|
-
return appOptions;
|
|
159
|
-
}
|
|
160
122
|
|
|
161
123
|
var windows = [
|
|
162
124
|
{
|
|
@@ -343,11 +305,11 @@ var LinuxConf = {
|
|
|
343
305
|
const platformConfigs = {
|
|
344
306
|
win32: WinConf,
|
|
345
307
|
darwin: MacConf,
|
|
346
|
-
linux: LinuxConf
|
|
308
|
+
linux: LinuxConf,
|
|
347
309
|
};
|
|
348
|
-
const { platform: platform$
|
|
310
|
+
const { platform: platform$2 } = process;
|
|
349
311
|
// @ts-ignore
|
|
350
|
-
const platformConfig = platformConfigs[platform$
|
|
312
|
+
const platformConfig = platformConfigs[platform$2];
|
|
351
313
|
let tauriConfig = {
|
|
352
314
|
tauri: {
|
|
353
315
|
...CommonConf.tauri,
|
|
@@ -355,42 +317,99 @@ let tauriConfig = {
|
|
|
355
317
|
},
|
|
356
318
|
package: CommonConf.package,
|
|
357
319
|
build: CommonConf.build,
|
|
358
|
-
pake: pakeConf
|
|
320
|
+
pake: pakeConf,
|
|
359
321
|
};
|
|
360
322
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
323
|
+
// Generates an identifier based on the given URL.
|
|
324
|
+
function getIdentifier(url) {
|
|
325
|
+
const postFixHash = crypto.createHash('md5').update(url).digest('hex').substring(0, 6);
|
|
326
|
+
return `pake-${postFixHash}`;
|
|
327
|
+
}
|
|
328
|
+
async function promptText(message, initial) {
|
|
329
|
+
const response = await prompts({
|
|
330
|
+
type: 'text',
|
|
331
|
+
name: 'content',
|
|
332
|
+
message,
|
|
333
|
+
initial,
|
|
334
|
+
});
|
|
335
|
+
return response.content;
|
|
336
|
+
}
|
|
337
|
+
function capitalizeFirstLetter(string) {
|
|
338
|
+
return string.charAt(0).toUpperCase() + string.slice(1);
|
|
339
|
+
}
|
|
340
|
+
function getSpinner(text) {
|
|
341
|
+
const loadingType = {
|
|
342
|
+
interval: 80,
|
|
343
|
+
frames: ['โฆ', 'โถ', 'โบ', 'โต', 'โธ', 'โน', 'โบ'],
|
|
344
|
+
};
|
|
345
|
+
return ora({
|
|
346
|
+
text: `${chalk.cyan(text)}\n`,
|
|
347
|
+
spinner: loadingType,
|
|
348
|
+
color: 'cyan',
|
|
349
|
+
}).start();
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const { platform: platform$1 } = process;
|
|
353
|
+
const IS_MAC = platform$1 === 'darwin';
|
|
354
|
+
const IS_WIN = platform$1 === 'win32';
|
|
355
|
+
const IS_LINUX = platform$1 === 'linux';
|
|
356
|
+
|
|
357
|
+
// Convert the current module URL to a file path
|
|
358
|
+
const currentModulePath = fileURLToPath(import.meta.url);
|
|
359
|
+
// Resolve the parent directory of the current module
|
|
360
|
+
const npmDirectory = path.join(path.dirname(currentModulePath), '..');
|
|
361
|
+
|
|
362
|
+
function shellExec(command) {
|
|
363
|
+
return new Promise((resolve, reject) => {
|
|
364
|
+
shelljs.exec(command, { async: true, silent: false, cwd: npmDirectory }, code => {
|
|
365
|
+
if (code === 0) {
|
|
366
|
+
resolve(0);
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
reject(new Error(`${code}`));
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const logger = {
|
|
376
|
+
info(...msg) {
|
|
377
|
+
log.info(...msg.map(m => chalk.white(m)));
|
|
378
|
+
},
|
|
379
|
+
debug(...msg) {
|
|
380
|
+
log.debug(...msg);
|
|
381
|
+
},
|
|
382
|
+
error(...msg) {
|
|
383
|
+
log.error(...msg.map(m => chalk.red(m)));
|
|
384
|
+
},
|
|
385
|
+
warn(...msg) {
|
|
386
|
+
log.info(...msg.map(m => chalk.yellow(m)));
|
|
387
|
+
},
|
|
388
|
+
success(...msg) {
|
|
389
|
+
log.info(...msg.map(m => chalk.green(m)));
|
|
390
|
+
},
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
const resolve = promisify(dns.resolve);
|
|
394
|
+
const ping = async (host) => {
|
|
395
|
+
const lookup = promisify(dns.lookup);
|
|
377
396
|
const ip = await lookup(host);
|
|
378
397
|
const start = new Date();
|
|
379
398
|
// Prevent timeouts from affecting user experience.
|
|
380
399
|
const requestPromise = new Promise((resolve, reject) => {
|
|
381
|
-
const req = http.get(`http://${ip.address}`,
|
|
400
|
+
const req = http.get(`http://${ip.address}`, res => {
|
|
382
401
|
const delay = new Date().getTime() - start.getTime();
|
|
383
402
|
res.resume();
|
|
384
403
|
resolve(delay);
|
|
385
404
|
});
|
|
386
|
-
req.on('error',
|
|
405
|
+
req.on('error', err => {
|
|
387
406
|
reject(err);
|
|
388
407
|
});
|
|
389
408
|
});
|
|
390
409
|
const timeoutPromise = new Promise((_, reject) => {
|
|
391
410
|
setTimeout(() => {
|
|
392
411
|
reject(new Error('Request timed out after 3 seconds'));
|
|
393
|
-
},
|
|
412
|
+
}, 1000);
|
|
394
413
|
});
|
|
395
414
|
return Promise.race([requestPromise, timeoutPromise]);
|
|
396
415
|
};
|
|
@@ -401,36 +420,35 @@ async function isChinaDomain(domain) {
|
|
|
401
420
|
}
|
|
402
421
|
catch (error) {
|
|
403
422
|
logger.debug(`${domain} can't be parse!`);
|
|
404
|
-
return
|
|
423
|
+
return true;
|
|
405
424
|
}
|
|
406
425
|
}
|
|
407
426
|
async function isChinaIP(ip, domain) {
|
|
408
427
|
try {
|
|
409
428
|
const delay = await ping(ip);
|
|
410
429
|
logger.debug(`${domain} latency is ${delay} ms`);
|
|
411
|
-
return delay >
|
|
430
|
+
return delay > 1000;
|
|
412
431
|
}
|
|
413
432
|
catch (error) {
|
|
414
433
|
logger.debug(`ping ${domain} failed!`);
|
|
415
|
-
return
|
|
434
|
+
return true;
|
|
416
435
|
}
|
|
417
436
|
}
|
|
418
437
|
|
|
419
438
|
async function installRust() {
|
|
420
|
-
const isInChina = await isChinaDomain(
|
|
439
|
+
const isInChina = await isChinaDomain('sh.rustup.rs');
|
|
421
440
|
const rustInstallScriptForMac = isInChina
|
|
422
441
|
? 'export RUSTUP_DIST_SERVER="https://rsproxy.cn" && export RUSTUP_UPDATE_ROOT="https://rsproxy.cn/rustup" && curl --proto "=https" --tlsv1.2 -sSf https://rsproxy.cn/rustup-init.sh | sh'
|
|
423
442
|
: "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y";
|
|
424
443
|
const rustInstallScriptForWindows = 'winget install --id Rustlang.Rustup';
|
|
425
|
-
const spinner =
|
|
444
|
+
const spinner = getSpinner('Downloading Rust...');
|
|
426
445
|
try {
|
|
427
446
|
await shellExec(IS_WIN ? rustInstallScriptForWindows : rustInstallScriptForMac);
|
|
428
|
-
spinner.succeed();
|
|
447
|
+
spinner.succeed(chalk.green('Rust installed successfully!'));
|
|
429
448
|
}
|
|
430
449
|
catch (error) {
|
|
431
450
|
console.error('Error installing Rust:', error.message);
|
|
432
|
-
spinner.fail();
|
|
433
|
-
//@ts-ignore
|
|
451
|
+
spinner.fail(chalk.red('Rust installation failed!'));
|
|
434
452
|
process.exit(1);
|
|
435
453
|
}
|
|
436
454
|
}
|
|
@@ -438,50 +456,8 @@ function checkRustInstalled() {
|
|
|
438
456
|
return shelljs.exec('rustc --version', { silent: true }).code === 0;
|
|
439
457
|
}
|
|
440
458
|
|
|
441
|
-
class BaseBuilder {
|
|
442
|
-
async prepare() {
|
|
443
|
-
// Windows and Linux need to install necessary build tools.
|
|
444
|
-
if (!IS_MAC) {
|
|
445
|
-
logger.info('Install Rust and required build tools to build the app.');
|
|
446
|
-
logger.info('See more in https://tauri.app/v1/guides/getting-started/prerequisites#installing.');
|
|
447
|
-
}
|
|
448
|
-
if (checkRustInstalled()) {
|
|
449
|
-
return;
|
|
450
|
-
}
|
|
451
|
-
const res = await prompts({
|
|
452
|
-
type: 'confirm',
|
|
453
|
-
message: 'Rust not detected. Install now?',
|
|
454
|
-
name: 'value',
|
|
455
|
-
});
|
|
456
|
-
if (res.value) {
|
|
457
|
-
await installRust();
|
|
458
|
-
}
|
|
459
|
-
else {
|
|
460
|
-
logger.error('Error: Rust required to package your webapp!');
|
|
461
|
-
process.exit(2);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
async runBuildCommand(directory, command) {
|
|
465
|
-
const spinner = ora('Building...').start();
|
|
466
|
-
setTimeout(() => spinner.succeed(), 5000);
|
|
467
|
-
const isChina = await isChinaDomain("www.npmjs.com");
|
|
468
|
-
if (isChina) {
|
|
469
|
-
logger.info("Located in China, using npm/Rust CN mirror.");
|
|
470
|
-
const rustProjectDir = path.join(directory, 'src-tauri', ".cargo");
|
|
471
|
-
await fsExtra.ensureDir(rustProjectDir);
|
|
472
|
-
const projectCnConf = path.join(directory, "src-tauri", "rust_proxy.toml");
|
|
473
|
-
const projectConf = path.join(rustProjectDir, "config");
|
|
474
|
-
await fsExtra.copy(projectCnConf, projectConf);
|
|
475
|
-
await shellExec(`cd "${directory}" && npm install --registry=https://registry.npmmirror.com && ${command}`);
|
|
476
|
-
}
|
|
477
|
-
else {
|
|
478
|
-
await shellExec(`cd "${directory}" && npm install && ${command}`);
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
|
|
483
459
|
async function mergeConfig(url, options, tauriConf) {
|
|
484
|
-
const { width, height, fullscreen, transparent,
|
|
460
|
+
const { width, height, fullscreen, transparent, userAgent, showMenu, showSystemTray, systemTrayIcon, iterCopyFile, identifier, name, resizable = true, } = options;
|
|
485
461
|
const { platform } = process;
|
|
486
462
|
// Set Windows parameters.
|
|
487
463
|
const tauriConfWindowOptions = {
|
|
@@ -492,28 +468,12 @@ async function mergeConfig(url, options, tauriConf) {
|
|
|
492
468
|
resizable,
|
|
493
469
|
};
|
|
494
470
|
Object.assign(tauriConf.pake.windows[0], { url, ...tauriConfWindowOptions });
|
|
495
|
-
// Determine whether the package name is valid.
|
|
496
|
-
// for Linux, package name must be a-z, 0-9 or "-", not allow to A-Z and other
|
|
497
|
-
const platformRegexMapping = {
|
|
498
|
-
linux: /[0-9]*[a-z]+[0-9]*\-?[0-9]*[a-z]*[0-9]*\-?[0-9]*[a-z]*[0-9]*/,
|
|
499
|
-
default: /([0-9]*[a-zA-Z]+[0-9]*)+/,
|
|
500
|
-
};
|
|
501
|
-
const reg = platformRegexMapping[platform] || platformRegexMapping.default;
|
|
502
|
-
const nameCheck = reg.test(name) && reg.exec(name)[0].length === name.length;
|
|
503
|
-
if (!nameCheck) {
|
|
504
|
-
const errorMsg = platform === 'linux'
|
|
505
|
-
? `Package name is invalid. It should only include lowercase letters, numbers, and dashes, and must contain at least one lowercase letter. Examples: com-123-xxx, 123pan, pan123, weread, we-read.`
|
|
506
|
-
: `Package name is invalid. It should only include letters and numbers, and must contain at least one letter. Examples: 123pan, 123Pan, Pan123, weread, WeRead, WERead.`;
|
|
507
|
-
logger.error(errorMsg);
|
|
508
|
-
process.exit();
|
|
509
|
-
}
|
|
510
471
|
tauriConf.package.productName = name;
|
|
511
472
|
tauriConf.tauri.bundle.identifier = identifier;
|
|
512
|
-
//
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
logger.warn('Your input might be a local file.');
|
|
473
|
+
//Judge the type of URL, whether it is a file or a website.
|
|
474
|
+
const pathExists = await fsExtra.pathExists(url);
|
|
475
|
+
if (pathExists) {
|
|
476
|
+
logger.warn('โผ Your input might be a local file.');
|
|
517
477
|
tauriConf.pake.windows[0].url_type = 'local';
|
|
518
478
|
const fileName = path.basename(url);
|
|
519
479
|
const dirName = path.dirname(url);
|
|
@@ -527,7 +487,7 @@ async function mergeConfig(url, options, tauriConf) {
|
|
|
527
487
|
fsExtra.moveSync(distDir, distBakDir, { overwrite: true });
|
|
528
488
|
fsExtra.copySync(dirName, distDir, { overwrite: true });
|
|
529
489
|
const filesToCopyBack = ['cli.js', 'about_pake.html'];
|
|
530
|
-
await Promise.all(filesToCopyBack.map(
|
|
490
|
+
await Promise.all(filesToCopyBack.map(file => fsExtra.copy(path.join(distBakDir, file), path.join(distDir, file))));
|
|
531
491
|
}
|
|
532
492
|
tauriConf.pake.windows[0].url = fileName;
|
|
533
493
|
tauriConf.pake.windows[0].url_type = 'local';
|
|
@@ -535,8 +495,7 @@ async function mergeConfig(url, options, tauriConf) {
|
|
|
535
495
|
else {
|
|
536
496
|
tauriConf.pake.windows[0].url_type = 'web';
|
|
537
497
|
// Set the secure domain for calling window.__TAURI__ to the application domain that has been set.
|
|
538
|
-
tauriConf.tauri.security.dangerousRemoteDomainIpcAccess[0].domain =
|
|
539
|
-
new URL(url).hostname;
|
|
498
|
+
tauriConf.tauri.security.dangerousRemoteDomainIpcAccess[0].domain = new URL(url).hostname;
|
|
540
499
|
}
|
|
541
500
|
const platformMap = {
|
|
542
501
|
win32: 'windows',
|
|
@@ -554,10 +513,11 @@ async function mergeConfig(url, options, tauriConf) {
|
|
|
554
513
|
delete tauriConf.tauri.bundle.deb.files;
|
|
555
514
|
const validTargets = ['all', 'deb', 'appimage'];
|
|
556
515
|
if (validTargets.includes(options.targets)) {
|
|
557
|
-
tauriConf.tauri.bundle.targets =
|
|
516
|
+
tauriConf.tauri.bundle.targets =
|
|
517
|
+
options.targets === 'all' ? ['deb', 'appimage'] : [options.targets];
|
|
558
518
|
}
|
|
559
519
|
else {
|
|
560
|
-
logger.warn(
|
|
520
|
+
logger.warn(`โผ The target must be one of ${validTargets.join(', ')}, the default 'deb' will be used.`);
|
|
561
521
|
}
|
|
562
522
|
}
|
|
563
523
|
// Set icon.
|
|
@@ -578,7 +538,7 @@ async function mergeConfig(url, options, tauriConf) {
|
|
|
578
538
|
fileExt: '.icns',
|
|
579
539
|
path: `icons/${name.toLowerCase()}.icns`,
|
|
580
540
|
defaultIcon: 'icons/icon.icns',
|
|
581
|
-
message: '
|
|
541
|
+
message: 'macOS icon must be .icns type.',
|
|
582
542
|
},
|
|
583
543
|
};
|
|
584
544
|
const iconInfo = platformIconMap[platform];
|
|
@@ -588,7 +548,7 @@ async function mergeConfig(url, options, tauriConf) {
|
|
|
588
548
|
let customIconExt = path.extname(options.icon).toLowerCase();
|
|
589
549
|
if (customIconExt !== iconInfo.fileExt) {
|
|
590
550
|
updateIconPath = false;
|
|
591
|
-
logger.warn(
|
|
551
|
+
logger.warn(`โผ ${iconInfo.message}, but you give ${customIconExt}`);
|
|
592
552
|
tauriConf.tauri.bundle.icon = [iconInfo.defaultIcon];
|
|
593
553
|
}
|
|
594
554
|
else {
|
|
@@ -600,11 +560,11 @@ async function mergeConfig(url, options, tauriConf) {
|
|
|
600
560
|
tauriConf.tauri.bundle.icon = [options.icon];
|
|
601
561
|
}
|
|
602
562
|
else {
|
|
603
|
-
logger.warn(
|
|
563
|
+
logger.warn(`โผ Icon will remain as default.`);
|
|
604
564
|
}
|
|
605
565
|
}
|
|
606
566
|
else {
|
|
607
|
-
logger.warn('Custom icon path may be invalid
|
|
567
|
+
logger.warn('โผ Custom icon path may be invalid, default icon will be used instead.');
|
|
608
568
|
tauriConf.tauri.bundle.icon = [iconInfo.defaultIcon];
|
|
609
569
|
}
|
|
610
570
|
// Set tray icon path.
|
|
@@ -620,13 +580,13 @@ async function mergeConfig(url, options, tauriConf) {
|
|
|
620
580
|
await fsExtra.copy(systemTrayIcon, trayIcoPath);
|
|
621
581
|
}
|
|
622
582
|
else {
|
|
623
|
-
logger.warn(
|
|
624
|
-
logger.warn(
|
|
583
|
+
logger.warn(`โผ System tray icon must be .ico or .png, but you provided ${iconExt}.`);
|
|
584
|
+
logger.warn(`โผ Default system tray icon will be used.`);
|
|
625
585
|
}
|
|
626
586
|
}
|
|
627
587
|
catch {
|
|
628
|
-
logger.warn(
|
|
629
|
-
logger.warn(
|
|
588
|
+
logger.warn(`โผ ${systemTrayIcon} not exists!`);
|
|
589
|
+
logger.warn(`โผ Default system tray icon will remain unchanged.`);
|
|
630
590
|
}
|
|
631
591
|
}
|
|
632
592
|
tauriConf.tauri.systemTray.iconPath = trayIconPath;
|
|
@@ -648,80 +608,141 @@ async function mergeConfig(url, options, tauriConf) {
|
|
|
648
608
|
await fsExtra.writeJson(configJsonPath, tauriConf2, { spaces: 4 });
|
|
649
609
|
}
|
|
650
610
|
|
|
651
|
-
class
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
if (
|
|
657
|
-
|
|
658
|
-
|
|
611
|
+
class BaseBuilder {
|
|
612
|
+
constructor(options) {
|
|
613
|
+
this.options = options;
|
|
614
|
+
}
|
|
615
|
+
async prepare() {
|
|
616
|
+
if (!IS_MAC) {
|
|
617
|
+
logger.info('โบ The first use requires installing system dependencies.');
|
|
618
|
+
logger.info('โบ See more in https://tauri.app/v1/guides/getting-started/prerequisites.');
|
|
619
|
+
}
|
|
620
|
+
if (!checkRustInstalled()) {
|
|
621
|
+
const res = await prompts({
|
|
622
|
+
type: 'confirm',
|
|
623
|
+
message: 'Rust not detected. Install now?',
|
|
624
|
+
name: 'value',
|
|
625
|
+
});
|
|
626
|
+
if (res.value) {
|
|
627
|
+
await installRust();
|
|
628
|
+
}
|
|
629
|
+
else {
|
|
630
|
+
logger.error('โ Rust required to package your webapp.');
|
|
631
|
+
process.exit(0);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
const isChina = await isChinaDomain('www.npmjs.com');
|
|
635
|
+
const spinner = getSpinner('Installing package...');
|
|
636
|
+
if (isChina) {
|
|
637
|
+
logger.info('โบ Located in China, using npm/rsProxy CN mirror.');
|
|
638
|
+
const rustProjectDir = path.join(npmDirectory, 'src-tauri', '.cargo');
|
|
639
|
+
await fsExtra.ensureDir(rustProjectDir);
|
|
640
|
+
const projectCnConf = path.join(npmDirectory, 'src-tauri', 'rust_proxy.toml');
|
|
641
|
+
const projectConf = path.join(rustProjectDir, 'config');
|
|
642
|
+
await fsExtra.copy(projectCnConf, projectConf);
|
|
643
|
+
await shellExec(`cd "${npmDirectory}" && npm install --registry=https://registry.npmmirror.com`);
|
|
659
644
|
}
|
|
660
645
|
else {
|
|
661
|
-
await
|
|
662
|
-
let arch = process.arch === "arm64" ? "aarch64" : process.arch;
|
|
663
|
-
dmgName = `${name}_${tauriConfig.package.version}_${arch}.dmg`;
|
|
646
|
+
await shellExec(`cd "${npmDirectory}" && npm install`);
|
|
664
647
|
}
|
|
665
|
-
|
|
666
|
-
|
|
648
|
+
spinner.succeed(chalk.green('Package installed!'));
|
|
649
|
+
}
|
|
650
|
+
async build(url) {
|
|
651
|
+
await this.buildAndCopy(url, this.options.targets);
|
|
652
|
+
}
|
|
653
|
+
async buildAndCopy(url, target) {
|
|
654
|
+
const { name } = this.options;
|
|
655
|
+
await mergeConfig(url, this.options, tauriConfig);
|
|
656
|
+
// Build app
|
|
657
|
+
const spinner = getSpinner('Building app...');
|
|
658
|
+
setTimeout(() => spinner.stop(), 3000);
|
|
659
|
+
await shellExec(`cd ${npmDirectory} && ${this.getBuildCommand()}`);
|
|
660
|
+
// Copy app
|
|
661
|
+
const fileName = this.getFileName();
|
|
662
|
+
const fileType = this.getFileType(target);
|
|
663
|
+
const appPath = this.getBuildAppPath(npmDirectory, fileName, fileType);
|
|
664
|
+
const distPath = path.resolve(`${name}.${fileType}`);
|
|
667
665
|
await fsExtra.copy(appPath, distPath);
|
|
668
666
|
await fsExtra.remove(appPath);
|
|
669
|
-
logger.success('Build success!');
|
|
670
|
-
logger.success('App installer located in', distPath);
|
|
667
|
+
logger.success('โ Build success!');
|
|
668
|
+
logger.success('โ App installer located in', distPath);
|
|
669
|
+
}
|
|
670
|
+
getFileType(target) {
|
|
671
|
+
return target;
|
|
672
|
+
}
|
|
673
|
+
getBuildCommand() {
|
|
674
|
+
return 'npm run build';
|
|
675
|
+
}
|
|
676
|
+
getBasePath() {
|
|
677
|
+
return 'src-tauri/target/release/bundle/';
|
|
678
|
+
}
|
|
679
|
+
getBuildAppPath(npmDirectory, fileName, fileType) {
|
|
680
|
+
return path.join(npmDirectory, this.getBasePath(), fileType.toLowerCase(), `${fileName}.${fileType}`);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
class MacBuilder extends BaseBuilder {
|
|
685
|
+
constructor(options) {
|
|
686
|
+
super(options);
|
|
687
|
+
this.options.targets = 'dmg';
|
|
688
|
+
}
|
|
689
|
+
getFileName() {
|
|
690
|
+
const { name } = this.options;
|
|
691
|
+
let arch;
|
|
692
|
+
if (this.options.multiArch) {
|
|
693
|
+
arch = 'universal';
|
|
694
|
+
}
|
|
695
|
+
else {
|
|
696
|
+
arch = process.arch === 'arm64' ? 'aarch64' : process.arch;
|
|
697
|
+
}
|
|
698
|
+
return `${name}_${tauriConfig.package.version}_${arch}`;
|
|
699
|
+
}
|
|
700
|
+
getBuildCommand() {
|
|
701
|
+
return this.options.multiArch ? 'npm run build:mac' : super.getBuildCommand();
|
|
671
702
|
}
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
703
|
+
getBasePath() {
|
|
704
|
+
return this.options.multiArch
|
|
705
|
+
? 'src-tauri/target/universal-apple-darwin/release/bundle'
|
|
706
|
+
: super.getBasePath();
|
|
675
707
|
}
|
|
676
708
|
}
|
|
677
709
|
|
|
678
710
|
class WinBuilder extends BaseBuilder {
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
await this.runBuildCommand(npmDirectory, 'npm run build');
|
|
683
|
-
const language = tauriConfig.tauri.bundle.windows.wix.language[0];
|
|
684
|
-
const arch = process.arch;
|
|
685
|
-
const msiName = `${name}_${tauriConfig.package.version}_${arch}_${language}.msi`;
|
|
686
|
-
const appPath = this.getBuildAppPath(npmDirectory, msiName);
|
|
687
|
-
const distPath = path.resolve(`${name}.msi`);
|
|
688
|
-
await fsExtra.copy(appPath, distPath);
|
|
689
|
-
await fsExtra.remove(appPath);
|
|
690
|
-
logger.success('Build success!');
|
|
691
|
-
logger.success('App installer located in', distPath);
|
|
711
|
+
constructor(options) {
|
|
712
|
+
super(options);
|
|
713
|
+
this.options.targets = 'msi';
|
|
692
714
|
}
|
|
693
|
-
|
|
694
|
-
|
|
715
|
+
getFileName() {
|
|
716
|
+
const { name } = this.options;
|
|
717
|
+
const { arch } = process;
|
|
718
|
+
const language = tauriConfig.tauri.bundle.windows.wix.language[0];
|
|
719
|
+
return `${name}_${tauriConfig.package.version}_${arch}_${language}`;
|
|
695
720
|
}
|
|
696
721
|
}
|
|
697
722
|
|
|
698
723
|
class LinuxBuilder extends BaseBuilder {
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
const
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
const appImageName = `${name}_${tauriConfig.package.version}_${arch}.AppImage`;
|
|
715
|
-
const appImagePath = this.getBuildAppPath(npmDirectory, "appimage", appImageName);
|
|
716
|
-
const distAppPath = path.resolve(`${name}.AppImage`);
|
|
717
|
-
await fsExtra.copy(appImagePath, distAppPath);
|
|
718
|
-
await fsExtra.remove(appImagePath);
|
|
719
|
-
logger.success('Build AppImage success!');
|
|
720
|
-
logger.success('AppImage installer located in', distAppPath);
|
|
724
|
+
constructor(options) {
|
|
725
|
+
super(options);
|
|
726
|
+
}
|
|
727
|
+
getFileName() {
|
|
728
|
+
const { name } = this.options;
|
|
729
|
+
const arch = process.arch === 'x64' ? 'amd64' : process.arch;
|
|
730
|
+
return `${name}_${tauriConfig.package.version}_${arch}`;
|
|
731
|
+
}
|
|
732
|
+
// Customize it, considering that there are all targets.
|
|
733
|
+
async build(url) {
|
|
734
|
+
const targetTypes = ['deb', 'appimage'];
|
|
735
|
+
for (const target of targetTypes) {
|
|
736
|
+
if (this.options.targets === target || this.options.targets === 'all') {
|
|
737
|
+
await this.buildAndCopy(url, target);
|
|
738
|
+
}
|
|
721
739
|
}
|
|
722
740
|
}
|
|
723
|
-
|
|
724
|
-
|
|
741
|
+
getFileType(target) {
|
|
742
|
+
if (target === 'appimage') {
|
|
743
|
+
return 'AppImage';
|
|
744
|
+
}
|
|
745
|
+
return super.getFileType(target);
|
|
725
746
|
}
|
|
726
747
|
}
|
|
727
748
|
|
|
@@ -732,120 +753,166 @@ const buildersMap = {
|
|
|
732
753
|
linux: LinuxBuilder,
|
|
733
754
|
};
|
|
734
755
|
class BuilderProvider {
|
|
735
|
-
static create() {
|
|
756
|
+
static create(options) {
|
|
736
757
|
const Builder = buildersMap[platform];
|
|
737
758
|
if (!Builder) {
|
|
738
759
|
throw new Error('The current system is not supported!');
|
|
739
760
|
}
|
|
740
|
-
return new Builder();
|
|
761
|
+
return new Builder(options);
|
|
741
762
|
}
|
|
742
763
|
}
|
|
743
764
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
email: "tw93@qq.com"
|
|
760
|
-
};
|
|
761
|
-
var keywords = [
|
|
762
|
-
"pake",
|
|
763
|
-
"pake-cli",
|
|
764
|
-
"rust",
|
|
765
|
-
"tauri",
|
|
766
|
-
"no-electron",
|
|
767
|
-
"productivity"
|
|
768
|
-
];
|
|
769
|
-
var files = [
|
|
770
|
-
"dist",
|
|
771
|
-
"src-tauri",
|
|
772
|
-
"cli.js"
|
|
773
|
-
];
|
|
774
|
-
var scripts = {
|
|
775
|
-
start: "npm run dev",
|
|
776
|
-
dev: "npm run tauri dev",
|
|
777
|
-
build: "npm run tauri build --release",
|
|
778
|
-
"build:mac": "npm run tauri build -- --target universal-apple-darwin",
|
|
779
|
-
"build:all-unix": "chmod +x ./script/build.sh && ./script/build.sh",
|
|
780
|
-
"build:all-windows": "pwsh ./script/build.ps1",
|
|
781
|
-
analyze: "cd src-tauri && cargo bloat --release --crates",
|
|
782
|
-
tauri: "tauri",
|
|
783
|
-
cli: "rollup -c rollup.config.js --watch",
|
|
784
|
-
"cli:build": "cross-env NODE_ENV=production rollup -c rollup.config.js",
|
|
785
|
-
prepublishOnly: "npm run cli:build"
|
|
786
|
-
};
|
|
787
|
-
var type = "module";
|
|
788
|
-
var exports = "./dist/pake.js";
|
|
789
|
-
var license = "MIT";
|
|
790
|
-
var dependencies = {
|
|
791
|
-
"@tauri-apps/api": "^1.4.0",
|
|
792
|
-
"@tauri-apps/cli": "^1.4.0",
|
|
793
|
-
axios: "^1.1.3",
|
|
794
|
-
chalk: "^5.1.2",
|
|
795
|
-
commander: "^11.0.0",
|
|
796
|
-
"file-type": "^18.0.0",
|
|
797
|
-
"fs-extra": "^11.1.0",
|
|
798
|
-
"is-url": "^1.2.4",
|
|
799
|
-
loglevel: "^1.8.1",
|
|
800
|
-
ora: "^6.1.2",
|
|
801
|
-
prompts: "^2.4.2",
|
|
802
|
-
psl: "^1.9.0",
|
|
803
|
-
shelljs: "^0.8.5",
|
|
804
|
-
"tmp-promise": "^3.0.3",
|
|
805
|
-
"update-notifier": "^6.0.2"
|
|
806
|
-
};
|
|
807
|
-
var devDependencies = {
|
|
808
|
-
"@rollup/plugin-alias": "^4.0.2",
|
|
809
|
-
"@rollup/plugin-commonjs": "^23.0.2",
|
|
810
|
-
"@rollup/plugin-json": "^5.0.2",
|
|
811
|
-
"@rollup/plugin-terser": "^0.1.0",
|
|
812
|
-
"@types/fs-extra": "^9.0.13",
|
|
813
|
-
"@types/is-url": "^1.2.30",
|
|
814
|
-
"@types/page-icon": "^0.3.4",
|
|
815
|
-
"@types/prompts": "^2.4.1",
|
|
816
|
-
"@types/psl": "^1.1.0",
|
|
817
|
-
"@types/shelljs": "^0.8.11",
|
|
818
|
-
"@types/tmp": "^0.2.3",
|
|
819
|
-
"@types/update-notifier": "^6.0.1",
|
|
820
|
-
"app-root-path": "^3.1.0",
|
|
821
|
-
"cross-env": "^7.0.3",
|
|
822
|
-
rollup: "^3.3.0",
|
|
823
|
-
"rollup-plugin-typescript2": "^0.34.1",
|
|
824
|
-
tslib: "^2.4.1",
|
|
825
|
-
typescript: "^4.9.3"
|
|
826
|
-
};
|
|
827
|
-
var packageJson = {
|
|
828
|
-
name: name,
|
|
829
|
-
version: version,
|
|
830
|
-
description: description,
|
|
831
|
-
engines: engines,
|
|
832
|
-
bin: bin,
|
|
833
|
-
repository: repository,
|
|
834
|
-
author: author,
|
|
835
|
-
keywords: keywords,
|
|
836
|
-
files: files,
|
|
837
|
-
scripts: scripts,
|
|
838
|
-
type: type,
|
|
839
|
-
exports: exports,
|
|
840
|
-
license: license,
|
|
841
|
-
dependencies: dependencies,
|
|
842
|
-
devDependencies: devDependencies
|
|
765
|
+
const DEFAULT_PAKE_OPTIONS = {
|
|
766
|
+
icon: '',
|
|
767
|
+
height: 780,
|
|
768
|
+
width: 1200,
|
|
769
|
+
fullscreen: false,
|
|
770
|
+
resizable: true,
|
|
771
|
+
transparent: false,
|
|
772
|
+
userAgent: '',
|
|
773
|
+
showMenu: false,
|
|
774
|
+
showSystemTray: false,
|
|
775
|
+
multiArch: false,
|
|
776
|
+
targets: 'deb',
|
|
777
|
+
iterCopyFile: false,
|
|
778
|
+
systemTrayIcon: '',
|
|
779
|
+
debug: false,
|
|
843
780
|
};
|
|
844
781
|
|
|
845
782
|
async function checkUpdateTips() {
|
|
846
783
|
updateNotifier({ pkg: packageJson }).notify();
|
|
847
784
|
}
|
|
848
785
|
|
|
786
|
+
async function handleIcon(options) {
|
|
787
|
+
if (options.icon) {
|
|
788
|
+
if (options.icon.startsWith('http')) {
|
|
789
|
+
return downloadIcon(options.icon);
|
|
790
|
+
}
|
|
791
|
+
else {
|
|
792
|
+
return path.resolve(options.icon);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
else {
|
|
796
|
+
logger.warn('โผ No icon given, default in use. For a custom icon, use --icon option.');
|
|
797
|
+
const iconPath = IS_WIN
|
|
798
|
+
? 'src-tauri/png/icon_256.ico'
|
|
799
|
+
: IS_LINUX
|
|
800
|
+
? 'src-tauri/png/icon_512.png'
|
|
801
|
+
: 'src-tauri/icons/icon.icns';
|
|
802
|
+
return path.join(npmDirectory, iconPath);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
async function downloadIcon(iconUrl) {
|
|
806
|
+
const spinner = getSpinner('Downloading icon...');
|
|
807
|
+
try {
|
|
808
|
+
const iconResponse = await axios.get(iconUrl, { responseType: 'arraybuffer' });
|
|
809
|
+
const iconData = await iconResponse.data;
|
|
810
|
+
if (!iconData) {
|
|
811
|
+
return null;
|
|
812
|
+
}
|
|
813
|
+
const fileDetails = await fileTypeFromBuffer(iconData);
|
|
814
|
+
if (!fileDetails) {
|
|
815
|
+
return null;
|
|
816
|
+
}
|
|
817
|
+
const { path: tempPath } = await dir();
|
|
818
|
+
const iconPath = `${tempPath}/icon.${fileDetails.ext}`;
|
|
819
|
+
await fsExtra.outputFile(iconPath, iconData);
|
|
820
|
+
spinner.succeed(chalk.green('Icon downloaded successfully!'));
|
|
821
|
+
return iconPath;
|
|
822
|
+
}
|
|
823
|
+
catch (error) {
|
|
824
|
+
spinner.fail(chalk.red('Icon download failed!'));
|
|
825
|
+
if (error.response && error.response.status === 404) {
|
|
826
|
+
return null;
|
|
827
|
+
}
|
|
828
|
+
throw error;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
// Extracts the domain from a given URL.
|
|
833
|
+
function getDomain(inputUrl) {
|
|
834
|
+
try {
|
|
835
|
+
const url = new URL(inputUrl);
|
|
836
|
+
// Use PSL to parse domain names.
|
|
837
|
+
const parsed = psl.parse(url.hostname);
|
|
838
|
+
// If domain is available, split it and return the SLD.
|
|
839
|
+
if ('domain' in parsed && parsed.domain) {
|
|
840
|
+
return parsed.domain.split('.')[0];
|
|
841
|
+
}
|
|
842
|
+
else {
|
|
843
|
+
return null;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
catch (error) {
|
|
847
|
+
return null;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
// Appends 'https://' protocol to the URL if not present.
|
|
851
|
+
function appendProtocol(inputUrl) {
|
|
852
|
+
try {
|
|
853
|
+
new URL(inputUrl);
|
|
854
|
+
return inputUrl;
|
|
855
|
+
}
|
|
856
|
+
catch {
|
|
857
|
+
return `https://${inputUrl}`;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
// Normalizes the URL by ensuring it has a protocol and is valid.
|
|
861
|
+
function normalizeUrl(urlToNormalize) {
|
|
862
|
+
const urlWithProtocol = appendProtocol(urlToNormalize);
|
|
863
|
+
if (isUrl(urlWithProtocol)) {
|
|
864
|
+
return urlWithProtocol;
|
|
865
|
+
}
|
|
866
|
+
else {
|
|
867
|
+
throw new Error(`Your url "${urlWithProtocol}" is invalid`);
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
function resolveAppName(name, platform) {
|
|
872
|
+
const domain = getDomain(name) || 'pake';
|
|
873
|
+
return platform !== 'linux' ? capitalizeFirstLetter(domain) : domain;
|
|
874
|
+
}
|
|
875
|
+
function isValidName(name, platform) {
|
|
876
|
+
const platformRegexMapping = {
|
|
877
|
+
linux: /^[a-z0-9]+(-[a-z0-9]+)*$/,
|
|
878
|
+
default: /^[a-zA-Z0-9]+$/,
|
|
879
|
+
};
|
|
880
|
+
const reg = platformRegexMapping[platform] || platformRegexMapping.default;
|
|
881
|
+
return !!name && reg.test(name);
|
|
882
|
+
}
|
|
883
|
+
async function handleOptions(options, url) {
|
|
884
|
+
const { platform } = process;
|
|
885
|
+
const isActions = process.env.GITHUB_ACTIONS;
|
|
886
|
+
let name = options.name;
|
|
887
|
+
const pathExists = await fsExtra.pathExists(url);
|
|
888
|
+
if (!options.name) {
|
|
889
|
+
const defaultName = pathExists ? '' : resolveAppName(url, platform);
|
|
890
|
+
const promptMessage = 'Enter your application name';
|
|
891
|
+
const namePrompt = await promptText(promptMessage, defaultName);
|
|
892
|
+
name = namePrompt || defaultName;
|
|
893
|
+
}
|
|
894
|
+
if (!isValidName(name, platform)) {
|
|
895
|
+
const LINUX_NAME_ERROR = `โ name should only include lowercase letters, numbers, and dashes, and must contain at least one lowercase letter. Examples: com-123-xxx, 123pan, pan123, weread, we-read.`;
|
|
896
|
+
const DEFAULT_NAME_ERROR = `โ Name should only include letters and numbers, and must contain at least one letter. Examples: 123pan, 123Pan, Pan123, weread, WeRead, WERead.`;
|
|
897
|
+
const errorMsg = platform === 'linux' ? LINUX_NAME_ERROR : DEFAULT_NAME_ERROR;
|
|
898
|
+
logger.error(errorMsg);
|
|
899
|
+
if (isActions) {
|
|
900
|
+
name = resolveAppName(url, platform);
|
|
901
|
+
logger.warn(`โผ Inside github actions, use the default name: ${name}`);
|
|
902
|
+
}
|
|
903
|
+
else {
|
|
904
|
+
process.exit(1);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
const appOptions = {
|
|
908
|
+
...options,
|
|
909
|
+
name,
|
|
910
|
+
identifier: getIdentifier(url),
|
|
911
|
+
};
|
|
912
|
+
appOptions.icon = await handleIcon(appOptions);
|
|
913
|
+
return appOptions;
|
|
914
|
+
}
|
|
915
|
+
|
|
849
916
|
function validateNumberInput(value) {
|
|
850
917
|
const parsedValue = Number(value);
|
|
851
918
|
if (isNaN(parsedValue)) {
|
|
@@ -866,61 +933,46 @@ function validateUrlInput(url) {
|
|
|
866
933
|
return url;
|
|
867
934
|
}
|
|
868
935
|
|
|
869
|
-
const DEFAULT_PAKE_OPTIONS = {
|
|
870
|
-
icon: '',
|
|
871
|
-
height: 780,
|
|
872
|
-
width: 1200,
|
|
873
|
-
fullscreen: false,
|
|
874
|
-
resizable: true,
|
|
875
|
-
transparent: false,
|
|
876
|
-
userAgent: '',
|
|
877
|
-
showMenu: false,
|
|
878
|
-
showSystemTray: false,
|
|
879
|
-
multiArch: false,
|
|
880
|
-
targets: 'deb',
|
|
881
|
-
iterCopyFile: false,
|
|
882
|
-
systemTrayIcon: '',
|
|
883
|
-
debug: false,
|
|
884
|
-
};
|
|
885
|
-
|
|
886
936
|
program
|
|
887
|
-
.
|
|
888
|
-
.
|
|
937
|
+
.description(chalk.green('Pake can turn any webpage into a desktop app with Rust.'))
|
|
938
|
+
.usage('[url] [options]')
|
|
889
939
|
.showHelpAfterError();
|
|
890
940
|
program
|
|
891
941
|
.argument('[url]', 'The web URL you want to package', validateUrlInput)
|
|
892
942
|
.option('--name <string>', 'Application name')
|
|
893
943
|
.option('--icon <string>', 'Application icon', DEFAULT_PAKE_OPTIONS.icon)
|
|
894
|
-
.option('--height <number>', 'Window height', validateNumberInput, DEFAULT_PAKE_OPTIONS.height)
|
|
895
944
|
.option('--width <number>', 'Window width', validateNumberInput, DEFAULT_PAKE_OPTIONS.width)
|
|
896
|
-
.option('--
|
|
897
|
-
.option('--
|
|
898
|
-
.option('--
|
|
945
|
+
.option('--height <number>', 'Window height', validateNumberInput, DEFAULT_PAKE_OPTIONS.height)
|
|
946
|
+
.option('--transparent', 'Only for Mac, hide title bar', DEFAULT_PAKE_OPTIONS.transparent)
|
|
947
|
+
.option('--fullscreen', 'Start in full screen', DEFAULT_PAKE_OPTIONS.fullscreen)
|
|
899
948
|
.option('--user-agent <string>', 'Custom user agent', DEFAULT_PAKE_OPTIONS.userAgent)
|
|
900
949
|
.option('--show-menu', 'Show menu in app', DEFAULT_PAKE_OPTIONS.showMenu)
|
|
901
950
|
.option('--show-system-tray', 'Show system tray in app', DEFAULT_PAKE_OPTIONS.showSystemTray)
|
|
902
951
|
.option('--system-tray-icon <string>', 'Custom system tray icon', DEFAULT_PAKE_OPTIONS.systemTrayIcon)
|
|
903
|
-
.option('--iter-copy-file', 'Copy files
|
|
904
|
-
.option('--multi-arch', '
|
|
905
|
-
.option('--targets <string>', 'Only for Linux, option "deb"
|
|
952
|
+
.option('--iter-copy-file', 'Copy files when URL is a local file', DEFAULT_PAKE_OPTIONS.iterCopyFile)
|
|
953
|
+
.option('--multi-arch', 'Only for Mac, supports both Intel and M1', DEFAULT_PAKE_OPTIONS.multiArch)
|
|
954
|
+
.option('--targets <string>', 'Only for Linux, option "deb" or "appimage"', DEFAULT_PAKE_OPTIONS.targets)
|
|
906
955
|
.option('--debug', 'Debug mode', DEFAULT_PAKE_OPTIONS.debug)
|
|
956
|
+
.version(packageJson.version, '-v, --version', 'Output the current version')
|
|
907
957
|
.action(async (url, options) => {
|
|
908
|
-
//Check for update prompt
|
|
909
958
|
await checkUpdateTips();
|
|
910
|
-
// If no URL is provided, display help information
|
|
911
959
|
if (!url) {
|
|
912
|
-
program.
|
|
960
|
+
program.outputHelp(str => {
|
|
961
|
+
return str
|
|
962
|
+
.split('\n')
|
|
963
|
+
.filter(line => !/((-h,|--help)|((-v|-V),|--version))\s+.+$/.test(line))
|
|
964
|
+
.join('\n');
|
|
965
|
+
});
|
|
966
|
+
process.exit(0);
|
|
913
967
|
}
|
|
914
968
|
log.setDefaultLevel('info');
|
|
915
969
|
if (options.debug) {
|
|
916
970
|
log.setLevel('debug');
|
|
917
971
|
}
|
|
918
|
-
const spinner = ora('Preparing...').start();
|
|
919
|
-
const builder = BuilderProvider.create();
|
|
920
|
-
await builder.prepare();
|
|
921
972
|
const appOptions = await handleOptions(options, url);
|
|
922
|
-
spinner.succeed();
|
|
923
973
|
log.debug('PakeAppOptions', appOptions);
|
|
924
|
-
|
|
974
|
+
const builder = BuilderProvider.create(appOptions);
|
|
975
|
+
await builder.prepare();
|
|
976
|
+
await builder.build(url);
|
|
925
977
|
});
|
|
926
978
|
program.parse();
|
package/package.json
CHANGED
|
@@ -136,8 +136,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
136
136
|
document.body.appendChild(m);
|
|
137
137
|
setTimeout(function () {
|
|
138
138
|
const d = 0.5;
|
|
139
|
-
m.style.transition =
|
|
140
|
-
'transform ' + d + 's ease-in, opacity ' + d + 's ease-in';
|
|
139
|
+
m.style.transition = 'transform ' + d + 's ease-in, opacity ' + d + 's ease-in';
|
|
141
140
|
m.style.opacity = '0';
|
|
142
141
|
setTimeout(function () {
|
|
143
142
|
document.body.removeChild(m);
|
|
@@ -151,14 +150,14 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
151
150
|
if (window.location.hostname === 'chat.openai.com') {
|
|
152
151
|
const originFetch = fetch;
|
|
153
152
|
window.fetch = (url, options) => {
|
|
154
|
-
return originFetch(url, options).then(async
|
|
153
|
+
return originFetch(url, options).then(async response => {
|
|
155
154
|
if (url.indexOf('/backend-api/models') === -1) {
|
|
156
155
|
return response;
|
|
157
156
|
}
|
|
158
157
|
const responseClone = response.clone();
|
|
159
158
|
let res = await responseClone.json();
|
|
160
|
-
res.models = res.models.map(
|
|
161
|
-
m.tags = m.tags.filter(
|
|
159
|
+
res.models = res.models.map(m => {
|
|
160
|
+
m.tags = m.tags.filter(t => {
|
|
162
161
|
return t !== 'mobile';
|
|
163
162
|
});
|
|
164
163
|
if (m.slug === 'gpt-4-mobile') {
|
|
@@ -82,14 +82,14 @@ async function invoke(cmd, args) {
|
|
|
82
82
|
|
|
83
83
|
// Judgment of file download.
|
|
84
84
|
function isDownloadLink(url) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
85
|
+
const fileExtensions = [
|
|
86
|
+
'3gp', '7z', 'ai', 'apk', 'avi', 'bmp', 'csv', 'dmg', 'doc', 'docx', 'fla', 'flv', 'gif', 'gz', 'gzip',
|
|
87
|
+
'ico', 'iso', 'indd', 'jar', 'jpeg', 'jpg', 'm3u8', 'mov', 'mp3', 'mp4', 'mpa', 'mpg',
|
|
88
|
+
'mpeg', 'msi', 'odt', 'ogg', 'ogv', 'pdf', 'png', 'ppt', 'pptx', 'psd', 'rar', 'raw', 'rss', 'svg',
|
|
89
|
+
'swf', 'tar', 'tif', 'tiff', 'ts', 'txt', 'wav', 'webm', 'webp', 'wma', 'wmv', 'xls', 'xlsx', 'xml', 'zip',
|
|
90
|
+
];
|
|
91
|
+
const downloadLinkPattern = new RegExp(`\\.(${fileExtensions.join('|')})$`, 'i');
|
|
92
|
+
return downloadLinkPattern.test(url);
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
// No need to go to the download link.
|
|
@@ -185,7 +185,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
185
185
|
|
|
186
186
|
// Rewrite the window.open function.
|
|
187
187
|
const originalWindowOpen = window.open;
|
|
188
|
-
window.open = function
|
|
188
|
+
window.open = function(url, name, specs) {
|
|
189
189
|
// Apple login and google login
|
|
190
190
|
if (name === 'AppleAuthentication') {
|
|
191
191
|
//do nothing
|
|
@@ -278,7 +278,7 @@ function downloadFromBlobUrl(blobUrl, filename) {
|
|
|
278
278
|
function detectDownloadByCreateAnchor() {
|
|
279
279
|
const createEle = document.createElement;
|
|
280
280
|
document.createElement = (el) => {
|
|
281
|
-
if (el !==
|
|
281
|
+
if (el !== 'a') return createEle.call(document, el);
|
|
282
282
|
const anchorEle = createEle.call(document, el);
|
|
283
283
|
|
|
284
284
|
// use addEventListener to avoid overriding the original click event.
|
|
@@ -290,5 +290,5 @@ function detectDownloadByCreateAnchor() {
|
|
|
290
290
|
});
|
|
291
291
|
|
|
292
292
|
return anchorEle;
|
|
293
|
-
}
|
|
293
|
+
};
|
|
294
294
|
}
|
|
@@ -9,9 +9,7 @@
|
|
|
9
9
|
"dangerousRemoteDomainIpcAccess": [
|
|
10
10
|
{
|
|
11
11
|
"domain": "weread.qq.com",
|
|
12
|
-
"windows": [
|
|
13
|
-
"pake"
|
|
14
|
-
],
|
|
12
|
+
"windows": ["pake"],
|
|
15
13
|
"enableTauriAPI": true
|
|
16
14
|
}
|
|
17
15
|
]
|
|
@@ -27,9 +25,7 @@
|
|
|
27
25
|
"all": true,
|
|
28
26
|
"fs": {
|
|
29
27
|
"all": true,
|
|
30
|
-
"scope": [
|
|
31
|
-
"$DOWNLOAD/*"
|
|
32
|
-
]
|
|
28
|
+
"scope": ["$DOWNLOAD/*"]
|
|
33
29
|
}
|
|
34
30
|
}
|
|
35
31
|
},
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
"copyright": "",
|
|
9
9
|
"deb": {
|
|
10
10
|
"depends": ["curl", "wget"],
|
|
11
|
-
"files": {
|
|
11
|
+
"files": {
|
|
12
|
+
"/usr/share/applications/com-tw93-weread.desktop": "assets/com-tw93-weread.desktop"
|
|
13
|
+
}
|
|
12
14
|
},
|
|
13
15
|
"externalBin": [],
|
|
14
16
|
"longDescription": "",
|