bern_target_api/
script.rs1use crate::BuildContext;
3pub fn dependency(context: &BuildContext) -> String {
4 let Some(backend) = context.target.javascript_backend() else {
5 return String::new();
6 };
7 format!(
8 "bern-script = {{ {}, features = [{}] }}\n",
9 context.framework.dependency("bern-script"),
10 if backend == "boa" { "\"boa\"" } else { "" }
11 )
12}
13pub fn webview(context: &BuildContext) -> bool {
14 context.target.javascript_backend() == Some("webview")
15}
16pub const RUNTIME: &str = bern_script::HOST_RUNTIME;
17pub const WINDOWS_SOURCES: [&str; 3] = [
21 include_str!("script.rs"),
22 include_str!("../templates/script/host.cpp"),
23 RUNTIME,
24];
25pub fn html(complete: &str) -> String {
26 format!(
27 "<!doctype html><meta charset=\"utf-8\"><body><script>function bernScriptComplete(value){{{complete}}}\n{RUNTIME}</script>"
28 )
29}
30pub const HEADER: &str = "\nvoid bern_script_start(void);\nvoid bern_script_stop(void);\nBernBuffer bern_script_poll(double now_ms);\nvoid bern_script_complete(const uint8_t *bytes, size_t len);\n";
31
32pub fn configure(files: &mut [crate::GeneratedFile], context: &BuildContext) {
34 let Some(backend) = context.target.javascript_backend() else {
35 return;
36 };
37 for file in files {
38 let path = file.path.to_string_lossy();
39 if path.ends_with("Cargo.toml") {
40 let source = String::from_utf8_lossy(&file.contents);
41 file.contents = source
42 .replacen(
43 "[dependencies]\n",
44 &format!("[dependencies]\n{}", dependency(context)),
45 1,
46 )
47 .into_bytes();
48 }
49 if backend == "boa" {
50 continue;
51 }
52 let mut source = String::from_utf8_lossy(&file.contents).into_owned();
53 if path.ends_with("/bern.h") {
54 source.push_str(HEADER);
55 } else if path.ends_with("BernRuntime.swift") {
56 source = source.replace(" platformWillStart()", " BernScriptHost.shared.start()\n platformWillStart()")
57 .replace(" if let handle { bern_app_stop(handle) }", " BernScriptHost.shared.stop()\n if let handle { bern_app_stop(handle) }")
58 .replace(" startupError = String(decoding:", " BernScriptHost.shared.stop()\n startupError = String(decoding:");
59 let html = html("window.webkit.messageHandlers.bernScript.postMessage(value)");
60 source.push_str(&include_str!("../templates/script/host.swift").replace(
61 "/*bern:script-html*/",
62 &serde_json::to_string(&html).unwrap(),
63 ));
64 } else if path.ends_with("BernRuntime.kt") {
65 source = source.replace("object BernNative {", "object BernNative {\n external fun scriptStart()\n external fun scriptStop()\n external fun scriptPoll(nowMs: Double): ByteArray\n external fun scriptComplete(bytes: ByteArray)")
66 .replace(" val reader = BernReader(BernNative.start(", " BernScriptHost.start(context)\n val reader = BernReader(BernNative.start(")
67 .replace(" startupError = reader.text()", " BernScriptHost.stop()\n startupError = reader.text()");
68 let html = html("BernScriptBridge.complete(value)");
69 source.push_str(&include_str!("../templates/script/host.kt").replace(
70 "/*bern:script-html*/",
71 &serde_json::to_string(&html).unwrap().replace('$', "\\$"),
72 ));
73 } else if path.ends_with("bern_base.h") {
74 source = source.replace(
75 "void bern_buffer_free(BernBuffer buffer);",
76 &format!("void bern_buffer_free(BernBuffer buffer);{HEADER}"),
77 );
78 } else if path.ends_with("bern_pch.h") {
79 source.push_str("\n#include <WebView2.h>\n#include <wrl.h>\n#pragma comment(lib, \"WebView2LoaderStatic.lib\")\n");
80 } else if path.ends_with("bern_base.cpp") {
81 source = source
82 .replace(
83 "namespace bern {",
84 "namespace bern::Scripts { void Start(); void Stop(); }\nnamespace bern {",
85 )
86 .replace(
87 " catalog = &app;",
88 " Scripts::Start();\n catalog = &app;",
89 )
90 .replace(
91 " auto app = handle;",
92 " Scripts::Stop();\n auto app = handle;",
93 )
94 .replace(
95 " if (!app_handle) {",
96 " if (!app_handle) {\n Scripts::Stop();",
97 );
98 let html = html("window.chrome.webview.postMessage(value)");
99 source.push_str(&include_str!("../templates/script/host.cpp").replace(
100 "/*bern:script-html*/",
101 &format!("R\"BERNJS({html})BERNJS\""),
102 ));
103 } else if path.ends_with("bern.js") && backend == "browser" {
104 source = source.replace(
105 " start(fingerprint, resources) {",
106 " start(fingerprint, resources) {\n this.stopScripts = startScriptHost(this);",
107 );
108 source = source.replace(
109 " throw new Error(message);",
110 " this.stopScripts?.();\n throw new Error(message);",
111 );
112 source.push_str(include_str!("../templates/script/host.js"));
113 } else {
114 continue;
115 }
116 file.contents = source.into_bytes();
117 }
118}