浏览代码

refactor(tauri): inject script with webview init API (#1186)

Lucas Fernandes Nogueira 4 年之前
父节点
当前提交
4412b7c438

+ 7 - 0
.changes/refactor-tauri-entry.md

@@ -0,0 +1,7 @@
+---
+"tauri": minor
+---
+
+The Tauri script is now injected with the webview `init` API, so it is available after page changes.
+It will no longer be injected on `${distDir}/index.tauri.html`, but we will add a `${distDir}/__tauri.js` file to read it at app compile time.
+You should add that to your `.gitignore` file.

+ 0 - 1
cli/core/Cargo.lock

@@ -2423,7 +2423,6 @@ dependencies = [
  "clap 3.0.0-beta.2",
  "colored",
  "convert_case",
- "handlebars",
  "html5ever",
  "http",
  "json-patch",

+ 0 - 1
cli/core/Cargo.toml

@@ -25,7 +25,6 @@ tiny_http = "0.8"
 attohttpc = "0.16"
 url = "2.2"
 http = "0.2"
-handlebars = "3.5"
 notify = "4.0"
 shared_child = "0.3"
 toml_edit = "0.2"

+ 11 - 3
cli/core/src/build.rs

@@ -8,7 +8,7 @@ use crate::helpers::{
   config::get as get_config,
   execute_with_output,
   manifest::rewrite_manifest,
-  TauriHtml,
+  TauriHtml, TauriScript,
 };
 use std::env::{set_current_dir, set_var};
 use std::fs::read_to_string;
@@ -97,15 +97,23 @@ impl Build {
     let config_guard = config.lock().unwrap();
     let config_ = config_guard.as_ref().unwrap();
 
+    // index.tauri.html
     let index_html_path = PathBuf::from(&config_.build.dist_dir).join("index.html");
     let tauri_html = TauriHtml::new(&config_.build.dist_dir, read_to_string(index_html_path)?)
       .inliner_enabled(config_.tauri.inliner.active && !config_.tauri.embedded_server.active)
-      .global_tauri(config_.build.with_global_tauri)
-      .generate()?;
+      .get()?;
     let tauri_index_html_path = PathBuf::from(&config_.build.dist_dir).join("index.tauri.html");
     let mut tauri_index_html_file = File::create(tauri_index_html_path)?;
     tauri_index_html_file.write_all(tauri_html.as_bytes())?;
 
+    // __tauri.js
+    let tauri_script = TauriScript::new()
+      .global_tauri(config_.build.with_global_tauri)
+      .get();
+    let tauri_script_path = PathBuf::from(&config_.build.dist_dir).join("__tauri.js");
+    let mut tauri_script_file = File::create(tauri_script_path)?;
+    tauri_script_file.write_all(tauri_script.as_bytes())?;
+
     let settings = settings_builder.build()?;
 
     if let Some(before_build) = &config_.build.before_build_command {

+ 17 - 4
cli/core/src/dev.rs

@@ -2,7 +2,7 @@ use crate::helpers::{
   app_paths::{app_dir, tauri_dir},
   config::{get as get_config, reload as reload_config, ConfigHandle},
   manifest::rewrite_manifest,
-  Logger, TauriHtml,
+  Logger, TauriHtml, TauriScript,
 };
 
 use attohttpc::{Method, RequestBuilder};
@@ -14,6 +14,9 @@ use url::Url;
 
 use std::env::set_var;
 use std::ffi::OsStr;
+use std::fs::File;
+use std::io::Write;
+use std::path::PathBuf;
 use std::process::{exit, Command};
 use std::sync::mpsc::{channel, Receiver};
 use std::sync::{Arc, Mutex};
@@ -138,6 +141,18 @@ impl Dev {
 
     rewrite_manifest(config.clone())?;
 
+    // __tauri.js
+    {
+      let config_guard = config.lock().unwrap();
+      let config_ = config_guard.as_ref().unwrap();
+      let tauri_script = TauriScript::new()
+        .global_tauri(config_.build.with_global_tauri)
+        .get();
+      let tauri_script_path = PathBuf::from(&config_.build.dist_dir).join("__tauri.js");
+      let mut tauri_script_file = File::create(tauri_script_path)?;
+      tauri_script_file.write_all(tauri_script.as_bytes())?;
+    }
+
     let (child_wait_tx, child_wait_rx) = channel();
     let child_wait_rx = Arc::new(Mutex::new(child_wait_rx));
 
@@ -250,9 +265,7 @@ fn proxy_dev_server(config: ConfigHandle, dev_path: &Url, dev_port: u16) -> crat
       let config_guard = config.lock().unwrap();
       let config = config_guard.as_ref().unwrap();
       let response = request_builder.send()?.text()?;
-      let tauri_html = TauriHtml::new(&config.build.dist_dir, response)
-        .global_tauri(config.build.with_global_tauri)
-        .generate()?;
+      let tauri_html = TauriHtml::new(&config.build.dist_dir, response).get()?;
       request.respond(Response::from_data(tauri_html))?;
     } else {
       let response = request_builder.send()?.bytes()?;

+ 2 - 2
cli/core/src/helpers/mod.rs

@@ -2,10 +2,10 @@ pub mod app_paths;
 pub mod config;
 mod logger;
 pub mod manifest;
-mod tauri_html;
+mod tauri_entry;
 
 pub use logger::Logger;
-pub use tauri_html::TauriHtml;
+pub use tauri_entry::{TauriHtml, TauriScript};
 
 use std::io::{BufRead, BufReader};
 use std::process::{Command, Stdio};

+ 68 - 0
cli/core/src/helpers/tauri_entry.rs

@@ -0,0 +1,68 @@
+use kuchiki::traits::*;
+use tauri_inliner::inline_html_string;
+
+use std::path::PathBuf;
+
+pub struct TauriHtml {
+  original: String,
+  html_dir: PathBuf,
+  inliner_enabled: bool,
+}
+
+impl TauriHtml {
+  pub fn new(html_dir: impl Into<PathBuf>, html: String) -> Self {
+    Self {
+      original: html,
+      html_dir: html_dir.into(),
+      inliner_enabled: false,
+    }
+  }
+
+  pub fn inliner_enabled(mut self, enabled: bool) -> Self {
+    self.inliner_enabled = enabled;
+    self
+  }
+
+  pub fn get(self) -> crate::Result<String> {
+    let html = if self.inliner_enabled {
+      inline_html_string(&self.original, self.html_dir, Default::default())?
+    } else {
+      self.original
+    };
+
+    let new_html = kuchiki::parse_html().one(html).to_string();
+
+    Ok(new_html)
+  }
+}
+
+#[derive(Default)]
+pub struct TauriScript {
+  global_tauri: bool,
+}
+
+impl TauriScript {
+  pub fn new() -> Self {
+    Default::default()
+  }
+
+  pub fn global_tauri(mut self, global_tauri: bool) -> Self {
+    self.global_tauri = global_tauri;
+    self
+  }
+
+  pub fn get(self) -> String {
+    let mut scripts = Vec::new();
+    scripts.push(include_str!("../templates/tauri.js"));
+    scripts.push(include_str!("../templates/mutation-observer.js"));
+
+    if self.global_tauri {
+      scripts.insert(
+        0,
+        include_str!(concat!(env!("OUT_DIR"), "/tauri.bundle.umd.js")),
+      );
+    }
+
+    scripts.join("\n\n")
+  }
+}

+ 0 - 107
cli/core/src/helpers/tauri_html.rs

@@ -1,107 +0,0 @@
-use handlebars::Handlebars;
-use html5ever::{interface::QualName, namespace_url, ns, LocalName};
-use kuchiki::{traits::*, NodeRef};
-use once_cell::sync::Lazy;
-use tauri_inliner::inline_html_string;
-
-use std::collections::BTreeMap;
-use std::path::PathBuf;
-
-fn handlebars() -> &'static Handlebars<'static> {
-  static HANDLEBARS: Lazy<Handlebars> = Lazy::new(|| {
-    let mut handlebars = Handlebars::new();
-
-    handlebars
-      .register_template_string(
-        "mutation-observer.js",
-        include_str!("../templates/mutation-observer.js"),
-      )
-      .map_err(|e| e.to_string())
-      .expect("Failed to setup handlebar template");
-    handlebars
-  });
-  &HANDLEBARS
-}
-
-pub struct TauriHtml {
-  original: String,
-  html_dir: PathBuf,
-  inliner_enabled: bool,
-  global_tauri: bool,
-}
-
-impl TauriHtml {
-  pub fn new(html_dir: impl Into<PathBuf>, html: String) -> Self {
-    Self {
-      original: html,
-      html_dir: html_dir.into(),
-      inliner_enabled: false,
-      global_tauri: false,
-    }
-  }
-
-  pub fn inliner_enabled(mut self, enabled: bool) -> Self {
-    self.inliner_enabled = enabled;
-    self
-  }
-
-  pub fn global_tauri(mut self, global_tauri: bool) -> Self {
-    self.global_tauri = global_tauri;
-    self
-  }
-
-  pub fn generate(self) -> crate::Result<String> {
-    let html = if self.inliner_enabled {
-      inline_html_string(&self.original, self.html_dir, Default::default())?
-    } else {
-      self.original
-    };
-
-    let document = kuchiki::parse_html().one(html);
-    let body = document.select_first("body");
-    let head = document.select_first("head");
-
-    let handlebars = handlebars();
-
-    if let Ok(ref head) = head {
-      let mut data = BTreeMap::new();
-      data.insert("target".to_string(), "head".to_string());
-      let head_mutation_observer_script =
-        create_script_element(&handlebars.render("mutation-observer.js", &data)?);
-      head.as_node().prepend(head_mutation_observer_script);
-    }
-
-    if let Ok(ref body) = body {
-      let mut data = BTreeMap::new();
-      data.insert("target".to_string(), "body".to_string());
-      let body_mutation_observer_script =
-        create_script_element(&handlebars.render("mutation-observer.js", &data)?);
-      body.as_node().prepend(body_mutation_observer_script);
-    }
-
-    if let Ok(target) = if head.is_ok() { head } else { body } {
-      let tauri_script = create_script_element(include_str!("../templates/tauri.js"));
-      target.as_node().prepend(tauri_script);
-
-      if self.global_tauri {
-        let global_api_script = create_script_element(include_str!(concat!(
-          env!("OUT_DIR"),
-          "/tauri.bundle.umd.js"
-        )));
-        target.as_node().prepend(global_api_script);
-      }
-    }
-
-    let new_html = document.to_string();
-    Ok(new_html)
-  }
-}
-
-fn create_script_element(content: &str) -> NodeRef {
-  let script = NodeRef::new_element(
-    QualName::new(None, ns!(html), LocalName::from("script")),
-    None,
-  );
-  script.append(NodeRef::new_text(content));
-  script
-}

+ 19 - 8
cli/core/src/templates/mutation-observer.js

@@ -1,4 +1,4 @@
-(function () {
+function __tauri_mutation_observer (target) {
   function loadAsset(path, type) {
     if (path) {
       window.__TAURI__.loadAsset(path, type)
@@ -23,14 +23,25 @@
     })
   })
 
-  {{#if (eq target "body")}}
-    var target = document.documentElement
-  {{ else }}
-    var target = document.head
-  {{/if}}
-
   observer.observe(target, {
     childList: true,
     subtree: true
   })
-})()
+}
+
+__tauri_mutation_observer(document.documentElement)
+if (
+  document.readyState === 'complete' ||
+  document.readyState === 'interactive'
+) {
+  __tauri_mutation_observer(document.head)
+} else {
+  window.addEventListener(
+    'DOMContentLoaded',
+    function () {
+      __tauri_mutation_observer(document.head)
+    },
+    true
+  )
+}
+

+ 22 - 8
cli/core/src/templates/tauri.js

@@ -116,15 +116,29 @@ if (!String.prototype.startsWith) {
         delete window[callback]
       }, true)
 
-      window.__TAURI_INVOKE_HANDLER__(
-        _objectSpread(
-          {
-            callback: callback,
-            error: error
-          },
-          args
+      if (window.__TAURI_INVOKE_HANDLER__) {
+        window.__TAURI_INVOKE_HANDLER__(
+          _objectSpread(
+            {
+              callback: callback,
+              error: error
+            },
+            args
+          )
         )
-      )
+      } else {
+        window.addEventListener('DOMContentLoaded', function () {
+          window.__TAURI_INVOKE_HANDLER__(
+            _objectSpread(
+              {
+                callback: callback,
+                error: error
+              },
+              args
+            )
+          )
+        })
+      }
     })
   }
 

+ 2 - 1
cli/tauri.js/test/jest/fixtures/app/.gitignore

@@ -1 +1,2 @@
-dist/index.tauri.html
+dist/index.tauri.html
+dist/__tauri.js

+ 1 - 0
cli/tauri.js/test/jest/fixtures/empty/.gitignore

@@ -1,2 +1,3 @@
 src-tauri
 dist/index.tauri.html
+dist/__tauri.js

+ 34 - 9
tauri/build.rs

@@ -1,6 +1,5 @@
 use cfg_aliases::cfg_aliases;
 
-#[cfg(any(feature = "embedded-server", feature = "no-server"))]
 use std::{
   env,
   error::Error,
@@ -11,7 +10,7 @@ use std::{
 
 #[cfg(any(feature = "embedded-server", feature = "no-server"))]
 pub fn main() -> Result<(), Box<dyn Error>> {
-  shared();
+  shared()?;
 
   let out_dir = env::var("OUT_DIR")?;
 
@@ -64,10 +63,11 @@ pub fn main() -> Result<(), Box<dyn Error>> {
         .build("data.rs", inlined_assets)
         .expect("failed to build data.rs");
 
-      let original_index_html_path = Path::new(&dist_path_string).join("index.tauri.html");
-      let original_index_html = read_to_string(original_index_html_path)?;
-
-      write!(index_html_file, "{}", original_index_html)?;
+      write!(
+        index_html_file,
+        "{}",
+        read_to_string(dist_path.join("index.tauri.html"))?
+      )?;
     }
     None => {
       // dummy assets
@@ -86,12 +86,37 @@ pub fn main() -> Result<(), Box<dyn Error>> {
 }
 
 #[cfg(not(any(feature = "embedded-server", feature = "no-server")))]
-pub fn main() {
-  shared();
+pub fn main() -> Result<(), Box<dyn Error>> {
+  shared()
 }
 
-fn shared() {
+fn shared() -> Result<(), Box<dyn Error>> {
+  let out_dir = env::var("OUT_DIR")?;
+  let dest_tauri_script_path = Path::new(&out_dir).join("__tauri.js");
+  let mut tauri_script_file = BufWriter::new(File::create(&dest_tauri_script_path)?);
+
+  match env::var_os("TAURI_DIST_DIR") {
+    Some(dist_path) => {
+      let dist_path_string = dist_path.into_string().unwrap();
+      let dist_path = Path::new(&dist_path_string);
+
+      write!(
+        tauri_script_file,
+        "{}",
+        read_to_string(dist_path.join("__tauri.js"))?
+      )?;
+    }
+    None => {
+      write!(
+        tauri_script_file,
+        r#"console.warning("Couldn't find ENV: TAURI_DIST_DIR, the Tauri API won't work. Please rebuild with the Tauri CLI.")"#,
+      )?;
+    }
+  }
+
   setup_env_aliases();
+
+  Ok(())
 }
 
 #[allow(clippy::cognitive_complexity)]

+ 1 - 0
tauri/examples/api/.gitignore

@@ -1,5 +1,6 @@
 /node_modules/
 /public/build/
 /public/index.tauri.html
+/public/__tauri.js
 
 .DS_Store

文件差异内容过多而无法显示
+ 0 - 0
tauri/examples/communication/dist/__tauri.js


文件差异内容过多而无法显示
+ 0 - 0
tauri/examples/communication/dist/index.tauri.html


+ 2 - 0
tauri/src/app/runner.rs

@@ -272,6 +272,7 @@ fn build_webview<W: Webview + 'static>(
 
   let init = format!(
     r#"
+      {tauri_init}
       {event_init}
       if (window.__TAURI_INVOKE_HANDLER__) {{
         window.__TAURI_INVOKE_HANDLER__({{ cmd: "__initialized" }})
@@ -282,6 +283,7 @@ fn build_webview<W: Webview + 'static>(
       }}
       {plugin_init}
     "#,
+    tauri_init = include_str!(concat!(env!("OUT_DIR"), "/__tauri.js")),
     event_init = init(),
     plugin_init = crate::async_runtime::block_on(crate::plugin::init_script(W::plugin_store()))
   );

部分文件因为文件数量过多而无法显示