pake-cli 3.11.7 โ 3.11.9
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/LICENSE +674 -21
- package/README.md +4 -0
- package/dist/cli.js +211 -110
- package/package.json +3 -2
- package/src-tauri/Cargo.lock +1 -1
- package/src-tauri/Cargo.toml +2 -2
- package/src-tauri/src/app/invoke.rs +1 -49
- package/src-tauri/src/app/window.rs +59 -2
- package/src-tauri/src/inject/event.js +27 -126
- package/src-tauri/src/lib.rs +4 -6
- package/src-tauri/tauri.conf.json +1 -1
package/README.md
CHANGED
|
@@ -207,3 +207,7 @@ Pake's development can not be without these Hackers. They contributed a lot of c
|
|
|
207
207
|
- I have two cats, TangYuan and Coke. If you think Pake delights your life, you can feed them <a href="https://cats.tw93.fun?name=Pake" target="_blank">canned food ๐ฅฉ</a>.
|
|
208
208
|
|
|
209
209
|
<a href="https://cats.tw93.fun?name=Pake"><img src="https://cdn.jsdelivr.net/gh/tw93/sponsors@main/assets/sponsors.svg" width="1000" loading="lazy" /></a>
|
|
210
|
+
|
|
211
|
+
## License
|
|
212
|
+
|
|
213
|
+
Pake is open source under GPL-3.0, see [LICENSE](./LICENSE); apps you build with Pake are entirely yours to use and distribute. If you fork Pake into your own product, to avoid confusion please give it a different name and credit Pake as the source.
|
package/dist/cli.js
CHANGED
|
@@ -20,7 +20,7 @@ import { InvalidArgumentError, program as program$1, Option } from 'commander';
|
|
|
20
20
|
import fs$1 from 'fs';
|
|
21
21
|
|
|
22
22
|
var name = "pake-cli";
|
|
23
|
-
var version = "3.11.
|
|
23
|
+
var version = "3.11.9";
|
|
24
24
|
var description = "๐คฑ๐ป Turn any webpage into a desktop app with one command. ๐คฑ๐ป ไธ้ฎๆๅ
็ฝ้กต็ๆ่ฝป้ๆก้ขๅบ็จใ";
|
|
25
25
|
var engines = {
|
|
26
26
|
node: ">=18.0.0"
|
|
@@ -58,6 +58,7 @@ var scripts = {
|
|
|
58
58
|
analyze: "cd src-tauri && cargo bloat --release --crates",
|
|
59
59
|
tauri: "tauri",
|
|
60
60
|
cli: "cross-env NODE_ENV=development rollup -c -w",
|
|
61
|
+
"cli:dev": "cross-env NODE_ENV=development rollup -c -w",
|
|
61
62
|
"cli:build": "cross-env NODE_ENV=production rollup -c",
|
|
62
63
|
test: "pnpm run cli:build && cross-env PAKE_CREATE_APP=1 node tests/index.js",
|
|
63
64
|
format: "prettier --write . --ignore-unknown && find tests -name '*.js' -exec sed -i '' 's/[[:space:]]*$//' {} \\; && cd src-tauri && cargo fmt --verbose",
|
|
@@ -68,7 +69,7 @@ var scripts = {
|
|
|
68
69
|
};
|
|
69
70
|
var type = "module";
|
|
70
71
|
var exports$1 = "./dist/cli.js";
|
|
71
|
-
var license = "
|
|
72
|
+
var license = "GPL-3.0-or-later";
|
|
72
73
|
var dependencies = {
|
|
73
74
|
"@tauri-apps/api": "~2.10.1",
|
|
74
75
|
"@tauri-apps/cli": "^2.10.0",
|
|
@@ -248,38 +249,10 @@ async function shellExec(command, timeout = 300000, env) {
|
|
|
248
249
|
if (error.timedOut) {
|
|
249
250
|
throw new Error(`Command timed out after ${timeout}ms: "${command}". Try increasing timeout or check network connectivity.`);
|
|
250
251
|
}
|
|
251
|
-
|
|
252
|
-
//
|
|
253
|
-
//
|
|
254
|
-
|
|
255
|
-
if (process.platform === 'linux' &&
|
|
256
|
-
(lowerError.includes('linuxdeploy') ||
|
|
257
|
-
lowerError.includes('appimage') ||
|
|
258
|
-
lowerError.includes('strip'))) {
|
|
259
|
-
errorMsg +=
|
|
260
|
-
'\n\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n' +
|
|
261
|
-
'Linux AppImage Build Failed\n' +
|
|
262
|
-
'โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n\n' +
|
|
263
|
-
'Cause: Strip tool incompatibility with glibc 2.38+\n' +
|
|
264
|
-
' (affects Debian Trixie, Arch Linux, and other modern distros)\n\n' +
|
|
265
|
-
'Quick fix:\n' +
|
|
266
|
-
' NO_STRIP=1 pake <url> --targets appimage --debug\n\n' +
|
|
267
|
-
'Alternatives:\n' +
|
|
268
|
-
' โข Use DEB format: pake <url> --targets deb\n' +
|
|
269
|
-
' โข Update binutils: sudo apt install binutils (or pacman -S binutils)\n' +
|
|
270
|
-
' โข Detailed guide: https://github.com/tw93/Pake/blob/main/docs/faq.md\n' +
|
|
271
|
-
'โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ';
|
|
272
|
-
if (lowerError.includes('fuse') ||
|
|
273
|
-
lowerError.includes('operation not permitted') ||
|
|
274
|
-
lowerError.includes('/dev/fuse')) {
|
|
275
|
-
errorMsg +=
|
|
276
|
-
'\n\nDocker / Container hint:\n' +
|
|
277
|
-
' AppImage tooling needs access to /dev/fuse. When running inside Docker, add:\n' +
|
|
278
|
-
' --privileged --device /dev/fuse --security-opt apparmor=unconfined\n' +
|
|
279
|
-
' or run on the host directly.';
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
throw new Error(errorMsg);
|
|
252
|
+
// AppImage/linuxdeploy guidance is added by the caller (BaseBuilder), which
|
|
253
|
+
// knows the build target. We only have the command line here (the tool's
|
|
254
|
+
// diagnostics stream to the terminal via stdio:inherit, not into the error).
|
|
255
|
+
throw new Error(`Error occurred while executing command "${command}". Exit code: ${exitCode}. Details: ${errorMessage}`);
|
|
283
256
|
}
|
|
284
257
|
}
|
|
285
258
|
|
|
@@ -437,6 +410,14 @@ function generateIdentifierSafeName(name) {
|
|
|
437
410
|
return cleaned;
|
|
438
411
|
}
|
|
439
412
|
|
|
413
|
+
const LINUX_TARGET_TYPES = ['deb', 'appimage', 'rpm', 'zst'];
|
|
414
|
+
// Returns the valid Linux build targets from a comma-separated targets
|
|
415
|
+
// string, preserving LINUX_TARGET_TYPES order. Unknown entries are dropped.
|
|
416
|
+
function filterLinuxTargets(targets) {
|
|
417
|
+
const requested = targets.split(',').map((target) => target.trim());
|
|
418
|
+
return LINUX_TARGET_TYPES.filter((target) => requested.includes(target));
|
|
419
|
+
}
|
|
420
|
+
|
|
440
421
|
/**
|
|
441
422
|
* Pure transform from CLI options to the window-config slice that gets
|
|
442
423
|
* merged into pake.json. Exposed for snapshot testing so option drift
|
|
@@ -562,18 +543,15 @@ Terminal=false
|
|
|
562
543
|
[desktopInstallPath]: `assets/${desktopFileName}`,
|
|
563
544
|
};
|
|
564
545
|
const validTargets = [
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
'rpm',
|
|
568
|
-
'deb-arm64',
|
|
569
|
-
'appimage-arm64',
|
|
570
|
-
'rpm-arm64',
|
|
546
|
+
...LINUX_TARGET_TYPES,
|
|
547
|
+
...LINUX_TARGET_TYPES.map((target) => `${target}-arm64`),
|
|
571
548
|
];
|
|
572
549
|
const baseTarget = options.targets.includes('-arm64')
|
|
573
550
|
? options.targets.replace('-arm64', '')
|
|
574
551
|
: options.targets;
|
|
575
552
|
if (validTargets.includes(options.targets)) {
|
|
576
|
-
|
|
553
|
+
// zst is repacked from the deb payload, so Tauri itself bundles a deb.
|
|
554
|
+
tauriConf.bundle.targets = [baseTarget === 'zst' ? 'deb' : baseTarget];
|
|
577
555
|
}
|
|
578
556
|
else {
|
|
579
557
|
logger.warn(`โผ The target must be one of ${validTargets.join(', ')}, the default 'deb' will be used.`);
|
|
@@ -808,7 +786,7 @@ function getBuildTimeout() {
|
|
|
808
786
|
}
|
|
809
787
|
let packageManagerCache = null;
|
|
810
788
|
function parseMajorVersion(version) {
|
|
811
|
-
const match = version.match(/^(\d+)/);
|
|
789
|
+
const match = version.match(/^v?(\d+)/);
|
|
812
790
|
return match ? Number(match[1]) : null;
|
|
813
791
|
}
|
|
814
792
|
function getPinnedPnpmMajorVersion() {
|
|
@@ -834,21 +812,10 @@ async function detectPackageManager() {
|
|
|
834
812
|
return packageManagerCache;
|
|
835
813
|
}
|
|
836
814
|
const { execa } = await import('execa');
|
|
815
|
+
let pnpmVersion;
|
|
837
816
|
try {
|
|
838
817
|
const { stdout } = await execa('pnpm', ['--version']);
|
|
839
|
-
|
|
840
|
-
const pinnedPnpmMajor = getPinnedPnpmMajorVersion();
|
|
841
|
-
if (pnpmMajor !== null &&
|
|
842
|
-
pinnedPnpmMajor !== null &&
|
|
843
|
-
pnpmMajor !== pinnedPnpmMajor &&
|
|
844
|
-
(await detectNpm(execa))) {
|
|
845
|
-
logger.warn(`โผ Detected pnpm v${stdout.trim()}, but Pake is pinned to ${packageJson.packageManager}; using npm for package installation instead.`);
|
|
846
|
-
packageManagerCache = 'npm';
|
|
847
|
-
return 'npm';
|
|
848
|
-
}
|
|
849
|
-
logger.info('โบ Using pnpm for package management.');
|
|
850
|
-
packageManagerCache = 'pnpm';
|
|
851
|
-
return 'pnpm';
|
|
818
|
+
pnpmVersion = stdout.trim();
|
|
852
819
|
}
|
|
853
820
|
catch {
|
|
854
821
|
if (await detectNpm(execa)) {
|
|
@@ -858,6 +825,24 @@ async function detectPackageManager() {
|
|
|
858
825
|
}
|
|
859
826
|
throw new Error('Neither pnpm nor npm is available. Please install a package manager.');
|
|
860
827
|
}
|
|
828
|
+
const normalizedPnpmVersion = pnpmVersion.startsWith('v')
|
|
829
|
+
? pnpmVersion
|
|
830
|
+
: `v${pnpmVersion}`;
|
|
831
|
+
const pnpmMajor = parseMajorVersion(pnpmVersion);
|
|
832
|
+
const pinnedPnpmMajor = getPinnedPnpmMajorVersion();
|
|
833
|
+
if (pnpmMajor !== null &&
|
|
834
|
+
pinnedPnpmMajor !== null &&
|
|
835
|
+
pnpmMajor !== pinnedPnpmMajor) {
|
|
836
|
+
if (!(await detectNpm(execa))) {
|
|
837
|
+
throw new Error(`Detected pnpm ${normalizedPnpmVersion}, but Pake is pinned to ${packageJson.packageManager}. Install npm so Pake can fall back, or use pnpm ${pinnedPnpmMajor}.x to match the project pin.`);
|
|
838
|
+
}
|
|
839
|
+
logger.warn(`โผ Detected pnpm ${normalizedPnpmVersion}, but Pake is pinned to ${packageJson.packageManager}; using npm for package management instead.`);
|
|
840
|
+
packageManagerCache = 'npm';
|
|
841
|
+
return 'npm';
|
|
842
|
+
}
|
|
843
|
+
logger.info('โบ Using pnpm for package management.');
|
|
844
|
+
packageManagerCache = 'pnpm';
|
|
845
|
+
return 'pnpm';
|
|
861
846
|
}
|
|
862
847
|
function getInstallCommand(packageManager, useCnMirror) {
|
|
863
848
|
const registryOption = useCnMirror
|
|
@@ -913,23 +898,30 @@ async function configureCargoRegistry(tauriSrcPath, useCnMirror) {
|
|
|
913
898
|
logger.warn(`โผ ${projectConf} still references rsproxy.cn. Remove it or set ${CN_MIRROR_ENV}=1 if you want to use the CN mirror.`);
|
|
914
899
|
}
|
|
915
900
|
}
|
|
916
|
-
/**
|
|
917
|
-
* Returns true when an error string looks like the well-known Tauri+linuxdeploy
|
|
918
|
-
* strip failure that we automatically retry with NO_STRIP=1.
|
|
919
|
-
*/
|
|
920
|
-
function isLinuxDeployStripError(error) {
|
|
921
|
-
if (!(error instanceof Error) || !error.message) {
|
|
922
|
-
return false;
|
|
923
|
-
}
|
|
924
|
-
const message = error.message.toLowerCase();
|
|
925
|
-
return (message.includes('linuxdeploy') ||
|
|
926
|
-
message.includes('failed to run linuxdeploy') ||
|
|
927
|
-
message.includes('strip:') ||
|
|
928
|
-
message.includes('unable to recognise the format of the input file') ||
|
|
929
|
-
message.includes('appimage tool failed') ||
|
|
930
|
-
message.includes('strip tool'));
|
|
931
|
-
}
|
|
932
901
|
|
|
902
|
+
// Appended to the error when a Linux AppImage build fails for good. linuxdeploy's
|
|
903
|
+
// diagnostics stream to the terminal (stdio: 'inherit') and never reach
|
|
904
|
+
// error.message, so we cannot name the exact cause. We only reach here after
|
|
905
|
+
// NO_STRIP=1 has been applied and still failed, so strip is shown as ruled out.
|
|
906
|
+
const APPIMAGE_BAR = 'โ'.repeat(56);
|
|
907
|
+
const APPIMAGE_FAILURE_GUIDANCE = `\n\n${APPIMAGE_BAR}\n` +
|
|
908
|
+
'Linux AppImage Build Failed\n' +
|
|
909
|
+
`${APPIMAGE_BAR}\n\n` +
|
|
910
|
+
'The AppImage bundler (linuxdeploy) failed. Common causes and fixes:\n\n' +
|
|
911
|
+
' โข Strip incompatibility (glibc 2.38+): NO_STRIP=1 was already applied and\n' +
|
|
912
|
+
' the build still failed, so strip is likely not the cause.\n' +
|
|
913
|
+
' โข Missing gdk-pixbuf loaders (e.g. "cannot stat\n' +
|
|
914
|
+
" '/usr/lib/gdk-pixbuf-2.0/...'\"): install them, then rebuild:\n" +
|
|
915
|
+
' Arch: sudo pacman -S gdk-pixbuf2 librsvg\n' +
|
|
916
|
+
' Debian: sudo apt install librsvg2-common gdk-pixbuf2.0-bin\n' +
|
|
917
|
+
' Fedora: sudo dnf install gdk-pixbuf2-modules librsvg2\n' +
|
|
918
|
+
' then: sudo gdk-pixbuf-query-loaders --update-cache\n' +
|
|
919
|
+
' (Arch refreshes the cache automatically via a pacman hook)\n' +
|
|
920
|
+
' โข Running in Docker/container: AppImage needs /dev/fuse:\n' +
|
|
921
|
+
' --privileged --device /dev/fuse --security-opt apparmor=unconfined\n\n' +
|
|
922
|
+
'Still stuck? Build a DEB instead: pake <url> --targets deb\n' +
|
|
923
|
+
'Detailed guide: https://github.com/tw93/Pake/blob/main/docs/faq.md\n' +
|
|
924
|
+
APPIMAGE_BAR;
|
|
933
925
|
class BaseBuilder {
|
|
934
926
|
constructor(options) {
|
|
935
927
|
this.options = options;
|
|
@@ -1004,7 +996,7 @@ class BaseBuilder {
|
|
|
1004
996
|
const command = `cd "${npmDirectory}" && ${packageManager} run tauri${argSeparator} dev --config "${configPath}" ${featureArgs}`;
|
|
1005
997
|
await shellExec(command);
|
|
1006
998
|
}
|
|
1007
|
-
async buildAndCopy(url, target) {
|
|
999
|
+
async buildAndCopy(url, target, logSuccess = true) {
|
|
1008
1000
|
const { name = 'pake-app' } = this.options;
|
|
1009
1001
|
await mergeConfig(url, this.options, tauriConfig);
|
|
1010
1002
|
const packageManager = await detectPackageManager();
|
|
@@ -1021,14 +1013,11 @@ class BaseBuilder {
|
|
|
1021
1013
|
...(process.env.NO_STRIP ? { NO_STRIP: process.env.NO_STRIP } : {}),
|
|
1022
1014
|
};
|
|
1023
1015
|
const resolveExecEnv = () => Object.keys(buildEnv).length > 0 ? buildEnv : undefined;
|
|
1024
|
-
|
|
1025
|
-
//
|
|
1026
|
-
//
|
|
1027
|
-
if (
|
|
1028
|
-
|
|
1029
|
-
logger.warn('โ Building AppImage on Linux may fail due to strip incompatibility with glibc 2.38+');
|
|
1030
|
-
logger.warn('โ If build fails, retry with: NO_STRIP=1 pake <url> --targets appimage');
|
|
1031
|
-
}
|
|
1016
|
+
const isLinuxAppImage = process.platform === 'linux' && target === 'appimage';
|
|
1017
|
+
// AppImage builds can fail at the linuxdeploy strip step on glibc 2.38+.
|
|
1018
|
+
// A real failure now prints full guidance, so only hint in debug mode.
|
|
1019
|
+
if (isLinuxAppImage && !buildEnv.NO_STRIP && this.options.debug) {
|
|
1020
|
+
logger.warn('โ AppImage strip step can fail on glibc 2.38+; Pake will auto-retry with NO_STRIP=1.');
|
|
1032
1021
|
}
|
|
1033
1022
|
const buildCommand = `cd "${npmDirectory}" && ${this.getBuildCommand(packageManager)}`;
|
|
1034
1023
|
const buildTimeout = getBuildTimeout();
|
|
@@ -1036,21 +1025,26 @@ class BaseBuilder {
|
|
|
1036
1025
|
await shellExec(buildCommand, buildTimeout, resolveExecEnv());
|
|
1037
1026
|
}
|
|
1038
1027
|
catch (error) {
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
!buildEnv.NO_STRIP &&
|
|
1042
|
-
isLinuxDeployStripError(error);
|
|
1043
|
-
if (shouldRetryWithoutStrip) {
|
|
1044
|
-
logger.warn('โ AppImage build failed during linuxdeploy strip step, retrying with NO_STRIP=1 automatically.');
|
|
1045
|
-
buildEnv = {
|
|
1046
|
-
...buildEnv,
|
|
1047
|
-
NO_STRIP: '1',
|
|
1048
|
-
};
|
|
1049
|
-
await shellExec(buildCommand, buildTimeout, resolveExecEnv());
|
|
1028
|
+
if (!isLinuxAppImage) {
|
|
1029
|
+
throw error;
|
|
1050
1030
|
}
|
|
1051
|
-
|
|
1031
|
+
// linuxdeploy's diagnostics stream to the terminal (stdio: 'inherit') and
|
|
1032
|
+
// never reach error.message, so we cannot classify the cause. strip is the
|
|
1033
|
+
// most common AppImage failure, so retry once with NO_STRIP=1; if that
|
|
1034
|
+
// (or an already-NO_STRIP run) still fails, surface all known causes.
|
|
1035
|
+
if (buildEnv.NO_STRIP) {
|
|
1036
|
+
error.message += APPIMAGE_FAILURE_GUIDANCE;
|
|
1052
1037
|
throw error;
|
|
1053
1038
|
}
|
|
1039
|
+
logger.warn('โ AppImage build failed, retrying once with NO_STRIP=1 (common glibc 2.38+ strip issue).');
|
|
1040
|
+
buildEnv = { ...buildEnv, NO_STRIP: '1' };
|
|
1041
|
+
try {
|
|
1042
|
+
await shellExec(buildCommand, buildTimeout, resolveExecEnv());
|
|
1043
|
+
}
|
|
1044
|
+
catch (retryError) {
|
|
1045
|
+
retryError.message += APPIMAGE_FAILURE_GUIDANCE;
|
|
1046
|
+
throw retryError;
|
|
1047
|
+
}
|
|
1054
1048
|
}
|
|
1055
1049
|
// Copy app
|
|
1056
1050
|
const fileName = this.getFileName();
|
|
@@ -1063,8 +1057,10 @@ class BaseBuilder {
|
|
|
1063
1057
|
await this.copyRawBinary(npmDirectory, name);
|
|
1064
1058
|
}
|
|
1065
1059
|
await fsExtra.remove(appPath);
|
|
1066
|
-
|
|
1067
|
-
|
|
1060
|
+
if (logSuccess) {
|
|
1061
|
+
logger.success('โ Build success!');
|
|
1062
|
+
logger.success('โ App installer located in', distPath);
|
|
1063
|
+
}
|
|
1068
1064
|
// Log binary location if preserved
|
|
1069
1065
|
if (this.options.keepBinary) {
|
|
1070
1066
|
const binaryPath = this.getRawBinaryPath(name);
|
|
@@ -1407,21 +1403,123 @@ class LinuxBuilder extends BaseBuilder {
|
|
|
1407
1403
|
return `${name}_${version}_${arch}`;
|
|
1408
1404
|
}
|
|
1409
1405
|
async build(url) {
|
|
1410
|
-
const
|
|
1411
|
-
|
|
1412
|
-
.
|
|
1413
|
-
|
|
1414
|
-
for (const target of
|
|
1415
|
-
|
|
1416
|
-
|
|
1406
|
+
const targets = filterLinuxTargets(this.options.targets);
|
|
1407
|
+
if (targets.length === 0) {
|
|
1408
|
+
throw new Error(`No valid Linux target in "${this.options.targets}". Valid targets: ${LINUX_TARGET_TYPES.join(', ')}.`);
|
|
1409
|
+
}
|
|
1410
|
+
for (const target of targets) {
|
|
1411
|
+
this.currentBuildType = target;
|
|
1412
|
+
if (target === 'zst') {
|
|
1413
|
+
await this.buildAndCopy(url, 'deb', false);
|
|
1414
|
+
await this.createArchPackageFromDeb();
|
|
1415
|
+
}
|
|
1416
|
+
else {
|
|
1417
1417
|
await this.buildAndCopy(url, target);
|
|
1418
1418
|
}
|
|
1419
1419
|
}
|
|
1420
1420
|
}
|
|
1421
|
+
async ensureArchPackagingTools() {
|
|
1422
|
+
const requiredTools = [
|
|
1423
|
+
{ tool: 'ar', pacmanPackage: 'binutils' },
|
|
1424
|
+
{ tool: 'bsdtar', pacmanPackage: 'libarchive' },
|
|
1425
|
+
];
|
|
1426
|
+
for (const { tool, pacmanPackage } of requiredTools) {
|
|
1427
|
+
try {
|
|
1428
|
+
await shellExec(`command -v ${tool} >/dev/null 2>&1`);
|
|
1429
|
+
}
|
|
1430
|
+
catch {
|
|
1431
|
+
throw new Error(`Building a zst package requires "${tool}". Install it first, e.g. "sudo pacman -S ${pacmanPackage}".`);
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
async createArchPackageFromDeb() {
|
|
1436
|
+
const { name = 'pake-app' } = this.options;
|
|
1437
|
+
const packageName = generateLinuxPackageName(name);
|
|
1438
|
+
const version = tauriConfig.version;
|
|
1439
|
+
const arch = this.buildArch === 'arm64' ? 'aarch64' : 'x86_64';
|
|
1440
|
+
const debPath = path.resolve(`${name}.deb`);
|
|
1441
|
+
const packagePath = path.resolve(`${name}-${version}-1-${arch}.pkg.tar.zst`);
|
|
1442
|
+
const workDir = path.resolve('.pake-arch-package');
|
|
1443
|
+
const dataDir = path.join(workDir, 'data');
|
|
1444
|
+
const controlDir = path.join(workDir, 'control');
|
|
1445
|
+
await this.ensureArchPackagingTools();
|
|
1446
|
+
await fsExtra.remove(workDir);
|
|
1447
|
+
await fsExtra.ensureDir(dataDir);
|
|
1448
|
+
await fsExtra.ensureDir(controlDir);
|
|
1449
|
+
try {
|
|
1450
|
+
await shellExec(`cd "${controlDir}" && ar x "${debPath}"`);
|
|
1451
|
+
const dataArchive = (await fsExtra.readdir(controlDir)).find((file) => file.startsWith('data.tar'));
|
|
1452
|
+
if (!dataArchive) {
|
|
1453
|
+
throw new Error(`Could not find data.tar payload in ${debPath}`);
|
|
1454
|
+
}
|
|
1455
|
+
await shellExec(`tar -xf "${path.join(controlDir, dataArchive)}" -C "${dataDir}"`);
|
|
1456
|
+
// Drop the desktop entry auto-generated by the Tauri deb bundler;
|
|
1457
|
+
// the payload already ships Pake's own com.pake.<name>.desktop.
|
|
1458
|
+
await fsExtra.remove(path.join(dataDir, 'usr', 'share', 'applications', `${packageName}.desktop`));
|
|
1459
|
+
const installedSize = await this.getDirectorySize(dataDir);
|
|
1460
|
+
const pkgInfo = `pkgname = ${packageName}
|
|
1461
|
+
pkgbase = ${packageName}
|
|
1462
|
+
pkgver = ${version}-1
|
|
1463
|
+
pkgdesc = ${name} Pake app
|
|
1464
|
+
url = https://github.com/tw93/Pake
|
|
1465
|
+
builddate = ${Math.floor(Date.now() / 1000)}
|
|
1466
|
+
packager = Pake
|
|
1467
|
+
size = ${installedSize}
|
|
1468
|
+
arch = ${arch}
|
|
1469
|
+
license = custom
|
|
1470
|
+
depend = cairo
|
|
1471
|
+
depend = desktop-file-utils
|
|
1472
|
+
depend = gdk-pixbuf2
|
|
1473
|
+
depend = glib2
|
|
1474
|
+
depend = gtk3
|
|
1475
|
+
depend = hicolor-icon-theme
|
|
1476
|
+
depend = libsoup3
|
|
1477
|
+
depend = pango
|
|
1478
|
+
depend = webkit2gtk-4.1
|
|
1479
|
+
`;
|
|
1480
|
+
await fsExtra.writeFile(path.join(dataDir, '.PKGINFO'), pkgInfo);
|
|
1481
|
+
await fsExtra.writeFile(path.join(dataDir, '.INSTALL'), `post_install() {
|
|
1482
|
+
gtk-update-icon-cache -q -t -f usr/share/icons/hicolor
|
|
1483
|
+
update-desktop-database -q usr/share/applications
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
post_upgrade() {
|
|
1487
|
+
post_install
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
post_remove() {
|
|
1491
|
+
gtk-update-icon-cache -q -t -f usr/share/icons/hicolor
|
|
1492
|
+
update-desktop-database -q usr/share/applications
|
|
1493
|
+
}
|
|
1494
|
+
`);
|
|
1495
|
+
await shellExec(`bsdtar --zstd -cf "${packagePath}" -C "${dataDir}" .PKGINFO .INSTALL usr`);
|
|
1496
|
+
await fsExtra.remove(debPath);
|
|
1497
|
+
logger.success('โ Build success!');
|
|
1498
|
+
logger.success('โ App installer located in', packagePath);
|
|
1499
|
+
}
|
|
1500
|
+
finally {
|
|
1501
|
+
await fsExtra.remove(workDir);
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
async getDirectorySize(directory) {
|
|
1505
|
+
let size = 0;
|
|
1506
|
+
for (const entry of await fsExtra.readdir(directory, {
|
|
1507
|
+
withFileTypes: true,
|
|
1508
|
+
})) {
|
|
1509
|
+
const entryPath = path.join(directory, entry.name);
|
|
1510
|
+
if (entry.isDirectory()) {
|
|
1511
|
+
size += await this.getDirectorySize(entryPath);
|
|
1512
|
+
}
|
|
1513
|
+
else if (entry.isFile()) {
|
|
1514
|
+
size += (await fsExtra.stat(entryPath)).size;
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
return size;
|
|
1518
|
+
}
|
|
1421
1519
|
// Override buildAndCopy to ensure currentBuildType is synced if called directly, though the loop above handles it most of the time.
|
|
1422
|
-
async buildAndCopy(url, target) {
|
|
1520
|
+
async buildAndCopy(url, target, logSuccess = true) {
|
|
1423
1521
|
this.currentBuildType = target;
|
|
1424
|
-
await super.buildAndCopy(url, target);
|
|
1522
|
+
await super.buildAndCopy(url, target, logSuccess);
|
|
1425
1523
|
}
|
|
1426
1524
|
getBuildCommand(packageManager = 'pnpm') {
|
|
1427
1525
|
const configPath = path.join('src-tauri', '.pake', 'tauri.conf.json');
|
|
@@ -2334,8 +2432,8 @@ function resolveLocalAppName(filePath, platform) {
|
|
|
2334
2432
|
return generateLinuxPackageName(baseName) || 'pake-app';
|
|
2335
2433
|
}
|
|
2336
2434
|
const normalized = baseName
|
|
2337
|
-
.replace(/[^a-zA-Z0-9\u4e00-\u9fff
|
|
2338
|
-
.replace(/^[
|
|
2435
|
+
.replace(/[^a-zA-Z0-9\u4e00-\u9fff .-]/g, '')
|
|
2436
|
+
.replace(/^[ .-]+/, '')
|
|
2339
2437
|
.replace(/\s+/g, ' ')
|
|
2340
2438
|
.trim();
|
|
2341
2439
|
return normalized || 'pake-app';
|
|
@@ -2343,7 +2441,7 @@ function resolveLocalAppName(filePath, platform) {
|
|
|
2343
2441
|
function isValidName(name, platform) {
|
|
2344
2442
|
const reg = platform === 'linux'
|
|
2345
2443
|
? /^[a-z0-9\u4e00-\u9fff][a-z0-9\u4e00-\u9fff-]*$/
|
|
2346
|
-
: /^[a-zA-Z0-9\u4e00-\u9fff][a-zA-Z0-9\u4e00-\u9fff
|
|
2444
|
+
: /^[a-zA-Z0-9\u4e00-\u9fff][a-zA-Z0-9\u4e00-\u9fff .-]*$/;
|
|
2347
2445
|
return !!name && reg.test(name);
|
|
2348
2446
|
}
|
|
2349
2447
|
async function handleOptions(options, url) {
|
|
@@ -2364,7 +2462,7 @@ async function handleOptions(options, url) {
|
|
|
2364
2462
|
}
|
|
2365
2463
|
if (name && !isValidName(name, platform)) {
|
|
2366
2464
|
const LINUX_NAME_ERROR = `โ Name should only include lowercase letters, numbers, and dashes (not leading dashes). Examples: com-123-xxx, 123pan, pan123, weread, we-read, 123.`;
|
|
2367
|
-
const DEFAULT_NAME_ERROR = `โ Name should only include letters, numbers, dashes, and spaces (not leading dashes and spaces). Examples: 123pan, 123Pan, Pan123, weread, WeRead, WERead, we-read, We Read, 123.`;
|
|
2465
|
+
const DEFAULT_NAME_ERROR = `โ Name should only include letters, numbers, dots, dashes, and spaces (not leading dots, dashes, and spaces). Examples: 123pan, 123Pan, Pan123, weread, WeRead, WERead, we-read, We Read, Vectorizer.AI, 123.`;
|
|
2368
2466
|
const errorMsg = platform === 'linux' ? LINUX_NAME_ERROR : DEFAULT_NAME_ERROR;
|
|
2369
2467
|
if (isActions) {
|
|
2370
2468
|
logger.error(errorMsg);
|
|
@@ -2443,9 +2541,12 @@ const DEFAULT_PAKE_OPTIONS = {
|
|
|
2443
2541
|
|
|
2444
2542
|
function validateNumberInput(value) {
|
|
2445
2543
|
const parsedValue = Number(value);
|
|
2446
|
-
if (
|
|
2544
|
+
if (!Number.isFinite(parsedValue)) {
|
|
2447
2545
|
throw new InvalidArgumentError('Not a number.');
|
|
2448
2546
|
}
|
|
2547
|
+
if (parsedValue < 0) {
|
|
2548
|
+
throw new InvalidArgumentError('Must not be negative.');
|
|
2549
|
+
}
|
|
2449
2550
|
return parsedValue;
|
|
2450
2551
|
}
|
|
2451
2552
|
function validateUrlInput(url) {
|
|
@@ -2578,8 +2679,8 @@ ${green('|_| \\__,_|_|\\_\\___| can turn any webpage into a desktop app with
|
|
|
2578
2679
|
.addOption(new Option('--zoom <number>', 'Initial page zoom level (50-200)')
|
|
2579
2680
|
.default(DEFAULT_PAKE_OPTIONS.zoom)
|
|
2580
2681
|
.argParser((value) => {
|
|
2581
|
-
const zoom =
|
|
2582
|
-
if (
|
|
2682
|
+
const zoom = Number(value);
|
|
2683
|
+
if (!Number.isFinite(zoom) || zoom < 50 || zoom > 200) {
|
|
2583
2684
|
throw new Error('--zoom must be a number between 50 and 200');
|
|
2584
2685
|
}
|
|
2585
2686
|
return zoom;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pake-cli",
|
|
3
|
-
"version": "3.11.
|
|
3
|
+
"version": "3.11.9",
|
|
4
4
|
"description": "๐คฑ๐ป Turn any webpage into a desktop app with one command. ๐คฑ๐ป ไธ้ฎๆๅ
็ฝ้กต็ๆ่ฝป้ๆก้ขๅบ็จใ",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=18.0.0"
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"analyze": "cd src-tauri && cargo bloat --release --crates",
|
|
39
39
|
"tauri": "tauri",
|
|
40
40
|
"cli": "cross-env NODE_ENV=development rollup -c -w",
|
|
41
|
+
"cli:dev": "cross-env NODE_ENV=development rollup -c -w",
|
|
41
42
|
"cli:build": "cross-env NODE_ENV=production rollup -c",
|
|
42
43
|
"test": "pnpm run cli:build && cross-env PAKE_CREATE_APP=1 node tests/index.js",
|
|
43
44
|
"format": "prettier --write . --ignore-unknown && find tests -name '*.js' -exec sed -i '' 's/[[:space:]]*$//' {} \\; && cd src-tauri && cargo fmt --verbose",
|
|
@@ -48,7 +49,7 @@
|
|
|
48
49
|
},
|
|
49
50
|
"type": "module",
|
|
50
51
|
"exports": "./dist/cli.js",
|
|
51
|
-
"license": "
|
|
52
|
+
"license": "GPL-3.0-or-later",
|
|
52
53
|
"dependencies": {
|
|
53
54
|
"@tauri-apps/api": "~2.10.1",
|
|
54
55
|
"@tauri-apps/cli": "^2.10.0",
|
package/src-tauri/Cargo.lock
CHANGED
package/src-tauri/Cargo.toml
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "pake"
|
|
3
|
-
version = "3.11.
|
|
3
|
+
version = "3.11.9"
|
|
4
4
|
description = "๐คฑ๐ป Turn any webpage into a desktop app with Rust."
|
|
5
5
|
authors = ["Tw93"]
|
|
6
|
-
license = "
|
|
6
|
+
license = "GPL-3.0-or-later"
|
|
7
7
|
repository = "https://github.com/tw93/Pake"
|
|
8
8
|
edition = "2021"
|
|
9
9
|
rust-version = "1.85.0"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
use crate::util::{check_file_or_append, get_download_message_with_lang, show_toast, MessageType};
|
|
2
|
-
use std::fs::
|
|
2
|
+
use std::fs::File;
|
|
3
3
|
use std::io::Write;
|
|
4
4
|
use std::str::FromStr;
|
|
5
5
|
use std::sync::atomic::{AtomicI64, Ordering};
|
|
@@ -73,13 +73,6 @@ pub struct DownloadFileParams {
|
|
|
73
73
|
language: Option<String>,
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
#[derive(serde::Deserialize)]
|
|
77
|
-
pub struct BinaryDownloadParams {
|
|
78
|
-
filename: String,
|
|
79
|
-
binary: Vec<u8>,
|
|
80
|
-
language: Option<String>,
|
|
81
|
-
}
|
|
82
|
-
|
|
83
76
|
#[derive(serde::Deserialize)]
|
|
84
77
|
pub struct NotificationParams {
|
|
85
78
|
title: String,
|
|
@@ -147,47 +140,6 @@ pub async fn download_file(app: AppHandle, params: DownloadFileParams) -> Result
|
|
|
147
140
|
}
|
|
148
141
|
}
|
|
149
142
|
|
|
150
|
-
#[command]
|
|
151
|
-
pub async fn download_file_by_binary(
|
|
152
|
-
app: AppHandle,
|
|
153
|
-
params: BinaryDownloadParams,
|
|
154
|
-
) -> Result<(), String> {
|
|
155
|
-
let window: WebviewWindow = app.get_webview_window("pake").ok_or("Window not found")?;
|
|
156
|
-
|
|
157
|
-
show_toast(
|
|
158
|
-
&window,
|
|
159
|
-
&get_download_message_with_lang(MessageType::Start, params.language.clone()),
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
let download_dir = app
|
|
163
|
-
.path()
|
|
164
|
-
.download_dir()
|
|
165
|
-
.map_err(|e| format!("Failed to get download dir: {}", e))?;
|
|
166
|
-
|
|
167
|
-
let output_path = download_dir.join(¶ms.filename);
|
|
168
|
-
|
|
169
|
-
let path_str = output_path.to_str().ok_or("Invalid output path")?;
|
|
170
|
-
|
|
171
|
-
let file_path = check_file_or_append(path_str);
|
|
172
|
-
|
|
173
|
-
match fs::write(file_path, ¶ms.binary) {
|
|
174
|
-
Ok(_) => {
|
|
175
|
-
show_toast(
|
|
176
|
-
&window,
|
|
177
|
-
&get_download_message_with_lang(MessageType::Success, params.language.clone()),
|
|
178
|
-
);
|
|
179
|
-
Ok(())
|
|
180
|
-
}
|
|
181
|
-
Err(e) => {
|
|
182
|
-
show_toast(
|
|
183
|
-
&window,
|
|
184
|
-
&get_download_message_with_lang(MessageType::Failure, params.language),
|
|
185
|
-
);
|
|
186
|
-
Err(e.to_string())
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
143
|
#[command]
|
|
192
144
|
pub fn send_notification(app: AppHandle, params: NotificationParams) -> Result<(), String> {
|
|
193
145
|
use tauri_plugin_notification::NotificationExt;
|