@yufengtadian/freedom-cli 1.1.11 → 1.10.12
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/README.md +36 -1
- package/lib/build.js +106 -2
- package/lib/cli.js +52 -2
- package/lib/dmg.js +43 -0
- package/lib/tui.js +2 -2
- package/lib/utils.js +6 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,11 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
AIGC:
|
|
3
|
+
Label: "1"
|
|
4
|
+
ContentProducer: 001191440300708461136T1XGW3
|
|
5
|
+
ProduceID: 84e692d233baf4c4ebb909c06f044dd3_70353d809c3a11f19bec525400826444
|
|
6
|
+
ReservedCode1: F+Qlzocu3qufDKwdTzizi2zKAc4B1PeLnx1QG2eBbCzoFNe8VCjjy7ZVan5+wjHbBQrGtKwJC6zkKzoSrFUq7Gv3XMiE7j3v8hF+C7Tmpw0ziqQdD/JCj6m6KXkxPjJqrRK2gF51x03qxPXi6Wsiye+vID13w/vsTmLretYS7o5/4Aagvd1xxvh/i3k=
|
|
7
|
+
ContentPropagator: 001191440300708461136T1XGW3
|
|
8
|
+
PropagateID: 84e692d233baf4c4ebb909c06f044dd3_70353d809c3a11f19bec525400826444
|
|
9
|
+
ReservedCode2: F+Qlzocu3qufDKwdTzizi2zKAc4B1PeLnx1QG2eBbCzoFNe8VCjjy7ZVan5+wjHbBQrGtKwJC6zkKzoSrFUq7Gv3XMiE7j3v8hF+C7Tmpw0ziqQdD/JCj6m6KXkxPjJqrRK2gF51x03qxPXi6Wsiye+vID13w/vsTmLretYS7o5/4Aagvd1xxvh/i3k=
|
|
10
|
+
---
|
|
11
|
+
|
|
1
12
|
# freedom-cli
|
|
2
13
|
|
|
3
|
-
Freedom 桌面壳打包工具:把你的 Web 前端一键打包成跨平台桌面应用(v1.
|
|
14
|
+
Freedom 桌面壳打包工具:把你的 Web 前端一键打包成跨平台桌面应用(v1.10.12)。
|
|
4
15
|
|
|
5
16
|
基于自研 Freedom WebView 壳层(对标 Wails / Tauri):前端完全自由、后端可任意语言、渲染复用系统 WebView(Windows WebView2 / macOS WKWebView / Linux WebKitGTK),产物为单个可执行文件 + resources 目录,前端页面内存加载,不占本地端口。
|
|
6
17
|
|
|
18
|
+
**v1.10.12 核心升级**:macOS 产物升级为标准 .app 应用包,直接产出 `.app.zip`(mac 用户解压即得 `.app`,拖入 /Applications 即可使用);在 macOS 上执行 `freedom dmg` 可再用系统 hdiutil 生成 `.dmg`。全程仍不依赖 Go 工具链。
|
|
19
|
+
|
|
7
20
|
**v1.1.11 核心升级**:不再依赖 Go 工具链。壳层以预编译二进制随 npm 包分发(`shell/<platform>/`),`freedom build` 直接复制壳 + 写入前端与配置即可出包,Windows / macOS / Linux 三平台一次全出,任意前后端语言自由组合。
|
|
8
21
|
|
|
22
|
+
**v1.10.12 新特性**:
|
|
23
|
+
- macOS 产物打包为标准 `.app` bundle + `.app.zip`:壳复制进 `Contents/MacOS/`,resources 与前端/后端随包,解压即用,无需任何语言运行时;
|
|
24
|
+
- `freedom dmg`:在 macOS 上用系统 hdiutil 把 `.app` 打成 `.dmg`(Windows / Linux 上运行会给出指引);
|
|
25
|
+
- TUI 壳管理去 Go 依赖感:「下载壳」标注无需 Go,「本地编译壳」明确为可选项(壳已预编译随包分发)。
|
|
26
|
+
|
|
9
27
|
**v1.1.11 新特性**:
|
|
10
28
|
- `freedom tui` 交互式终端界面:菜单化完成打包 / 新建项目 / 修改配置 / 壳管理 / 教程,零依赖、方向键 + 回车导航、支持搜索过滤与多选;
|
|
11
29
|
- 前端构建缓存:源码无改动时跳过重复 `vite build`(秒级复用,典型加速 10 倍+),`--no-cache` 强制全量重建;
|
|
@@ -46,6 +64,20 @@ freedom build --no-cache # 忽略前端构建缓存,强制重建
|
|
|
46
64
|
|
|
47
65
|
构建产物默认输出到 `dist/`。`dist/<应用名>.exe`(或对应平台可执行文件)为壳层程序,运行时从 `dist/resources/` 加载前端页面与配置,不弹 cmd 黑窗。
|
|
48
66
|
|
|
67
|
+
**macOS 产物(v1.10.12)**:`freedom build --platform mac` 除裸可执行文件外,额外生成标准 `.app` bundle 与 `<应用名>-<平台>.app.zip`:
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
dist/<应用名>.app/
|
|
71
|
+
└── Contents/
|
|
72
|
+
├── Info.plist
|
|
73
|
+
├── MacOS/
|
|
74
|
+
│ ├── <应用名> # 壳可执行文件
|
|
75
|
+
│ └── resources/ # 前端页面 + config.json + backend/
|
|
76
|
+
└── Resources/
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
mac 用户解压 `.app.zip` 即得 `.app`,拖入 `/Applications` 即可直接运行,无需安装任何语言运行时。如需 `.dmg` 安装映像,把产物拷到 macOS 上执行 `freedom dmg`(用系统 hdiutil 生成),或仅分发包内 `.app.zip`。
|
|
80
|
+
|
|
49
81
|
**构建缓存**:CLI 会在 `.freedom/vite-dist/` 记录前端源码指纹,源码无改动时复用上次构建产物直接进入分发阶段(秒级完成)。修改了 `src/` / `index.html` / `vite.config.js` 等任一处会触发自动重建;需要强制全量时可加 `--no-cache`。
|
|
50
82
|
|
|
51
83
|
**三平台打包**:壳层二进制随包分发,无需本机安装 Go 或交叉编译工具链。首次构建某平台时若本地缺少对应壳二进制,CLI 自动从 GitHub Releases 下载(`freedom shell download <platform>` 可手动补齐);仅当你想用本机 Go 工具链亲自编译壳时才需要 `freedom shell build`(只产出本机平台)。多平台分发为并行执行,全平台打包耗时大幅缩短。
|
|
@@ -99,6 +131,7 @@ window.freedom.window.minimize(); // 窗口控制
|
|
|
99
131
|
- Node.js >= 18
|
|
100
132
|
- 不需要 Go 工具链(壳层为随包分发的预编译二进制)
|
|
101
133
|
- 可选:`freedom shell build` 需本机 Go(含 CGO,Windows 需 MSVC 编译环境),仅用于亲自编译壳
|
|
134
|
+
- `.dmg` 打包需在 macOS 上执行(依赖系统 hdiutil);`.app.zip` 在任意平台均可直接产出
|
|
102
135
|
|
|
103
136
|
## 命令
|
|
104
137
|
|
|
@@ -109,6 +142,7 @@ freedom build [--platform win-x64|mac-arm64|linux-x64|all] [--no-cache]
|
|
|
109
142
|
freedom titlebar <native|hidden|frameless>
|
|
110
143
|
freedom config [get|set]
|
|
111
144
|
freedom shell list|download <platform>|build <platform>
|
|
145
|
+
freedom dmg [--platform <plat>] # 在 macOS 上把 .app 打包为 .dmg
|
|
112
146
|
freedom tutorial
|
|
113
147
|
freedom help
|
|
114
148
|
```
|
|
@@ -135,3 +169,4 @@ export default {
|
|
|
135
169
|
## License
|
|
136
170
|
|
|
137
171
|
MIT
|
|
172
|
+
*(内容由AI生成,仅供参考)*
|
package/lib/build.js
CHANGED
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
// - 写 resources/config.json(窗口 + 后端配置,壳运行时读取)
|
|
12
12
|
// - 复制壳为 outDir/<app>[.exe]
|
|
13
13
|
// - 复制 backend/ 到 resources/backend/(配置了 backend 时)
|
|
14
|
+
// 4. macOS 平台额外生成 .app bundle + .app.zip:
|
|
15
|
+
// outDir/<app>.app/Contents/{Info.plist, MacOS/<app>, MacOS/resources/}
|
|
16
|
+
// 壳加载 exe 同目录 resources/,因此 .app 无需改壳即可运行;
|
|
17
|
+
// mac 用户解压 .app.zip 得 .app,拖入 /Applications 即可使用;
|
|
18
|
+
// 如需 .dmg,在 macOS 上运行 freedom dmg 用系统 hdiutil 生成。
|
|
14
19
|
//
|
|
15
20
|
// 产物不嵌入 HTML,页面与配置均在 exe 同目录 resources/ 下,因此壳可复用、
|
|
16
21
|
// 可跨平台分发、无需在目标机器安装任何语言运行时。
|
|
@@ -23,6 +28,7 @@ const { copyDir } = require('./utils');
|
|
|
23
28
|
const {
|
|
24
29
|
ALL_PLATFORMS,
|
|
25
30
|
isWinPlat,
|
|
31
|
+
isMacPlat,
|
|
26
32
|
platformExeName,
|
|
27
33
|
nativePlatform,
|
|
28
34
|
localShellPath,
|
|
@@ -69,6 +75,7 @@ async function build(projectDir, opts = {}) {
|
|
|
69
75
|
const cfg = await loadConfig(dir);
|
|
70
76
|
|
|
71
77
|
const name = (cfg.name || 'freedom-app').replace(/[^a-zA-Z0-9_.-]/g, '-');
|
|
78
|
+
const version = String(cfg.version || appVersionFromPkg(dir) || '1.0.0');
|
|
72
79
|
const outDir = String(cfg.outDir || 'dist').trim() || 'dist';
|
|
73
80
|
const outDirPath = path.resolve(dir, outDir);
|
|
74
81
|
const platforms = parsePlatforms(opts.platform);
|
|
@@ -103,7 +110,7 @@ async function build(projectDir, opts = {}) {
|
|
|
103
110
|
const results = await Promise.all(
|
|
104
111
|
platforms.map(async (plat) => {
|
|
105
112
|
const targetDir = multi ? path.join(outDirPath, plat) : outDirPath;
|
|
106
|
-
const outFile = await emitPlatform({ plat, name, targetDir, html, configJSON, backendDir, hasBackend, autoDownload });
|
|
113
|
+
const outFile = await emitPlatform({ plat, name, version, targetDir, html, configJSON, backendDir, hasBackend, autoDownload });
|
|
107
114
|
return { plat, outFile };
|
|
108
115
|
})
|
|
109
116
|
);
|
|
@@ -111,7 +118,7 @@ async function build(projectDir, opts = {}) {
|
|
|
111
118
|
return { results };
|
|
112
119
|
}
|
|
113
120
|
|
|
114
|
-
async function emitPlatform({ plat, name, targetDir, html, configJSON, backendDir, hasBackend, autoDownload }) {
|
|
121
|
+
async function emitPlatform({ plat, name, version, targetDir, html, configJSON, backendDir, hasBackend, autoDownload }) {
|
|
115
122
|
// 取预编译壳二进制
|
|
116
123
|
const shell = localShellPath(plat);
|
|
117
124
|
if (!fs.existsSync(shell)) {
|
|
@@ -144,9 +151,106 @@ async function emitPlatform({ plat, name, targetDir, html, configJSON, backendDi
|
|
|
144
151
|
copyDir(backendDir, path.join(resDir, 'backend'));
|
|
145
152
|
}
|
|
146
153
|
|
|
154
|
+
// macOS:额外生成 .app bundle + .app.zip(供 mac 用户解压即用)
|
|
155
|
+
// 壳加载 exe 同目录 resources/,故把 resources 放进 Contents/MacOS/ 即可运行,无需改壳。
|
|
156
|
+
if (isMacPlat(plat)) {
|
|
157
|
+
const appZip = createMacApp({ targetDir, name, plat, exeName, resDir, version });
|
|
158
|
+
process.stdout.write(
|
|
159
|
+
`[freedom] macOS 产物已打包为 .app.zip:${path.relative(process.cwd(), appZip)}\n`
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
147
163
|
return outFile;
|
|
148
164
|
}
|
|
149
165
|
|
|
166
|
+
// 把已分发的裸产物升级为标准 .app bundle,并压缩为 .app.zip
|
|
167
|
+
function createMacApp({ targetDir, name, plat, exeName, resDir, version }) {
|
|
168
|
+
const appDir = path.join(targetDir, `${name}.app`);
|
|
169
|
+
const contents = path.join(appDir, 'Contents');
|
|
170
|
+
const macos = path.join(contents, 'MacOS');
|
|
171
|
+
const appRes = path.join(macos, 'resources');
|
|
172
|
+
fs.mkdirSync(macos, { recursive: true });
|
|
173
|
+
fs.mkdirSync(path.join(contents, 'Resources'), { recursive: true });
|
|
174
|
+
|
|
175
|
+
// 可执行文件:exe 名与 app 同名,放入 MacOS/
|
|
176
|
+
const exeDst = path.join(macos, name);
|
|
177
|
+
fs.copyFileSync(path.join(targetDir, exeName), exeDst);
|
|
178
|
+
fs.chmodSync(exeDst, 0o755);
|
|
179
|
+
|
|
180
|
+
// resources -> MacOS/resources(壳的运行时目录)
|
|
181
|
+
if (fs.existsSync(resDir)) {
|
|
182
|
+
copyDir(resDir, appRes);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Info.plist
|
|
186
|
+
fs.writeFileSync(path.join(contents, 'Info.plist'), renderInfoPlist(name, version), 'utf8');
|
|
187
|
+
|
|
188
|
+
// .app.zip:解压即得 .app,拖入 /Applications 即可使用
|
|
189
|
+
const zipPath = path.join(targetDir, `${name}-${plat}.app.zip`);
|
|
190
|
+
zipDir(zipPath, appDir);
|
|
191
|
+
return zipPath;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function renderInfoPlist(name, version) {
|
|
195
|
+
const safe = String(name).replace(/&/g, '&');
|
|
196
|
+
const bundleId = `com.freedom.app.${String(name).toLowerCase().replace(/[^a-z0-9.-]/g, '-')}`;
|
|
197
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
198
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
199
|
+
<plist version="1.0">
|
|
200
|
+
<dict>
|
|
201
|
+
<key>CFBundleName</key>
|
|
202
|
+
<string>${safe}</string>
|
|
203
|
+
<key>CFBundleDisplayName</key>
|
|
204
|
+
<string>${safe}</string>
|
|
205
|
+
<key>CFBundleExecutable</key>
|
|
206
|
+
<string>${safe}</string>
|
|
207
|
+
<key>CFBundleIdentifier</key>
|
|
208
|
+
<string>${bundleId}</string>
|
|
209
|
+
<key>CFBundleVersion</key>
|
|
210
|
+
<string>${version}</string>
|
|
211
|
+
<key>CFBundleShortVersionString</key>
|
|
212
|
+
<string>${version}</string>
|
|
213
|
+
<key>CFBundlePackageType</key>
|
|
214
|
+
<string>APPL</string>
|
|
215
|
+
<key>CFBundleInfoDictionaryVersion</key>
|
|
216
|
+
<string>6.0</string>
|
|
217
|
+
<key>LSMinimumSystemVersion</key>
|
|
218
|
+
<string>10.13</string>
|
|
219
|
+
<key>NSHighResolutionCapable</key>
|
|
220
|
+
<true/>
|
|
221
|
+
<key>NSAppTransportSecurity</key>
|
|
222
|
+
<dict>
|
|
223
|
+
<key>NSAllowsArbitraryLoads</key>
|
|
224
|
+
<true/>
|
|
225
|
+
</dict>
|
|
226
|
+
</dict>
|
|
227
|
+
</plist>
|
|
228
|
+
`;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// 用系统 tar(Windows 为 bsdtar)把目录压成 zip:跨平台可用,无需额外依赖
|
|
232
|
+
function zipDir(zipPath, dir) {
|
|
233
|
+
const parent = path.dirname(dir);
|
|
234
|
+
const base = path.basename(dir);
|
|
235
|
+
if (fs.existsSync(zipPath)) fs.unlinkSync(zipPath);
|
|
236
|
+
const res = spawnSync('tar', ['-a', '-c', '-f', zipPath, base], { cwd: parent, encoding: 'utf8' });
|
|
237
|
+
if (res.error || res.status !== 0) {
|
|
238
|
+
throw new Error(
|
|
239
|
+
`打包 ${base} 为 zip 失败:${(res.stderr || res.stdout || '').trim() || res.error.message}`
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// 从项目 package.json 取版本号(cfg.version 优先)
|
|
245
|
+
function appVersionFromPkg(dir) {
|
|
246
|
+
try {
|
|
247
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf8'));
|
|
248
|
+
return typeof pkg.version === 'string' ? pkg.version : null;
|
|
249
|
+
} catch (e) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
150
254
|
// 渲染 resources/config.json(与壳 configfile.go 的 runtimeConfigFile 字段对齐)
|
|
151
255
|
function renderConfigJSON(cfg, name) {
|
|
152
256
|
const obj = {
|
package/lib/cli.js
CHANGED
|
@@ -22,8 +22,9 @@ freedom - Freedom 桌面壳打包工具 v${VERSION}
|
|
|
22
22
|
freedom titlebar <native|hidden|frameless>
|
|
23
23
|
一键切换标题栏策略
|
|
24
24
|
freedom shell list 列出本地已就绪的预编译壳
|
|
25
|
-
freedom shell download <plat> 从 GitHub Releases
|
|
26
|
-
freedom shell build <plat> 本地用 Go
|
|
25
|
+
freedom shell download <plat> 从 GitHub Releases 下载预编译壳(无需 Go)
|
|
26
|
+
freedom shell build <plat> 本地用 Go 编译壳(可选,壳已预编译一般无需)
|
|
27
|
+
freedom dmg [--platform <plat>] 将已构建的 .app 打包为 .dmg(需在 macOS 上执行)
|
|
27
28
|
freedom config 查看当前配置
|
|
28
29
|
freedom config get <key> 读取单个配置项
|
|
29
30
|
freedom config set <key> <value> 修改单个配置项
|
|
@@ -34,6 +35,11 @@ freedom - Freedom 桌面壳打包工具 v${VERSION}
|
|
|
34
35
|
平台(<p>/<plat>):win-x64 / darwin-x64 / darwin-arm64 / linux-x64 / linux-arm64,
|
|
35
36
|
快捷别名:win / mac / linux / all。
|
|
36
37
|
|
|
38
|
+
macOS 产物说明:
|
|
39
|
+
freedom build --platform mac 直接产出 <app>.app.zip(解压即得 .app,
|
|
40
|
+
拖入 /Applications 即可使用,无需安装任何语言运行时);如需 .dmg,
|
|
41
|
+
在 macOS 上执行 freedom dmg 用系统 hdiutil 生成。
|
|
42
|
+
|
|
37
43
|
标题栏策略说明:
|
|
38
44
|
native 保留系统原生标题栏(默认)
|
|
39
45
|
hidden 隐藏标题栏视觉,仅保留 Windows 原生最小化/最大化/关闭按钮
|
|
@@ -90,6 +96,10 @@ async function run(argv) {
|
|
|
90
96
|
return await runShell(rest);
|
|
91
97
|
}
|
|
92
98
|
|
|
99
|
+
case 'dmg': {
|
|
100
|
+
return await runDmg(rest);
|
|
101
|
+
}
|
|
102
|
+
|
|
93
103
|
case 'titlebar': {
|
|
94
104
|
const mode = rest[0];
|
|
95
105
|
if (!['native', 'hidden', 'frameless'].includes(mode)) {
|
|
@@ -183,6 +193,7 @@ async function runShell(rest) {
|
|
|
183
193
|
}
|
|
184
194
|
const dest = buildShell(plat);
|
|
185
195
|
console.log(`[freedom] 已编译 ${plat} 壳:${dest}`);
|
|
196
|
+
console.log('[freedom] 提示:壳已预编译随包分发,一般无需本地编译。');
|
|
186
197
|
return 0;
|
|
187
198
|
}
|
|
188
199
|
default:
|
|
@@ -191,6 +202,45 @@ async function runShell(rest) {
|
|
|
191
202
|
}
|
|
192
203
|
}
|
|
193
204
|
|
|
205
|
+
// freedom dmg:在 macOS 上把已构建的 .app 打包为 .dmg(依赖系统 hdiutil)
|
|
206
|
+
async function runDmg(rest) {
|
|
207
|
+
const { loadConfig, nativePlatform } = require('./utils');
|
|
208
|
+
const { makeDmg } = require('./dmg');
|
|
209
|
+
const dir = process.cwd();
|
|
210
|
+
const cfg = await loadConfig(dir);
|
|
211
|
+
const name = (cfg.name || 'freedom-app').replace(/[^a-zA-Z0-9_.-]/g, '-');
|
|
212
|
+
const outDir = String(cfg.outDir || 'dist').trim() || 'dist';
|
|
213
|
+
|
|
214
|
+
const platArg = rest.find((a) => a.startsWith('--platform'));
|
|
215
|
+
const plat = platArg
|
|
216
|
+
? (platArg.includes('=') ? platArg.split('=')[1] : rest[rest.indexOf(platArg) + 1])
|
|
217
|
+
: nativePlatform();
|
|
218
|
+
if (!plat || !plat.startsWith('darwin')) {
|
|
219
|
+
console.error('[freedom] dmg 仅支持 macOS 平台(darwin-x64 / darwin-arm64)。');
|
|
220
|
+
return 1;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const baseDir = path.resolve(dir, outDir);
|
|
224
|
+
// 兼容 build --platform all 的 outDir/<plat> 布局
|
|
225
|
+
const candidates = [path.join(baseDir, `${name}.app`), path.join(baseDir, plat, `${name}.app`)];
|
|
226
|
+
const appDir = candidates.find((p) => fs.existsSync(p));
|
|
227
|
+
if (!appDir) {
|
|
228
|
+
console.error(`[freedom] 未找到 ${name}.app(已检查 ${candidates.join(' / ')})。`);
|
|
229
|
+
console.error(' 请先在 macOS 上运行 freedom build --platform mac 生成 .app。');
|
|
230
|
+
return 1;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const outPath = path.join(path.dirname(appDir), `${name}-${plat}.dmg`);
|
|
234
|
+
try {
|
|
235
|
+
const dmgPath = await makeDmg(appDir, outPath, name);
|
|
236
|
+
console.log(`[freedom] 已生成 dmg:${dmgPath}`);
|
|
237
|
+
return 0;
|
|
238
|
+
} catch (e) {
|
|
239
|
+
console.error(`[freedom] ${e.message}`);
|
|
240
|
+
return 1;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
194
244
|
function openBrowser(file) {
|
|
195
245
|
const { spawn } = require('child_process');
|
|
196
246
|
const plat = process.platform;
|
package/lib/dmg.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// freedom dmg:把已构建的 .app bundle 打包为 .dmg(macOS 分发格式)
|
|
4
|
+
//
|
|
5
|
+
// 说明:.dmg 依赖 macOS 系统自带的 hdiutil,只能在 macOS 上生成。
|
|
6
|
+
// Windows / Linux 上运行 freedom build 已直接产出 .app.zip(解压即得 .app,
|
|
7
|
+
// 拖入 /Applications 即可使用),无需 dmg;如仍需 dmg,把产物拷到 Mac 上执行
|
|
8
|
+
// freedom dmg 即可。
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const { spawnSync } = require('child_process');
|
|
13
|
+
|
|
14
|
+
async function makeDmg(appDir, outPath, volName) {
|
|
15
|
+
if (process.platform !== 'darwin') {
|
|
16
|
+
throw new Error(
|
|
17
|
+
'.dmg 依赖 macOS 系统 hdiutil,只能在 macOS 上生成。\n' +
|
|
18
|
+
' 当前平台可直接分发 .app.zip(解压即得 .app,拖入 /Applications 即可使用)。\n' +
|
|
19
|
+
' 如需 .dmg,请把 .app 拷到 macOS 上运行:freedom dmg'
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
23
|
+
if (fs.existsSync(outPath)) fs.unlinkSync(outPath);
|
|
24
|
+
|
|
25
|
+
const args = [
|
|
26
|
+
'create',
|
|
27
|
+
'-volname',
|
|
28
|
+
String(volName || path.basename(appDir, '.app')),
|
|
29
|
+
'-srcfolder',
|
|
30
|
+
appDir,
|
|
31
|
+
'-ov',
|
|
32
|
+
'-format',
|
|
33
|
+
'UDZO',
|
|
34
|
+
outPath,
|
|
35
|
+
];
|
|
36
|
+
const res = spawnSync('hdiutil', args, { encoding: 'utf8' });
|
|
37
|
+
if (res.error || res.status !== 0) {
|
|
38
|
+
throw new Error(`hdiutil 生成 dmg 失败:\n${(res.stderr || res.stdout || '').trim()}`);
|
|
39
|
+
}
|
|
40
|
+
return outPath;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = { makeDmg };
|
package/lib/tui.js
CHANGED
|
@@ -259,7 +259,7 @@ async function configFlow(tui, cwd) {
|
|
|
259
259
|
async function shellFlow(tui) {
|
|
260
260
|
const { listLocal, downloadShell, buildShell } = require('./shell');
|
|
261
261
|
for (;;) {
|
|
262
|
-
const items = ['查看已就绪壳', '
|
|
262
|
+
const items = ['查看已就绪壳', '下载壳(无需 Go)', '本地编译壳(可选·需 Go)', '返回'];
|
|
263
263
|
const idx = await tui.menu('壳管理', items);
|
|
264
264
|
if (idx === null || idx === items.length - 1) return;
|
|
265
265
|
|
|
@@ -277,7 +277,7 @@ async function shellFlow(tui) {
|
|
|
277
277
|
console.log(`${C.fgGreen}[freedom]${C.reset} 已下载 ${ALL_PLATFORMS[p]} 壳:${dest}`);
|
|
278
278
|
});
|
|
279
279
|
} else {
|
|
280
|
-
const p = await tui.menu('
|
|
280
|
+
const p = await tui.menu('选择要编译的壳平台(可选·需 Go,壳已预编译随包分发,一般无需编译)', ALL_PLATFORMS);
|
|
281
281
|
if (p === null) continue;
|
|
282
282
|
await tui.runTask(async () => {
|
|
283
283
|
const dest = buildShell(ALL_PLATFORMS[p]);
|
package/lib/utils.js
CHANGED
|
@@ -42,6 +42,11 @@ function isWinPlat(plat) {
|
|
|
42
42
|
return plat.startsWith('win');
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
// macOS 平台(darwin-*):产物需升级为 .app bundle + .app.zip 供 mac 用户解压即用
|
|
46
|
+
function isMacPlat(plat) {
|
|
47
|
+
return plat.startsWith('darwin');
|
|
48
|
+
}
|
|
49
|
+
|
|
45
50
|
// 平台 key -> 产物可执行文件名(Windows 带 .exe,其余无扩展名)
|
|
46
51
|
function platformExeName(plat, appName) {
|
|
47
52
|
return isWinPlat(plat) ? `${appName}.exe` : appName;
|
|
@@ -106,6 +111,7 @@ module.exports = {
|
|
|
106
111
|
ALL_PLATFORMS,
|
|
107
112
|
SHELL_EXE_NAME,
|
|
108
113
|
isWinPlat,
|
|
114
|
+
isMacPlat,
|
|
109
115
|
platformExeName,
|
|
110
116
|
nativePlatform,
|
|
111
117
|
localShellPath,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yufengtadian/freedom-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.12",
|
|
4
4
|
"description": "Freedom WebView desktop shell packaging tool - no Go toolchain required, one command packs three-platform desktop apps",
|
|
5
5
|
"keywords": ["desktop", "webview", "electron-alternative", "wails", "tauri", "frontend", "cross-platform"],
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,3 +26,4 @@
|
|
|
26
26
|
"node": ">=18"
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
+
|