pake-cli 3.2.0-beta1 → 3.2.0-beta11
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 +102 -19
- package/dist/dev.js +276 -72
- package/dist/dev.js.map +1 -1
- package/package.json +10 -7
- package/src-tauri/.cargo/config.toml +19 -4
- package/src-tauri/.pake/icons/githubcustomtraypng.icns +0 -0
- package/src-tauri/.pake/icons/githubcustomtraytest.icns +0 -0
- package/src-tauri/.pake/icons/githubsystemtraytest.icns +0 -0
- package/src-tauri/.pake/icons/testfix.icns +0 -0
- package/src-tauri/.pake/icons/twi.icns +0 -0
- package/src-tauri/.pake/icons/twitter.icns +0 -0
- package/src-tauri/.pake/icons/twitter1.icns +0 -0
- package/src-tauri/.pake/icons/twitter3.icns +0 -0
- package/src-tauri/.pake/icons/twitter4.icns +0 -0
- package/src-tauri/.pake/icons/twitter5.icns +0 -0
- package/src-tauri/.pake/icons/twitterapp.icns +0 -0
- package/src-tauri/.pake/icons/twittercustomtray.icns +0 -0
- package/src-tauri/.pake/icons/twitterfinal.icns +0 -0
- package/src-tauri/.pake/icons/twitteroptimized.icns +0 -0
- package/src-tauri/.pake/icons/twittertest.icns +0 -0
- package/src-tauri/.pake/icons/wk.icns +0 -0
- package/src-tauri/.pake/icons/wk1.icns +0 -0
- package/src-tauri/.pake/pake.json +33 -0
- package/src-tauri/.pake/png/githubcustomtraypng.png +0 -0
- package/src-tauri/.pake/png/twi_tray.png +0 -0
- package/src-tauri/.pake/png/twitter5_tray.png +0 -0
- package/src-tauri/.pake/png/twittercustomtray.png +0 -0
- package/src-tauri/.pake/png/twitterfinal_tray.png +0 -0
- package/src-tauri/.pake/png/twitteroptimized_tray.png +0 -0
- package/src-tauri/.pake/tauri.conf.json +24 -0
- package/src-tauri/.pake/tauri.linux.conf.json +16 -0
- package/src-tauri/.pake/tauri.macos.conf.json +15 -0
- package/src-tauri/.pake/tauri.windows.conf.json +15 -0
- package/src-tauri/gen/schemas/acl-manifests.json +1 -1
- package/src-tauri/gen/schemas/desktop-schema.json +0 -198
- package/src-tauri/gen/schemas/macOS-schema.json +0 -198
- package/src-tauri/pake.json +2 -1
- package/src-tauri/src/app/config.rs +2 -0
- package/src-tauri/src/app/window.rs +11 -7
- package/src-tauri/tauri.linux.conf.json +2 -2
- package/cli.js +0 -2
package/dist/dev.js
CHANGED
|
@@ -10,7 +10,7 @@ import prompts from 'prompts';
|
|
|
10
10
|
import ora from 'ora';
|
|
11
11
|
import { fileTypeFromBuffer } from 'file-type';
|
|
12
12
|
import * as psl from 'psl';
|
|
13
|
-
import '
|
|
13
|
+
import icongen from 'icon-gen';
|
|
14
14
|
import { execa, execaSync } from 'execa';
|
|
15
15
|
import dns from 'dns';
|
|
16
16
|
import http from 'http';
|
|
@@ -35,34 +35,36 @@ const DEFAULT_PAKE_OPTIONS = {
|
|
|
35
35
|
targets: 'deb',
|
|
36
36
|
useLocalFile: false,
|
|
37
37
|
systemTrayIcon: '',
|
|
38
|
-
proxyUrl:
|
|
38
|
+
proxyUrl: '',
|
|
39
39
|
debug: false,
|
|
40
40
|
inject: [],
|
|
41
41
|
installerLanguage: 'en-US',
|
|
42
|
+
hideOnClose: true,
|
|
43
|
+
incognito: false,
|
|
42
44
|
};
|
|
43
45
|
// Just for cli development
|
|
44
46
|
const DEFAULT_DEV_PAKE_OPTIONS = {
|
|
45
47
|
...DEFAULT_PAKE_OPTIONS,
|
|
46
|
-
url: 'https://
|
|
47
|
-
name: '
|
|
48
|
+
url: 'https://weekly.tw93.fun/',
|
|
49
|
+
name: 'Weekly',
|
|
48
50
|
hideTitleBar: true,
|
|
49
51
|
};
|
|
50
52
|
|
|
51
53
|
const logger = {
|
|
52
54
|
info(...msg) {
|
|
53
|
-
log.info(...msg.map(m => chalk.white(m)));
|
|
55
|
+
log.info(...msg.map((m) => chalk.white(m)));
|
|
54
56
|
},
|
|
55
57
|
debug(...msg) {
|
|
56
58
|
log.debug(...msg);
|
|
57
59
|
},
|
|
58
60
|
error(...msg) {
|
|
59
|
-
log.error(...msg.map(m => chalk.red(m)));
|
|
61
|
+
log.error(...msg.map((m) => chalk.red(m)));
|
|
60
62
|
},
|
|
61
63
|
warn(...msg) {
|
|
62
|
-
log.info(...msg.map(m => chalk.yellow(m)));
|
|
64
|
+
log.info(...msg.map((m) => chalk.yellow(m)));
|
|
63
65
|
},
|
|
64
66
|
success(...msg) {
|
|
65
|
-
log.info(...msg.map(m => chalk.green(m)));
|
|
67
|
+
log.info(...msg.map((m) => chalk.green(m)));
|
|
66
68
|
},
|
|
67
69
|
};
|
|
68
70
|
|
|
@@ -70,8 +72,7 @@ const logger = {
|
|
|
70
72
|
const currentModulePath = fileURLToPath(import.meta.url);
|
|
71
73
|
// Resolve the parent directory of the current module
|
|
72
74
|
const npmDirectory = path.join(path.dirname(currentModulePath), '..');
|
|
73
|
-
const tauriConfigDirectory = path.join(npmDirectory, 'src-tauri', '.pake')
|
|
74
|
-
;
|
|
75
|
+
const tauriConfigDirectory = path.join(npmDirectory, 'src-tauri', '.pake');
|
|
75
76
|
|
|
76
77
|
const { platform: platform$2 } = process;
|
|
77
78
|
const IS_MAC = platform$2 === 'darwin';
|
|
@@ -80,7 +81,11 @@ const IS_LINUX = platform$2 === 'linux';
|
|
|
80
81
|
|
|
81
82
|
// Generates an identifier based on the given URL.
|
|
82
83
|
function getIdentifier(url) {
|
|
83
|
-
const postFixHash = crypto
|
|
84
|
+
const postFixHash = crypto
|
|
85
|
+
.createHash('md5')
|
|
86
|
+
.update(url)
|
|
87
|
+
.digest('hex')
|
|
88
|
+
.substring(0, 6);
|
|
84
89
|
return `com.pake.${postFixHash}`;
|
|
85
90
|
}
|
|
86
91
|
async function promptText(message, initial) {
|
|
@@ -107,7 +112,70 @@ function getSpinner(text) {
|
|
|
107
112
|
}).start();
|
|
108
113
|
}
|
|
109
114
|
|
|
110
|
-
|
|
115
|
+
// Extracts the domain from a given URL.
|
|
116
|
+
function getDomain(inputUrl) {
|
|
117
|
+
try {
|
|
118
|
+
const url = new URL(inputUrl);
|
|
119
|
+
// Use PSL to parse domain names.
|
|
120
|
+
const parsed = psl.parse(url.hostname);
|
|
121
|
+
// If domain is available, split it and return the SLD.
|
|
122
|
+
if ('domain' in parsed && parsed.domain) {
|
|
123
|
+
return parsed.domain.split('.')[0];
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Helper function to convert icon to required platform format
|
|
135
|
+
async function convertIconFormat(inputPath, appName) {
|
|
136
|
+
try {
|
|
137
|
+
const { path: outputDir } = await dir();
|
|
138
|
+
const platformOutputDir = path.join(outputDir, 'converted-icons');
|
|
139
|
+
await fsExtra.ensureDir(platformOutputDir);
|
|
140
|
+
// Get the required format based on current platform
|
|
141
|
+
const requiredFormat = IS_WIN ? 'ico' : IS_LINUX ? 'png' : 'icns';
|
|
142
|
+
const outputFileName = IS_WIN
|
|
143
|
+
? `${appName.toLowerCase()}_256.ico`
|
|
144
|
+
: IS_LINUX
|
|
145
|
+
? `${appName.toLowerCase()}_512.png`
|
|
146
|
+
: `${appName.toLowerCase()}.icns`;
|
|
147
|
+
const outputPath = path.join(platformOutputDir, outputFileName);
|
|
148
|
+
// Convert using icon-gen
|
|
149
|
+
if (requiredFormat === 'icns') {
|
|
150
|
+
await icongen(inputPath, platformOutputDir, {
|
|
151
|
+
report: false,
|
|
152
|
+
icns: {
|
|
153
|
+
name: appName.toLowerCase(),
|
|
154
|
+
sizes: [16, 32, 64, 128, 256, 512, 1024]
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
else if (requiredFormat === 'ico') {
|
|
159
|
+
await icongen(inputPath, platformOutputDir, {
|
|
160
|
+
report: false,
|
|
161
|
+
ico: {
|
|
162
|
+
name: `${appName.toLowerCase()}_256`,
|
|
163
|
+
sizes: [16, 24, 32, 48, 64, 128, 256]
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
// For Linux, just copy the PNG file
|
|
169
|
+
await fsExtra.copy(inputPath, outputPath);
|
|
170
|
+
}
|
|
171
|
+
return outputPath;
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
logger.warn(`✼ Icon format conversion failed: ${error.message}`);
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async function handleIcon(options, url) {
|
|
111
179
|
if (options.icon) {
|
|
112
180
|
if (options.icon.startsWith('http')) {
|
|
113
181
|
return downloadIcon(options.icon);
|
|
@@ -117,6 +185,13 @@ async function handleIcon(options) {
|
|
|
117
185
|
}
|
|
118
186
|
}
|
|
119
187
|
else {
|
|
188
|
+
// Try to get favicon from website if URL is provided and it's a web URL
|
|
189
|
+
if (url && url.startsWith('http') && options.name) {
|
|
190
|
+
const faviconPath = await tryGetFavicon(url, options.name);
|
|
191
|
+
if (faviconPath) {
|
|
192
|
+
return faviconPath;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
120
195
|
logger.warn('✼ No icon given, default in use. For a custom icon, use --icon option.');
|
|
121
196
|
const iconPath = IS_WIN
|
|
122
197
|
? 'src-tauri/png/icon_256.ico'
|
|
@@ -126,16 +201,69 @@ async function handleIcon(options) {
|
|
|
126
201
|
return path.join(npmDirectory, iconPath);
|
|
127
202
|
}
|
|
128
203
|
}
|
|
129
|
-
async function
|
|
130
|
-
const
|
|
204
|
+
async function tryGetFavicon(url, appName) {
|
|
205
|
+
const domain = getDomain(url);
|
|
206
|
+
if (!domain) {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
const spinner = getSpinner('Fetching website favicon...');
|
|
210
|
+
// List of logo/favicon services to try in order of preference
|
|
211
|
+
const logoServices = [
|
|
212
|
+
// Professional Logo APIs (free tier, higher quality)
|
|
213
|
+
`https://logo.clearbit.com/${domain}?size=256`,
|
|
214
|
+
`https://logo.uplead.com/${domain}`,
|
|
215
|
+
// Google's reliable service with larger size
|
|
216
|
+
`https://www.google.com/s2/favicons?domain=${domain}&sz=256`,
|
|
217
|
+
// Other favicon services
|
|
218
|
+
`https://favicon.is/${domain}`,
|
|
219
|
+
`https://icons.duckduckgo.com/ip3/${domain}.ico`,
|
|
220
|
+
`https://icon.horse/icon/${domain}`,
|
|
221
|
+
// Direct favicon checks
|
|
222
|
+
`https://${domain}/favicon.ico`,
|
|
223
|
+
`https://www.${domain}/favicon.ico`,
|
|
224
|
+
`https://${domain}/apple-touch-icon.png`,
|
|
225
|
+
`https://${domain}/apple-touch-icon-precomposed.png`,
|
|
226
|
+
];
|
|
227
|
+
for (const logoUrl of logoServices) {
|
|
228
|
+
try {
|
|
229
|
+
const faviconPath = await downloadIcon(logoUrl, false);
|
|
230
|
+
if (faviconPath) {
|
|
231
|
+
spinner.text = 'Converting favicon to platform format...';
|
|
232
|
+
// Convert to the correct format for the current platform
|
|
233
|
+
const convertedPath = await convertIconFormat(faviconPath, appName);
|
|
234
|
+
if (convertedPath) {
|
|
235
|
+
spinner.succeed(chalk.green(`Favicon downloaded and converted for ${domain}!`));
|
|
236
|
+
return convertedPath;
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
// If conversion fails, try the next service
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
// Continue to next service
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
spinner.fail(chalk.yellow(`No favicon found for ${domain}, using default icon.`));
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
async function downloadIcon(iconUrl, showSpinner = true) {
|
|
253
|
+
const spinner = showSpinner ? getSpinner('Downloading icon...') : null;
|
|
131
254
|
try {
|
|
132
|
-
const iconResponse = await axios.get(iconUrl, {
|
|
255
|
+
const iconResponse = await axios.get(iconUrl, {
|
|
256
|
+
responseType: 'arraybuffer',
|
|
257
|
+
timeout: 10000, // 10 second timeout
|
|
258
|
+
});
|
|
133
259
|
const iconData = await iconResponse.data;
|
|
134
|
-
if (!iconData) {
|
|
260
|
+
if (!iconData || iconData.byteLength < 100) {
|
|
261
|
+
// Skip very small responses (likely error pages)
|
|
135
262
|
return null;
|
|
136
263
|
}
|
|
137
264
|
const fileDetails = await fileTypeFromBuffer(iconData);
|
|
138
|
-
if (!fileDetails) {
|
|
265
|
+
if (!fileDetails || !['png', 'ico', 'jpeg', 'jpg', 'gif', 'webp'].includes(fileDetails.ext)) {
|
|
266
|
+
// Only accept common image formats
|
|
139
267
|
return null;
|
|
140
268
|
}
|
|
141
269
|
const { path: tempPath } = await dir();
|
|
@@ -149,33 +277,21 @@ async function downloadIcon(iconUrl) {
|
|
|
149
277
|
await fsExtra.outputFile(iconPath, iconData);
|
|
150
278
|
}
|
|
151
279
|
await fsExtra.outputFile(iconPath, iconData);
|
|
152
|
-
spinner
|
|
280
|
+
if (spinner) {
|
|
281
|
+
spinner.succeed(chalk.green('Icon downloaded successfully!'));
|
|
282
|
+
}
|
|
153
283
|
return iconPath;
|
|
154
284
|
}
|
|
155
285
|
catch (error) {
|
|
156
|
-
spinner
|
|
286
|
+
if (spinner) {
|
|
287
|
+
spinner.fail(chalk.red('Icon download failed!'));
|
|
288
|
+
}
|
|
157
289
|
if (error.response && error.response.status === 404) {
|
|
158
290
|
return null;
|
|
159
291
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Extracts the domain from a given URL.
|
|
165
|
-
function getDomain(inputUrl) {
|
|
166
|
-
try {
|
|
167
|
-
const url = new URL(inputUrl);
|
|
168
|
-
// Use PSL to parse domain names.
|
|
169
|
-
const parsed = psl.parse(url.hostname);
|
|
170
|
-
// If domain is available, split it and return the SLD.
|
|
171
|
-
if ('domain' in parsed && parsed.domain) {
|
|
172
|
-
return parsed.domain.split('.')[0];
|
|
173
|
-
}
|
|
174
|
-
else {
|
|
175
|
-
return null;
|
|
292
|
+
if (showSpinner) {
|
|
293
|
+
throw error;
|
|
176
294
|
}
|
|
177
|
-
}
|
|
178
|
-
catch (error) {
|
|
179
295
|
return null;
|
|
180
296
|
}
|
|
181
297
|
}
|
|
@@ -186,8 +302,8 @@ function resolveAppName(name, platform) {
|
|
|
186
302
|
}
|
|
187
303
|
function isValidName(name, platform) {
|
|
188
304
|
const platformRegexMapping = {
|
|
189
|
-
linux: /^[a-z0-9]
|
|
190
|
-
default: /^[a-zA-Z0-9]
|
|
305
|
+
linux: /^[a-z0-9][a-z0-9-]*$/,
|
|
306
|
+
default: /^[a-zA-Z0-9][a-zA-Z0-9- ]*$/,
|
|
191
307
|
};
|
|
192
308
|
const reg = platformRegexMapping[platform] || platformRegexMapping.default;
|
|
193
309
|
return !!name && reg.test(name);
|
|
@@ -203,9 +319,14 @@ async function handleOptions(options, url) {
|
|
|
203
319
|
const namePrompt = await promptText(promptMessage, defaultName);
|
|
204
320
|
name = namePrompt || defaultName;
|
|
205
321
|
}
|
|
322
|
+
// Handle platform-specific name formatting
|
|
323
|
+
if (name && platform === 'linux') {
|
|
324
|
+
// Convert to lowercase and replace spaces with dashes for Linux
|
|
325
|
+
name = name.toLowerCase().replace(/\s+/g, '-');
|
|
326
|
+
}
|
|
206
327
|
if (!isValidName(name, platform)) {
|
|
207
|
-
const LINUX_NAME_ERROR = `✕
|
|
208
|
-
const DEFAULT_NAME_ERROR = `✕ Name should only include letters
|
|
328
|
+
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.`;
|
|
329
|
+
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.`;
|
|
209
330
|
const errorMsg = platform === 'linux' ? LINUX_NAME_ERROR : DEFAULT_NAME_ERROR;
|
|
210
331
|
logger.error(errorMsg);
|
|
211
332
|
if (isActions) {
|
|
@@ -221,13 +342,13 @@ async function handleOptions(options, url) {
|
|
|
221
342
|
name,
|
|
222
343
|
identifier: getIdentifier(url),
|
|
223
344
|
};
|
|
224
|
-
appOptions.icon = await handleIcon(appOptions);
|
|
345
|
+
appOptions.icon = await handleIcon(appOptions, url);
|
|
225
346
|
return appOptions;
|
|
226
347
|
}
|
|
227
348
|
|
|
228
349
|
var windows = [
|
|
229
350
|
{
|
|
230
|
-
url: "https://
|
|
351
|
+
url: "https://weekly.tw93.fun/",
|
|
231
352
|
url_type: "web",
|
|
232
353
|
hide_title_bar: true,
|
|
233
354
|
fullscreen: false,
|
|
@@ -237,7 +358,9 @@ var windows = [
|
|
|
237
358
|
always_on_top: false,
|
|
238
359
|
dark_mode: false,
|
|
239
360
|
activation_shortcut: "",
|
|
240
|
-
disabled_web_shortcuts: false
|
|
361
|
+
disabled_web_shortcuts: false,
|
|
362
|
+
hide_on_close: true,
|
|
363
|
+
incognito: false
|
|
241
364
|
}
|
|
242
365
|
];
|
|
243
366
|
var user_agent = {
|
|
@@ -263,13 +386,13 @@ var pakeConf = {
|
|
|
263
386
|
proxy_url: proxy_url
|
|
264
387
|
};
|
|
265
388
|
|
|
266
|
-
var productName$1 = "
|
|
267
|
-
var identifier = "com.pake.
|
|
389
|
+
var productName$1 = "Weekly";
|
|
390
|
+
var identifier = "com.pake.weekly";
|
|
268
391
|
var version = "1.0.0";
|
|
269
392
|
var app = {
|
|
270
393
|
withGlobalTauri: true,
|
|
271
394
|
trayIcon: {
|
|
272
|
-
iconPath: "png/
|
|
395
|
+
iconPath: "png/weekly_512.png",
|
|
273
396
|
iconAsTemplate: false,
|
|
274
397
|
id: "pake-tray"
|
|
275
398
|
}
|
|
@@ -287,12 +410,12 @@ var CommonConf = {
|
|
|
287
410
|
|
|
288
411
|
var bundle$2 = {
|
|
289
412
|
icon: [
|
|
290
|
-
"png/
|
|
291
|
-
"png/
|
|
413
|
+
"png/weekly_256.ico",
|
|
414
|
+
"png/weekly_32.ico"
|
|
292
415
|
],
|
|
293
416
|
active: true,
|
|
294
417
|
resources: [
|
|
295
|
-
"png/
|
|
418
|
+
"png/weekly_32.ico"
|
|
296
419
|
],
|
|
297
420
|
targets: [
|
|
298
421
|
"msi"
|
|
@@ -313,7 +436,7 @@ var WinConf = {
|
|
|
313
436
|
|
|
314
437
|
var bundle$1 = {
|
|
315
438
|
icon: [
|
|
316
|
-
"icons/
|
|
439
|
+
"icons/weekly.icns"
|
|
317
440
|
],
|
|
318
441
|
active: true,
|
|
319
442
|
macOS: {
|
|
@@ -326,10 +449,10 @@ var MacConf = {
|
|
|
326
449
|
bundle: bundle$1
|
|
327
450
|
};
|
|
328
451
|
|
|
329
|
-
var productName = "
|
|
452
|
+
var productName = "weekly";
|
|
330
453
|
var bundle = {
|
|
331
454
|
icon: [
|
|
332
|
-
"png/
|
|
455
|
+
"png/weekly_512.png"
|
|
333
456
|
],
|
|
334
457
|
active: true,
|
|
335
458
|
linux: {
|
|
@@ -339,7 +462,7 @@ var bundle = {
|
|
|
339
462
|
"wget"
|
|
340
463
|
],
|
|
341
464
|
files: {
|
|
342
|
-
"/usr/share/applications/com-pake-
|
|
465
|
+
"/usr/share/applications/com-pake-weekly.desktop": "assets/com-pake-weekly.desktop"
|
|
343
466
|
}
|
|
344
467
|
}
|
|
345
468
|
},
|
|
@@ -374,16 +497,23 @@ let tauriConfig = {
|
|
|
374
497
|
pake: pakeConf,
|
|
375
498
|
};
|
|
376
499
|
|
|
377
|
-
async function shellExec(command) {
|
|
500
|
+
async function shellExec(command, timeout = 300000) {
|
|
378
501
|
try {
|
|
379
502
|
const { exitCode } = await execa(command, {
|
|
380
503
|
cwd: npmDirectory,
|
|
381
|
-
stdio: 'inherit'
|
|
504
|
+
stdio: 'inherit',
|
|
505
|
+
shell: true,
|
|
506
|
+
timeout,
|
|
382
507
|
});
|
|
383
508
|
return exitCode;
|
|
384
509
|
}
|
|
385
510
|
catch (error) {
|
|
386
|
-
|
|
511
|
+
const exitCode = error.exitCode ?? 'unknown';
|
|
512
|
+
const errorMessage = error.message || 'Unknown error occurred';
|
|
513
|
+
if (error.timedOut) {
|
|
514
|
+
throw new Error(`Command timed out after ${timeout}ms: "${command}". Try increasing timeout or check network connectivity.`);
|
|
515
|
+
}
|
|
516
|
+
throw new Error(`Error occurred while executing command "${command}". Exit code: ${exitCode}. Details: ${errorMessage}`);
|
|
387
517
|
}
|
|
388
518
|
}
|
|
389
519
|
|
|
@@ -394,12 +524,12 @@ const ping = async (host) => {
|
|
|
394
524
|
const start = new Date();
|
|
395
525
|
// Prevent timeouts from affecting user experience.
|
|
396
526
|
const requestPromise = new Promise((resolve, reject) => {
|
|
397
|
-
const req = http.get(`http://${ip.address}`, res => {
|
|
527
|
+
const req = http.get(`http://${ip.address}`, (res) => {
|
|
398
528
|
const delay = new Date().getTime() - start.getTime();
|
|
399
529
|
res.resume();
|
|
400
530
|
resolve(delay);
|
|
401
531
|
});
|
|
402
|
-
req.on('error', err => {
|
|
532
|
+
req.on('error', (err) => {
|
|
403
533
|
reject(err);
|
|
404
534
|
});
|
|
405
535
|
});
|
|
@@ -461,21 +591,42 @@ function checkRustInstalled() {
|
|
|
461
591
|
}
|
|
462
592
|
|
|
463
593
|
async function combineFiles(files, output) {
|
|
464
|
-
const contents = files.map(file => {
|
|
594
|
+
const contents = files.map((file) => {
|
|
465
595
|
const fileContent = fs.readFileSync(file);
|
|
466
596
|
if (file.endsWith('.css')) {
|
|
467
597
|
return ("window.addEventListener('DOMContentLoaded', (_event) => { const css = `" +
|
|
468
598
|
fileContent +
|
|
469
599
|
"`; const style = document.createElement('style'); style.innerHTML = css; document.head.appendChild(style); });");
|
|
470
600
|
}
|
|
471
|
-
return "window.addEventListener('DOMContentLoaded', (_event) => { " +
|
|
601
|
+
return ("window.addEventListener('DOMContentLoaded', (_event) => { " +
|
|
602
|
+
fileContent +
|
|
603
|
+
' });');
|
|
472
604
|
});
|
|
473
605
|
fs.writeFileSync(output, contents.join('\n'));
|
|
474
606
|
return files;
|
|
475
607
|
}
|
|
476
608
|
|
|
477
609
|
async function mergeConfig(url, options, tauriConf) {
|
|
478
|
-
|
|
610
|
+
// Ensure .pake directory exists and copy source templates if needed
|
|
611
|
+
const srcTauriDir = path.join(npmDirectory, 'src-tauri');
|
|
612
|
+
await fsExtra.ensureDir(tauriConfigDirectory);
|
|
613
|
+
// Copy source config files to .pake directory (as templates)
|
|
614
|
+
const sourceFiles = [
|
|
615
|
+
'tauri.conf.json',
|
|
616
|
+
'tauri.macos.conf.json',
|
|
617
|
+
'tauri.windows.conf.json',
|
|
618
|
+
'tauri.linux.conf.json',
|
|
619
|
+
'pake.json',
|
|
620
|
+
];
|
|
621
|
+
await Promise.all(sourceFiles.map(async (file) => {
|
|
622
|
+
const sourcePath = path.join(srcTauriDir, file);
|
|
623
|
+
const destPath = path.join(tauriConfigDirectory, file);
|
|
624
|
+
if ((await fsExtra.pathExists(sourcePath)) &&
|
|
625
|
+
!(await fsExtra.pathExists(destPath))) {
|
|
626
|
+
await fsExtra.copy(sourcePath, destPath);
|
|
627
|
+
}
|
|
628
|
+
}));
|
|
629
|
+
const { width, height, fullscreen, hideTitleBar, alwaysOnTop, appVersion, darkMode, disabledWebShortcuts, activationShortcut, userAgent, showSystemTray, systemTrayIcon, useLocalFile, identifier, name, resizable = true, inject, proxyUrl, installerLanguage, hideOnClose, incognito, title, } = options;
|
|
479
630
|
const { platform } = process;
|
|
480
631
|
// Set Windows parameters.
|
|
481
632
|
const tauriConfWindowOptions = {
|
|
@@ -488,6 +639,9 @@ async function mergeConfig(url, options, tauriConf) {
|
|
|
488
639
|
always_on_top: alwaysOnTop,
|
|
489
640
|
dark_mode: darkMode,
|
|
490
641
|
disabled_web_shortcuts: disabledWebShortcuts,
|
|
642
|
+
hide_on_close: hideOnClose,
|
|
643
|
+
incognito: incognito,
|
|
644
|
+
title: title || null,
|
|
491
645
|
};
|
|
492
646
|
Object.assign(tauriConf.pake.windows[0], { url, ...tauriConfWindowOptions });
|
|
493
647
|
tauriConf.productName = name;
|
|
@@ -515,7 +669,7 @@ async function mergeConfig(url, options, tauriConf) {
|
|
|
515
669
|
// ignore it, because about_pake.html have be erased.
|
|
516
670
|
// const filesToCopyBack = ['cli.js', 'about_pake.html'];
|
|
517
671
|
const filesToCopyBack = ['cli.js'];
|
|
518
|
-
await Promise.all(filesToCopyBack.map(file => fsExtra.copy(path.join(distBakDir, file), path.join(distDir, file))));
|
|
672
|
+
await Promise.all(filesToCopyBack.map((file) => fsExtra.copy(path.join(distBakDir, file), path.join(distDir, file))));
|
|
519
673
|
}
|
|
520
674
|
tauriConf.pake.windows[0].url = fileName;
|
|
521
675
|
tauriConf.pake.windows[0].url_type = 'local';
|
|
@@ -619,11 +773,11 @@ async function mergeConfig(url, options, tauriConf) {
|
|
|
619
773
|
const injectFilePath = path.join(npmDirectory, `src-tauri/src/inject/custom.js`);
|
|
620
774
|
// inject js or css files
|
|
621
775
|
if (inject?.length > 0) {
|
|
622
|
-
if (!inject.every(item => item.endsWith('.css') || item.endsWith('.js'))) {
|
|
776
|
+
if (!inject.every((item) => item.endsWith('.css') || item.endsWith('.js'))) {
|
|
623
777
|
logger.error('The injected file must be in either CSS or JS format.');
|
|
624
778
|
return;
|
|
625
779
|
}
|
|
626
|
-
const files = inject.map(filepath =>
|
|
780
|
+
const files = inject.map((filepath) => path.isAbsolute(filepath) ? filepath : path.join(process.cwd(), filepath));
|
|
627
781
|
tauriConf.pake.inject = files;
|
|
628
782
|
await combineFiles(files, injectFilePath);
|
|
629
783
|
}
|
|
@@ -685,14 +839,21 @@ class BaseBuilder {
|
|
|
685
839
|
const rustProjectDir = path.join(tauriSrcPath, '.cargo');
|
|
686
840
|
const projectConf = path.join(rustProjectDir, 'config.toml');
|
|
687
841
|
await fsExtra.ensureDir(rustProjectDir);
|
|
842
|
+
// For global CLI installation, always use npm
|
|
843
|
+
const packageManager = 'npm';
|
|
844
|
+
const registryOption = isChina
|
|
845
|
+
? ' --registry=https://registry.npmmirror.com'
|
|
846
|
+
: '';
|
|
847
|
+
// Windows环境下需要更长的超时时间
|
|
848
|
+
const timeout = process.platform === 'win32' ? 600000 : 300000;
|
|
688
849
|
if (isChina) {
|
|
689
850
|
logger.info('✺ Located in China, using npm/rsProxy CN mirror.');
|
|
690
851
|
const projectCnConf = path.join(tauriSrcPath, 'rust_proxy.toml');
|
|
691
852
|
await fsExtra.copy(projectCnConf, projectConf);
|
|
692
|
-
await shellExec(`cd "${npmDirectory}" &&
|
|
853
|
+
await shellExec(`cd "${npmDirectory}" && ${packageManager} install${registryOption}`, timeout);
|
|
693
854
|
}
|
|
694
855
|
else {
|
|
695
|
-
await shellExec(`cd "${npmDirectory}" &&
|
|
856
|
+
await shellExec(`cd "${npmDirectory}" && ${packageManager} install`, timeout);
|
|
696
857
|
}
|
|
697
858
|
spinner.succeed(chalk.green('Package installed!'));
|
|
698
859
|
if (!tauriTargetPathExists) {
|
|
@@ -726,25 +887,66 @@ class BaseBuilder {
|
|
|
726
887
|
return target;
|
|
727
888
|
}
|
|
728
889
|
getBuildCommand() {
|
|
729
|
-
|
|
730
|
-
|
|
890
|
+
const baseCommand = this.options.debug
|
|
891
|
+
? 'npm run build:debug'
|
|
892
|
+
: 'npm run build';
|
|
893
|
+
// Use temporary config directory to avoid modifying source files
|
|
894
|
+
const configPath = path.join(npmDirectory, 'src-tauri', '.pake', 'tauri.conf.json');
|
|
895
|
+
let fullCommand = `${baseCommand} -- -c "${configPath}"`;
|
|
896
|
+
// For macOS, use app bundles by default unless DMG is explicitly requested
|
|
897
|
+
if (IS_MAC && this.options.targets === 'app') {
|
|
898
|
+
fullCommand += ' --bundles app';
|
|
899
|
+
}
|
|
900
|
+
// Add macos-proxy feature for modern macOS (Darwin 23+ = macOS 14+)
|
|
901
|
+
if (IS_MAC) {
|
|
902
|
+
const macOSVersion = this.getMacOSMajorVersion();
|
|
903
|
+
if (macOSVersion >= 23) {
|
|
904
|
+
fullCommand += ' --features macos-proxy';
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
return fullCommand;
|
|
908
|
+
}
|
|
909
|
+
getMacOSMajorVersion() {
|
|
910
|
+
try {
|
|
911
|
+
const os = require('os');
|
|
912
|
+
const release = os.release();
|
|
913
|
+
const majorVersion = parseInt(release.split('.')[0], 10);
|
|
914
|
+
return majorVersion;
|
|
915
|
+
}
|
|
916
|
+
catch (error) {
|
|
917
|
+
return 0; // Disable proxy feature if version detection fails
|
|
918
|
+
}
|
|
731
919
|
}
|
|
732
920
|
getBasePath() {
|
|
733
921
|
const basePath = this.options.debug ? 'debug' : 'release';
|
|
734
922
|
return `src-tauri/target/${basePath}/bundle/`;
|
|
735
923
|
}
|
|
736
924
|
getBuildAppPath(npmDirectory, fileName, fileType) {
|
|
737
|
-
|
|
925
|
+
// For app bundles on macOS, the directory is 'macos', not 'app'
|
|
926
|
+
const bundleDir = fileType.toLowerCase() === 'app' ? 'macos' : fileType.toLowerCase();
|
|
927
|
+
return path.join(npmDirectory, this.getBasePath(), bundleDir, `${fileName}.${fileType}`);
|
|
738
928
|
}
|
|
739
929
|
}
|
|
740
930
|
|
|
741
931
|
class MacBuilder extends BaseBuilder {
|
|
742
932
|
constructor(options) {
|
|
743
933
|
super(options);
|
|
744
|
-
|
|
934
|
+
// Use DMG by default for distribution
|
|
935
|
+
// Only create app bundles for testing to avoid user interaction
|
|
936
|
+
if (process.env.PAKE_CREATE_APP === '1') {
|
|
937
|
+
this.options.targets = 'app';
|
|
938
|
+
}
|
|
939
|
+
else {
|
|
940
|
+
this.options.targets = 'dmg';
|
|
941
|
+
}
|
|
745
942
|
}
|
|
746
943
|
getFileName() {
|
|
747
944
|
const { name } = this.options;
|
|
945
|
+
// For app bundles, use simple name without version/arch
|
|
946
|
+
if (this.options.targets === 'app') {
|
|
947
|
+
return name;
|
|
948
|
+
}
|
|
949
|
+
// For DMG files, use versioned filename
|
|
748
950
|
let arch;
|
|
749
951
|
if (this.options.multiArch) {
|
|
750
952
|
arch = 'universal';
|
|
@@ -755,7 +957,9 @@ class MacBuilder extends BaseBuilder {
|
|
|
755
957
|
return `${name}_${tauriConfig.version}_${arch}`;
|
|
756
958
|
}
|
|
757
959
|
getBuildCommand() {
|
|
758
|
-
return this.options.multiArch
|
|
960
|
+
return this.options.multiArch
|
|
961
|
+
? 'npm run build:mac'
|
|
962
|
+
: super.getBuildCommand();
|
|
759
963
|
}
|
|
760
964
|
getBasePath() {
|
|
761
965
|
return this.options.multiArch
|