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.
Files changed (101) hide show
  1. package/README.md +64 -18
  2. package/cjpm.toml +52 -52
  3. package/cli/cjpm.toml +10 -1
  4. package/cli/src/doctor.cj +4 -2
  5. package/cli/src/main.cj +11 -3
  6. package/cli/src/manifest.cj +558 -0
  7. package/cli/src/project.cj +38 -22
  8. package/cli/src/scaffold.cj +369 -361
  9. package/cli/src/sync.cj +526 -0
  10. package/cli/src/util.cj +168 -159
  11. package/cli/templates/app/README.md +48 -80
  12. package/cli/templates/app/{capabilities → assets/capabilities}/default.json +7 -7
  13. package/cli/templates/app/{ui → assets/ui}/index.html +118 -118
  14. package/cli/templates/app/cj-tauri.toml +22 -0
  15. package/cli/templates/app/cjpm.toml +34 -17
  16. package/cli/templates/app/src/main.cj +90 -88
  17. package/cli/templates/app-react/README.md +56 -67
  18. package/cli/templates/app-react/{capabilities → assets/capabilities}/default.json +7 -7
  19. package/cli/templates/app-react/{ui → assets/ui}/package.json +19 -19
  20. package/cli/templates/app-react/{ui → assets/ui}/src/main.jsx +6 -6
  21. package/cli/templates/app-react/{ui → assets/ui}/vite.config.js +24 -24
  22. package/cli/templates/app-react/cj-tauri.toml +23 -0
  23. package/cli/templates/app-react/cjpm.toml +34 -17
  24. package/cli/templates/app-react/src/main.cj +91 -102
  25. package/cli/templates/app-vue/README.md +56 -60
  26. package/cli/templates/app-vue/{capabilities → assets/capabilities}/default.json +7 -7
  27. package/cli/templates/app-vue/{ui → assets/ui}/package.json +18 -18
  28. package/cli/templates/app-vue/{ui → assets/ui}/src/main.js +4 -4
  29. package/cli/templates/app-vue/{ui → assets/ui}/vite.config.js +24 -24
  30. package/cli/templates/app-vue/cj-tauri.toml +23 -0
  31. package/cli/templates/app-vue/cjpm.toml +34 -17
  32. package/cli/templates/app-vue/src/main.cj +91 -102
  33. package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/app.json5 +13 -13
  34. package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/resources/base/element/string.json +8 -8
  35. package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/resources/base/media/layered_image.json +6 -6
  36. package/cli/templates/{ohos-pc → ohos-skeleton}/README.md +79 -79
  37. package/cli/templates/{ohos-pc → ohos-skeleton}/build-profile.json5 +46 -46
  38. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/build-profile.json5 +26 -26
  39. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/cjpm.toml +43 -43
  40. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/hvigorfile.ts +6 -6
  41. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/oh-package.json5 +9 -9
  42. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/cangjie/ability_stage.cj +17 -17
  43. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/cangjie/app_host.cj +94 -94
  44. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/cangjie/index.cj +167 -167
  45. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/cangjie/main_ability.cj +139 -139
  46. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/module.json5 +49 -49
  47. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/element/color.json +8 -8
  48. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/element/string.json +16 -16
  49. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/media/layered_image.json +6 -6
  50. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/profile/main_pages.json +2 -2
  51. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/rawfile/capabilities.json +6 -6
  52. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/rawfile/index.html +92 -92
  53. package/cli/templates/{ohos-pc → ohos-skeleton}/hvigor/hvigor-config.json5 +5 -5
  54. package/cli/templates/{ohos-pc → ohos-skeleton}/hvigorfile.ts +6 -6
  55. package/cli/templates/{ohos-pc → ohos-skeleton}/oh-package.json5 +10 -10
  56. package/package.json +4 -2
  57. package/prebuilt/win32-x64/cj-tauri.exe +0 -0
  58. package/src/app.cj +49 -20
  59. package/src/app_spec.cj +76 -0
  60. package/src/arkweb_script.cj +142 -154
  61. package/src/host.cj +20 -17
  62. package/src/host_arkweb.cj +778 -740
  63. package/src/host_webkit.cj +22 -0
  64. package/src/host_webview2.cj +22 -0
  65. package/src/launch.cj +62 -0
  66. package/src/platform/platform_app_context.cj +77 -0
  67. package/src/platform/platform_assets.cj +33 -0
  68. package/src/platform/platform_discover.cj +176 -0
  69. package/src/platform/platform_env.cj +29 -0
  70. package/src/platform/platform_feature.cj +31 -0
  71. package/src/platform/platform_page_source.cj +87 -0
  72. package/src/platform/platform_target_info.cj +32 -0
  73. package/src/platform_host_provider.cj +87 -0
  74. package/src/plugin.cj +36 -2
  75. package/src/plugins/clipboard/plugin_clipboard.cj +8 -0
  76. package/src/plugins/menu/plugin_menu.cj +158 -150
  77. package/src/plugins/notification/plugin_notification.cj +8 -0
  78. package/src/plugins/serial/plugin_serial.cj +454 -442
  79. package/src/plugins/serial/serial_bridge.cj +194 -186
  80. package/src/plugins/serial/serial_ohos_stub.cj +61 -0
  81. package/src/plugins/shell/plugin_shell.cj +258 -230
  82. package/src/plugins/sqlite/plugin_sqlite.cj +710 -697
  83. package/src/plugins/sqlite/sqlite_ffi.cj +211 -206
  84. package/src/plugins/sqlite/sqlite_ohos_stub.cj +91 -0
  85. package/src/tests/app_spec_test.cj +127 -0
  86. package/src/tests/fake_host.cj +23 -8
  87. package/src/tests/platform_contract_test.cj +251 -0
  88. package/src/tests/plugin_test.cj +585 -542
  89. package/src/version.cj +1 -1
  90. package/src/host_factory.cj +0 -48
  91. /package/cli/templates/app-react/{ui → assets/ui}/index.html +0 -0
  92. /package/cli/templates/app-react/{ui → assets/ui}/src/App.css +0 -0
  93. /package/cli/templates/app-react/{ui → assets/ui}/src/App.jsx +0 -0
  94. /package/cli/templates/app-vue/{ui → assets/ui}/index.html +0 -0
  95. /package/cli/templates/app-vue/{ui → assets/ui}/src/App.vue +0 -0
  96. /package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/resources/base/media/background.png +0 -0
  97. /package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/resources/base/media/foreground.png +0 -0
  98. /package/cli/templates/{ohos-pc → ohos-skeleton}/build-hap.sh +0 -0
  99. /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/media/background.png +0 -0
  100. /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/media/foreground.png +0 -0
  101. /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/media/startIcon.png +0 -0
