cj-tauri 0.0.0-stage → 0.8.0
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 +21 -0
- package/README.md +68 -3
- package/bin/cj-tauri.js +287 -0
- package/cjpm.toml +25 -0
- package/cli/cjpm.toml +13 -0
- package/cli/src/main.cj +95 -0
- package/cli/src/project.cj +696 -0
- package/cli/src/resolve.cj +304 -0
- package/cli/src/scaffold.cj +244 -0
- package/cli/src/util.cj +159 -0
- package/cli/templates/app/README.md +80 -0
- package/cli/templates/app/capabilities/default.json +7 -0
- package/cli/templates/app/cjpm.toml +17 -0
- package/cli/templates/app/src/main.cj +88 -0
- package/cli/templates/app/ui/index.html +118 -0
- package/cli/templates/app-react/README.md +67 -0
- package/cli/templates/app-react/capabilities/default.json +7 -0
- package/cli/templates/app-react/cjpm.toml +17 -0
- package/cli/templates/app-react/src/main.cj +102 -0
- package/cli/templates/app-react/ui/index.html +25 -0
- package/cli/templates/app-react/ui/package.json +19 -0
- package/cli/templates/app-react/ui/src/App.css +67 -0
- package/cli/templates/app-react/ui/src/App.jsx +124 -0
- package/cli/templates/app-react/ui/src/main.jsx +6 -0
- package/cli/templates/app-react/ui/vite.config.js +24 -0
- package/cli/templates/app-vue/README.md +60 -0
- package/cli/templates/app-vue/capabilities/default.json +7 -0
- package/cli/templates/app-vue/cjpm.toml +17 -0
- package/cli/templates/app-vue/src/main.cj +102 -0
- package/cli/templates/app-vue/ui/index.html +25 -0
- package/cli/templates/app-vue/ui/package.json +18 -0
- package/cli/templates/app-vue/ui/src/App.vue +176 -0
- package/cli/templates/app-vue/ui/src/main.js +4 -0
- package/cli/templates/app-vue/ui/vite.config.js +24 -0
- package/native/bridge_core.c +797 -0
- package/native/bridge_core.h +378 -0
- package/native/bridge_js.h +108 -0
- package/native/bridge_linux.c +1268 -0
- package/native/bridge_win.c +1633 -0
- package/native/build_linux.sh +51 -0
- package/native/build_win.bat +61 -0
- package/native/test_host_win.c +60 -0
- package/native/tests/gtk_threading_probe.c +143 -0
- package/native/tests/test_bridge_core.c +701 -0
- package/package.json +42 -6
- package/prebuilt/win32-x64/cj-tauri.exe +0 -0
- package/src/api_system.cj +59 -0
- package/src/app.cj +777 -0
- package/src/capability.cj +269 -0
- package/src/capability_loader.cj +76 -0
- package/src/host.cj +433 -0
- package/src/host_webkit.cj +372 -0
- package/src/host_webview2.cj +375 -0
- package/src/ipc_hub.cj +288 -0
- package/src/ipc_message.cj +285 -0
- package/src/menu.cj +196 -0
- package/src/plugin.cj +259 -0
- package/src/plugin_dialog.cj +152 -0
- package/src/plugin_fs.cj +118 -0
- package/src/plugin_menu.cj +148 -0
- package/src/plugin_serial.cj +489 -0
- package/src/plugin_shell.cj +228 -0
- package/src/serial_bridge.cj +185 -0
- package/src/shell_event.cj +68 -0
- package/src/tests/capability_test.cj +190 -0
- package/src/tests/fake_host.cj +121 -0
- package/src/tests/host_route_test.cj +108 -0
- package/src/tests/ipc_hub_test.cj +474 -0
- package/src/tests/menu_test.cj +108 -0
- package/src/tests/plugin_dialog_test.cj +143 -0
- package/src/tests/plugin_menu_test.cj +97 -0
- package/src/tests/plugin_serial_test.cj +267 -0
- package/src/tests/plugin_shell_test.cj +188 -0
- package/src/tests/plugin_test.cj +524 -0
- package/src/tests/shell_event_test.cj +68 -0
- package/src/tests/system_host_test.cj +79 -0
- package/src/tests/version_test.cj +65 -0
- package/src/tests/window_registry_test.cj +123 -0
- package/src/version.cj +23 -0
- package/src/window.cj +42 -0
- package/src/window_registry.cj +116 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 yangyongzhen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,68 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# cj-tauri
|
|
2
|
+
|
|
3
|
+
用[仓颉语言](https://cangjie-lang.cn)写桌面应用的**类 Tauri 框架**脚手架 CLI:
|
|
4
|
+
仓颉后端(静态编译)+ 系统 WebView 前端(HTML/CSS/JS),三件套对标 Tauri ——
|
|
5
|
+
WebView 宿主、IPC 双向桥(`invoke` / `resolve` / `event`)、能力安全模型(capability 白名单)。
|
|
6
|
+
|
|
7
|
+
- 仓库与完整文档:<https://atomgit.com/qq8864/cj-tauri>
|
|
8
|
+
- 当前状态:**Windows(WebView2)与 Linux(WebKitGTK)均已实机跑通**;鸿蒙 ArkWeb 为架构预留位
|
|
9
|
+
|
|
10
|
+
## 前置条件:仓颉 SDK
|
|
11
|
+
|
|
12
|
+
**这个包不替代仓颉 SDK**。应用后端就是仓颉代码,`cj-tauri dev` / `build` 要调用 SDK 里的 `cjc` / `cjpm`
|
|
13
|
+
把你的应用编译出来。所以请先装 SDK(验证版本 **1.2.0**,含 **stdx**),并让本包能找到它:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Linux / macOS
|
|
17
|
+
export CANGJIE_HOME=/opt/cangjie/cangjie
|
|
18
|
+
|
|
19
|
+
# Windows cmd
|
|
20
|
+
set CANGJIE_HOME=D:\Program Files (x86)\Cangjie
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
不设 `CANGJIE_HOME` 时,本包会探测常见安装位置(Windows `D:\Program Files (x86)\Cangjie`、
|
|
24
|
+
Linux `/opt/cangjie/cangjie`)。装好后自检:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx cj-tauri info # 打印框架 / 项目 / stdx / SDK / cjpm / C 桥 的解析结果
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
没装 SDK 时本包会直接给出提示与安装说明,而不是抛一句 `cjpm: command not found`。
|
|
31
|
+
另外 `npx cj-tauri --version` 不需要 SDK 也能查版本。
|
|
32
|
+
|
|
33
|
+
## 用法
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx cj-tauri create myapp # 创建项目(默认模板:内联 HTML,零 Node)
|
|
37
|
+
cd myapp
|
|
38
|
+
npx cj-tauri dev # 构建 C 桥 + cjpm build + 启动窗口
|
|
39
|
+
|
|
40
|
+
npx cj-tauri create myapp-vue --template vue # Vue 3 + Vite
|
|
41
|
+
npx cj-tauri create myapp-react --template react # React 18 + Vite
|
|
42
|
+
|
|
43
|
+
npm i -g cj-tauri # 也可以全局安装,之后直接用 cj-tauri <子命令>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
| 子命令 | 作用 |
|
|
47
|
+
|---|---|
|
|
48
|
+
| `create <项目名> [--template app\|vue\|react]` | 生成工程(`src/` + `ui/` + `capabilities/` + `cjpm.toml`) |
|
|
49
|
+
| `dev` | 构建 C 桥 + `cjpm build` + 启动应用;Vite 模板会接管 dev server(HMR) |
|
|
50
|
+
| `build` | 构建 C 桥 + `cjpm build` |
|
|
51
|
+
| `run` | 运行已构建产物 |
|
|
52
|
+
| `info` | 环境自检(排障先跑它) |
|
|
53
|
+
| `help` | 帮助 |
|
|
54
|
+
|
|
55
|
+
## 这个包装层做了什么
|
|
56
|
+
|
|
57
|
+
- **跨平台入口**:Windows / Linux / Git Bash 都是同一条 `cj-tauri` 命令,不再分 `.bat` 与 `.sh`;
|
|
58
|
+
- **环境自检**:`cjpm` 与 SDK 运行时库的定位、`PATH` / `LD_LIBRARY_PATH` 的拼装都在这里完成;
|
|
59
|
+
- **CLI 本体的获取**:优先用包内预编译二进制(`prebuilt/<平台-架构>/`),没有则把 CLI 源码拷到
|
|
60
|
+
`~/.cache/cj-tauri/<版本>/` 下用本机 `cjpm build` 一次并缓存 —— 不往安装目录写产物,避免全局安装只读;
|
|
61
|
+
- **数据目录**:Linux/macOS 用 `$XDG_CACHE_HOME/cj-tauri`(默认 `~/.cache/cj-tauri`),Windows 用 `%LOCALAPPDATA%\cj-tauri`。
|
|
62
|
+
|
|
63
|
+
已知限制:首次 `dev` / `build` 会把 C 桥构建到**框架目录的 `native/`** 下(应用 `cjpm.toml` 的
|
|
64
|
+
链接参数指向那里),所以包安装目录需要可写;如果装在只读路径下,请改用仓库内运行方式。
|
|
65
|
+
|
|
66
|
+
## License
|
|
67
|
+
|
|
68
|
+
[MIT](https://atomgit.com/qq8864/cj-tauri/blob/main/LICENSE) © 2026 yangyongzhen
|
package/bin/cj-tauri.js
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* cj-tauri npm 包启动器(跨平台:Windows / Linux,Git Bash 亦可)
|
|
4
|
+
*
|
|
5
|
+
* 职责:把「仓颉环境自检 + 平台差异 + CLI 本体的构建与缓存 + 退出码传递」收进一个入口,
|
|
6
|
+
* 让 npm 安装后的用法与仓库内一致:
|
|
7
|
+
*
|
|
8
|
+
* npx cj-tauri create myapp
|
|
9
|
+
* npx cj-tauri dev
|
|
10
|
+
*
|
|
11
|
+
* 与仓库内 cli/cj-tauri.sh、cli/cj-tauri.bat 的关系:那两个是**开发用**启动器(假定在仓库里跑),
|
|
12
|
+
* 本文件是**发布用**入口(在任意目录跑)。PATH / LD_LIBRARY_PATH 的拼装规则与它们保持一致,
|
|
13
|
+
* 改动其中一处请三处一起改。
|
|
14
|
+
*
|
|
15
|
+
* CLI 可执行文件的解析顺序(对应「优先预编译、缺失回落源码构建」的打包策略):
|
|
16
|
+
* 1. 包内预编译 <pkg>/prebuilt/<平台-架构>/cj-tauri[.exe] —— 打 tarball 时若本机有产物就带上
|
|
17
|
+
* 2. 本机缓存 <缓存>/<版本>/<平台-架构>/cj-tauri[.exe] —— 首次构建后复用
|
|
18
|
+
* 3. 源码构建 把 <pkg>/cli 拷到缓存目录再 `cjpm build`;不往安装目录写产物,
|
|
19
|
+
* 因为全局安装的包目录可能是只读的
|
|
20
|
+
*
|
|
21
|
+
* 仓颉 SDK(cjc + cjpm + stdx)是硬前置:应用的**后端**就是仓颉代码,没 SDK 连编译都做不了。
|
|
22
|
+
* 缺 SDK 时这里给出可操作的提示,而不是让 cjpm 报 "command not found"。
|
|
23
|
+
*/
|
|
24
|
+
'use strict';
|
|
25
|
+
|
|
26
|
+
const fs = require('fs');
|
|
27
|
+
const os = require('os');
|
|
28
|
+
const path = require('path');
|
|
29
|
+
const { spawnSync } = require('child_process');
|
|
30
|
+
|
|
31
|
+
const PKG_ROOT = path.resolve(__dirname, '..');
|
|
32
|
+
const PKG = JSON.parse(fs.readFileSync(path.join(PKG_ROOT, 'package.json'), 'utf8'));
|
|
33
|
+
const VERSION = PKG.version;
|
|
34
|
+
const IS_WIN = process.platform === 'win32';
|
|
35
|
+
const PLATFORM_KEY = `${process.platform}-${process.arch}`;
|
|
36
|
+
const CLI_FILE = IS_WIN ? 'cj-tauri.exe' : 'cj-tauri';
|
|
37
|
+
/** 构建产物在 cjpm 的默认输出位置,包内 CLI 与本机构建时同名 */
|
|
38
|
+
const BUILT_CLI_FILE = IS_WIN ? 'main.exe' : 'main';
|
|
39
|
+
|
|
40
|
+
/** 诊断输出走 stderr:与仓库内启动器一致,不污染子命令的 stdout(stdout 要能被脚本消费) */
|
|
41
|
+
function note(msg) {
|
|
42
|
+
process.stderr.write(`[cj-tauri] ${msg}\n`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** 只有带上 CJ_TAURI_VERBOSE=1 才打印「用了哪份 CLI」这类细节(默认安静,排障时可开) */
|
|
46
|
+
function verbose(msg) {
|
|
47
|
+
if (process.env.CJ_TAURI_VERBOSE) note(msg);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** 取第一个存在的路径 */
|
|
51
|
+
function firstExisting(candidates) {
|
|
52
|
+
for (const p of candidates) {
|
|
53
|
+
if (p && fs.existsSync(p)) {
|
|
54
|
+
return p;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 解析仓颉 SDK 根目录:CANGJIE_HOME 优先,其次按平台探常见安装位置。
|
|
62
|
+
* 与 cli/src/resolve.cj 的取向一致(那边还能从 PATH 里的 cjc 反推,这里只做静态探测)。
|
|
63
|
+
*/
|
|
64
|
+
function resolveCangjieHome() {
|
|
65
|
+
const env = process.env.CANGJIE_HOME;
|
|
66
|
+
if (env && !fs.existsSync(env)) {
|
|
67
|
+
note(`告警: CANGJIE_HOME=${env} 目录不存在,继续按默认路径探测`);
|
|
68
|
+
}
|
|
69
|
+
const defaults = IS_WIN
|
|
70
|
+
? ['D:\\Program Files (x86)\\Cangjie', 'C:\\Cangjie']
|
|
71
|
+
: ['/opt/cangjie/cangjie', path.join(os.homedir(), 'cangjie')];
|
|
72
|
+
return firstExisting([env, ...defaults]);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 本平台运行时库目录。
|
|
77
|
+
* SDK 里可能同时装了多个平台的运行时(linux* 与 windows* 并存),必须按宿主平台挑,
|
|
78
|
+
* 挑错会变成「找不到 libcangjie-runtime.so / 找不到 DLL」。
|
|
79
|
+
* (macOS 与鸿蒙尚未实机验证,这里只做前缀匹配,不额外发明平台标识。)
|
|
80
|
+
*/
|
|
81
|
+
function resolveRuntimeDir(home) {
|
|
82
|
+
const base = path.join(home, 'runtime', 'lib');
|
|
83
|
+
if (!fs.existsSync(base)) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
const prefix = IS_WIN ? 'windows' : (process.platform === 'darwin' ? 'darwin' : 'linux');
|
|
87
|
+
const hit = fs.readdirSync(base).find((n) => n.toLowerCase().startsWith(prefix));
|
|
88
|
+
return hit ? path.join(base, hit) : null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** 找 cjpm:SDK 的 tools/bin 优先,其次 PATH(与两个开发启动器的顺序一致) */
|
|
92
|
+
function resolveCjpm(home) {
|
|
93
|
+
const exe = IS_WIN ? 'cjpm.exe' : 'cjpm';
|
|
94
|
+
const inSdk = path.join(home, 'tools', 'bin', exe);
|
|
95
|
+
if (fs.existsSync(inSdk)) {
|
|
96
|
+
return inSdk;
|
|
97
|
+
}
|
|
98
|
+
for (const dir of (process.env.PATH || '').split(path.delimiter)) {
|
|
99
|
+
if (!dir) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const p = path.join(dir, exe);
|
|
103
|
+
if (fs.existsSync(p)) {
|
|
104
|
+
return p;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* 拼装子进程环境。
|
|
112
|
+
* PATH = 运行时库目录 + bin + tools/bin + tools/lib + ~/.cjpm/bin + 原 PATH
|
|
113
|
+
* LD_LIBRARY_PATH = 运行时库目录 + third_party/llvm/lib(仅非 Windows)
|
|
114
|
+
* Linux 上动态库不走 PATH,少了 LD_LIBRARY_PATH,CLI 会直接因 libcangjie-runtime.so 起不来。
|
|
115
|
+
* CJ_TAURI_ROOT 指向包根(包就是一棵「框架根」:含 cjpm.toml 与 cli/templates),
|
|
116
|
+
* CLI 靠它定位模板与框架源码;用户已显式设置时不覆盖。
|
|
117
|
+
*/
|
|
118
|
+
function buildEnv(home, runtimeDir) {
|
|
119
|
+
const env = Object.assign({}, process.env);
|
|
120
|
+
const parts = [];
|
|
121
|
+
if (runtimeDir) {
|
|
122
|
+
parts.push(runtimeDir);
|
|
123
|
+
}
|
|
124
|
+
parts.push(path.join(home, 'bin'), path.join(home, 'tools', 'bin'), path.join(home, 'tools', 'lib'));
|
|
125
|
+
parts.push(path.join(os.homedir(), '.cjpm', 'bin'));
|
|
126
|
+
env.PATH = parts.join(path.delimiter) + path.delimiter + (env.PATH || '');
|
|
127
|
+
|
|
128
|
+
if (!IS_WIN) {
|
|
129
|
+
const ld = [];
|
|
130
|
+
if (runtimeDir) {
|
|
131
|
+
ld.push(runtimeDir);
|
|
132
|
+
}
|
|
133
|
+
ld.push(path.join(home, 'third_party', 'llvm', 'lib'));
|
|
134
|
+
if (env.LD_LIBRARY_PATH) {
|
|
135
|
+
ld.push(env.LD_LIBRARY_PATH);
|
|
136
|
+
}
|
|
137
|
+
env.LD_LIBRARY_PATH = ld.join(':');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (!env.CJ_TAURI_ROOT) {
|
|
141
|
+
env.CJ_TAURI_ROOT = PKG_ROOT;
|
|
142
|
+
}
|
|
143
|
+
return env;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** 缓存根:Linux/macOS 走 XDG 惯例,Windows 走 LOCALAPPDATA */
|
|
147
|
+
function cacheRoot() {
|
|
148
|
+
if (IS_WIN) {
|
|
149
|
+
const base = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
|
|
150
|
+
return path.join(base, 'cj-tauri');
|
|
151
|
+
}
|
|
152
|
+
const base = process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache');
|
|
153
|
+
return path.join(base, 'cj-tauri');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** 缺 SDK 时的提示:给可操作的下一步,而不是把 cjpm 的 not found 甩给用户 */
|
|
157
|
+
function printMissingSdkHint(reason) {
|
|
158
|
+
const lines = [
|
|
159
|
+
`错误: 找不到仓颉 SDK(cjc / cjpm)${reason}`,
|
|
160
|
+
'cj-tauri 要把应用后端编译成仓颉可执行文件,所以必须先装仓颉 SDK(含 stdx)。',
|
|
161
|
+
'请安装 SDK 1.2.0 以上版本,然后把 CANGJIE_HOME 指向 SDK 根目录:',
|
|
162
|
+
IS_WIN
|
|
163
|
+
? ' Windows: set CANGJIE_HOME=D:\\Program Files (x86)\\Cangjie'
|
|
164
|
+
: ' Linux : export CANGJIE_HOME=/opt/cangjie/cangjie',
|
|
165
|
+
'安装说明: https://atomgit.com/qq8864/cj-tauri/blob/main/docs/使用文档.md',
|
|
166
|
+
'设好后自检: cj-tauri info',
|
|
167
|
+
'Cangjie SDK not found (cjc/cjpm). Set CANGJIE_HOME to your SDK root, then run: cj-tauri info',
|
|
168
|
+
];
|
|
169
|
+
for (const line of lines) {
|
|
170
|
+
note(line);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** 包内预编译二进制(打包机有 CLI 产物时才会存在) */
|
|
175
|
+
function prebuiltCliPath() {
|
|
176
|
+
return path.join(PKG_ROOT, 'prebuilt', PLATFORM_KEY, CLI_FILE);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** 本机缓存里的 CLI(源码构建后的产物落在这里,供后续复用) */
|
|
180
|
+
function cachedCliPath() {
|
|
181
|
+
return path.join(cacheRoot(), VERSION, PLATFORM_KEY, CLI_FILE);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** 递归拷贝,跳过产物目录(target 里有几 MB 编译中间物,且不该带进缓存) */
|
|
185
|
+
function copyDir(from, to, skipNames) {
|
|
186
|
+
fs.mkdirSync(to, { recursive: true });
|
|
187
|
+
for (const entry of fs.readdirSync(from, { withFileTypes: true })) {
|
|
188
|
+
if (skipNames.includes(entry.name)) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
const src = path.join(from, entry.name);
|
|
192
|
+
const dst = path.join(to, entry.name);
|
|
193
|
+
if (entry.isDirectory()) {
|
|
194
|
+
copyDir(src, dst, skipNames);
|
|
195
|
+
} else {
|
|
196
|
+
fs.copyFileSync(src, dst);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* 用本机 cjpm 构建 CLI 本体,产物落缓存。
|
|
203
|
+
* 刻意把源码拷到缓存目录再构建:`cjpm build` 会写 target/,
|
|
204
|
+
* 而全局安装的包目录可能是只读的(系统 node_modules 尤其如此)。
|
|
205
|
+
*/
|
|
206
|
+
function buildCli(env, cjpm) {
|
|
207
|
+
const buildDir = path.join(cacheRoot(), VERSION, 'build', 'cli');
|
|
208
|
+
note(`首次运行:正在构建 CLI 本体(${cjpm} build)...`);
|
|
209
|
+
fs.rmSync(buildDir, { recursive: true, force: true });
|
|
210
|
+
copyDir(path.join(PKG_ROOT, 'cli'), buildDir, ['target']);
|
|
211
|
+
|
|
212
|
+
const r = spawnSync(cjpm, ['build'], { cwd: buildDir, env, stdio: 'inherit' });
|
|
213
|
+
const produced = path.join(buildDir, 'target', 'release', 'bin', BUILT_CLI_FILE);
|
|
214
|
+
if (r.status !== 0 || !fs.existsSync(produced)) {
|
|
215
|
+
note(`错误: CLI 本体构建失败(cjpm 退出码 ${r.status === null ? 'null' : r.status})`);
|
|
216
|
+
note(` 可手动重试: cd "${buildDir}" && cjpm build`);
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const dst = cachedCliPath();
|
|
221
|
+
fs.mkdirSync(path.dirname(dst), { recursive: true });
|
|
222
|
+
fs.copyFileSync(produced, dst);
|
|
223
|
+
if (!IS_WIN) {
|
|
224
|
+
fs.chmodSync(dst, 0o755);
|
|
225
|
+
}
|
|
226
|
+
note(`CLI 本体已缓存: ${dst}`);
|
|
227
|
+
return dst;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** 解析要执行的 CLI:预编译 → 缓存 → 现场构建 */
|
|
231
|
+
function resolveCli(env, cjpm) {
|
|
232
|
+
const prebuilt = prebuiltCliPath();
|
|
233
|
+
if (fs.existsSync(prebuilt)) {
|
|
234
|
+
verbose(`使用包内预编译 CLI: ${prebuilt}`);
|
|
235
|
+
return prebuilt;
|
|
236
|
+
}
|
|
237
|
+
const cached = cachedCliPath();
|
|
238
|
+
if (fs.existsSync(cached)) {
|
|
239
|
+
verbose(`使用缓存 CLI: ${cached}`);
|
|
240
|
+
return cached;
|
|
241
|
+
}
|
|
242
|
+
return buildCli(env, cjpm);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function main() {
|
|
246
|
+
const argv = process.argv.slice(2);
|
|
247
|
+
const home = resolveCangjieHome();
|
|
248
|
+
const cjpm = home ? resolveCjpm(home) : null;
|
|
249
|
+
|
|
250
|
+
if (!home || !cjpm) {
|
|
251
|
+
// --version 不需要 SDK:版本直接读 package.json(与 CLI 的 cliVersion() 同源,
|
|
252
|
+
// scripts/check-version.sh 会校验两者一致),让没装 SDK 的人也能查版本号。
|
|
253
|
+
if (['--version', '-V', '-v'].includes(argv[0])) {
|
|
254
|
+
process.stdout.write(`cj-tauri ${VERSION}\n`);
|
|
255
|
+
return 0;
|
|
256
|
+
}
|
|
257
|
+
printMissingSdkHint(home ? '(SDK 里没有 tools/bin/cjpm)' : '(CANGJIE_HOME 未设置或指向的目录不存在)');
|
|
258
|
+
return 1;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const runtimeDir = resolveRuntimeDir(home);
|
|
262
|
+
if (!runtimeDir) {
|
|
263
|
+
note(`告警: 在 ${path.join(home, 'runtime', 'lib')} 下没找到本平台运行时库目录`);
|
|
264
|
+
}
|
|
265
|
+
const env = buildEnv(home, runtimeDir);
|
|
266
|
+
const cli = resolveCli(env, cjpm);
|
|
267
|
+
if (!cli) {
|
|
268
|
+
return 1;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// stdio 全继承:CLI 的进度(含 dev 的桥/构建日志)与窗口输出直达终端。
|
|
272
|
+
// 不 detach,Ctrl-C 时终端把 SIGINT 发给**整个前台进程组**,本进程与 CLI 同时收到:
|
|
273
|
+
// CLI 自己会收掉 dev server 与子进程,这里只需如实回传状态码。
|
|
274
|
+
const r = spawnSync(cli, argv, { stdio: 'inherit', env });
|
|
275
|
+
if (r.error) {
|
|
276
|
+
note(`错误: 启动 CLI 失败: ${r.error.message}`);
|
|
277
|
+
return 1;
|
|
278
|
+
}
|
|
279
|
+
if (r.signal) {
|
|
280
|
+
// 按 shell 惯例回 128+n,让脚本能区分「被打断」与「正常失败」
|
|
281
|
+
return 128 + (os.constants.signals[r.signal] || 0);
|
|
282
|
+
}
|
|
283
|
+
return r.status === null ? 1 : r.status;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// 用 exitCode 而不是 process.exit():后者可能截断还没 flush 的 stdout 缓冲
|
|
287
|
+
process.exitCode = main();
|
package/cjpm.toml
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
cjc-version = "1.0.5"
|
|
3
|
+
name = "cjTauri"
|
|
4
|
+
description = "仓颉版 Tauri:轻量跨端混合开发框架(WebView 宿主 + IPC 桥 + 能力安全模型)"
|
|
5
|
+
version = "0.8.0"
|
|
6
|
+
target-dir = ""
|
|
7
|
+
output-type = "static"
|
|
8
|
+
compile-option = ""
|
|
9
|
+
override-compile-option = ""
|
|
10
|
+
link-option = ""
|
|
11
|
+
package-configuration = {}
|
|
12
|
+
|
|
13
|
+
[dependencies]
|
|
14
|
+
|
|
15
|
+
[target.x86_64-unknown-linux-gnu]
|
|
16
|
+
link-option = "-Lnative -lcjtbridge -lwebkit2gtk-4.1 -lgtk-3 -lgdk-3 -lglib-2.0 -lgio-2.0 -lsoup-3.0 -ljavascriptcoregtk-4.1 -lgobject-2.0"
|
|
17
|
+
|
|
18
|
+
[target.x86_64-unknown-linux-gnu.bin-dependencies]
|
|
19
|
+
path-option = ["/root/.cangjie/stdx/cangjie-stdx-linux-x64-1.0.5.1/dynamic/stdx"]
|
|
20
|
+
|
|
21
|
+
[target.x86_64-w64-mingw32]
|
|
22
|
+
link-option = "-Lnative -lcjtbridge"
|
|
23
|
+
|
|
24
|
+
[target.x86_64-w64-mingw32.bin-dependencies]
|
|
25
|
+
path-option = ["D:\\cangjie-stdx\\windows_x86_64_cjnative\\dynamic\\stdx"]
|
package/cli/cjpm.toml
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
cjc-version = "1.0.5"
|
|
3
|
+
name = "cli"
|
|
4
|
+
description = "cj-tauri CLI:脚手架 create / dev / build / run / info"
|
|
5
|
+
version = "0.8.0"
|
|
6
|
+
target-dir = ""
|
|
7
|
+
output-type = "executable"
|
|
8
|
+
compile-option = ""
|
|
9
|
+
override-compile-option = ""
|
|
10
|
+
link-option = ""
|
|
11
|
+
package-configuration = {}
|
|
12
|
+
|
|
13
|
+
[dependencies]
|
package/cli/src/main.cj
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cj-tauri CLI 入口:create / dev / build / run / info / help
|
|
3
|
+
*/
|
|
4
|
+
package cli
|
|
5
|
+
|
|
6
|
+
func usage(): Unit {
|
|
7
|
+
println("cj-tauri ${cliVersion()} — 仓颉版 Tauri CLI(脚手架 + 构建 + 运行)")
|
|
8
|
+
println("")
|
|
9
|
+
println("用法:")
|
|
10
|
+
println(" cj-tauri create <项目名> [--template <t>]")
|
|
11
|
+
println(" 创建新项目(src/ + ui/ + capabilities/ + cjpm.toml)")
|
|
12
|
+
println(" 模板: app(内联单文件,缺省)/ vue / react")
|
|
13
|
+
println(" cj-tauri dev 构建 C 桥 + cjpm build + 启动应用")
|
|
14
|
+
println(" cj-tauri build 构建 C 桥 + cjpm build")
|
|
15
|
+
println(" cj-tauri run 运行已构建产物")
|
|
16
|
+
println(" cj-tauri info [--json] 环境自检(排障用);--json 只输出插件清单(机器可读)")
|
|
17
|
+
println(" cj-tauri help 显示本帮助")
|
|
18
|
+
println("")
|
|
19
|
+
println("环境变量:")
|
|
20
|
+
println(" CJ_TAURI_ROOT 框架仓库根目录(不在仓库内运行时必须设置)")
|
|
21
|
+
println(" CANGJIE_HOME 仓颉 SDK 根目录(默认从 PATH 中的 cjc 反推)")
|
|
22
|
+
println(" CANGJIE_STDX stdx 动态库目录(默认从框架/项目 cjpm.toml 读取)")
|
|
23
|
+
println(" CJ_TAURI_MANIFEST")
|
|
24
|
+
println(" 仅由 cj-tauri info 设置(json / text):应用装配完就打印清单并退出,")
|
|
25
|
+
println(" 不创建窗口——无显示环境也能取到真实的插件装配结果")
|
|
26
|
+
println("")
|
|
27
|
+
println("示例:")
|
|
28
|
+
println(" cd /tmp && cj-tauri create myapp && cd myapp && cj-tauri dev")
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
main(args: Array<String>): Int64 {
|
|
32
|
+
if (args.size == 0) {
|
|
33
|
+
usage()
|
|
34
|
+
return 1
|
|
35
|
+
}
|
|
36
|
+
let sub = args[0]
|
|
37
|
+
if (sub == "help" || sub == "-h" || sub == "--help") {
|
|
38
|
+
usage()
|
|
39
|
+
return 0
|
|
40
|
+
}
|
|
41
|
+
if (sub == "--version" || sub == "-V" || sub == "-v") {
|
|
42
|
+
println("cj-tauri ${cliVersion()}")
|
|
43
|
+
return 0
|
|
44
|
+
}
|
|
45
|
+
if (sub == "create") {
|
|
46
|
+
var name = ""
|
|
47
|
+
var tpl = "app"
|
|
48
|
+
var i = 1
|
|
49
|
+
while (i < args.size) {
|
|
50
|
+
let a = args[i]
|
|
51
|
+
if (a == "--template" || a == "-t") {
|
|
52
|
+
if (i + 1 >= args.size) {
|
|
53
|
+
eprintln("用法: cj-tauri create <项目名> [--template app|vue|react]")
|
|
54
|
+
return 1
|
|
55
|
+
}
|
|
56
|
+
tpl = args[i + 1]
|
|
57
|
+
i = i + 2
|
|
58
|
+
continue
|
|
59
|
+
}
|
|
60
|
+
if (name.size == 0) {
|
|
61
|
+
name = a
|
|
62
|
+
}
|
|
63
|
+
i = i + 1
|
|
64
|
+
}
|
|
65
|
+
if (name.size == 0) {
|
|
66
|
+
eprintln("用法: cj-tauri create <项目名> [--template app|vue|react]")
|
|
67
|
+
return 1
|
|
68
|
+
}
|
|
69
|
+
return cmdCreate(name, tpl)
|
|
70
|
+
}
|
|
71
|
+
if (sub == "dev") {
|
|
72
|
+
return cmdDev()
|
|
73
|
+
}
|
|
74
|
+
if (sub == "build") {
|
|
75
|
+
return cmdBuild()
|
|
76
|
+
}
|
|
77
|
+
if (sub == "run") {
|
|
78
|
+
return cmdRun()
|
|
79
|
+
}
|
|
80
|
+
if (sub == "info") {
|
|
81
|
+
var asJson = false
|
|
82
|
+
var i = 1
|
|
83
|
+
while (i < args.size) {
|
|
84
|
+
if (args[i] == "--json") {
|
|
85
|
+
asJson = true
|
|
86
|
+
}
|
|
87
|
+
i = i + 1
|
|
88
|
+
}
|
|
89
|
+
return cmdInfo(asJson)
|
|
90
|
+
}
|
|
91
|
+
eprintln("未知子命令: ${sub}")
|
|
92
|
+
println("")
|
|
93
|
+
usage()
|
|
94
|
+
return 1
|
|
95
|
+
}
|