cj-tauri 1.1.2 → 2.0.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/README.md +64 -18
- package/cjpm.toml +52 -52
- package/cli/cjpm.toml +10 -1
- package/cli/src/doctor.cj +4 -2
- package/cli/src/main.cj +11 -3
- package/cli/src/manifest.cj +558 -0
- package/cli/src/project.cj +38 -22
- package/cli/src/scaffold.cj +369 -361
- package/cli/src/sync.cj +526 -0
- package/cli/src/util.cj +168 -159
- package/cli/templates/app/README.md +48 -80
- package/cli/templates/app/{capabilities → assets/capabilities}/default.json +7 -7
- package/cli/templates/app/{ui → assets/ui}/index.html +118 -118
- package/cli/templates/app/cj-tauri.toml +22 -0
- package/cli/templates/app/cjpm.toml +34 -17
- package/cli/templates/app/src/main.cj +90 -88
- package/cli/templates/app-react/README.md +56 -67
- package/cli/templates/app-react/{capabilities → assets/capabilities}/default.json +7 -7
- package/cli/templates/app-react/{ui → assets/ui}/package.json +19 -19
- package/cli/templates/app-react/{ui → assets/ui}/src/main.jsx +6 -6
- package/cli/templates/app-react/{ui → assets/ui}/vite.config.js +24 -24
- package/cli/templates/app-react/cj-tauri.toml +23 -0
- package/cli/templates/app-react/cjpm.toml +34 -17
- package/cli/templates/app-react/src/main.cj +91 -102
- package/cli/templates/app-vue/README.md +56 -60
- package/cli/templates/app-vue/{capabilities → assets/capabilities}/default.json +7 -7
- package/cli/templates/app-vue/{ui → assets/ui}/package.json +18 -18
- package/cli/templates/app-vue/{ui → assets/ui}/src/main.js +4 -4
- package/cli/templates/app-vue/{ui → assets/ui}/vite.config.js +24 -24
- package/cli/templates/app-vue/cj-tauri.toml +23 -0
- package/cli/templates/app-vue/cjpm.toml +34 -17
- package/cli/templates/app-vue/src/main.cj +91 -102
- package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/app.json5 +13 -13
- package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/resources/base/element/string.json +8 -8
- package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/resources/base/media/layered_image.json +6 -6
- package/cli/templates/{ohos-pc → ohos-skeleton}/README.md +79 -79
- package/cli/templates/{ohos-pc → ohos-skeleton}/build-profile.json5 +46 -46
- package/cli/templates/{ohos-pc → ohos-skeleton}/entry/build-profile.json5 +26 -26
- package/cli/templates/{ohos-pc → ohos-skeleton}/entry/cjpm.toml +43 -43
- package/cli/templates/{ohos-pc → ohos-skeleton}/entry/hvigorfile.ts +6 -6
- package/cli/templates/{ohos-pc → ohos-skeleton}/entry/oh-package.json5 +9 -9
- package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/cangjie/ability_stage.cj +17 -17
- package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/cangjie/app_host.cj +94 -94
- package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/cangjie/index.cj +167 -167
- package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/cangjie/main_ability.cj +139 -139
- package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/module.json5 +49 -49
- package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/element/color.json +8 -8
- package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/element/string.json +16 -16
- package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/media/layered_image.json +6 -6
- package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/profile/main_pages.json +2 -2
- package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/rawfile/capabilities.json +6 -6
- package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/rawfile/index.html +92 -92
- package/cli/templates/{ohos-pc → ohos-skeleton}/hvigor/hvigor-config.json5 +5 -5
- package/cli/templates/{ohos-pc → ohos-skeleton}/hvigorfile.ts +6 -6
- package/cli/templates/{ohos-pc → ohos-skeleton}/oh-package.json5 +10 -10
- package/package.json +4 -2
- package/prebuilt/win32-x64/cj-tauri.exe +0 -0
- package/src/app.cj +49 -20
- package/src/app_spec.cj +76 -0
- package/src/arkweb_script.cj +142 -154
- package/src/host.cj +20 -17
- package/src/host_arkweb.cj +778 -740
- package/src/host_webkit.cj +22 -0
- package/src/host_webview2.cj +22 -0
- package/src/launch.cj +62 -0
- package/src/platform/platform_app_context.cj +77 -0
- package/src/platform/platform_assets.cj +33 -0
- package/src/platform/platform_discover.cj +176 -0
- package/src/platform/platform_env.cj +29 -0
- package/src/platform/platform_feature.cj +31 -0
- package/src/platform/platform_page_source.cj +87 -0
- package/src/platform/platform_target_info.cj +32 -0
- package/src/platform_host_provider.cj +87 -0
- package/src/plugin.cj +36 -2
- package/src/plugins/clipboard/plugin_clipboard.cj +8 -0
- package/src/plugins/menu/plugin_menu.cj +158 -150
- package/src/plugins/notification/plugin_notification.cj +8 -0
- package/src/plugins/serial/plugin_serial.cj +454 -442
- package/src/plugins/serial/serial_bridge.cj +194 -186
- package/src/plugins/serial/serial_ohos_stub.cj +61 -0
- package/src/plugins/shell/plugin_shell.cj +258 -230
- package/src/plugins/sqlite/plugin_sqlite.cj +710 -697
- package/src/plugins/sqlite/sqlite_ffi.cj +211 -206
- package/src/plugins/sqlite/sqlite_ohos_stub.cj +91 -0
- package/src/tests/app_spec_test.cj +127 -0
- package/src/tests/fake_host.cj +23 -8
- package/src/tests/platform_contract_test.cj +251 -0
- package/src/tests/plugin_test.cj +585 -542
- package/src/version.cj +1 -1
- package/src/host_factory.cj +0 -48
- /package/cli/templates/app-react/{ui → assets/ui}/index.html +0 -0
- /package/cli/templates/app-react/{ui → assets/ui}/src/App.css +0 -0
- /package/cli/templates/app-react/{ui → assets/ui}/src/App.jsx +0 -0
- /package/cli/templates/app-vue/{ui → assets/ui}/index.html +0 -0
- /package/cli/templates/app-vue/{ui → assets/ui}/src/App.vue +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/resources/base/media/background.png +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/resources/base/media/foreground.png +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/build-hap.sh +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/media/background.png +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/media/foreground.png +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/media/startIcon.png +0 -0
package/cli/src/scaffold.cj
CHANGED
|
@@ -1,361 +1,369 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* cj-tauri CLI:项目脚手架(create)
|
|
3
|
-
*
|
|
4
|
-
* 模板位于 <框架根>/cli/templates/<模板目录>,占位符形如 {{PROJECT_NAME}};
|
|
5
|
-
* 递归拷贝 +
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
let
|
|
17
|
-
let
|
|
18
|
-
let
|
|
19
|
-
let
|
|
20
|
-
|
|
21
|
-
let
|
|
22
|
-
/**
|
|
23
|
-
let
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
this.
|
|
30
|
-
this.
|
|
31
|
-
this.
|
|
32
|
-
this.
|
|
33
|
-
this.
|
|
34
|
-
this.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
linkOpt = linkOpt + "
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
s =
|
|
55
|
-
s = s + "
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
out =
|
|
64
|
-
out = out.replace("{{
|
|
65
|
-
out = out.replace("{{
|
|
66
|
-
out = out.replace("{{
|
|
67
|
-
out = out.replace("{{
|
|
68
|
-
out = out.replace("{{
|
|
69
|
-
out = out.replace("{{
|
|
70
|
-
out = out.replace("{{
|
|
71
|
-
out = out.replace("{{
|
|
72
|
-
out = out.replace("{{
|
|
73
|
-
out = out.replace("{{
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
f.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
eprintln("
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
eprintln("
|
|
288
|
-
return 1
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
} else
|
|
306
|
-
eprintln("[cj-tauri] 错误:
|
|
307
|
-
eprintln("
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
println("")
|
|
340
|
-
println("
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
}
|
|
345
|
-
println("
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
println("")
|
|
357
|
-
println("
|
|
358
|
-
println("
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
|
|
1
|
+
/**
|
|
2
|
+
* cj-tauri CLI:项目脚手架(create)
|
|
3
|
+
*
|
|
4
|
+
* 模板位于 <框架根>/cli/templates/<模板目录>,占位符形如 {{PROJECT_NAME}};
|
|
5
|
+
* 递归拷贝 + 文本渲染,生成「新形态」工程(cj-tauri.toml 清单 + 业务 static 包 + assets/),
|
|
6
|
+
* 骨架由 cj-tauri sync 派生(RFC-002 §7):桌面 cj-tauri dev,鸿蒙 targets/ohos 的 build-hap.sh。
|
|
7
|
+
* ohos-skeleton 目录是 cj-tauri sync ohos 的 DevEco 骨架源,不是 create 模板。
|
|
8
|
+
*/
|
|
9
|
+
package cli
|
|
10
|
+
|
|
11
|
+
import std.fs.*
|
|
12
|
+
import std.collection.*
|
|
13
|
+
|
|
14
|
+
/** 模板占位符取值 */
|
|
15
|
+
public class TemplateVars {
|
|
16
|
+
let projectName: String
|
|
17
|
+
let packageName: String
|
|
18
|
+
let frameworkRoot: String
|
|
19
|
+
let nativeDir: String
|
|
20
|
+
let stdxDir: String
|
|
21
|
+
let targetSection: String
|
|
22
|
+
/** 鸿蒙应用的安装 ID(bundleName),桌面模板不用 */
|
|
23
|
+
let bundleName: String
|
|
24
|
+
/** 应用显示名(桌面模板用项目名,鸿蒙写进 string.json) */
|
|
25
|
+
let appLabel: String
|
|
26
|
+
|
|
27
|
+
public init(projectName: String, packageName: String, frameworkRoot: String, stdxDir: String,
|
|
28
|
+
bundleName: String, appLabel: String) {
|
|
29
|
+
this.projectName = projectName
|
|
30
|
+
this.packageName = packageName
|
|
31
|
+
this.frameworkRoot = frameworkRoot
|
|
32
|
+
this.nativeDir = joinPath(frameworkRoot, "native")
|
|
33
|
+
this.stdxDir = stdxDir
|
|
34
|
+
this.targetSection = buildTargetSection(joinPath(frameworkRoot, "native"), stdxDir)
|
|
35
|
+
this.bundleName = bundleName
|
|
36
|
+
this.appLabel = appLabel
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** 生成 cjpm.toml 的 [target.<triple>] 段落(含桥链接参数与 stdx 路径) */
|
|
41
|
+
func buildTargetSection(nativeDir: String, stdxDir: String): String {
|
|
42
|
+
let triple = hostTriple()
|
|
43
|
+
var linkOpt = "-L" + escapeTomlString(nativeDir) + " -lcjtbridge"
|
|
44
|
+
if (hostIsWindows()) {
|
|
45
|
+
// GUI 子系统:不写这个开关,产物是 console 程序,双击时会多出一个控制台黑窗口(AGENTS §4)。
|
|
46
|
+
// 取值只能用链接器那套——cjc 把 --link-options 直通 ld.lld,gcc 的 -mwindows 会被拒。
|
|
47
|
+
// 注意别在这儿写「行内 `#` 注释」:Cangjie 里 `#` 是 raw string 前缀、不是注释符,
|
|
48
|
+
// 生成出来的 toml 那行也就只放开关、不放尾注(保证与文档里的行数表格一致)。
|
|
49
|
+
linkOpt = linkOpt + " --subsystem=windows"
|
|
50
|
+
} else {
|
|
51
|
+
linkOpt = linkOpt + " -lwebkit2gtk-4.1 -lgtk-3 -lgdk-3 -lglib-2.0 -lgio-2.0 -lsoup-3.0" +
|
|
52
|
+
" -ljavascriptcoregtk-4.1 -lgobject-2.0"
|
|
53
|
+
}
|
|
54
|
+
var s = "[target." + triple + "]\n"
|
|
55
|
+
s = s + " link-option = \"" + linkOpt + "\"\n"
|
|
56
|
+
s = s + "\n[target." + triple + ".bin-dependencies]\n"
|
|
57
|
+
s = s + " path-option = [\"" + escapeTomlString(stdxDir) + "\"]\n"
|
|
58
|
+
return s
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** 渲染模板文本 */
|
|
62
|
+
func renderTemplate(content: String, v: TemplateVars): String {
|
|
63
|
+
var out = content
|
|
64
|
+
out = out.replace("{{PROJECT_NAME}}", v.projectName)
|
|
65
|
+
out = out.replace("{{PACKAGE_NAME}}", v.packageName)
|
|
66
|
+
out = out.replace("{{CJC_VERSION}}", minCjcVersion())
|
|
67
|
+
out = out.replace("{{FRAMEWORK_ROOT_TOML}}", escapeTomlString(v.frameworkRoot))
|
|
68
|
+
out = out.replace("{{NATIVE_DIR_TOML}}", escapeTomlString(v.nativeDir))
|
|
69
|
+
out = out.replace("{{STDX_DIR_TOML}}", escapeTomlString(v.stdxDir))
|
|
70
|
+
out = out.replace("{{TARGET_SECTION}}", v.targetSection)
|
|
71
|
+
out = out.replace("{{FRAMEWORK_ROOT}}", v.frameworkRoot)
|
|
72
|
+
out = out.replace("{{NATIVE_DIR}}", v.nativeDir)
|
|
73
|
+
out = out.replace("{{STDX_DIR}}", v.stdxDir)
|
|
74
|
+
out = out.replace("{{BUNDLE_NAME}}", v.bundleName)
|
|
75
|
+
out = out.replace("{{APP_LABEL}}", v.appLabel)
|
|
76
|
+
return out
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 模板里哪些文件必须**按字节原样拷**
|
|
81
|
+
*
|
|
82
|
+
* 模板要带图标(`resources/base/media/` 下的 png),而 `readText` 走 UTF-8——二进制过一遍
|
|
83
|
+
* 文本渲染会当场损坏(就算侥幸没崩,png 也变得打不开,要到装到设备上才看得见白方块)。
|
|
84
|
+
* 判据只看扩展名:模板里不需要「文本渲染的二进制」,真需要再另说。
|
|
85
|
+
*/
|
|
86
|
+
func isBinaryTemplateFile(name: String): Bool {
|
|
87
|
+
let b = name.toArray()
|
|
88
|
+
let out = ArrayList<UInt8>()
|
|
89
|
+
for (i in 0..b.size) {
|
|
90
|
+
out.add(toLowerByte(b[i]))
|
|
91
|
+
}
|
|
92
|
+
let lower = String.fromUtf8(out.toArray())
|
|
93
|
+
let exts = ["png", "jpg", "jpeg", "gif", "webp", "ico", "ttf", "otf", "woff", "woff2",
|
|
94
|
+
"so", "dll", "a", "lib", "hap", "zip", "gz", "pdf", "wav", "mp3", "mp4"]
|
|
95
|
+
for (ext in exts) {
|
|
96
|
+
if (lower.endsWith("." + ext)) {
|
|
97
|
+
return true
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return false
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** 项目名 → 仓颉包名(lowerCamelCase):my-app → myApp */
|
|
104
|
+
func packageNameFrom(name: String): Option<String> {
|
|
105
|
+
let b = name.toArray()
|
|
106
|
+
if (b.size == 0) {
|
|
107
|
+
return None
|
|
108
|
+
}
|
|
109
|
+
let out = ArrayList<UInt8>()
|
|
110
|
+
var upperNext = false
|
|
111
|
+
var first = true
|
|
112
|
+
for (i in 0..b.size) {
|
|
113
|
+
let c = b[i]
|
|
114
|
+
if (c == 0x2D || c == 0x5F || c == 0x2E) { // - _ .
|
|
115
|
+
upperNext = true
|
|
116
|
+
continue
|
|
117
|
+
}
|
|
118
|
+
if (!isAsciiAlpha(c) && !isAsciiDigit(c)) {
|
|
119
|
+
return None
|
|
120
|
+
}
|
|
121
|
+
var ch = c
|
|
122
|
+
if (upperNext) {
|
|
123
|
+
ch = toUpperByte(c)
|
|
124
|
+
upperNext = false
|
|
125
|
+
}
|
|
126
|
+
if (first) {
|
|
127
|
+
if (!isAsciiAlpha(ch)) {
|
|
128
|
+
return None
|
|
129
|
+
}
|
|
130
|
+
ch = toLowerByte(ch)
|
|
131
|
+
first = false
|
|
132
|
+
}
|
|
133
|
+
out.add(ch)
|
|
134
|
+
}
|
|
135
|
+
if (out.size == 0 || first) {
|
|
136
|
+
return None
|
|
137
|
+
}
|
|
138
|
+
return Some(String.fromUtf8(out.toArray()))
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* 按字节覆盖写
|
|
143
|
+
*
|
|
144
|
+
* 模板里的图片不能走 `writeText`:`readText` 是 UTF-8 解码,读 png 当场抛
|
|
145
|
+
* `IllegalArgumentException: Invalid unicode scalar value`(create 直接崩、目录留半成品)。
|
|
146
|
+
*/
|
|
147
|
+
func writeBytes(p: String, data: Array<UInt8>): Unit {
|
|
148
|
+
if (exists(p)) {
|
|
149
|
+
remove(p)
|
|
150
|
+
}
|
|
151
|
+
let f = File.create(p)
|
|
152
|
+
f.write(data)
|
|
153
|
+
f.close()
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** 递归拷贝模板目录:目录建目录、文本渲染后写出、二进制原样拷 */
|
|
157
|
+
func copyTree(src: String, dst: String, v: TemplateVars): Int64 {
|
|
158
|
+
var count = 0
|
|
159
|
+
for (info in listDir(src)) {
|
|
160
|
+
let nm = baseName(info.path.toString())
|
|
161
|
+
if (nm.size == 0) {
|
|
162
|
+
continue
|
|
163
|
+
}
|
|
164
|
+
let s = joinPath(src, nm)
|
|
165
|
+
let d = joinPath(dst, nm)
|
|
166
|
+
if (info.isDirectory()) {
|
|
167
|
+
ensureDir(d)
|
|
168
|
+
count = count + copyTree(s, d, v)
|
|
169
|
+
} else if (isBinaryTemplateFile(nm)) {
|
|
170
|
+
writeBytes(d, File.readFrom(s))
|
|
171
|
+
count = count + 1
|
|
172
|
+
} else {
|
|
173
|
+
writeText(d, renderTemplate(readText(s), v))
|
|
174
|
+
count = count + 1
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return count
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** 模板别名 → 仓库里的模板目录名(缺省 app,0.3.x 的老用法不受影响) */
|
|
181
|
+
func templateDirName(tpl: String): Option<String> {
|
|
182
|
+
if (tpl == "app" || tpl == "vanilla") {
|
|
183
|
+
return Some("app") // 内联 HTML 单文件,零 Node 依赖
|
|
184
|
+
}
|
|
185
|
+
if (tpl == "vue") {
|
|
186
|
+
return Some("app-vue") // Vue 3 + Vite:dev 走 HMR,发布产单文件
|
|
187
|
+
}
|
|
188
|
+
if (tpl == "react") {
|
|
189
|
+
return Some("app-react") // React 18 + Vite
|
|
190
|
+
}
|
|
191
|
+
// 鸿蒙生态(PC / 手机 / 平板):新形态是统一工程,模板内容同 app——
|
|
192
|
+
// 清单决定目标端,骨架由 cj-tauri sync ohos 派生;旧 ohos-pc 命名保留兼容
|
|
193
|
+
if (tpl == "ohos" || tpl == "ohos-pc" || tpl == "harmonyos" || tpl == "harmonyos-pc") {
|
|
194
|
+
return Some("app")
|
|
195
|
+
}
|
|
196
|
+
// 内部用:cj-tauri sync ohos 的骨架源(DevEco 工程,不是 create 模板)
|
|
197
|
+
if (tpl == "ohos-skeleton") {
|
|
198
|
+
return Some("ohos-skeleton")
|
|
199
|
+
}
|
|
200
|
+
return None
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** create --template 参数是否鸿蒙生态(下一步提示用;新形态下不能按目录名判) */
|
|
204
|
+
func isOhosTemplate(tpl: String): Bool {
|
|
205
|
+
return tpl == "ohos" || tpl == "ohos-pc" || tpl == "harmonyos" || tpl == "harmonyos-pc"
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* 项目名 → 缺省 bundleName(`com.example.<小写下划线名>`)
|
|
210
|
+
*
|
|
211
|
+
* 鸿蒙要求 bundleName 每一段以字母开头、只含字母数字下划线,且至少三段;
|
|
212
|
+
* 项目名里的 `-` `.` 之类一律折成 `_`,数字开头补 `app` 前缀。用户可以用 `--bundle` 覆盖。
|
|
213
|
+
*/
|
|
214
|
+
func bundleNameFrom(projectName: String): String {
|
|
215
|
+
let b = projectName.toArray()
|
|
216
|
+
let out = ArrayList<UInt8>()
|
|
217
|
+
for (i in 0..b.size) {
|
|
218
|
+
let c = toLowerByte(b[i])
|
|
219
|
+
if (isAsciiAlpha(c) || isAsciiDigit(c)) {
|
|
220
|
+
out.add(c)
|
|
221
|
+
} else {
|
|
222
|
+
out.add(0x5F) // '_'
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
var s = String.fromUtf8(out.toArray())
|
|
226
|
+
if (s.size > 0 && isAsciiDigit(s.toArray()[0])) {
|
|
227
|
+
s = "app" + s
|
|
228
|
+
}
|
|
229
|
+
if (s.size == 0) {
|
|
230
|
+
s = "app"
|
|
231
|
+
}
|
|
232
|
+
return "com.example." + s
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** 解析模板目录:先查别名表,查不到就把参数当模板目录名直接用(方便自己往仓库里加模板) */
|
|
236
|
+
func resolveTemplateDir(fwRoot: String, tpl: String): Option<String> {
|
|
237
|
+
let base = joinPath(joinPath(fwRoot, "cli"), "templates")
|
|
238
|
+
var name = tpl
|
|
239
|
+
if (let Some(n) <- templateDirName(tpl)) {
|
|
240
|
+
name = n
|
|
241
|
+
}
|
|
242
|
+
let dir = joinPath(base, name)
|
|
243
|
+
if (exists(dir)) {
|
|
244
|
+
return Some(dir)
|
|
245
|
+
}
|
|
246
|
+
return None
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** 列出仓库里实际存在的模板目录(拼错误提示用) */
|
|
250
|
+
func availableTemplates(fwRoot: String): String {
|
|
251
|
+
let base = joinPath(joinPath(fwRoot, "cli"), "templates")
|
|
252
|
+
var out = ""
|
|
253
|
+
if (exists(base)) {
|
|
254
|
+
for (info in listDir(base)) {
|
|
255
|
+
if (!info.isDirectory()) {
|
|
256
|
+
continue
|
|
257
|
+
}
|
|
258
|
+
let nm = baseName(info.path.toString())
|
|
259
|
+
if (nm.size == 0) {
|
|
260
|
+
continue
|
|
261
|
+
}
|
|
262
|
+
if (out.size > 0) {
|
|
263
|
+
out = out + " / "
|
|
264
|
+
}
|
|
265
|
+
out = out + nm
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return out
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
func cmdCreate(nameArg: String, tpl: String, bundleArg: String, labelArg: String): Int64 {
|
|
272
|
+
let targetDir = stripTrailingSep(nameArg)
|
|
273
|
+
if (targetDir.size == 0) {
|
|
274
|
+
eprintln("用法: cj-tauri create <项目名> [--template app|vue|react|ohos]")
|
|
275
|
+
return 1
|
|
276
|
+
}
|
|
277
|
+
let projectName = baseName(targetDir)
|
|
278
|
+
var pkg = ""
|
|
279
|
+
if (let Some(p) <- packageNameFrom(projectName)) {
|
|
280
|
+
pkg = p
|
|
281
|
+
} else {
|
|
282
|
+
eprintln("[cj-tauri] 错误: 项目名 '${projectName}' 推导不出合法仓颉包名")
|
|
283
|
+
eprintln(" 要求以字母开头,只含字母/数字/-/_/.(例如 my-app)")
|
|
284
|
+
return 1
|
|
285
|
+
}
|
|
286
|
+
if (exists(targetDir)) {
|
|
287
|
+
eprintln("[cj-tauri] 错误: 目录已存在: ${targetDir}")
|
|
288
|
+
return 1
|
|
289
|
+
}
|
|
290
|
+
var fwRoot = ""
|
|
291
|
+
if (let Some(r) <- resolveFrameworkRoot()) {
|
|
292
|
+
fwRoot = r
|
|
293
|
+
} else {
|
|
294
|
+
eprintln("[cj-tauri] 错误: 找不到 cj-tauri 框架根目录")
|
|
295
|
+
eprintln(" 请设置环境变量 CJ_TAURI_ROOT 指向 cj-tauri 仓库根目录")
|
|
296
|
+
return 1
|
|
297
|
+
}
|
|
298
|
+
// 模板先解析。新形态业务 toml 的 stdx 走 ${CANGJIE_STDX} 环境变量
|
|
299
|
+
// (cjpm 构建期替换),create 不再需要解析 stdx
|
|
300
|
+
var tplDir = ""
|
|
301
|
+
var dirName = ""
|
|
302
|
+
if (let Some(d) <- resolveTemplateDir(fwRoot, tpl)) {
|
|
303
|
+
tplDir = d
|
|
304
|
+
dirName = baseName(d)
|
|
305
|
+
} else {
|
|
306
|
+
eprintln("[cj-tauri] 错误: 未知模板 '${tpl}'")
|
|
307
|
+
eprintln(" 可用模板: ${availableTemplates(fwRoot)}")
|
|
308
|
+
eprintln(" 别名: app(内联单文件,缺省)/ vue / react / ohos")
|
|
309
|
+
return 1
|
|
310
|
+
}
|
|
311
|
+
if (dirName == "ohos-skeleton") {
|
|
312
|
+
eprintln("[cj-tauri] 错误: ohos-skeleton 是 cj-tauri sync ohos 的骨架源,不能用于 create")
|
|
313
|
+
eprintln(" 创建鸿蒙工程请用: cj-tauri create ${nameArg} --template ohos")
|
|
314
|
+
return 1
|
|
315
|
+
}
|
|
316
|
+
let ohos = isOhosTemplate(tpl)
|
|
317
|
+
warnIfSpacey("框架", fwRoot)
|
|
318
|
+
|
|
319
|
+
var bundle = bundleArg
|
|
320
|
+
if (bundle.size == 0) {
|
|
321
|
+
bundle = bundleNameFrom(projectName)
|
|
322
|
+
}
|
|
323
|
+
var label = labelArg
|
|
324
|
+
if (label.size == 0) {
|
|
325
|
+
label = projectName
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
let v = TemplateVars(projectName, pkg, fwRoot, "", bundle, label)
|
|
329
|
+
ensureDir(targetDir)
|
|
330
|
+
let count = copyTree(tplDir, targetDir, v)
|
|
331
|
+
|
|
332
|
+
println("[cj-tauri] 已创建项目 ${targetDir}/(${count} 个文件)")
|
|
333
|
+
println("")
|
|
334
|
+
println(" 项目名 : ${projectName}")
|
|
335
|
+
println(" 模板 : ${tpl}")
|
|
336
|
+
println(" 仓颉包名 : ${pkg}")
|
|
337
|
+
println(" 框架依赖 : ${fwRoot}")
|
|
338
|
+
if (ohos) {
|
|
339
|
+
println(" 应用名 : ${label}")
|
|
340
|
+
println(" bundle : ${bundle}")
|
|
341
|
+
}
|
|
342
|
+
println("")
|
|
343
|
+
println("下一步:")
|
|
344
|
+
println(" cd ${targetDir}")
|
|
345
|
+
println(" cj-tauri sync # 按清单派生 targets/ 骨架(desktop + ohos)")
|
|
346
|
+
if (dirName == "app-vue" || dirName == "app-react") {
|
|
347
|
+
println(" cd assets/ui && npm install && cd ../.. # 前端依赖(需 Node.js)")
|
|
348
|
+
println(" cj-tauri dev # 起 Vite dev server(热更新)+ 编译 + 启动窗口")
|
|
349
|
+
} else {
|
|
350
|
+
println(" cj-tauri dev # 构建 C 桥 + 编译 + 启动窗口")
|
|
351
|
+
}
|
|
352
|
+
if (ohos) {
|
|
353
|
+
println(" 鸿蒙端: export CANGJIE_STDX_OHOS=<鸿蒙版 stdx>/linux_ohos_aarch64_cjnative/dynamic/stdx")
|
|
354
|
+
println(" cd targets/ohos && bash build-hap.sh [--install]")
|
|
355
|
+
println("")
|
|
356
|
+
println(" 提示: 模板不带签名证书——首次在 DevEco 里 File → Project Structure → Signing Configs")
|
|
357
|
+
println(" 勾「Automatically generate signature」(要登录华为账号);不签名只能编出")
|
|
358
|
+
println(" *-unsigned.hap,装不进设备。")
|
|
359
|
+
} else {
|
|
360
|
+
println("")
|
|
361
|
+
println(" 提示: cjpm.toml 里的框架路径是本机绝对路径;换机器需重新执行 create 或手改这一处。")
|
|
362
|
+
}
|
|
363
|
+
if (exists(joinPath(joinPath(joinPath(targetDir, "assets"), "ui"), "package.json"))) {
|
|
364
|
+
println("")
|
|
365
|
+
println(" 提示: 该模板前端用 Vite;首次 cj-tauri dev 会自动 npm install 并起 dev server")
|
|
366
|
+
println(" (页面走 CJ_TAURI_DEV_URL,改 assets/ui/src 下的前端源码不重启应用就能看到效果)。")
|
|
367
|
+
}
|
|
368
|
+
return 0
|
|
369
|
+
}
|