@@ -1,6 +1,7 @@
1
1
  package cjTauri.tests
2
2
 
3
3
  import cjTauri.*
4
+ import cjTauri.platform.*
4
5
  import std.collection.*
5
6
 
6
7
  /*
@@ -47,23 +48,34 @@ class FakeHost <: WebViewHost {
47
48
  public var notifiedArgs: String = ""
48
49
  // 能力位:桩给「有菜单」,用例要测「平台没这个本事」时自行覆盖它
49
50
  public var caps: Int32 = CJ_CAP_MENU
51
+ // load(source) 收敛(批次 3-PR3):记最近一次 load 的来源原文与 resolver 注入痕迹
52
+ public var loadedSource: String = ""
53
+ public var resolverInjected: Option<AssetResolver> = None
50
54
 
51
55
  public init(windowLabel: String) {
52
56
  this.windowLabel = windowLabel
53
57
  }
54
58
 
55
- public func start(html: String): Unit {
56
- this.started = true
57
- this.shownHtml = html
58
- }
59
-
60
59
  public func label(): String {
61
60
  return this.windowLabel
62
61
  }
63
62
 
64
- public func startUrl(url: String): Unit {
63
+ public func setAssetResolver(resolver: Option<AssetResolver>): Unit {
64
+ this.resolverInjected = resolver
65
+ }
66
+
67
+ /**
68
+ * 桩的 load 不做资产解析(那是对应真宿主的事,resolvePage 本身有独立单测):
69
+ * 只记痕——用例断言的是「装配层把哪种来源投给了宿主」
70
+ */
71
+ public func load(source: PageSource): Unit {
65
72
  this.started = true
66
- this.startedUrl = url
73
+ match (source) {
74
+ case Inline(html) => this.shownHtml = html
75
+ case Url(url) => this.startedUrl = url
76
+ case DevServer(url) => this.startedUrl = url
77
+ case Asset(name) => this.loadedSource = "asset:${name}"
78
+ }
67
79
  }
68
80
 
69
81
  public func addInitScript(js: String): Unit {
@@ -176,8 +188,11 @@ class FakeHost <: WebViewHost {
176
188
  this.handler = handler
177
189
  }
178
190
 
191
+ // run 收尾开关:run() 阻塞在 waitForExit,用例把它置 true 让 run 返回(批次 3-PR3 的 load 接线用例用)
192
+ public var quitNow: Bool = false
193
+
179
194
  public func shouldQuit(): Bool {
180
- return false
195
+ return this.quitNow
181
196
  }
182
197
 
183
198
  public func dispose(): Unit {
@@ -0,0 +1,251 @@
1
+ package cjTauri.tests
2
+
3
+ import cjTauri.*
4
+ import cjTauri.platform.*
5
+ import std.collection.*
6
+ import std.unittest.*
7
+ import std.unittest.testmacro.*
8
+
9
+ /*
10
+ * 平台契约骨架测试(RFC-002 批次 0)
11
+ * 不创建窗口、不依赖宿主:TargetInfo / 特性探测 / PageSource / 宿主注册表,纯逻辑。
12
+ */
13
+
14
+ @Test
15
+ func targetInfoDescribe(): Unit {
16
+ let t = TargetInfo("Linux", "x86_64", "webkitgtk", "desktop")
17
+ @Expect(t.os, "Linux")
18
+ @Expect(t.describe(), "Linux/x86_64 host=webkitgtk display=desktop")
19
+ }
20
+
21
+ @Test
22
+ func appContextFeatureProbe(): Unit {
23
+ let fs = ArrayList<PlatformFeature>()
24
+ fs.add(PlatformFeature.Tray)
25
+ fs.add(PlatformFeature.MultiWindow)
26
+ let ctx = AppContext(
27
+ AppPaths("/tmp/d", "/tmp/c", "/tmp/t"),
28
+ TargetInfo("Linux", "x86_64", "webkitgtk", "desktop"),
29
+ false, fs)
30
+ @Expect(ctx.supports(PlatformFeature.Tray), true)
31
+ @Expect(ctx.supports(PlatformFeature.FilePicker), false)
32
+ @Expect(ctx.dev, false)
33
+ }
34
+
35
+ @Test
36
+ func pageSourceMatch(): Unit {
37
+ let src = PageSource.Asset("ui/index.html")
38
+ match (src) {
39
+ case Asset(name) => @Expect(name, "ui/index.html")
40
+ case _ => @Expect(false, true) // 不应到达:Asset 之外的分支
41
+ }
42
+ }
43
+
44
+ @Test
45
+ func desktopDiscoverDefaults(): Unit {
46
+ let ctx = Platform.desktop("com.example.test", "x86_64")
47
+ @Expect(ctx.paths.dataDir.endsWith("com.example.test"), true)
48
+ @Expect(ctx.supports(PlatformFeature.Tray), true)
49
+ @Expect(ctx.target.display, "desktop")
50
+ }
51
+
52
+ @Test
53
+ func mobileDiscoverSandbox(): Unit {
54
+ let ctx = Platform.mobile("/data/app/el2/base/files", "aarch64")
55
+ @Expect(ctx.paths.dataDir, "/data/app/el2/base/files")
56
+ @Expect(ctx.dev, false)
57
+ @Expect(ctx.supports(PlatformFeature.SoftKeyboardInset), true)
58
+ @Expect(ctx.supports(PlatformFeature.Tray), false)
59
+ }
60
+
61
+ /** 优先级最高者胜;create 返回假宿主(pickHost 会真实调用它) */
62
+ class HighPrioProvider <: HostProvider {
63
+ public func priority(target: TargetInfo): Int64 {
64
+ return 10
65
+ }
66
+ public func create(label: String, target: TargetInfo): WebViewHost {
67
+ return FakeHost(label)
68
+ }
69
+ }
70
+
71
+ class LowPrioProvider <: HostProvider {
72
+ public func priority(target: TargetInfo): Int64 {
73
+ return 1
74
+ }
75
+ public func create(label: String, target: TargetInfo): WebViewHost {
76
+ return FakeHost(label)
77
+ }
78
+ }
79
+
80
+ class NullProvider <: HostProvider {
81
+ public func priority(target: TargetInfo): Int64 {
82
+ return 0
83
+ }
84
+ public func create(label: String, target: TargetInfo): WebViewHost {
85
+ return FakeHost(label)
86
+ }
87
+ }
88
+
89
+ @Test
90
+ func hostRegistryPicksHighestPriority(): Unit {
91
+ let before = hostProviderCount()
92
+ registerHost(NullProvider())
93
+ registerHost(LowPrioProvider())
94
+ registerHost(HighPrioProvider())
95
+ @Expect(hostProviderCount(), before + 3)
96
+ let target = TargetInfo("Linux", "x86_64", "webkitgtk", "desktop")
97
+ let picked = pickHost("main", target)
98
+ @Expect(picked.isSome(), true)
99
+ // 胜出者必须是优先级 10 的实现(create 真实执行,label 透传)
100
+ @Expect(picked.getOrThrow().label(), "main")
101
+ }
102
+
103
+ // ---- 批次 1:PageSource → PageLoad 决策 ----
104
+
105
+ @Test
106
+ func resolvePageInlineAndUrl(): Unit {
107
+ match (resolvePage(PageSource.Inline("<p>hi</p>"), None)) {
108
+ case StartHtml(html) => @Expect(html, "<p>hi</p>")
109
+ case _ => @Expect(false, true)
110
+ }
111
+ match (resolvePage(PageSource.Url("https://example.com"), None)) {
112
+ case StartUrl(url) => @Expect(url, "https://example.com")
113
+ case _ => @Expect(false, true)
114
+ }
115
+ // DevServer 与 Url 同路:都走 startUrl
116
+ match (resolvePage(PageSource.DevServer("http://127.0.0.1:5173"), None)) {
117
+ case StartUrl(url) => @Expect(url, "http://127.0.0.1:5173")
118
+ case _ => @Expect(false, true)
119
+ }
120
+ }
121
+
122
+ /** 返回固定内容的解析器 */
123
+ class StubResolver <: AssetResolver {
124
+ let table: HashMap<String, String>
125
+ init(table: HashMap<String, String>) {
126
+ this.table = table
127
+ }
128
+ public func readText(name: String): Option<String> {
129
+ return table.get(name)
130
+ }
131
+ public func exists(name: String): Bool {
132
+ return table.contains(name)
133
+ }
134
+ }
135
+
136
+ @Test
137
+ func resolvePageAssetViaResolver(): Unit {
138
+ let t = HashMap<String, String>()
139
+ t.add("ui/index.html", "<html>ok</html>")
140
+ let src = resolvePage(PageSource.Asset("ui/index.html"), Some(StubResolver(t)))
141
+ match (src) {
142
+ case StartHtml(html) => @Expect(html, "<html>ok</html>")
143
+ case _ => @Expect(false, true)
144
+ }
145
+ }
146
+
147
+ @Test
148
+ func resolvePageAssetMissingThrowsReadableError(): Unit {
149
+ let t = HashMap<String, String>()
150
+ var msg = ""
151
+ try {
152
+ let _ = resolvePage(PageSource.Asset("no/such.html"), Some(StubResolver(t)))
153
+ } catch (e: Exception) {
154
+ msg = e.message
155
+ }
156
+ @Expect(msg.contains("no/such.html"), true)
157
+ }
158
+
159
+ @Test
160
+ func resolvePageAssetNoResolverReadsFromCwd(): Unit {
161
+ // 未注入解析器:按 cwd 相对路径读盘(与模板 textOrFile 桌面行为等价)。
162
+ // 测试 cwd = 仓库根,读一个确定存在的文件验证「读到了」而非白屏。
163
+ let src = resolvePage(PageSource.Asset("cjpm.toml"), None)
164
+ match (src) {
165
+ case StartHtml(html) => @Expect(html.size > 0, true)
166
+ case _ => @Expect(false, true)
167
+ }
168
+ }
169
+
170
+ // ---- 批次 1 评审修正:devMode 自动改写(RFC §4.3)----
171
+
172
+ @Test
173
+ func rewriteForDevOnlyTouchesAsset(): Unit {
174
+ // Asset + devUrl → DevServer
175
+ match (rewriteForDev(PageSource.Asset("ui/index.html"), Some("http://127.0.0.1:5173"))) {
176
+ case DevServer(url) => @Expect(url, "http://127.0.0.1:5173")
177
+ case _ => @Expect(false, true)
178
+ }
179
+ // 显式 Inline 是业务意图,不改写
180
+ match (rewriteForDev(PageSource.Inline("<p/>"), Some("http://d"))) {
181
+ case Inline(html) => @Expect(html, "<p/>")
182
+ case _ => @Expect(false, true)
183
+ }
184
+ // 无 devUrl 原样返回
185
+ match (rewriteForDev(PageSource.Asset("ui/index.html"), None)) {
186
+ case Asset(name) => @Expect(name, "ui/index.html")
187
+ case _ => @Expect(false, true)
188
+ }
189
+ }
190
+
191
+ // ---- 批次 2:DirectoryAssetResolver ----
192
+
193
+ @Test
194
+ func directoryAssetResolverReadsRepo(): Unit {
195
+ let r = DirectoryAssetResolver(".")
196
+ let t = r.readText("cjpm.toml")
197
+ @Expect(t.isSome(), true)
198
+ @Expect(t.getOrThrow().size > 0, true)
199
+ @Expect(r.exists("cjpm.toml"), true)
200
+ @Expect(r.readText("no/such/file.txt").isSome(), false)
201
+ @Expect(r.exists("no/such/file.txt"), false)
202
+ }
203
+
204
+ // ---- 批次 1:guessTarget 与 resolveHost 接线 ----
205
+
206
+ @Test
207
+ func guessTargetDesktopFields(): Unit {
208
+ let t = Platform.guessTarget()
209
+ @Expect(t.display, "desktop")
210
+ @Expect(t.hostKind, expectedDesktopHostKind())
211
+ @Expect(t.os, expectedDesktopOs())
212
+ }
213
+
214
+ // 桌面两平台的宿主形态与系统名本来就不同(Windows = webview2 / Windows,Linux = webkitgtk / Linux)。
215
+ // 期望值写死一套会让用例在另一个平台上恒红——本机 Windows 上这条一直是红的(写死成 Linux 那套)。
216
+ @When[os == "Windows"]
217
+ func expectedDesktopHostKind(): String {
218
+ return "webview2"
219
+ }
220
+
221
+ @When[os != "Windows"]
222
+ func expectedDesktopHostKind(): String {
223
+ return "webkitgtk"
224
+ }
225
+
226
+ @When[os == "Windows"]
227
+ func expectedDesktopOs(): String {
228
+ return "Windows"
229
+ }
230
+
231
+ @When[os != "Windows"]
232
+ func expectedDesktopOs(): String {
233
+ return "Linux"
234
+ }
235
+
236
+ /** 注册表有匹配时,resolveHost 必须取注册表而不是内置宿主 */
237
+ class WinProvider <: HostProvider {
238
+ public func priority(target: TargetInfo): Int64 {
239
+ return 5
240
+ }
241
+ public func create(label: String, target: TargetInfo): WebViewHost {
242
+ return FakeHost(label)
243
+ }
244
+ }
245
+
246
+ @Test
247
+ func resolveHostPrefersRegistryOverBuiltin(): Unit {
248
+ registerHost(WinProvider())
249
+ let h = resolveHost("registry-win")
250
+ @Expect(h.label(), "registry-win")
251
+ }